File: | net/rtable.c |
Warning: | line 504, column 23 Division by zero |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: rtable.c,v 1.76 2022/01/02 22:36:04 jsg Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2014-2016 Martin Pieuchot | |||
5 | * | |||
6 | * Permission to use, copy, modify, and distribute this software for any | |||
7 | * purpose with or without fee is hereby granted, provided that the above | |||
8 | * copyright notice and this permission notice appear in all copies. | |||
9 | * | |||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
17 | */ | |||
18 | ||||
19 | #ifndef _KERNEL1 | |||
20 | #include "kern_compat.h" | |||
21 | #else | |||
22 | #include <sys/param.h> | |||
23 | #include <sys/systm.h> | |||
24 | #include <sys/socket.h> | |||
25 | #include <sys/malloc.h> | |||
26 | #include <sys/queue.h> | |||
27 | #include <sys/domain.h> | |||
28 | #include <sys/srp.h> | |||
29 | #endif | |||
30 | ||||
31 | #include <net/rtable.h> | |||
32 | #include <net/route.h> | |||
33 | ||||
34 | /* | |||
35 | * Structures used by rtable_get() to retrieve the corresponding | |||
36 | * routing table for a given pair of ``af'' and ``rtableid''. | |||
37 | * | |||
38 | * Note that once allocated routing table heads are never freed. | |||
39 | * This way we do not need to reference count them. | |||
40 | * | |||
41 | * afmap rtmap/dommp | |||
42 | * ----------- --------- ----- | |||
43 | * | 0 |--------> | 0 | 0 | ... | 0 | Array mapping rtableid (=index) | |||
44 | * ----------- --------- ----- to rdomain/loopback (=value). | |||
45 | * | AF_INET |. | |||
46 | * ----------- `. .---------. .---------. | |||
47 | * ... `----> | rtable0 | ... | rtableN | Array of pointers for | |||
48 | * ----------- '---------' '---------' IPv4 routing tables | |||
49 | * | AF_MPLS | indexed by ``rtableid''. | |||
50 | * ----------- | |||
51 | */ | |||
52 | struct srp *afmap; | |||
53 | uint8_t af2idx[AF_MAX36+1]; /* To only allocate supported AF */ | |||
54 | uint8_t af2idx_max; | |||
55 | ||||
56 | /* Array of routing table pointers. */ | |||
57 | struct rtmap { | |||
58 | unsigned int limit; | |||
59 | void **tbl; | |||
60 | }; | |||
61 | ||||
62 | /* | |||
63 | * Array of rtableid -> rdomain mapping. | |||
64 | * | |||
65 | * Only used for the first index as described above. | |||
66 | */ | |||
67 | struct dommp { | |||
68 | unsigned int limit; | |||
69 | /* | |||
70 | * Array to get the routing domain and loopback interface related to | |||
71 | * a routing table. Format: | |||
72 | * | |||
73 | * 8 unused bits | 16 bits for loopback index | 8 bits for rdomain | |||
74 | */ | |||
75 | unsigned int *value; | |||
76 | }; | |||
77 | ||||
78 | unsigned int rtmap_limit = 0; | |||
79 | ||||
80 | void rtmap_init(void); | |||
81 | void rtmap_grow(unsigned int, sa_family_t); | |||
82 | void rtmap_dtor(void *, void *); | |||
83 | ||||
84 | struct srp_gc rtmap_gc = SRP_GC_INITIALIZER(rtmap_dtor, NULL){ (rtmap_dtor), (((void *)0)), { .refs = 1 } }; | |||
85 | ||||
86 | void rtable_init_backend(void); | |||
87 | void *rtable_alloc(unsigned int, unsigned int, unsigned int); | |||
88 | void *rtable_get(unsigned int, sa_family_t); | |||
89 | ||||
90 | void | |||
91 | rtmap_init(void) | |||
92 | { | |||
93 | const struct domain *dp; | |||
94 | int i; | |||
95 | ||||
96 | /* Start with a single table for every domain that requires it. */ | |||
97 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
98 | if (dp->dom_rtoffset == 0) | |||
99 | continue; | |||
100 | ||||
101 | rtmap_grow(1, dp->dom_family); | |||
102 | } | |||
103 | ||||
104 | /* Initialize the rtableid->rdomain mapping table. */ | |||
105 | rtmap_grow(1, 0); | |||
106 | ||||
107 | rtmap_limit = 1; | |||
108 | } | |||
109 | ||||
110 | /* | |||
111 | * Grow the size of the array of routing table for AF ``af'' to ``nlimit''. | |||
112 | */ | |||
113 | void | |||
114 | rtmap_grow(unsigned int nlimit, sa_family_t af) | |||
115 | { | |||
116 | struct rtmap *map, *nmap; | |||
117 | int i; | |||
118 | ||||
119 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c" , 119, "_kernel_lock_held()")); | |||
120 | ||||
121 | KASSERT(nlimit > rtmap_limit)((nlimit > rtmap_limit) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/net/rtable.c", 121, "nlimit > rtmap_limit" )); | |||
122 | ||||
123 | nmap = malloc(sizeof(*nmap), M_RTABLE5, M_WAITOK0x0001); | |||
124 | nmap->limit = nlimit; | |||
125 | nmap->tbl = mallocarray(nlimit, sizeof(*nmap[0].tbl), M_RTABLE5, | |||
126 | M_WAITOK0x0001|M_ZERO0x0008); | |||
127 | ||||
128 | map = srp_get_locked(&afmap[af2idx[af]]); | |||
129 | if (map != NULL((void *)0)) { | |||
130 | KASSERT(map->limit == rtmap_limit)((map->limit == rtmap_limit) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/net/rtable.c", 130, "map->limit == rtmap_limit" )); | |||
131 | ||||
132 | for (i = 0; i < map->limit; i++) | |||
133 | nmap->tbl[i] = map->tbl[i]; | |||
134 | } | |||
135 | ||||
136 | srp_update_locked(&rtmap_gc, &afmap[af2idx[af]], nmap); | |||
137 | } | |||
138 | ||||
139 | void | |||
140 | rtmap_dtor(void *null, void *xmap) | |||
141 | { | |||
142 | struct rtmap *map = xmap; | |||
143 | ||||
144 | /* | |||
145 | * doesn't need to be serialized since this is the last reference | |||
146 | * to this map. there's nothing to race against. | |||
147 | */ | |||
148 | free(map->tbl, M_RTABLE5, map->limit * sizeof(*map[0].tbl)); | |||
149 | free(map, M_RTABLE5, sizeof(*map)); | |||
150 | } | |||
151 | ||||
152 | void | |||
153 | rtable_init(void) | |||
154 | { | |||
155 | const struct domain *dp; | |||
156 | int i; | |||
157 | ||||
158 | KASSERT(sizeof(struct rtmap) == sizeof(struct dommp))((sizeof(struct rtmap) == sizeof(struct dommp)) ? (void)0 : __assert ("diagnostic ", "/usr/src/sys/net/rtable.c", 158, "sizeof(struct rtmap) == sizeof(struct dommp)" )); | |||
159 | ||||
160 | /* We use index 0 for the rtable/rdomain map. */ | |||
161 | af2idx_max = 1; | |||
162 | memset(af2idx, 0, sizeof(af2idx))__builtin_memset((af2idx), (0), (sizeof(af2idx))); | |||
163 | ||||
164 | /* | |||
165 | * Compute the maximum supported key length in case the routing | |||
166 | * table backend needs it. | |||
167 | */ | |||
168 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
169 | if (dp->dom_rtoffset == 0) | |||
170 | continue; | |||
171 | ||||
172 | af2idx[dp->dom_family] = af2idx_max++; | |||
173 | } | |||
174 | rtable_init_backend(); | |||
175 | ||||
176 | /* | |||
177 | * Allocate AF-to-id table now that we now how many AFs this | |||
178 | * kernel supports. | |||
179 | */ | |||
180 | afmap = mallocarray(af2idx_max + 1, sizeof(*afmap), M_RTABLE5, | |||
181 | M_WAITOK0x0001|M_ZERO0x0008); | |||
182 | ||||
183 | rtmap_init(); | |||
184 | ||||
185 | if (rtable_add(0) != 0) | |||
186 | panic("unable to create default routing table"); | |||
187 | } | |||
188 | ||||
189 | int | |||
190 | rtable_add(unsigned int id) | |||
191 | { | |||
192 | const struct domain *dp; | |||
193 | void *tbl; | |||
194 | struct rtmap *map; | |||
195 | struct dommp *dmm; | |||
196 | sa_family_t af; | |||
197 | unsigned int off, alen; | |||
198 | int i, error = 0; | |||
199 | ||||
200 | if (id > RT_TABLEID_MAX255) | |||
201 | return (EINVAL22); | |||
202 | ||||
203 | KERNEL_LOCK()_kernel_lock(); | |||
204 | ||||
205 | if (rtable_exists(id)) | |||
206 | goto out; | |||
207 | ||||
208 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
209 | if (dp->dom_rtoffset == 0) | |||
210 | continue; | |||
211 | ||||
212 | af = dp->dom_family; | |||
213 | off = dp->dom_rtoffset; | |||
214 | alen = dp->dom_maxplen; | |||
215 | ||||
216 | if (id >= rtmap_limit) | |||
217 | rtmap_grow(id + 1, af); | |||
218 | ||||
219 | tbl = rtable_alloc(id, alen, off); | |||
220 | if (tbl == NULL((void *)0)) { | |||
221 | error = ENOMEM12; | |||
222 | goto out; | |||
223 | } | |||
224 | ||||
225 | map = srp_get_locked(&afmap[af2idx[af]]); | |||
226 | map->tbl[id] = tbl; | |||
227 | } | |||
228 | ||||
229 | /* Reflect possible growth. */ | |||
230 | if (id >= rtmap_limit) { | |||
231 | rtmap_grow(id + 1, 0); | |||
232 | rtmap_limit = id + 1; | |||
233 | } | |||
234 | ||||
235 | /* Use main rtable/rdomain by default. */ | |||
236 | dmm = srp_get_locked(&afmap[0]); | |||
237 | dmm->value[id] = 0; | |||
238 | out: | |||
239 | KERNEL_UNLOCK()_kernel_unlock(); | |||
240 | ||||
241 | return (error); | |||
242 | } | |||
243 | ||||
244 | void * | |||
245 | rtable_get(unsigned int rtableid, sa_family_t af) | |||
246 | { | |||
247 | struct rtmap *map; | |||
248 | void *tbl = NULL((void *)0); | |||
249 | struct srp_ref sr; | |||
250 | ||||
251 | if (af >= nitems(af2idx)(sizeof((af2idx)) / sizeof((af2idx)[0])) || af2idx[af] == 0) | |||
252 | return (NULL((void *)0)); | |||
253 | ||||
254 | map = srp_enter(&sr, &afmap[af2idx[af]]); | |||
255 | if (rtableid < map->limit) | |||
256 | tbl = map->tbl[rtableid]; | |||
257 | srp_leave(&sr); | |||
258 | ||||
259 | return (tbl); | |||
260 | } | |||
261 | ||||
262 | int | |||
263 | rtable_exists(unsigned int rtableid) | |||
264 | { | |||
265 | const struct domain *dp; | |||
266 | void *tbl; | |||
267 | int i; | |||
268 | ||||
269 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
270 | if (dp->dom_rtoffset == 0) | |||
271 | continue; | |||
272 | ||||
273 | tbl = rtable_get(rtableid, dp->dom_family); | |||
274 | if (tbl != NULL((void *)0)) | |||
275 | return (1); | |||
276 | } | |||
277 | ||||
278 | return (0); | |||
279 | } | |||
280 | ||||
281 | int | |||
282 | rtable_empty(unsigned int rtableid) | |||
283 | { | |||
284 | const struct domain *dp; | |||
285 | int i; | |||
286 | struct art_root *tbl; | |||
287 | ||||
288 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
289 | if (dp->dom_rtoffset == 0) | |||
290 | continue; | |||
291 | ||||
292 | tbl = rtable_get(rtableid, dp->dom_family); | |||
293 | if (tbl == NULL((void *)0)) | |||
294 | continue; | |||
295 | if (tbl->ar_root.ref != NULL((void *)0)) | |||
296 | return (0); | |||
297 | } | |||
298 | ||||
299 | return (1); | |||
300 | } | |||
301 | ||||
302 | unsigned int | |||
303 | rtable_l2(unsigned int rtableid) | |||
304 | { | |||
305 | struct dommp *dmm; | |||
306 | unsigned int rdomain = 0; | |||
307 | struct srp_ref sr; | |||
308 | ||||
309 | dmm = srp_enter(&sr, &afmap[0]); | |||
310 | if (rtableid < dmm->limit) | |||
311 | rdomain = (dmm->value[rtableid] & RT_TABLEID_MASK0xff); | |||
312 | srp_leave(&sr); | |||
313 | ||||
314 | return (rdomain); | |||
315 | } | |||
316 | ||||
317 | unsigned int | |||
318 | rtable_loindex(unsigned int rtableid) | |||
319 | { | |||
320 | struct dommp *dmm; | |||
321 | unsigned int loifidx = 0; | |||
322 | struct srp_ref sr; | |||
323 | ||||
324 | dmm = srp_enter(&sr, &afmap[0]); | |||
325 | if (rtableid < dmm->limit) | |||
326 | loifidx = (dmm->value[rtableid] >> RT_TABLEID_BITS8); | |||
327 | srp_leave(&sr); | |||
328 | ||||
329 | return (loifidx); | |||
330 | } | |||
331 | ||||
332 | void | |||
333 | rtable_l2set(unsigned int rtableid, unsigned int rdomain, unsigned int loifidx) | |||
334 | { | |||
335 | struct dommp *dmm; | |||
336 | unsigned int value; | |||
337 | ||||
338 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c" , 338, "_kernel_lock_held()")); | |||
339 | ||||
340 | if (!rtable_exists(rtableid) || !rtable_exists(rdomain)) | |||
341 | return; | |||
342 | ||||
343 | value = (rdomain & RT_TABLEID_MASK0xff) | (loifidx << RT_TABLEID_BITS8); | |||
344 | ||||
345 | dmm = srp_get_locked(&afmap[0]); | |||
346 | dmm->value[rtableid] = value; | |||
347 | } | |||
348 | ||||
349 | ||||
350 | static inline uint8_t *satoaddr(struct art_root *, struct sockaddr *); | |||
351 | ||||
352 | int an_match(struct art_node *, struct sockaddr *, int); | |||
353 | void rtentry_ref(void *, void *); | |||
354 | void rtentry_unref(void *, void *); | |||
355 | ||||
356 | void rtable_mpath_insert(struct art_node *, struct rtentry *); | |||
357 | ||||
358 | struct srpl_rc rt_rc = SRPL_RC_INITIALIZER(rtentry_ref, rtentry_unref, NULL){ rtentry_ref, { (rtentry_unref), (((void *)0)), { .refs = 1 } } }; | |||
359 | ||||
360 | void | |||
361 | rtable_init_backend(void) | |||
362 | { | |||
363 | art_init(); | |||
364 | } | |||
365 | ||||
366 | void * | |||
367 | rtable_alloc(unsigned int rtableid, unsigned int alen, unsigned int off) | |||
368 | { | |||
369 | return (art_alloc(rtableid, alen, off)); | |||
370 | } | |||
371 | ||||
372 | int | |||
373 | rtable_setsource(unsigned int rtableid, int af, struct sockaddr *src) | |||
374 | { | |||
375 | struct art_root *ar; | |||
376 | ||||
377 | if ((ar = rtable_get(rtableid, af)) == NULL((void *)0)) | |||
378 | return (EAFNOSUPPORT47); | |||
379 | ||||
380 | ar->source = src; | |||
381 | ||||
382 | return (0); | |||
383 | } | |||
384 | ||||
385 | struct sockaddr * | |||
386 | rtable_getsource(unsigned int rtableid, int af) | |||
387 | { | |||
388 | struct art_root *ar; | |||
389 | ||||
390 | ar = rtable_get(rtableid, af); | |||
391 | if (ar == NULL((void *)0)) | |||
392 | return (NULL((void *)0)); | |||
393 | ||||
394 | return (ar->source); | |||
395 | } | |||
396 | ||||
397 | void | |||
398 | rtable_clearsource(unsigned int rtableid, struct sockaddr *src) | |||
399 | { | |||
400 | struct sockaddr *addr; | |||
401 | ||||
402 | addr = rtable_getsource(rtableid, src->sa_family); | |||
403 | if (addr && (addr->sa_len == src->sa_len)) { | |||
404 | if (memcmp(src, addr, addr->sa_len)__builtin_memcmp((src), (addr), (addr->sa_len)) == 0) { | |||
405 | rtable_setsource(rtableid, src->sa_family, NULL((void *)0)); | |||
406 | } | |||
407 | } | |||
408 | } | |||
409 | ||||
410 | struct rtentry * | |||
411 | rtable_lookup(unsigned int rtableid, struct sockaddr *dst, | |||
412 | struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio) | |||
413 | { | |||
414 | struct art_root *ar; | |||
415 | struct art_node *an; | |||
416 | struct rtentry *rt = NULL((void *)0); | |||
417 | struct srp_ref sr, nsr; | |||
418 | uint8_t *addr; | |||
419 | int plen; | |||
420 | ||||
421 | ar = rtable_get(rtableid, dst->sa_family); | |||
422 | if (ar == NULL((void *)0)) | |||
423 | return (NULL((void *)0)); | |||
424 | ||||
425 | addr = satoaddr(ar, dst); | |||
426 | ||||
427 | /* No need for a perfect match. */ | |||
428 | if (mask == NULL((void *)0)) { | |||
429 | an = art_match(ar, addr, &nsr); | |||
430 | if (an == NULL((void *)0)) | |||
431 | goto out; | |||
432 | } else { | |||
433 | plen = rtable_satoplen(dst->sa_family, mask); | |||
434 | if (plen == -1) | |||
435 | return (NULL((void *)0)); | |||
436 | ||||
437 | an = art_lookup(ar, addr, plen, &nsr); | |||
438 | ||||
439 | /* Make sure we've got a perfect match. */ | |||
440 | if (!an_match(an, dst, plen)) | |||
441 | goto out; | |||
442 | } | |||
443 | ||||
444 | SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next)for ((rt) = srp_enter((&sr), &(&an->an_pointer .an__rtlist)->sl_head); (rt) != ((void *)0); (rt) = srp_follow ((&sr), &(rt)->rt_next.se_next)) { | |||
445 | if (prio != RTP_ANY64 && | |||
446 | (rt->rt_priority & RTP_MASK0x7f) != (prio & RTP_MASK0x7f)) | |||
447 | continue; | |||
448 | ||||
449 | if (gateway == NULL((void *)0)) | |||
450 | break; | |||
451 | ||||
452 | if (rt->rt_gateway->sa_len == gateway->sa_len && | |||
453 | memcmp(rt->rt_gateway, gateway, gateway->sa_len)__builtin_memcmp((rt->rt_gateway), (gateway), (gateway-> sa_len)) == 0) | |||
454 | break; | |||
455 | } | |||
456 | if (rt != NULL((void *)0)) | |||
457 | rtref(rt); | |||
458 | ||||
459 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
460 | out: | |||
461 | srp_leave(&nsr); | |||
462 | ||||
463 | return (rt); | |||
464 | } | |||
465 | ||||
466 | struct rtentry * | |||
467 | rtable_match(unsigned int rtableid, struct sockaddr *dst, uint32_t *src) | |||
468 | { | |||
469 | struct art_root *ar; | |||
470 | struct art_node *an; | |||
471 | struct rtentry *rt = NULL((void *)0); | |||
472 | struct srp_ref sr, nsr; | |||
473 | uint8_t *addr; | |||
474 | int hash; | |||
475 | ||||
476 | ar = rtable_get(rtableid, dst->sa_family); | |||
477 | if (ar == NULL((void *)0)) | |||
| ||||
478 | return (NULL((void *)0)); | |||
479 | ||||
480 | addr = satoaddr(ar, dst); | |||
481 | ||||
482 | an = art_match(ar, addr, &nsr); | |||
483 | if (an == NULL((void *)0)) | |||
484 | goto out; | |||
485 | ||||
486 | rt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist) ->sl_head); | |||
487 | rtref(rt); | |||
488 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
489 | ||||
490 | /* Gateway selection by Hash-Threshold (RFC 2992) */ | |||
491 | if ((hash = rt_hash(rt, dst, src)) != -1) { | |||
492 | struct rtentry *mrt; | |||
493 | int threshold, npaths = 0; | |||
494 | ||||
495 | KASSERT(hash <= 0xffff)((hash <= 0xffff) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c" , 495, "hash <= 0xffff")); | |||
496 | ||||
497 | SRPL_FOREACH(mrt, &sr, &an->an_rtlist, rt_next)for ((mrt) = srp_enter((&sr), &(&an->an_pointer .an__rtlist)->sl_head); (mrt) != ((void *)0); (mrt) = srp_follow ((&sr), &(mrt)->rt_next.se_next)) { | |||
498 | /* Only count nexthops with the same priority. */ | |||
499 | if (mrt->rt_priority == rt->rt_priority) | |||
500 | npaths++; | |||
501 | } | |||
502 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
503 | ||||
504 | threshold = (0xffff / npaths) + 1; | |||
| ||||
505 | ||||
506 | /* | |||
507 | * we have no protection against concurrent modification of the | |||
508 | * route list attached to the node, so we won't necessarily | |||
509 | * have the same number of routes. for most modifications, | |||
510 | * we'll pick a route that we wouldn't have if we only saw the | |||
511 | * list before or after the change. if we were going to use | |||
512 | * the last available route, but it got removed, we'll hit | |||
513 | * the end of the list and then pick the first route. | |||
514 | */ | |||
515 | ||||
516 | mrt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist) ->sl_head); | |||
517 | while (hash > threshold && mrt != NULL((void *)0)) { | |||
518 | if (mrt->rt_priority == rt->rt_priority) | |||
519 | hash -= threshold; | |||
520 | mrt = SRPL_FOLLOW(&sr, mrt, rt_next)srp_follow((&sr), &(mrt)->rt_next.se_next); | |||
521 | } | |||
522 | ||||
523 | if (mrt != NULL((void *)0)) { | |||
524 | rtref(mrt); | |||
525 | rtfree(rt); | |||
526 | rt = mrt; | |||
527 | } | |||
528 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
529 | } | |||
530 | out: | |||
531 | srp_leave(&nsr); | |||
532 | return (rt); | |||
533 | } | |||
534 | ||||
535 | int | |||
536 | rtable_insert(unsigned int rtableid, struct sockaddr *dst, | |||
537 | struct sockaddr *mask, struct sockaddr *gateway, uint8_t prio, | |||
538 | struct rtentry *rt) | |||
539 | { | |||
540 | struct rtentry *mrt; | |||
541 | struct srp_ref sr; | |||
542 | struct art_root *ar; | |||
543 | struct art_node *an, *prev; | |||
544 | uint8_t *addr; | |||
545 | int plen; | |||
546 | unsigned int rt_flags; | |||
547 | int error = 0; | |||
548 | ||||
549 | ar = rtable_get(rtableid, dst->sa_family); | |||
550 | if (ar == NULL((void *)0)) | |||
551 | return (EAFNOSUPPORT47); | |||
552 | ||||
553 | addr = satoaddr(ar, dst); | |||
554 | plen = rtable_satoplen(dst->sa_family, mask); | |||
555 | if (plen == -1) | |||
556 | return (EINVAL22); | |||
557 | ||||
558 | rtref(rt); /* guarantee rtfree won't do anything during insert */ | |||
559 | rw_enter_write(&ar->ar_lock); | |||
560 | ||||
561 | /* Do not permit exactly the same dst/mask/gw pair. */ | |||
562 | an = art_lookup(ar, addr, plen, &sr); | |||
563 | srp_leave(&sr); /* an can't go away while we have the lock */ | |||
564 | if (an_match(an, dst, plen)) { | |||
565 | struct rtentry *mrt; | |||
566 | int mpathok = ISSET(rt->rt_flags, RTF_MPATH)((rt->rt_flags) & (0x40000)); | |||
567 | ||||
568 | SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next)for ((mrt) = srp_get_locked(&(&an->an_pointer.an__rtlist )->sl_head); (mrt) != ((void *)0); (mrt) = srp_get_locked( &((mrt))->rt_next.se_next)) { | |||
569 | if (prio != RTP_ANY64 && | |||
570 | (mrt->rt_priority & RTP_MASK0x7f) != (prio & RTP_MASK0x7f)) | |||
571 | continue; | |||
572 | ||||
573 | if (!mpathok || | |||
574 | (mrt->rt_gateway->sa_len == gateway->sa_len && | |||
575 | !memcmp(mrt->rt_gateway, gateway, gateway->sa_len)__builtin_memcmp((mrt->rt_gateway), (gateway), (gateway-> sa_len)))){ | |||
576 | error = EEXIST17; | |||
577 | goto leave; | |||
578 | } | |||
579 | } | |||
580 | } | |||
581 | ||||
582 | an = art_get(dst, plen); | |||
583 | if (an == NULL((void *)0)) { | |||
584 | error = ENOBUFS55; | |||
585 | goto leave; | |||
586 | } | |||
587 | ||||
588 | /* prepare for immediate operation if insert succeeds */ | |||
589 | rt_flags = rt->rt_flags; | |||
590 | rt->rt_flags &= ~RTF_MPATH0x40000; | |||
591 | rt->rt_dest = dst; | |||
592 | rt->rt_plen = plen; | |||
593 | SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next)do { void *head; srp_init(&(rt)->rt_next.se_next); head = srp_get_locked(&(&an->an_pointer.an__rtlist)-> sl_head); if (head != ((void *)0)) { (&rt_rc)->srpl_ref (&(&rt_rc)->srpl_gc.srp_gc_cookie, head); srp_update_locked (&(&rt_rc)->srpl_gc, &(rt)->rt_next.se_next , head); } (&rt_rc)->srpl_ref(&(&rt_rc)->srpl_gc .srp_gc_cookie, rt); srp_update_locked(&(&rt_rc)-> srpl_gc, &(&an->an_pointer.an__rtlist)->sl_head , (rt)); } while (0); | |||
594 | ||||
595 | prev = art_insert(ar, an, addr, plen); | |||
596 | if (prev != an) { | |||
597 | SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0) | |||
598 | rt_next)do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0); | |||
599 | rt->rt_flags = rt_flags; | |||
600 | art_put(an); | |||
601 | ||||
602 | if (prev == NULL((void *)0)) { | |||
603 | error = ESRCH3; | |||
604 | goto leave; | |||
605 | } | |||
606 | ||||
607 | an = prev; | |||
608 | ||||
609 | mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head ); | |||
610 | KASSERT(mrt != NULL)((mrt != ((void *)0)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c" , 610, "mrt != NULL")); | |||
611 | KASSERT((rt->rt_flags & RTF_MPATH) || mrt->rt_priority != prio)(((rt->rt_flags & 0x40000) || mrt->rt_priority != prio ) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c" , 611, "(rt->rt_flags & RTF_MPATH) || mrt->rt_priority != prio" )); | |||
612 | ||||
613 | /* | |||
614 | * An ART node with the same destination/netmask already | |||
615 | * exists, MPATH conflict must have been already checked. | |||
616 | */ | |||
617 | if (rt->rt_flags & RTF_MPATH0x40000) { | |||
618 | /* | |||
619 | * Only keep the RTF_MPATH flag if two routes have | |||
620 | * the same gateway. | |||
621 | */ | |||
622 | rt->rt_flags &= ~RTF_MPATH0x40000; | |||
623 | SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next)for ((mrt) = srp_get_locked(&(&an->an_pointer.an__rtlist )->sl_head); (mrt) != ((void *)0); (mrt) = srp_get_locked( &((mrt))->rt_next.se_next)) { | |||
624 | if (mrt->rt_priority == prio) { | |||
625 | mrt->rt_flags |= RTF_MPATH0x40000; | |||
626 | rt->rt_flags |= RTF_MPATH0x40000; | |||
627 | } | |||
628 | } | |||
629 | } | |||
630 | ||||
631 | /* Put newly inserted entry at the right place. */ | |||
632 | rtable_mpath_insert(an, rt); | |||
633 | } | |||
634 | leave: | |||
635 | rw_exit_write(&ar->ar_lock); | |||
636 | rtfree(rt); | |||
637 | return (error); | |||
638 | } | |||
639 | ||||
640 | int | |||
641 | rtable_delete(unsigned int rtableid, struct sockaddr *dst, | |||
642 | struct sockaddr *mask, struct rtentry *rt) | |||
643 | { | |||
644 | struct art_root *ar; | |||
645 | struct art_node *an; | |||
646 | struct srp_ref sr; | |||
647 | uint8_t *addr; | |||
648 | int plen; | |||
649 | struct rtentry *mrt; | |||
650 | int npaths = 0; | |||
651 | int error = 0; | |||
652 | ||||
653 | ar = rtable_get(rtableid, dst->sa_family); | |||
654 | if (ar == NULL((void *)0)) | |||
655 | return (EAFNOSUPPORT47); | |||
656 | ||||
657 | addr = satoaddr(ar, dst); | |||
658 | plen = rtable_satoplen(dst->sa_family, mask); | |||
659 | if (plen == -1) | |||
660 | return (EINVAL22); | |||
661 | ||||
662 | rtref(rt); /* guarantee rtfree won't do anything under ar_lock */ | |||
663 | rw_enter_write(&ar->ar_lock); | |||
664 | an = art_lookup(ar, addr, plen, &sr); | |||
665 | srp_leave(&sr); /* an can't go away while we have the lock */ | |||
666 | ||||
667 | /* Make sure we've got a perfect match. */ | |||
668 | if (!an_match(an, dst, plen)) { | |||
669 | error = ESRCH3; | |||
670 | goto leave; | |||
671 | } | |||
672 | ||||
673 | /* | |||
674 | * If other multipath route entries are still attached to | |||
675 | * this ART node we only have to unlink it. | |||
676 | */ | |||
677 | SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next)for ((mrt) = srp_get_locked(&(&an->an_pointer.an__rtlist )->sl_head); (mrt) != ((void *)0); (mrt) = srp_get_locked( &((mrt))->rt_next.se_next)) | |||
678 | npaths++; | |||
679 | ||||
680 | if (npaths > 1) { | |||
681 | KASSERT(rt->rt_refcnt >= 1)((rt->rt_refcnt >= 1) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/net/rtable.c", 681, "rt->rt_refcnt >= 1" )); | |||
682 | SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0) | |||
683 | rt_next)do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0); | |||
684 | ||||
685 | mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head ); | |||
686 | if (npaths == 2) | |||
687 | mrt->rt_flags &= ~RTF_MPATH0x40000; | |||
688 | ||||
689 | goto leave; | |||
690 | } | |||
691 | ||||
692 | if (art_delete(ar, an, addr, plen) == NULL((void *)0)) | |||
693 | panic("art_delete failed to find node %p", an); | |||
694 | ||||
695 | KASSERT(rt->rt_refcnt >= 1)((rt->rt_refcnt >= 1) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/net/rtable.c", 695, "rt->rt_refcnt >= 1" )); | |||
696 | SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next)do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0); | |||
697 | art_put(an); | |||
698 | ||||
699 | leave: | |||
700 | rw_exit_write(&ar->ar_lock); | |||
701 | rtfree(rt); | |||
702 | ||||
703 | return (error); | |||
704 | } | |||
705 | ||||
706 | struct rtable_walk_cookie { | |||
707 | int (*rwc_func)(struct rtentry *, void *, unsigned int); | |||
708 | void *rwc_arg; | |||
709 | struct rtentry **rwc_prt; | |||
710 | unsigned int rwc_rid; | |||
711 | }; | |||
712 | ||||
713 | /* | |||
714 | * Helper for rtable_walk to keep the ART code free from any "struct rtentry". | |||
715 | */ | |||
716 | int | |||
717 | rtable_walk_helper(struct art_node *an, void *xrwc) | |||
718 | { | |||
719 | struct srp_ref sr; | |||
720 | struct rtable_walk_cookie *rwc = xrwc; | |||
721 | struct rtentry *rt; | |||
722 | int error = 0; | |||
723 | ||||
724 | SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next)for ((rt) = srp_enter((&sr), &(&an->an_pointer .an__rtlist)->sl_head); (rt) != ((void *)0); (rt) = srp_follow ((&sr), &(rt)->rt_next.se_next)) { | |||
725 | error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid); | |||
726 | if (error != 0) | |||
727 | break; | |||
728 | } | |||
729 | if (rwc->rwc_prt != NULL((void *)0) && rt != NULL((void *)0)) { | |||
730 | rtref(rt); | |||
731 | *rwc->rwc_prt = rt; | |||
732 | } | |||
733 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
734 | ||||
735 | return (error); | |||
736 | } | |||
737 | ||||
738 | int | |||
739 | rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt, | |||
740 | int (*func)(struct rtentry *, void *, unsigned int), void *arg) | |||
741 | { | |||
742 | struct art_root *ar; | |||
743 | struct rtable_walk_cookie rwc; | |||
744 | int error; | |||
745 | ||||
746 | ar = rtable_get(rtableid, af); | |||
747 | if (ar == NULL((void *)0)) | |||
748 | return (EAFNOSUPPORT47); | |||
749 | ||||
750 | rwc.rwc_func = func; | |||
751 | rwc.rwc_arg = arg; | |||
752 | rwc.rwc_prt = prt; | |||
753 | rwc.rwc_rid = rtableid; | |||
754 | ||||
755 | error = art_walk(ar, rtable_walk_helper, &rwc); | |||
756 | ||||
757 | return (error); | |||
758 | } | |||
759 | ||||
760 | struct rtentry * | |||
761 | rtable_iterate(struct rtentry *rt0) | |||
762 | { | |||
763 | struct rtentry *rt = NULL((void *)0); | |||
764 | struct srp_ref sr; | |||
765 | ||||
766 | rt = SRPL_NEXT(&sr, rt0, rt_next)srp_enter((&sr), &(rt0)->rt_next.se_next); | |||
767 | if (rt != NULL((void *)0)) | |||
768 | rtref(rt); | |||
769 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
770 | rtfree(rt0); | |||
771 | return (rt); | |||
772 | } | |||
773 | ||||
774 | int | |||
775 | rtable_mpath_capable(unsigned int rtableid, sa_family_t af) | |||
776 | { | |||
777 | return (1); | |||
778 | } | |||
779 | ||||
780 | int | |||
781 | rtable_mpath_reprio(unsigned int rtableid, struct sockaddr *dst, | |||
782 | int plen, uint8_t prio, struct rtentry *rt) | |||
783 | { | |||
784 | struct art_root *ar; | |||
785 | struct art_node *an; | |||
786 | struct srp_ref sr; | |||
787 | uint8_t *addr; | |||
788 | int error = 0; | |||
789 | ||||
790 | ar = rtable_get(rtableid, dst->sa_family); | |||
791 | if (ar == NULL((void *)0)) | |||
792 | return (EAFNOSUPPORT47); | |||
793 | ||||
794 | addr = satoaddr(ar, dst); | |||
795 | ||||
796 | rw_enter_write(&ar->ar_lock); | |||
797 | an = art_lookup(ar, addr, plen, &sr); | |||
798 | srp_leave(&sr); /* an can't go away while we have the lock */ | |||
799 | ||||
800 | /* Make sure we've got a perfect match. */ | |||
801 | if (!an_match(an, dst, plen)) { | |||
802 | error = ESRCH3; | |||
803 | } else if (SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head ) == rt && | |||
804 | SRPL_NEXT_LOCKED(rt, rt_next)srp_get_locked(&(rt)->rt_next.se_next) == NULL((void *)0)) { | |||
805 | /* | |||
806 | * If there's only one entry on the list do not go | |||
807 | * through an insert/remove cycle. This is done to | |||
808 | * guarantee that ``an->an_rtlist'' is never empty | |||
809 | * when a node is in the tree. | |||
810 | */ | |||
811 | rt->rt_priority = prio; | |||
812 | } else { | |||
813 | rtref(rt); /* keep rt alive in between remove and insert */ | |||
814 | SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist,do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0) | |||
815 | rt, rtentry, rt_next)do { struct srp *ref; struct rtentry *c, *n; ref = &(& an->an_pointer.an__rtlist)->sl_head; while ((c = srp_get_locked (ref)) != (rt)) ref = &c->rt_next.se_next; n = srp_get_locked (&(c)->rt_next.se_next); if (n != ((void *)0)) (&rt_rc )->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie, n ); srp_update_locked(&(&rt_rc)->srpl_gc, ref, n); srp_update_locked (&(&rt_rc)->srpl_gc, &c->rt_next.se_next, ( (void *)0)); } while (0); | |||
816 | rt->rt_priority = prio; | |||
817 | rtable_mpath_insert(an, rt); | |||
818 | rtfree(rt); | |||
819 | error = EAGAIN35; | |||
820 | } | |||
821 | rw_exit_write(&ar->ar_lock); | |||
822 | ||||
823 | return (error); | |||
824 | } | |||
825 | ||||
826 | void | |||
827 | rtable_mpath_insert(struct art_node *an, struct rtentry *rt) | |||
828 | { | |||
829 | struct rtentry *mrt, *prt = NULL((void *)0); | |||
830 | uint8_t prio = rt->rt_priority; | |||
831 | ||||
832 | if ((mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head )) == NULL((void *)0)) { | |||
833 | SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next)do { void *head; srp_init(&(rt)->rt_next.se_next); head = srp_get_locked(&(&an->an_pointer.an__rtlist)-> sl_head); if (head != ((void *)0)) { (&rt_rc)->srpl_ref (&(&rt_rc)->srpl_gc.srp_gc_cookie, head); srp_update_locked (&(&rt_rc)->srpl_gc, &(rt)->rt_next.se_next , head); } (&rt_rc)->srpl_ref(&(&rt_rc)->srpl_gc .srp_gc_cookie, rt); srp_update_locked(&(&rt_rc)-> srpl_gc, &(&an->an_pointer.an__rtlist)->sl_head , (rt)); } while (0); | |||
834 | return; | |||
835 | } | |||
836 | ||||
837 | /* Iterate until we find the route to be placed after ``rt''. */ | |||
838 | while (mrt->rt_priority <= prio && SRPL_NEXT_LOCKED(mrt, rt_next)srp_get_locked(&(mrt)->rt_next.se_next)) { | |||
839 | prt = mrt; | |||
840 | mrt = SRPL_NEXT_LOCKED(mrt, rt_next)srp_get_locked(&(mrt)->rt_next.se_next); | |||
841 | } | |||
842 | ||||
843 | if (mrt->rt_priority <= prio) { | |||
844 | SRPL_INSERT_AFTER_LOCKED(&rt_rc, mrt, rt, rt_next)do { void *next; srp_init(&(rt)->rt_next.se_next); next = srp_get_locked(&(mrt)->rt_next.se_next); if (next != ((void *)0)) { (&rt_rc)->srpl_ref(&(&rt_rc)-> srpl_gc.srp_gc_cookie, next); srp_update_locked(&(&rt_rc )->srpl_gc, &(rt)->rt_next.se_next, next); } (& rt_rc)->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie , rt); srp_update_locked(&(&rt_rc)->srpl_gc, & (mrt)->rt_next.se_next, (rt)); } while (0); | |||
845 | } else if (prt != NULL((void *)0)) { | |||
846 | SRPL_INSERT_AFTER_LOCKED(&rt_rc, prt, rt, rt_next)do { void *next; srp_init(&(rt)->rt_next.se_next); next = srp_get_locked(&(prt)->rt_next.se_next); if (next != ((void *)0)) { (&rt_rc)->srpl_ref(&(&rt_rc)-> srpl_gc.srp_gc_cookie, next); srp_update_locked(&(&rt_rc )->srpl_gc, &(rt)->rt_next.se_next, next); } (& rt_rc)->srpl_ref(&(&rt_rc)->srpl_gc.srp_gc_cookie , rt); srp_update_locked(&(&rt_rc)->srpl_gc, & (prt)->rt_next.se_next, (rt)); } while (0); | |||
847 | } else { | |||
848 | SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next)do { void *head; srp_init(&(rt)->rt_next.se_next); head = srp_get_locked(&(&an->an_pointer.an__rtlist)-> sl_head); if (head != ((void *)0)) { (&rt_rc)->srpl_ref (&(&rt_rc)->srpl_gc.srp_gc_cookie, head); srp_update_locked (&(&rt_rc)->srpl_gc, &(rt)->rt_next.se_next , head); } (&rt_rc)->srpl_ref(&(&rt_rc)->srpl_gc .srp_gc_cookie, rt); srp_update_locked(&(&rt_rc)-> srpl_gc, &(&an->an_pointer.an__rtlist)->sl_head , (rt)); } while (0); | |||
849 | } | |||
850 | } | |||
851 | ||||
852 | /* | |||
853 | * Returns 1 if ``an'' perfectly matches (``dst'', ``plen''), 0 otherwise. | |||
854 | */ | |||
855 | int | |||
856 | an_match(struct art_node *an, struct sockaddr *dst, int plen) | |||
857 | { | |||
858 | struct rtentry *rt; | |||
859 | struct srp_ref sr; | |||
860 | int match; | |||
861 | ||||
862 | if (an == NULL((void *)0) || an->an_plen != plen) | |||
863 | return (0); | |||
864 | ||||
865 | rt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist) ->sl_head); | |||
866 | match = (memcmp(rt->rt_dest, dst, dst->sa_len)__builtin_memcmp((rt->rt_dest), (dst), (dst->sa_len)) == 0); | |||
867 | SRPL_LEAVE(&sr)srp_leave((&sr)); | |||
868 | ||||
869 | return (match); | |||
870 | } | |||
871 | ||||
872 | void | |||
873 | rtentry_ref(void *null, void *xrt) | |||
874 | { | |||
875 | struct rtentry *rt = xrt; | |||
876 | ||||
877 | rtref(rt); | |||
878 | } | |||
879 | ||||
880 | void | |||
881 | rtentry_unref(void *null, void *xrt) | |||
882 | { | |||
883 | struct rtentry *rt = xrt; | |||
884 | ||||
885 | rtfree(rt); | |||
886 | } | |||
887 | ||||
888 | /* | |||
889 | * Return a pointer to the address (key). This is an heritage from the | |||
890 | * BSD radix tree needed to skip the non-address fields from the flavor | |||
891 | * of "struct sockaddr" used by this routing table. | |||
892 | */ | |||
893 | static inline uint8_t * | |||
894 | satoaddr(struct art_root *at, struct sockaddr *sa) | |||
895 | { | |||
896 | return (((uint8_t *)sa) + at->ar_off); | |||
897 | } | |||
898 | ||||
899 | /* | |||
900 | * Return the prefix length of a mask. | |||
901 | */ | |||
902 | int | |||
903 | rtable_satoplen(sa_family_t af, struct sockaddr *mask) | |||
904 | { | |||
905 | const struct domain *dp; | |||
906 | uint8_t *ap, *ep; | |||
907 | int mlen, plen = 0; | |||
908 | int i; | |||
909 | ||||
910 | for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) { | |||
911 | if (dp->dom_rtoffset == 0) | |||
912 | continue; | |||
913 | ||||
914 | if (af == dp->dom_family) | |||
915 | break; | |||
916 | } | |||
917 | if (dp == NULL((void *)0)) | |||
918 | return (-1); | |||
919 | ||||
920 | /* Host route */ | |||
921 | if (mask == NULL((void *)0)) | |||
922 | return (dp->dom_maxplen); | |||
923 | ||||
924 | mlen = mask->sa_len; | |||
925 | ||||
926 | /* Default route */ | |||
927 | if (mlen == 0) | |||
928 | return (0); | |||
929 | ||||
930 | ap = (uint8_t *)((uint8_t *)mask) + dp->dom_rtoffset; | |||
931 | ep = (uint8_t *)((uint8_t *)mask) + mlen; | |||
932 | if (ap > ep) | |||
933 | return (-1); | |||
934 | ||||
935 | /* Trim trailing zeroes. */ | |||
936 | while (ap < ep && ep[-1] == 0) | |||
937 | ep--; | |||
938 | ||||
939 | if (ap == ep) | |||
940 | return (0); | |||
941 | ||||
942 | /* "Beauty" adapted from sbin/route/show.c ... */ | |||
943 | while (ap < ep) { | |||
944 | switch (*ap++) { | |||
945 | case 0xff: | |||
946 | plen += 8; | |||
947 | break; | |||
948 | case 0xfe: | |||
949 | plen += 7; | |||
950 | goto out; | |||
951 | case 0xfc: | |||
952 | plen += 6; | |||
953 | goto out; | |||
954 | case 0xf8: | |||
955 | plen += 5; | |||
956 | goto out; | |||
957 | case 0xf0: | |||
958 | plen += 4; | |||
959 | goto out; | |||
960 | case 0xe0: | |||
961 | plen += 3; | |||
962 | goto out; | |||
963 | case 0xc0: | |||
964 | plen += 2; | |||
965 | goto out; | |||
966 | case 0x80: | |||
967 | plen += 1; | |||
968 | goto out; | |||
969 | default: | |||
970 | /* Non contiguous mask. */ | |||
971 | return (-1); | |||
972 | } | |||
973 | } | |||
974 | ||||
975 | out: | |||
976 | if (plen > dp->dom_maxplen || ap != ep) | |||
977 | return -1; | |||
978 | ||||
979 | return (plen); | |||
980 | } |