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