xref: /openbsd-src/sys/net/rtable.c (revision 91defc8b61fe9ff881eb2b8e8b6070d9e4869019)
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