xref: /openbsd-src/usr.sbin/bgpd/rde_trie.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: rde_trie.c,v 1.17 2022/12/28 21:30:16 jmc Exp $ */
2a02daaddSclaudio 
3a02daaddSclaudio /*
4a02daaddSclaudio  * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org>
5a02daaddSclaudio  *
6a02daaddSclaudio  * Permission to use, copy, modify, and distribute this software for any
7a02daaddSclaudio  * purpose with or without fee is hereby granted, provided that the above
8a02daaddSclaudio  * copyright notice and this permission notice appear in all copies.
9a02daaddSclaudio  *
10a02daaddSclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a02daaddSclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a02daaddSclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a02daaddSclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a02daaddSclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a02daaddSclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a02daaddSclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a02daaddSclaudio  */
18a02daaddSclaudio #include <sys/types.h>
19a02daaddSclaudio #include <sys/queue.h>
20a02daaddSclaudio 
21a02daaddSclaudio #include <netinet/in.h>
22a02daaddSclaudio 
23a02daaddSclaudio #include <stdlib.h>
24a02daaddSclaudio #include <stdio.h>
25a02daaddSclaudio #include <string.h>
26a02daaddSclaudio 
27a02daaddSclaudio #include "bgpd.h"
28a02daaddSclaudio #include "rde.h"
29a02daaddSclaudio 
30a02daaddSclaudio /*
31a02daaddSclaudio  * Bitwise compressed trie for prefix-sets.
32a02daaddSclaudio  * Since the trie is path compressed the traversing function needs to check
33a02daaddSclaudio  * that the lookup is still on path before taking a branch.
34a02daaddSclaudio  * There are two types of nodes in the trie. Internal branch nodes and real
35a02daaddSclaudio  * nodes. Internal nodes are added when needed because off path nodes are being
36a02daaddSclaudio  * inserted and are just used for branching.
37a02daaddSclaudio  * During lookup every node defines which bit is checked for branching. This
38*3a50f0a9Sjmc  * offset is strictly increasing. For IPv4 the maximum is therefore 32 levels.
39a02daaddSclaudio  * Every node checks the bit at position plen to decide which branch to take.
40a02daaddSclaudio  * The real nodes also include a prefixlen mask which represents the prefixlen
41a02daaddSclaudio  * range that was defined. The prefixlen mask for IPv4 only has 32 bits but
42a02daaddSclaudio  * the prefixlen range is from 0 - 32 which needs 33bits. Now prefixlen 0 is
43a02daaddSclaudio  * special and only one element -- the default route -- can have prefixlen 0.
44a02daaddSclaudio  * So this default route is added as a flag to the trie_head and needs to be
45a02daaddSclaudio  * checked independent of the trie when doing a lookup.
46a02daaddSclaudio  * On insertion a prefix is added with its prefixlen plen and a min & max
47a02daaddSclaudio  * for the prefixlen range. The following precondition needs to hold
48a02daaddSclaudio  *     plen <= min <= max
49a02daaddSclaudio  * To only match the prefix itself min and max are set to plen.
50a02daaddSclaudio  * If a same prefix (addr/plen) is added to the trie but with different
51a02daaddSclaudio  * min & max values then the masks of both nodes are merged with a binary OR.
52a02daaddSclaudio  * The match function returns true the moment the first node is found which
53a02daaddSclaudio  * covers the looked up prefix and where the prefixlen mask matches for the
54451e36b1Sdenis  * looked up prefixlen. The moment the plen of a node is bigger than the
55451e36b1Sdenis  * prefixlen of the looked up prefix the search can be stopped since
56451e36b1Sdenis  * there will be no match.
57a02daaddSclaudio  */
58a02daaddSclaudio struct tentry_v4 {
59a02daaddSclaudio 	struct tentry_v4	*trie[2];
6059e404fbSclaudio 	struct set_table	*set;	/* for roa source-as set */
61a02daaddSclaudio 	struct in_addr		 addr;
62a02daaddSclaudio 	struct in_addr		 plenmask;
6339386878Sclaudio 	uint8_t			 plen;
6439386878Sclaudio 	uint8_t			 node;
65a02daaddSclaudio };
66a02daaddSclaudio 
67a02daaddSclaudio struct tentry_v6 {
68a02daaddSclaudio 	struct tentry_v6	*trie[2];
6959e404fbSclaudio 	struct set_table	*set;	/* for roa source-as set */
70a02daaddSclaudio 	struct in6_addr		 addr;
71a02daaddSclaudio 	struct in6_addr		 plenmask;
7239386878Sclaudio 	uint8_t			 plen;
7339386878Sclaudio 	uint8_t			 node;
74a02daaddSclaudio };
75a02daaddSclaudio 
76a02daaddSclaudio /*
77a02daaddSclaudio  * Find first different bit between a & b starting from the MSB,
78a02daaddSclaudio  * a & b have to be different.
79a02daaddSclaudio  */
80a02daaddSclaudio static int
inet4findmsb(struct in_addr * a,struct in_addr * b)81a02daaddSclaudio inet4findmsb(struct in_addr *a, struct in_addr *b)
82a02daaddSclaudio {
83a02daaddSclaudio 	int r = 0;
8439386878Sclaudio 	uint32_t v;
85a02daaddSclaudio 
86a02daaddSclaudio 	v = ntohl(a->s_addr ^ b->s_addr);
87a02daaddSclaudio 	if (v > 0xffff) { r += 16; v >>= 16; }
88a02daaddSclaudio 	if (v > 0x00ff) { r += 8; v >>= 8; }
89a02daaddSclaudio 	if (v > 0x000f) { r += 4; v >>= 4; }
90a02daaddSclaudio 	if (v > 0x0003) { r += 2; v >>= 2; }
91a02daaddSclaudio 	if (v > 0x0001) { r += 1; }
92a02daaddSclaudio 
93a02daaddSclaudio 	return 31 - r;
94a02daaddSclaudio }
95a02daaddSclaudio 
96a02daaddSclaudio /*
97a02daaddSclaudio  * Find first different bit between a & b starting from the MSB,
98a02daaddSclaudio  * a & b have to be different.
99a02daaddSclaudio  */
100a02daaddSclaudio static int
inet6findmsb(struct in6_addr * a,struct in6_addr * b)101a02daaddSclaudio inet6findmsb(struct in6_addr *a, struct in6_addr *b)
102a02daaddSclaudio {
103a02daaddSclaudio 	int r = 0;
10439386878Sclaudio 	uint8_t i, x;
105a02daaddSclaudio 
106a02daaddSclaudio 	for (i = 0; i < sizeof(*a) && a->s6_addr[i] == b->s6_addr[i]; i++)
107a02daaddSclaudio 		;
108a02daaddSclaudio 	/* first different octet */
109a02daaddSclaudio 	x = a->s6_addr[i] ^ b->s6_addr[i];
110a02daaddSclaudio 
111a02daaddSclaudio 	/* first different bit */
112a02daaddSclaudio 	if (x > 0xf) { r += 4; x >>= 4; }
113a02daaddSclaudio 	if (x > 0x3) { r += 2; x >>= 2; }
114a02daaddSclaudio 	if (x > 0x1) { r += 1; }
115a02daaddSclaudio 
116a02daaddSclaudio 	return i * 8 + 7 - r;
117a02daaddSclaudio }
118a02daaddSclaudio 
119a02daaddSclaudio static int
inet4isset(struct in_addr * addr,uint8_t bit)12039386878Sclaudio inet4isset(struct in_addr *addr, uint8_t bit)
121a02daaddSclaudio {
122ee3cba03Sclaudio 	return addr->s_addr & htonl(1U << (31 - bit));
123a02daaddSclaudio }
124a02daaddSclaudio 
125a02daaddSclaudio static int
inet6isset(struct in6_addr * addr,uint8_t bit)12639386878Sclaudio inet6isset(struct in6_addr *addr, uint8_t bit)
127a02daaddSclaudio {
128a02daaddSclaudio 	return addr->s6_addr[bit / 8] & (0x80 >> (bit % 8));
129a02daaddSclaudio }
130a02daaddSclaudio 
131a02daaddSclaudio static void
inet4setbit(struct in_addr * addr,uint8_t bit)13239386878Sclaudio inet4setbit(struct in_addr *addr, uint8_t bit)
133a02daaddSclaudio {
134a02daaddSclaudio 	/* bit 0 sets the MSB and 31 sets the LSB */
135ee3cba03Sclaudio 	addr->s_addr |= htonl(1U << (31 - bit));
136a02daaddSclaudio }
137a02daaddSclaudio 
138a02daaddSclaudio static void
inet6setbit(struct in6_addr * addr,uint8_t bit)13939386878Sclaudio inet6setbit(struct in6_addr *addr, uint8_t bit)
140a02daaddSclaudio {
141a02daaddSclaudio 	addr->s6_addr[bit / 8] |= (0x80 >> (bit % 8));
142a02daaddSclaudio }
143a02daaddSclaudio 
1444b62fd1aSclaudio static struct tentry_v4 *
trie_add_v4(struct trie_head * th,struct in_addr * prefix,uint8_t plen)14539386878Sclaudio trie_add_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen)
146a02daaddSclaudio {
147a02daaddSclaudio 	struct tentry_v4 *n, *new, *b, **prev;
1484b62fd1aSclaudio 	struct in_addr p;
149a02daaddSclaudio 
150a02daaddSclaudio 	inet4applymask(&p, prefix, plen);
151a02daaddSclaudio 
152a02daaddSclaudio 	/* walk tree finding spot to insert */
153a02daaddSclaudio 	prev = &th->root_v4;
154a02daaddSclaudio 	n = *prev;
155a02daaddSclaudio 	while (n) {
156a02daaddSclaudio 		struct in_addr mp;
15739386878Sclaudio 		uint8_t minlen;
158a02daaddSclaudio 
159a02daaddSclaudio 		minlen = n->plen > plen ? plen : n->plen;
160a02daaddSclaudio 		inet4applymask(&mp, &p, minlen);
161a02daaddSclaudio 		if (n->addr.s_addr != mp.s_addr) {
162a02daaddSclaudio 			/*
163a02daaddSclaudio 			 * out of path, insert intermediary node between
164a02daaddSclaudio 			 * np and n, then insert n and new node there
165a02daaddSclaudio 			 */
166a02daaddSclaudio 			if ((b = calloc(1, sizeof(*b))) == NULL)
1674b62fd1aSclaudio 				return NULL;
168e5b62a74Sclaudio 			rdemem.pset_cnt++;
169e5b62a74Sclaudio 			rdemem.pset_size += sizeof(*b);
170a02daaddSclaudio 			b->plen = inet4findmsb(&n->addr, &mp);
171a02daaddSclaudio 			inet4applymask(&b->addr, &n->addr, b->plen);
172a02daaddSclaudio 
173a02daaddSclaudio 			*prev = b;
174a02daaddSclaudio 			if (inet4isset(&n->addr, b->plen)) {
175a02daaddSclaudio 				b->trie[1] = n;
176a02daaddSclaudio 				prev = &b->trie[0];
177a02daaddSclaudio 			} else {
178a02daaddSclaudio 				b->trie[0] = n;
179a02daaddSclaudio 				prev = &b->trie[1];
180a02daaddSclaudio 			}
181a02daaddSclaudio 			n = NULL;
182a02daaddSclaudio 			break;
183a02daaddSclaudio 		}
184a02daaddSclaudio 
185a02daaddSclaudio 		if (n->plen > plen) {
186a02daaddSclaudio 			/* n is more specific, just insert new in between */
187a02daaddSclaudio 			break;
188a02daaddSclaudio 		}
189a02daaddSclaudio 
190a02daaddSclaudio 		if (n->plen == plen) {
191a02daaddSclaudio 			/* matching node, adjust */
1927ff9bf35Sclaudio 			if (n->node == 0)
1937ff9bf35Sclaudio 				th->v4_cnt++;
194a02daaddSclaudio 			n->node = 1;
1954b62fd1aSclaudio 			return n;
196a02daaddSclaudio 		}
197a02daaddSclaudio 
198a02daaddSclaudio 		/* no need to check for n->plen == 32 because of above if */
199a02daaddSclaudio 		if (inet4isset(&p, n->plen))
200a02daaddSclaudio 			prev = &n->trie[1];
201a02daaddSclaudio 		else
202a02daaddSclaudio 			prev = &n->trie[0];
203a02daaddSclaudio 		n = *prev;
204a02daaddSclaudio 	}
205a02daaddSclaudio 
206a02daaddSclaudio 	/* create new node */
207a02daaddSclaudio 	if ((new = calloc(1, sizeof(*new))) == NULL)
2084b62fd1aSclaudio 		return NULL;
2097ff9bf35Sclaudio 	th->v4_cnt++;
210e5b62a74Sclaudio 	rdemem.pset_cnt++;
211e5b62a74Sclaudio 	rdemem.pset_size += sizeof(*new);
212a02daaddSclaudio 	new->addr = p;
213a02daaddSclaudio 	new->plen = plen;
214a02daaddSclaudio 	new->node = 1;
215a02daaddSclaudio 
216a02daaddSclaudio 	/* link node */
217a02daaddSclaudio 	*prev = new;
218a02daaddSclaudio 	if (n) {
219a02daaddSclaudio 		if (inet4isset(&n->addr, new->plen))
220a02daaddSclaudio 			new->trie[1] = n;
221a02daaddSclaudio 		else
222a02daaddSclaudio 			new->trie[0] = n;
223a02daaddSclaudio 	}
2244b62fd1aSclaudio 	return new;
225a02daaddSclaudio }
226a02daaddSclaudio 
2274b62fd1aSclaudio static struct tentry_v6 *
trie_add_v6(struct trie_head * th,struct in6_addr * prefix,uint8_t plen)22839386878Sclaudio trie_add_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen)
229a02daaddSclaudio {
230a02daaddSclaudio 	struct tentry_v6 *n, *new, *b, **prev;
2314b62fd1aSclaudio 	struct in6_addr p;
232a02daaddSclaudio 
233a02daaddSclaudio 	inet6applymask(&p, prefix, plen);
234a02daaddSclaudio 
235a02daaddSclaudio 	/* walk tree finding spot to insert */
236a02daaddSclaudio 	prev = &th->root_v6;
237a02daaddSclaudio 	n = *prev;
238a02daaddSclaudio 	while (n) {
239a02daaddSclaudio 		struct in6_addr mp;
24039386878Sclaudio 		uint8_t minlen;
241a02daaddSclaudio 
242a02daaddSclaudio 		minlen = n->plen > plen ? plen : n->plen;
243a02daaddSclaudio 		inet6applymask(&mp, &p, minlen);
244a02daaddSclaudio 		if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) {
245a02daaddSclaudio 			/*
246a02daaddSclaudio 			 * out of path, insert intermediary node between
247a02daaddSclaudio 			 * np and n, then insert n and new node there
248a02daaddSclaudio 			 */
249a02daaddSclaudio 			if ((b = calloc(1, sizeof(*b))) == NULL)
2504b62fd1aSclaudio 				return NULL;
251e5b62a74Sclaudio 			rdemem.pset_cnt++;
252e5b62a74Sclaudio 			rdemem.pset_size += sizeof(*b);
253a02daaddSclaudio 			b->plen = inet6findmsb(&n->addr, &mp);
254a02daaddSclaudio 			inet6applymask(&b->addr, &n->addr, b->plen);
255a02daaddSclaudio 
256a02daaddSclaudio 			*prev = b;
257a02daaddSclaudio 			if (inet6isset(&n->addr, b->plen)) {
258a02daaddSclaudio 				b->trie[1] = n;
259a02daaddSclaudio 				prev = &b->trie[0];
260a02daaddSclaudio 			} else {
261a02daaddSclaudio 				b->trie[0] = n;
262a02daaddSclaudio 				prev = &b->trie[1];
263a02daaddSclaudio 			}
264a02daaddSclaudio 			n = NULL;
265a02daaddSclaudio 			break;
266a02daaddSclaudio 		}
267a02daaddSclaudio 
268a02daaddSclaudio 		if (n->plen > plen) {
269a02daaddSclaudio 			/* n is more specific, just insert new in between */
270a02daaddSclaudio 			break;
271a02daaddSclaudio 		}
272a02daaddSclaudio 
273a02daaddSclaudio 		if (n->plen == plen) {
274a02daaddSclaudio 			/* matching node, adjust */
2757ff9bf35Sclaudio 			if (n->node == 0)
2767ff9bf35Sclaudio 				th->v6_cnt++;
277a02daaddSclaudio 			n->node = 1;
2784b62fd1aSclaudio 			return n;
279a02daaddSclaudio 		}
280a02daaddSclaudio 
281451e36b1Sdenis 		/* no need to check for n->plen == 128 because of above if */
282a02daaddSclaudio 		if (inet6isset(&p, n->plen))
283a02daaddSclaudio 			prev = &n->trie[1];
284a02daaddSclaudio 		else
285a02daaddSclaudio 			prev = &n->trie[0];
286a02daaddSclaudio 		n = *prev;
287a02daaddSclaudio 	}
288a02daaddSclaudio 
289a02daaddSclaudio 	/* create new node */
290a02daaddSclaudio 	if ((new = calloc(1, sizeof(*new))) == NULL)
2914b62fd1aSclaudio 		return NULL;
2927ff9bf35Sclaudio 	th->v6_cnt++;
293e5b62a74Sclaudio 	rdemem.pset_cnt++;
294e5b62a74Sclaudio 	rdemem.pset_size += sizeof(*new);
295a02daaddSclaudio 	new->addr = p;
296a02daaddSclaudio 	new->plen = plen;
297a02daaddSclaudio 	new->node = 1;
298a02daaddSclaudio 
299a02daaddSclaudio 	/* link node */
300a02daaddSclaudio 	*prev = new;
301a02daaddSclaudio 	if (n) {
302a02daaddSclaudio 		if (inet6isset(&n->addr, new->plen))
303a02daaddSclaudio 			new->trie[1] = n;
304a02daaddSclaudio 		else
305a02daaddSclaudio 			new->trie[0] = n;
306a02daaddSclaudio 	}
3074b62fd1aSclaudio 	return new;
308a02daaddSclaudio }
309a02daaddSclaudio 
3104b62fd1aSclaudio /*
3114b62fd1aSclaudio  * Insert prefix/plen into the trie with a prefixlen mask covering min - max.
3124b62fd1aSclaudio  * If plen == min == max then only the prefix/plen will match and no longer
3134b62fd1aSclaudio  * match is possible. Else all prefixes under prefix/plen with a prefixlen
3144b62fd1aSclaudio  * between min and max will match.
3154b62fd1aSclaudio  */
316a02daaddSclaudio int
trie_add(struct trie_head * th,struct bgpd_addr * prefix,uint8_t plen,uint8_t min,uint8_t max)31739386878Sclaudio trie_add(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen,
31839386878Sclaudio     uint8_t min, uint8_t max)
319a02daaddSclaudio {
3204b62fd1aSclaudio 	struct tentry_v4 *n4;
3214b62fd1aSclaudio 	struct tentry_v6 *n6;
32239386878Sclaudio 	uint8_t i;
3234b62fd1aSclaudio 
324a02daaddSclaudio 	/* precondition plen <= min <= max */
325a02daaddSclaudio 	if (plen > min || min > max)
326a02daaddSclaudio 		return -1;
3274b62fd1aSclaudio 	if (prefix->aid != AID_INET && prefix->aid != AID_INET6)
3284b62fd1aSclaudio 		return -1;
3294b62fd1aSclaudio 
3304b62fd1aSclaudio 	/*
3314b62fd1aSclaudio 	 * Check for default route, this is special cased since prefixlen 0
3324b62fd1aSclaudio 	 * can't be checked in the prefixlen mask plenmask.  Also there is
3334b62fd1aSclaudio 	 * only one default route so using a flag for this works.
3344b62fd1aSclaudio 	 */
3354b62fd1aSclaudio 	if (min == 0) {
3364b62fd1aSclaudio 		if (prefix->aid == AID_INET)
3374b62fd1aSclaudio 			th->match_default_v4 = 1;
3384b62fd1aSclaudio 		else
3394b62fd1aSclaudio 			th->match_default_v6 = 1;
3404b62fd1aSclaudio 
3414b62fd1aSclaudio 		if (max == 0)	/* just the default route */
3424b62fd1aSclaudio 			return 0;
3434b62fd1aSclaudio 		min = 1;
3444b62fd1aSclaudio 	}
345a02daaddSclaudio 
346a02daaddSclaudio 	switch (prefix->aid) {
347a02daaddSclaudio 	case AID_INET:
348a02daaddSclaudio 		if (max > 32)
349a02daaddSclaudio 			return -1;
3504b62fd1aSclaudio 
3514b62fd1aSclaudio 		n4 = trie_add_v4(th, &prefix->v4, plen);
3524b62fd1aSclaudio 		if (n4 == NULL)
3534b62fd1aSclaudio 			return -1;
3544b62fd1aSclaudio 		/*
3554b62fd1aSclaudio 		 * The prefixlen min - max goes from 1 to 32 but the bitmask
3564b62fd1aSclaudio 		 * starts at 0 and so all bits are set with an offset of -1.
3574b62fd1aSclaudio 		 * The default /0 route is handled specially above.
3584b62fd1aSclaudio 		 */
3594b62fd1aSclaudio 		for (i = min; i <= max; i++)
3604b62fd1aSclaudio 			inet4setbit(&n4->plenmask, i - 1);
3614b62fd1aSclaudio 		break;
362a02daaddSclaudio 	case AID_INET6:
363a02daaddSclaudio 		if (max > 128)
364a02daaddSclaudio 			return -1;
3654b62fd1aSclaudio 
3664b62fd1aSclaudio 		n6 = trie_add_v6(th, &prefix->v6, plen);
3674b62fd1aSclaudio 		if (n6 == NULL)
3684b62fd1aSclaudio 			return -1;
3694b62fd1aSclaudio 
3704b62fd1aSclaudio 		/* See above for the - 1 reason. */
3714b62fd1aSclaudio 		for (i = min; i <= max; i++)
3724b62fd1aSclaudio 			inet6setbit(&n6->plenmask, i - 1);
3734b62fd1aSclaudio 		break;
3744b62fd1aSclaudio 	}
3754b62fd1aSclaudio 	return 0;
3764b62fd1aSclaudio }
3774b62fd1aSclaudio 
3784b62fd1aSclaudio /*
37959e404fbSclaudio  * Insert a ROA entry for prefix/plen. The prefix will insert a set with
3804b62fd1aSclaudio  * source_as and the maxlen as data. This makes it possible to validate if a
3814b62fd1aSclaudio  * prefix is matching this ROA record. It is possible to insert prefixes with
3824b62fd1aSclaudio  * source_as = 0. These entries will never return ROA_VALID on check and can
3834b62fd1aSclaudio  * be used to cover a large prefix as ROA_INVALID unless a more specific route
3844b62fd1aSclaudio  * is a match.
3854b62fd1aSclaudio  */
3864b62fd1aSclaudio int
trie_roa_add(struct trie_head * th,struct roa * roa)3876aa533f4Sclaudio trie_roa_add(struct trie_head *th, struct roa *roa)
3884b62fd1aSclaudio {
3894b62fd1aSclaudio 	struct tentry_v4 *n4;
3904b62fd1aSclaudio 	struct tentry_v6 *n6;
39159e404fbSclaudio 	struct set_table **stp;
3926aa533f4Sclaudio 	struct roa_set rs, *rsp;
3934b62fd1aSclaudio 
3944b62fd1aSclaudio 	/* ignore possible default route since it does not make sense */
3954b62fd1aSclaudio 
3966aa533f4Sclaudio 	switch (roa->aid) {
3974b62fd1aSclaudio 	case AID_INET:
3986aa533f4Sclaudio 		if (roa->prefixlen > 32)
3994b62fd1aSclaudio 			return -1;
4004b62fd1aSclaudio 
4016aa533f4Sclaudio 		n4 = trie_add_v4(th, &roa->prefix.inet, roa->prefixlen);
4024b62fd1aSclaudio 		if (n4 == NULL)
4034b62fd1aSclaudio 			return -1;
40459e404fbSclaudio 		stp = &n4->set;
4054b62fd1aSclaudio 		break;
4064b62fd1aSclaudio 	case AID_INET6:
4076aa533f4Sclaudio 		if (roa->prefixlen > 128)
4084b62fd1aSclaudio 			return -1;
4094b62fd1aSclaudio 
4106aa533f4Sclaudio 		n6 = trie_add_v6(th, &roa->prefix.inet6, roa->prefixlen);
4114b62fd1aSclaudio 		if (n6 == NULL)
4124b62fd1aSclaudio 			return -1;
41359e404fbSclaudio 		stp = &n6->set;
4144b62fd1aSclaudio 		break;
415a02daaddSclaudio 	default:
416a02daaddSclaudio 		/* anything else fails */
417a02daaddSclaudio 		return -1;
418a02daaddSclaudio 	}
4194b62fd1aSclaudio 
4206aa533f4Sclaudio 	if (*stp == NULL)
4216aa533f4Sclaudio 		if ((*stp = set_new(1, sizeof(rs))) == NULL)
4224b62fd1aSclaudio 			return -1;
4236aa533f4Sclaudio 
4246aa533f4Sclaudio 	/* merge sets with same key, longer maxlen wins */
4256aa533f4Sclaudio 	if ((rsp = set_match(*stp, roa->asnum)) != NULL) {
4266aa533f4Sclaudio 		if (rsp->maxlen < roa->maxlen)
4276aa533f4Sclaudio 			rsp->maxlen = roa->maxlen;
4286aa533f4Sclaudio 	} else {
4296aa533f4Sclaudio 		rs.as = roa->asnum;
4306aa533f4Sclaudio 		rs.maxlen = roa->maxlen;
4316aa533f4Sclaudio 		if (set_add(*stp, &rs, 1) != 0)
4326aa533f4Sclaudio 			return -1;
4336aa533f4Sclaudio 		/* prep data so that set_match works */
4346aa533f4Sclaudio 		set_prep(*stp);
4356aa533f4Sclaudio 	}
4364b62fd1aSclaudio 
4374b62fd1aSclaudio 	return 0;
438a02daaddSclaudio }
439a02daaddSclaudio 
440a02daaddSclaudio static void
trie_free_v4(struct tentry_v4 * n)441a02daaddSclaudio trie_free_v4(struct tentry_v4 *n)
442a02daaddSclaudio {
443a02daaddSclaudio 	if (n == NULL)
444a02daaddSclaudio 		return;
445a02daaddSclaudio 	trie_free_v4(n->trie[0]);
446a02daaddSclaudio 	trie_free_v4(n->trie[1]);
44759e404fbSclaudio 	set_free(n->set);
448a02daaddSclaudio 	free(n);
449e5b62a74Sclaudio 	rdemem.pset_cnt--;
450e5b62a74Sclaudio 	rdemem.pset_size -= sizeof(*n);
451a02daaddSclaudio }
452a02daaddSclaudio 
453a02daaddSclaudio static void
trie_free_v6(struct tentry_v6 * n)454a02daaddSclaudio trie_free_v6(struct tentry_v6 *n)
455a02daaddSclaudio {
456a02daaddSclaudio 	if (n == NULL)
457a02daaddSclaudio 		return;
458a02daaddSclaudio 	trie_free_v6(n->trie[0]);
459a02daaddSclaudio 	trie_free_v6(n->trie[1]);
46059e404fbSclaudio 	set_free(n->set);
461a02daaddSclaudio 	free(n);
462e5b62a74Sclaudio 	rdemem.pset_cnt--;
463e5b62a74Sclaudio 	rdemem.pset_size -= sizeof(*n);
464a02daaddSclaudio }
465a02daaddSclaudio 
466a02daaddSclaudio void
trie_free(struct trie_head * th)467a02daaddSclaudio trie_free(struct trie_head *th)
468a02daaddSclaudio {
469a02daaddSclaudio 	trie_free_v4(th->root_v4);
470a02daaddSclaudio 	trie_free_v6(th->root_v6);
4716f8eff73Sclaudio 	memset(th, 0, sizeof(*th));
472a02daaddSclaudio }
473a02daaddSclaudio 
474a02daaddSclaudio static int
trie_match_v4(struct trie_head * th,struct in_addr * prefix,uint8_t plen,int orlonger)47539386878Sclaudio trie_match_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen,
4769ed42aa2Sbenno     int orlonger)
477a02daaddSclaudio {
478a02daaddSclaudio 	struct tentry_v4 *n;
479a02daaddSclaudio 
480a02daaddSclaudio 	if (plen == 0) {
481a02daaddSclaudio 		/* special handling for default route */
482a02daaddSclaudio 		return th->match_default_v4;
483a02daaddSclaudio 	}
484a02daaddSclaudio 
485a02daaddSclaudio 	n = th->root_v4;
486a02daaddSclaudio 	while (n) {
487a02daaddSclaudio 		struct in_addr mp;
488a02daaddSclaudio 
489a02daaddSclaudio 		if (n->plen > plen)
490a02daaddSclaudio 			break;	/* too specific, no match possible */
491a02daaddSclaudio 
492a02daaddSclaudio 		inet4applymask(&mp, prefix, n->plen);
493a02daaddSclaudio 		if (n->addr.s_addr != mp.s_addr)
494a02daaddSclaudio 			break;	/* off path, no match possible */
495a02daaddSclaudio 
4969ed42aa2Sbenno 		/* the match covers all larger prefixlens */
4979ed42aa2Sbenno 		if (n->node && orlonger)
4989ed42aa2Sbenno 			return 1;
4999ed42aa2Sbenno 
500a02daaddSclaudio 		/* plen is from 1 - 32 but the bitmask starts with 0 */
501a02daaddSclaudio 		if (n->node && inet4isset(&n->plenmask, plen - 1))
502a02daaddSclaudio 			return 1;	/* prefixlen allowed, match */
503a02daaddSclaudio 
504a02daaddSclaudio 		if (n->plen == 32)
505a02daaddSclaudio 			break;	/* can't go deeper */
506a02daaddSclaudio 		if (inet4isset(prefix, n->plen))
507a02daaddSclaudio 			n = n->trie[1];
508a02daaddSclaudio 		else
509a02daaddSclaudio 			n = n->trie[0];
510a02daaddSclaudio 	}
511a02daaddSclaudio 
512a02daaddSclaudio 	return 0;
513a02daaddSclaudio }
514a02daaddSclaudio 
515a02daaddSclaudio static int
trie_match_v6(struct trie_head * th,struct in6_addr * prefix,uint8_t plen,int orlonger)51639386878Sclaudio trie_match_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen,
5179ed42aa2Sbenno     int orlonger)
518a02daaddSclaudio {
519a02daaddSclaudio 	struct tentry_v6 *n;
520a02daaddSclaudio 
521a02daaddSclaudio 	if (plen == 0) {
522a02daaddSclaudio 		/* special handling for default route */
523a02daaddSclaudio 		return th->match_default_v6;
524a02daaddSclaudio 	}
525a02daaddSclaudio 
526a02daaddSclaudio 	n = th->root_v6;
527a02daaddSclaudio 	while (n) {
528a02daaddSclaudio 		struct in6_addr mp;
529a02daaddSclaudio 
530a02daaddSclaudio 		if (n->plen > plen)
531a02daaddSclaudio 			break;	/* too specific, no match possible */
532a02daaddSclaudio 
533a02daaddSclaudio 		inet6applymask(&mp, prefix, n->plen);
534a02daaddSclaudio 		if (memcmp(&n->addr, &mp, sizeof(mp)) != 0)
535a02daaddSclaudio 			break;	/* off path, no match possible */
536a02daaddSclaudio 
5379ed42aa2Sbenno 		/* the match covers all larger prefixlens */
5389ed42aa2Sbenno 		if (n->node && orlonger)
5399ed42aa2Sbenno 			return 1;
5409ed42aa2Sbenno 
541a02daaddSclaudio 		/* plen is from 1 - 128 but the bitmask starts with 0 */
542a02daaddSclaudio 		if (n->node && inet6isset(&n->plenmask, plen - 1))
543a02daaddSclaudio 			return 1;	/* prefixlen allowed, match */
544a02daaddSclaudio 
545a02daaddSclaudio 		if (n->plen == 128)
546a02daaddSclaudio 			break;	/* can't go deeper */
547a02daaddSclaudio 		if (inet6isset(prefix, n->plen))
548a02daaddSclaudio 			n = n->trie[1];
549a02daaddSclaudio 		else
550a02daaddSclaudio 			n = n->trie[0];
551a02daaddSclaudio 	}
552a02daaddSclaudio 	return 0;
553a02daaddSclaudio }
554a02daaddSclaudio 
555a02daaddSclaudio /* find first matching element in the trie for prefix "prefix/plen" */
556a02daaddSclaudio int
trie_match(struct trie_head * th,struct bgpd_addr * prefix,uint8_t plen,int orlonger)55739386878Sclaudio trie_match(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen,
5589ed42aa2Sbenno     int orlonger)
559a02daaddSclaudio {
560a02daaddSclaudio 	switch (prefix->aid) {
561a02daaddSclaudio 	case AID_INET:
5629ed42aa2Sbenno 		return trie_match_v4(th, &prefix->v4, plen, orlonger);
563a02daaddSclaudio 	case AID_INET6:
5649ed42aa2Sbenno 		return trie_match_v6(th, &prefix->v6, plen, orlonger);
565a02daaddSclaudio 	default:
566a02daaddSclaudio 		/* anything else is no match */
567a02daaddSclaudio 		return 0;
568a02daaddSclaudio 	}
569a02daaddSclaudio }
570a02daaddSclaudio 
571a02daaddSclaudio static int
trie_roa_check_v4(struct trie_head * th,struct in_addr * prefix,uint8_t plen,uint32_t as)57239386878Sclaudio trie_roa_check_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen,
57339386878Sclaudio     uint32_t as)
5744b62fd1aSclaudio {
5754b62fd1aSclaudio 	struct tentry_v4 *n;
5764b62fd1aSclaudio 	struct roa_set *rs;
5776f1dba6eSclaudio 	int validity = ROA_NOTFOUND;
5784b62fd1aSclaudio 
5794b62fd1aSclaudio 	/* ignore possible default route since it does not make sense */
5804b62fd1aSclaudio 
5814b62fd1aSclaudio 	n = th->root_v4;
5824b62fd1aSclaudio 	while (n) {
5834b62fd1aSclaudio 		struct in_addr mp;
5844b62fd1aSclaudio 
5854b62fd1aSclaudio 		if (n->plen > plen)
5864b62fd1aSclaudio 			break;	/* too specific, no match possible */
5874b62fd1aSclaudio 
5884b62fd1aSclaudio 		inet4applymask(&mp, prefix, n->plen);
5894b62fd1aSclaudio 		if (n->addr.s_addr != mp.s_addr)
5904b62fd1aSclaudio 			break;	/* off path, no other match possible */
5914b62fd1aSclaudio 
5924b62fd1aSclaudio 		if (n->node) {
5934b62fd1aSclaudio 			/*
5944b62fd1aSclaudio 			 * The prefix is covered by this roa node
595*3a50f0a9Sjmc 			 * therefore invalid unless roa_set matches.
5964b62fd1aSclaudio 			 */
5974b62fd1aSclaudio 			validity = ROA_INVALID;
5984b62fd1aSclaudio 
5996f1dba6eSclaudio 			/* AS_NONE can never match, so don't try */
6006f1dba6eSclaudio 			if (as != AS_NONE) {
601d0605468Sclaudio 				if ((rs = set_match(n->set, as)) != NULL) {
602d0605468Sclaudio 				    if (plen == n->plen || plen <= rs->maxlen)
6034b62fd1aSclaudio 					return ROA_VALID;
6044b62fd1aSclaudio 				}
6054b62fd1aSclaudio 			}
606d0605468Sclaudio 		}
6074b62fd1aSclaudio 
6084b62fd1aSclaudio 		if (n->plen == 32)
6094b62fd1aSclaudio 			break;	/* can't go deeper */
6104b62fd1aSclaudio 		if (inet4isset(prefix, n->plen))
6114b62fd1aSclaudio 			n = n->trie[1];
6124b62fd1aSclaudio 		else
6134b62fd1aSclaudio 			n = n->trie[0];
6144b62fd1aSclaudio 	}
6154b62fd1aSclaudio 
6164b62fd1aSclaudio 	return validity;
6174b62fd1aSclaudio }
6184b62fd1aSclaudio 
6194b62fd1aSclaudio static int
trie_roa_check_v6(struct trie_head * th,struct in6_addr * prefix,uint8_t plen,uint32_t as)62039386878Sclaudio trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen,
62139386878Sclaudio     uint32_t as)
6224b62fd1aSclaudio {
6234b62fd1aSclaudio 	struct tentry_v6 *n;
6244b62fd1aSclaudio 	struct roa_set *rs;
6256f1dba6eSclaudio 	int validity = ROA_NOTFOUND;
6264b62fd1aSclaudio 
6274b62fd1aSclaudio 	/* ignore possible default route since it does not make sense */
6284b62fd1aSclaudio 
6294b62fd1aSclaudio 	n = th->root_v6;
6304b62fd1aSclaudio 	while (n) {
6314b62fd1aSclaudio 		struct in6_addr mp;
6324b62fd1aSclaudio 
6334b62fd1aSclaudio 		if (n->plen > plen)
6344b62fd1aSclaudio 			break;	/* too specific, no match possible */
6354b62fd1aSclaudio 
6364b62fd1aSclaudio 		inet6applymask(&mp, prefix, n->plen);
6374b62fd1aSclaudio 		if (memcmp(&n->addr, &mp, sizeof(mp)) != 0)
6384b62fd1aSclaudio 			break;	/* off path, no other match possible */
6394b62fd1aSclaudio 
6404b62fd1aSclaudio 		if (n->node) {
6414b62fd1aSclaudio 			/*
6424b62fd1aSclaudio 			 * This prefix is covered by this roa node.
643*3a50f0a9Sjmc 			 * Therefore invalid unless proven otherwise.
6444b62fd1aSclaudio 			 */
6454b62fd1aSclaudio 			validity = ROA_INVALID;
6464b62fd1aSclaudio 
6476f1dba6eSclaudio 			/* AS_NONE can never match, so don't try */
6486f1dba6eSclaudio 			if (as != AS_NONE) {
64959e404fbSclaudio 				if ((rs = set_match(n->set, as)) != NULL)
6504b62fd1aSclaudio 				    if (plen == n->plen || plen <= rs->maxlen)
6514b62fd1aSclaudio 					return ROA_VALID;
6524b62fd1aSclaudio 			}
6534b62fd1aSclaudio 		}
6544b62fd1aSclaudio 
6554b62fd1aSclaudio 		if (n->plen == 128)
6564b62fd1aSclaudio 			break;	/* can't go deeper */
6574b62fd1aSclaudio 		if (inet6isset(prefix, n->plen))
6584b62fd1aSclaudio 			n = n->trie[1];
6594b62fd1aSclaudio 		else
6604b62fd1aSclaudio 			n = n->trie[0];
6614b62fd1aSclaudio 	}
6624b62fd1aSclaudio 
6634b62fd1aSclaudio 	return validity;
6644b62fd1aSclaudio }
6654b62fd1aSclaudio 
6664b62fd1aSclaudio /*
6674b62fd1aSclaudio  * Do a ROA (Route Origin Validation) check.  Look for elements in the trie
6684b62fd1aSclaudio  * which cover prefix "prefix/plen" and match the source-as as.
6696f1dba6eSclaudio  * AS_NONE can be used when the source-as is unknown (e.g. AS_SET).
6706f1dba6eSclaudio  * The check will then only return ROA_NOTFOUND or ROA_INVALID depending if
6716f1dba6eSclaudio  * the prefix is covered by the ROA.
6724b62fd1aSclaudio  */
6734b62fd1aSclaudio int
trie_roa_check(struct trie_head * th,struct bgpd_addr * prefix,uint8_t plen,uint32_t as)67439386878Sclaudio trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen,
67539386878Sclaudio     uint32_t as)
6764b62fd1aSclaudio {
6774b62fd1aSclaudio 	/* valid, invalid, unknown */
6784b62fd1aSclaudio 	switch (prefix->aid) {
6794b62fd1aSclaudio 	case AID_INET:
6804b62fd1aSclaudio 		return trie_roa_check_v4(th, &prefix->v4, plen, as);
6814b62fd1aSclaudio 	case AID_INET6:
6824b62fd1aSclaudio 		return trie_roa_check_v6(th, &prefix->v6, plen, as);
6834b62fd1aSclaudio 	default:
6846f1dba6eSclaudio 		/* anything else is not-found */
6856f1dba6eSclaudio 		return ROA_NOTFOUND;
6864b62fd1aSclaudio 	}
6874b62fd1aSclaudio }
6884b62fd1aSclaudio 
6894b62fd1aSclaudio static int
trie_equal_v4(struct tentry_v4 * a,struct tentry_v4 * b)690a02daaddSclaudio trie_equal_v4(struct tentry_v4 *a, struct tentry_v4 *b)
691a02daaddSclaudio {
692a02daaddSclaudio 	if (a == NULL && b == NULL)
693a02daaddSclaudio 		return 1;
694a02daaddSclaudio 	if (a == NULL || b == NULL)
695a02daaddSclaudio 		return 0;
696a02daaddSclaudio 
697a02daaddSclaudio 	if (a->addr.s_addr != b->addr.s_addr ||
698a02daaddSclaudio 	    a->plen != b->plen ||
699a02daaddSclaudio 	    a->node != b->node ||
700a02daaddSclaudio 	    a->plenmask.s_addr != b->plenmask.s_addr)
701a02daaddSclaudio 		return 0;
702a02daaddSclaudio 
70359e404fbSclaudio 	if (set_equal(a->set, b->set) == 0)
70459e404fbSclaudio 		return 0;
70559e404fbSclaudio 
706a02daaddSclaudio 	if (trie_equal_v4(a->trie[0], b->trie[0]) == 0 ||
707a02daaddSclaudio 	    trie_equal_v4(a->trie[1], b->trie[1]) == 0)
708a02daaddSclaudio 		return 0;
709a02daaddSclaudio 
710a02daaddSclaudio 	return 1;
711a02daaddSclaudio }
712a02daaddSclaudio 
713a02daaddSclaudio static int
trie_equal_v6(struct tentry_v6 * a,struct tentry_v6 * b)714a02daaddSclaudio trie_equal_v6(struct tentry_v6 *a, struct tentry_v6 *b)
715a02daaddSclaudio {
716a02daaddSclaudio 	if (a == NULL && b == NULL)
717a02daaddSclaudio 		return 1;
718a02daaddSclaudio 	if (a == NULL || b == NULL)
719a02daaddSclaudio 		return 0;
720a02daaddSclaudio 
721a02daaddSclaudio 	if (memcmp(&a->addr, &b->addr, sizeof(a->addr)) != 0 ||
722a02daaddSclaudio 	    a->plen != b->plen ||
723a02daaddSclaudio 	    a->node != b->node ||
724a02daaddSclaudio 	    memcmp(&a->plenmask, &b->plenmask, sizeof(a->plenmask)) != 0)
725a02daaddSclaudio 		return 0;
726a02daaddSclaudio 
72759e404fbSclaudio 	if (set_equal(a->set, b->set) == 0)
72859e404fbSclaudio 		return 0;
72959e404fbSclaudio 
730a02daaddSclaudio 	if (trie_equal_v6(a->trie[0], b->trie[0]) == 0 ||
731a02daaddSclaudio 	    trie_equal_v6(a->trie[1], b->trie[1]) == 0)
732a02daaddSclaudio 		return 0;
733a02daaddSclaudio 
734a02daaddSclaudio 	return 1;
735a02daaddSclaudio }
736a02daaddSclaudio 
737451e36b1Sdenis /* Compare two tries and return 1 if they are the same else 0. */
738a02daaddSclaudio int
trie_equal(struct trie_head * a,struct trie_head * b)739a02daaddSclaudio trie_equal(struct trie_head *a, struct trie_head *b)
740a02daaddSclaudio {
741a02daaddSclaudio 	if (a->match_default_v4 != b->match_default_v4 ||
742a02daaddSclaudio 	    a->match_default_v6 != b->match_default_v6)
743a02daaddSclaudio 		return 0;
744a02daaddSclaudio 	if (trie_equal_v4(a->root_v4, b->root_v4) == 0)
745a02daaddSclaudio 		return 0;
746a02daaddSclaudio 	if (trie_equal_v6(a->root_v6, b->root_v6) == 0)
747a02daaddSclaudio 		return 0;
748a02daaddSclaudio 	return 1;
749a02daaddSclaudio }
750a02daaddSclaudio 
751a02daaddSclaudio /* debugging functions for printing the trie */
752a02daaddSclaudio static void
trie_dump_v4(struct tentry_v4 * n)753a02daaddSclaudio trie_dump_v4(struct tentry_v4 *n)
754a02daaddSclaudio {
755a02daaddSclaudio 	if (n == NULL)
756a02daaddSclaudio 		return;
757a02daaddSclaudio 	if (n->node)
758d1ecd085Sclaudio 		fprintf(stderr, "%s/%u plenmask %08x\n", inet_ntoa(n->addr),
759d1ecd085Sclaudio 		    n->plen, n->plenmask.s_addr);
760a02daaddSclaudio 	else
761d1ecd085Sclaudio 		fprintf(stderr, "   %s/%u\n", inet_ntoa(n->addr), n->plen);
762a02daaddSclaudio 
763a02daaddSclaudio 	trie_dump_v4(n->trie[0]);
764a02daaddSclaudio 	trie_dump_v4(n->trie[1]);
765a02daaddSclaudio }
766a02daaddSclaudio 
767a02daaddSclaudio static void
trie_dump_v6(struct tentry_v6 * n)768a02daaddSclaudio trie_dump_v6(struct tentry_v6 *n)
769a02daaddSclaudio {
770a02daaddSclaudio 	char buf[48];
771a02daaddSclaudio 	unsigned int i;
772a02daaddSclaudio 
773a02daaddSclaudio 	if (n == NULL)
774a02daaddSclaudio 		return;
775a02daaddSclaudio 	if (n->node) {
776d1ecd085Sclaudio 		fprintf(stderr, "%s/%u plenmask ",
777a02daaddSclaudio 		    inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen);
778a02daaddSclaudio 		for (i = 0; i < sizeof(n->plenmask); i++)
779d1ecd085Sclaudio 			fprintf(stderr, "%02x", n->plenmask.s6_addr[i]);
780d1ecd085Sclaudio 		fprintf(stderr, "\n");
781a02daaddSclaudio 	} else
782d1ecd085Sclaudio 		fprintf(stderr, "   %s/%u\n",
783a02daaddSclaudio 		    inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen);
784a02daaddSclaudio 
785a02daaddSclaudio 	trie_dump_v6(n->trie[0]);
786a02daaddSclaudio 	trie_dump_v6(n->trie[1]);
787a02daaddSclaudio }
788a02daaddSclaudio 
789a02daaddSclaudio void
trie_dump(struct trie_head * th)790a02daaddSclaudio trie_dump(struct trie_head *th)
791a02daaddSclaudio {
792a02daaddSclaudio 	if (th->match_default_v4)
793451e36b1Sdenis 		fprintf(stderr, "0.0.0.0/0 plenmask 0\n");
794a02daaddSclaudio 	trie_dump_v4(th->root_v4);
795a02daaddSclaudio 	if (th->match_default_v6)
796d1ecd085Sclaudio 		fprintf(stderr, "::/0 plenmask 0\n");
797a02daaddSclaudio 	trie_dump_v6(th->root_v6);
798a02daaddSclaudio }
799