1 /* $OpenBSD: rde_trie_test.c,v 1.14 2024/02/14 07:56:23 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
host_ip(const char * s,struct bgpd_addr * h,uint8_t * len)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
host(const char * s,struct bgpd_addr * h,uint8_t * len)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 *
print_prefix(struct bgpd_addr * p)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->v4, buf, sizeof(buf)) == NULL)
104 return "?";
105 } else if (p->aid == AID_INET6) {
106 if (inet_ntop(AF_INET6, &p->v6, buf, sizeof(buf)) == NULL)
107 return "?";
108 } else {
109 return "???";
110 }
111 return buf;
112 }
113
114 static void
parse_file(FILE * in,struct trie_head * th)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
parse_roa_file(FILE * in,struct trie_head * th)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
test_file(FILE * in,struct trie_head * th)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
test_roa_file(FILE * in,struct trie_head * th)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
usage(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
main(int argc,char ** argv)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