1 /*	$OpenBSD: rde_trie_test.c,v 1.13 2022/02/07 09:31:21 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 #include <sys/socket.h>
21 
22 #include <err.h>
23 #include <limits.h>
24 #include <netdb.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <util.h>
30 
31 #include "bgpd.h"
32 #include "rde.h"
33 
34 struct rde_memstats rdemem;
35 
36 int roa;
37 int orlonger;
38 
39 int
40 host_ip(const char *s, struct bgpd_addr *h, uint8_t *len)
41 {
42 	struct addrinfo	hints, *res;
43 	int		bits;
44 
45 	bzero(&hints, sizeof(hints));
46 	hints.ai_family = AF_UNSPEC;
47 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
48 	hints.ai_flags = AI_NUMERICHOST;
49 	if (getaddrinfo(s, NULL, &hints, &res) == 0) {
50 		*len = res->ai_family == AF_INET6 ? 128 : 32;
51 		sa2addr(res->ai_addr, h, NULL);
52 		freeaddrinfo(res);
53 	} else {        /* ie. for 10/8 parsing */
54 		if ((bits = inet_net_pton(AF_INET, s, &h->v4, sizeof(h->v4))) == -1)
55 			return (0);
56 		*len = bits;
57 		h->aid = AID_INET;
58 	}
59 
60 	return (1);
61 }
62 
63 int
64 host(const char *s, struct bgpd_addr *h, uint8_t *len)
65 {
66 	int		 mask = 128;
67 	char		*p, *ps;
68 	const char	*errstr;
69 
70 	if ((ps = strdup(s)) == NULL)
71 		errx(1, "%s: strdup", __func__);
72 
73 	if ((p = strrchr(ps, '/')) != NULL) {
74 		mask = strtonum(p+1, 0, 128, &errstr);
75 		if (errstr) {
76 			warnx("prefixlen is %s: %s", errstr, p+1);
77 			return (0);
78 		}
79 		p[0] = '\0';
80 	}
81 
82 	bzero(h, sizeof(*h));
83 
84 	if (host_ip(ps, h, len) == 0) {
85 		free(ps);
86 		return (0);
87 	}
88 
89 	if (p != NULL)
90 		*len = mask;
91 
92 	free(ps);
93 	return (1);
94 }
95 
96 
97 static const char *
98 print_prefix(struct bgpd_addr *p)
99 {
100 	static char buf[48];
101 
102 	if (p->aid == AID_INET) {
103 		if (inet_ntop(AF_INET, &p->ba, buf, sizeof(buf)) == NULL)
104 			return "?";
105 	} else if (p->aid == AID_INET6) {
106 		if (inet_ntop(AF_INET6, &p->ba, buf, sizeof(buf)) == NULL)
107 			return "?";
108 	} else {
109 		return "???";
110 	}
111 	return buf;
112 }
113 
114 static void
115 parse_file(FILE *in, struct trie_head *th)
116 {
117 	const char *errstr;
118 	char *line, *s;
119 	struct bgpd_addr prefix;
120 	uint8_t plen;
121 
122 	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
123 		int state = 0;
124 		uint8_t min = 255, max = 255, maskmax = 0;
125 
126 		while ((s = strsep(&line, " \t\n"))) {
127 			if (*s == '\0')
128 				continue;
129 			switch (state) {
130 			case 0:
131 				if (!host(s, &prefix, &plen))
132 					errx(1, "%s: could not parse "
133 					    "prefix \"%s\"", __func__, s);
134 				if (prefix.aid == AID_INET6)
135 					maskmax = 128;
136 				else
137 					maskmax = 32;
138 				break;
139 			case 1:
140 				min = strtonum(s, 0, maskmax, &errstr);
141 				if (errstr != NULL)
142 					errx(1, "min is %s: %s", errstr, s);
143 				break;
144 			case 2:
145 				max = strtonum(s, 0, maskmax, &errstr);
146 				if (errstr != NULL)
147 					errx(1, "max is %s: %s", errstr, s);
148 				break;
149 			default:
150 				errx(1, "could not parse \"%s\", confused", s);
151 			}
152 			state++;
153 		}
154 		if (state == 0)
155 			continue;
156 		if (max == 255)
157 			max = maskmax;
158 		if (min == 255)
159 			min = plen;
160 
161 		if (trie_add(th, &prefix, plen, min, max) != 0)
162 			errx(1, "trie_add(%s, %u, %u, %u) failed",
163 			    print_prefix(&prefix), plen, min, max);
164 
165 		free(line);
166 	}
167 }
168 
169 static void
170 parse_roa_file(FILE *in, struct trie_head *th)
171 {
172 	const char *errstr;
173 	char *line, *s;
174 	struct set_table *set = NULL;
175 	struct roa roa;
176 	struct bgpd_addr prefix;
177 	uint8_t plen;
178 
179 	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
180 		int state = 0;
181 		uint32_t as;
182 		uint8_t max = 0;
183 
184 		while ((s = strsep(&line, " \t\n"))) {
185 			if (*s == '\0')
186 				continue;
187 			if (strcmp(s, "source-as") == 0) {
188 				state = 4;
189 				continue;
190 			}
191 			if (strcmp(s, "maxlen") == 0) {
192 				state = 2;
193 				continue;
194 			}
195 			if (strcmp(s, "prefix") == 0) {
196 				state = 0;
197 				continue;
198 			}
199 			switch (state) {
200 			case 0:
201 				if (!host(s, &prefix, &plen))
202 					errx(1, "%s: could not parse "
203 					    "prefix \"%s\"", __func__, s);
204 				break;
205 			case 2:
206 				max = strtonum(s, 0, 128, &errstr);
207 				if (errstr != NULL)
208 					errx(1, "max is %s: %s", errstr, s);
209 				break;
210 			case 4:
211 				as = strtonum(s, 0, UINT_MAX, &errstr);
212 				if (errstr != NULL)
213 					errx(1, "source-as is %s: %s", errstr,
214 					    s);
215 				break;
216 			default:
217 				errx(1, "could not parse \"%s\", confused", s);
218 			}
219 		}
220 
221 		roa.aid = prefix.aid;
222 		roa.prefix.inet6 = prefix.v6;
223 		roa.prefixlen = plen;
224 		roa.maxlen = max;
225 		roa.asnum = as;
226 		if (trie_roa_add(th, &roa) != 0)
227 			errx(1, "trie_roa_add(%s, %u) failed",
228 			    print_prefix(&prefix), plen);
229 
230 		free(line);
231 	}
232 }
233 
234 static void
235 test_file(FILE *in, struct trie_head *th)
236 {
237 	char *line;
238 	struct bgpd_addr prefix;
239 	uint8_t plen;
240 
241 	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
242 		if (!host(line, &prefix, &plen))
243 			errx(1, "%s: could not parse prefix \"%s\"",
244 			    __func__, line);
245 		printf("%s/%u ", print_prefix(&prefix), plen);
246 		if (trie_match(th, &prefix, plen, orlonger))
247 			printf("MATCH\n");
248 		else
249 			printf("miss\n");
250 		free(line);
251 	}
252 }
253 
254 static void
255 test_roa_file(FILE *in, struct trie_head *th)
256 {
257 	const char *errstr;
258 	char *line, *s;
259 	struct bgpd_addr prefix;
260 	uint8_t plen;
261 	uint32_t as;
262 	int r;
263 
264 	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
265 		s = strchr(line, ' ');
266 		if (s)
267 			*s++ = '\0';
268 		if (!host(line, &prefix, &plen))
269 			errx(1, "%s: could not parse prefix \"%s\"",
270 			    __func__, line);
271 		if (s)
272 			s = strstr(s, "source-as");
273 		if (s) {
274 			s += strlen("source-as");
275 			as = strtonum(s, 0, UINT_MAX, &errstr);
276 			if (errstr != NULL)
277 				errx(1, "source-as is %s: %s", errstr, s);
278 		} else
279 			as = 0;
280 		printf("%s/%u source-as %u is ",
281 		    print_prefix(&prefix), plen, as);
282 		r = trie_roa_check(th, &prefix, plen, as);
283 		switch (r) {
284 		case ROA_NOTFOUND:
285 			printf("not found\n");
286 			break;
287 		case ROA_VALID:
288 			printf("VALID\n");
289 			break;
290 		case ROA_INVALID:
291 			printf("invalid\n");
292 			break;
293 		default:
294 			printf("UNEXPECTED %d\n", r);
295 			break;
296 		}
297 		free(line);
298 	}
299 }
300 
301 static void
302 usage(void)
303 {
304 	extern char *__progname;
305 	fprintf(stderr, "usage: %s [-or] prefixfile testfile\n", __progname);
306 	exit(1);
307 }
308 
309 int
310 main(int argc, char **argv)
311 {
312 	struct trie_head th = { 0 };
313 	FILE *in, *tin;
314 	int ch;
315 
316 	while ((ch = getopt(argc, argv, "or")) != -1) {
317 		switch (ch) {
318 		case 'o':
319 			orlonger = 1;
320 			break;
321 		case 'r':
322 			roa = 1;
323 			break;
324 		default:
325 			usage();
326 			/* NOTREACHED */
327 		}
328 	}
329 	argc -= optind;
330 	argv += optind;
331 
332 	if (argc != 2)
333 		usage();
334 
335 	in = fopen(argv[0], "r");
336 	if (in == NULL)
337 		err(1, "fopen(%s)", argv[0]);
338 	tin = fopen(argv[1], "r");
339 	if (tin == NULL)
340 		err(1, "fopen(%s)", argv[1]);
341 
342 	if (roa)
343 		parse_roa_file(in, &th);
344 	else
345 		parse_file(in, &th);
346 	/* trie_dump(&th); */
347 	if (trie_equal(&th, &th) == 0)
348 		errx(1, "trie_equal failure");
349 	if (roa)
350 		test_roa_file(tin, &th);
351 	else
352 		test_file(tin, &th);
353 
354 	trie_free(&th);
355 }
356