Bug Summary

File:net/rtable.c
Warning:line 515, column 23
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name rtable.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -target-feature +retpoline-external-thunk -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/legacy-dpm -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu13 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/inc -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/inc/pmfw_if -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D SUSPEND -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/scan/2024-01-11-110808-61670-1 -x c /usr/src/sys/net/rtable.c
1/* $OpenBSD: rtable.c,v 1.85 2023/11/12 17:51:40 bluhm 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 */
52struct srp *afmap;
53uint8_t af2idx[AF_MAX36+1]; /* To only allocate supported AF */
54uint8_t af2idx_max;
55
56/* Array of routing table pointers. */
57struct 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 */
67struct 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
78unsigned int rtmap_limit = 0;
79
80void rtmap_init(void);
81void rtmap_grow(unsigned int, sa_family_t);
82void rtmap_dtor(void *, void *);
83
84struct srp_gc rtmap_gc = SRP_GC_INITIALIZER(rtmap_dtor, NULL){ (rtmap_dtor), (((void *)0)), { .r_refs = 1, .r_traceidx = 0
} }
;
85
86void rtable_init_backend(void);
87void *rtable_alloc(unsigned int, unsigned int, unsigned int);
88void *rtable_get(unsigned int, sa_family_t);
89
90void
91rtmap_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 */
113void
114rtmap_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
139void
140rtmap_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
152void
153rtable_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 rt_timer_init();
189}
190
191int
192rtable_add(unsigned int id)
193{
194 const struct domain *dp;
195 void *tbl;
196 struct rtmap *map;
197 struct dommp *dmm;
198 sa_family_t af;
199 unsigned int off, alen;
200 int i, error = 0;
201
202 if (id > RT_TABLEID_MAX255)
203 return (EINVAL22);
204
205 KERNEL_LOCK()_kernel_lock();
206
207 if (rtable_exists(id))
208 goto out;
209
210 for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) {
211 if (dp->dom_rtoffset == 0)
212 continue;
213
214 af = dp->dom_family;
215 off = dp->dom_rtoffset;
216 alen = dp->dom_maxplen;
217
218 if (id >= rtmap_limit)
219 rtmap_grow(id + 1, af);
220
221 tbl = rtable_alloc(id, alen, off);
222 if (tbl == NULL((void *)0)) {
223 error = ENOMEM12;
224 goto out;
225 }
226
227 map = srp_get_locked(&afmap[af2idx[af]]);
228 map->tbl[id] = tbl;
229 }
230
231 /* Reflect possible growth. */
232 if (id >= rtmap_limit) {
233 rtmap_grow(id + 1, 0);
234 rtmap_limit = id + 1;
235 }
236
237 /* Use main rtable/rdomain by default. */
238 dmm = srp_get_locked(&afmap[0]);
239 dmm->value[id] = 0;
240out:
241 KERNEL_UNLOCK()_kernel_unlock();
242
243 return (error);
244}
245
246void *
247rtable_get(unsigned int rtableid, sa_family_t af)
248{
249 struct rtmap *map;
250 void *tbl = NULL((void *)0);
251 struct srp_ref sr;
252
253 if (af >= nitems(af2idx)(sizeof((af2idx)) / sizeof((af2idx)[0])) || af2idx[af] == 0)
254 return (NULL((void *)0));
255
256 map = srp_enter(&sr, &afmap[af2idx[af]]);
257 if (rtableid < map->limit)
258 tbl = map->tbl[rtableid];
259 srp_leave(&sr);
260
261 return (tbl);
262}
263
264int
265rtable_exists(unsigned int rtableid)
266{
267 const struct domain *dp;
268 void *tbl;
269 int i;
270
271 for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) {
272 if (dp->dom_rtoffset == 0)
273 continue;
274
275 tbl = rtable_get(rtableid, dp->dom_family);
276 if (tbl != NULL((void *)0))
277 return (1);
278 }
279
280 return (0);
281}
282
283int
284rtable_empty(unsigned int rtableid)
285{
286 const struct domain *dp;
287 int i;
288 struct art_root *tbl;
289
290 for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) {
291 if (dp->dom_rtoffset == 0)
292 continue;
293
294 tbl = rtable_get(rtableid, dp->dom_family);
295 if (tbl == NULL((void *)0))
296 continue;
297 if (tbl->ar_root.ref != NULL((void *)0))
298 return (0);
299 }
300
301 return (1);
302}
303
304unsigned int
305rtable_l2(unsigned int rtableid)
306{
307 struct dommp *dmm;
308 unsigned int rdomain = 0;
309 struct srp_ref sr;
310
311 dmm = srp_enter(&sr, &afmap[0]);
312 if (rtableid < dmm->limit)
313 rdomain = (dmm->value[rtableid] & RT_TABLEID_MASK0xff);
314 srp_leave(&sr);
315
316 return (rdomain);
317}
318
319unsigned int
320rtable_loindex(unsigned int rtableid)
321{
322 struct dommp *dmm;
323 unsigned int loifidx = 0;
324 struct srp_ref sr;
325
326 dmm = srp_enter(&sr, &afmap[0]);
327 if (rtableid < dmm->limit)
328 loifidx = (dmm->value[rtableid] >> RT_TABLEID_BITS8);
329 srp_leave(&sr);
330
331 return (loifidx);
332}
333
334void
335rtable_l2set(unsigned int rtableid, unsigned int rdomain, unsigned int loifidx)
336{
337 struct dommp *dmm;
338 unsigned int value;
339
340 KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c"
, 340, "_kernel_lock_held()"))
;
341
342 if (!rtable_exists(rtableid) || !rtable_exists(rdomain))
343 return;
344
345 value = (rdomain & RT_TABLEID_MASK0xff) | (loifidx << RT_TABLEID_BITS8);
346
347 dmm = srp_get_locked(&afmap[0]);
348 dmm->value[rtableid] = value;
349}
350
351
352static inline const uint8_t *satoaddr(struct art_root *,
353 const struct sockaddr *);
354
355int an_match(struct art_node *, const struct sockaddr *, int);
356void rtentry_ref(void *, void *);
357void rtentry_unref(void *, void *);
358
359void rtable_mpath_insert(struct art_node *, struct rtentry *);
360
361struct srpl_rc rt_rc = SRPL_RC_INITIALIZER(rtentry_ref, rtentry_unref, NULL){ rtentry_ref, { (rtentry_unref), (((void *)0)), { .r_refs = 1
, .r_traceidx = 0 } } }
;
362
363void
364rtable_init_backend(void)
365{
366 art_init();
367}
368
369void *
370rtable_alloc(unsigned int rtableid, unsigned int alen, unsigned int off)
371{
372 return (art_alloc(rtableid, alen, off));
373}
374
375int
376rtable_setsource(unsigned int rtableid, int af, struct sockaddr *src)
377{
378 struct art_root *ar;
379
380 NET_ASSERT_LOCKED_EXCLUSIVE()do { int _s = rw_status(&netlock); if ((splassert_ctl >
0) && (_s != 0x0001UL)) splassert_fail(0x0001UL, _s,
__func__); } while (0)
;
381
382 if ((ar = rtable_get(rtableid, af)) == NULL((void *)0))
383 return (EAFNOSUPPORT47);
384
385 ar->ar_source = src;
386
387 return (0);
388}
389
390struct sockaddr *
391rtable_getsource(unsigned int rtableid, int af)
392{
393 struct art_root *ar;
394
395 NET_ASSERT_LOCKED()do { int _s = rw_status(&netlock); if ((splassert_ctl >
0) && (_s != 0x0001UL && _s != 0x0002UL)) splassert_fail
(0x0002UL, _s, __func__); } while (0)
;
396
397 ar = rtable_get(rtableid, af);
398 if (ar == NULL((void *)0))
399 return (NULL((void *)0));
400
401 return (ar->ar_source);
402}
403
404void
405rtable_clearsource(unsigned int rtableid, struct sockaddr *src)
406{
407 struct sockaddr *addr;
408
409 addr = rtable_getsource(rtableid, src->sa_family);
410 if (addr && (addr->sa_len == src->sa_len)) {
411 if (memcmp(src, addr, addr->sa_len)__builtin_memcmp((src), (addr), (addr->sa_len)) == 0) {
412 rtable_setsource(rtableid, src->sa_family, NULL((void *)0));
413 }
414 }
415}
416
417struct rtentry *
418rtable_lookup(unsigned int rtableid, const struct sockaddr *dst,
419 const struct sockaddr *mask, const struct sockaddr *gateway, uint8_t prio)
420{
421 struct art_root *ar;
422 struct art_node *an;
423 struct rtentry *rt = NULL((void *)0);
424 struct srp_ref sr, nsr;
425 const uint8_t *addr;
426 int plen;
427
428 ar = rtable_get(rtableid, dst->sa_family);
429 if (ar == NULL((void *)0))
430 return (NULL((void *)0));
431
432 addr = satoaddr(ar, dst);
433
434 /* No need for a perfect match. */
435 if (mask == NULL((void *)0)) {
436 an = art_match(ar, addr, &nsr);
437 if (an == NULL((void *)0))
438 goto out;
439 } else {
440 plen = rtable_satoplen(dst->sa_family, mask);
441 if (plen == -1)
442 return (NULL((void *)0));
443
444 an = art_lookup(ar, addr, plen, &nsr);
445
446 /* Make sure we've got a perfect match. */
447 if (!an_match(an, dst, plen))
448 goto out;
449 }
450
451 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))
{
452 if (prio != RTP_ANY64 &&
453 (rt->rt_priority & RTP_MASK0x7f) != (prio & RTP_MASK0x7f))
454 continue;
455
456 if (gateway == NULL((void *)0))
457 break;
458
459 if (rt->rt_gateway->sa_len == gateway->sa_len &&
460 memcmp(rt->rt_gateway, gateway, gateway->sa_len)__builtin_memcmp((rt->rt_gateway), (gateway), (gateway->
sa_len))
== 0)
461 break;
462 }
463 if (rt != NULL((void *)0))
464 rtref(rt);
465
466 SRPL_LEAVE(&sr)srp_leave((&sr));
467out:
468 srp_leave(&nsr);
469
470 return (rt);
471}
472
473struct rtentry *
474rtable_match(unsigned int rtableid, const struct sockaddr *dst, uint32_t *src)
475{
476 struct art_root *ar;
477 struct art_node *an;
478 struct rtentry *rt = NULL((void *)0);
479 struct srp_ref sr, nsr;
480 const uint8_t *addr;
481 int hash;
482
483 ar = rtable_get(rtableid, dst->sa_family);
484 if (ar == NULL((void *)0))
1
Assuming 'ar' is not equal to NULL
2
Taking false branch
485 return (NULL((void *)0));
486
487 addr = satoaddr(ar, dst);
488
489 an = art_match(ar, addr, &nsr);
490 if (an == NULL((void *)0))
3
Assuming 'an' is not equal to NULL
4
Taking false branch
491 goto out;
492
493 rt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist)
->sl_head)
;
494 if (rt == NULL((void *)0)) {
5
Assuming 'rt' is not equal to NULL
6
Taking false branch
495 SRPL_LEAVE(&sr)srp_leave((&sr));
496 goto out;
497 }
498 rtref(rt);
499 SRPL_LEAVE(&sr)srp_leave((&sr));
500
501 /* Gateway selection by Hash-Threshold (RFC 2992) */
502 if ((hash = rt_hash(rt, dst, src)) != -1) {
7
Assuming the condition is true
8
Taking true branch
503 struct rtentry *mrt;
504 int threshold, npaths = 0;
9
'npaths' initialized to 0
505
506 KASSERT(hash <= 0xffff)((hash <= 0xffff) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c"
, 506, "hash <= 0xffff"))
;
10
Assuming 'hash' is <= 65535
11
'?' condition is true
507
508 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))
{
12
Assuming 'mrt' is equal to null
13
Loop condition is false. Execution continues on line 513
509 /* Only count nexthops with the same priority. */
510 if (mrt->rt_priority == rt->rt_priority)
511 npaths++;
512 }
513 SRPL_LEAVE(&sr)srp_leave((&sr));
514
515 threshold = (0xffff / npaths) + 1;
14
Division by zero
516
517 /*
518 * we have no protection against concurrent modification of the
519 * route list attached to the node, so we won't necessarily
520 * have the same number of routes. for most modifications,
521 * we'll pick a route that we wouldn't have if we only saw the
522 * list before or after the change. if we were going to use
523 * the last available route, but it got removed, we'll hit
524 * the end of the list and then pick the first route.
525 */
526
527 mrt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist)
->sl_head)
;
528 while (hash > threshold && mrt != NULL((void *)0)) {
529 if (mrt->rt_priority == rt->rt_priority)
530 hash -= threshold;
531 mrt = SRPL_FOLLOW(&sr, mrt, rt_next)srp_follow((&sr), &(mrt)->rt_next.se_next);
532 }
533
534 if (mrt != NULL((void *)0)) {
535 rtref(mrt);
536 rtfree(rt);
537 rt = mrt;
538 }
539 SRPL_LEAVE(&sr)srp_leave((&sr));
540 }
541out:
542 srp_leave(&nsr);
543 return (rt);
544}
545
546int
547rtable_insert(unsigned int rtableid, struct sockaddr *dst,
548 const struct sockaddr *mask, const struct sockaddr *gateway, uint8_t prio,
549 struct rtentry *rt)
550{
551 struct rtentry *mrt;
552 struct srp_ref sr;
553 struct art_root *ar;
554 struct art_node *an, *prev;
555 const uint8_t *addr;
556 int plen;
557 unsigned int rt_flags;
558 int error = 0;
559
560 ar = rtable_get(rtableid, dst->sa_family);
561 if (ar == NULL((void *)0))
562 return (EAFNOSUPPORT47);
563
564 addr = satoaddr(ar, dst);
565 plen = rtable_satoplen(dst->sa_family, mask);
566 if (plen == -1)
567 return (EINVAL22);
568
569 rtref(rt); /* guarantee rtfree won't do anything during insert */
570 rw_enter_write(&ar->ar_lock);
571
572 /* Do not permit exactly the same dst/mask/gw pair. */
573 an = art_lookup(ar, addr, plen, &sr);
574 srp_leave(&sr); /* an can't go away while we have the lock */
575 if (an_match(an, dst, plen)) {
576 struct rtentry *mrt;
577 int mpathok = ISSET(rt->rt_flags, RTF_MPATH)((rt->rt_flags) & (0x40000));
578
579 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))
{
580 if (prio != RTP_ANY64 &&
581 (mrt->rt_priority & RTP_MASK0x7f) != (prio & RTP_MASK0x7f))
582 continue;
583
584 if (!mpathok ||
585 (mrt->rt_gateway->sa_len == gateway->sa_len &&
586 memcmp(mrt->rt_gateway, gateway,__builtin_memcmp((mrt->rt_gateway), (gateway), (gateway->
sa_len))
587 gateway->sa_len)__builtin_memcmp((mrt->rt_gateway), (gateway), (gateway->
sa_len))
== 0)) {
588 error = EEXIST17;
589 goto leave;
590 }
591 }
592 }
593
594 an = art_get(plen);
595 if (an == NULL((void *)0)) {
596 error = ENOBUFS55;
597 goto leave;
598 }
599
600 /* prepare for immediate operation if insert succeeds */
601 rt_flags = rt->rt_flags;
602 rt->rt_flags &= ~RTF_MPATH0x40000;
603 rt->rt_dest = dst;
604 rt->rt_plen = plen;
605 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)
;
606
607 prev = art_insert(ar, an, addr, plen);
608 if (prev != an) {
609 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)
610 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)
;
611 rt->rt_flags = rt_flags;
612 art_put(an);
613
614 if (prev == NULL((void *)0)) {
615 error = ESRCH3;
616 goto leave;
617 }
618
619 an = prev;
620
621 mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head
)
;
622 KASSERT(mrt != NULL)((mrt != ((void *)0)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/net/rtable.c"
, 622, "mrt != NULL"))
;
623 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"
, 623, "(rt->rt_flags & RTF_MPATH) || mrt->rt_priority != prio"
))
;
624
625 /*
626 * An ART node with the same destination/netmask already
627 * exists, MPATH conflict must have been already checked.
628 */
629 if (rt->rt_flags & RTF_MPATH0x40000) {
630 /*
631 * Only keep the RTF_MPATH flag if two routes have
632 * the same gateway.
633 */
634 rt->rt_flags &= ~RTF_MPATH0x40000;
635 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))
{
636 if (mrt->rt_priority == prio) {
637 mrt->rt_flags |= RTF_MPATH0x40000;
638 rt->rt_flags |= RTF_MPATH0x40000;
639 }
640 }
641 }
642
643 /* Put newly inserted entry at the right place. */
644 rtable_mpath_insert(an, rt);
645 }
646leave:
647 rw_exit_write(&ar->ar_lock);
648 rtfree(rt);
649 return (error);
650}
651
652int
653rtable_delete(unsigned int rtableid, const struct sockaddr *dst,
654 const struct sockaddr *mask, struct rtentry *rt)
655{
656 struct art_root *ar;
657 struct art_node *an;
658 struct srp_ref sr;
659 const uint8_t *addr;
660 int plen;
661 struct rtentry *mrt;
662 int npaths = 0;
663 int error = 0;
664
665 ar = rtable_get(rtableid, dst->sa_family);
666 if (ar == NULL((void *)0))
667 return (EAFNOSUPPORT47);
668
669 addr = satoaddr(ar, dst);
670 plen = rtable_satoplen(dst->sa_family, mask);
671 if (plen == -1)
672 return (EINVAL22);
673
674 rtref(rt); /* guarantee rtfree won't do anything under ar_lock */
675 rw_enter_write(&ar->ar_lock);
676 an = art_lookup(ar, addr, plen, &sr);
677 srp_leave(&sr); /* an can't go away while we have the lock */
678
679 /* Make sure we've got a perfect match. */
680 if (!an_match(an, dst, plen)) {
681 error = ESRCH3;
682 goto leave;
683 }
684
685 /*
686 * If other multipath route entries are still attached to
687 * this ART node we only have to unlink it.
688 */
689 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))
690 npaths++;
691
692 if (npaths > 1) {
693 KASSERT(refcnt_read(&rt->rt_refcnt) >= 1)((refcnt_read(&rt->rt_refcnt) >= 1) ? (void)0 : __assert
("diagnostic ", "/usr/src/sys/net/rtable.c", 693, "refcnt_read(&rt->rt_refcnt) >= 1"
))
;
694 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)
695 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)
;
696
697 mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head
)
;
698 if (npaths == 2)
699 mrt->rt_flags &= ~RTF_MPATH0x40000;
700
701 goto leave;
702 }
703
704 if (art_delete(ar, an, addr, plen) == NULL((void *)0))
705 panic("art_delete failed to find node %p", an);
706
707 KASSERT(refcnt_read(&rt->rt_refcnt) >= 1)((refcnt_read(&rt->rt_refcnt) >= 1) ? (void)0 : __assert
("diagnostic ", "/usr/src/sys/net/rtable.c", 707, "refcnt_read(&rt->rt_refcnt) >= 1"
))
;
708 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)
;
709 art_put(an);
710
711leave:
712 rw_exit_write(&ar->ar_lock);
713 rtfree(rt);
714
715 return (error);
716}
717
718struct rtable_walk_cookie {
719 int (*rwc_func)(struct rtentry *, void *, unsigned int);
720 void *rwc_arg;
721 struct rtentry **rwc_prt;
722 unsigned int rwc_rid;
723};
724
725/*
726 * Helper for rtable_walk to keep the ART code free from any "struct rtentry".
727 */
728int
729rtable_walk_helper(struct art_node *an, void *xrwc)
730{
731 struct srp_ref sr;
732 struct rtable_walk_cookie *rwc = xrwc;
733 struct rtentry *rt;
734 int error = 0;
735
736 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))
{
737 error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid);
738 if (error != 0)
739 break;
740 }
741 if (rwc->rwc_prt != NULL((void *)0) && rt != NULL((void *)0)) {
742 rtref(rt);
743 *rwc->rwc_prt = rt;
744 }
745 SRPL_LEAVE(&sr)srp_leave((&sr));
746
747 return (error);
748}
749
750int
751rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt,
752 int (*func)(struct rtentry *, void *, unsigned int), void *arg)
753{
754 struct art_root *ar;
755 struct rtable_walk_cookie rwc;
756 int error;
757
758 ar = rtable_get(rtableid, af);
759 if (ar == NULL((void *)0))
760 return (EAFNOSUPPORT47);
761
762 rwc.rwc_func = func;
763 rwc.rwc_arg = arg;
764 rwc.rwc_prt = prt;
765 rwc.rwc_rid = rtableid;
766
767 error = art_walk(ar, rtable_walk_helper, &rwc);
768
769 return (error);
770}
771
772struct rtentry *
773rtable_iterate(struct rtentry *rt0)
774{
775 struct rtentry *rt = NULL((void *)0);
776 struct srp_ref sr;
777
778 rt = SRPL_NEXT(&sr, rt0, rt_next)srp_enter((&sr), &(rt0)->rt_next.se_next);
779 if (rt != NULL((void *)0))
780 rtref(rt);
781 SRPL_LEAVE(&sr)srp_leave((&sr));
782 rtfree(rt0);
783 return (rt);
784}
785
786int
787rtable_mpath_capable(unsigned int rtableid, sa_family_t af)
788{
789 return (1);
790}
791
792int
793rtable_mpath_reprio(unsigned int rtableid, struct sockaddr *dst,
794 int plen, uint8_t prio, struct rtentry *rt)
795{
796 struct art_root *ar;
797 struct art_node *an;
798 struct srp_ref sr;
799 const uint8_t *addr;
800 int error = 0;
801
802 ar = rtable_get(rtableid, dst->sa_family);
803 if (ar == NULL((void *)0))
804 return (EAFNOSUPPORT47);
805
806 addr = satoaddr(ar, dst);
807
808 rw_enter_write(&ar->ar_lock);
809 an = art_lookup(ar, addr, plen, &sr);
810 srp_leave(&sr); /* an can't go away while we have the lock */
811
812 /* Make sure we've got a perfect match. */
813 if (!an_match(an, dst, plen)) {
814 error = ESRCH3;
815 } else if (SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head
)
== rt &&
816 SRPL_NEXT_LOCKED(rt, rt_next)srp_get_locked(&(rt)->rt_next.se_next) == NULL((void *)0)) {
817 /*
818 * If there's only one entry on the list do not go
819 * through an insert/remove cycle. This is done to
820 * guarantee that ``an->an_rtlist'' is never empty
821 * when a node is in the tree.
822 */
823 rt->rt_priority = prio;
824 } else {
825 rtref(rt); /* keep rt alive in between remove and insert */
826 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)
827 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)
;
828 rt->rt_priority = prio;
829 rtable_mpath_insert(an, rt);
830 rtfree(rt);
831 error = EAGAIN35;
832 }
833 rw_exit_write(&ar->ar_lock);
834
835 return (error);
836}
837
838void
839rtable_mpath_insert(struct art_node *an, struct rtentry *rt)
840{
841 struct rtentry *mrt, *prt = NULL((void *)0);
842 uint8_t prio = rt->rt_priority;
843
844 if ((mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)srp_get_locked(&(&an->an_pointer.an__rtlist)->sl_head
)
) == NULL((void *)0)) {
845 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)
;
846 return;
847 }
848
849 /* Iterate until we find the route to be placed after ``rt''. */
850 while (mrt->rt_priority <= prio && SRPL_NEXT_LOCKED(mrt, rt_next)srp_get_locked(&(mrt)->rt_next.se_next)) {
851 prt = mrt;
852 mrt = SRPL_NEXT_LOCKED(mrt, rt_next)srp_get_locked(&(mrt)->rt_next.se_next);
853 }
854
855 if (mrt->rt_priority <= prio) {
856 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)
;
857 } else if (prt != NULL((void *)0)) {
858 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)
;
859 } else {
860 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)
;
861 }
862}
863
864/*
865 * Returns 1 if ``an'' perfectly matches (``dst'', ``plen''), 0 otherwise.
866 */
867int
868an_match(struct art_node *an, const struct sockaddr *dst, int plen)
869{
870 struct rtentry *rt;
871 struct srp_ref sr;
872 int match;
873
874 if (an == NULL((void *)0) || an->an_plen != plen)
875 return (0);
876
877 rt = SRPL_FIRST(&sr, &an->an_rtlist)srp_enter((&sr), &(&an->an_pointer.an__rtlist)
->sl_head)
;
878 match = (memcmp(rt->rt_dest, dst, dst->sa_len)__builtin_memcmp((rt->rt_dest), (dst), (dst->sa_len)) == 0);
879 SRPL_LEAVE(&sr)srp_leave((&sr));
880
881 return (match);
882}
883
884void
885rtentry_ref(void *null, void *xrt)
886{
887 struct rtentry *rt = xrt;
888
889 rtref(rt);
890}
891
892void
893rtentry_unref(void *null, void *xrt)
894{
895 struct rtentry *rt = xrt;
896
897 rtfree(rt);
898}
899
900/*
901 * Return a pointer to the address (key). This is an heritage from the
902 * BSD radix tree needed to skip the non-address fields from the flavor
903 * of "struct sockaddr" used by this routing table.
904 */
905static inline const uint8_t *
906satoaddr(struct art_root *at, const struct sockaddr *sa)
907{
908 return (((const uint8_t *)sa) + at->ar_off);
909}
910
911/*
912 * Return the prefix length of a mask.
913 */
914int
915rtable_satoplen(sa_family_t af, const struct sockaddr *mask)
916{
917 const struct domain *dp;
918 uint8_t *ap, *ep;
919 int mlen, plen = 0;
920 int i;
921
922 for (i = 0; (dp = domains[i]) != NULL((void *)0); i++) {
923 if (dp->dom_rtoffset == 0)
924 continue;
925
926 if (af == dp->dom_family)
927 break;
928 }
929 if (dp == NULL((void *)0))
930 return (-1);
931
932 /* Host route */
933 if (mask == NULL((void *)0))
934 return (dp->dom_maxplen);
935
936 mlen = mask->sa_len;
937
938 /* Default route */
939 if (mlen == 0)
940 return (0);
941
942 ap = (uint8_t *)((uint8_t *)mask) + dp->dom_rtoffset;
943 ep = (uint8_t *)((uint8_t *)mask) + mlen;
944 if (ap > ep)
945 return (-1);
946
947 /* Trim trailing zeroes. */
948 while (ap < ep && ep[-1] == 0)
949 ep--;
950
951 if (ap == ep)
952 return (0);
953
954 /* "Beauty" adapted from sbin/route/show.c ... */
955 while (ap < ep) {
956 switch (*ap++) {
957 case 0xff:
958 plen += 8;
959 break;
960 case 0xfe:
961 plen += 7;
962 goto out;
963 case 0xfc:
964 plen += 6;
965 goto out;
966 case 0xf8:
967 plen += 5;
968 goto out;
969 case 0xf0:
970 plen += 4;
971 goto out;
972 case 0xe0:
973 plen += 3;
974 goto out;
975 case 0xc0:
976 plen += 2;
977 goto out;
978 case 0x80:
979 plen += 1;
980 goto out;
981 default:
982 /* Non contiguous mask. */
983 return (-1);
984 }
985 }
986
987out:
988 if (plen > dp->dom_maxplen || ap != ep)
989 return -1;
990
991 return (plen);
992}