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