1*91defc8bSclaudio /* $OpenBSD: rtable.c,v 1.87 2024/04/09 12:53:08 claudio Exp $ */
255df0a74Smpi
355df0a74Smpi /*
43a63dfc0Smpi * Copyright (c) 2014-2016 Martin Pieuchot
555df0a74Smpi *
655df0a74Smpi * Permission to use, copy, modify, and distribute this software for any
755df0a74Smpi * purpose with or without fee is hereby granted, provided that the above
855df0a74Smpi * copyright notice and this permission notice appear in all copies.
955df0a74Smpi *
1055df0a74Smpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1155df0a74Smpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1255df0a74Smpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1355df0a74Smpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1455df0a74Smpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1555df0a74Smpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1655df0a74Smpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1755df0a74Smpi */
1855df0a74Smpi
19b228adf0Smpi #ifndef _KERNEL
20b228adf0Smpi #include "kern_compat.h"
21b228adf0Smpi #else
2255df0a74Smpi #include <sys/param.h>
2355df0a74Smpi #include <sys/systm.h>
2455df0a74Smpi #include <sys/socket.h>
253a49c894Smpi #include <sys/malloc.h>
263a49c894Smpi #include <sys/queue.h>
272d6ba272Smpi #include <sys/domain.h>
2838caa7a0Smpi #include <sys/srp.h>
29b228adf0Smpi #endif
3055df0a74Smpi
3155df0a74Smpi #include <net/rtable.h>
3255df0a74Smpi #include <net/route.h>
33*91defc8bSclaudio #include <net/art.h>
3455df0a74Smpi
3595346806Smpi /*
3638caa7a0Smpi * Structures used by rtable_get() to retrieve the corresponding
3738caa7a0Smpi * routing table for a given pair of ``af'' and ``rtableid''.
3838caa7a0Smpi *
3938caa7a0Smpi * Note that once allocated routing table heads are never freed.
4038caa7a0Smpi * This way we do not need to reference count them.
4195346806Smpi *
4295346806Smpi * afmap rtmap/dommp
4395346806Smpi * ----------- --------- -----
4495346806Smpi * | 0 |--------> | 0 | 0 | ... | 0 | Array mapping rtableid (=index)
453a63dfc0Smpi * ----------- --------- ----- to rdomain/loopback (=value).
4695346806Smpi * | AF_INET |.
4795346806Smpi * ----------- `. .---------. .---------.
4895346806Smpi * ... `----> | rtable0 | ... | rtableN | Array of pointers for
4995346806Smpi * ----------- '---------' '---------' IPv4 routing tables
5095346806Smpi * | AF_MPLS | indexed by ``rtableid''.
5195346806Smpi * -----------
5295346806Smpi */
5395346806Smpi struct srp *afmap;
542d6ba272Smpi uint8_t af2idx[AF_MAX+1]; /* To only allocate supported AF */
55463f286bSmpi uint8_t af2idx_max;
562d6ba272Smpi
5795346806Smpi /* Array of routing table pointers. */
5895346806Smpi struct rtmap {
5995346806Smpi unsigned int limit;
60463f286bSmpi void **tbl;
6195346806Smpi };
6295346806Smpi
633a63dfc0Smpi /*
643a63dfc0Smpi * Array of rtableid -> rdomain mapping.
653a63dfc0Smpi *
66df8d9afdSjsg * Only used for the first index as described above.
673a63dfc0Smpi */
6895346806Smpi struct dommp {
6995346806Smpi unsigned int limit;
703a63dfc0Smpi /*
713a63dfc0Smpi * Array to get the routing domain and loopback interface related to
723a63dfc0Smpi * a routing table. Format:
733a63dfc0Smpi *
743a63dfc0Smpi * 8 unused bits | 16 bits for loopback index | 8 bits for rdomain
753a63dfc0Smpi */
763a63dfc0Smpi unsigned int *value;
77463f286bSmpi };
78463f286bSmpi
7995346806Smpi unsigned int rtmap_limit = 0;
8095346806Smpi
8195346806Smpi void rtmap_init(void);
8295346806Smpi void rtmap_grow(unsigned int, sa_family_t);
8395346806Smpi void rtmap_dtor(void *, void *);
8495346806Smpi
8595346806Smpi struct srp_gc rtmap_gc = SRP_GC_INITIALIZER(rtmap_dtor, NULL);
862d6ba272Smpi
87d29df515Sclaudio void rtable_init_backend(void);
88d28efb3fSmpi void *rtable_alloc(unsigned int, unsigned int, unsigned int);
8956588de8Smpi void *rtable_get(unsigned int, sa_family_t);
903a49c894Smpi
9155df0a74Smpi void
rtmap_init(void)9295346806Smpi rtmap_init(void)
9395346806Smpi {
94c90e7dccSbluhm const struct domain *dp;
9595346806Smpi int i;
9695346806Smpi
9795346806Smpi /* Start with a single table for every domain that requires it. */
9895346806Smpi for (i = 0; (dp = domains[i]) != NULL; i++) {
9995346806Smpi if (dp->dom_rtoffset == 0)
10095346806Smpi continue;
10195346806Smpi
10295346806Smpi rtmap_grow(1, dp->dom_family);
10395346806Smpi }
10495346806Smpi
10595346806Smpi /* Initialize the rtableid->rdomain mapping table. */
10695346806Smpi rtmap_grow(1, 0);
10795346806Smpi
10895346806Smpi rtmap_limit = 1;
10995346806Smpi }
11095346806Smpi
11195346806Smpi /*
11295346806Smpi * Grow the size of the array of routing table for AF ``af'' to ``nlimit''.
11395346806Smpi */
11495346806Smpi void
rtmap_grow(unsigned int nlimit,sa_family_t af)11595346806Smpi rtmap_grow(unsigned int nlimit, sa_family_t af)
11695346806Smpi {
11795346806Smpi struct rtmap *map, *nmap;
11895346806Smpi int i;
11995346806Smpi
12095346806Smpi KERNEL_ASSERT_LOCKED();
12195346806Smpi
12295346806Smpi KASSERT(nlimit > rtmap_limit);
12395346806Smpi
12495346806Smpi nmap = malloc(sizeof(*nmap), M_RTABLE, M_WAITOK);
12595346806Smpi nmap->limit = nlimit;
12695346806Smpi nmap->tbl = mallocarray(nlimit, sizeof(*nmap[0].tbl), M_RTABLE,
12795346806Smpi M_WAITOK|M_ZERO);
12895346806Smpi
12995346806Smpi map = srp_get_locked(&afmap[af2idx[af]]);
13095346806Smpi if (map != NULL) {
13195346806Smpi KASSERT(map->limit == rtmap_limit);
13295346806Smpi
13395346806Smpi for (i = 0; i < map->limit; i++)
13495346806Smpi nmap->tbl[i] = map->tbl[i];
13595346806Smpi }
13695346806Smpi
13795346806Smpi srp_update_locked(&rtmap_gc, &afmap[af2idx[af]], nmap);
13895346806Smpi }
13995346806Smpi
14095346806Smpi void
rtmap_dtor(void * null,void * xmap)14195346806Smpi rtmap_dtor(void *null, void *xmap)
14295346806Smpi {
14395346806Smpi struct rtmap *map = xmap;
14495346806Smpi
14595346806Smpi /*
146678831beSjsg * doesn't need to be serialized since this is the last reference
14795346806Smpi * to this map. there's nothing to race against.
14895346806Smpi */
14995346806Smpi free(map->tbl, M_RTABLE, map->limit * sizeof(*map[0].tbl));
15095346806Smpi free(map, M_RTABLE, sizeof(*map));
15195346806Smpi }
15295346806Smpi
15395346806Smpi void
rtable_init(void)1542d6ba272Smpi rtable_init(void)
1552d6ba272Smpi {
156c90e7dccSbluhm const struct domain *dp;
1572d6ba272Smpi int i;
1582d6ba272Smpi
1593a63dfc0Smpi KASSERT(sizeof(struct rtmap) == sizeof(struct dommp));
1603a63dfc0Smpi
161463f286bSmpi /* We use index 0 for the rtable/rdomain map. */
162463f286bSmpi af2idx_max = 1;
1632d6ba272Smpi memset(af2idx, 0, sizeof(af2idx));
1642d6ba272Smpi
1652d6ba272Smpi /*
1662d6ba272Smpi * Compute the maximum supported key length in case the routing
1672d6ba272Smpi * table backend needs it.
1682d6ba272Smpi */
1692d6ba272Smpi for (i = 0; (dp = domains[i]) != NULL; i++) {
17095346806Smpi if (dp->dom_rtoffset == 0)
17195346806Smpi continue;
17295346806Smpi
1732d6ba272Smpi af2idx[dp->dom_family] = af2idx_max++;
174463f286bSmpi }
175d29df515Sclaudio rtable_init_backend();
1762d6ba272Smpi
17795346806Smpi /*
17895346806Smpi * Allocate AF-to-id table now that we now how many AFs this
17995346806Smpi * kernel supports.
18095346806Smpi */
18195346806Smpi afmap = mallocarray(af2idx_max + 1, sizeof(*afmap), M_RTABLE,
18295346806Smpi M_WAITOK|M_ZERO);
183463f286bSmpi
18495346806Smpi rtmap_init();
1853a63dfc0Smpi
1863a63dfc0Smpi if (rtable_add(0) != 0)
1873a63dfc0Smpi panic("unable to create default routing table");
188f35efbbdSbluhm
189f35efbbdSbluhm rt_timer_init();
190463f286bSmpi }
191463f286bSmpi
1922d6ba272Smpi int
rtable_add(unsigned int id)1932d6ba272Smpi rtable_add(unsigned int id)
1942d6ba272Smpi {
195c90e7dccSbluhm const struct domain *dp;
19695346806Smpi void *tbl;
19795346806Smpi struct rtmap *map;
19895346806Smpi struct dommp *dmm;
199463f286bSmpi sa_family_t af;
200d28efb3fSmpi unsigned int off, alen;
20151772eb6Smvs int i, error = 0;
20295346806Smpi
203a7d9f313Smpi if (id > RT_TABLEID_MAX)
2042d6ba272Smpi return (EINVAL);
2052d6ba272Smpi
20651772eb6Smvs KERNEL_LOCK();
20751772eb6Smvs
208a7d9f313Smpi if (rtable_exists(id))
20951772eb6Smvs goto out;
210a7d9f313Smpi
2112d6ba272Smpi for (i = 0; (dp = domains[i]) != NULL; i++) {
2122d6ba272Smpi if (dp->dom_rtoffset == 0)
2132d6ba272Smpi continue;
214463f286bSmpi
215463f286bSmpi af = dp->dom_family;
216463f286bSmpi off = dp->dom_rtoffset;
217d28efb3fSmpi alen = dp->dom_maxplen;
218463f286bSmpi
21995346806Smpi if (id >= rtmap_limit)
22095346806Smpi rtmap_grow(id + 1, af);
221463f286bSmpi
222d28efb3fSmpi tbl = rtable_alloc(id, alen, off);
22351772eb6Smvs if (tbl == NULL) {
22451772eb6Smvs error = ENOMEM;
22551772eb6Smvs goto out;
22651772eb6Smvs }
227463f286bSmpi
22895346806Smpi map = srp_get_locked(&afmap[af2idx[af]]);
22995346806Smpi map->tbl[id] = tbl;
2302d6ba272Smpi }
2312d6ba272Smpi
232463f286bSmpi /* Reflect possible growth. */
23395346806Smpi if (id >= rtmap_limit) {
23495346806Smpi rtmap_grow(id + 1, 0);
23595346806Smpi rtmap_limit = id + 1;
2362d6ba272Smpi }
237463f286bSmpi
238463f286bSmpi /* Use main rtable/rdomain by default. */
23995346806Smpi dmm = srp_get_locked(&afmap[0]);
2403a63dfc0Smpi dmm->value[id] = 0;
24151772eb6Smvs out:
24251772eb6Smvs KERNEL_UNLOCK();
243463f286bSmpi
24451772eb6Smvs return (error);
2452d6ba272Smpi }
2462d6ba272Smpi
247463f286bSmpi void *
rtable_get(unsigned int rtableid,sa_family_t af)248463f286bSmpi rtable_get(unsigned int rtableid, sa_family_t af)
249463f286bSmpi {
25095346806Smpi struct rtmap *map;
25195346806Smpi void *tbl = NULL;
252ca268887Sdlg struct srp_ref sr;
25395346806Smpi
25495346806Smpi if (af >= nitems(af2idx) || af2idx[af] == 0)
255463f286bSmpi return (NULL);
256463f286bSmpi
257ca268887Sdlg map = srp_enter(&sr, &afmap[af2idx[af]]);
25895346806Smpi if (rtableid < map->limit)
25995346806Smpi tbl = map->tbl[rtableid];
260ca268887Sdlg srp_leave(&sr);
261463f286bSmpi
26295346806Smpi return (tbl);
2631f5a9102Smpi }
2641f5a9102Smpi
2652d6ba272Smpi int
rtable_exists(unsigned int rtableid)2662d6ba272Smpi rtable_exists(unsigned int rtableid)
2672d6ba272Smpi {
268c90e7dccSbluhm const struct domain *dp;
2691f5a9102Smpi void *tbl;
27038caa7a0Smpi int i;
271463f286bSmpi
272463f286bSmpi for (i = 0; (dp = domains[i]) != NULL; i++) {
273463f286bSmpi if (dp->dom_rtoffset == 0)
274463f286bSmpi continue;
2752d6ba272Smpi
2761f5a9102Smpi tbl = rtable_get(rtableid, dp->dom_family);
2771f5a9102Smpi if (tbl != NULL)
27838caa7a0Smpi return (1);
2792d6ba272Smpi }
2802d6ba272Smpi
28138caa7a0Smpi return (0);
282463f286bSmpi }
283463f286bSmpi
284c58a9139Shenning int
rtable_empty(unsigned int rtableid)285c58a9139Shenning rtable_empty(unsigned int rtableid)
286c58a9139Shenning {
287c90e7dccSbluhm const struct domain *dp;
288c58a9139Shenning int i;
289c58a9139Shenning struct art_root *tbl;
290c58a9139Shenning
291c58a9139Shenning for (i = 0; (dp = domains[i]) != NULL; i++) {
292c58a9139Shenning if (dp->dom_rtoffset == 0)
293c58a9139Shenning continue;
294c58a9139Shenning
295c58a9139Shenning tbl = rtable_get(rtableid, dp->dom_family);
296c58a9139Shenning if (tbl == NULL)
297c58a9139Shenning continue;
298c58a9139Shenning if (tbl->ar_root.ref != NULL)
299c58a9139Shenning return (0);
300c58a9139Shenning }
301c58a9139Shenning
302c58a9139Shenning return (1);
303c58a9139Shenning }
304c58a9139Shenning
3052d6ba272Smpi unsigned int
rtable_l2(unsigned int rtableid)3062d6ba272Smpi rtable_l2(unsigned int rtableid)
3072d6ba272Smpi {
30895346806Smpi struct dommp *dmm;
30995346806Smpi unsigned int rdomain = 0;
310ca268887Sdlg struct srp_ref sr;
3112d6ba272Smpi
312ca268887Sdlg dmm = srp_enter(&sr, &afmap[0]);
31395346806Smpi if (rtableid < dmm->limit)
3143a63dfc0Smpi rdomain = (dmm->value[rtableid] & RT_TABLEID_MASK);
315ca268887Sdlg srp_leave(&sr);
31695346806Smpi
31795346806Smpi return (rdomain);
3182d6ba272Smpi }
3192d6ba272Smpi
3203a63dfc0Smpi unsigned int
rtable_loindex(unsigned int rtableid)3213a63dfc0Smpi rtable_loindex(unsigned int rtableid)
3222d6ba272Smpi {
32395346806Smpi struct dommp *dmm;
3243a63dfc0Smpi unsigned int loifidx = 0;
3253a63dfc0Smpi struct srp_ref sr;
3263a63dfc0Smpi
3273a63dfc0Smpi dmm = srp_enter(&sr, &afmap[0]);
3283a63dfc0Smpi if (rtableid < dmm->limit)
3293a63dfc0Smpi loifidx = (dmm->value[rtableid] >> RT_TABLEID_BITS);
3303a63dfc0Smpi srp_leave(&sr);
3313a63dfc0Smpi
3323a63dfc0Smpi return (loifidx);
3333a63dfc0Smpi }
3343a63dfc0Smpi
3353a63dfc0Smpi void
rtable_l2set(unsigned int rtableid,unsigned int rdomain,unsigned int loifidx)3363a63dfc0Smpi rtable_l2set(unsigned int rtableid, unsigned int rdomain, unsigned int loifidx)
3373a63dfc0Smpi {
3383a63dfc0Smpi struct dommp *dmm;
3393a63dfc0Smpi unsigned int value;
34095346806Smpi
34195346806Smpi KERNEL_ASSERT_LOCKED();
34295346806Smpi
34395346806Smpi if (!rtable_exists(rtableid) || !rtable_exists(rdomain))
3442d6ba272Smpi return;
3452d6ba272Smpi
3463a63dfc0Smpi value = (rdomain & RT_TABLEID_MASK) | (loifidx << RT_TABLEID_BITS);
3473a63dfc0Smpi
34895346806Smpi dmm = srp_get_locked(&afmap[0]);
3493a63dfc0Smpi dmm->value[rtableid] = value;
3502d6ba272Smpi }
3512d6ba272Smpi
3523a49c894Smpi
353a3e24bb9Sbluhm static inline const uint8_t *satoaddr(struct art_root *,
354a3e24bb9Sbluhm const struct sockaddr *);
3553a49c894Smpi
3569a32bf7bSbluhm int an_match(struct art_node *, const struct sockaddr *, int);
357cf41c536Smpi void rtentry_ref(void *, void *);
358cf41c536Smpi void rtentry_unref(void *, void *);
359cf41c536Smpi
3609b397541Sjmatthew void rtable_mpath_insert(struct art_node *, struct rtentry *);
3619b397541Sjmatthew
362cf41c536Smpi struct srpl_rc rt_rc = SRPL_RC_INITIALIZER(rtentry_ref, rtentry_unref, NULL);
363cf41c536Smpi
3643a49c894Smpi void
rtable_init_backend(void)365d29df515Sclaudio rtable_init_backend(void)
3663a49c894Smpi {
367d560d45fSmpi art_init();
3683a49c894Smpi }
3693a49c894Smpi
370463f286bSmpi void *
rtable_alloc(unsigned int rtableid,unsigned int alen,unsigned int off)371d28efb3fSmpi rtable_alloc(unsigned int rtableid, unsigned int alen, unsigned int off)
3723a49c894Smpi {
373d28efb3fSmpi return (art_alloc(rtableid, alen, off));
374463f286bSmpi }
3752d6ba272Smpi
37672366f00Sdenis int
rtable_setsource(unsigned int rtableid,int af,struct sockaddr * src)3776950b880Sdenis rtable_setsource(unsigned int rtableid, int af, struct sockaddr *src)
37872366f00Sdenis {
37972366f00Sdenis struct art_root *ar;
38072366f00Sdenis
381eed9a617Sbluhm NET_ASSERT_LOCKED_EXCLUSIVE();
382eed9a617Sbluhm
3836950b880Sdenis if ((ar = rtable_get(rtableid, af)) == NULL)
38472366f00Sdenis return (EAFNOSUPPORT);
38572366f00Sdenis
386f8c3e5b6Smvs ar->ar_source = src;
38772366f00Sdenis
38872366f00Sdenis return (0);
38972366f00Sdenis }
39072366f00Sdenis
39172366f00Sdenis struct sockaddr *
rtable_getsource(unsigned int rtableid,int af)39272366f00Sdenis rtable_getsource(unsigned int rtableid, int af)
39372366f00Sdenis {
39472366f00Sdenis struct art_root *ar;
39572366f00Sdenis
396eed9a617Sbluhm NET_ASSERT_LOCKED();
397eed9a617Sbluhm
39872366f00Sdenis ar = rtable_get(rtableid, af);
39972366f00Sdenis if (ar == NULL)
40072366f00Sdenis return (NULL);
40172366f00Sdenis
402f8c3e5b6Smvs return (ar->ar_source);
40372366f00Sdenis }
40472366f00Sdenis
40572366f00Sdenis void
rtable_clearsource(unsigned int rtableid,struct sockaddr * src)40672366f00Sdenis rtable_clearsource(unsigned int rtableid, struct sockaddr *src)
40772366f00Sdenis {
40872366f00Sdenis struct sockaddr *addr;
40972366f00Sdenis
41072366f00Sdenis addr = rtable_getsource(rtableid, src->sa_family);
41172366f00Sdenis if (addr && (addr->sa_len == src->sa_len)) {
41272366f00Sdenis if (memcmp(src, addr, addr->sa_len) == 0) {
4136950b880Sdenis rtable_setsource(rtableid, src->sa_family, NULL);
41472366f00Sdenis }
41572366f00Sdenis }
41672366f00Sdenis }
41772366f00Sdenis
4183a49c894Smpi struct rtentry *
rtable_lookup(unsigned int rtableid,const struct sockaddr * dst,const struct sockaddr * mask,const struct sockaddr * gateway,uint8_t prio)4199a32bf7bSbluhm rtable_lookup(unsigned int rtableid, const struct sockaddr *dst,
4209a32bf7bSbluhm const struct sockaddr *mask, const struct sockaddr *gateway, uint8_t prio)
4213a49c894Smpi {
4223a49c894Smpi struct art_root *ar;
4233a49c894Smpi struct art_node *an;
424e0d1f3a9Sjmatthew struct rtentry *rt = NULL;
425e0d1f3a9Sjmatthew struct srp_ref sr, nsr;
426a3e24bb9Sbluhm const uint8_t *addr;
4273a49c894Smpi int plen;
4283a49c894Smpi
4293a49c894Smpi ar = rtable_get(rtableid, dst->sa_family);
4303a49c894Smpi if (ar == NULL)
4313a49c894Smpi return (NULL);
4323a49c894Smpi
4331ff67b00Smpi addr = satoaddr(ar, dst);
4341ff67b00Smpi
4353a49c894Smpi /* No need for a perfect match. */
4363a49c894Smpi if (mask == NULL) {
437e0d1f3a9Sjmatthew an = art_match(ar, addr, &nsr);
4381f5a9102Smpi if (an == NULL)
439e0d1f3a9Sjmatthew goto out;
4403a49c894Smpi } else {
441ad38292eSmpi plen = rtable_satoplen(dst->sa_family, mask);
4423a49c894Smpi if (plen == -1)
44338caa7a0Smpi return (NULL);
4441ff67b00Smpi
445e0d1f3a9Sjmatthew an = art_lookup(ar, addr, plen, &nsr);
446e0d1f3a9Sjmatthew
4471ff67b00Smpi /* Make sure we've got a perfect match. */
448c42687bdSmpi if (!an_match(an, dst, plen))
449e0d1f3a9Sjmatthew goto out;
4503a49c894Smpi }
4513a49c894Smpi
452ca268887Sdlg SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) {
453941aa11dSmpi if (prio != RTP_ANY &&
454941aa11dSmpi (rt->rt_priority & RTP_MASK) != (prio & RTP_MASK))
455941aa11dSmpi continue;
456941aa11dSmpi
457941aa11dSmpi if (gateway == NULL)
458941aa11dSmpi break;
459941aa11dSmpi
460941aa11dSmpi if (rt->rt_gateway->sa_len == gateway->sa_len &&
461941aa11dSmpi memcmp(rt->rt_gateway, gateway, gateway->sa_len) == 0)
462941aa11dSmpi break;
463941aa11dSmpi }
464e0d1f3a9Sjmatthew if (rt != NULL)
465ea00ba5eSmpi rtref(rt);
466e0d1f3a9Sjmatthew
467ca268887Sdlg SRPL_LEAVE(&sr);
468e0d1f3a9Sjmatthew out:
469e0d1f3a9Sjmatthew srp_leave(&nsr);
470ea00ba5eSmpi
471ea00ba5eSmpi return (rt);
4723a49c894Smpi }
4733a49c894Smpi
4743a49c894Smpi struct rtentry *
rtable_match(unsigned int rtableid,const struct sockaddr * dst,uint32_t * src)475a3e24bb9Sbluhm rtable_match(unsigned int rtableid, const struct sockaddr *dst, uint32_t *src)
4763a49c894Smpi {
4773a49c894Smpi struct art_root *ar;
478b853bc15Smpi struct art_node *an;
4791ed40c79Smpi struct rtentry *rt = NULL;
480e0d1f3a9Sjmatthew struct srp_ref sr, nsr;
481a3e24bb9Sbluhm const uint8_t *addr;
4826ffe7926Smpi int hash;
4833a49c894Smpi
4843a49c894Smpi ar = rtable_get(rtableid, dst->sa_family);
4853a49c894Smpi if (ar == NULL)
4863a49c894Smpi return (NULL);
4873a49c894Smpi
4881ff67b00Smpi addr = satoaddr(ar, dst);
4891ed40c79Smpi
490e0d1f3a9Sjmatthew an = art_match(ar, addr, &nsr);
4913a49c894Smpi if (an == NULL)
4921ed40c79Smpi goto out;
4933a49c894Smpi
494f8e0b8c2Smpi rt = SRPL_FIRST(&sr, &an->an_rtlist);
49555bfdc9fSbluhm if (rt == NULL) {
49655bfdc9fSbluhm SRPL_LEAVE(&sr);
49755bfdc9fSbluhm goto out;
49855bfdc9fSbluhm }
49930355f36Smpi rtref(rt);
500ca268887Sdlg SRPL_LEAVE(&sr);
50130355f36Smpi
5026ffe7926Smpi /* Gateway selection by Hash-Threshold (RFC 2992) */
503d6737014Smpi if ((hash = rt_hash(rt, dst, src)) != -1) {
5046ffe7926Smpi struct rtentry *mrt;
5056ffe7926Smpi int threshold, npaths = 0;
5066ffe7926Smpi
5076ffe7926Smpi KASSERT(hash <= 0xffff);
5086ffe7926Smpi
509ca268887Sdlg SRPL_FOREACH(mrt, &sr, &an->an_rtlist, rt_next) {
5106ffe7926Smpi /* Only count nexthops with the same priority. */
5116ffe7926Smpi if (mrt->rt_priority == rt->rt_priority)
5126ffe7926Smpi npaths++;
5136ffe7926Smpi }
514ca268887Sdlg SRPL_LEAVE(&sr);
5156ffe7926Smpi
5166ffe7926Smpi threshold = (0xffff / npaths) + 1;
5176ffe7926Smpi
518e0d1f3a9Sjmatthew /*
519e0d1f3a9Sjmatthew * we have no protection against concurrent modification of the
520e0d1f3a9Sjmatthew * route list attached to the node, so we won't necessarily
521e0d1f3a9Sjmatthew * have the same number of routes. for most modifications,
522e0d1f3a9Sjmatthew * we'll pick a route that we wouldn't have if we only saw the
523e0d1f3a9Sjmatthew * list before or after the change. if we were going to use
524e0d1f3a9Sjmatthew * the last available route, but it got removed, we'll hit
525e0d1f3a9Sjmatthew * the end of the list and then pick the first route.
526e0d1f3a9Sjmatthew */
527e0d1f3a9Sjmatthew
528f8e0b8c2Smpi mrt = SRPL_FIRST(&sr, &an->an_rtlist);
5296ffe7926Smpi while (hash > threshold && mrt != NULL) {
5306ffe7926Smpi if (mrt->rt_priority == rt->rt_priority)
5316ffe7926Smpi hash -= threshold;
532f8e0b8c2Smpi mrt = SRPL_FOLLOW(&sr, mrt, rt_next);
5336ffe7926Smpi }
5346ffe7926Smpi
5356ffe7926Smpi if (mrt != NULL) {
5366ffe7926Smpi rtref(mrt);
5376ffe7926Smpi rtfree(rt);
5386ffe7926Smpi rt = mrt;
5396ffe7926Smpi }
540ca268887Sdlg SRPL_LEAVE(&sr);
5416ffe7926Smpi }
5421ed40c79Smpi out:
543e0d1f3a9Sjmatthew srp_leave(&nsr);
54430355f36Smpi return (rt);
5453a49c894Smpi }
5463a49c894Smpi
5473a49c894Smpi int
rtable_insert(unsigned int rtableid,struct sockaddr * dst,const struct sockaddr * mask,const struct sockaddr * gateway,uint8_t prio,struct rtentry * rt)5483a49c894Smpi rtable_insert(unsigned int rtableid, struct sockaddr *dst,
5499a32bf7bSbluhm const struct sockaddr *mask, const struct sockaddr *gateway, uint8_t prio,
55036efdebbSmpi struct rtentry *rt)
5513a49c894Smpi {
5523a49c894Smpi struct rtentry *mrt;
553e0d1f3a9Sjmatthew struct srp_ref sr;
5543a49c894Smpi struct art_root *ar;
5553a49c894Smpi struct art_node *an, *prev;
556a3e24bb9Sbluhm const uint8_t *addr;
55738caa7a0Smpi int plen;
558eb83904dSdlg unsigned int rt_flags;
559e0d1f3a9Sjmatthew int error = 0;
5603a49c894Smpi
5613a49c894Smpi ar = rtable_get(rtableid, dst->sa_family);
5623a49c894Smpi if (ar == NULL)
5633a49c894Smpi return (EAFNOSUPPORT);
5643a49c894Smpi
5651ff67b00Smpi addr = satoaddr(ar, dst);
566ad38292eSmpi plen = rtable_satoplen(dst->sa_family, mask);
56738caa7a0Smpi if (plen == -1)
56838caa7a0Smpi return (EINVAL);
5693a49c894Smpi
570e0d1f3a9Sjmatthew rtref(rt); /* guarantee rtfree won't do anything during insert */
5719b397541Sjmatthew rw_enter_write(&ar->ar_lock);
572e0d1f3a9Sjmatthew
57336efdebbSmpi /* Do not permit exactly the same dst/mask/gw pair. */
574e0d1f3a9Sjmatthew an = art_lookup(ar, addr, plen, &sr);
575e0d1f3a9Sjmatthew srp_leave(&sr); /* an can't go away while we have the lock */
576c42687bdSmpi if (an_match(an, dst, plen)) {
57736efdebbSmpi struct rtentry *mrt;
57836efdebbSmpi int mpathok = ISSET(rt->rt_flags, RTF_MPATH);
57936efdebbSmpi
580cf41c536Smpi SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next) {
58136efdebbSmpi if (prio != RTP_ANY &&
58236efdebbSmpi (mrt->rt_priority & RTP_MASK) != (prio & RTP_MASK))
58336efdebbSmpi continue;
58436efdebbSmpi
5851f5a9102Smpi if (!mpathok ||
5861f5a9102Smpi (mrt->rt_gateway->sa_len == gateway->sa_len &&
587daa2315fSbluhm memcmp(mrt->rt_gateway, gateway,
588daa2315fSbluhm gateway->sa_len) == 0)) {
589e0d1f3a9Sjmatthew error = EEXIST;
590e0d1f3a9Sjmatthew goto leave;
5911f5a9102Smpi }
59236efdebbSmpi }
59336efdebbSmpi }
59436efdebbSmpi
5955c969a7eSbluhm an = art_get(plen);
596e0d1f3a9Sjmatthew if (an == NULL) {
597e0d1f3a9Sjmatthew error = ENOBUFS;
598e0d1f3a9Sjmatthew goto leave;
599e0d1f3a9Sjmatthew }
6003a49c894Smpi
601eb83904dSdlg /* prepare for immediate operation if insert succeeds */
602eb83904dSdlg rt_flags = rt->rt_flags;
6033a49c894Smpi rt->rt_flags &= ~RTF_MPATH;
604eb83904dSdlg rt->rt_dest = dst;
605eb83904dSdlg rt->rt_plen = plen;
606eb83904dSdlg SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
607eb83904dSdlg
608eb83904dSdlg prev = art_insert(ar, an, addr, plen);
609eb83904dSdlg if (prev != an) {
610eb83904dSdlg SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,
611eb83904dSdlg rt_next);
612eb83904dSdlg rt->rt_flags = rt_flags;
6131b3ea614Smpi art_put(an);
614eb83904dSdlg
615e0d1f3a9Sjmatthew if (prev == NULL) {
616e0d1f3a9Sjmatthew error = ESRCH;
617e0d1f3a9Sjmatthew goto leave;
618e0d1f3a9Sjmatthew }
619eb83904dSdlg
6203a49c894Smpi an = prev;
6213a49c894Smpi
622cf41c536Smpi mrt = SRPL_FIRST_LOCKED(&an->an_rtlist);
6233a49c894Smpi KASSERT(mrt != NULL);
6243a49c894Smpi KASSERT((rt->rt_flags & RTF_MPATH) || mrt->rt_priority != prio);
6253a49c894Smpi
6263a49c894Smpi /*
6273a49c894Smpi * An ART node with the same destination/netmask already
6283a49c894Smpi * exists, MPATH conflict must have been already checked.
6293a49c894Smpi */
6303a49c894Smpi if (rt->rt_flags & RTF_MPATH) {
6313a49c894Smpi /*
6323a49c894Smpi * Only keep the RTF_MPATH flag if two routes have
6333a49c894Smpi * the same gateway.
6343a49c894Smpi */
6353a49c894Smpi rt->rt_flags &= ~RTF_MPATH;
636cf41c536Smpi SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next) {
6373a49c894Smpi if (mrt->rt_priority == prio) {
6383a49c894Smpi mrt->rt_flags |= RTF_MPATH;
6393a49c894Smpi rt->rt_flags |= RTF_MPATH;
6403a49c894Smpi }
6413a49c894Smpi }
6423a49c894Smpi }
643eb83904dSdlg
644eb83904dSdlg /* Put newly inserted entry at the right place. */
6459b397541Sjmatthew rtable_mpath_insert(an, rt);
6463a49c894Smpi }
647e0d1f3a9Sjmatthew leave:
6489b397541Sjmatthew rw_exit_write(&ar->ar_lock);
649e0d1f3a9Sjmatthew rtfree(rt);
650e0d1f3a9Sjmatthew return (error);
6513a49c894Smpi }
6523a49c894Smpi
6533a49c894Smpi int
rtable_delete(unsigned int rtableid,const struct sockaddr * dst,const struct sockaddr * mask,struct rtentry * rt)6549a32bf7bSbluhm rtable_delete(unsigned int rtableid, const struct sockaddr *dst,
6559a32bf7bSbluhm const struct sockaddr *mask, struct rtentry *rt)
6563a49c894Smpi {
6573a49c894Smpi struct art_root *ar;
658700b4ca9Smpi struct art_node *an;
659e0d1f3a9Sjmatthew struct srp_ref sr;
660a3e24bb9Sbluhm const uint8_t *addr;
66138caa7a0Smpi int plen;
662a2082c75Smpi struct rtentry *mrt;
663a2082c75Smpi int npaths = 0;
6649b397541Sjmatthew int error = 0;
665a2082c75Smpi
666700b4ca9Smpi ar = rtable_get(rtableid, dst->sa_family);
667700b4ca9Smpi if (ar == NULL)
668700b4ca9Smpi return (EAFNOSUPPORT);
669700b4ca9Smpi
670700b4ca9Smpi addr = satoaddr(ar, dst);
671700b4ca9Smpi plen = rtable_satoplen(dst->sa_family, mask);
6722a5d130bSclaudio if (plen == -1)
6732a5d130bSclaudio return (EINVAL);
674700b4ca9Smpi
6759b397541Sjmatthew rtref(rt); /* guarantee rtfree won't do anything under ar_lock */
6769b397541Sjmatthew rw_enter_write(&ar->ar_lock);
677e0d1f3a9Sjmatthew an = art_lookup(ar, addr, plen, &sr);
678e0d1f3a9Sjmatthew srp_leave(&sr); /* an can't go away while we have the lock */
679e0d1f3a9Sjmatthew
680700b4ca9Smpi /* Make sure we've got a perfect match. */
681c42687bdSmpi if (!an_match(an, dst, plen)) {
6829b397541Sjmatthew error = ESRCH;
6839b397541Sjmatthew goto leave;
6849b397541Sjmatthew }
685700b4ca9Smpi
686a2082c75Smpi /*
687a2082c75Smpi * If other multipath route entries are still attached to
688a2082c75Smpi * this ART node we only have to unlink it.
689a2082c75Smpi */
690cf41c536Smpi SRPL_FOREACH_LOCKED(mrt, &an->an_rtlist, rt_next)
691a2082c75Smpi npaths++;
692a2082c75Smpi
693a2082c75Smpi if (npaths > 1) {
6947bfa74bbSbluhm KASSERT(refcnt_read(&rt->rt_refcnt) >= 1);
695cf41c536Smpi SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry,
696cf41c536Smpi rt_next);
697a2082c75Smpi
698cf41c536Smpi mrt = SRPL_FIRST_LOCKED(&an->an_rtlist);
699a2082c75Smpi if (npaths == 2)
700a2082c75Smpi mrt->rt_flags &= ~RTF_MPATH;
7019b397541Sjmatthew
7029b397541Sjmatthew goto leave;
703a2082c75Smpi }
7043a49c894Smpi
70538caa7a0Smpi if (art_delete(ar, an, addr, plen) == NULL)
7069b397541Sjmatthew panic("art_delete failed to find node %p", an);
707a2082c75Smpi
7087bfa74bbSbluhm KASSERT(refcnt_read(&rt->rt_refcnt) >= 1);
709cf41c536Smpi SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist, rt, rtentry, rt_next);
7101b3ea614Smpi art_put(an);
7119b397541Sjmatthew
7129b397541Sjmatthew leave:
7139b397541Sjmatthew rw_exit_write(&ar->ar_lock);
7149b397541Sjmatthew rtfree(rt);
7159b397541Sjmatthew
7169b397541Sjmatthew return (error);
7173a49c894Smpi }
7183a49c894Smpi
7193a49c894Smpi struct rtable_walk_cookie {
7203a49c894Smpi int (*rwc_func)(struct rtentry *, void *, unsigned int);
7213a49c894Smpi void *rwc_arg;
722cf34c7c3Smpi struct rtentry **rwc_prt;
7233a49c894Smpi unsigned int rwc_rid;
7243a49c894Smpi };
7253a49c894Smpi
7263a49c894Smpi /*
7273a49c894Smpi * Helper for rtable_walk to keep the ART code free from any "struct rtentry".
7283a49c894Smpi */
7293a49c894Smpi int
rtable_walk_helper(struct art_node * an,void * xrwc)7303a49c894Smpi rtable_walk_helper(struct art_node *an, void *xrwc)
7313a49c894Smpi {
73248938141Smpi struct srp_ref sr;
7333a49c894Smpi struct rtable_walk_cookie *rwc = xrwc;
73448938141Smpi struct rtentry *rt;
7353a49c894Smpi int error = 0;
7363a49c894Smpi
73748938141Smpi SRPL_FOREACH(rt, &sr, &an->an_rtlist, rt_next) {
738cf34c7c3Smpi error = (*rwc->rwc_func)(rt, rwc->rwc_arg, rwc->rwc_rid);
739cf34c7c3Smpi if (error != 0)
7403a49c894Smpi break;
7413a49c894Smpi }
742cf34c7c3Smpi if (rwc->rwc_prt != NULL && rt != NULL) {
743cf34c7c3Smpi rtref(rt);
744cf34c7c3Smpi *rwc->rwc_prt = rt;
745cf34c7c3Smpi }
74648938141Smpi SRPL_LEAVE(&sr);
7473a49c894Smpi
7483a49c894Smpi return (error);
7493a49c894Smpi }
7503a49c894Smpi
7513a49c894Smpi int
rtable_walk(unsigned int rtableid,sa_family_t af,struct rtentry ** prt,int (* func)(struct rtentry *,void *,unsigned int),void * arg)752cf34c7c3Smpi rtable_walk(unsigned int rtableid, sa_family_t af, struct rtentry **prt,
7533a49c894Smpi int (*func)(struct rtentry *, void *, unsigned int), void *arg)
7543a49c894Smpi {
7553a49c894Smpi struct art_root *ar;
7563a49c894Smpi struct rtable_walk_cookie rwc;
75730b29ff5Sjmatthew int error;
7583a49c894Smpi
7593a49c894Smpi ar = rtable_get(rtableid, af);
7603a49c894Smpi if (ar == NULL)
7613a49c894Smpi return (EAFNOSUPPORT);
7623a49c894Smpi
7633a49c894Smpi rwc.rwc_func = func;
7643a49c894Smpi rwc.rwc_arg = arg;
765cf34c7c3Smpi rwc.rwc_prt = prt;
7663a49c894Smpi rwc.rwc_rid = rtableid;
7673a49c894Smpi
768cf34c7c3Smpi error = art_walk(ar, rtable_walk_helper, &rwc);
76930b29ff5Sjmatthew
77030b29ff5Sjmatthew return (error);
7713a49c894Smpi }
7723a49c894Smpi
7735ea6c205Smpi struct rtentry *
rtable_iterate(struct rtentry * rt0)7745ea6c205Smpi rtable_iterate(struct rtentry *rt0)
7755ea6c205Smpi {
7761c3d6e8fSmpi struct rtentry *rt = NULL;
7771c3d6e8fSmpi struct srp_ref sr;
7785ea6c205Smpi
7791c3d6e8fSmpi rt = SRPL_NEXT(&sr, rt0, rt_next);
7805ea6c205Smpi if (rt != NULL)
7815ea6c205Smpi rtref(rt);
7821c3d6e8fSmpi SRPL_LEAVE(&sr);
7831c3d6e8fSmpi rtfree(rt0);
7841c3d6e8fSmpi return (rt);
7855ea6c205Smpi }
7865ea6c205Smpi
7873a49c894Smpi int
rtable_mpath_capable(unsigned int rtableid,sa_family_t af)7883a49c894Smpi rtable_mpath_capable(unsigned int rtableid, sa_family_t af)
7893a49c894Smpi {
7903a49c894Smpi return (1);
7913a49c894Smpi }
7923a49c894Smpi
7937d1be0daSmpi int
rtable_mpath_reprio(unsigned int rtableid,struct sockaddr * dst,int plen,uint8_t prio,struct rtentry * rt)7947d1be0daSmpi rtable_mpath_reprio(unsigned int rtableid, struct sockaddr *dst,
795798874c8Sclaudio int plen, uint8_t prio, struct rtentry *rt)
7963a49c894Smpi {
7977d1be0daSmpi struct art_root *ar;
7987d1be0daSmpi struct art_node *an;
799e0d1f3a9Sjmatthew struct srp_ref sr;
800a3e24bb9Sbluhm const uint8_t *addr;
8019b397541Sjmatthew int error = 0;
802b6cffe09Smpi
8037d1be0daSmpi ar = rtable_get(rtableid, dst->sa_family);
8047d1be0daSmpi if (ar == NULL)
8057d1be0daSmpi return (EAFNOSUPPORT);
8067d1be0daSmpi
8077d1be0daSmpi addr = satoaddr(ar, dst);
8087d1be0daSmpi
8099b397541Sjmatthew rw_enter_write(&ar->ar_lock);
810e0d1f3a9Sjmatthew an = art_lookup(ar, addr, plen, &sr);
811e0d1f3a9Sjmatthew srp_leave(&sr); /* an can't go away while we have the lock */
812e0d1f3a9Sjmatthew
8137d1be0daSmpi /* Make sure we've got a perfect match. */
81471f3119fSmpi if (!an_match(an, dst, plen)) {
8159b397541Sjmatthew error = ESRCH;
81671f3119fSmpi } else if (SRPL_FIRST_LOCKED(&an->an_rtlist) == rt &&
81771f3119fSmpi SRPL_NEXT_LOCKED(rt, rt_next) == NULL) {
81871f3119fSmpi /*
81971f3119fSmpi * If there's only one entry on the list do not go
82071f3119fSmpi * through an insert/remove cycle. This is done to
82171f3119fSmpi * guarantee that ``an->an_rtlist'' is never empty
82271f3119fSmpi * when a node is in the tree.
82371f3119fSmpi */
82471f3119fSmpi rt->rt_priority = prio;
82571f3119fSmpi } else {
8269b397541Sjmatthew rtref(rt); /* keep rt alive in between remove and insert */
8279b397541Sjmatthew SRPL_REMOVE_LOCKED(&rt_rc, &an->an_rtlist,
8289b397541Sjmatthew rt, rtentry, rt_next);
829b6cffe09Smpi rt->rt_priority = prio;
8309b397541Sjmatthew rtable_mpath_insert(an, rt);
8319b397541Sjmatthew rtfree(rt);
832b4d8ed19Smpi error = EAGAIN;
8339b397541Sjmatthew }
8349b397541Sjmatthew rw_exit_write(&ar->ar_lock);
8359b397541Sjmatthew
8369b397541Sjmatthew return (error);
8379b397541Sjmatthew }
8389b397541Sjmatthew
8399b397541Sjmatthew void
rtable_mpath_insert(struct art_node * an,struct rtentry * rt)8409b397541Sjmatthew rtable_mpath_insert(struct art_node *an, struct rtentry *rt)
8419b397541Sjmatthew {
8429b397541Sjmatthew struct rtentry *mrt, *prt = NULL;
8439b397541Sjmatthew uint8_t prio = rt->rt_priority;
844b6cffe09Smpi
845f76c3713Smpi if ((mrt = SRPL_FIRST_LOCKED(&an->an_rtlist)) == NULL) {
846f76c3713Smpi SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
847f76c3713Smpi return;
848f76c3713Smpi }
849f76c3713Smpi
850f76c3713Smpi /* Iterate until we find the route to be placed after ``rt''. */
851f76c3713Smpi while (mrt->rt_priority <= prio && SRPL_NEXT_LOCKED(mrt, rt_next)) {
852a1f50d91Smpi prt = mrt;
853cf41c536Smpi mrt = SRPL_NEXT_LOCKED(mrt, rt_next);
854b6cffe09Smpi }
855b6cffe09Smpi
856f76c3713Smpi if (mrt->rt_priority <= prio) {
857cf41c536Smpi SRPL_INSERT_AFTER_LOCKED(&rt_rc, mrt, rt, rt_next);
858f76c3713Smpi } else if (prt != NULL) {
859f76c3713Smpi SRPL_INSERT_AFTER_LOCKED(&rt_rc, prt, rt, rt_next);
860a1f50d91Smpi } else {
861cf41c536Smpi SRPL_INSERT_HEAD_LOCKED(&rt_rc, &an->an_rtlist, rt, rt_next);
862b6cffe09Smpi }
8633a49c894Smpi }
8643a49c894Smpi
865c42687bdSmpi /*
866c42687bdSmpi * Returns 1 if ``an'' perfectly matches (``dst'', ``plen''), 0 otherwise.
867c42687bdSmpi */
868c42687bdSmpi int
an_match(struct art_node * an,const struct sockaddr * dst,int plen)8699a32bf7bSbluhm an_match(struct art_node *an, const struct sockaddr *dst, int plen)
870c42687bdSmpi {
871c42687bdSmpi struct rtentry *rt;
872c42687bdSmpi struct srp_ref sr;
873c42687bdSmpi int match;
874c42687bdSmpi
875c42687bdSmpi if (an == NULL || an->an_plen != plen)
876c42687bdSmpi return (0);
877c42687bdSmpi
878c42687bdSmpi rt = SRPL_FIRST(&sr, &an->an_rtlist);
879d75bd533Sbluhm match = (rt != NULL && memcmp(rt->rt_dest, dst, dst->sa_len) == 0);
880c42687bdSmpi SRPL_LEAVE(&sr);
881c42687bdSmpi
882c42687bdSmpi return (match);
883c42687bdSmpi }
884c42687bdSmpi
885cf41c536Smpi void
rtentry_ref(void * null,void * xrt)886cf41c536Smpi rtentry_ref(void *null, void *xrt)
887cf41c536Smpi {
888cf41c536Smpi struct rtentry *rt = xrt;
889cf41c536Smpi
890cf41c536Smpi rtref(rt);
891cf41c536Smpi }
892cf41c536Smpi
893cf41c536Smpi void
rtentry_unref(void * null,void * xrt)894cf41c536Smpi rtentry_unref(void *null, void *xrt)
895cf41c536Smpi {
896cf41c536Smpi struct rtentry *rt = xrt;
897cf41c536Smpi
898cf41c536Smpi rtfree(rt);
899cf41c536Smpi }
900cf41c536Smpi
9013a49c894Smpi /*
9021ff67b00Smpi * Return a pointer to the address (key). This is an heritage from the
9031ff67b00Smpi * BSD radix tree needed to skip the non-address fields from the flavor
9041ff67b00Smpi * of "struct sockaddr" used by this routing table.
9051ff67b00Smpi */
906a3e24bb9Sbluhm static inline const uint8_t *
satoaddr(struct art_root * at,const struct sockaddr * sa)907a3e24bb9Sbluhm satoaddr(struct art_root *at, const struct sockaddr *sa)
9081ff67b00Smpi {
909a3e24bb9Sbluhm return (((const uint8_t *)sa) + at->ar_off);
9101ff67b00Smpi }
9111ff67b00Smpi
9121ff67b00Smpi /*
9133a49c894Smpi * Return the prefix length of a mask.
9143a49c894Smpi */
915ad38292eSmpi int
rtable_satoplen(sa_family_t af,const struct sockaddr * mask)9169a32bf7bSbluhm rtable_satoplen(sa_family_t af, const struct sockaddr *mask)
9173a49c894Smpi {
918c90e7dccSbluhm const struct domain *dp;
9193a49c894Smpi uint8_t *ap, *ep;
920ad38292eSmpi int mlen, plen = 0;
921ad38292eSmpi int i;
922ad38292eSmpi
923ad38292eSmpi for (i = 0; (dp = domains[i]) != NULL; i++) {
924ad38292eSmpi if (dp->dom_rtoffset == 0)
925ad38292eSmpi continue;
926ad38292eSmpi
927ad38292eSmpi if (af == dp->dom_family)
928ad38292eSmpi break;
929ad38292eSmpi }
930ad38292eSmpi if (dp == NULL)
931ad38292eSmpi return (-1);
9323a49c894Smpi
9333a49c894Smpi /* Host route */
9343a49c894Smpi if (mask == NULL)
935ad38292eSmpi return (dp->dom_maxplen);
9363a49c894Smpi
9373a49c894Smpi mlen = mask->sa_len;
9383a49c894Smpi
9393a49c894Smpi /* Default route */
9403a49c894Smpi if (mlen == 0)
9413a49c894Smpi return (0);
9423a49c894Smpi
943ad38292eSmpi ap = (uint8_t *)((uint8_t *)mask) + dp->dom_rtoffset;
9443a49c894Smpi ep = (uint8_t *)((uint8_t *)mask) + mlen;
9453a49c894Smpi if (ap > ep)
9463a49c894Smpi return (-1);
9473a49c894Smpi
9482a5d130bSclaudio /* Trim trailing zeroes. */
949fab4809eSanton while (ap < ep && ep[-1] == 0)
9502a5d130bSclaudio ep--;
9512a5d130bSclaudio
9523a49c894Smpi if (ap == ep)
9533a49c894Smpi return (0);
9543a49c894Smpi
9553a49c894Smpi /* "Beauty" adapted from sbin/route/show.c ... */
9563a49c894Smpi while (ap < ep) {
9572a5d130bSclaudio switch (*ap++) {
9583a49c894Smpi case 0xff:
9593a49c894Smpi plen += 8;
9603a49c894Smpi break;
9613a49c894Smpi case 0xfe:
9623a49c894Smpi plen += 7;
9633a49c894Smpi goto out;
9643a49c894Smpi case 0xfc:
9653a49c894Smpi plen += 6;
9663a49c894Smpi goto out;
9673a49c894Smpi case 0xf8:
9683a49c894Smpi plen += 5;
9693a49c894Smpi goto out;
9703a49c894Smpi case 0xf0:
9713a49c894Smpi plen += 4;
9723a49c894Smpi goto out;
9733a49c894Smpi case 0xe0:
9743a49c894Smpi plen += 3;
9753a49c894Smpi goto out;
9763a49c894Smpi case 0xc0:
9773a49c894Smpi plen += 2;
9783a49c894Smpi goto out;
9793a49c894Smpi case 0x80:
9803a49c894Smpi plen += 1;
9813a49c894Smpi goto out;
9823a49c894Smpi default:
9833a49c894Smpi /* Non contiguous mask. */
9843a49c894Smpi return (-1);
9853a49c894Smpi }
9863a49c894Smpi }
9873a49c894Smpi
9883a49c894Smpi out:
9892a5d130bSclaudio if (plen > dp->dom_maxplen || ap != ep)
9902a5d130bSclaudio return -1;
9913a49c894Smpi
9923a49c894Smpi return (plen);
9933a49c894Smpi }
994