1 | /* |
---|
2 | * Copyright (c) 1980, 1986, 1991, 1993 |
---|
3 | * The Regents of the University of California. 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 | * @(#)route.c 8.2 (Berkeley) 11/15/93 |
---|
34 | * $Id$ |
---|
35 | */ |
---|
36 | |
---|
37 | #include "opt_mrouting.h" |
---|
38 | |
---|
39 | #include <sys/param.h> |
---|
40 | #include <sys/queue.h> |
---|
41 | #include <sys/systm.h> |
---|
42 | #include <sys/kernel.h> |
---|
43 | #include <sys/proc.h> |
---|
44 | #include <sys/mbuf.h> |
---|
45 | #include <sys/socket.h> |
---|
46 | #include <sys/socketvar.h> |
---|
47 | #include <sys/domain.h> |
---|
48 | #include <sys/protosw.h> |
---|
49 | #include <sys/ioctl.h> |
---|
50 | |
---|
51 | #include <net/if.h> |
---|
52 | #include <net/route.h> |
---|
53 | #include <net/raw_cb.h> |
---|
54 | |
---|
55 | #include <netinet/in.h> |
---|
56 | #include <netinet/in_var.h> |
---|
57 | #include <netinet/ip_mroute.h> |
---|
58 | |
---|
59 | #define SA(p) ((struct sockaddr *)(p)) |
---|
60 | |
---|
61 | struct route_cb route_cb; |
---|
62 | static struct rtstat rtstat; |
---|
63 | struct radix_node_head *rt_tables[AF_MAX+1]; |
---|
64 | |
---|
65 | static int rttrash; /* routes not in table but not freed */ |
---|
66 | |
---|
67 | static void rt_maskedcopy __P((struct sockaddr *, |
---|
68 | struct sockaddr *, struct sockaddr *)); |
---|
69 | static void rtable_init __P((void **)); |
---|
70 | |
---|
71 | static void |
---|
72 | rtable_init(table) |
---|
73 | void **table; |
---|
74 | { |
---|
75 | struct domain *dom; |
---|
76 | for (dom = domains; dom; dom = dom->dom_next) |
---|
77 | if (dom->dom_rtattach) |
---|
78 | dom->dom_rtattach(&table[dom->dom_family], |
---|
79 | dom->dom_rtoffset); |
---|
80 | } |
---|
81 | |
---|
82 | void |
---|
83 | route_init() |
---|
84 | { |
---|
85 | rn_init(); /* initialize all zeroes, all ones, mask table */ |
---|
86 | rtable_init((void **)rt_tables); |
---|
87 | } |
---|
88 | |
---|
89 | /* |
---|
90 | * Packet routing routines. |
---|
91 | */ |
---|
92 | void |
---|
93 | rtalloc(ro) |
---|
94 | register struct route *ro; |
---|
95 | { |
---|
96 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) |
---|
97 | return; /* XXX */ |
---|
98 | ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); |
---|
99 | } |
---|
100 | |
---|
101 | void |
---|
102 | rtalloc_ign(ro, ignore) |
---|
103 | register struct route *ro; |
---|
104 | u_long ignore; |
---|
105 | { |
---|
106 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) |
---|
107 | return; /* XXX */ |
---|
108 | ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); |
---|
109 | } |
---|
110 | |
---|
111 | /* |
---|
112 | * Look up the route that matches the address given |
---|
113 | * Or, at least try.. Create a cloned route if needed. |
---|
114 | */ |
---|
115 | struct rtentry * |
---|
116 | rtalloc1(dst, report, ignflags) |
---|
117 | register struct sockaddr *dst; |
---|
118 | int report; |
---|
119 | u_long ignflags; |
---|
120 | { |
---|
121 | register struct radix_node_head *rnh = rt_tables[dst->sa_family]; |
---|
122 | register struct rtentry *rt; |
---|
123 | register struct radix_node *rn; |
---|
124 | struct rtentry *newrt = 0; |
---|
125 | struct rt_addrinfo info; |
---|
126 | u_long nflags; |
---|
127 | int s = splnet(), err = 0, msgtype = RTM_MISS; |
---|
128 | |
---|
129 | /* |
---|
130 | * Look up the address in the table for that Address Family |
---|
131 | */ |
---|
132 | if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && |
---|
133 | ((rn->rn_flags & RNF_ROOT) == 0)) { |
---|
134 | /* |
---|
135 | * If we find it and it's not the root node, then |
---|
136 | * get a refernce on the rtentry associated. |
---|
137 | */ |
---|
138 | newrt = rt = (struct rtentry *)rn; |
---|
139 | nflags = rt->rt_flags & ~ignflags; |
---|
140 | if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) { |
---|
141 | /* |
---|
142 | * We are apparently adding (report = 0 in delete). |
---|
143 | * If it requires that it be cloned, do so. |
---|
144 | * (This implies it wasn't a HOST route.) |
---|
145 | */ |
---|
146 | err = rtrequest(RTM_RESOLVE, dst, SA(0), |
---|
147 | SA(0), 0, &newrt); |
---|
148 | if (err) { |
---|
149 | /* |
---|
150 | * If the cloning didn't succeed, maybe |
---|
151 | * what we have will do. Return that. |
---|
152 | */ |
---|
153 | newrt = rt; |
---|
154 | rt->rt_refcnt++; |
---|
155 | goto miss; |
---|
156 | } |
---|
157 | if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { |
---|
158 | /* |
---|
159 | * If the new route specifies it be |
---|
160 | * externally resolved, then go do that. |
---|
161 | */ |
---|
162 | msgtype = RTM_RESOLVE; |
---|
163 | goto miss; |
---|
164 | } |
---|
165 | } else |
---|
166 | rt->rt_refcnt++; |
---|
167 | } else { |
---|
168 | /* |
---|
169 | * Either we hit the root or couldn't find any match, |
---|
170 | * Which basically means |
---|
171 | * "caint get there frm here" |
---|
172 | */ |
---|
173 | rtstat.rts_unreach++; |
---|
174 | miss: if (report) { |
---|
175 | /* |
---|
176 | * If required, report the failure to the supervising |
---|
177 | * Authorities. |
---|
178 | * For a delete, this is not an error. (report == 0) |
---|
179 | */ |
---|
180 | bzero((caddr_t)&info, sizeof(info)); |
---|
181 | info.rti_info[RTAX_DST] = dst; |
---|
182 | rt_missmsg(msgtype, &info, 0, err); |
---|
183 | } |
---|
184 | } |
---|
185 | splx(s); |
---|
186 | return (newrt); |
---|
187 | } |
---|
188 | |
---|
189 | void |
---|
190 | rtfree(rt) |
---|
191 | register struct rtentry *rt; |
---|
192 | { |
---|
193 | register struct radix_node_head *rnh = |
---|
194 | rt_tables[rt_key(rt)->sa_family]; |
---|
195 | register struct ifaddr *ifa; |
---|
196 | |
---|
197 | if (rt == 0 || rnh == 0) |
---|
198 | panic("rtfree"); |
---|
199 | rt->rt_refcnt--; |
---|
200 | if(rnh->rnh_close && rt->rt_refcnt == 0) { |
---|
201 | rnh->rnh_close((struct radix_node *)rt, rnh); |
---|
202 | } |
---|
203 | if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { |
---|
204 | if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
---|
205 | panic ("rtfree 2"); |
---|
206 | rttrash--; |
---|
207 | if (rt->rt_refcnt < 0) { |
---|
208 | printf("rtfree: %p not freed (neg refs)\n", rt); |
---|
209 | return; |
---|
210 | } |
---|
211 | ifa = rt->rt_ifa; |
---|
212 | IFAFREE(ifa); |
---|
213 | if (rt->rt_parent) { |
---|
214 | RTFREE(rt->rt_parent); |
---|
215 | } |
---|
216 | Free(rt_key(rt)); |
---|
217 | Free(rt); |
---|
218 | } |
---|
219 | } |
---|
220 | |
---|
221 | void |
---|
222 | ifafree(ifa) |
---|
223 | register struct ifaddr *ifa; |
---|
224 | { |
---|
225 | if (ifa == NULL) |
---|
226 | panic("ifafree"); |
---|
227 | if (ifa->ifa_refcnt == 0) |
---|
228 | free(ifa, M_IFADDR); |
---|
229 | else |
---|
230 | ifa->ifa_refcnt--; |
---|
231 | } |
---|
232 | |
---|
233 | /* |
---|
234 | * Force a routing table entry to the specified |
---|
235 | * destination to go through the given gateway. |
---|
236 | * Normally called as a result of a routing redirect |
---|
237 | * message from the network layer. |
---|
238 | * |
---|
239 | * N.B.: must be called at splnet |
---|
240 | * |
---|
241 | */ |
---|
242 | void |
---|
243 | rtredirect(dst, gateway, netmask, flags, src, rtp) |
---|
244 | struct sockaddr *dst, *gateway, *netmask, *src; |
---|
245 | int flags; |
---|
246 | struct rtentry **rtp; |
---|
247 | { |
---|
248 | register struct rtentry *rt; |
---|
249 | int error = 0; |
---|
250 | short *stat = 0; |
---|
251 | struct rt_addrinfo info; |
---|
252 | struct ifaddr *ifa; |
---|
253 | |
---|
254 | /* verify the gateway is directly reachable */ |
---|
255 | if ((ifa = ifa_ifwithnet(gateway)) == 0) { |
---|
256 | error = ENETUNREACH; |
---|
257 | goto out; |
---|
258 | } |
---|
259 | rt = rtalloc1(dst, 0, 0UL); |
---|
260 | /* |
---|
261 | * If the redirect isn't from our current router for this dst, |
---|
262 | * it's either old or wrong. If it redirects us to ourselves, |
---|
263 | * we have a routing loop, perhaps as a result of an interface |
---|
264 | * going down recently. |
---|
265 | */ |
---|
266 | #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) |
---|
267 | if (!(flags & RTF_DONE) && rt && |
---|
268 | (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) |
---|
269 | error = EINVAL; |
---|
270 | else if (ifa_ifwithaddr(gateway)) |
---|
271 | error = EHOSTUNREACH; |
---|
272 | if (error) |
---|
273 | goto done; |
---|
274 | /* |
---|
275 | * Create a new entry if we just got back a wildcard entry |
---|
276 | * or the the lookup failed. This is necessary for hosts |
---|
277 | * which use routing redirects generated by smart gateways |
---|
278 | * to dynamically build the routing tables. |
---|
279 | */ |
---|
280 | if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) |
---|
281 | goto create; |
---|
282 | /* |
---|
283 | * Don't listen to the redirect if it's |
---|
284 | * for a route to an interface. |
---|
285 | */ |
---|
286 | if (rt->rt_flags & RTF_GATEWAY) { |
---|
287 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
---|
288 | /* |
---|
289 | * Changing from route to net => route to host. |
---|
290 | * Create new route, rather than smashing route to net. |
---|
291 | */ |
---|
292 | create: |
---|
293 | flags |= RTF_GATEWAY | RTF_DYNAMIC; |
---|
294 | error = rtrequest((int)RTM_ADD, dst, gateway, |
---|
295 | netmask, flags, |
---|
296 | (struct rtentry **)0); |
---|
297 | stat = &rtstat.rts_dynamic; |
---|
298 | } else { |
---|
299 | /* |
---|
300 | * Smash the current notion of the gateway to |
---|
301 | * this destination. Should check about netmask!!! |
---|
302 | */ |
---|
303 | rt->rt_flags |= RTF_MODIFIED; |
---|
304 | flags |= RTF_MODIFIED; |
---|
305 | stat = &rtstat.rts_newgateway; |
---|
306 | rt_setgate(rt, rt_key(rt), gateway); |
---|
307 | } |
---|
308 | } else |
---|
309 | error = EHOSTUNREACH; |
---|
310 | done: |
---|
311 | if (rt) { |
---|
312 | if (rtp && !error) |
---|
313 | *rtp = rt; |
---|
314 | else |
---|
315 | rtfree(rt); |
---|
316 | } |
---|
317 | out: |
---|
318 | if (error) |
---|
319 | rtstat.rts_badredirect++; |
---|
320 | else if (stat != NULL) |
---|
321 | (*stat)++; |
---|
322 | bzero((caddr_t)&info, sizeof(info)); |
---|
323 | info.rti_info[RTAX_DST] = dst; |
---|
324 | info.rti_info[RTAX_GATEWAY] = gateway; |
---|
325 | info.rti_info[RTAX_NETMASK] = netmask; |
---|
326 | info.rti_info[RTAX_AUTHOR] = src; |
---|
327 | rt_missmsg(RTM_REDIRECT, &info, flags, error); |
---|
328 | } |
---|
329 | |
---|
330 | /* |
---|
331 | * Routing table ioctl interface. |
---|
332 | */ |
---|
333 | int |
---|
334 | rtioctl(req, data, p) |
---|
335 | int req; |
---|
336 | caddr_t data; |
---|
337 | struct proc *p; |
---|
338 | { |
---|
339 | #ifdef INET |
---|
340 | /* Multicast goop, grrr... */ |
---|
341 | #ifdef MROUTING |
---|
342 | return mrt_ioctl(req, data); |
---|
343 | #else |
---|
344 | return mrt_ioctl(req, data, p); |
---|
345 | #endif |
---|
346 | #else /* INET */ |
---|
347 | return ENXIO; |
---|
348 | #endif /* INET */ |
---|
349 | } |
---|
350 | |
---|
351 | struct ifaddr * |
---|
352 | ifa_ifwithroute(flags, dst, gateway) |
---|
353 | int flags; |
---|
354 | struct sockaddr *dst, *gateway; |
---|
355 | { |
---|
356 | register struct ifaddr *ifa; |
---|
357 | if ((flags & RTF_GATEWAY) == 0) { |
---|
358 | /* |
---|
359 | * If we are adding a route to an interface, |
---|
360 | * and the interface is a pt to pt link |
---|
361 | * we should search for the destination |
---|
362 | * as our clue to the interface. Otherwise |
---|
363 | * we can use the local address. |
---|
364 | */ |
---|
365 | ifa = 0; |
---|
366 | if (flags & RTF_HOST) { |
---|
367 | ifa = ifa_ifwithdstaddr(dst); |
---|
368 | } |
---|
369 | if (ifa == 0) |
---|
370 | ifa = ifa_ifwithaddr(gateway); |
---|
371 | } else { |
---|
372 | /* |
---|
373 | * If we are adding a route to a remote net |
---|
374 | * or host, the gateway may still be on the |
---|
375 | * other end of a pt to pt link. |
---|
376 | */ |
---|
377 | ifa = ifa_ifwithdstaddr(gateway); |
---|
378 | } |
---|
379 | if (ifa == 0) |
---|
380 | ifa = ifa_ifwithnet(gateway); |
---|
381 | if (ifa == 0) { |
---|
382 | struct rtentry *rt = rtalloc1(dst, 0, 0UL); |
---|
383 | if (rt == 0) |
---|
384 | return (0); |
---|
385 | rt->rt_refcnt--; |
---|
386 | if ((ifa = rt->rt_ifa) == 0) |
---|
387 | return (0); |
---|
388 | } |
---|
389 | if (ifa->ifa_addr->sa_family != dst->sa_family) { |
---|
390 | struct ifaddr *oifa = ifa; |
---|
391 | ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); |
---|
392 | if (ifa == 0) |
---|
393 | ifa = oifa; |
---|
394 | } |
---|
395 | return (ifa); |
---|
396 | } |
---|
397 | |
---|
398 | #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
---|
399 | |
---|
400 | static int rt_fixdelete(struct radix_node *, void *); |
---|
401 | static int rt_fixchange(struct radix_node *, void *); |
---|
402 | |
---|
403 | struct rtfc_arg { |
---|
404 | struct rtentry *rt0; |
---|
405 | struct radix_node_head *rnh; |
---|
406 | }; |
---|
407 | |
---|
408 | /* |
---|
409 | * Do appropriate manipulations of a routing tree given |
---|
410 | * all the bits of info needed |
---|
411 | */ |
---|
412 | int |
---|
413 | rtrequest(req, dst, gateway, netmask, flags, ret_nrt) |
---|
414 | int req, flags; |
---|
415 | struct sockaddr *dst, *gateway, *netmask; |
---|
416 | struct rtentry **ret_nrt; |
---|
417 | { |
---|
418 | int s = splnet(); int error = 0; |
---|
419 | register struct rtentry *rt; |
---|
420 | register struct radix_node *rn; |
---|
421 | register struct radix_node_head *rnh; |
---|
422 | struct ifaddr *ifa; |
---|
423 | struct sockaddr *ndst; |
---|
424 | #define senderr(x) { error = x ; goto bad; } |
---|
425 | |
---|
426 | /* |
---|
427 | * Find the correct routing tree to use for this Address Family |
---|
428 | */ |
---|
429 | if ((rnh = rt_tables[dst->sa_family]) == 0) |
---|
430 | senderr(ESRCH); |
---|
431 | /* |
---|
432 | * If we are adding a host route then we don't want to put |
---|
433 | * a netmask in the tree |
---|
434 | */ |
---|
435 | if (flags & RTF_HOST) |
---|
436 | netmask = 0; |
---|
437 | switch (req) { |
---|
438 | case RTM_DELETE: |
---|
439 | /* |
---|
440 | * Remove the item from the tree and return it. |
---|
441 | * Complain if it is not there and do no more processing. |
---|
442 | */ |
---|
443 | if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) |
---|
444 | senderr(ESRCH); |
---|
445 | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
---|
446 | panic ("rtrequest delete"); |
---|
447 | rt = (struct rtentry *)rn; |
---|
448 | |
---|
449 | /* |
---|
450 | * Now search what's left of the subtree for any cloned |
---|
451 | * routes which might have been formed from this node. |
---|
452 | */ |
---|
453 | if ((rt->rt_flags & RTF_PRCLONING) && netmask) { |
---|
454 | rnh->rnh_walktree_from(rnh, dst, netmask, |
---|
455 | rt_fixdelete, rt); |
---|
456 | } |
---|
457 | |
---|
458 | /* |
---|
459 | * Remove any external references we may have. |
---|
460 | * This might result in another rtentry being freed if |
---|
461 | * we held it's last reference. |
---|
462 | */ |
---|
463 | if (rt->rt_gwroute) { |
---|
464 | rt = rt->rt_gwroute; |
---|
465 | RTFREE(rt); |
---|
466 | (rt = (struct rtentry *)rn)->rt_gwroute = 0; |
---|
467 | } |
---|
468 | |
---|
469 | /* |
---|
470 | * NB: RTF_UP must be set during the search above, |
---|
471 | * because we might delete the last ref, causing |
---|
472 | * rt to get freed prematurely. |
---|
473 | */ |
---|
474 | rt->rt_flags &= ~RTF_UP; |
---|
475 | |
---|
476 | /* |
---|
477 | * If there is llinfo or similar associated with the |
---|
478 | * route, give the interface a chance to deal with it.. |
---|
479 | */ |
---|
480 | if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) |
---|
481 | ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); |
---|
482 | rttrash++; |
---|
483 | /* |
---|
484 | * If the caller wants it, then it can have it, but it's up to it |
---|
485 | * to free the rtentry as we won't be doing it. |
---|
486 | */ |
---|
487 | if (ret_nrt) |
---|
488 | *ret_nrt = rt; |
---|
489 | else if (rt->rt_refcnt <= 0) { |
---|
490 | rt->rt_refcnt++; /* make a 1->0 transition */ |
---|
491 | rtfree(rt); |
---|
492 | } |
---|
493 | break; |
---|
494 | |
---|
495 | case RTM_RESOLVE: |
---|
496 | if (ret_nrt == 0 || (rt = *ret_nrt) == 0) |
---|
497 | senderr(EINVAL); |
---|
498 | ifa = rt->rt_ifa; |
---|
499 | flags = rt->rt_flags & |
---|
500 | ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); |
---|
501 | flags |= RTF_WASCLONED; |
---|
502 | gateway = rt->rt_gateway; |
---|
503 | if ((netmask = rt->rt_genmask) == 0) |
---|
504 | flags |= RTF_HOST; |
---|
505 | goto makeroute; |
---|
506 | |
---|
507 | case RTM_ADD: |
---|
508 | if ((flags & RTF_GATEWAY) && !gateway) |
---|
509 | panic("rtrequest: GATEWAY but no gateway"); |
---|
510 | |
---|
511 | if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) |
---|
512 | senderr(ENETUNREACH); |
---|
513 | |
---|
514 | makeroute: |
---|
515 | R_Malloc(rt, struct rtentry *, sizeof(*rt)); |
---|
516 | if (rt == 0) |
---|
517 | senderr(ENOBUFS); |
---|
518 | Bzero(rt, sizeof(*rt)); |
---|
519 | rt->rt_flags = RTF_UP | flags; |
---|
520 | if ((error = rt_setgate(rt, dst, gateway))) { |
---|
521 | Free(rt); |
---|
522 | senderr(error); |
---|
523 | } |
---|
524 | ndst = rt_key(rt); |
---|
525 | if (netmask) { |
---|
526 | rt_maskedcopy(dst, ndst, netmask); |
---|
527 | } else |
---|
528 | Bcopy(dst, ndst, dst->sa_len); |
---|
529 | |
---|
530 | /* |
---|
531 | * This moved from below so that rnh->rnh_addaddr() can |
---|
532 | * examine the ifa and ifp if it so desires. |
---|
533 | */ |
---|
534 | ifa->ifa_refcnt++; |
---|
535 | rt->rt_ifa = ifa; |
---|
536 | rt->rt_ifp = ifa->ifa_ifp; |
---|
537 | |
---|
538 | rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, |
---|
539 | rnh, rt->rt_nodes); |
---|
540 | if (rn == 0) { |
---|
541 | struct rtentry *rt2; |
---|
542 | /* |
---|
543 | * Uh-oh, we already have one of these in the tree. |
---|
544 | * We do a special hack: if the route that's already |
---|
545 | * there was generated by the protocol-cloning |
---|
546 | * mechanism, then we just blow it away and retry |
---|
547 | * the insertion of the new one. |
---|
548 | */ |
---|
549 | rt2 = rtalloc1(dst, 0, RTF_PRCLONING); |
---|
550 | if (rt2 && rt2->rt_parent) { |
---|
551 | rtrequest(RTM_DELETE, |
---|
552 | (struct sockaddr *)rt_key(rt2), |
---|
553 | rt2->rt_gateway, |
---|
554 | rt_mask(rt2), rt2->rt_flags, 0); |
---|
555 | RTFREE(rt2); |
---|
556 | rn = rnh->rnh_addaddr((caddr_t)ndst, |
---|
557 | (caddr_t)netmask, |
---|
558 | rnh, rt->rt_nodes); |
---|
559 | } else if (rt2) { |
---|
560 | RTFREE(rt2); |
---|
561 | } |
---|
562 | } |
---|
563 | |
---|
564 | if (rn == 0) { |
---|
565 | if (rt->rt_gwroute) |
---|
566 | rtfree(rt->rt_gwroute); |
---|
567 | if (rt->rt_ifa) { |
---|
568 | IFAFREE(rt->rt_ifa); |
---|
569 | } |
---|
570 | Free(rt_key(rt)); |
---|
571 | Free(rt); |
---|
572 | senderr(EEXIST); |
---|
573 | } |
---|
574 | rt->rt_parent = 0; |
---|
575 | |
---|
576 | if (req == RTM_RESOLVE) { |
---|
577 | rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ |
---|
578 | if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { |
---|
579 | rt->rt_parent = (*ret_nrt); |
---|
580 | (*ret_nrt)->rt_refcnt++; |
---|
581 | } |
---|
582 | } |
---|
583 | if (ifa->ifa_rtrequest) |
---|
584 | ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); |
---|
585 | /* |
---|
586 | * We repeat the same procedure from rt_setgate() here because |
---|
587 | * it doesn't fire when we call it there because the node |
---|
588 | * hasn't been added to the tree yet. |
---|
589 | */ |
---|
590 | if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { |
---|
591 | struct rtfc_arg arg; |
---|
592 | arg.rnh = rnh; |
---|
593 | arg.rt0 = rt; |
---|
594 | rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), |
---|
595 | rt_fixchange, &arg); |
---|
596 | } |
---|
597 | |
---|
598 | if (ret_nrt) { |
---|
599 | *ret_nrt = rt; |
---|
600 | rt->rt_refcnt++; |
---|
601 | } |
---|
602 | break; |
---|
603 | } |
---|
604 | bad: |
---|
605 | splx(s); |
---|
606 | return (error); |
---|
607 | } |
---|
608 | |
---|
609 | /* |
---|
610 | * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' |
---|
611 | * (i.e., the routes related to it by the operation of cloning). This |
---|
612 | * routine is iterated over all potential former-child-routes by way of |
---|
613 | * rnh->rnh_walktree_from() above, and those that actually are children of |
---|
614 | * the late parent (passed in as VP here) are themselves deleted. |
---|
615 | */ |
---|
616 | static int |
---|
617 | rt_fixdelete(struct radix_node *rn, void *vp) |
---|
618 | { |
---|
619 | struct rtentry *rt = (struct rtentry *)rn; |
---|
620 | struct rtentry *rt0 = vp; |
---|
621 | |
---|
622 | if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { |
---|
623 | return rtrequest(RTM_DELETE, rt_key(rt), |
---|
624 | (struct sockaddr *)0, rt_mask(rt), |
---|
625 | rt->rt_flags, (struct rtentry **)0); |
---|
626 | } |
---|
627 | return 0; |
---|
628 | } |
---|
629 | |
---|
630 | /* |
---|
631 | * This routine is called from rt_setgate() to do the analogous thing for |
---|
632 | * adds and changes. There is the added complication in this case of a |
---|
633 | * middle insert; i.e., insertion of a new network route between an older |
---|
634 | * network route and (cloned) host routes. For this reason, a simple check |
---|
635 | * of rt->rt_parent is insufficient; each candidate route must be tested |
---|
636 | * against the (mask, value) of the new route (passed as before in vp) |
---|
637 | * to see if the new route matches it. Unfortunately, this has the obnoxious |
---|
638 | * property of also triggering for insertion /above/ a pre-existing network |
---|
639 | * route and clones. Sigh. This may be fixed some day. |
---|
640 | * |
---|
641 | * XXX - it may be possible to do fixdelete() for changes and reserve this |
---|
642 | * routine just for adds. I'm not sure why I thought it was necessary to do |
---|
643 | * changes this way. |
---|
644 | */ |
---|
645 | #ifdef DEBUG |
---|
646 | int rtfcdebug = 0; |
---|
647 | #endif |
---|
648 | |
---|
649 | static int |
---|
650 | rt_fixchange(struct radix_node *rn, void *vp) |
---|
651 | { |
---|
652 | struct rtentry *rt = (struct rtentry *)rn; |
---|
653 | struct rtfc_arg *ap = vp; |
---|
654 | struct rtentry *rt0 = ap->rt0; |
---|
655 | struct radix_node_head *rnh = ap->rnh; |
---|
656 | u_char *xk1, *xm1, *xk2; |
---|
657 | int i, len; |
---|
658 | |
---|
659 | #ifdef DEBUG |
---|
660 | if (rtfcdebug) |
---|
661 | printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); |
---|
662 | #endif |
---|
663 | |
---|
664 | if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { |
---|
665 | #ifdef DEBUG |
---|
666 | if(rtfcdebug) printf("no parent or pinned\n"); |
---|
667 | #endif |
---|
668 | return 0; |
---|
669 | } |
---|
670 | |
---|
671 | if (rt->rt_parent == rt0) { |
---|
672 | #ifdef DEBUG |
---|
673 | if(rtfcdebug) printf("parent match\n"); |
---|
674 | #endif |
---|
675 | return rtrequest(RTM_DELETE, rt_key(rt), |
---|
676 | (struct sockaddr *)0, rt_mask(rt), |
---|
677 | rt->rt_flags, (struct rtentry **)0); |
---|
678 | } |
---|
679 | |
---|
680 | /* |
---|
681 | * There probably is a function somewhere which does this... |
---|
682 | * if not, there should be. |
---|
683 | */ |
---|
684 | len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, |
---|
685 | ((struct sockaddr *)rt_key(rt))->sa_len); |
---|
686 | |
---|
687 | xk1 = (u_char *)rt_key(rt0); |
---|
688 | xm1 = (u_char *)rt_mask(rt0); |
---|
689 | xk2 = (u_char *)rt_key(rt); |
---|
690 | |
---|
691 | for (i = rnh->rnh_treetop->rn_off; i < len; i++) { |
---|
692 | if ((xk2[i] & xm1[i]) != xk1[i]) { |
---|
693 | #ifdef DEBUG |
---|
694 | if(rtfcdebug) printf("no match\n"); |
---|
695 | #endif |
---|
696 | return 0; |
---|
697 | } |
---|
698 | } |
---|
699 | |
---|
700 | /* |
---|
701 | * OK, this node is a clone, and matches the node currently being |
---|
702 | * changed/added under the node's mask. So, get rid of it. |
---|
703 | */ |
---|
704 | #ifdef DEBUG |
---|
705 | if(rtfcdebug) printf("deleting\n"); |
---|
706 | #endif |
---|
707 | return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, |
---|
708 | rt_mask(rt), rt->rt_flags, (struct rtentry **)0); |
---|
709 | } |
---|
710 | |
---|
711 | int |
---|
712 | rt_setgate(rt0, dst, gate) |
---|
713 | struct rtentry *rt0; |
---|
714 | struct sockaddr *dst, *gate; |
---|
715 | { |
---|
716 | caddr_t new, old; |
---|
717 | int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); |
---|
718 | register struct rtentry *rt = rt0; |
---|
719 | struct radix_node_head *rnh = rt_tables[dst->sa_family]; |
---|
720 | |
---|
721 | /* |
---|
722 | * A host route with the destination equal to the gateway |
---|
723 | * will interfere with keeping LLINFO in the routing |
---|
724 | * table, so disallow it. |
---|
725 | */ |
---|
726 | if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == |
---|
727 | (RTF_HOST|RTF_GATEWAY)) && |
---|
728 | (dst->sa_len == gate->sa_len) && |
---|
729 | (bcmp(dst, gate, dst->sa_len) == 0)) { |
---|
730 | /* |
---|
731 | * The route might already exist if this is an RTM_CHANGE |
---|
732 | * or a routing redirect, so try to delete it. |
---|
733 | */ |
---|
734 | if (rt_key(rt0)) |
---|
735 | rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), |
---|
736 | rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); |
---|
737 | return EADDRNOTAVAIL; |
---|
738 | } |
---|
739 | |
---|
740 | if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { |
---|
741 | old = (caddr_t)rt_key(rt); |
---|
742 | R_Malloc(new, caddr_t, dlen + glen); |
---|
743 | if (new == 0) |
---|
744 | return ENOBUFS; |
---|
745 | rt->rt_nodes->rn_key = new; |
---|
746 | } else { |
---|
747 | new = rt->rt_nodes->rn_key; |
---|
748 | old = 0; |
---|
749 | } |
---|
750 | Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); |
---|
751 | if (old) { |
---|
752 | Bcopy(dst, new, dlen); |
---|
753 | Free(old); |
---|
754 | } |
---|
755 | if (rt->rt_gwroute) { |
---|
756 | rt = rt->rt_gwroute; RTFREE(rt); |
---|
757 | rt = rt0; rt->rt_gwroute = 0; |
---|
758 | } |
---|
759 | /* |
---|
760 | * Cloning loop avoidance: |
---|
761 | * In the presence of protocol-cloning and bad configuration, |
---|
762 | * it is possible to get stuck in bottomless mutual recursion |
---|
763 | * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing |
---|
764 | * protocol-cloning to operate for gateways (which is probably the |
---|
765 | * correct choice anyway), and avoid the resulting reference loops |
---|
766 | * by disallowing any route to run through itself as a gateway. |
---|
767 | * This is obviuosly mandatory when we get rt->rt_output(). |
---|
768 | */ |
---|
769 | if (rt->rt_flags & RTF_GATEWAY) { |
---|
770 | rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); |
---|
771 | if (rt->rt_gwroute == rt) { |
---|
772 | RTFREE(rt->rt_gwroute); |
---|
773 | rt->rt_gwroute = 0; |
---|
774 | return EDQUOT; /* failure */ |
---|
775 | } |
---|
776 | } |
---|
777 | |
---|
778 | /* |
---|
779 | * This isn't going to do anything useful for host routes, so |
---|
780 | * don't bother. Also make sure we have a reasonable mask |
---|
781 | * (we don't yet have one during adds). |
---|
782 | */ |
---|
783 | if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { |
---|
784 | struct rtfc_arg arg; |
---|
785 | arg.rnh = rnh; |
---|
786 | arg.rt0 = rt; |
---|
787 | rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), |
---|
788 | rt_fixchange, &arg); |
---|
789 | } |
---|
790 | |
---|
791 | return 0; |
---|
792 | } |
---|
793 | |
---|
794 | static void |
---|
795 | rt_maskedcopy(src, dst, netmask) |
---|
796 | struct sockaddr *src, *dst, *netmask; |
---|
797 | { |
---|
798 | register u_char *cp1 = (u_char *)src; |
---|
799 | register u_char *cp2 = (u_char *)dst; |
---|
800 | register u_char *cp3 = (u_char *)netmask; |
---|
801 | u_char *cplim = cp2 + *cp3; |
---|
802 | u_char *cplim2 = cp2 + *cp1; |
---|
803 | |
---|
804 | *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ |
---|
805 | cp3 += 2; |
---|
806 | if (cplim > cplim2) |
---|
807 | cplim = cplim2; |
---|
808 | while (cp2 < cplim) |
---|
809 | *cp2++ = *cp1++ & *cp3++; |
---|
810 | if (cp2 < cplim2) |
---|
811 | bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); |
---|
812 | } |
---|
813 | |
---|
814 | /* |
---|
815 | * Set up a routing table entry, normally |
---|
816 | * for an interface. |
---|
817 | */ |
---|
818 | int |
---|
819 | rtinit(ifa, cmd, flags) |
---|
820 | register struct ifaddr *ifa; |
---|
821 | int cmd, flags; |
---|
822 | { |
---|
823 | register struct rtentry *rt; |
---|
824 | register struct sockaddr *dst; |
---|
825 | register struct sockaddr *deldst; |
---|
826 | struct mbuf *m = 0; |
---|
827 | struct rtentry *nrt = 0; |
---|
828 | int error; |
---|
829 | |
---|
830 | dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; |
---|
831 | /* |
---|
832 | * If it's a delete, check that if it exists, it's on the correct |
---|
833 | * interface or we might scrub a route to another ifa which would |
---|
834 | * be confusing at best and possibly worse. |
---|
835 | */ |
---|
836 | if (cmd == RTM_DELETE) { |
---|
837 | /* |
---|
838 | * It's a delete, so it should already exist.. |
---|
839 | * If it's a net, mask off the host bits |
---|
840 | * (Assuming we have a mask) |
---|
841 | */ |
---|
842 | if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { |
---|
843 | m = m_get(M_WAIT, MT_SONAME); |
---|
844 | deldst = mtod(m, struct sockaddr *); |
---|
845 | rt_maskedcopy(dst, deldst, ifa->ifa_netmask); |
---|
846 | dst = deldst; |
---|
847 | } |
---|
848 | /* |
---|
849 | * Get an rtentry that is in the routing tree and |
---|
850 | * contains the correct info. (if this fails we can't get there). |
---|
851 | * We set "report" to FALSE so that if it doesn't exist, |
---|
852 | * it doesn't report an error or clone a route, etc. etc. |
---|
853 | */ |
---|
854 | rt = rtalloc1(dst, 0, 0UL); |
---|
855 | if (rt) { |
---|
856 | /* |
---|
857 | * Ok so we found the rtentry. it has an extra reference |
---|
858 | * for us at this stage. we won't need that so |
---|
859 | * lop that off now. |
---|
860 | */ |
---|
861 | rt->rt_refcnt--; |
---|
862 | if (rt->rt_ifa != ifa) { |
---|
863 | /* |
---|
864 | * If the interface in the rtentry doesn't match |
---|
865 | * the interface we are using, then we don't |
---|
866 | * want to delete it, so return an error. |
---|
867 | * This seems to be the only point of |
---|
868 | * this whole RTM_DELETE clause. |
---|
869 | */ |
---|
870 | if (m) |
---|
871 | (void) m_free(m); |
---|
872 | return (flags & RTF_HOST ? EHOSTUNREACH |
---|
873 | : ENETUNREACH); |
---|
874 | } |
---|
875 | } |
---|
876 | /* XXX */ |
---|
877 | #if 0 |
---|
878 | else { |
---|
879 | /* |
---|
880 | * One would think that as we are deleting, and we know |
---|
881 | * it doesn't exist, we could just return at this point |
---|
882 | * with an "ELSE" clause, but apparently not.. |
---|
883 | */ |
---|
884 | return (flags & RTF_HOST ? EHOSTUNREACH |
---|
885 | : ENETUNREACH); |
---|
886 | } |
---|
887 | #endif |
---|
888 | } |
---|
889 | /* |
---|
890 | * Do the actual request |
---|
891 | */ |
---|
892 | error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, |
---|
893 | flags | ifa->ifa_flags, &nrt); |
---|
894 | if (m) |
---|
895 | (void) m_free(m); |
---|
896 | /* |
---|
897 | * If we are deleting, and we found an entry, then |
---|
898 | * it's been removed from the tree.. now throw it away. |
---|
899 | */ |
---|
900 | if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { |
---|
901 | /* |
---|
902 | * notify any listenning routing agents of the change |
---|
903 | */ |
---|
904 | rt_newaddrmsg(cmd, ifa, error, nrt); |
---|
905 | if (rt->rt_refcnt <= 0) { |
---|
906 | rt->rt_refcnt++; /* need a 1->0 transition to free */ |
---|
907 | rtfree(rt); |
---|
908 | } |
---|
909 | } |
---|
910 | |
---|
911 | /* |
---|
912 | * We are adding, and we have a returned routing entry. |
---|
913 | * We need to sanity check the result. |
---|
914 | */ |
---|
915 | if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { |
---|
916 | /* |
---|
917 | * We just wanted to add it.. we don't actually need a reference |
---|
918 | */ |
---|
919 | rt->rt_refcnt--; |
---|
920 | /* |
---|
921 | * If it came back with an unexpected interface, then it must |
---|
922 | * have already existed or something. (XXX) |
---|
923 | */ |
---|
924 | if (rt->rt_ifa != ifa) { |
---|
925 | printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, |
---|
926 | rt->rt_ifa); |
---|
927 | /* |
---|
928 | * Ask that the route we got back be removed |
---|
929 | * from the routing tables as we are trying |
---|
930 | * to supersede it. |
---|
931 | */ |
---|
932 | if (rt->rt_ifa->ifa_rtrequest) |
---|
933 | rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); |
---|
934 | /* |
---|
935 | * Remove the referenve to the it's ifaddr. |
---|
936 | */ |
---|
937 | IFAFREE(rt->rt_ifa); |
---|
938 | /* |
---|
939 | * And substitute in references to the ifaddr |
---|
940 | * we are adding. |
---|
941 | */ |
---|
942 | rt->rt_ifa = ifa; |
---|
943 | rt->rt_ifp = ifa->ifa_ifp; |
---|
944 | ifa->ifa_refcnt++; |
---|
945 | /* |
---|
946 | * Now add it to the routing table |
---|
947 | * XXX could we have just left it? |
---|
948 | * as it might have been in the right place.. |
---|
949 | */ |
---|
950 | if (ifa->ifa_rtrequest) |
---|
951 | ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); |
---|
952 | } |
---|
953 | /* |
---|
954 | * notify any listenning routing agents of the change |
---|
955 | */ |
---|
956 | rt_newaddrmsg(cmd, ifa, error, nrt); |
---|
957 | } |
---|
958 | return (error); |
---|
959 | } |
---|