1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (c) 1980, 1986, 1991, 1993 |
---|
5 | * The Regents of the University of California. All rights reserved. |
---|
6 | * |
---|
7 | * Redistribution and use in source and binary forms, with or without |
---|
8 | * modification, are permitted provided that the following conditions |
---|
9 | * are met: |
---|
10 | * 1. Redistributions of source code must retain the above copyright |
---|
11 | * notice, this list of conditions and the following disclaimer. |
---|
12 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
13 | * notice, this list of conditions and the following disclaimer in the |
---|
14 | * documentation and/or other materials provided with the distribution. |
---|
15 | * 3. Neither the name of the University nor the names of its contributors |
---|
16 | * may be used to endorse or promote products derived from this software |
---|
17 | * without specific prior written permission. |
---|
18 | * |
---|
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
---|
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
---|
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
29 | * SUCH DAMAGE. |
---|
30 | * |
---|
31 | * @(#)route.c 8.3.1.1 (Berkeley) 2/23/95 |
---|
32 | * $FreeBSD$ |
---|
33 | */ |
---|
34 | /************************************************************************ |
---|
35 | * Note: In this file a 'fib' is a "forwarding information base" * |
---|
36 | * Which is the new name for an in kernel routing (next hop) table. * |
---|
37 | ***********************************************************************/ |
---|
38 | |
---|
39 | #include <rtems/bsd/local/opt_inet.h> |
---|
40 | #include <rtems/bsd/local/opt_inet6.h> |
---|
41 | #include <rtems/bsd/local/opt_route.h> |
---|
42 | #include <rtems/bsd/local/opt_sctp.h> |
---|
43 | #include <rtems/bsd/local/opt_mrouting.h> |
---|
44 | #include <rtems/bsd/local/opt_mpath.h> |
---|
45 | |
---|
46 | #include <sys/param.h> |
---|
47 | #include <sys/systm.h> |
---|
48 | #include <sys/malloc.h> |
---|
49 | #include <sys/mbuf.h> |
---|
50 | #include <sys/socket.h> |
---|
51 | #include <sys/sysctl.h> |
---|
52 | #include <sys/syslog.h> |
---|
53 | #include <sys/sysproto.h> |
---|
54 | #include <sys/proc.h> |
---|
55 | #include <sys/domain.h> |
---|
56 | #include <sys/kernel.h> |
---|
57 | |
---|
58 | #include <net/if.h> |
---|
59 | #include <net/if_var.h> |
---|
60 | #include <net/if_dl.h> |
---|
61 | #include <net/route.h> |
---|
62 | #include <net/route_var.h> |
---|
63 | #include <net/vnet.h> |
---|
64 | #include <net/flowtable.h> |
---|
65 | |
---|
66 | #ifdef RADIX_MPATH |
---|
67 | #include <net/radix_mpath.h> |
---|
68 | #endif |
---|
69 | |
---|
70 | #include <netinet/in.h> |
---|
71 | #include <netinet/ip_mroute.h> |
---|
72 | |
---|
73 | #include <vm/uma.h> |
---|
74 | #ifdef __rtems__ |
---|
75 | #include <machine/rtems-bsd-syscall-api.h> |
---|
76 | #include <sys/file.h> |
---|
77 | #endif /* __rtems__ */ |
---|
78 | |
---|
79 | #define RT_MAXFIBS UINT16_MAX |
---|
80 | |
---|
81 | /* Kernel config default option. */ |
---|
82 | #ifdef ROUTETABLES |
---|
83 | #if ROUTETABLES <= 0 |
---|
84 | #error "ROUTETABLES defined too low" |
---|
85 | #endif |
---|
86 | #if ROUTETABLES > RT_MAXFIBS |
---|
87 | #error "ROUTETABLES defined too big" |
---|
88 | #endif |
---|
89 | #define RT_NUMFIBS ROUTETABLES |
---|
90 | #endif /* ROUTETABLES */ |
---|
91 | /* Initialize to default if not otherwise set. */ |
---|
92 | #ifndef RT_NUMFIBS |
---|
93 | #define RT_NUMFIBS 1 |
---|
94 | #endif |
---|
95 | |
---|
96 | #if defined(INET) || defined(INET6) |
---|
97 | #ifdef SCTP |
---|
98 | extern void sctp_addr_change(struct ifaddr *ifa, int cmd); |
---|
99 | #endif /* SCTP */ |
---|
100 | #endif |
---|
101 | |
---|
102 | |
---|
103 | /* This is read-only.. */ |
---|
104 | u_int rt_numfibs = RT_NUMFIBS; |
---|
105 | SYSCTL_UINT(_net, OID_AUTO, fibs, CTLFLAG_RDTUN, &rt_numfibs, 0, ""); |
---|
106 | |
---|
107 | /* |
---|
108 | * By default add routes to all fibs for new interfaces. |
---|
109 | * Once this is set to 0 then only allocate routes on interface |
---|
110 | * changes for the FIB of the caller when adding a new set of addresses |
---|
111 | * to an interface. XXX this is a shotgun aproach to a problem that needs |
---|
112 | * a more fine grained solution.. that will come. |
---|
113 | * XXX also has the problems getting the FIB from curthread which will not |
---|
114 | * always work given the fib can be overridden and prefixes can be added |
---|
115 | * from the network stack context. |
---|
116 | */ |
---|
117 | VNET_DEFINE(u_int, rt_add_addr_allfibs) = 1; |
---|
118 | SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, |
---|
119 | &VNET_NAME(rt_add_addr_allfibs), 0, ""); |
---|
120 | |
---|
121 | VNET_DEFINE(struct rtstat, rtstat); |
---|
122 | #define V_rtstat VNET(rtstat) |
---|
123 | |
---|
124 | VNET_DEFINE(struct rib_head *, rt_tables); |
---|
125 | #define V_rt_tables VNET(rt_tables) |
---|
126 | |
---|
127 | VNET_DEFINE(int, rttrash); /* routes not in table but not freed */ |
---|
128 | #define V_rttrash VNET(rttrash) |
---|
129 | |
---|
130 | |
---|
131 | /* |
---|
132 | * Convert a 'struct radix_node *' to a 'struct rtentry *'. |
---|
133 | * The operation can be done safely (in this code) because a |
---|
134 | * 'struct rtentry' starts with two 'struct radix_node''s, the first |
---|
135 | * one representing leaf nodes in the routing tree, which is |
---|
136 | * what the code in radix.c passes us as a 'struct radix_node'. |
---|
137 | * |
---|
138 | * But because there are a lot of assumptions in this conversion, |
---|
139 | * do not cast explicitly, but always use the macro below. |
---|
140 | */ |
---|
141 | #define RNTORT(p) ((struct rtentry *)(p)) |
---|
142 | |
---|
143 | static VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */ |
---|
144 | #define V_rtzone VNET(rtzone) |
---|
145 | |
---|
146 | static int rtrequest1_fib_change(struct rib_head *, struct rt_addrinfo *, |
---|
147 | struct rtentry **, u_int); |
---|
148 | static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *); |
---|
149 | static int rt_ifdelroute(const struct rtentry *rt, void *arg); |
---|
150 | static struct rtentry *rt_unlinkrte(struct rib_head *rnh, |
---|
151 | struct rt_addrinfo *info, int *perror); |
---|
152 | static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); |
---|
153 | #ifdef RADIX_MPATH |
---|
154 | static struct radix_node *rt_mpath_unlink(struct rib_head *rnh, |
---|
155 | struct rt_addrinfo *info, struct rtentry *rto, int *perror); |
---|
156 | #endif |
---|
157 | static int rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, |
---|
158 | int flags); |
---|
159 | |
---|
160 | struct if_mtuinfo |
---|
161 | { |
---|
162 | struct ifnet *ifp; |
---|
163 | int mtu; |
---|
164 | }; |
---|
165 | |
---|
166 | static int if_updatemtu_cb(struct radix_node *, void *); |
---|
167 | |
---|
168 | /* |
---|
169 | * handler for net.my_fibnum |
---|
170 | */ |
---|
171 | static int |
---|
172 | sysctl_my_fibnum(SYSCTL_HANDLER_ARGS) |
---|
173 | { |
---|
174 | int fibnum; |
---|
175 | int error; |
---|
176 | |
---|
177 | #ifndef __rtems__ |
---|
178 | fibnum = curthread->td_proc->p_fibnum; |
---|
179 | #else /* __rtems__ */ |
---|
180 | fibnum = BSD_DEFAULT_FIB; |
---|
181 | #endif /* __rtems__ */ |
---|
182 | error = sysctl_handle_int(oidp, &fibnum, 0, req); |
---|
183 | return (error); |
---|
184 | } |
---|
185 | |
---|
186 | SYSCTL_PROC(_net, OID_AUTO, my_fibnum, CTLTYPE_INT|CTLFLAG_RD, |
---|
187 | NULL, 0, &sysctl_my_fibnum, "I", "default FIB of caller"); |
---|
188 | |
---|
189 | static __inline struct rib_head ** |
---|
190 | rt_tables_get_rnh_ptr(int table, int fam) |
---|
191 | { |
---|
192 | struct rib_head **rnh; |
---|
193 | |
---|
194 | KASSERT(table >= 0 && table < rt_numfibs, ("%s: table out of bounds.", |
---|
195 | __func__)); |
---|
196 | KASSERT(fam >= 0 && fam < (AF_MAX+1), ("%s: fam out of bounds.", |
---|
197 | __func__)); |
---|
198 | |
---|
199 | /* rnh is [fib=0][af=0]. */ |
---|
200 | rnh = (struct rib_head **)V_rt_tables; |
---|
201 | /* Get the offset to the requested table and fam. */ |
---|
202 | rnh += table * (AF_MAX+1) + fam; |
---|
203 | |
---|
204 | return (rnh); |
---|
205 | } |
---|
206 | |
---|
207 | struct rib_head * |
---|
208 | rt_tables_get_rnh(int table, int fam) |
---|
209 | { |
---|
210 | |
---|
211 | return (*rt_tables_get_rnh_ptr(table, fam)); |
---|
212 | } |
---|
213 | |
---|
214 | u_int |
---|
215 | rt_tables_get_gen(int table, int fam) |
---|
216 | { |
---|
217 | struct rib_head *rnh; |
---|
218 | |
---|
219 | rnh = *rt_tables_get_rnh_ptr(table, fam); |
---|
220 | KASSERT(rnh != NULL, ("%s: NULL rib_head pointer table %d fam %d", |
---|
221 | __func__, table, fam)); |
---|
222 | return (rnh->rnh_gen); |
---|
223 | } |
---|
224 | |
---|
225 | |
---|
226 | /* |
---|
227 | * route initialization must occur before ip6_init2(), which happenas at |
---|
228 | * SI_ORDER_MIDDLE. |
---|
229 | */ |
---|
230 | static void |
---|
231 | route_init(void) |
---|
232 | { |
---|
233 | |
---|
234 | /* whack the tunable ints into line. */ |
---|
235 | if (rt_numfibs > RT_MAXFIBS) |
---|
236 | rt_numfibs = RT_MAXFIBS; |
---|
237 | if (rt_numfibs == 0) |
---|
238 | rt_numfibs = 1; |
---|
239 | } |
---|
240 | SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, 0); |
---|
241 | |
---|
242 | static int |
---|
243 | rtentry_zinit(void *mem, int size, int how) |
---|
244 | { |
---|
245 | struct rtentry *rt = mem; |
---|
246 | |
---|
247 | rt->rt_pksent = counter_u64_alloc(how); |
---|
248 | if (rt->rt_pksent == NULL) |
---|
249 | return (ENOMEM); |
---|
250 | |
---|
251 | RT_LOCK_INIT(rt); |
---|
252 | |
---|
253 | return (0); |
---|
254 | } |
---|
255 | |
---|
256 | static void |
---|
257 | rtentry_zfini(void *mem, int size) |
---|
258 | { |
---|
259 | struct rtentry *rt = mem; |
---|
260 | |
---|
261 | RT_LOCK_DESTROY(rt); |
---|
262 | counter_u64_free(rt->rt_pksent); |
---|
263 | } |
---|
264 | |
---|
265 | static int |
---|
266 | rtentry_ctor(void *mem, int size, void *arg, int how) |
---|
267 | { |
---|
268 | struct rtentry *rt = mem; |
---|
269 | |
---|
270 | bzero(rt, offsetof(struct rtentry, rt_endzero)); |
---|
271 | counter_u64_zero(rt->rt_pksent); |
---|
272 | rt->rt_chain = NULL; |
---|
273 | |
---|
274 | return (0); |
---|
275 | } |
---|
276 | |
---|
277 | static void |
---|
278 | rtentry_dtor(void *mem, int size, void *arg) |
---|
279 | { |
---|
280 | struct rtentry *rt = mem; |
---|
281 | |
---|
282 | RT_UNLOCK_COND(rt); |
---|
283 | } |
---|
284 | |
---|
285 | static void |
---|
286 | vnet_route_init(const void *unused __unused) |
---|
287 | { |
---|
288 | struct domain *dom; |
---|
289 | struct rib_head **rnh; |
---|
290 | int table; |
---|
291 | int fam; |
---|
292 | |
---|
293 | V_rt_tables = malloc(rt_numfibs * (AF_MAX+1) * |
---|
294 | sizeof(struct rib_head *), M_RTABLE, M_WAITOK|M_ZERO); |
---|
295 | |
---|
296 | V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), |
---|
297 | rtentry_ctor, rtentry_dtor, |
---|
298 | rtentry_zinit, rtentry_zfini, UMA_ALIGN_PTR, 0); |
---|
299 | for (dom = domains; dom; dom = dom->dom_next) { |
---|
300 | if (dom->dom_rtattach == NULL) |
---|
301 | continue; |
---|
302 | |
---|
303 | for (table = 0; table < rt_numfibs; table++) { |
---|
304 | fam = dom->dom_family; |
---|
305 | if (table != 0 && fam != AF_INET6 && fam != AF_INET) |
---|
306 | break; |
---|
307 | |
---|
308 | rnh = rt_tables_get_rnh_ptr(table, fam); |
---|
309 | if (rnh == NULL) |
---|
310 | panic("%s: rnh NULL", __func__); |
---|
311 | dom->dom_rtattach((void **)rnh, 0); |
---|
312 | } |
---|
313 | } |
---|
314 | } |
---|
315 | VNET_SYSINIT(vnet_route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, |
---|
316 | vnet_route_init, 0); |
---|
317 | |
---|
318 | #ifdef VIMAGE |
---|
319 | static void |
---|
320 | vnet_route_uninit(const void *unused __unused) |
---|
321 | { |
---|
322 | int table; |
---|
323 | int fam; |
---|
324 | struct domain *dom; |
---|
325 | struct rib_head **rnh; |
---|
326 | |
---|
327 | for (dom = domains; dom; dom = dom->dom_next) { |
---|
328 | if (dom->dom_rtdetach == NULL) |
---|
329 | continue; |
---|
330 | |
---|
331 | for (table = 0; table < rt_numfibs; table++) { |
---|
332 | fam = dom->dom_family; |
---|
333 | |
---|
334 | if (table != 0 && fam != AF_INET6 && fam != AF_INET) |
---|
335 | break; |
---|
336 | |
---|
337 | rnh = rt_tables_get_rnh_ptr(table, fam); |
---|
338 | if (rnh == NULL) |
---|
339 | panic("%s: rnh NULL", __func__); |
---|
340 | dom->dom_rtdetach((void **)rnh, 0); |
---|
341 | } |
---|
342 | } |
---|
343 | |
---|
344 | free(V_rt_tables, M_RTABLE); |
---|
345 | uma_zdestroy(V_rtzone); |
---|
346 | } |
---|
347 | VNET_SYSUNINIT(vnet_route_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, |
---|
348 | vnet_route_uninit, 0); |
---|
349 | #endif |
---|
350 | |
---|
351 | struct rib_head * |
---|
352 | rt_table_init(int offset) |
---|
353 | { |
---|
354 | struct rib_head *rh; |
---|
355 | |
---|
356 | rh = malloc(sizeof(struct rib_head), M_RTABLE, M_WAITOK | M_ZERO); |
---|
357 | |
---|
358 | /* TODO: These details should be hidded inside radix.c */ |
---|
359 | /* Init masks tree */ |
---|
360 | rn_inithead_internal(&rh->head, rh->rnh_nodes, offset); |
---|
361 | rn_inithead_internal(&rh->rmhead.head, rh->rmhead.mask_nodes, 0); |
---|
362 | rh->head.rnh_masks = &rh->rmhead; |
---|
363 | |
---|
364 | /* Init locks */ |
---|
365 | RIB_LOCK_INIT(rh); |
---|
366 | |
---|
367 | /* Finally, set base callbacks */ |
---|
368 | rh->rnh_addaddr = rn_addroute; |
---|
369 | rh->rnh_deladdr = rn_delete; |
---|
370 | rh->rnh_matchaddr = rn_match; |
---|
371 | rh->rnh_lookup = rn_lookup; |
---|
372 | rh->rnh_walktree = rn_walktree; |
---|
373 | rh->rnh_walktree_from = rn_walktree_from; |
---|
374 | |
---|
375 | return (rh); |
---|
376 | } |
---|
377 | |
---|
378 | static int |
---|
379 | rt_freeentry(struct radix_node *rn, void *arg) |
---|
380 | { |
---|
381 | struct radix_head * const rnh = arg; |
---|
382 | struct radix_node *x; |
---|
383 | |
---|
384 | x = (struct radix_node *)rn_delete(rn + 2, NULL, rnh); |
---|
385 | if (x != NULL) |
---|
386 | R_Free(x); |
---|
387 | return (0); |
---|
388 | } |
---|
389 | |
---|
390 | void |
---|
391 | rt_table_destroy(struct rib_head *rh) |
---|
392 | { |
---|
393 | |
---|
394 | rn_walktree(&rh->rmhead.head, rt_freeentry, &rh->rmhead.head); |
---|
395 | |
---|
396 | /* Assume table is already empty */ |
---|
397 | RIB_LOCK_DESTROY(rh); |
---|
398 | free(rh, M_RTABLE); |
---|
399 | } |
---|
400 | |
---|
401 | |
---|
402 | #ifndef _SYS_SYSPROTO_H_ |
---|
403 | struct setfib_args { |
---|
404 | int fibnum; |
---|
405 | }; |
---|
406 | #endif |
---|
407 | #ifdef __rtems__ |
---|
408 | static |
---|
409 | #endif /* __rtems__ */ |
---|
410 | int |
---|
411 | sys_setfib(struct thread *td, struct setfib_args *uap) |
---|
412 | { |
---|
413 | if (uap->fibnum < 0 || uap->fibnum >= rt_numfibs) |
---|
414 | return EINVAL; |
---|
415 | #ifndef __rtems__ |
---|
416 | td->td_proc->p_fibnum = uap->fibnum; |
---|
417 | #else /* __rtems__ */ |
---|
418 | if (uap->fibnum != BSD_DEFAULT_FIB) |
---|
419 | return EINVAL; |
---|
420 | #endif /* __rtems__ */ |
---|
421 | return (0); |
---|
422 | } |
---|
423 | #ifdef __rtems__ |
---|
424 | int |
---|
425 | setfib(int fibnum) |
---|
426 | { |
---|
427 | struct setfib_args ua = { |
---|
428 | .fibnum = fibnum |
---|
429 | }; |
---|
430 | int error; |
---|
431 | |
---|
432 | error = sys_setfib(NULL, &ua); |
---|
433 | |
---|
434 | return rtems_bsd_error_to_status_and_errno(error); |
---|
435 | } |
---|
436 | #endif /* __rtems__ */ |
---|
437 | |
---|
438 | /* |
---|
439 | * Packet routing routines. |
---|
440 | */ |
---|
441 | void |
---|
442 | rtalloc_ign_fib(struct route *ro, u_long ignore, u_int fibnum) |
---|
443 | { |
---|
444 | struct rtentry *rt; |
---|
445 | |
---|
446 | if ((rt = ro->ro_rt) != NULL) { |
---|
447 | if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) |
---|
448 | return; |
---|
449 | RTFREE(rt); |
---|
450 | ro->ro_rt = NULL; |
---|
451 | } |
---|
452 | ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, ignore, fibnum); |
---|
453 | if (ro->ro_rt) |
---|
454 | RT_UNLOCK(ro->ro_rt); |
---|
455 | } |
---|
456 | |
---|
457 | /* |
---|
458 | * Look up the route that matches the address given |
---|
459 | * Or, at least try.. Create a cloned route if needed. |
---|
460 | * |
---|
461 | * The returned route, if any, is locked. |
---|
462 | */ |
---|
463 | struct rtentry * |
---|
464 | rtalloc1(struct sockaddr *dst, int report, u_long ignflags) |
---|
465 | { |
---|
466 | |
---|
467 | return (rtalloc1_fib(dst, report, ignflags, RT_DEFAULT_FIB)); |
---|
468 | } |
---|
469 | |
---|
470 | struct rtentry * |
---|
471 | rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, |
---|
472 | u_int fibnum) |
---|
473 | { |
---|
474 | struct rib_head *rh; |
---|
475 | struct radix_node *rn; |
---|
476 | struct rtentry *newrt; |
---|
477 | struct rt_addrinfo info; |
---|
478 | int err = 0, msgtype = RTM_MISS; |
---|
479 | |
---|
480 | KASSERT((fibnum < rt_numfibs), ("rtalloc1_fib: bad fibnum")); |
---|
481 | rh = rt_tables_get_rnh(fibnum, dst->sa_family); |
---|
482 | newrt = NULL; |
---|
483 | if (rh == NULL) |
---|
484 | goto miss; |
---|
485 | |
---|
486 | /* |
---|
487 | * Look up the address in the table for that Address Family |
---|
488 | */ |
---|
489 | RIB_RLOCK(rh); |
---|
490 | rn = rh->rnh_matchaddr(dst, &rh->head); |
---|
491 | if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { |
---|
492 | newrt = RNTORT(rn); |
---|
493 | RT_LOCK(newrt); |
---|
494 | RT_ADDREF(newrt); |
---|
495 | RIB_RUNLOCK(rh); |
---|
496 | return (newrt); |
---|
497 | |
---|
498 | } else |
---|
499 | RIB_RUNLOCK(rh); |
---|
500 | |
---|
501 | /* |
---|
502 | * Either we hit the root or could not find any match, |
---|
503 | * which basically means: "cannot get there from here". |
---|
504 | */ |
---|
505 | miss: |
---|
506 | V_rtstat.rts_unreach++; |
---|
507 | |
---|
508 | if (report) { |
---|
509 | /* |
---|
510 | * If required, report the failure to the supervising |
---|
511 | * Authorities. |
---|
512 | * For a delete, this is not an error. (report == 0) |
---|
513 | */ |
---|
514 | bzero(&info, sizeof(info)); |
---|
515 | info.rti_info[RTAX_DST] = dst; |
---|
516 | rt_missmsg_fib(msgtype, &info, 0, err, fibnum); |
---|
517 | } |
---|
518 | return (newrt); |
---|
519 | } |
---|
520 | |
---|
521 | /* |
---|
522 | * Remove a reference count from an rtentry. |
---|
523 | * If the count gets low enough, take it out of the routing table |
---|
524 | */ |
---|
525 | void |
---|
526 | rtfree(struct rtentry *rt) |
---|
527 | { |
---|
528 | struct rib_head *rnh; |
---|
529 | |
---|
530 | KASSERT(rt != NULL,("%s: NULL rt", __func__)); |
---|
531 | rnh = rt_tables_get_rnh(rt->rt_fibnum, rt_key(rt)->sa_family); |
---|
532 | KASSERT(rnh != NULL,("%s: NULL rnh", __func__)); |
---|
533 | |
---|
534 | RT_LOCK_ASSERT(rt); |
---|
535 | |
---|
536 | /* |
---|
537 | * The callers should use RTFREE_LOCKED() or RTFREE(), so |
---|
538 | * we should come here exactly with the last reference. |
---|
539 | */ |
---|
540 | RT_REMREF(rt); |
---|
541 | if (rt->rt_refcnt > 0) { |
---|
542 | log(LOG_DEBUG, "%s: %p has %d refs\n", __func__, rt, rt->rt_refcnt); |
---|
543 | goto done; |
---|
544 | } |
---|
545 | |
---|
546 | /* |
---|
547 | * On last reference give the "close method" a chance |
---|
548 | * to cleanup private state. This also permits (for |
---|
549 | * IPv4 and IPv6) a chance to decide if the routing table |
---|
550 | * entry should be purged immediately or at a later time. |
---|
551 | * When an immediate purge is to happen the close routine |
---|
552 | * typically calls rtexpunge which clears the RTF_UP flag |
---|
553 | * on the entry so that the code below reclaims the storage. |
---|
554 | */ |
---|
555 | if (rt->rt_refcnt == 0 && rnh->rnh_close) |
---|
556 | rnh->rnh_close((struct radix_node *)rt, &rnh->head); |
---|
557 | |
---|
558 | /* |
---|
559 | * If we are no longer "up" (and ref == 0) |
---|
560 | * then we can free the resources associated |
---|
561 | * with the route. |
---|
562 | */ |
---|
563 | if ((rt->rt_flags & RTF_UP) == 0) { |
---|
564 | if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
---|
565 | panic("rtfree 2"); |
---|
566 | /* |
---|
567 | * the rtentry must have been removed from the routing table |
---|
568 | * so it is represented in rttrash.. remove that now. |
---|
569 | */ |
---|
570 | V_rttrash--; |
---|
571 | #ifdef DIAGNOSTIC |
---|
572 | if (rt->rt_refcnt < 0) { |
---|
573 | printf("rtfree: %p not freed (neg refs)\n", rt); |
---|
574 | goto done; |
---|
575 | } |
---|
576 | #endif |
---|
577 | /* |
---|
578 | * release references on items we hold them on.. |
---|
579 | * e.g other routes and ifaddrs. |
---|
580 | */ |
---|
581 | if (rt->rt_ifa) |
---|
582 | ifa_free(rt->rt_ifa); |
---|
583 | /* |
---|
584 | * The key is separatly alloc'd so free it (see rt_setgate()). |
---|
585 | * This also frees the gateway, as they are always malloc'd |
---|
586 | * together. |
---|
587 | */ |
---|
588 | R_Free(rt_key(rt)); |
---|
589 | |
---|
590 | /* |
---|
591 | * and the rtentry itself of course |
---|
592 | */ |
---|
593 | uma_zfree(V_rtzone, rt); |
---|
594 | return; |
---|
595 | } |
---|
596 | done: |
---|
597 | RT_UNLOCK(rt); |
---|
598 | } |
---|
599 | |
---|
600 | |
---|
601 | /* |
---|
602 | * Force a routing table entry to the specified |
---|
603 | * destination to go through the given gateway. |
---|
604 | * Normally called as a result of a routing redirect |
---|
605 | * message from the network layer. |
---|
606 | */ |
---|
607 | void |
---|
608 | rtredirect_fib(struct sockaddr *dst, |
---|
609 | struct sockaddr *gateway, |
---|
610 | struct sockaddr *netmask, |
---|
611 | int flags, |
---|
612 | struct sockaddr *src, |
---|
613 | u_int fibnum) |
---|
614 | { |
---|
615 | struct rtentry *rt; |
---|
616 | int error = 0; |
---|
617 | short *stat = NULL; |
---|
618 | struct rt_addrinfo info; |
---|
619 | struct ifaddr *ifa; |
---|
620 | struct rib_head *rnh; |
---|
621 | |
---|
622 | ifa = NULL; |
---|
623 | rnh = rt_tables_get_rnh(fibnum, dst->sa_family); |
---|
624 | if (rnh == NULL) { |
---|
625 | error = EAFNOSUPPORT; |
---|
626 | goto out; |
---|
627 | } |
---|
628 | |
---|
629 | /* verify the gateway is directly reachable */ |
---|
630 | if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) { |
---|
631 | error = ENETUNREACH; |
---|
632 | goto out; |
---|
633 | } |
---|
634 | rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */ |
---|
635 | /* |
---|
636 | * If the redirect isn't from our current router for this dst, |
---|
637 | * it's either old or wrong. If it redirects us to ourselves, |
---|
638 | * we have a routing loop, perhaps as a result of an interface |
---|
639 | * going down recently. |
---|
640 | */ |
---|
641 | if (!(flags & RTF_DONE) && rt) { |
---|
642 | if (!sa_equal(src, rt->rt_gateway)) { |
---|
643 | error = EINVAL; |
---|
644 | goto done; |
---|
645 | } |
---|
646 | if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) { |
---|
647 | error = EINVAL; |
---|
648 | goto done; |
---|
649 | } |
---|
650 | } |
---|
651 | if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) { |
---|
652 | error = EHOSTUNREACH; |
---|
653 | goto done; |
---|
654 | } |
---|
655 | /* |
---|
656 | * Create a new entry if we just got back a wildcard entry |
---|
657 | * or the lookup failed. This is necessary for hosts |
---|
658 | * which use routing redirects generated by smart gateways |
---|
659 | * to dynamically build the routing tables. |
---|
660 | */ |
---|
661 | if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) |
---|
662 | goto create; |
---|
663 | /* |
---|
664 | * Don't listen to the redirect if it's |
---|
665 | * for a route to an interface. |
---|
666 | */ |
---|
667 | if (rt->rt_flags & RTF_GATEWAY) { |
---|
668 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
---|
669 | /* |
---|
670 | * Changing from route to net => route to host. |
---|
671 | * Create new route, rather than smashing route to net. |
---|
672 | */ |
---|
673 | create: |
---|
674 | if (rt != NULL) |
---|
675 | RTFREE_LOCKED(rt); |
---|
676 | |
---|
677 | flags |= RTF_DYNAMIC; |
---|
678 | bzero((caddr_t)&info, sizeof(info)); |
---|
679 | info.rti_info[RTAX_DST] = dst; |
---|
680 | info.rti_info[RTAX_GATEWAY] = gateway; |
---|
681 | info.rti_info[RTAX_NETMASK] = netmask; |
---|
682 | info.rti_ifa = ifa; |
---|
683 | info.rti_flags = flags; |
---|
684 | error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); |
---|
685 | if (rt != NULL) { |
---|
686 | RT_LOCK(rt); |
---|
687 | flags = rt->rt_flags; |
---|
688 | } |
---|
689 | |
---|
690 | stat = &V_rtstat.rts_dynamic; |
---|
691 | } else { |
---|
692 | |
---|
693 | /* |
---|
694 | * Smash the current notion of the gateway to |
---|
695 | * this destination. Should check about netmask!!! |
---|
696 | */ |
---|
697 | if ((flags & RTF_GATEWAY) == 0) |
---|
698 | rt->rt_flags &= ~RTF_GATEWAY; |
---|
699 | rt->rt_flags |= RTF_MODIFIED; |
---|
700 | flags |= RTF_MODIFIED; |
---|
701 | stat = &V_rtstat.rts_newgateway; |
---|
702 | /* |
---|
703 | * add the key and gateway (in one malloc'd chunk). |
---|
704 | */ |
---|
705 | RT_UNLOCK(rt); |
---|
706 | RIB_WLOCK(rnh); |
---|
707 | RT_LOCK(rt); |
---|
708 | rt_setgate(rt, rt_key(rt), gateway); |
---|
709 | RIB_WUNLOCK(rnh); |
---|
710 | } |
---|
711 | } else |
---|
712 | error = EHOSTUNREACH; |
---|
713 | done: |
---|
714 | if (rt) |
---|
715 | RTFREE_LOCKED(rt); |
---|
716 | out: |
---|
717 | if (error) |
---|
718 | V_rtstat.rts_badredirect++; |
---|
719 | else if (stat != NULL) |
---|
720 | (*stat)++; |
---|
721 | bzero((caddr_t)&info, sizeof(info)); |
---|
722 | info.rti_info[RTAX_DST] = dst; |
---|
723 | info.rti_info[RTAX_GATEWAY] = gateway; |
---|
724 | info.rti_info[RTAX_NETMASK] = netmask; |
---|
725 | info.rti_info[RTAX_AUTHOR] = src; |
---|
726 | rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum); |
---|
727 | if (ifa != NULL) |
---|
728 | ifa_free(ifa); |
---|
729 | } |
---|
730 | |
---|
731 | /* |
---|
732 | * Routing table ioctl interface. |
---|
733 | */ |
---|
734 | int |
---|
735 | rtioctl_fib(u_long req, caddr_t data, u_int fibnum) |
---|
736 | { |
---|
737 | |
---|
738 | /* |
---|
739 | * If more ioctl commands are added here, make sure the proper |
---|
740 | * super-user checks are being performed because it is possible for |
---|
741 | * prison-root to make it this far if raw sockets have been enabled |
---|
742 | * in jails. |
---|
743 | */ |
---|
744 | #ifdef INET |
---|
745 | /* Multicast goop, grrr... */ |
---|
746 | return mrt_ioctl ? mrt_ioctl(req, data, fibnum) : EOPNOTSUPP; |
---|
747 | #else /* INET */ |
---|
748 | return ENXIO; |
---|
749 | #endif /* INET */ |
---|
750 | } |
---|
751 | |
---|
752 | struct ifaddr * |
---|
753 | ifa_ifwithroute(int flags, const struct sockaddr *dst, struct sockaddr *gateway, |
---|
754 | u_int fibnum) |
---|
755 | { |
---|
756 | struct ifaddr *ifa; |
---|
757 | int not_found = 0; |
---|
758 | |
---|
759 | if ((flags & RTF_GATEWAY) == 0) { |
---|
760 | /* |
---|
761 | * If we are adding a route to an interface, |
---|
762 | * and the interface is a pt to pt link |
---|
763 | * we should search for the destination |
---|
764 | * as our clue to the interface. Otherwise |
---|
765 | * we can use the local address. |
---|
766 | */ |
---|
767 | ifa = NULL; |
---|
768 | if (flags & RTF_HOST) |
---|
769 | ifa = ifa_ifwithdstaddr(dst, fibnum); |
---|
770 | if (ifa == NULL) |
---|
771 | ifa = ifa_ifwithaddr(gateway); |
---|
772 | } else { |
---|
773 | /* |
---|
774 | * If we are adding a route to a remote net |
---|
775 | * or host, the gateway may still be on the |
---|
776 | * other end of a pt to pt link. |
---|
777 | */ |
---|
778 | ifa = ifa_ifwithdstaddr(gateway, fibnum); |
---|
779 | } |
---|
780 | if (ifa == NULL) |
---|
781 | ifa = ifa_ifwithnet(gateway, 0, fibnum); |
---|
782 | if (ifa == NULL) { |
---|
783 | struct rtentry *rt = rtalloc1_fib(gateway, 0, 0, fibnum); |
---|
784 | if (rt == NULL) |
---|
785 | return (NULL); |
---|
786 | /* |
---|
787 | * dismiss a gateway that is reachable only |
---|
788 | * through the default router |
---|
789 | */ |
---|
790 | switch (gateway->sa_family) { |
---|
791 | case AF_INET: |
---|
792 | if (satosin(rt_key(rt))->sin_addr.s_addr == INADDR_ANY) |
---|
793 | not_found = 1; |
---|
794 | break; |
---|
795 | case AF_INET6: |
---|
796 | if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(rt))->sin6_addr)) |
---|
797 | not_found = 1; |
---|
798 | break; |
---|
799 | default: |
---|
800 | break; |
---|
801 | } |
---|
802 | if (!not_found && rt->rt_ifa != NULL) { |
---|
803 | ifa = rt->rt_ifa; |
---|
804 | ifa_ref(ifa); |
---|
805 | } |
---|
806 | RT_REMREF(rt); |
---|
807 | RT_UNLOCK(rt); |
---|
808 | if (not_found || ifa == NULL) |
---|
809 | return (NULL); |
---|
810 | } |
---|
811 | if (ifa->ifa_addr->sa_family != dst->sa_family) { |
---|
812 | struct ifaddr *oifa = ifa; |
---|
813 | ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); |
---|
814 | if (ifa == NULL) |
---|
815 | ifa = oifa; |
---|
816 | else |
---|
817 | ifa_free(oifa); |
---|
818 | } |
---|
819 | return (ifa); |
---|
820 | } |
---|
821 | |
---|
822 | /* |
---|
823 | * Do appropriate manipulations of a routing tree given |
---|
824 | * all the bits of info needed |
---|
825 | */ |
---|
826 | int |
---|
827 | rtrequest_fib(int req, |
---|
828 | struct sockaddr *dst, |
---|
829 | struct sockaddr *gateway, |
---|
830 | struct sockaddr *netmask, |
---|
831 | int flags, |
---|
832 | struct rtentry **ret_nrt, |
---|
833 | u_int fibnum) |
---|
834 | { |
---|
835 | struct rt_addrinfo info; |
---|
836 | |
---|
837 | if (dst->sa_len == 0) |
---|
838 | return(EINVAL); |
---|
839 | |
---|
840 | bzero((caddr_t)&info, sizeof(info)); |
---|
841 | info.rti_flags = flags; |
---|
842 | info.rti_info[RTAX_DST] = dst; |
---|
843 | info.rti_info[RTAX_GATEWAY] = gateway; |
---|
844 | info.rti_info[RTAX_NETMASK] = netmask; |
---|
845 | return rtrequest1_fib(req, &info, ret_nrt, fibnum); |
---|
846 | } |
---|
847 | |
---|
848 | |
---|
849 | /* |
---|
850 | * Copy most of @rt data into @info. |
---|
851 | * |
---|
852 | * If @flags contains NHR_COPY, copies dst,netmask and gw to the |
---|
853 | * pointers specified by @info structure. Assume such pointers |
---|
854 | * are zeroed sockaddr-like structures with sa_len field initialized |
---|
855 | * to reflect size of the provided buffer. if no NHR_COPY is specified, |
---|
856 | * point dst,netmask and gw @info fields to appropriate @rt values. |
---|
857 | * |
---|
858 | * if @flags contains NHR_REF, do refcouting on rt_ifp. |
---|
859 | * |
---|
860 | * Returns 0 on success. |
---|
861 | */ |
---|
862 | int |
---|
863 | rt_exportinfo(struct rtentry *rt, struct rt_addrinfo *info, int flags) |
---|
864 | { |
---|
865 | struct rt_metrics *rmx; |
---|
866 | struct sockaddr *src, *dst; |
---|
867 | int sa_len; |
---|
868 | |
---|
869 | if (flags & NHR_COPY) { |
---|
870 | /* Copy destination if dst is non-zero */ |
---|
871 | src = rt_key(rt); |
---|
872 | dst = info->rti_info[RTAX_DST]; |
---|
873 | sa_len = src->sa_len; |
---|
874 | if (dst != NULL) { |
---|
875 | if (src->sa_len > dst->sa_len) |
---|
876 | return (ENOMEM); |
---|
877 | memcpy(dst, src, src->sa_len); |
---|
878 | info->rti_addrs |= RTA_DST; |
---|
879 | } |
---|
880 | |
---|
881 | /* Copy mask if set && dst is non-zero */ |
---|
882 | src = rt_mask(rt); |
---|
883 | dst = info->rti_info[RTAX_NETMASK]; |
---|
884 | if (src != NULL && dst != NULL) { |
---|
885 | |
---|
886 | /* |
---|
887 | * Radix stores different value in sa_len, |
---|
888 | * assume rt_mask() to have the same length |
---|
889 | * as rt_key() |
---|
890 | */ |
---|
891 | if (sa_len > dst->sa_len) |
---|
892 | return (ENOMEM); |
---|
893 | memcpy(dst, src, src->sa_len); |
---|
894 | info->rti_addrs |= RTA_NETMASK; |
---|
895 | } |
---|
896 | |
---|
897 | /* Copy gateway is set && dst is non-zero */ |
---|
898 | src = rt->rt_gateway; |
---|
899 | dst = info->rti_info[RTAX_GATEWAY]; |
---|
900 | if ((rt->rt_flags & RTF_GATEWAY) && src != NULL && dst != NULL){ |
---|
901 | if (src->sa_len > dst->sa_len) |
---|
902 | return (ENOMEM); |
---|
903 | memcpy(dst, src, src->sa_len); |
---|
904 | info->rti_addrs |= RTA_GATEWAY; |
---|
905 | } |
---|
906 | } else { |
---|
907 | info->rti_info[RTAX_DST] = rt_key(rt); |
---|
908 | info->rti_addrs |= RTA_DST; |
---|
909 | if (rt_mask(rt) != NULL) { |
---|
910 | info->rti_info[RTAX_NETMASK] = rt_mask(rt); |
---|
911 | info->rti_addrs |= RTA_NETMASK; |
---|
912 | } |
---|
913 | if (rt->rt_flags & RTF_GATEWAY) { |
---|
914 | info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; |
---|
915 | info->rti_addrs |= RTA_GATEWAY; |
---|
916 | } |
---|
917 | } |
---|
918 | |
---|
919 | rmx = info->rti_rmx; |
---|
920 | if (rmx != NULL) { |
---|
921 | info->rti_mflags |= RTV_MTU; |
---|
922 | rmx->rmx_mtu = rt->rt_mtu; |
---|
923 | } |
---|
924 | |
---|
925 | info->rti_flags = rt->rt_flags; |
---|
926 | info->rti_ifp = rt->rt_ifp; |
---|
927 | info->rti_ifa = rt->rt_ifa; |
---|
928 | |
---|
929 | if (flags & NHR_REF) { |
---|
930 | /* Do 'traditional' refcouting */ |
---|
931 | if_ref(info->rti_ifp); |
---|
932 | } |
---|
933 | |
---|
934 | return (0); |
---|
935 | } |
---|
936 | |
---|
937 | /* |
---|
938 | * Lookups up route entry for @dst in RIB database for fib @fibnum. |
---|
939 | * Exports entry data to @info using rt_exportinfo(). |
---|
940 | * |
---|
941 | * if @flags contains NHR_REF, refcouting is performed on rt_ifp. |
---|
942 | * All references can be released later by calling rib_free_info() |
---|
943 | * |
---|
944 | * Returns 0 on success. |
---|
945 | * Returns ENOENT for lookup failure, ENOMEM for export failure. |
---|
946 | */ |
---|
947 | int |
---|
948 | rib_lookup_info(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags, |
---|
949 | uint32_t flowid, struct rt_addrinfo *info) |
---|
950 | { |
---|
951 | struct rib_head *rh; |
---|
952 | struct radix_node *rn; |
---|
953 | struct rtentry *rt; |
---|
954 | int error; |
---|
955 | |
---|
956 | KASSERT((fibnum < rt_numfibs), ("rib_lookup_rte: bad fibnum")); |
---|
957 | rh = rt_tables_get_rnh(fibnum, dst->sa_family); |
---|
958 | if (rh == NULL) |
---|
959 | return (ENOENT); |
---|
960 | |
---|
961 | RIB_RLOCK(rh); |
---|
962 | rn = rh->rnh_matchaddr(__DECONST(void *, dst), &rh->head); |
---|
963 | if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { |
---|
964 | rt = RNTORT(rn); |
---|
965 | /* Ensure route & ifp is UP */ |
---|
966 | if (RT_LINK_IS_UP(rt->rt_ifp)) { |
---|
967 | flags = (flags & NHR_REF) | NHR_COPY; |
---|
968 | error = rt_exportinfo(rt, info, flags); |
---|
969 | RIB_RUNLOCK(rh); |
---|
970 | |
---|
971 | return (error); |
---|
972 | } |
---|
973 | } |
---|
974 | RIB_RUNLOCK(rh); |
---|
975 | |
---|
976 | return (ENOENT); |
---|
977 | } |
---|
978 | |
---|
979 | /* |
---|
980 | * Releases all references acquired by rib_lookup_info() when |
---|
981 | * called with NHR_REF flags. |
---|
982 | */ |
---|
983 | void |
---|
984 | rib_free_info(struct rt_addrinfo *info) |
---|
985 | { |
---|
986 | |
---|
987 | if_rele(info->rti_ifp); |
---|
988 | } |
---|
989 | |
---|
990 | /* |
---|
991 | * Iterates over all existing fibs in system calling |
---|
992 | * @setwa_f function prior to traversing each fib. |
---|
993 | * Calls @wa_f function for each element in current fib. |
---|
994 | * If af is not AF_UNSPEC, iterates over fibs in particular |
---|
995 | * address family. |
---|
996 | */ |
---|
997 | void |
---|
998 | rt_foreach_fib_walk(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, |
---|
999 | void *arg) |
---|
1000 | { |
---|
1001 | struct rib_head *rnh; |
---|
1002 | uint32_t fibnum; |
---|
1003 | int i; |
---|
1004 | |
---|
1005 | for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { |
---|
1006 | /* Do we want some specific family? */ |
---|
1007 | if (af != AF_UNSPEC) { |
---|
1008 | rnh = rt_tables_get_rnh(fibnum, af); |
---|
1009 | if (rnh == NULL) |
---|
1010 | continue; |
---|
1011 | if (setwa_f != NULL) |
---|
1012 | setwa_f(rnh, fibnum, af, arg); |
---|
1013 | |
---|
1014 | RIB_WLOCK(rnh); |
---|
1015 | rnh->rnh_walktree(&rnh->head, (walktree_f_t *)wa_f,arg); |
---|
1016 | RIB_WUNLOCK(rnh); |
---|
1017 | continue; |
---|
1018 | } |
---|
1019 | |
---|
1020 | for (i = 1; i <= AF_MAX; i++) { |
---|
1021 | rnh = rt_tables_get_rnh(fibnum, i); |
---|
1022 | if (rnh == NULL) |
---|
1023 | continue; |
---|
1024 | if (setwa_f != NULL) |
---|
1025 | setwa_f(rnh, fibnum, i, arg); |
---|
1026 | |
---|
1027 | RIB_WLOCK(rnh); |
---|
1028 | rnh->rnh_walktree(&rnh->head, (walktree_f_t *)wa_f,arg); |
---|
1029 | RIB_WUNLOCK(rnh); |
---|
1030 | } |
---|
1031 | } |
---|
1032 | } |
---|
1033 | |
---|
1034 | struct rt_delinfo |
---|
1035 | { |
---|
1036 | struct rt_addrinfo info; |
---|
1037 | struct rib_head *rnh; |
---|
1038 | struct rtentry *head; |
---|
1039 | }; |
---|
1040 | |
---|
1041 | /* |
---|
1042 | * Conditionally unlinks @rn from radix tree based |
---|
1043 | * on info data passed in @arg. |
---|
1044 | */ |
---|
1045 | static int |
---|
1046 | rt_checkdelroute(struct radix_node *rn, void *arg) |
---|
1047 | { |
---|
1048 | struct rt_delinfo *di; |
---|
1049 | struct rt_addrinfo *info; |
---|
1050 | struct rtentry *rt; |
---|
1051 | int error; |
---|
1052 | |
---|
1053 | di = (struct rt_delinfo *)arg; |
---|
1054 | rt = (struct rtentry *)rn; |
---|
1055 | info = &di->info; |
---|
1056 | error = 0; |
---|
1057 | |
---|
1058 | info->rti_info[RTAX_DST] = rt_key(rt); |
---|
1059 | info->rti_info[RTAX_NETMASK] = rt_mask(rt); |
---|
1060 | info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; |
---|
1061 | |
---|
1062 | rt = rt_unlinkrte(di->rnh, info, &error); |
---|
1063 | if (rt == NULL) { |
---|
1064 | /* Either not allowed or not matched. Skip entry */ |
---|
1065 | return (0); |
---|
1066 | } |
---|
1067 | |
---|
1068 | /* Entry was unlinked. Add to the list and return */ |
---|
1069 | rt->rt_chain = di->head; |
---|
1070 | di->head = rt; |
---|
1071 | |
---|
1072 | return (0); |
---|
1073 | } |
---|
1074 | |
---|
1075 | /* |
---|
1076 | * Iterates over all existing fibs in system. |
---|
1077 | * Deletes each element for which @filter_f function returned |
---|
1078 | * non-zero value. |
---|
1079 | * If @af is not AF_UNSPEC, iterates over fibs in particular |
---|
1080 | * address family. |
---|
1081 | */ |
---|
1082 | void |
---|
1083 | rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg) |
---|
1084 | { |
---|
1085 | struct rib_head *rnh; |
---|
1086 | struct rt_delinfo di; |
---|
1087 | struct rtentry *rt; |
---|
1088 | uint32_t fibnum; |
---|
1089 | int i, start, end; |
---|
1090 | |
---|
1091 | bzero(&di, sizeof(di)); |
---|
1092 | di.info.rti_filter = filter_f; |
---|
1093 | di.info.rti_filterdata = arg; |
---|
1094 | |
---|
1095 | for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { |
---|
1096 | /* Do we want some specific family? */ |
---|
1097 | if (af != AF_UNSPEC) { |
---|
1098 | start = af; |
---|
1099 | end = af; |
---|
1100 | } else { |
---|
1101 | start = 1; |
---|
1102 | end = AF_MAX; |
---|
1103 | } |
---|
1104 | |
---|
1105 | for (i = start; i <= end; i++) { |
---|
1106 | rnh = rt_tables_get_rnh(fibnum, i); |
---|
1107 | if (rnh == NULL) |
---|
1108 | continue; |
---|
1109 | di.rnh = rnh; |
---|
1110 | |
---|
1111 | RIB_WLOCK(rnh); |
---|
1112 | rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); |
---|
1113 | RIB_WUNLOCK(rnh); |
---|
1114 | |
---|
1115 | if (di.head == NULL) |
---|
1116 | continue; |
---|
1117 | |
---|
1118 | /* We might have something to reclaim */ |
---|
1119 | while (di.head != NULL) { |
---|
1120 | rt = di.head; |
---|
1121 | di.head = rt->rt_chain; |
---|
1122 | rt->rt_chain = NULL; |
---|
1123 | |
---|
1124 | /* TODO std rt -> rt_addrinfo export */ |
---|
1125 | di.info.rti_info[RTAX_DST] = rt_key(rt); |
---|
1126 | di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); |
---|
1127 | |
---|
1128 | rt_notifydelete(rt, &di.info); |
---|
1129 | RTFREE_LOCKED(rt); |
---|
1130 | } |
---|
1131 | |
---|
1132 | } |
---|
1133 | } |
---|
1134 | } |
---|
1135 | |
---|
1136 | /* |
---|
1137 | * Delete Routes for a Network Interface |
---|
1138 | * |
---|
1139 | * Called for each routing entry via the rnh->rnh_walktree() call above |
---|
1140 | * to delete all route entries referencing a detaching network interface. |
---|
1141 | * |
---|
1142 | * Arguments: |
---|
1143 | * rt pointer to rtentry |
---|
1144 | * arg argument passed to rnh->rnh_walktree() - detaching interface |
---|
1145 | * |
---|
1146 | * Returns: |
---|
1147 | * 0 successful |
---|
1148 | * errno failed - reason indicated |
---|
1149 | */ |
---|
1150 | static int |
---|
1151 | rt_ifdelroute(const struct rtentry *rt, void *arg) |
---|
1152 | { |
---|
1153 | struct ifnet *ifp = arg; |
---|
1154 | |
---|
1155 | if (rt->rt_ifp != ifp) |
---|
1156 | return (0); |
---|
1157 | |
---|
1158 | /* |
---|
1159 | * Protect (sorta) against walktree recursion problems |
---|
1160 | * with cloned routes |
---|
1161 | */ |
---|
1162 | if ((rt->rt_flags & RTF_UP) == 0) |
---|
1163 | return (0); |
---|
1164 | |
---|
1165 | return (1); |
---|
1166 | } |
---|
1167 | |
---|
1168 | /* |
---|
1169 | * Delete all remaining routes using this interface |
---|
1170 | * Unfortuneatly the only way to do this is to slog through |
---|
1171 | * the entire routing table looking for routes which point |
---|
1172 | * to this interface...oh well... |
---|
1173 | */ |
---|
1174 | void |
---|
1175 | rt_flushifroutes_af(struct ifnet *ifp, int af) |
---|
1176 | { |
---|
1177 | KASSERT((af >= 1 && af <= AF_MAX), ("%s: af %d not >= 1 and <= %d", |
---|
1178 | __func__, af, AF_MAX)); |
---|
1179 | |
---|
1180 | rt_foreach_fib_walk_del(af, rt_ifdelroute, ifp); |
---|
1181 | } |
---|
1182 | |
---|
1183 | void |
---|
1184 | rt_flushifroutes(struct ifnet *ifp) |
---|
1185 | { |
---|
1186 | |
---|
1187 | rt_foreach_fib_walk_del(AF_UNSPEC, rt_ifdelroute, ifp); |
---|
1188 | } |
---|
1189 | |
---|
1190 | /* |
---|
1191 | * Conditionally unlinks rtentry matching data inside @info from @rnh. |
---|
1192 | * Returns unlinked, locked and referenced @rtentry on success, |
---|
1193 | * Returns NULL and sets @perror to: |
---|
1194 | * ESRCH - if prefix was not found, |
---|
1195 | * EADDRINUSE - if trying to delete PINNED route without appropriate flag. |
---|
1196 | * ENOENT - if supplied filter function returned 0 (not matched). |
---|
1197 | */ |
---|
1198 | static struct rtentry * |
---|
1199 | rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, int *perror) |
---|
1200 | { |
---|
1201 | struct sockaddr *dst, *netmask; |
---|
1202 | struct rtentry *rt; |
---|
1203 | struct radix_node *rn; |
---|
1204 | |
---|
1205 | dst = info->rti_info[RTAX_DST]; |
---|
1206 | netmask = info->rti_info[RTAX_NETMASK]; |
---|
1207 | |
---|
1208 | rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head); |
---|
1209 | if (rt == NULL) { |
---|
1210 | *perror = ESRCH; |
---|
1211 | return (NULL); |
---|
1212 | } |
---|
1213 | |
---|
1214 | if ((info->rti_flags & RTF_PINNED) == 0) { |
---|
1215 | /* Check if target route can be deleted */ |
---|
1216 | if (rt->rt_flags & RTF_PINNED) { |
---|
1217 | *perror = EADDRINUSE; |
---|
1218 | return (NULL); |
---|
1219 | } |
---|
1220 | } |
---|
1221 | |
---|
1222 | if (info->rti_filter != NULL) { |
---|
1223 | if (info->rti_filter(rt, info->rti_filterdata) == 0) { |
---|
1224 | /* Not matched */ |
---|
1225 | *perror = ENOENT; |
---|
1226 | return (NULL); |
---|
1227 | } |
---|
1228 | |
---|
1229 | /* |
---|
1230 | * Filter function requested rte deletion. |
---|
1231 | * Ease the caller work by filling in remaining info |
---|
1232 | * from that particular entry. |
---|
1233 | */ |
---|
1234 | info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; |
---|
1235 | } |
---|
1236 | |
---|
1237 | /* |
---|
1238 | * Remove the item from the tree and return it. |
---|
1239 | * Complain if it is not there and do no more processing. |
---|
1240 | */ |
---|
1241 | *perror = ESRCH; |
---|
1242 | #ifdef RADIX_MPATH |
---|
1243 | if (rt_mpath_capable(rnh)) |
---|
1244 | rn = rt_mpath_unlink(rnh, info, rt, perror); |
---|
1245 | else |
---|
1246 | #endif |
---|
1247 | rn = rnh->rnh_deladdr(dst, netmask, &rnh->head); |
---|
1248 | if (rn == NULL) |
---|
1249 | return (NULL); |
---|
1250 | |
---|
1251 | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
---|
1252 | panic ("rtrequest delete"); |
---|
1253 | |
---|
1254 | rt = RNTORT(rn); |
---|
1255 | RT_LOCK(rt); |
---|
1256 | RT_ADDREF(rt); |
---|
1257 | rt->rt_flags &= ~RTF_UP; |
---|
1258 | |
---|
1259 | *perror = 0; |
---|
1260 | |
---|
1261 | return (rt); |
---|
1262 | } |
---|
1263 | |
---|
1264 | static void |
---|
1265 | rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info) |
---|
1266 | { |
---|
1267 | struct ifaddr *ifa; |
---|
1268 | |
---|
1269 | /* |
---|
1270 | * give the protocol a chance to keep things in sync. |
---|
1271 | */ |
---|
1272 | ifa = rt->rt_ifa; |
---|
1273 | if (ifa != NULL && ifa->ifa_rtrequest != NULL) |
---|
1274 | ifa->ifa_rtrequest(RTM_DELETE, rt, info); |
---|
1275 | |
---|
1276 | /* |
---|
1277 | * One more rtentry floating around that is not |
---|
1278 | * linked to the routing table. rttrash will be decremented |
---|
1279 | * when RTFREE(rt) is eventually called. |
---|
1280 | */ |
---|
1281 | V_rttrash++; |
---|
1282 | } |
---|
1283 | |
---|
1284 | |
---|
1285 | /* |
---|
1286 | * These (questionable) definitions of apparent local variables apply |
---|
1287 | * to the next two functions. XXXXXX!!! |
---|
1288 | */ |
---|
1289 | #define dst info->rti_info[RTAX_DST] |
---|
1290 | #define gateway info->rti_info[RTAX_GATEWAY] |
---|
1291 | #define netmask info->rti_info[RTAX_NETMASK] |
---|
1292 | #define ifaaddr info->rti_info[RTAX_IFA] |
---|
1293 | #define ifpaddr info->rti_info[RTAX_IFP] |
---|
1294 | #define flags info->rti_flags |
---|
1295 | |
---|
1296 | /* |
---|
1297 | * Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined, |
---|
1298 | * it will be referenced so the caller must free it. |
---|
1299 | */ |
---|
1300 | int |
---|
1301 | rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) |
---|
1302 | { |
---|
1303 | struct ifaddr *ifa; |
---|
1304 | int error = 0; |
---|
1305 | |
---|
1306 | /* |
---|
1307 | * ifp may be specified by sockaddr_dl |
---|
1308 | * when protocol address is ambiguous. |
---|
1309 | */ |
---|
1310 | if (info->rti_ifp == NULL && ifpaddr != NULL && |
---|
1311 | ifpaddr->sa_family == AF_LINK && |
---|
1312 | (ifa = ifa_ifwithnet(ifpaddr, 0, fibnum)) != NULL) { |
---|
1313 | info->rti_ifp = ifa->ifa_ifp; |
---|
1314 | ifa_free(ifa); |
---|
1315 | } |
---|
1316 | if (info->rti_ifa == NULL && ifaaddr != NULL) |
---|
1317 | info->rti_ifa = ifa_ifwithaddr(ifaaddr); |
---|
1318 | if (info->rti_ifa == NULL) { |
---|
1319 | struct sockaddr *sa; |
---|
1320 | |
---|
1321 | sa = ifaaddr != NULL ? ifaaddr : |
---|
1322 | (gateway != NULL ? gateway : dst); |
---|
1323 | if (sa != NULL && info->rti_ifp != NULL) |
---|
1324 | info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp); |
---|
1325 | else if (dst != NULL && gateway != NULL) |
---|
1326 | info->rti_ifa = ifa_ifwithroute(flags, dst, gateway, |
---|
1327 | fibnum); |
---|
1328 | else if (sa != NULL) |
---|
1329 | info->rti_ifa = ifa_ifwithroute(flags, sa, sa, |
---|
1330 | fibnum); |
---|
1331 | } |
---|
1332 | if ((ifa = info->rti_ifa) != NULL) { |
---|
1333 | if (info->rti_ifp == NULL) |
---|
1334 | info->rti_ifp = ifa->ifa_ifp; |
---|
1335 | } else |
---|
1336 | error = ENETUNREACH; |
---|
1337 | return (error); |
---|
1338 | } |
---|
1339 | |
---|
1340 | static int |
---|
1341 | if_updatemtu_cb(struct radix_node *rn, void *arg) |
---|
1342 | { |
---|
1343 | struct rtentry *rt; |
---|
1344 | struct if_mtuinfo *ifmtu; |
---|
1345 | |
---|
1346 | rt = (struct rtentry *)rn; |
---|
1347 | ifmtu = (struct if_mtuinfo *)arg; |
---|
1348 | |
---|
1349 | if (rt->rt_ifp != ifmtu->ifp) |
---|
1350 | return (0); |
---|
1351 | |
---|
1352 | if (rt->rt_mtu >= ifmtu->mtu) { |
---|
1353 | /* We have to decrease mtu regardless of flags */ |
---|
1354 | rt->rt_mtu = ifmtu->mtu; |
---|
1355 | return (0); |
---|
1356 | } |
---|
1357 | |
---|
1358 | /* |
---|
1359 | * New MTU is bigger. Check if are allowed to alter it |
---|
1360 | */ |
---|
1361 | if ((rt->rt_flags & (RTF_FIXEDMTU | RTF_GATEWAY | RTF_HOST)) != 0) { |
---|
1362 | |
---|
1363 | /* |
---|
1364 | * Skip routes with user-supplied MTU and |
---|
1365 | * non-interface routes |
---|
1366 | */ |
---|
1367 | return (0); |
---|
1368 | } |
---|
1369 | |
---|
1370 | /* We are safe to update route MTU */ |
---|
1371 | rt->rt_mtu = ifmtu->mtu; |
---|
1372 | |
---|
1373 | return (0); |
---|
1374 | } |
---|
1375 | |
---|
1376 | void |
---|
1377 | rt_updatemtu(struct ifnet *ifp) |
---|
1378 | { |
---|
1379 | struct if_mtuinfo ifmtu; |
---|
1380 | struct rib_head *rnh; |
---|
1381 | int i, j; |
---|
1382 | |
---|
1383 | ifmtu.ifp = ifp; |
---|
1384 | |
---|
1385 | /* |
---|
1386 | * Try to update rt_mtu for all routes using this interface |
---|
1387 | * Unfortunately the only way to do this is to traverse all |
---|
1388 | * routing tables in all fibs/domains. |
---|
1389 | */ |
---|
1390 | for (i = 1; i <= AF_MAX; i++) { |
---|
1391 | ifmtu.mtu = if_getmtu_family(ifp, i); |
---|
1392 | for (j = 0; j < rt_numfibs; j++) { |
---|
1393 | rnh = rt_tables_get_rnh(j, i); |
---|
1394 | if (rnh == NULL) |
---|
1395 | continue; |
---|
1396 | RIB_WLOCK(rnh); |
---|
1397 | rnh->rnh_walktree(&rnh->head, if_updatemtu_cb, &ifmtu); |
---|
1398 | RIB_WUNLOCK(rnh); |
---|
1399 | } |
---|
1400 | } |
---|
1401 | } |
---|
1402 | |
---|
1403 | |
---|
1404 | #if 0 |
---|
1405 | int p_sockaddr(char *buf, int buflen, struct sockaddr *s); |
---|
1406 | int rt_print(char *buf, int buflen, struct rtentry *rt); |
---|
1407 | |
---|
1408 | int |
---|
1409 | p_sockaddr(char *buf, int buflen, struct sockaddr *s) |
---|
1410 | { |
---|
1411 | void *paddr = NULL; |
---|
1412 | |
---|
1413 | switch (s->sa_family) { |
---|
1414 | case AF_INET: |
---|
1415 | paddr = &((struct sockaddr_in *)s)->sin_addr; |
---|
1416 | break; |
---|
1417 | case AF_INET6: |
---|
1418 | paddr = &((struct sockaddr_in6 *)s)->sin6_addr; |
---|
1419 | break; |
---|
1420 | } |
---|
1421 | |
---|
1422 | if (paddr == NULL) |
---|
1423 | return (0); |
---|
1424 | |
---|
1425 | if (inet_ntop(s->sa_family, paddr, buf, buflen) == NULL) |
---|
1426 | return (0); |
---|
1427 | |
---|
1428 | return (strlen(buf)); |
---|
1429 | } |
---|
1430 | |
---|
1431 | int |
---|
1432 | rt_print(char *buf, int buflen, struct rtentry *rt) |
---|
1433 | { |
---|
1434 | struct sockaddr *addr, *mask; |
---|
1435 | int i = 0; |
---|
1436 | |
---|
1437 | addr = rt_key(rt); |
---|
1438 | mask = rt_mask(rt); |
---|
1439 | |
---|
1440 | i = p_sockaddr(buf, buflen, addr); |
---|
1441 | if (!(rt->rt_flags & RTF_HOST)) { |
---|
1442 | buf[i++] = '/'; |
---|
1443 | i += p_sockaddr(buf + i, buflen - i, mask); |
---|
1444 | } |
---|
1445 | |
---|
1446 | if (rt->rt_flags & RTF_GATEWAY) { |
---|
1447 | buf[i++] = '>'; |
---|
1448 | i += p_sockaddr(buf + i, buflen - i, rt->rt_gateway); |
---|
1449 | } |
---|
1450 | |
---|
1451 | return (i); |
---|
1452 | } |
---|
1453 | #endif |
---|
1454 | |
---|
1455 | #ifdef RADIX_MPATH |
---|
1456 | /* |
---|
1457 | * Deletes key for single-path routes, unlinks rtentry with |
---|
1458 | * gateway specified in @info from multi-path routes. |
---|
1459 | * |
---|
1460 | * Returnes unlinked entry. In case of failure, returns NULL |
---|
1461 | * and sets @perror to ESRCH. |
---|
1462 | */ |
---|
1463 | static struct radix_node * |
---|
1464 | rt_mpath_unlink(struct rib_head *rnh, struct rt_addrinfo *info, |
---|
1465 | struct rtentry *rto, int *perror) |
---|
1466 | { |
---|
1467 | /* |
---|
1468 | * if we got multipath routes, we require users to specify |
---|
1469 | * a matching RTAX_GATEWAY. |
---|
1470 | */ |
---|
1471 | struct rtentry *rt; // *rto = NULL; |
---|
1472 | struct radix_node *rn; |
---|
1473 | struct sockaddr *gw; |
---|
1474 | |
---|
1475 | gw = info->rti_info[RTAX_GATEWAY]; |
---|
1476 | rt = rt_mpath_matchgate(rto, gw); |
---|
1477 | if (rt == NULL) { |
---|
1478 | *perror = ESRCH; |
---|
1479 | return (NULL); |
---|
1480 | } |
---|
1481 | |
---|
1482 | /* |
---|
1483 | * this is the first entry in the chain |
---|
1484 | */ |
---|
1485 | if (rto == rt) { |
---|
1486 | rn = rn_mpath_next((struct radix_node *)rt); |
---|
1487 | /* |
---|
1488 | * there is another entry, now it's active |
---|
1489 | */ |
---|
1490 | if (rn) { |
---|
1491 | rto = RNTORT(rn); |
---|
1492 | RT_LOCK(rto); |
---|
1493 | rto->rt_flags |= RTF_UP; |
---|
1494 | RT_UNLOCK(rto); |
---|
1495 | } else if (rt->rt_flags & RTF_GATEWAY) { |
---|
1496 | /* |
---|
1497 | * For gateway routes, we need to |
---|
1498 | * make sure that we we are deleting |
---|
1499 | * the correct gateway. |
---|
1500 | * rt_mpath_matchgate() does not |
---|
1501 | * check the case when there is only |
---|
1502 | * one route in the chain. |
---|
1503 | */ |
---|
1504 | if (gw && |
---|
1505 | (rt->rt_gateway->sa_len != gw->sa_len || |
---|
1506 | memcmp(rt->rt_gateway, gw, gw->sa_len))) { |
---|
1507 | *perror = ESRCH; |
---|
1508 | return (NULL); |
---|
1509 | } |
---|
1510 | } |
---|
1511 | |
---|
1512 | /* |
---|
1513 | * use the normal delete code to remove |
---|
1514 | * the first entry |
---|
1515 | */ |
---|
1516 | rn = rnh->rnh_deladdr(dst, netmask, &rnh->head); |
---|
1517 | *perror = 0; |
---|
1518 | return (rn); |
---|
1519 | } |
---|
1520 | |
---|
1521 | /* |
---|
1522 | * if the entry is 2nd and on up |
---|
1523 | */ |
---|
1524 | if (rt_mpath_deldup(rto, rt) == 0) |
---|
1525 | panic ("rtrequest1: rt_mpath_deldup"); |
---|
1526 | *perror = 0; |
---|
1527 | rn = (struct radix_node *)rt; |
---|
1528 | return (rn); |
---|
1529 | } |
---|
1530 | #endif |
---|
1531 | |
---|
1532 | #ifdef FLOWTABLE |
---|
1533 | static struct rtentry * |
---|
1534 | rt_flowtable_check_route(struct rib_head *rnh, struct rt_addrinfo *info) |
---|
1535 | { |
---|
1536 | #if defined(INET6) || defined(INET) |
---|
1537 | struct radix_node *rn; |
---|
1538 | #endif |
---|
1539 | struct rtentry *rt0; |
---|
1540 | |
---|
1541 | rt0 = NULL; |
---|
1542 | /* "flow-table" only supports IPv6 and IPv4 at the moment. */ |
---|
1543 | switch (dst->sa_family) { |
---|
1544 | #ifdef INET6 |
---|
1545 | case AF_INET6: |
---|
1546 | #endif |
---|
1547 | #ifdef INET |
---|
1548 | case AF_INET: |
---|
1549 | #endif |
---|
1550 | #if defined(INET6) || defined(INET) |
---|
1551 | rn = rnh->rnh_matchaddr(dst, &rnh->head); |
---|
1552 | if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { |
---|
1553 | struct sockaddr *mask; |
---|
1554 | u_char *m, *n; |
---|
1555 | int len; |
---|
1556 | |
---|
1557 | /* |
---|
1558 | * compare mask to see if the new route is |
---|
1559 | * more specific than the existing one |
---|
1560 | */ |
---|
1561 | rt0 = RNTORT(rn); |
---|
1562 | RT_LOCK(rt0); |
---|
1563 | RT_ADDREF(rt0); |
---|
1564 | RT_UNLOCK(rt0); |
---|
1565 | /* |
---|
1566 | * A host route is already present, so |
---|
1567 | * leave the flow-table entries as is. |
---|
1568 | */ |
---|
1569 | if (rt0->rt_flags & RTF_HOST) { |
---|
1570 | RTFREE(rt0); |
---|
1571 | rt0 = NULL; |
---|
1572 | } else if (!(flags & RTF_HOST) && netmask) { |
---|
1573 | mask = rt_mask(rt0); |
---|
1574 | len = mask->sa_len; |
---|
1575 | m = (u_char *)mask; |
---|
1576 | n = (u_char *)netmask; |
---|
1577 | while (len-- > 0) { |
---|
1578 | if (*n != *m) |
---|
1579 | break; |
---|
1580 | n++; |
---|
1581 | m++; |
---|
1582 | } |
---|
1583 | if (len == 0 || (*n < *m)) { |
---|
1584 | RTFREE(rt0); |
---|
1585 | rt0 = NULL; |
---|
1586 | } |
---|
1587 | } |
---|
1588 | } |
---|
1589 | #endif/* INET6 || INET */ |
---|
1590 | } |
---|
1591 | |
---|
1592 | return (rt0); |
---|
1593 | } |
---|
1594 | #endif |
---|
1595 | |
---|
1596 | int |
---|
1597 | rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, |
---|
1598 | u_int fibnum) |
---|
1599 | { |
---|
1600 | int error = 0; |
---|
1601 | struct rtentry *rt, *rt_old; |
---|
1602 | #ifdef FLOWTABLE |
---|
1603 | struct rtentry *rt0; |
---|
1604 | #endif |
---|
1605 | struct radix_node *rn; |
---|
1606 | struct rib_head *rnh; |
---|
1607 | struct ifaddr *ifa; |
---|
1608 | struct sockaddr *ndst; |
---|
1609 | struct sockaddr_storage mdst; |
---|
1610 | |
---|
1611 | KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); |
---|
1612 | KASSERT((flags & RTF_RNH_LOCKED) == 0, ("rtrequest1_fib: locked")); |
---|
1613 | switch (dst->sa_family) { |
---|
1614 | case AF_INET6: |
---|
1615 | case AF_INET: |
---|
1616 | /* We support multiple FIBs. */ |
---|
1617 | break; |
---|
1618 | default: |
---|
1619 | fibnum = RT_DEFAULT_FIB; |
---|
1620 | break; |
---|
1621 | } |
---|
1622 | |
---|
1623 | /* |
---|
1624 | * Find the correct routing tree to use for this Address Family |
---|
1625 | */ |
---|
1626 | rnh = rt_tables_get_rnh(fibnum, dst->sa_family); |
---|
1627 | if (rnh == NULL) |
---|
1628 | return (EAFNOSUPPORT); |
---|
1629 | |
---|
1630 | /* |
---|
1631 | * If we are adding a host route then we don't want to put |
---|
1632 | * a netmask in the tree, nor do we want to clone it. |
---|
1633 | */ |
---|
1634 | if (flags & RTF_HOST) |
---|
1635 | netmask = NULL; |
---|
1636 | |
---|
1637 | switch (req) { |
---|
1638 | case RTM_DELETE: |
---|
1639 | if (netmask) { |
---|
1640 | rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask); |
---|
1641 | dst = (struct sockaddr *)&mdst; |
---|
1642 | } |
---|
1643 | |
---|
1644 | RIB_WLOCK(rnh); |
---|
1645 | rt = rt_unlinkrte(rnh, info, &error); |
---|
1646 | RIB_WUNLOCK(rnh); |
---|
1647 | if (error != 0) |
---|
1648 | return (error); |
---|
1649 | |
---|
1650 | rt_notifydelete(rt, info); |
---|
1651 | |
---|
1652 | /* |
---|
1653 | * If the caller wants it, then it can have it, |
---|
1654 | * but it's up to it to free the rtentry as we won't be |
---|
1655 | * doing it. |
---|
1656 | */ |
---|
1657 | if (ret_nrt) { |
---|
1658 | *ret_nrt = rt; |
---|
1659 | RT_UNLOCK(rt); |
---|
1660 | } else |
---|
1661 | RTFREE_LOCKED(rt); |
---|
1662 | break; |
---|
1663 | case RTM_RESOLVE: |
---|
1664 | /* |
---|
1665 | * resolve was only used for route cloning |
---|
1666 | * here for compat |
---|
1667 | */ |
---|
1668 | break; |
---|
1669 | case RTM_ADD: |
---|
1670 | if ((flags & RTF_GATEWAY) && !gateway) |
---|
1671 | return (EINVAL); |
---|
1672 | if (dst && gateway && (dst->sa_family != gateway->sa_family) && |
---|
1673 | (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) |
---|
1674 | return (EINVAL); |
---|
1675 | |
---|
1676 | if (info->rti_ifa == NULL) { |
---|
1677 | error = rt_getifa_fib(info, fibnum); |
---|
1678 | if (error) |
---|
1679 | return (error); |
---|
1680 | } else |
---|
1681 | ifa_ref(info->rti_ifa); |
---|
1682 | ifa = info->rti_ifa; |
---|
1683 | rt = uma_zalloc(V_rtzone, M_NOWAIT); |
---|
1684 | if (rt == NULL) { |
---|
1685 | ifa_free(ifa); |
---|
1686 | return (ENOBUFS); |
---|
1687 | } |
---|
1688 | rt->rt_flags = RTF_UP | flags; |
---|
1689 | rt->rt_fibnum = fibnum; |
---|
1690 | /* |
---|
1691 | * Add the gateway. Possibly re-malloc-ing the storage for it. |
---|
1692 | */ |
---|
1693 | if ((error = rt_setgate(rt, dst, gateway)) != 0) { |
---|
1694 | ifa_free(ifa); |
---|
1695 | uma_zfree(V_rtzone, rt); |
---|
1696 | return (error); |
---|
1697 | } |
---|
1698 | |
---|
1699 | /* |
---|
1700 | * point to the (possibly newly malloc'd) dest address. |
---|
1701 | */ |
---|
1702 | ndst = (struct sockaddr *)rt_key(rt); |
---|
1703 | |
---|
1704 | /* |
---|
1705 | * make sure it contains the value we want (masked if needed). |
---|
1706 | */ |
---|
1707 | if (netmask) { |
---|
1708 | rt_maskedcopy(dst, ndst, netmask); |
---|
1709 | } else |
---|
1710 | bcopy(dst, ndst, dst->sa_len); |
---|
1711 | |
---|
1712 | /* |
---|
1713 | * We use the ifa reference returned by rt_getifa_fib(). |
---|
1714 | * This moved from below so that rnh->rnh_addaddr() can |
---|
1715 | * examine the ifa and ifa->ifa_ifp if it so desires. |
---|
1716 | */ |
---|
1717 | rt->rt_ifa = ifa; |
---|
1718 | rt->rt_ifp = ifa->ifa_ifp; |
---|
1719 | rt->rt_weight = 1; |
---|
1720 | |
---|
1721 | rt_setmetrics(info, rt); |
---|
1722 | |
---|
1723 | RIB_WLOCK(rnh); |
---|
1724 | RT_LOCK(rt); |
---|
1725 | #ifdef RADIX_MPATH |
---|
1726 | /* do not permit exactly the same dst/mask/gw pair */ |
---|
1727 | if (rt_mpath_capable(rnh) && |
---|
1728 | rt_mpath_conflict(rnh, rt, netmask)) { |
---|
1729 | RIB_WUNLOCK(rnh); |
---|
1730 | |
---|
1731 | ifa_free(rt->rt_ifa); |
---|
1732 | R_Free(rt_key(rt)); |
---|
1733 | uma_zfree(V_rtzone, rt); |
---|
1734 | return (EEXIST); |
---|
1735 | } |
---|
1736 | #endif |
---|
1737 | |
---|
1738 | #ifdef FLOWTABLE |
---|
1739 | rt0 = rt_flowtable_check_route(rnh, info); |
---|
1740 | #endif /* FLOWTABLE */ |
---|
1741 | |
---|
1742 | /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ |
---|
1743 | rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); |
---|
1744 | |
---|
1745 | rt_old = NULL; |
---|
1746 | if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) { |
---|
1747 | |
---|
1748 | /* |
---|
1749 | * Force removal and re-try addition |
---|
1750 | * TODO: better multipath&pinned support |
---|
1751 | */ |
---|
1752 | struct sockaddr *info_dst = info->rti_info[RTAX_DST]; |
---|
1753 | info->rti_info[RTAX_DST] = ndst; |
---|
1754 | /* Do not delete existing PINNED(interface) routes */ |
---|
1755 | info->rti_flags &= ~RTF_PINNED; |
---|
1756 | rt_old = rt_unlinkrte(rnh, info, &error); |
---|
1757 | info->rti_flags |= RTF_PINNED; |
---|
1758 | info->rti_info[RTAX_DST] = info_dst; |
---|
1759 | if (rt_old != NULL) |
---|
1760 | rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, |
---|
1761 | rt->rt_nodes); |
---|
1762 | } |
---|
1763 | RIB_WUNLOCK(rnh); |
---|
1764 | |
---|
1765 | if (rt_old != NULL) |
---|
1766 | RT_UNLOCK(rt_old); |
---|
1767 | |
---|
1768 | /* |
---|
1769 | * If it still failed to go into the tree, |
---|
1770 | * then un-make it (this should be a function) |
---|
1771 | */ |
---|
1772 | if (rn == NULL) { |
---|
1773 | ifa_free(rt->rt_ifa); |
---|
1774 | R_Free(rt_key(rt)); |
---|
1775 | uma_zfree(V_rtzone, rt); |
---|
1776 | #ifdef FLOWTABLE |
---|
1777 | if (rt0 != NULL) |
---|
1778 | RTFREE(rt0); |
---|
1779 | #endif |
---|
1780 | return (EEXIST); |
---|
1781 | } |
---|
1782 | #ifdef FLOWTABLE |
---|
1783 | else if (rt0 != NULL) { |
---|
1784 | flowtable_route_flush(dst->sa_family, rt0); |
---|
1785 | RTFREE(rt0); |
---|
1786 | } |
---|
1787 | #endif |
---|
1788 | |
---|
1789 | if (rt_old != NULL) { |
---|
1790 | rt_notifydelete(rt_old, info); |
---|
1791 | RTFREE(rt_old); |
---|
1792 | } |
---|
1793 | |
---|
1794 | /* |
---|
1795 | * If this protocol has something to add to this then |
---|
1796 | * allow it to do that as well. |
---|
1797 | */ |
---|
1798 | if (ifa->ifa_rtrequest) |
---|
1799 | ifa->ifa_rtrequest(req, rt, info); |
---|
1800 | |
---|
1801 | /* |
---|
1802 | * actually return a resultant rtentry and |
---|
1803 | * give the caller a single reference. |
---|
1804 | */ |
---|
1805 | if (ret_nrt) { |
---|
1806 | *ret_nrt = rt; |
---|
1807 | RT_ADDREF(rt); |
---|
1808 | } |
---|
1809 | rnh->rnh_gen++; /* Routing table updated */ |
---|
1810 | RT_UNLOCK(rt); |
---|
1811 | break; |
---|
1812 | case RTM_CHANGE: |
---|
1813 | RIB_WLOCK(rnh); |
---|
1814 | error = rtrequest1_fib_change(rnh, info, ret_nrt, fibnum); |
---|
1815 | RIB_WUNLOCK(rnh); |
---|
1816 | break; |
---|
1817 | default: |
---|
1818 | error = EOPNOTSUPP; |
---|
1819 | } |
---|
1820 | |
---|
1821 | return (error); |
---|
1822 | } |
---|
1823 | |
---|
1824 | #undef dst |
---|
1825 | #undef gateway |
---|
1826 | #undef netmask |
---|
1827 | #undef ifaaddr |
---|
1828 | #undef ifpaddr |
---|
1829 | #undef flags |
---|
1830 | |
---|
1831 | static int |
---|
1832 | rtrequest1_fib_change(struct rib_head *rnh, struct rt_addrinfo *info, |
---|
1833 | struct rtentry **ret_nrt, u_int fibnum) |
---|
1834 | { |
---|
1835 | struct rtentry *rt = NULL; |
---|
1836 | int error = 0; |
---|
1837 | int free_ifa = 0; |
---|
1838 | int family, mtu; |
---|
1839 | struct if_mtuinfo ifmtu; |
---|
1840 | |
---|
1841 | rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], |
---|
1842 | info->rti_info[RTAX_NETMASK], &rnh->head); |
---|
1843 | |
---|
1844 | if (rt == NULL) |
---|
1845 | return (ESRCH); |
---|
1846 | |
---|
1847 | #ifdef RADIX_MPATH |
---|
1848 | /* |
---|
1849 | * If we got multipath routes, |
---|
1850 | * we require users to specify a matching RTAX_GATEWAY. |
---|
1851 | */ |
---|
1852 | if (rt_mpath_capable(rnh)) { |
---|
1853 | rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); |
---|
1854 | if (rt == NULL) |
---|
1855 | return (ESRCH); |
---|
1856 | } |
---|
1857 | #endif |
---|
1858 | |
---|
1859 | RT_LOCK(rt); |
---|
1860 | |
---|
1861 | rt_setmetrics(info, rt); |
---|
1862 | |
---|
1863 | /* |
---|
1864 | * New gateway could require new ifaddr, ifp; |
---|
1865 | * flags may also be different; ifp may be specified |
---|
1866 | * by ll sockaddr when protocol address is ambiguous |
---|
1867 | */ |
---|
1868 | if (((rt->rt_flags & RTF_GATEWAY) && |
---|
1869 | info->rti_info[RTAX_GATEWAY] != NULL) || |
---|
1870 | info->rti_info[RTAX_IFP] != NULL || |
---|
1871 | (info->rti_info[RTAX_IFA] != NULL && |
---|
1872 | !sa_equal(info->rti_info[RTAX_IFA], rt->rt_ifa->ifa_addr))) { |
---|
1873 | |
---|
1874 | error = rt_getifa_fib(info, fibnum); |
---|
1875 | if (info->rti_ifa != NULL) |
---|
1876 | free_ifa = 1; |
---|
1877 | |
---|
1878 | if (error != 0) |
---|
1879 | goto bad; |
---|
1880 | } |
---|
1881 | |
---|
1882 | /* Check if outgoing interface has changed */ |
---|
1883 | if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa && |
---|
1884 | rt->rt_ifa != NULL && rt->rt_ifa->ifa_rtrequest != NULL) { |
---|
1885 | rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, info); |
---|
1886 | ifa_free(rt->rt_ifa); |
---|
1887 | } |
---|
1888 | /* Update gateway address */ |
---|
1889 | if (info->rti_info[RTAX_GATEWAY] != NULL) { |
---|
1890 | error = rt_setgate(rt, rt_key(rt), info->rti_info[RTAX_GATEWAY]); |
---|
1891 | if (error != 0) |
---|
1892 | goto bad; |
---|
1893 | |
---|
1894 | rt->rt_flags &= ~RTF_GATEWAY; |
---|
1895 | rt->rt_flags |= (RTF_GATEWAY & info->rti_flags); |
---|
1896 | } |
---|
1897 | |
---|
1898 | if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa) { |
---|
1899 | ifa_ref(info->rti_ifa); |
---|
1900 | rt->rt_ifa = info->rti_ifa; |
---|
1901 | rt->rt_ifp = info->rti_ifp; |
---|
1902 | } |
---|
1903 | /* Allow some flags to be toggled on change. */ |
---|
1904 | rt->rt_flags &= ~RTF_FMASK; |
---|
1905 | rt->rt_flags |= info->rti_flags & RTF_FMASK; |
---|
1906 | |
---|
1907 | if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest != NULL) |
---|
1908 | rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info); |
---|
1909 | |
---|
1910 | /* Alter route MTU if necessary */ |
---|
1911 | if (rt->rt_ifp != NULL) { |
---|
1912 | family = info->rti_info[RTAX_DST]->sa_family; |
---|
1913 | mtu = if_getmtu_family(rt->rt_ifp, family); |
---|
1914 | /* Set default MTU */ |
---|
1915 | if (rt->rt_mtu == 0) |
---|
1916 | rt->rt_mtu = mtu; |
---|
1917 | if (rt->rt_mtu != mtu) { |
---|
1918 | /* Check if we really need to update */ |
---|
1919 | ifmtu.ifp = rt->rt_ifp; |
---|
1920 | ifmtu.mtu = mtu; |
---|
1921 | if_updatemtu_cb(rt->rt_nodes, &ifmtu); |
---|
1922 | } |
---|
1923 | } |
---|
1924 | |
---|
1925 | if (ret_nrt) { |
---|
1926 | *ret_nrt = rt; |
---|
1927 | RT_ADDREF(rt); |
---|
1928 | } |
---|
1929 | bad: |
---|
1930 | RT_UNLOCK(rt); |
---|
1931 | if (free_ifa != 0) |
---|
1932 | ifa_free(info->rti_ifa); |
---|
1933 | return (error); |
---|
1934 | } |
---|
1935 | |
---|
1936 | static void |
---|
1937 | rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt) |
---|
1938 | { |
---|
1939 | |
---|
1940 | if (info->rti_mflags & RTV_MTU) { |
---|
1941 | if (info->rti_rmx->rmx_mtu != 0) { |
---|
1942 | |
---|
1943 | /* |
---|
1944 | * MTU was explicitly provided by user. |
---|
1945 | * Keep it. |
---|
1946 | */ |
---|
1947 | rt->rt_flags |= RTF_FIXEDMTU; |
---|
1948 | } else { |
---|
1949 | |
---|
1950 | /* |
---|
1951 | * User explicitly sets MTU to 0. |
---|
1952 | * Assume rollback to default. |
---|
1953 | */ |
---|
1954 | rt->rt_flags &= ~RTF_FIXEDMTU; |
---|
1955 | } |
---|
1956 | rt->rt_mtu = info->rti_rmx->rmx_mtu; |
---|
1957 | } |
---|
1958 | if (info->rti_mflags & RTV_WEIGHT) |
---|
1959 | rt->rt_weight = info->rti_rmx->rmx_weight; |
---|
1960 | /* Kernel -> userland timebase conversion. */ |
---|
1961 | if (info->rti_mflags & RTV_EXPIRE) |
---|
1962 | rt->rt_expire = info->rti_rmx->rmx_expire ? |
---|
1963 | info->rti_rmx->rmx_expire - time_second + time_uptime : 0; |
---|
1964 | } |
---|
1965 | |
---|
1966 | int |
---|
1967 | rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) |
---|
1968 | { |
---|
1969 | /* XXX dst may be overwritten, can we move this to below */ |
---|
1970 | int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); |
---|
1971 | |
---|
1972 | /* |
---|
1973 | * Prepare to store the gateway in rt->rt_gateway. |
---|
1974 | * Both dst and gateway are stored one after the other in the same |
---|
1975 | * malloc'd chunk. If we have room, we can reuse the old buffer, |
---|
1976 | * rt_gateway already points to the right place. |
---|
1977 | * Otherwise, malloc a new block and update the 'dst' address. |
---|
1978 | */ |
---|
1979 | if (rt->rt_gateway == NULL || glen > SA_SIZE(rt->rt_gateway)) { |
---|
1980 | caddr_t new; |
---|
1981 | |
---|
1982 | R_Malloc(new, caddr_t, dlen + glen); |
---|
1983 | if (new == NULL) |
---|
1984 | return ENOBUFS; |
---|
1985 | /* |
---|
1986 | * XXX note, we copy from *dst and not *rt_key(rt) because |
---|
1987 | * rt_setgate() can be called to initialize a newly |
---|
1988 | * allocated route entry, in which case rt_key(rt) == NULL |
---|
1989 | * (and also rt->rt_gateway == NULL). |
---|
1990 | * Free()/free() handle a NULL argument just fine. |
---|
1991 | */ |
---|
1992 | bcopy(dst, new, dlen); |
---|
1993 | R_Free(rt_key(rt)); /* free old block, if any */ |
---|
1994 | rt_key(rt) = (struct sockaddr *)new; |
---|
1995 | rt->rt_gateway = (struct sockaddr *)(new + dlen); |
---|
1996 | } |
---|
1997 | |
---|
1998 | /* |
---|
1999 | * Copy the new gateway value into the memory chunk. |
---|
2000 | */ |
---|
2001 | bcopy(gate, rt->rt_gateway, glen); |
---|
2002 | |
---|
2003 | return (0); |
---|
2004 | } |
---|
2005 | |
---|
2006 | void |
---|
2007 | rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) |
---|
2008 | { |
---|
2009 | u_char *cp1 = (u_char *)src; |
---|
2010 | u_char *cp2 = (u_char *)dst; |
---|
2011 | u_char *cp3 = (u_char *)netmask; |
---|
2012 | u_char *cplim = cp2 + *cp3; |
---|
2013 | u_char *cplim2 = cp2 + *cp1; |
---|
2014 | |
---|
2015 | *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ |
---|
2016 | cp3 += 2; |
---|
2017 | if (cplim > cplim2) |
---|
2018 | cplim = cplim2; |
---|
2019 | while (cp2 < cplim) |
---|
2020 | *cp2++ = *cp1++ & *cp3++; |
---|
2021 | if (cp2 < cplim2) |
---|
2022 | bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); |
---|
2023 | } |
---|
2024 | |
---|
2025 | /* |
---|
2026 | * Set up a routing table entry, normally |
---|
2027 | * for an interface. |
---|
2028 | */ |
---|
2029 | #define _SOCKADDR_TMPSIZE 128 /* Not too big.. kernel stack size is limited */ |
---|
2030 | static inline int |
---|
2031 | rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) |
---|
2032 | { |
---|
2033 | struct sockaddr *dst; |
---|
2034 | struct sockaddr *netmask; |
---|
2035 | struct rtentry *rt = NULL; |
---|
2036 | struct rt_addrinfo info; |
---|
2037 | int error = 0; |
---|
2038 | int startfib, endfib; |
---|
2039 | char tempbuf[_SOCKADDR_TMPSIZE]; |
---|
2040 | int didwork = 0; |
---|
2041 | int a_failure = 0; |
---|
2042 | static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; |
---|
2043 | struct rib_head *rnh; |
---|
2044 | |
---|
2045 | if (flags & RTF_HOST) { |
---|
2046 | dst = ifa->ifa_dstaddr; |
---|
2047 | netmask = NULL; |
---|
2048 | } else { |
---|
2049 | dst = ifa->ifa_addr; |
---|
2050 | netmask = ifa->ifa_netmask; |
---|
2051 | } |
---|
2052 | if (dst->sa_len == 0) |
---|
2053 | return(EINVAL); |
---|
2054 | switch (dst->sa_family) { |
---|
2055 | case AF_INET6: |
---|
2056 | case AF_INET: |
---|
2057 | /* We support multiple FIBs. */ |
---|
2058 | break; |
---|
2059 | default: |
---|
2060 | fibnum = RT_DEFAULT_FIB; |
---|
2061 | break; |
---|
2062 | } |
---|
2063 | if (fibnum == RT_ALL_FIBS) { |
---|
2064 | if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD) |
---|
2065 | #ifndef __rtems__ |
---|
2066 | startfib = endfib = ifa->ifa_ifp->if_fib; |
---|
2067 | #else /* __rtems__ */ |
---|
2068 | startfib = endfib = BSD_DEFAULT_FIB; |
---|
2069 | #endif /* __rtems__ */ |
---|
2070 | else { |
---|
2071 | startfib = 0; |
---|
2072 | endfib = rt_numfibs - 1; |
---|
2073 | } |
---|
2074 | } else { |
---|
2075 | KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum")); |
---|
2076 | startfib = fibnum; |
---|
2077 | endfib = fibnum; |
---|
2078 | } |
---|
2079 | |
---|
2080 | /* |
---|
2081 | * If it's a delete, check that if it exists, |
---|
2082 | * it's on the correct interface or we might scrub |
---|
2083 | * a route to another ifa which would |
---|
2084 | * be confusing at best and possibly worse. |
---|
2085 | */ |
---|
2086 | if (cmd == RTM_DELETE) { |
---|
2087 | /* |
---|
2088 | * It's a delete, so it should already exist.. |
---|
2089 | * If it's a net, mask off the host bits |
---|
2090 | * (Assuming we have a mask) |
---|
2091 | * XXX this is kinda inet specific.. |
---|
2092 | */ |
---|
2093 | if (netmask != NULL) { |
---|
2094 | rt_maskedcopy(dst, (struct sockaddr *)tempbuf, netmask); |
---|
2095 | dst = (struct sockaddr *)tempbuf; |
---|
2096 | } |
---|
2097 | } |
---|
2098 | /* |
---|
2099 | * Now go through all the requested tables (fibs) and do the |
---|
2100 | * requested action. Realistically, this will either be fib 0 |
---|
2101 | * for protocols that don't do multiple tables or all the |
---|
2102 | * tables for those that do. |
---|
2103 | */ |
---|
2104 | for ( fibnum = startfib; fibnum <= endfib; fibnum++) { |
---|
2105 | if (cmd == RTM_DELETE) { |
---|
2106 | struct radix_node *rn; |
---|
2107 | /* |
---|
2108 | * Look up an rtentry that is in the routing tree and |
---|
2109 | * contains the correct info. |
---|
2110 | */ |
---|
2111 | rnh = rt_tables_get_rnh(fibnum, dst->sa_family); |
---|
2112 | if (rnh == NULL) |
---|
2113 | /* this table doesn't exist but others might */ |
---|
2114 | continue; |
---|
2115 | RIB_RLOCK(rnh); |
---|
2116 | rn = rnh->rnh_lookup(dst, netmask, &rnh->head); |
---|
2117 | #ifdef RADIX_MPATH |
---|
2118 | if (rt_mpath_capable(rnh)) { |
---|
2119 | |
---|
2120 | if (rn == NULL) |
---|
2121 | error = ESRCH; |
---|
2122 | else { |
---|
2123 | rt = RNTORT(rn); |
---|
2124 | /* |
---|
2125 | * for interface route the |
---|
2126 | * rt->rt_gateway is sockaddr_intf |
---|
2127 | * for cloning ARP entries, so |
---|
2128 | * rt_mpath_matchgate must use the |
---|
2129 | * interface address |
---|
2130 | */ |
---|
2131 | rt = rt_mpath_matchgate(rt, |
---|
2132 | ifa->ifa_addr); |
---|
2133 | if (rt == NULL) |
---|
2134 | error = ESRCH; |
---|
2135 | } |
---|
2136 | } |
---|
2137 | #endif |
---|
2138 | error = (rn == NULL || |
---|
2139 | (rn->rn_flags & RNF_ROOT) || |
---|
2140 | RNTORT(rn)->rt_ifa != ifa); |
---|
2141 | RIB_RUNLOCK(rnh); |
---|
2142 | if (error) { |
---|
2143 | /* this is only an error if bad on ALL tables */ |
---|
2144 | continue; |
---|
2145 | } |
---|
2146 | } |
---|
2147 | /* |
---|
2148 | * Do the actual request |
---|
2149 | */ |
---|
2150 | bzero((caddr_t)&info, sizeof(info)); |
---|
2151 | info.rti_ifa = ifa; |
---|
2152 | info.rti_flags = flags | |
---|
2153 | (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; |
---|
2154 | info.rti_info[RTAX_DST] = dst; |
---|
2155 | /* |
---|
2156 | * doing this for compatibility reasons |
---|
2157 | */ |
---|
2158 | if (cmd == RTM_ADD) |
---|
2159 | info.rti_info[RTAX_GATEWAY] = |
---|
2160 | (struct sockaddr *)&null_sdl; |
---|
2161 | else |
---|
2162 | info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; |
---|
2163 | info.rti_info[RTAX_NETMASK] = netmask; |
---|
2164 | error = rtrequest1_fib(cmd, &info, &rt, fibnum); |
---|
2165 | |
---|
2166 | if (error == 0 && rt != NULL) { |
---|
2167 | /* |
---|
2168 | * notify any listening routing agents of the change |
---|
2169 | */ |
---|
2170 | RT_LOCK(rt); |
---|
2171 | #ifdef RADIX_MPATH |
---|
2172 | /* |
---|
2173 | * in case address alias finds the first address |
---|
2174 | * e.g. ifconfig bge0 192.0.2.246/24 |
---|
2175 | * e.g. ifconfig bge0 192.0.2.247/24 |
---|
2176 | * the address set in the route is 192.0.2.246 |
---|
2177 | * so we need to replace it with 192.0.2.247 |
---|
2178 | */ |
---|
2179 | if (memcmp(rt->rt_ifa->ifa_addr, |
---|
2180 | ifa->ifa_addr, ifa->ifa_addr->sa_len)) { |
---|
2181 | ifa_free(rt->rt_ifa); |
---|
2182 | ifa_ref(ifa); |
---|
2183 | rt->rt_ifp = ifa->ifa_ifp; |
---|
2184 | rt->rt_ifa = ifa; |
---|
2185 | } |
---|
2186 | #endif |
---|
2187 | /* |
---|
2188 | * doing this for compatibility reasons |
---|
2189 | */ |
---|
2190 | if (cmd == RTM_ADD) { |
---|
2191 | ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = |
---|
2192 | rt->rt_ifp->if_type; |
---|
2193 | ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = |
---|
2194 | rt->rt_ifp->if_index; |
---|
2195 | } |
---|
2196 | RT_ADDREF(rt); |
---|
2197 | RT_UNLOCK(rt); |
---|
2198 | rt_newaddrmsg_fib(cmd, ifa, error, rt, fibnum); |
---|
2199 | RT_LOCK(rt); |
---|
2200 | RT_REMREF(rt); |
---|
2201 | if (cmd == RTM_DELETE) { |
---|
2202 | /* |
---|
2203 | * If we are deleting, and we found an entry, |
---|
2204 | * then it's been removed from the tree.. |
---|
2205 | * now throw it away. |
---|
2206 | */ |
---|
2207 | RTFREE_LOCKED(rt); |
---|
2208 | } else { |
---|
2209 | if (cmd == RTM_ADD) { |
---|
2210 | /* |
---|
2211 | * We just wanted to add it.. |
---|
2212 | * we don't actually need a reference. |
---|
2213 | */ |
---|
2214 | RT_REMREF(rt); |
---|
2215 | } |
---|
2216 | RT_UNLOCK(rt); |
---|
2217 | } |
---|
2218 | didwork = 1; |
---|
2219 | } |
---|
2220 | if (error) |
---|
2221 | a_failure = error; |
---|
2222 | } |
---|
2223 | if (cmd == RTM_DELETE) { |
---|
2224 | if (didwork) { |
---|
2225 | error = 0; |
---|
2226 | } else { |
---|
2227 | /* we only give an error if it wasn't in any table */ |
---|
2228 | error = ((flags & RTF_HOST) ? |
---|
2229 | EHOSTUNREACH : ENETUNREACH); |
---|
2230 | } |
---|
2231 | } else { |
---|
2232 | if (a_failure) { |
---|
2233 | /* return an error if any of them failed */ |
---|
2234 | error = a_failure; |
---|
2235 | } |
---|
2236 | } |
---|
2237 | return (error); |
---|
2238 | } |
---|
2239 | |
---|
2240 | /* |
---|
2241 | * Set up a routing table entry, normally |
---|
2242 | * for an interface. |
---|
2243 | */ |
---|
2244 | int |
---|
2245 | rtinit(struct ifaddr *ifa, int cmd, int flags) |
---|
2246 | { |
---|
2247 | struct sockaddr *dst; |
---|
2248 | int fib = RT_DEFAULT_FIB; |
---|
2249 | |
---|
2250 | if (flags & RTF_HOST) { |
---|
2251 | dst = ifa->ifa_dstaddr; |
---|
2252 | } else { |
---|
2253 | dst = ifa->ifa_addr; |
---|
2254 | } |
---|
2255 | |
---|
2256 | switch (dst->sa_family) { |
---|
2257 | case AF_INET6: |
---|
2258 | case AF_INET: |
---|
2259 | /* We do support multiple FIBs. */ |
---|
2260 | fib = RT_ALL_FIBS; |
---|
2261 | break; |
---|
2262 | } |
---|
2263 | return (rtinit1(ifa, cmd, flags, fib)); |
---|
2264 | } |
---|
2265 | |
---|
2266 | /* |
---|
2267 | * Announce interface address arrival/withdraw |
---|
2268 | * Returns 0 on success. |
---|
2269 | */ |
---|
2270 | int |
---|
2271 | rt_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) |
---|
2272 | { |
---|
2273 | |
---|
2274 | KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, |
---|
2275 | ("unexpected cmd %d", cmd)); |
---|
2276 | |
---|
2277 | KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), |
---|
2278 | ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); |
---|
2279 | |
---|
2280 | #if defined(INET) || defined(INET6) |
---|
2281 | #ifdef SCTP |
---|
2282 | /* |
---|
2283 | * notify the SCTP stack |
---|
2284 | * this will only get called when an address is added/deleted |
---|
2285 | * XXX pass the ifaddr struct instead if ifa->ifa_addr... |
---|
2286 | */ |
---|
2287 | sctp_addr_change(ifa, cmd); |
---|
2288 | #endif /* SCTP */ |
---|
2289 | #endif |
---|
2290 | return (rtsock_addrmsg(cmd, ifa, fibnum)); |
---|
2291 | } |
---|
2292 | |
---|
2293 | /* |
---|
2294 | * Announce route addition/removal. |
---|
2295 | * Users of this function MUST validate input data BEFORE calling. |
---|
2296 | * However we have to be able to handle invalid data: |
---|
2297 | * if some userland app sends us "invalid" route message (invalid mask, |
---|
2298 | * no dst, wrong address families, etc...) we need to pass it back |
---|
2299 | * to app (and any other rtsock consumers) with rtm_errno field set to |
---|
2300 | * non-zero value. |
---|
2301 | * Returns 0 on success. |
---|
2302 | */ |
---|
2303 | int |
---|
2304 | rt_routemsg(int cmd, struct ifnet *ifp, int error, struct rtentry *rt, |
---|
2305 | int fibnum) |
---|
2306 | { |
---|
2307 | |
---|
2308 | KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, |
---|
2309 | ("unexpected cmd %d", cmd)); |
---|
2310 | |
---|
2311 | KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), |
---|
2312 | ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); |
---|
2313 | |
---|
2314 | KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); |
---|
2315 | |
---|
2316 | return (rtsock_routemsg(cmd, ifp, error, rt, fibnum)); |
---|
2317 | } |
---|
2318 | |
---|
2319 | void |
---|
2320 | rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) |
---|
2321 | { |
---|
2322 | |
---|
2323 | rt_newaddrmsg_fib(cmd, ifa, error, rt, RT_ALL_FIBS); |
---|
2324 | } |
---|
2325 | |
---|
2326 | /* |
---|
2327 | * This is called to generate messages from the routing socket |
---|
2328 | * indicating a network interface has had addresses associated with it. |
---|
2329 | */ |
---|
2330 | void |
---|
2331 | rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt, |
---|
2332 | int fibnum) |
---|
2333 | { |
---|
2334 | |
---|
2335 | KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, |
---|
2336 | ("unexpected cmd %u", cmd)); |
---|
2337 | KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), |
---|
2338 | ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); |
---|
2339 | |
---|
2340 | if (cmd == RTM_ADD) { |
---|
2341 | rt_addrmsg(cmd, ifa, fibnum); |
---|
2342 | if (rt != NULL) |
---|
2343 | rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); |
---|
2344 | } else { |
---|
2345 | if (rt != NULL) |
---|
2346 | rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); |
---|
2347 | rt_addrmsg(cmd, ifa, fibnum); |
---|
2348 | } |
---|
2349 | } |
---|
2350 | |
---|