rde_trie_test.c revision 1.3
1/*	$OpenBSD: rde_trie_test.c,v 1.3 2018/09/10 20:51:59 benno 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 <netdb.h>
24#include <string.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <util.h>
29
30#include "bgpd.h"
31#include "rde.h"
32
33
34static int
35host_v4(const char *s, struct bgpd_addr *h, u_int8_t *len, int *orl)
36{
37	struct in_addr ina = { 0 };
38	int bits = 32;
39
40	memset(h, 0, sizeof(*h));
41	if (strrchr(s, '/') != NULL) {
42		if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
43			return (0);
44	} else {
45		if (inet_pton(AF_INET, s, &ina) != 1)
46			return (0);
47	}
48	h->aid = AID_INET;
49	h->v4.s_addr = ina.s_addr;
50	*len = bits;
51	return (1);
52}
53
54static int
55host_v6(const char *s, struct bgpd_addr *h, u_int8_t *len, int *orl)
56{
57	struct addrinfo hints, *res;
58	const char *errstr;
59	char *p;
60	int mask = 128;
61
62	memset(h, 0, sizeof(*h));
63	if ((p = strrchr(s, '/')) != NULL) {
64		mask = strtonum(p + 1, 0, 128, &errstr);
65		if (errstr)
66			return (0);
67		*p = '\0';
68	}
69
70        bzero(&hints, sizeof(hints));
71        hints.ai_family = AF_INET6;
72        hints.ai_socktype = SOCK_DGRAM; /*dummy*/
73        hints.ai_flags = AI_NUMERICHOST;
74        if (getaddrinfo(s, "0", &hints, &res) == 0) {
75		h->aid = AID_INET6;
76		memcpy(&h->v6, &res->ai_addr->sa_data[6], sizeof(h->v6));
77                freeaddrinfo(res);
78		*len = mask;
79		*p = '/';
80                return (1);
81        }
82	*p = '/';
83
84        return (0);
85}
86
87static int
88host_l(char *s, struct bgpd_addr *h, u_int8_t *len, int *orl)
89{
90	char *c, *t;
91
92	*orl = 0;
93	if ((c = strchr(s, '\t')) != NULL) {
94		if (c[1] == '1') {
95			*orl = 1;
96		}
97		*c = '\0';
98	}
99
100	if (host_v4(s, h, len, orl))
101		return (1);
102	if (host_v6(s, h, len, orl))
103		return (1);
104	return (0);
105}
106
107static const char *
108print_prefix(struct bgpd_addr *p)
109{
110	static char buf[48];
111
112	if (p->aid == AID_INET) {
113		if (inet_ntop(AF_INET, &p->ba, buf, sizeof(buf)) == NULL)
114			return "?";
115	} else if (p->aid == AID_INET6) {
116		if (inet_ntop(AF_INET6, &p->ba, buf, sizeof(buf)) == NULL)
117			return "?";
118	} else {
119		return "???";
120	}
121	return buf;
122}
123
124static void
125parse_file(FILE *in, struct trie_head *th)
126{
127	const char *errstr;
128	char *line, *s;
129	struct bgpd_addr prefix;
130	u_int8_t plen, min, max, maskmax;
131	int foo;
132
133	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
134		int state = 0;
135		while ((s = strsep(&line, " \t"))) {
136			if (*s == '\0')
137				break;
138			switch (state) {
139			case 0:
140				if (!host_l(s, &prefix, &plen, &foo))
141					errx(1, "could not parse prefix \"%s\"",
142					    s);
143				break;
144			case 1:
145				if (prefix.aid == AID_INET6)
146					maskmax = 128;
147				else
148					maskmax = 32;
149				min = strtonum(s, 0, maskmax, &errstr);
150				if (errstr != NULL)
151					errx(1, "min is %s: %s", errstr, s);
152				break;
153			case 2:
154				if (prefix.aid == AID_INET6)
155					maskmax = 128;
156				else
157					maskmax = 32;
158				max = strtonum(s, 0, maskmax, &errstr);
159				if (errstr != NULL)
160					errx(1, "max is %s: %s", errstr, s);
161				break;
162			default:
163				errx(1, "could not parse \"%s\", confused", s);
164			}
165			state++;
166		}
167
168		if (trie_add(th, &prefix, plen, min, max) != 0)
169			errx(1, "trie_add(%s, %u, %u, %u) failed",
170			    print_prefix(&prefix), plen, min, max);
171
172		free(line);
173	}
174}
175
176static void
177test_file(FILE *in, struct trie_head *th)
178{
179	char *line;
180	struct bgpd_addr prefix;
181	u_int8_t plen;
182	int orlonger;
183
184	while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) {
185		if (!host_l(line, &prefix, &plen, &orlonger))
186			errx(1, "could not parse prefix \"%s\"", line);
187		printf("%s ", line);
188		if (trie_match(th, &prefix, plen, orlonger))
189			printf("MATCH %i\n", orlonger);
190		else
191			printf("miss %i\n", orlonger);
192		free(line);
193	}
194}
195
196static void
197usage(void)
198{
199        extern char *__progname;
200	fprintf(stderr, "usage: %s prefixfile testfile\n", __progname);
201	exit(1);
202}
203
204int
205main(int argc, char **argv)
206{
207	struct trie_head th = { 0 };
208	FILE *in, *tin;
209	int ch;
210
211	if (argc != 3)
212		usage();
213
214	in = fopen(argv[1], "r");
215	if (in == NULL)
216		err(1, "fopen(%s)", argv[0]);
217	tin = fopen(argv[2], "r");
218	if (tin == NULL)
219		err(1, "fopen(%s)", argv[1]);
220
221	parse_file(in, &th);
222	/* trie_dump(&th); */
223	if (trie_equal(&th, &th) == 0)
224		errx(1, "trie_equal failure");
225	test_file(tin, &th);
226
227	trie_free(&th);
228}
229