/* $OpenBSD: rde_trie_test.c,v 1.14 2024/02/14 07:56:23 claudio Exp $ */ /* * Copyright (c) 2018 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "bgpd.h" #include "rde.h" struct rde_memstats rdemem; int roa; int orlonger; int host_ip(const char *s, struct bgpd_addr *h, uint8_t *len) { struct addrinfo hints, *res; int bits; bzero(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(s, NULL, &hints, &res) == 0) { *len = res->ai_family == AF_INET6 ? 128 : 32; sa2addr(res->ai_addr, h, NULL); freeaddrinfo(res); } else { /* ie. for 10/8 parsing */ if ((bits = inet_net_pton(AF_INET, s, &h->v4, sizeof(h->v4))) == -1) return (0); *len = bits; h->aid = AID_INET; } return (1); } int host(const char *s, struct bgpd_addr *h, uint8_t *len) { int mask = 128; char *p, *ps; const char *errstr; if ((ps = strdup(s)) == NULL) errx(1, "%s: strdup", __func__); if ((p = strrchr(ps, '/')) != NULL) { mask = strtonum(p+1, 0, 128, &errstr); if (errstr) { warnx("prefixlen is %s: %s", errstr, p+1); return (0); } p[0] = '\0'; } bzero(h, sizeof(*h)); if (host_ip(ps, h, len) == 0) { free(ps); return (0); } if (p != NULL) *len = mask; free(ps); return (1); } static const char * print_prefix(struct bgpd_addr *p) { static char buf[48]; if (p->aid == AID_INET) { if (inet_ntop(AF_INET, &p->v4, buf, sizeof(buf)) == NULL) return "?"; } else if (p->aid == AID_INET6) { if (inet_ntop(AF_INET6, &p->v6, buf, sizeof(buf)) == NULL) return "?"; } else { return "???"; } return buf; } static void parse_file(FILE *in, struct trie_head *th) { const char *errstr; char *line, *s; struct bgpd_addr prefix; uint8_t plen; while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) { int state = 0; uint8_t min = 255, max = 255, maskmax = 0; while ((s = strsep(&line, " \t\n"))) { if (*s == '\0') continue; switch (state) { case 0: if (!host(s, &prefix, &plen)) errx(1, "%s: could not parse " "prefix \"%s\"", __func__, s); if (prefix.aid == AID_INET6) maskmax = 128; else maskmax = 32; break; case 1: min = strtonum(s, 0, maskmax, &errstr); if (errstr != NULL) errx(1, "min is %s: %s", errstr, s); break; case 2: max = strtonum(s, 0, maskmax, &errstr); if (errstr != NULL) errx(1, "max is %s: %s", errstr, s); break; default: errx(1, "could not parse \"%s\", confused", s); } state++; } if (state == 0) continue; if (max == 255) max = maskmax; if (min == 255) min = plen; if (trie_add(th, &prefix, plen, min, max) != 0) errx(1, "trie_add(%s, %u, %u, %u) failed", print_prefix(&prefix), plen, min, max); free(line); } } static void parse_roa_file(FILE *in, struct trie_head *th) { const char *errstr; char *line, *s; struct set_table *set = NULL; struct roa roa; struct bgpd_addr prefix; uint8_t plen; while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) { int state = 0; uint32_t as; uint8_t max = 0; while ((s = strsep(&line, " \t\n"))) { if (*s == '\0') continue; if (strcmp(s, "source-as") == 0) { state = 4; continue; } if (strcmp(s, "maxlen") == 0) { state = 2; continue; } if (strcmp(s, "prefix") == 0) { state = 0; continue; } switch (state) { case 0: if (!host(s, &prefix, &plen)) errx(1, "%s: could not parse " "prefix \"%s\"", __func__, s); break; case 2: max = strtonum(s, 0, 128, &errstr); if (errstr != NULL) errx(1, "max is %s: %s", errstr, s); break; case 4: as = strtonum(s, 0, UINT_MAX, &errstr); if (errstr != NULL) errx(1, "source-as is %s: %s", errstr, s); break; default: errx(1, "could not parse \"%s\", confused", s); } } roa.aid = prefix.aid; roa.prefix.inet6 = prefix.v6; roa.prefixlen = plen; roa.maxlen = max; roa.asnum = as; if (trie_roa_add(th, &roa) != 0) errx(1, "trie_roa_add(%s, %u) failed", print_prefix(&prefix), plen); free(line); } } static void test_file(FILE *in, struct trie_head *th) { char *line; struct bgpd_addr prefix; uint8_t plen; while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) { if (!host(line, &prefix, &plen)) errx(1, "%s: could not parse prefix \"%s\"", __func__, line); printf("%s/%u ", print_prefix(&prefix), plen); if (trie_match(th, &prefix, plen, orlonger)) printf("MATCH\n"); else printf("miss\n"); free(line); } } static void test_roa_file(FILE *in, struct trie_head *th) { const char *errstr; char *line, *s; struct bgpd_addr prefix; uint8_t plen; uint32_t as; int r; while ((line = fparseln(in, NULL, NULL, NULL, FPARSELN_UNESCALL))) { s = strchr(line, ' '); if (s) *s++ = '\0'; if (!host(line, &prefix, &plen)) errx(1, "%s: could not parse prefix \"%s\"", __func__, line); if (s) s = strstr(s, "source-as"); if (s) { s += strlen("source-as"); as = strtonum(s, 0, UINT_MAX, &errstr); if (errstr != NULL) errx(1, "source-as is %s: %s", errstr, s); } else as = 0; printf("%s/%u source-as %u is ", print_prefix(&prefix), plen, as); r = trie_roa_check(th, &prefix, plen, as); switch (r) { case ROA_NOTFOUND: printf("not found\n"); break; case ROA_VALID: printf("VALID\n"); break; case ROA_INVALID: printf("invalid\n"); break; default: printf("UNEXPECTED %d\n", r); break; } free(line); } } static void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-or] prefixfile testfile\n", __progname); exit(1); } int main(int argc, char **argv) { struct trie_head th = { 0 }; FILE *in, *tin; int ch; while ((ch = getopt(argc, argv, "or")) != -1) { switch (ch) { case 'o': orlonger = 1; break; case 'r': roa = 1; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 2) usage(); in = fopen(argv[0], "r"); if (in == NULL) err(1, "fopen(%s)", argv[0]); tin = fopen(argv[1], "r"); if (tin == NULL) err(1, "fopen(%s)", argv[1]); if (roa) parse_roa_file(in, &th); else parse_file(in, &th); /* trie_dump(&th); */ if (trie_equal(&th, &th) == 0) errx(1, "trie_equal failure"); if (roa) test_roa_file(tin, &th); else test_file(tin, &th); trie_free(&th); }