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