route.c revision 1.5 1 /*
2 * Copyright (c) 1980, 1986, 1991 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * from: @(#)route.c 7.22 (Berkeley) 6/27/91
34 * $Id: route.c,v 1.5 1993/12/18 00:41:03 mycroft Exp $
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/proc.h>
40 #include <sys/mbuf.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/domain.h>
44 #include <sys/protosw.h>
45 #include <sys/ioctl.h>
46
47 #include <net/if.h>
48 #include <net/af.h>
49 #include <net/route.h>
50 #include <net/raw_cb.h>
51 #include <net/netisr.h>
52
53 #include <netinet/in.h>
54 #include <netinet/in_var.h>
55
56 #ifdef NS
57 #include <netns/ns.h>
58 #endif
59
60 #include <machine/mtpr.h>
61
62 #define SA(p) ((struct sockaddr *)(p))
63
64 int rttrash; /* routes not in table but not freed */
65 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
66 int rthashsize = RTHASHSIZ; /* for netstat, etc. */
67
68 static int rtinits_done = 0;
69 struct radix_node_head *ns_rnhead, *in_rnhead;
70 struct radix_node *rn_match(), *rn_delete(), *rn_addroute();
71
72 rtinitheads()
73 {
74 if (rtinits_done == 0 &&
75 #ifdef NS
76 rn_inithead(&ns_rnhead, 16, AF_NS) &&
77 #endif
78 rn_inithead(&in_rnhead, 32, AF_INET))
79 rtinits_done = 1;
80 }
81
82 /*
83 * Packet routing routines.
84 */
85 rtalloc(ro)
86 register struct route *ro;
87 {
88 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
89 return; /* XXX */
90 ro->ro_rt = rtalloc1(&ro->ro_dst, 1);
91 }
92
93 struct rtentry *
94 rtalloc1(dst, report)
95 register struct sockaddr *dst;
96 int report;
97 {
98 register struct radix_node_head *rnh;
99 register struct rtentry *rt;
100 register struct radix_node *rn;
101 struct rtentry *newrt = 0;
102 int s = splnet(), err = 0, msgtype = RTM_MISS;
103
104 for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); )
105 rnh = rnh->rnh_next;
106 if (rnh && rnh->rnh_treetop &&
107 (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) &&
108 ((rn->rn_flags & RNF_ROOT) == 0)) {
109 newrt = rt = (struct rtentry *)rn;
110 if (report && (rt->rt_flags & RTF_CLONING)) {
111 if ((err = rtrequest(RTM_RESOLVE, dst, SA(0),
112 SA(0), 0, &newrt)) ||
113 ((rt->rt_flags & RTF_XRESOLVE)
114 && (msgtype = RTM_RESOLVE))) /* intended! */
115 goto miss;
116 } else
117 rt->rt_refcnt++;
118 } else {
119 rtstat.rts_unreach++;
120 miss: if (report)
121 rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err);
122 }
123 splx(s);
124 return (newrt);
125 }
126
127 rtfree(rt)
128 register struct rtentry *rt;
129 {
130 register struct ifaddr *ifa;
131 if (rt == 0)
132 panic("rtfree");
133 rt->rt_refcnt--;
134 if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
135 rttrash--;
136 if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
137 panic ("rtfree 2");
138 free((caddr_t)rt, M_RTABLE);
139 }
140 }
141
142 /*
143 * Force a routing table entry to the specified
144 * destination to go through the given gateway.
145 * Normally called as a result of a routing redirect
146 * message from the network layer.
147 *
148 * N.B.: must be called at splnet
149 *
150 */
151 rtredirect(dst, gateway, netmask, flags, src, rtp)
152 struct sockaddr *dst, *gateway, *netmask, *src;
153 int flags;
154 struct rtentry **rtp;
155 {
156 register struct rtentry *rt = 0;
157 int error = 0;
158 short *stat = 0;
159
160 /* verify the gateway is directly reachable */
161 if (ifa_ifwithnet(gateway) == 0) {
162 error = ENETUNREACH;
163 goto done;
164 }
165 rt = rtalloc1(dst, 0);
166 /*
167 * If the redirect isn't from our current router for this dst,
168 * it's either old or wrong. If it redirects us to ourselves,
169 * we have a routing loop, perhaps as a result of an interface
170 * going down recently.
171 */
172 #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
173 if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway))
174 error = EINVAL;
175 else if (ifa_ifwithaddr(gateway))
176 error = EHOSTUNREACH;
177 if (error)
178 goto done;
179 /*
180 * Create a new entry if we just got back a wildcard entry
181 * or the the lookup failed. This is necessary for hosts
182 * which use routing redirects generated by smart gateways
183 * to dynamically build the routing tables.
184 */
185 if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
186 goto create;
187 /*
188 * Don't listen to the redirect if it's
189 * for a route to an interface.
190 */
191 if (rt->rt_flags & RTF_GATEWAY) {
192 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
193 /*
194 * Changing from route to net => route to host.
195 * Create new route, rather than smashing route to net.
196 */
197 create:
198 flags |= RTF_GATEWAY | RTF_DYNAMIC;
199 error = rtrequest((int)RTM_ADD, dst, gateway,
200 SA(0), flags,
201 (struct rtentry **)0);
202 stat = &rtstat.rts_dynamic;
203 } else {
204 /*
205 * Smash the current notion of the gateway to
206 * this destination. Should check about netmask!!!
207 */
208 if (gateway->sa_len <= rt->rt_gateway->sa_len) {
209 Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
210 rt->rt_flags |= RTF_MODIFIED;
211 flags |= RTF_MODIFIED;
212 stat = &rtstat.rts_newgateway;
213 } else
214 error = ENOSPC;
215 }
216 } else
217 error = EHOSTUNREACH;
218 done:
219 if (rt) {
220 if (rtp && !error)
221 *rtp = rt;
222 else
223 rtfree(rt);
224 }
225 if (error)
226 rtstat.rts_badredirect++;
227 else
228 (stat && (*stat)++);
229 rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error);
230 }
231
232 /*
233 * Routing table ioctl interface.
234 */
235 rtioctl(req, data, p)
236 int req;
237 caddr_t data;
238 struct proc *p;
239 {
240 #ifndef COMPAT_43
241 return (EOPNOTSUPP);
242 #else
243 register struct ortentry *entry = (struct ortentry *)data;
244 int error;
245 struct sockaddr *netmask = 0;
246
247 if (req == SIOCADDRT)
248 req = RTM_ADD;
249 else if (req == SIOCDELRT)
250 req = RTM_DELETE;
251 else
252 return (EINVAL);
253
254 if (error = suser(p->p_ucred, &p->p_acflag))
255 return (error);
256 #if BYTE_ORDER != BIG_ENDIAN
257 if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) {
258 entry->rt_dst.sa_family = entry->rt_dst.sa_len;
259 entry->rt_dst.sa_len = 16;
260 }
261 if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) {
262 entry->rt_gateway.sa_family = entry->rt_gateway.sa_len;
263 entry->rt_gateway.sa_len = 16;
264 }
265 #else
266 if (entry->rt_dst.sa_len == 0)
267 entry->rt_dst.sa_len = 16;
268 if (entry->rt_gateway.sa_len == 0)
269 entry->rt_gateway.sa_len = 16;
270 #endif
271 if ((entry->rt_flags & RTF_HOST) == 0)
272 switch (entry->rt_dst.sa_family) {
273 #ifdef INET
274 case AF_INET:
275 {
276 extern struct sockaddr_in icmpmask;
277 struct sockaddr_in *dst_in =
278 (struct sockaddr_in *)&entry->rt_dst;
279
280 in_sockmaskof(dst_in->sin_addr, &icmpmask);
281 netmask = (struct sockaddr *)&icmpmask;
282 }
283 break;
284 #endif
285 #ifdef NS
286 case AF_NS:
287 {
288 extern struct sockaddr_ns ns_netmask;
289 netmask = (struct sockaddr *)&ns_netmask;
290 }
291 #endif
292 }
293 error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask,
294 entry->rt_flags, (struct rtentry **)0);
295 rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL),
296 &(entry->rt_dst), &(entry->rt_gateway),
297 netmask, SA(0), entry->rt_flags, error);
298 return (error);
299 #endif
300 }
301
302 struct ifaddr *
303 ifa_ifwithroute(flags, dst, gateway)
304 int flags;
305 struct sockaddr *dst, *gateway;
306 {
307 register struct ifaddr *ifa;
308 if ((flags & RTF_GATEWAY) == 0) {
309 /*
310 * If we are adding a route to an interface,
311 * and the interface is a pt to pt link
312 * we should search for the destination
313 * as our clue to the interface. Otherwise
314 * we can use the local address.
315 */
316 ifa = 0;
317 if (flags & RTF_HOST)
318 ifa = ifa_ifwithdstaddr(dst);
319 if (ifa == 0)
320 ifa = ifa_ifwithaddr(gateway);
321 } else {
322 /*
323 * If we are adding a route to a remote net
324 * or host, the gateway may still be on the
325 * other end of a pt to pt link.
326 */
327 ifa = ifa_ifwithdstaddr(gateway);
328 }
329 if (ifa == 0)
330 ifa = ifa_ifwithnet(gateway);
331 if (ifa == 0) {
332 struct rtentry *rt = rtalloc1(dst, 0);
333 if (rt == 0)
334 return (0);
335 rt->rt_refcnt--;
336 if ((ifa = rt->rt_ifa) == 0)
337 return (0);
338 }
339 if (ifa->ifa_addr->sa_family != dst->sa_family) {
340 struct ifaddr *oifa = ifa, *ifaof_ifpforaddr();
341 ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
342 if (ifa == 0)
343 ifa = oifa;
344 }
345 return (ifa);
346 }
347
348 #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
349
350 rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
351 int req, flags;
352 struct sockaddr *dst, *gateway, *netmask;
353 struct rtentry **ret_nrt;
354 {
355 int s = splnet(), len, error = 0;
356 register struct rtentry *rt;
357 register struct radix_node *rn;
358 register struct radix_node_head *rnh;
359 struct ifaddr *ifa, *ifa_ifwithdstaddr();
360 struct sockaddr *ndst;
361 u_char af = dst->sa_family;
362 #define senderr(x) { error = x ; goto bad; }
363
364 if (rtinits_done == 0)
365 rtinitheads();
366 for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); )
367 rnh = rnh->rnh_next;
368 if (rnh == 0)
369 senderr(ESRCH);
370 if (flags & RTF_HOST)
371 netmask = 0;
372 switch (req) {
373 case RTM_DELETE:
374 if (ret_nrt && (rt = *ret_nrt)) {
375 RTFREE(rt);
376 *ret_nrt = 0;
377 }
378 if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask,
379 rnh->rnh_treetop)) == 0)
380 senderr(ESRCH);
381 if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
382 panic ("rtrequest delete");
383 rt = (struct rtentry *)rn;
384 rt->rt_flags &= ~RTF_UP;
385 if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
386 ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
387 rttrash++;
388 if (rt->rt_refcnt <= 0)
389 rtfree(rt);
390 break;
391
392 case RTM_RESOLVE:
393 if (ret_nrt == 0 || (rt = *ret_nrt) == 0)
394 senderr(EINVAL);
395 ifa = rt->rt_ifa;
396 flags = rt->rt_flags & ~RTF_CLONING;
397 gateway = rt->rt_gateway;
398 if ((netmask = rt->rt_genmask) == 0)
399 flags |= RTF_HOST;
400 goto makeroute;
401
402 case RTM_ADD:
403 if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
404 senderr(ENETUNREACH);
405 makeroute:
406 len = sizeof (*rt) + ROUNDUP(gateway->sa_len)
407 + ROUNDUP(dst->sa_len);
408 R_Malloc(rt, struct rtentry *, len);
409 if (rt == 0)
410 senderr(ENOBUFS);
411 Bzero(rt, len);
412 ndst = (struct sockaddr *)(rt + 1);
413 if (netmask) {
414 rt_maskedcopy(dst, ndst, netmask);
415 } else
416 Bcopy(dst, ndst, dst->sa_len);
417 rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask,
418 rnh->rnh_treetop, rt->rt_nodes);
419 if (rn == 0) {
420 free((caddr_t)rt, M_RTABLE);
421 senderr(EEXIST);
422 }
423 rt->rt_ifa = ifa;
424 rt->rt_ifp = ifa->ifa_ifp;
425 rt->rt_flags = RTF_UP | flags;
426 rt->rt_gateway = (struct sockaddr *)
427 (rn->rn_key + ROUNDUP(dst->sa_len));
428 Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
429 if (req == RTM_RESOLVE)
430 rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
431 if (ifa->ifa_rtrequest)
432 ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));
433 if (ret_nrt) {
434 *ret_nrt = rt;
435 rt->rt_refcnt++;
436 }
437 break;
438 }
439 bad:
440 splx(s);
441 return (error);
442 }
443
444 rt_maskedcopy(src, dst, netmask)
445 struct sockaddr *src, *dst, *netmask;
446 {
447 register u_char *cp1 = (u_char *)src;
448 register u_char *cp2 = (u_char *)dst;
449 register u_char *cp3 = (u_char *)netmask;
450 u_char *cplim = cp2 + *cp3;
451 u_char *cplim2 = cp2 + *cp1;
452
453 *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
454 cp3 += 2;
455 if (cplim > cplim2)
456 cplim = cplim2;
457 while (cp2 < cplim)
458 *cp2++ = *cp1++ & *cp3++;
459 if (cp2 < cplim2)
460 bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2));
461 }
462 /*
463 * Set up a routing table entry, normally
464 * for an interface.
465 */
466 rtinit(ifa, cmd, flags)
467 register struct ifaddr *ifa;
468 int cmd, flags;
469 {
470 register struct rtentry *rt;
471 register struct sockaddr *dst;
472 register struct sockaddr *deldst;
473 struct mbuf *m = 0;
474 int error;
475
476 dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
477 if (ifa->ifa_flags & IFA_ROUTE) {
478 if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) {
479 RTFREE(rt);
480 ifa->ifa_rt = 0;
481 }
482 }
483 if (cmd == RTM_DELETE) {
484 if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
485 m = m_get(M_WAIT, MT_SONAME);
486 deldst = mtod(m, struct sockaddr *);
487 rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
488 dst = deldst;
489 }
490 if (rt = rtalloc1(dst, 0)) {
491 rt->rt_refcnt--;
492 if (rt->rt_ifa != ifa) {
493 if (m)
494 (void) m_free(m);
495 return (flags & RTF_HOST ? EHOSTUNREACH
496 : ENETUNREACH);
497 }
498 }
499 }
500 error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
501 flags | ifa->ifa_flags, &ifa->ifa_rt);
502 if (m)
503 (void) m_free(m);
504 if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt)
505 && rt->rt_ifa != ifa) {
506 rt->rt_ifa = ifa;
507 rt->rt_ifp = ifa->ifa_ifp;
508 }
509 return (error);
510 }
511