From dc779fa437a559de7417c6ea1f86bb1311bf5485 Mon Sep 17 00:00:00 2001 From: Henning Brauer Date: Sat, 3 Mar 2007 11:45:31 +0000 Subject: add irrfilter mode. generates bgpd filter rules from the Internet Routing Registry aka IRR aka the aut-num, as-set and route objects in the RIPE, ARIN, APNIC ... databases accessed via whois, using the Routing Policy Specificaion Language RPSL. implement the whois query interface, an RPSL parser (of course only the parts we need), recursive as-set resolver, prefixes per AS lookup, and an ouput module to make up the rules. work in progress, not ready for general consumption yet. import agreed by theo & claudio --- usr.sbin/bgpctl/Makefile | 4 +- usr.sbin/bgpctl/bgpctl.c | 11 +- usr.sbin/bgpctl/irr_asset.c | 234 +++++++++++++++++++++++++ usr.sbin/bgpctl/irr_output.c | 183 ++++++++++++++++++++ usr.sbin/bgpctl/irr_parser.c | 395 +++++++++++++++++++++++++++++++++++++++++++ usr.sbin/bgpctl/irr_prefix.c | 102 +++++++++++ usr.sbin/bgpctl/irrfilter.c | 52 ++++++ usr.sbin/bgpctl/irrfilter.h | 96 +++++++++++ usr.sbin/bgpctl/parser.c | 16 +- usr.sbin/bgpctl/parser.h | 5 +- usr.sbin/bgpctl/whois.c | 149 ++++++++++++++++ 11 files changed, 1241 insertions(+), 6 deletions(-) create mode 100644 usr.sbin/bgpctl/irr_asset.c create mode 100644 usr.sbin/bgpctl/irr_output.c create mode 100644 usr.sbin/bgpctl/irr_parser.c create mode 100644 usr.sbin/bgpctl/irr_prefix.c create mode 100644 usr.sbin/bgpctl/irrfilter.c create mode 100644 usr.sbin/bgpctl/irrfilter.h create mode 100644 usr.sbin/bgpctl/whois.c diff --git a/usr.sbin/bgpctl/Makefile b/usr.sbin/bgpctl/Makefile index 0ed3be67547..71ace1ba13f 100644 --- a/usr.sbin/bgpctl/Makefile +++ b/usr.sbin/bgpctl/Makefile @@ -1,9 +1,11 @@ -# $OpenBSD: Makefile,v 1.8 2006/11/26 11:31:12 deraadt Exp $ +# $OpenBSD: Makefile,v 1.9 2007/03/03 11:45:30 henning Exp $ .PATH: ${.CURDIR}/../bgpd PROG= bgpctl SRCS= bgpctl.c parser.c buffer.c imsg.c util.c +SRCS+= irrfilter.c whois.c irr_asset.c irr_prefix.c irr_output.c +SRCS+= irr_parser.c CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c index adfb300fc51..768c4e680d8 100644 --- a/usr.sbin/bgpctl/bgpctl.c +++ b/usr.sbin/bgpctl/bgpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.c,v 1.115 2007/02/22 08:38:19 henning Exp $ */ +/* $OpenBSD: bgpctl.c,v 1.116 2007/03/03 11:45:30 henning Exp $ */ /* * Copyright (c) 2003 Henning Brauer @@ -36,6 +36,7 @@ #include "rde.h" #include "log.h" #include "parser.h" +#include "irrfilter.h" enum neighbor_views { NV_DEFAULT, @@ -102,10 +103,11 @@ main(int argc, char *argv[]) struct parse_result *res; struct ctl_neighbor neighbor; struct ctl_show_rib_request ribreq; - char *sockname; + char *sockname, *outdir; enum imsg_type type; sockname = SOCKET_NAME; + outdir = getcwd(NULL, 0); while ((ch = getopt(argc, argv, "ns:")) != -1) { switch (ch) { case 'n': @@ -126,6 +128,9 @@ main(int argc, char *argv[]) if ((res = parse(argc, argv)) == NULL) exit(1); + if (res->action == IRRFILTER) + irr_main(res->as.as, res->flags, outdir); + memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr)); strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr)); @@ -147,6 +152,7 @@ main(int argc, char *argv[]) switch (res->action) { case NONE: + case IRRFILTER: usage(); /* not reached */ case SHOW: @@ -352,6 +358,7 @@ main(int argc, char *argv[]) case NETWORK_ADD: case NETWORK_REMOVE: case NETWORK_FLUSH: + case IRRFILTER: break; } imsg_free(&imsg); diff --git a/usr.sbin/bgpctl/irr_asset.c b/usr.sbin/bgpctl/irr_asset.c new file mode 100644 index 00000000000..0cf54557a49 --- /dev/null +++ b/usr.sbin/bgpctl/irr_asset.c @@ -0,0 +1,234 @@ +/* $OpenBSD: irr_asset.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 "irrfilter.h" + +int as_set_compare(struct as_set *, struct as_set *); +struct as_set *as_set_find(char *); + +RB_HEAD(as_set_h, as_set) as_set_h; +RB_PROTOTYPE(as_set_h, as_set, entry, as_set_compare) +RB_GENERATE(as_set_h, as_set, entry, as_set_compare) + +struct as_set *curass; + +struct as_set *asset_get(char *); +void asset_resolve(struct as_set *); +int asset_merge(struct as_set *, struct as_set *); +int asset_add_as(struct as_set *, char *); +int asset_add_asset(struct as_set *, char *); + +struct as_set * +asset_expand(char *name) +{ + struct as_set *ass; + + ass = asset_get(name); + asset_resolve(ass); + + return (ass); +} + +struct as_set * +asset_get(char *name) +{ + struct as_set *ass, *mas; + u_int i; + int r; + + + /* + * the caching prevents the endless recursion. + * MUST have the RB_INSERT before calling self again. + */ + + /* cached? then things are easy */ + if ((ass = as_set_find(name)) != NULL) + return ass; + + if ((ass = calloc(1, sizeof(*ass))) == NULL) + err(1, "expand_as_set calloc"); + if ((ass->name = strdup(name)) == NULL) + err(1, "expand_as_set strdup"); + RB_INSERT(as_set_h, &as_set_h, ass); + + curass = ass; + if ((r = whois(name, QTYPE_ASSET)) == -1) + errx(1, "whois error, asset_get %s", name); + curass = NULL; + + /* + * if there are no members, this is an aut-num. + * if thsi was specified directly in the policy, + * make a dummy as-set with the the AS as name + * and its only member */ + if (ass->n_members == 0) + asset_add_as(ass, name); + + for (i = 0; i < ass->n_members; i++) { + mas = asset_get(ass->members[i]); + if (mas->n_members == 0) + asset_add_as(ass, ass->members[i]); + else + asset_add_asset(ass, ass->members[i]); + } + + return (ass); +} + +void +asset_resolve(struct as_set *ass) +{ + struct as_set *mas; + u_int i; + + /* + * traverse all as_set members and fold their + * members as into this as_set. + * ass->n_as_set is a moving target, it grows + * as member as-sets' member as-sets are beeing + * added. + * remove processed member as-sets (all!) only + * after we are done, they're needed for dupe + * detection + */ + + for (i = 0; i < ass->n_as_set; i++) { + if ((mas = as_set_find(ass->as_set[i])) == NULL) + errx(1, "asset_get %s: %s unresolved?!?", + ass->name, ass->as_set[i]); + if (asset_merge(ass, mas) == -1) + errx(1, "asset_merge failed"); + } + + for (i = 0; i < ass->n_as_set; i++) { + free(ass->as_set[i]); + ass->as_set[i] = NULL; + } + free(ass->as_set); + ass->as_set = NULL; + ass->n_as_set = 0; +} + +int +asset_merge(struct as_set *ass, struct as_set *mas) +{ + u_int i, j; + + /* merge ASes from the member into the parent */ + for (i = 0; i < mas->n_as; i++) { + for (j = 0; j < ass->n_as && strcmp(ass->as[j], + mas->as[i]); j++) + ; /* nothing */ + if (j == ass->n_as) + if (asset_add_as(ass, mas->as[i]) == -1) + return (-1); + } + + /* merge as-set members from the member into the parent */ + for (i = 0; i < mas->n_as_set; i++) { + if (!strcmp(ass->name, mas->as_set[i])) /* skip self! */ + continue; + for (j = 0; j < ass->n_as_set && strcmp(ass->as_set[j], + mas->as_set[i]); j++) + ; /* nothing */ + if (j == ass->n_as_set) + if (asset_add_asset(ass, mas->as_set[i]) == -1) + return (-1); + } + + return (0); +} + +int +asset_addmember(char *s) +{ + void *p; + + if ((p = realloc(curass->members, + (curass->n_members + 1) * sizeof(char *))) == NULL) + err(1, "asset_addmember strdup"); + curass->members = p; + curass->n_members++; + + if ((curass->members[curass->n_members - 1] = + strdup(s)) == NULL) + err(1, "asset_addmember strdup"); + + return (0); +} + +int +asset_add_as(struct as_set *ass, char *s) +{ + void *p; + + if ((p = realloc(ass->as, + (ass->n_as + 1) * sizeof(char *))) == NULL) + err(1, "asset_add_as strdup"); + ass->as = p; + ass->n_as++; + + if ((ass->as[ass->n_as - 1] = + strdup(s)) == NULL) + err(1, "asset_add_as strdup"); + + return (0); +} + +int +asset_add_asset(struct as_set *ass, char *s) +{ + void *p; + + if ((p = realloc(ass->as_set, + (ass->n_as_set + 1) * sizeof(char *))) == NULL) + err(1, "asset_add_asset strdup"); + ass->as_set = p; + ass->n_as_set++; + + if ((ass->as_set[ass->n_as_set - 1] = + strdup(s)) == NULL) + err(1, "asset_add_asset strdup"); + + return (0); +} + +/* RB helpers */ +int +as_set_compare(struct as_set *a, struct as_set *b) +{ + return (strcmp(a->name, b->name)); +} + +struct as_set * +as_set_find(char *name) +{ + struct as_set s; + + s.name = name; + return (RB_FIND(as_set_h, &as_set_h, &s)); +} diff --git a/usr.sbin/bgpctl/irr_output.c b/usr.sbin/bgpctl/irr_output.c new file mode 100644 index 00000000000..b0503ca2d4a --- /dev/null +++ b/usr.sbin/bgpctl/irr_output.c @@ -0,0 +1,183 @@ +/* $OpenBSD: irr_output.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 "irrfilter.h" + +int process_policies(struct policy_head *, char *); +void policy_prettyprint(FILE *, struct policy_item *); +void policy_torule(FILE *, struct policy_item *); +char *action_torule(char *); +void print_rule(FILE *, enum pdir, char *, char *, char *); + +int +write_filters(char *outpath) +{ + struct router *r; + int ret = 0; + + while ((r = TAILQ_FIRST(&router_head)) != NULL) { + TAILQ_REMOVE(&router_head, r, entry); + printf("\nRouter %s\n", r->address); + + if (process_policies(&r->policy_h, + r->address) == -1) + ret = -1; + + free(r->address); + free(r); + } + + return (ret); +} + +int +process_policies(struct policy_head *head, char *router) +{ + struct policy_item *pi; + + while ((pi = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, pi, entry); + + policy_prettyprint(stdout, pi); + policy_torule(stdout, pi); + + free(pi->peer_as); + free(pi->peer_addr); + free(pi->action); + free(pi->filter); + free(pi); + } + + return (0); +} + +void +policy_prettyprint(FILE *fh, struct policy_item *pi) +{ + if (pi->dir == IMPORT) + fprintf(fh, "# import: from "); + else + fprintf(fh, "# export: to "); + fprintf(fh, "%s ", pi->peer_as); + if (pi->peer_addr) + fprintf(fh, "%s ", pi->peer_addr); + if (pi->action) + fprintf(fh, "action %s ", pi->action); + fprintf(fh, "%s %s\n", pi->dir == IMPORT ? "accept" : "announce", + pi->filter); +} + +void +policy_torule(FILE *fh, struct policy_item *pi) +{ + struct as_set *ass; + struct prefix_set *pfxs; + u_int i, j; + + if (pi->filter == NULL || !strcasecmp(pi->filter, "any")) + print_rule(fh, pi->dir, pi->peer_addr, pi->action, NULL); + else { + ass = asset_expand(pi->filter); + + for (i = 0; i < ass->n_as; i++) { + pfxs = prefixset_get(ass->as[i]); + printf("# prefixes from %s\n", ass->as[i]); + for (j = 0; j < pfxs->prefixcnt; j++) + print_rule(fh, pi->dir, pi->peer_addr, + pi->action, pfxs->prefix[j]); + } + } +} + +/* XXX should really be parsed earlier! */ +char * +action_torule(char *s) +{ + int cnt = 0; + char *key, *val, *pre, *buf, *tmp; + static char abuf[8192]; + char ebuf[2048]; + + if ((tmp = strdup(s)) == NULL) + err(1, "foo"); + abuf[0] = '\0'; + buf = abuf; + while ((val = strsep(&tmp, ";")) != NULL && *val) { + key = strsep(&val, "="); + if (key == NULL || val == NULL) + err(1, "format error in action spec\n"); + + EATWS(key); + EATWS(val); + + if (cnt++ == 0) + pre = " set {"; + else + pre = ","; + + if (!strcmp(key, "pref")) + snprintf(ebuf, sizeof(ebuf), + "%s localpref=%s", pre, val); + else if (!strcmp(key, "med")) + snprintf(ebuf, sizeof(ebuf), + "%s med=%s", pre, val); + else + warnx("unknown action key \"%s\"", key); + + strlcat(abuf, ebuf, sizeof(abuf)); + } + if (cnt > 0) + strlcat(abuf, " }", sizeof(abuf)); + + free(tmp); + return (abuf); +} + +void +print_rule(FILE *fh, enum pdir pdir, char *peerspec, char *actspec, + char *prefix) +{ + char *fmt = "allow quick %s %s%s%s%s\n"; + char *peer = "any"; + char *action = ""; + char *dir; + + if (pdir == IMPORT) + dir = "from"; + else + dir = "to"; + + if (peerspec) + peer = peerspec; + + if (actspec) + action = action_torule(actspec); + + if (prefix == NULL) + fprintf(fh, fmt, dir, peer, action, "", ""); + else + fprintf(fh, fmt, dir, peer, action, " prefix ", prefix); +} diff --git a/usr.sbin/bgpctl/irr_parser.c b/usr.sbin/bgpctl/irr_parser.c new file mode 100644 index 00000000000..8d9e34a9e45 --- /dev/null +++ b/usr.sbin/bgpctl/irr_parser.c @@ -0,0 +1,395 @@ +/* $OpenBSD: irr_parser.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 "irrfilter.h" + +#define PARSEBUF_INCREMENT 4096 + +int lineno; +char *parsebuf = NULL; +size_t parsebuflen = 0; + +void grow_parsebuf(void); +char *irr_getln(FILE *f); +int parse_policy(char *, char *); +int policy_additem(char *, struct policy_item *); +int parse_asset(char *, char *); +int parse_route(char *, char *); + +int +parse_response(FILE *f, enum qtype qtype) +{ + char *key, *val; + int cnt, n; + + lineno = 1; + cnt = 0; + while ((val = irr_getln(f)) != NULL) { + if (!strncmp(val, "%ERROR:101:", 11)) /* no entries found */ + return (0); + + if (val[0] == '%') { + warnx("message from whois server: %s", val); + return (-1); + } + + key = strsep(&val, ":"); + if (val == NULL) { + warnx("%u: %s", lineno, key); + warnx("no \":\" found!"); + return (-1); + } + while (ISWS(*val)) + val++; + + switch (qtype) { + case QTYPE_OWNAS: + if ((n = parse_policy(key, val)) == -1) + return (-1); + break; + case QTYPE_ASSET: + if ((n = parse_asset(key, val)) == -1) + return (-1); + break; + case QTYPE_ROUTE: + if ((n = parse_route(key, val)) == -1) + return (-1); + break; + default: + err(1, "king bula suffers from dementia"); + } + cnt += n; + } + + return (cnt); +} + +void +grow_parsebuf(void) +{ + char *p; + size_t newlen; + + newlen = parsebuflen + PARSEBUF_INCREMENT; + if ((p = realloc(parsebuf, newlen)) == NULL) + err(1, "grow_parsebuf realloc"); + parsebuf = p; + parsebuflen = newlen; + + if (0) + fprintf(stderr, "parsebuf now %lu bytes\n", (ulong)parsebuflen); +} + +char * +irr_getln(FILE *f) +{ + int c, next, last; + char *p; + + if (parsebuf == NULL) + grow_parsebuf(); + p = parsebuf; + last = -1; + + do { + c = getc(f); + + if (p == parsebuf) { /* beginning of new line */ + if (c == '%') { + next = getc(f); + switch (next) { + case ' ': /* comment. skip over */ + while ((c = getc(f)) != '\n' && + c != EOF) + ; /* nothing */ + break; + case '\n': + case EOF: + c = next; + break; + default: + ungetc(next, f); + break; + } + } + } + + if (c == '#') /* skip until \n */ + while ((c = getc(f)) != '\n' && c != EOF) + ; /* nothing */ + + if (c == '\n') { + lineno++; + next = getc(f); + if (next == '+') /* continuation, skip the + */ + c = getc(f); + else if (ISWS(next)) /* continuation */ + c = next; + else + ungetc(next, f); + } + + + if (c == '\n' || c == EOF) { + if (c == EOF) + if (ferror(f)) + err(1, "ferror"); + if (p > parsebuf) { + *p = '\0'; + return (parsebuf); + } + } else { + if (!(ISWS(c) && ISWS(last))) { + if (p + 1 >= parsebuf + parsebuflen - 1) { + size_t offset; + + offset = p - parsebuf; + grow_parsebuf(); + p = parsebuf + offset; + } + if (ISWS(c)) /* equal opportunity whitespace */ + *p++ = ' '; + else + *p++ = (char)c; + } + last = c; + } + } while (c != EOF); + + return (NULL); +} + +/* + * parse the policy from an aut-num object + */ + +enum policy_parser_st { + PO_NONE, + PO_PEER_KEY, + PO_PEER_AS, + PO_PEER_ADDR, + PO_RTR_KEY, + PO_RTR_ADDR, + PO_ACTION_KEY, + PO_ACTION_SPEC, + PO_FILTER_KEY, + PO_FILTER_SPEC +}; + +int +parse_policy(char *key, char *val) +{ + struct policy_item *pi; + enum pdir dir; + enum policy_parser_st st = PO_NONE, nextst; + char *tok, *router = NULL, *p; + + if (!strcmp(key, "import")) + dir = IMPORT; + else if (!strcmp(key, "export")) + dir = EXPORT; + else /* ignore! */ + return (0); + + if (dir == EXPORT && (irrflags & F_IMPORTONLY)) + return (0); + + if ((pi = calloc(1, sizeof(*pi))) == NULL) + err(1, "parse_policy calloc"); + pi->dir = dir; + + while ((tok = strsep(&val, " ")) != NULL) { + nextst = PO_NONE; + if (dir == IMPORT) { + if (!strcmp(tok, "from")) + nextst = PO_PEER_KEY; + else if (!strcmp(tok, "at")) + nextst = PO_RTR_KEY; + else if (!strcmp(tok, "action")) + nextst = PO_ACTION_KEY; + else if (!strcmp(tok, "accept")) + nextst = PO_FILTER_KEY; + } else if (dir == EXPORT) { + if (!strcmp(tok, "to")) + nextst = PO_PEER_KEY; + else if (!strcmp(tok, "at")) + nextst = PO_RTR_KEY; + else if (!strcmp(tok, "announce")) + nextst = PO_FILTER_KEY; + } + + if (nextst == PO_FILTER_KEY) /* rest is filter spec */ + if ((pi->filter = strdup(val)) == NULL) + err(1, NULL); + + if (nextst == PO_ACTION_KEY) { + /* action list. ends after last ; */ + p = strrchr(val, ';'); + if (p == NULL || !ISWS(*++p)) + errx(1, "syntax error in action spec"); + *p = '\0'; + if ((pi->action = strdup(val)) == NULL) + err(1, NULL); + val = ++p; + while (ISWS(*p)) + p++; + } + + switch (st) { + case PO_NONE: + if (nextst != PO_PEER_KEY) + goto ppoerr; + st = nextst; + break; + case PO_PEER_KEY: + if (pi->peer_as == NULL) { + if (nextst != PO_NONE) + goto ppoerr; + if ((pi->peer_as = strdup(tok)) == NULL) + err(1, NULL); + } else { + switch (nextst) { + case PO_NONE: + if (!strcasecmp(tok, "and") || + !strcasecmp(tok, "or") || + !strcasecmp(tok, "not")) + fprintf(stderr, "compound peering " + "statements are not supported"); + else /* peer address */ + if ((pi->peer_addr = strdup(tok)) == NULL) + err(1, NULL); + break; + case PO_RTR_KEY: + case PO_ACTION_KEY: + case PO_FILTER_KEY: + st = nextst; + break; + default: + goto ppoerr; + } + } + break; + case PO_PEER_AS: + case PO_PEER_ADDR: + err(1, "state error"); + break; + case PO_RTR_KEY: + if (nextst != PO_NONE) + goto ppoerr; + /* rtr address */ + if ((router = strdup(tok)) == NULL) + err(1, NULL); + st = PO_RTR_ADDR; + break; + case PO_RTR_ADDR: + if (nextst != PO_ACTION_KEY && + nextst != PO_FILTER_KEY) + goto ppoerr; + st = nextst; + break; + case PO_ACTION_KEY: + /* already handled, next must be FILTER_KEY */ + if (nextst != PO_FILTER_KEY) + goto ppoerr; + st = nextst; + break; + case PO_FILTER_KEY: + /* already handled */ + break; + case PO_ACTION_SPEC: + case PO_FILTER_SPEC: + err(1, "state error"); + break; + } + } + + if (st != PO_FILTER_KEY) + err(1, "state error"); + + if (policy_additem(router, pi) == -1) + return (-1); + + return (1); + +ppoerr: + free(pi); + fprintf(stderr, "%u: parse error\n", lineno); + return (-1); +} + +int +policy_additem(char *router, struct policy_item *pi) +{ + struct router *r; + + for (r = TAILQ_FIRST(&router_head); r != NULL && + strcmp(r->address, router); r = TAILQ_NEXT(r, entry)) + ; /* nothing */ + + if (r == NULL) { + if ((r = calloc(1, sizeof(*r))) == NULL || + (r->address = strdup(router)) == NULL) + err(1, NULL); + TAILQ_INIT(&r->policy_h); + TAILQ_INSERT_TAIL(&router_head, r, entry); + } + + TAILQ_INSERT_TAIL(&r->policy_h, pi, entry); + + return (0); +} + +/* + * parse as-set: get members + */ + +int +parse_asset(char *key, char *val) +{ + char *tok; + + if (strcmp(key, "members")) /* ignore everything else */ + return (0); + + while ((tok = strsep(&val, ",")) != NULL) { + EATWS(tok); + asset_addmember(tok); + } + + return (1); +} + +/* + * parse route obj: just get the prefix + */ +int +parse_route(char *key, char *val) +{ + if (strcmp(key, "route")) /* ignore everything else */ + return (0); + + return(prefixset_addmember(val)); +} diff --git a/usr.sbin/bgpctl/irr_prefix.c b/usr.sbin/bgpctl/irr_prefix.c new file mode 100644 index 00000000000..d694ae09cf7 --- /dev/null +++ b/usr.sbin/bgpctl/irr_prefix.c @@ -0,0 +1,102 @@ +/* $OpenBSD: irr_prefix.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 "irrfilter.h" + +int prefix_set_compare(struct prefix_set *, struct prefix_set *); +struct prefix_set + *prefix_set_find(char *); + +RB_HEAD(prefix_set_h, prefix_set) prefix_set_h; +RB_PROTOTYPE(prefix_set_h, prefix_set, entry, prefix_set_compare) +RB_GENERATE(prefix_set_h, prefix_set, entry, prefix_set_compare) + +struct prefix_set *curpfxs = NULL; + +struct prefix_set * +prefixset_get(char *as) +{ + struct prefix_set *pfxs; + int r; + + if ((pfxs = prefix_set_find(as)) != NULL) + return (pfxs); + + /* nothing found, resolve and store */ + if ((pfxs = calloc(1, sizeof(*pfxs))) == NULL) + err(1, "get_prefixset calloc"); + if ((pfxs->as = strdup(as)) == NULL) + err(1, "get_prefixset strdup"); + RB_INSERT(prefix_set_h, &prefix_set_h, pfxs); + + curpfxs = pfxs; + if ((r = whois(as, QTYPE_ROUTE)) == -1) + errx(1, "whois error, prefixset_get %s", as); + curpfxs = NULL; + + return (pfxs); +} + +int +prefixset_addmember(char *s) +{ + void *p; + u_int i; + + /* yes, there are dupes... e. g. from multiple sources */ + for (i = 0; i < curpfxs->prefixcnt; i++) + if (!strcmp(curpfxs->prefix[i], s)) + return (0); + + if ((p = realloc(curpfxs->prefix, + (curpfxs->prefixcnt + 1) * sizeof(char *))) == NULL) + err(1, "prefixset_addmember strdup"); + curpfxs->prefix = p; + curpfxs->prefixcnt++; + + if ((curpfxs->prefix[curpfxs->prefixcnt - 1] = + strdup(s)) == NULL) + err(1, "prefixset_addmember strdup"); + + return (1); +} + + +/* RB helpers */ +int +prefix_set_compare(struct prefix_set *a, struct prefix_set *b) +{ + return (strcmp(a->as, b->as)); +} + +struct prefix_set * +prefix_set_find(char *as) +{ + struct prefix_set s; + + s.as = as; + return (RB_FIND(prefix_set_h, &prefix_set_h, &s)); +} diff --git a/usr.sbin/bgpctl/irrfilter.c b/usr.sbin/bgpctl/irrfilter.c new file mode 100644 index 00000000000..203411d2d7c --- /dev/null +++ b/usr.sbin/bgpctl/irrfilter.c @@ -0,0 +1,52 @@ +/* $OpenBSD: irrfilter.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 "irrfilter.h" + +__dead void +irr_main(u_int32_t AS, int flags, char *outdir) +{ + char *query; + int r; + + fprintf(stderr, "irrfilter for: %u\n", AS); + + irrflags = flags; + TAILQ_INIT(&router_head); + + /* send query for own AS, parse policy */ + if (asprintf(&query, "AS%u", AS) == -1) + err(1, "parse_policy asprintf"); + if ((r = whois(query, QTYPE_OWNAS)) == -1) + exit (1); + if (r == 0) + errx(1, "aut-num object %s not found", query); + free(query); + + write_filters(outdir); + + exit (0); +} diff --git a/usr.sbin/bgpctl/irrfilter.h b/usr.sbin/bgpctl/irrfilter.h new file mode 100644 index 00000000000..1cd55f7dce5 --- /dev/null +++ b/usr.sbin/bgpctl/irrfilter.h @@ -0,0 +1,96 @@ +/* $OpenBSD: irrfilter.h,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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 + +#define F_IMPORTONLY 0x01 /* skip export: items */ + +int irrflags; + +enum pdir { + PDIR_NONE, + IMPORT, + EXPORT +}; + +struct policy_item { + TAILQ_ENTRY(policy_item) entry; + enum pdir dir; + char *peer_as; + char *peer_addr; + char *action; + char *filter; +}; + +TAILQ_HEAD(policy_head, policy_item); + +struct router { + TAILQ_ENTRY(router) entry; + char *address; + struct policy_head policy_h; +}; + +TAILQ_HEAD(router_head, router) router_head; + +/* keep qtype and qtype_objs in whois.c in sync! */ +enum qtype { + QTYPE_NONE, + QTYPE_OWNAS, + QTYPE_ASSET, + QTYPE_ROUTE +}; + +struct as_set { + RB_ENTRY(as_set) entry; + char *name; + char **members; /* direct members */ + char **as_set; /* members as-set */ + char **as; /* members aut-num */ + u_int n_members; + u_int n_as_set; + u_int n_as; +}; + +struct prefix_set { + RB_ENTRY(prefix_set) entry; + char *as; + char **prefix; + u_int prefixcnt; +}; + +/* eat trailing and leading whitespace */ +#define ISWS(x) (x == ' ' || x == '\t') +#define EATWS(s) \ + do { \ + char *ps; \ + while (ISWS(*s)) \ + s++; \ + ps = s + strlen(s) - 1; \ + while (ps && ps >= s && ISWS(*ps)) \ + *ps-- = '\0'; \ + } while (0); + +__dead void irr_main(u_int32_t, int, char *); +int whois(const char *, enum qtype); +int parse_response(FILE *, enum qtype); +int write_filters(char *); +struct as_set *asset_expand(char *); +int asset_addmember(char *); +struct prefix_set *prefixset_get(char *); +int prefixset_addmember(char *); diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c index caec5342bc0..1a066909c2e 100644 --- a/usr.sbin/bgpctl/parser.c +++ b/usr.sbin/bgpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.38 2007/02/22 08:38:19 henning Exp $ */ +/* $OpenBSD: parser.c,v 1.39 2007/03/03 11:45:30 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -28,6 +28,7 @@ #include #include "parser.h" +#include "irrfilter.h" enum token_type { NOTOKEN, @@ -84,6 +85,8 @@ static const struct token t_pftable[]; static const struct token t_prepnbr[]; static const struct token t_prepself[]; static const struct token t_weight[]; +static const struct token t_irrfilter[]; +static const struct token t_irrfilter_opts[]; static const struct token t_main[] = { { KEYWORD, "reload", RELOAD, NULL}, @@ -91,6 +94,7 @@ static const struct token t_main[] = { { KEYWORD, "fib", FIB, t_fib}, { KEYWORD, "neighbor", NEIGHBOR, t_neighbor}, { KEYWORD, "network", NONE, t_network}, + { KEYWORD, "irrfilter", IRRFILTER, t_irrfilter}, { ENDTOKEN, "", NONE, NULL} }; @@ -271,6 +275,16 @@ static const struct token t_weight[] = { { ENDTOKEN, "", NONE, NULL} }; +static const struct token t_irrfilter[] = { + { ASNUM, "", NONE, t_irrfilter_opts}, + { ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_irrfilter_opts[] = { + { NOTOKEN, "", NONE, NULL}, + { FLAG, "importonly", F_IMPORTONLY, t_irrfilter_opts}, + { ENDTOKEN, "", NONE, NULL} +}; static struct parse_result res; diff --git a/usr.sbin/bgpctl/parser.h b/usr.sbin/bgpctl/parser.h index a2732c8d9b4..fa92508744b 100644 --- a/usr.sbin/bgpctl/parser.h +++ b/usr.sbin/bgpctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.14 2006/08/23 08:21:11 claudio Exp $ */ +/* $OpenBSD: parser.h,v 1.15 2007/03/03 11:45:30 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -44,7 +44,8 @@ enum actions { NETWORK_ADD, NETWORK_REMOVE, NETWORK_FLUSH, - NETWORK_SHOW + NETWORK_SHOW, + IRRFILTER }; struct parse_result { diff --git a/usr.sbin/bgpctl/whois.c b/usr.sbin/bgpctl/whois.c new file mode 100644 index 00000000000..cecbf53e092 --- /dev/null +++ b/usr.sbin/bgpctl/whois.c @@ -0,0 +1,149 @@ +/* $OpenBSD: whois.c,v 1.1 2007/03/03 11:45:30 henning Exp $ */ + +/* + * Copyright (c) 2007 Henning Brauer + * + * 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 MIND, 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. + */ + +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "irrfilter.h" + +#define WHOIS_STDOPTS "-r -a" + +char *qtype_opts[] = { + "", + "-T aut-num", + "-K -T as-set", + "-K -T route -i origin" +}; + +char *server = "whois.ripe.net"; +char *port = "whois"; + +int +whois(const char *query, enum qtype qtype) +{ + FILE *sfw, *sfr; + int s, r = -1, error = 0, attempt, ret; + struct addrinfo hints, *res, *ai; + const char *reason = NULL; + char *fmt; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(server, port, &hints, &res); + if (error) { + if (error == EAI_SERVICE) + warnx("%s: bad port", port); + else + warnx("%s: %s", server, gai_strerror(error)); + return (1); + } + + for (s = -1, ai = res; ai != NULL; ai = ai->ai_next) { + attempt = 0; + do { + attempt++; + if (s != -1) + close (s); + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) { + error = errno; + reason = "socket"; + } else + r = connect(s, ai->ai_addr, ai->ai_addrlen); + } while (r == -1 && errno == ETIMEDOUT && attempt <= 3); + + if (r == -1) { + error = errno; + reason = "connect"; + close(s); + s = -1; + continue; + } + if (s != -1) + break; /*okay*/ + } + freeaddrinfo(res); + + if (s == -1) { + if (reason) { + errno = error; + warn("%s: %s", server, reason); + } else + warn("unknown error in connection attempt"); + return (1); + } + + sfr = fdopen(s, "r"); + sfw = fdopen(s, "w"); + if (sfr == NULL || sfw == NULL) + err(1, "fdopen"); + fmt = "%s %s %s\r\n"; + fprintf(sfw, fmt, WHOIS_STDOPTS, qtype_opts[qtype], query); + fflush(sfw); + + if ((ret = parse_response(sfr, qtype)) == -1) + warnx("parse error, query=\"%s %s\"", qtype_opts[qtype], query); + + fclose(sfw); + fclose(sfr); + close(s); + return (ret); +} -- cgit v1.2.3