rde_trie_test.c revision 1.8
1/*	$OpenBSD: rde_trie_test.c,v 1.8 2018/09/29 08:13:56 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
34int roa;
35int orlonger;
36
37int
38host_ip(const char *s, struct bgpd_addr *h, u_int8_t *len)
39{
40	struct addrinfo	hints, *res;
41	int		bits;
42
43	bzero(&hints, sizeof(hints));
44	hints.ai_family = AF_UNSPEC;
45	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
46	hints.ai_flags = AI_NUMERICHOST;
47	if (getaddrinfo(s, NULL, &hints, &res) == 0) {
48		*len = res->ai_family == AF_INET6 ? 128 : 32;
49		sa2addr(res->ai_addr, h);
50		freeaddrinfo(res);
51	} else {        /* ie. for 10/8 parsing */
52		if ((bits = inet_net_pton(AF_INET, s, &h->v4, sizeof(h->v4))) == -1)
53			return (0);
54		*len = bits;
55		h->aid = AID_INET;
56	}
57
58	return (1);
59}
60
61int
62host(const char *s, struct bgpd_addr *h, u_int8_t *len)
63{
64	int		 mask = 128;
65	char		*p, *ps;
66	const char	*errstr;
67
68	if ((ps = strdup(s)) == NULL)
69		errx(1, "%s: strdup", __func__);
70
71	if ((p = strrchr(ps, '/')) != NULL) {
72		mask = strtonum(p+1, 0, 128, &errstr);
73		if (errstr) {
74			warnx("prefixlen is %s: %s", errstr, p+1);
75			return (0);
76		}
77		p[0] = '\0';
78	}
79
80	bzero(h, sizeof(*h));
81
82	if (host_ip(ps, h, len) == 0) {
83		free(ps);
84		return (0);
85	}
86
87	if (p != NULL)
88		*len = mask;
89
90	free(ps);
91	return (1);
92}
93
94
95static const char *
96print_prefix(struct bgpd_addr *p)
97{
98	static char buf[48];
99
100	if (p->aid == AID_INET) {
101		if (inet_ntop(AF_INET, &p->ba, buf, sizeof(buf)) == NULL)
102			return "?";
103	} else if (p->aid == AID_INET6) {
104		if (inet_ntop(AF_INET6, &p->ba, buf, sizeof(buf)) == NULL)
105			return "?";
106	} else {
107		return "???";
108	}
109	return buf;
110}
111
112static void
113parse_file(FILE *in, struct trie_head *th)
114{
115	const char *errstr;
116	char *line, *s;
117	struct bgpd_addr prefix;
118	u_int8_t plen;
119
120	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
121		int state = 0;
122		u_int8_t min = 255, max = 255, maskmax = 0;
123
124		while ((s = strsep(&line, " \t\n"))) {
125			if (*s == '\0')
126				continue;
127			switch (state) {
128			case 0:
129				if (!host(s, &prefix, &plen))
130					errx(1, "%s: could not parse "
131					    "prefix \"%s\"", __func__, s);
132				if (prefix.aid == AID_INET6)
133					maskmax = 128;
134				else
135					maskmax = 32;
136				break;
137			case 1:
138				min = strtonum(s, 0, maskmax, &errstr);
139				if (errstr != NULL)
140					errx(1, "min is %s: %s", errstr, s);
141				break;
142			case 2:
143				max = strtonum(s, 0, maskmax, &errstr);
144				if (errstr != NULL)
145					errx(1, "max is %s: %s", errstr, s);
146				break;
147			default:
148				errx(1, "could not parse \"%s\", confused", s);
149			}
150			state++;
151		}
152		if (state == 0)
153			continue;
154		if (max == 255)
155			max = maskmax;
156		if (min == 255)
157			min = plen;
158
159		if (trie_add(th, &prefix, plen, min, max) != 0)
160			errx(1, "trie_add(%s, %u, %u, %u) failed",
161			    print_prefix(&prefix), plen, min, max);
162
163		free(line);
164	}
165}
166
167static void
168parse_roa_file(FILE *in, struct trie_head *th)
169{
170	const char *errstr;
171	char *line, *s;
172	struct set_table *set = NULL;
173	struct roa_set rs;
174	struct bgpd_addr prefix;
175	u_int8_t plen;
176
177	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
178		int state = 0;
179		u_int32_t as;
180		u_int8_t max = 0;
181
182		while ((s = strsep(&line, " \t\n"))) {
183			if (*s == '\0')
184				continue;
185			if (strcmp(s, "source-as") == 0) {
186				state = 4;
187				continue;
188			}
189			if (strcmp(s, "maxlen") == 0) {
190				state = 2;
191				continue;
192			}
193			if (strcmp(s, "prefix") == 0) {
194				state = 0;
195				continue;
196			}
197			switch (state) {
198			case 0:
199				if (!host(s, &prefix, &plen))
200					errx(1, "%s: could not parse "
201					    "prefix \"%s\"", __func__, s);
202				break;
203			case 2:
204				max = strtonum(s, 0, 128, &errstr);
205				if (errstr != NULL)
206					errx(1, "max is %s: %s", errstr, s);
207				break;
208			case 4:
209				as = strtonum(s, 0, UINT_MAX, &errstr);
210				if (errstr != NULL)
211					errx(1, "source-as is %s: %s", errstr,
212					    s);
213				break;
214			default:
215				errx(1, "could not parse \"%s\", confused", s);
216			}
217		}
218
219		if (state == 0) {
220			set_prep(set);
221			if (trie_roa_add(th, &prefix, plen, set) != 0)
222				errx(1, "trie_roa_add(%s, %u) failed",
223				    print_prefix(&prefix), plen);
224			set = NULL;
225		} else {
226			if (set == NULL) {
227				if ((set = set_new(1, sizeof(rs))) == NULL)
228					err(1, "set_new");
229			}
230			rs.as = as;
231			rs.maxlen = max;
232			if (set_add(set, &rs, 1) != 0)
233				err(1, "set_add");
234		}
235
236		free(line);
237	}
238}
239
240static void
241test_file(FILE *in, struct trie_head *th)
242{
243	char *line;
244	struct bgpd_addr prefix;
245	u_int8_t plen;
246
247	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
248		if (!host(line, &prefix, &plen))
249			errx(1, "%s: could not parse prefix \"%s\"",
250			    __func__, line);
251		printf("%s/%u ", print_prefix(&prefix), plen);
252		if (trie_match(th, &prefix, plen, orlonger))
253			printf("MATCH\n");
254		else
255			printf("miss\n");
256		free(line);
257	}
258}
259
260static void
261test_roa_file(FILE *in, struct trie_head *th)
262{
263	const char *errstr;
264	char *line, *s;
265	struct bgpd_addr prefix;
266	u_int8_t plen;
267	u_int32_t as;
268	int r;
269
270	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
271		s = strchr(line, ' ');
272		if (s)
273			*s++ = '\0';
274		if (!host(line, &prefix, &plen))
275			errx(1, "%s: could not parse prefix \"%s\"",
276			    __func__, line);
277		if (s)
278			s = strstr(s, "source-as");
279		if (s) {
280			s += strlen("source-as");
281			as = strtonum(s, 0, UINT_MAX, &errstr);
282			if (errstr != NULL)
283				errx(1, "source-as is %s: %s", errstr, s);
284		} else
285			as = 0;
286		printf("%s/%u source-as %u is ",
287		    print_prefix(&prefix), plen, as);
288		r = trie_roa_check(th, &prefix, plen, as);
289		switch (r) {
290		case ROA_NOTFOUND:
291			printf("not found\n");
292			break;
293		case ROA_VALID:
294			printf("VALID\n");
295			break;
296		case ROA_INVALID:
297			printf("invalid\n");
298			break;
299		default:
300			printf("UNEXPECTED %d\n", r);
301			break;
302		}
303		free(line);
304	}
305}
306
307static void
308usage(void)
309{
310        extern char *__progname;
311	fprintf(stderr, "usage: %s [-or] prefixfile testfile\n", __progname);
312	exit(1);
313}
314
315int
316main(int argc, char **argv)
317{
318	struct trie_head th = { 0 };
319	FILE *in, *tin;
320	int ch;
321
322	while ((ch = getopt(argc, argv, "or")) != -1) {
323		switch (ch) {
324		case 'o':
325			orlonger = 1;
326			break;
327		case 'r':
328			roa = 1;
329			break;
330		default:
331			usage();
332			/* NOTREACHED */
333		}
334	}
335	argc -= optind;
336	argv += optind;
337
338	if (argc != 2)
339		usage();
340
341	in = fopen(argv[0], "r");
342	if (in == NULL)
343		err(1, "fopen(%s)", argv[0]);
344	tin = fopen(argv[1], "r");
345	if (tin == NULL)
346		err(1, "fopen(%s)", argv[1]);
347
348	if (roa)
349		parse_roa_file(in, &th);
350	else
351		parse_file(in, &th);
352	/* trie_dump(&th); */
353	if (trie_equal(&th, &th) == 0)
354		errx(1, "trie_equal failure");
355	if (roa)
356		test_roa_file(tin, &th);
357	else
358		test_file(tin, &th);
359
360	trie_free(&th);
361}
362