diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2018-09-29 08:11:12 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2018-09-29 08:11:12 +0000 |
commit | 3e707678d2170dade0d8c727c0d4467b3334a779 (patch) | |
tree | c48e07dd99920136ec89a730561e4182e0c2bab1 | |
parent | d036d37464c59cd3ed85183b9251d7de0622e405 (diff) |
Implement origin validation in bgpd. This introduces two new tables, the
roa-set for RPKI based origin validation and a origin-set which allows to
lookup a source-as / prefix pair.
For RPKI a config can be built like this:
roa-set {
165.254.255.0/24 source-as 15562
193.0.0.0/21 maxlen 24 source-as 3333
}
deny from any ovs invalid
match from any ovs valid set community local-as:42
match from any ovs not-found set community local-as:43
Origin sets are similar but only match when the source-as / prefix pair is
valid.
match from any origin-set ARINDB set community local-as:44
Committing this now so that further work can be done in tree.
OK benno@, job@
-rw-r--r-- | usr.sbin/bgpd/bgpd.c | 50 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 56 | ||||
-rw-r--r-- | usr.sbin/bgpd/config.c | 60 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 136 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 70 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 169 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 40 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 47 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 39 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_rib.c | 42 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_trie.c | 24 |
11 files changed, 519 insertions, 214 deletions
diff --git a/usr.sbin/bgpd/bgpd.c b/usr.sbin/bgpd/bgpd.c index bc7a0445f16..0c4fad69c0d 100644 --- a/usr.sbin/bgpd/bgpd.c +++ b/usr.sbin/bgpd/bgpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.c,v 1.203 2018/09/29 07:58:06 claudio Exp $ */ +/* $OpenBSD: bgpd.c,v 1.204 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -506,15 +506,15 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) return (-1); /* prefixsets for filters in the RDE */ - while ((ps = SIMPLEQ_FIRST(conf->prefixsets)) != NULL) { - SIMPLEQ_REMOVE_HEAD(conf->prefixsets, entry); - if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSET, 0, 0, -1, + while ((ps = SIMPLEQ_FIRST(&conf->prefixsets)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&conf->prefixsets, entry); + if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIX_SET, 0, 0, -1, ps->name, sizeof(ps->name)) == -1) return (-1); RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) { RB_REMOVE(prefixset_tree, &ps->psitems, psi); - if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSETITEM, 0, - 0, -1, psi, sizeof(*psi)) == -1) + if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIX_SET_ITEM, + 0, 0, -1, psi, sizeof(*psi)) == -1) return (-1); set_free(psi->set); free(psi); @@ -522,10 +522,10 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) free(ps); } - /* roasets for filters in the RDE */ - while ((ps = SIMPLEQ_FIRST(conf->roasets)) != NULL) { - SIMPLEQ_REMOVE_HEAD(conf->roasets, entry); - if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, + /* originsets for filters in the RDE */ + while ((ps = SIMPLEQ_FIRST(&conf->originsets)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&conf->originsets, entry); + if (imsg_compose(ibuf_rde, IMSG_RECONF_ORIGIN_SET, 0, 0, -1, ps->name, sizeof(ps->name)) == -1) return (-1); RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) { @@ -536,12 +536,12 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) for (i = 0; i < n; i += l) { l = (n - i > 1024 ? 1024 : n - i); if (imsg_compose(ibuf_rde, - IMSG_RECONF_ROA_AS_SET_ITEMS, + IMSG_RECONF_ROA_SET_ITEMS, 0, 0, -1, rs + i, l * sizeof(*rs)) == -1) return -1; } - if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSETITEM, 0, - 0, -1, psi, sizeof(*psi)) == -1) + if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIX_SET_ITEM, + 0, 0, -1, psi, sizeof(*psi)) == -1) return (-1); set_free(psi->set); free(psi); @@ -549,6 +549,30 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) free(ps); } + if (!RB_EMPTY(&conf->roa)) { + if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, + NULL, 0) == -1) + return (-1); + RB_FOREACH_SAFE(psi, prefixset_tree, &conf->roa, npsi) { + struct roa_set *rs; + size_t i, l, n; + RB_REMOVE(prefixset_tree, &conf->roa, psi); + rs = set_get(psi->set, &n); + for (i = 0; i < n; i += l) { + l = (n - i > 1024 ? 1024 : n - i); + if (imsg_compose(ibuf_rde, + IMSG_RECONF_ROA_SET_ITEMS, + 0, 0, -1, rs + i, l * sizeof(*rs)) == -1) + return -1; + } + if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIX_SET_ITEM, + 0, 0, -1, psi, sizeof(*psi)) == -1) + return (-1); + set_free(psi->set); + free(psi); + } + } + /* as-sets for filters in the RDE */ while ((aset = SIMPLEQ_FIRST(conf->as_sets)) != NULL) { struct ibuf *wbuf; diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index b35e09fd804..8e6a4bcb2c7 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.346 2018/09/29 07:58:06 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.347 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -212,8 +212,25 @@ TAILQ_HEAD(network_head, network); struct prefixset; SIMPLEQ_HEAD(prefixset_head, prefixset); -struct rde_prefixset_head; -struct rde_prefixset; +struct prefixset_item; +RB_HEAD(prefixset_tree, prefixset_item); + +struct tentry_v4; +struct tentry_v6; +struct trie_head { + struct tentry_v4 *root_v4; + struct tentry_v6 *root_v6; + int match_default_v4; + int match_default_v6; +}; + +struct rde_prefixset { + char name[SET_NAME_LEN]; + struct trie_head th; + SIMPLEQ_ENTRY(rde_prefixset) entry; + int dirty; +}; +SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset); struct set_table; struct as_set; @@ -228,10 +245,12 @@ struct bgpd_config { struct filter_head *filters; struct listen_addrs *listen_addrs; struct mrt_head *mrt; - struct prefixset_head *prefixsets; - struct prefixset_head *roasets; - struct rde_prefixset_head *rde_prefixsets; - struct rde_prefixset_head *rde_roasets; + struct prefixset_head prefixsets; + struct prefixset_head originsets; + struct prefixset_tree roa; + struct rde_prefixset_head rde_prefixsets; + struct rde_prefixset_head rde_originsets; + struct rde_prefixset rde_roa; struct as_set_head *as_sets; char *csock; char *rcsock; @@ -428,13 +447,14 @@ enum imsg_type { IMSG_RECONF_RDOMAIN_EXPORT, IMSG_RECONF_RDOMAIN_IMPORT, IMSG_RECONF_RDOMAIN_DONE, - IMSG_RECONF_PREFIXSET, - IMSG_RECONF_PREFIXSETITEM, + IMSG_RECONF_PREFIX_SET, + IMSG_RECONF_PREFIX_SET_ITEM, IMSG_RECONF_AS_SET, IMSG_RECONF_AS_SET_ITEMS, IMSG_RECONF_AS_SET_DONE, + IMSG_RECONF_ORIGIN_SET, IMSG_RECONF_ROA_SET, - IMSG_RECONF_ROA_AS_SET_ITEMS, + IMSG_RECONF_ROA_SET_ITEMS, IMSG_RECONF_DRAIN, IMSG_RECONF_DONE, IMSG_UPDATE, @@ -696,6 +716,16 @@ struct filter_prefixset { struct rde_prefixset *ps; }; +struct filter_originset { + char name[SET_NAME_LEN]; + struct rde_prefixset *ps; +}; + +struct filter_ovs { + u_int8_t validity; + u_int8_t is_set; +}; + struct filter_community { int as; int type; @@ -887,6 +917,8 @@ struct filter_match { struct filter_largecommunity large_community; struct filter_extcommunity ext_community; struct filter_prefixset prefixset; + struct filter_originset originset; + struct filter_ovs ovs; }; union filter_rule_ptr { @@ -968,7 +1000,6 @@ struct prefixset_item { RB_ENTRY(prefixset_item) entry; struct set_table *set; }; -RB_HEAD(prefixset_tree, prefixset_item); struct prefixset { int sflags; @@ -1016,6 +1047,8 @@ extern struct rib_names ribnames; /* 4-byte magic AS number */ #define AS_TRANS 23456 +/* AS_NONE for origin validation */ +#define AS_NONE 0 struct rde_memstats { int64_t path_cnt; @@ -1100,6 +1133,7 @@ int control_imsg_relay(struct imsg *); struct bgpd_config *new_config(void); void free_config(struct bgpd_config *); void free_prefixsets(struct prefixset_head *); +void free_prefixtree(struct prefixset_tree *); void filterlist_free(struct filter_head *); int host(const char *, struct bgpd_addr *, u_int8_t *); void copy_filterset(struct filter_set_head *, struct filter_set_head *); diff --git a/usr.sbin/bgpd/config.c b/usr.sbin/bgpd/config.c index 34509dd21ad..a8cb616790f 100644 --- a/usr.sbin/bgpd/config.c +++ b/usr.sbin/bgpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.76 2018/09/21 20:45:50 kn Exp $ */ +/* $OpenBSD: config.c,v 1.77 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org> @@ -63,11 +63,6 @@ new_config(void) conf->default_tableid) == -1) fatal(NULL); - if ((conf->prefixsets = calloc(1, sizeof(struct prefixset_head))) - == NULL) - fatal(NULL); - if ((conf->roasets = calloc(1, sizeof(struct prefixset_head))) == NULL) - fatal(NULL); if ((conf->as_sets = calloc(1, sizeof(struct as_set_head))) == NULL) fatal(NULL); if ((conf->filters = calloc(1, sizeof(struct filter_head))) == NULL) @@ -81,8 +76,9 @@ new_config(void) /* init the various list for later */ TAILQ_INIT(&conf->networks); SIMPLEQ_INIT(&conf->rdomains); - SIMPLEQ_INIT(conf->prefixsets); - SIMPLEQ_INIT(conf->roasets); + SIMPLEQ_INIT(&conf->prefixsets); + SIMPLEQ_INIT(&conf->originsets); + RB_INIT(&conf->roa); SIMPLEQ_INIT(conf->as_sets); TAILQ_INIT(conf->filters); @@ -122,22 +118,25 @@ void free_prefixsets(struct prefixset_head *psh) { struct prefixset *ps; - struct prefixset_item *psi, *npsi; - - if (psh == NULL) - return; while (!SIMPLEQ_EMPTY(psh)) { ps = SIMPLEQ_FIRST(psh); - RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) { - RB_REMOVE(prefixset_tree, &ps->psitems, psi); - set_free(psi->set); - free(psi); - } + free_prefixtree(&ps->psitems); SIMPLEQ_REMOVE_HEAD(psh, entry); free(ps); } - free(psh); +} + +void +free_prefixtree(struct prefixset_tree *p) +{ + struct prefixset_item *psi, *npsi; + + RB_FOREACH_SAFE(psi, prefixset_tree, p, npsi) { + RB_REMOVE(prefixset_tree, p, psi); + set_free(psi->set); + free(psi); + } } void @@ -149,8 +148,9 @@ free_config(struct bgpd_config *conf) free_rdomains(&conf->rdomains); free_networks(&conf->networks); filterlist_free(conf->filters); - free_prefixsets(conf->prefixsets); - free_prefixsets(conf->roasets); + free_prefixsets(&conf->prefixsets); + free_prefixsets(&conf->originsets); + free_prefixtree(&conf->roa); as_sets_free(conf->as_sets); while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) { @@ -224,15 +224,19 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, xconf->filters = conf->filters; conf->filters = NULL; + /* switch the roa, first remove the old one */ + free_prefixtree(&xconf->roa); + /* then move the RB tree root */ + RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa); + RB_ROOT(&conf->roa) = NULL; + /* switch the prefixsets, first remove the old ones */ - free_prefixsets(xconf->prefixsets); - xconf->prefixsets = conf->prefixsets; - conf->prefixsets = NULL; + free_prefixsets(&xconf->prefixsets); + SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets); - /* switch the roasets, first remove the old ones */ - free_prefixsets(xconf->roasets); - xconf->roasets = conf->roasets; - conf->roasets = NULL; + /* switch the originsets, first remove the old ones */ + free_prefixsets(&xconf->originsets); + SIMPLEQ_CONCAT(&xconf->originsets, &conf->originsets); /* switch the as_sets, first remove the old ones */ as_sets_free(xconf->as_sets); @@ -511,7 +515,7 @@ expand_networks(struct bgpd_config *c) TAILQ_FOREACH_SAFE(n, nw, entry, tmp) { if (n->net.type == NETWORK_PREFIXSET) { TAILQ_REMOVE(nw, n, entry); - if ((ps = find_prefixset(n->net.psname, c->prefixsets)) + if ((ps = find_prefixset(n->net.psname, &c->prefixsets)) == NULL) fatal("%s: prefixset %s not found", __func__, n->net.psname); diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 43ecd4a3f30..9e2359f31a3 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.360 2018/09/27 13:48:00 benno Exp $ */ +/* $OpenBSD: parse.y,v 1.361 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -92,8 +92,8 @@ static struct peer *peer_l, *peer_l_old; static struct peer *curpeer; static struct peer *curgroup; static struct rdomain *currdom; -static struct prefixset *curpset; -static struct prefixset *curroaset; +static struct prefixset *curpset, *curoset; +static struct prefixset_tree *curpsitree; static struct filter_head *filter_l; static struct filter_head *peerfilter_l; static struct filter_head *groupfilter_l; @@ -213,7 +213,8 @@ typedef struct { %token FROM TO ANY %token CONNECTED STATIC %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE -%token PREFIX PREFIXLEN PREFIXSET ROASET +%token PREFIX PREFIXLEN PREFIXSET +%token ROASET ORIGINSET OVS %token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY @@ -226,7 +227,7 @@ typedef struct { %token <v.number> NUMBER %type <v.number> asnumber as4number as4number_any optnumber %type <v.number> espah family restart origincode nettype -%type <v.number> yesno inout restricted +%type <v.number> yesno inout restricted validity %type <v.string> string %type <v.addr> address %type <v.prefix> prefix addrspec @@ -253,6 +254,7 @@ grammar : /* empty */ | grammar as_set '\n' | grammar prefixset '\n' | grammar roa_set '\n' + | grammar origin_set '\n' | grammar conf_main '\n' | grammar rdomain '\n' | grammar neighbor '\n' @@ -432,7 +434,7 @@ prefixset : PREFIXSET STRING '{' optnl { } free($2); } prefixset_l optnl '}' { - SIMPLEQ_INSERT_TAIL(conf->prefixsets, curpset, entry); + SIMPLEQ_INSERT_TAIL(&conf->prefixsets, curpset, entry); curpset = NULL; } | PREFIXSET STRING '{' optnl '}' { @@ -441,7 +443,7 @@ prefixset : PREFIXSET STRING '{' optnl { YYERROR; } free($2); - SIMPLEQ_INSERT_TAIL(conf->prefixsets, curpset, entry); + SIMPLEQ_INSERT_TAIL(&conf->prefixsets, curpset, entry); curpset = NULL; } @@ -492,24 +494,35 @@ prefixset_item : prefix prefixlenop { } ; -roa_set : ROASET STRING '{' optnl { - if ((curroaset = new_prefix_set($2, 1)) == NULL) { +roa_set : ROASET '{' optnl { + curpsitree = &conf->roa; + } roa_set_l optnl '}' { + curpsitree = NULL; + } + | ROASET '{' optnl '}' /* nothing */ + ; + +origin_set : ORIGINSET STRING '{' optnl { + if ((curoset = new_prefix_set($2, 1)) == NULL) { free($2); YYERROR; } + curpsitree = &curoset->psitems; free($2); } roa_set_l optnl '}' { - SIMPLEQ_INSERT_TAIL(conf->roasets, curroaset, entry); - curroaset = NULL; + SIMPLEQ_INSERT_TAIL(&conf->originsets, curoset, entry); + curoset = NULL; + curpsitree = NULL; } - | ROASET STRING '{' optnl '}' { - if ((curroaset = new_prefix_set($2, 1)) == NULL) { + | ORIGINSET STRING '{' optnl '}' { + if ((curoset = new_prefix_set($2, 1)) == NULL) { free($2); YYERROR; } free($2); - SIMPLEQ_INSERT_TAIL(conf->roasets, curroaset, entry); - curroaset = NULL; + SIMPLEQ_INSERT_TAIL(&conf->originsets, curoset, entry); + curoset = NULL; + curpsitree = NULL; } ; @@ -864,7 +877,7 @@ network : NETWORK prefix filter_set { | NETWORK PREFIXSET STRING filter_set { struct prefixset *ps; struct network *n; - if ((ps = find_prefixset($3, conf->prefixsets)) + if ((ps = find_prefixset($3, &conf->prefixsets)) == NULL) { yyerror("prefix-set %s not defined", ps->name); free($3); @@ -2175,6 +2188,21 @@ filter_elm : filter_prefix_h { free($2); free($3); } + | EXTCOMMUNITY OVS STRING { + if (fmopts.m.ext_community.flags & + EXT_COMMUNITY_FLAG_VALID) { + yyerror("\"ext-community\" already specified"); + free($3); + YYERROR; + } + + if (parseextcommunity(&fmopts.m.ext_community, + "ovs", $3) == -1) { + free($3); + YYERROR; + } + free($3); + } | NEXTHOP address { if (fmopts.m.nexthop.flags) { yyerror("nexthop already specified"); @@ -2204,9 +2232,9 @@ filter_elm : filter_prefix_h { free($2); YYERROR; } - if ((ps = find_prefixset($2, conf->prefixsets)) + if ((ps = find_prefixset($2, &conf->prefixsets)) == NULL) { - yyerror("prefix-set not defined"); + yyerror("prefix-set '%s' not defined", $2); free($2); YYERROR; } @@ -2228,7 +2256,7 @@ filter_elm : filter_prefix_h { if ($3.op == OP_RANGE && ps->sflags & PREFIXSET_FLAG_OPS) { yyerror("prefix-set %s contains prefixlen " "operators and cannot be used with an " - "or-longer filter", ps->name); + "or-longer filter", $2); free($2); YYERROR; } @@ -2237,9 +2265,36 @@ filter_elm : filter_prefix_h { fmopts.m.prefixset.flags |= PREFIXSET_FLAG_LONGER; fmopts.m.prefixset.flags |= PREFIXSET_FLAG_FILTER; - fmopts.m.prefixset.ps = NULL; free($2); } + | ORIGINSET STRING { + if (fmopts.m.originset.name[0] != '\0') { + yyerror("origin-set filter already specified"); + free($2); + YYERROR; + } + if (find_prefixset($2, &conf->originsets) == NULL) { + yyerror("origin-set '%s' not defined", $2); + free($2); + YYERROR; + } + if (strlcpy(fmopts.m.originset.name, $2, + sizeof(fmopts.m.originset.name)) >= + sizeof(fmopts.m.originset.name)) { + yyerror("origin-set name too long"); + free($2); + YYERROR; + } + free($2); + } + | OVS validity { + if (fmopts.m.ovs.is_set) { + yyerror("ovs filter already specified"); + YYERROR; + } + fmopts.m.ovs.validity = $2; + fmopts.m.ovs.is_set = 1; + } ; prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); } @@ -2647,6 +2702,22 @@ filter_set_opt : LOCALPREF NUMBER { free($3); free($4); } + | EXTCOMMUNITY delete OVS STRING { + if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) + fatal(NULL); + if ($2) + $$->type = ACTION_DEL_EXT_COMMUNITY; + else + $$->type = ACTION_SET_EXT_COMMUNITY; + + if (parseextcommunity(&$$->action.ext_community, + "ovs", $4) == -1) { + free($4); + free($$); + YYERROR; + } + free($4); + } | ORIGIN origincode { if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); @@ -2655,7 +2726,7 @@ filter_set_opt : LOCALPREF NUMBER { } ; -origincode : string { +origincode : STRING { if (!strcmp($1, "egp")) $$ = ORIGIN_EGP; else if (!strcmp($1, "igp")) @@ -2670,6 +2741,21 @@ origincode : string { free($1); }; +validity : STRING { + if (!strcmp($1, "not-found")) + $$ = ROA_NOTFOUND; + else if (!strcmp($1, "invalid")) + $$ = ROA_INVALID; + else if (!strcmp($1, "valid")) + $$ = ROA_VALID; + else { + yyerror("unknown validity \"%s\"", $1); + free($1); + YYERROR; + } + free($1); + }; + optnl : /* empty */ | '\n' optnl ; @@ -2798,7 +2884,9 @@ lookup(char *s) { "on", ON}, { "or-longer", LONGER}, { "origin", ORIGIN}, + { "origin-set", ORIGINSET}, { "out", OUT}, + { "ovs", OVS}, { "passive", PASSIVE}, { "password", PASSWORD}, { "peer-as", PEERAS}, @@ -4287,12 +4375,12 @@ static struct prefixset * new_prefix_set(char *name, int is_roa) { const char *type = "prefix-set"; - struct prefixset_head *sets = conf->prefixsets; + struct prefixset_head *sets = &conf->prefixsets; struct prefixset *pset; if (is_roa) { type = "roa-set"; - sets = conf->roasets; + sets = &conf->originsets; } if (find_prefixset(name, sets) != NULL) { @@ -4321,7 +4409,7 @@ add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max) /* no prefixlen option in this tree */ npsi->p.op = OP_NONE; npsi->p.len_max = npsi->p.len_min = npsi->p.len; - psi = RB_INSERT(prefixset_tree, &curroaset->psitems, npsi); + psi = RB_INSERT(prefixset_tree, curpsitree, npsi); if (psi == NULL) psi = npsi; diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 451a50bf686..e9dfebdffa4 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.122 2018/09/21 04:55:27 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.123 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -42,7 +42,8 @@ const char *print_af(u_int8_t); void print_network(struct network_config *, const char *); void print_as_sets(struct as_set_head *); void print_prefixsets(struct prefixset_head *); -void print_roasets(struct prefixset_head *); +void print_originsets(struct prefixset_head *); +void print_roa(struct prefixset_tree *p); void print_peer(struct peer_config *, struct bgpd_config *, const char *); const char *print_auth_alg(u_int8_t); @@ -86,11 +87,11 @@ print_prefix(struct filter_prefix *p) printf(" prefixlen %u >< %u ", p->len_min, p->len_max); break; case OP_RANGE: - if (p->len_min == p->len_max) + if (p->len_min == p->len_max && p->len != p->len_min) printf(" prefixlen = %u", p->len_min); else if (p->len == p->len_min && p->len_max == max_len) printf(" or-longer"); - else if (p->len == p->len_min) + else if (p->len == p->len_min && p->len != p->len_max) printf(" maxlen %u", p->len_max); else if (p->len_max == max_len) printf(" prefixlen >= %u", p->len_min); @@ -487,7 +488,7 @@ print_prefixsets(struct prefixset_head *psh) } void -print_roasets(struct prefixset_head *psh) +print_originsets(struct prefixset_head *psh) { struct prefixset *ps; struct prefixset_item *psi; @@ -495,16 +496,11 @@ print_roasets(struct prefixset_head *psh) size_t i, n; SIMPLEQ_FOREACH(ps, psh, entry) { - int count = 0; - printf("roa-set \"%s\" {", ps->name); + printf("origin-set \"%s\" {", ps->name); RB_FOREACH(psi, prefixset_tree, &ps->psitems) { rs = set_get(psi->set, &n); for (i = 0; i < n; i++) { - if (count++ % 2 == 0) - printf("\n\t"); - else - printf(", "); - + printf("\n\t"); print_prefix(&psi->p); if (psi->p.len != rs[i].maxlen) printf(" maxlen %u", rs[i].maxlen); @@ -516,6 +512,30 @@ print_roasets(struct prefixset_head *psh) } void +print_roa(struct prefixset_tree *p) +{ + struct prefixset_item *psi; + struct roa_set *rs; + size_t i, n; + + if (RB_EMPTY(p)) + return; + + printf("roa-set {"); + RB_FOREACH(psi, prefixset_tree, p) { + rs = set_get(psi->set, &n); + for (i = 0; i < n; i++) { + printf("\n\t"); + print_prefix(&psi->p); + if (psi->p.len != rs[i].maxlen) + printf(" maxlen %u", rs[i].maxlen); + printf(" source-as %u", rs[i].as); + } + } + printf("\n}\n\n"); +} + +void print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c) { char *method; @@ -741,17 +761,36 @@ print_rule(struct peer *peer_l, struct filter_rule *r) } else printf("any "); + if (r->match.ovs.is_set) { + switch (r->match.ovs.validity) { + case ROA_VALID: + printf("ovs valid "); + break; + case ROA_INVALID: + printf("ovs invalid "); + break; + case ROA_NOTFOUND: + printf("ovs not-found "); + break; + default: + printf("ovs ??? %d ??? ", r->match.ovs.validity); + } + } + if (r->match.prefix.addr.aid != AID_UNSPEC) { printf("prefix "); print_prefix(&r->match.prefix); printf(" "); } - if (r->match.prefixset.flags & PREFIXSET_FLAG_FILTER) + if (r->match.prefixset.name[0] != '\0') printf("prefix-set \"%s\" ", r->match.prefixset.name); if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER) printf("or-longer "); + if (r->match.originset.name[0] != '\0') + printf("origin-set \"%s\" ", r->match.originset.name); + if (r->match.nexthop.flags) { if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR) printf("nexthop neighbor "); @@ -919,9 +958,10 @@ print_config(struct bgpd_config *conf, struct rib_names *rib_l, struct rdomain *rd; print_mainconf(conf); - print_prefixsets(conf->prefixsets); + print_roa(&conf->roa); print_as_sets(conf->as_sets); - print_roasets(conf->roasets); + print_prefixsets(&conf->prefixsets); + print_originsets(&conf->originsets); TAILQ_FOREACH(n, net_l, entry) print_network(&n->net, ""); if (!SIMPLEQ_EMPTY(rdom_l)) diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 53c0b4078aa..8c4e2bdf017 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.429 2018/09/29 07:58:06 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.430 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -103,7 +103,9 @@ void rde_update6_queue_runner(u_int8_t); struct rde_prefixset *rde_find_prefixset(char *, struct rde_prefixset_head *); void rde_free_prefixsets(struct rde_prefixset_head *); void rde_mark_prefixsets_dirty(struct rde_prefixset_head *, - struct rde_prefixset_head *); + struct rde_prefixset_head *); +u_int8_t rde_roa_validity(struct rde_prefixset *, + struct bgpd_addr *, u_int8_t, u_int32_t); void peer_init(u_int32_t); void peer_shutdown(void); @@ -130,8 +132,9 @@ struct bgpd_config *conf, *nconf; time_t reloadtime; struct rde_peer_head peerlist; struct rde_peer *peerself; -struct rde_prefixset_head *prefixsets_tmp, *prefixsets_old; -struct rde_prefixset_head *roasets_tmp, *roasets_old; +struct rde_prefixset_head prefixsets_old; +struct rde_prefixset_head originsets_old; +struct rde_prefixset roa_old; struct as_set_head *as_sets_tmp, *as_sets_old; struct filter_head *out_rules, *out_rules_tmp; struct rdomain_head *rdomains_l, *newdomains; @@ -500,6 +503,7 @@ rde_dispatch_imsg_session(struct imsgbuf *ibuf) asp->origin = csr.origin; asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC; asp->aspath = aspath_get(asdata, csr.aspath_len); + asp->source_as = aspath_origin(asp->aspath); netconf_s.asp = asp; break; case IMSG_NETWORK_ATTR: @@ -771,16 +775,6 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) sizeof(struct bgpd_config)) fatalx("IMSG_RECONF_CONF bad len"); reloadtime = time(NULL); - prefixsets_tmp = calloc(1, - sizeof(struct rde_prefixset_head)); - if (prefixsets_tmp == NULL) - fatal(NULL); - SIMPLEQ_INIT(prefixsets_tmp); - roasets_tmp = calloc(1, - sizeof(struct rde_prefixset_head)); - if (roasets_tmp == NULL) - fatal(NULL); - SIMPLEQ_INIT(roasets_tmp); as_sets_tmp = calloc(1, sizeof(struct as_set_head)); if (as_sets_tmp == NULL) @@ -803,6 +797,9 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) break; ribs[rid].state = RECONF_DELETE; } + SIMPLEQ_INIT(&nconf->rde_prefixsets); + SIMPLEQ_INIT(&nconf->rde_originsets); + memset(&nconf->rde_roa, 0, sizeof(nconf->rde_roa)); break; case IMSG_RECONF_RIB: if (imsg.hdr.len - IMSG_HEADER_SIZE != @@ -838,14 +835,22 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) if ((r = malloc(sizeof(struct filter_rule))) == NULL) fatal(NULL); memcpy(r, imsg.data, sizeof(struct filter_rule)); - if (r->match.prefixset.flags != 0) { + if (r->match.prefixset.name[0] != '\0') { r->match.prefixset.ps = rde_find_prefixset(r->match.prefixset.name, - prefixsets_tmp); + &nconf->rde_prefixsets); if (r->match.prefixset.ps == NULL) log_warnx("%s: no prefixset for %s", __func__, r->match.prefixset.name); } + if (r->match.originset.name[0] != '\0') { + r->match.originset.ps = + rde_find_prefixset(r->match.originset.name, + &nconf->rde_originsets); + if (r->match.originset.ps == NULL) + log_warnx("%s: no origin-set for %s", + __func__, r->match.originset.name); + } if (r->match.as.flags & AS_FLAG_AS_SET_NAME) { struct as_set * aset; @@ -883,24 +888,32 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) } else TAILQ_INSERT_TAIL(out_rules_tmp, r, entry); break; - case IMSG_RECONF_PREFIXSET: - case IMSG_RECONF_ROA_SET: + case IMSG_RECONF_PREFIX_SET: + case IMSG_RECONF_ORIGIN_SET: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(ps->name)) - fatalx("IMSG_RECONF_PREFIXSET bad len"); + fatalx("IMSG_RECONF_PREFIX_SET bad len"); ps = calloc(1, sizeof(struct rde_prefixset)); if (ps == NULL) fatal(NULL); memcpy(ps->name, imsg.data, sizeof(ps->name)); - if (imsg.hdr.type == IMSG_RECONF_ROA_SET) { - SIMPLEQ_INSERT_TAIL(roasets_tmp, ps, entry); - ps->roa = 1; - } else - SIMPLEQ_INSERT_TAIL(prefixsets_tmp, ps, entry); + if (imsg.hdr.type == IMSG_RECONF_ORIGIN_SET) { + SIMPLEQ_INSERT_TAIL(&nconf->rde_originsets, ps, + entry); + } else { + SIMPLEQ_INSERT_TAIL(&nconf->rde_prefixsets, ps, + entry); + } last_prefixset = ps; last_set = NULL; break; - case IMSG_RECONF_ROA_AS_SET_ITEMS: + case IMSG_RECONF_ROA_SET: + strlcpy(nconf->rde_roa.name, "RPKI ROA", + sizeof(nconf->rde_roa.name)); + last_prefixset = &nconf->rde_roa; + last_set = NULL; + break; + case IMSG_RECONF_ROA_SET_ITEMS: nmemb = imsg.hdr.len - IMSG_HEADER_SIZE; nmemb /= sizeof(struct roa_set); if (last_set == NULL) { @@ -911,14 +924,14 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) if (set_add(last_set, imsg.data, nmemb) != 0) fatal(NULL); break; - case IMSG_RECONF_PREFIXSETITEM: + case IMSG_RECONF_PREFIX_SET_ITEM: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(psi)) - fatalx("IMSG_RECONF_PREFIXSETITEM bad len"); + fatalx("IMSG_RECONF_PREFIX_SET_ITEM bad len"); memcpy(&psi, imsg.data, sizeof(psi)); if (last_prefixset == NULL) fatalx("King Bula has no prefixset"); - if (last_prefixset->roa) { + if (last_set) { set_prep(last_set); rv = trie_roa_add(&last_prefixset->th, &psi.p.addr, psi.p.len, last_set); @@ -1130,6 +1143,10 @@ rde_update_dispatch(struct imsg *imsg) } } + if (state.aspath.flags & F_ATTR_ASPATH) + state.aspath.source_as = + aspath_origin(state.aspath.aspath); + rde_reflector(peer, &state.aspath); } @@ -1388,12 +1405,17 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in, struct filterstate state; struct prefix *p; enum filter_actions action; + u_int8_t vstate; u_int16_t i; const char *wmsg = "filtered, withdraw"; peer->prefix_rcvd_update++; + vstate = rde_roa_validity(&conf->rde_roa, prefix, prefixlen, + in->aspath.source_as); + /* add original path to the Adj-RIB-In */ - if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen)) + if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen, + vstate)) peer->prefix_cnt++; /* max prefix checker */ @@ -1424,7 +1446,7 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in, &state.nexthop->exit_nexthop, prefix, prefixlen); path_update(&ribs[i].rib, peer, &state, prefix, - prefixlen); + prefixlen, vstate); } else if (prefix_remove(&ribs[i].rib, peer, prefix, prefixlen)) { rde_update_log(wmsg, i, peer, @@ -2831,16 +2853,21 @@ rde_reload_done(void) nconf->flags &= ~BGPD_FLAG_NO_EVALUATE; } - prefixsets_old = conf->rde_prefixsets; - roasets_old = conf->rde_roasets; + SIMPLEQ_INIT(&prefixsets_old); + SIMPLEQ_INIT(&originsets_old); + SIMPLEQ_CONCAT(&prefixsets_old, &conf->rde_prefixsets); + SIMPLEQ_CONCAT(&originsets_old, &conf->rde_originsets); + roa_old = conf->rde_roa; as_sets_old = conf->as_sets; memcpy(conf, nconf, sizeof(struct bgpd_config)); conf->listen_addrs = NULL; conf->csock = NULL; conf->rcsock = NULL; - conf->prefixsets = NULL; - conf->roasets = NULL; + SIMPLEQ_INIT(&conf->rde_prefixsets); + SIMPLEQ_INIT(&conf->rde_originsets); + SIMPLEQ_CONCAT(&conf->rde_prefixsets, &nconf->rde_prefixsets); + SIMPLEQ_CONCAT(&conf->rde_originsets, &nconf->rde_originsets); free(nconf); nconf = NULL; @@ -2864,17 +2891,19 @@ rde_reload_done(void) rdomains_l = newdomains; /* XXX WHERE IS THE SYNC ??? */ - rde_mark_prefixsets_dirty(prefixsets_old, prefixsets_tmp); - rde_mark_prefixsets_dirty(roasets_old, roasets_tmp); + /* check if roa changed */ + if (trie_equal(&conf->rde_roa.th, &roa_old.th) == 0) { + log_debug("roa change: reloading Adj-RIB-In"); + conf->rde_roa.dirty = 1; + reload++; /* run softreconf in */ + } + trie_free(&roa_old.th); /* old roa no longer needed */ + + rde_mark_prefixsets_dirty(&prefixsets_old, &conf->rde_prefixsets); + rde_mark_prefixsets_dirty(&originsets_old, &conf->rde_originsets); as_sets_mark_dirty(as_sets_old, as_sets_tmp); - /* swap the prefixsets */ - conf->rde_prefixsets = prefixsets_tmp; - prefixsets_tmp = NULL; - /* the roa-sets */ - conf->rde_roasets = roasets_tmp; - roasets_tmp = NULL; - /* and the as_sets */ + /* swap the as_sets */ conf->as_sets = as_sets_tmp; as_sets_tmp = NULL; @@ -3063,10 +3092,8 @@ rde_softreconfig_done(void) ribs[rid].state = RECONF_NONE; } - rde_free_prefixsets(prefixsets_old); - prefixsets_old = NULL; - rde_free_prefixsets(roasets_old); - roasets_old = NULL; + rde_free_prefixsets(&prefixsets_old); + rde_free_prefixsets(&originsets_old); as_sets_free(as_sets_old); as_sets_old = NULL; @@ -3085,25 +3112,39 @@ rde_softreconfig_in(struct rib_entry *re, void *bula) struct rde_peer *peer; struct rde_aspath *asp; enum filter_actions action; - struct bgpd_addr addr; + struct bgpd_addr prefix; + int force_eval; + u_int8_t vstate; u_int16_t i; pt = re->prefix; - pt_getaddr(pt, &addr); + pt_getaddr(pt, &prefix); LIST_FOREACH(p, &re->prefix_h, rib_l) { asp = prefix_aspath(p); peer = prefix_peer(p); + force_eval = 0; + + if (conf->rde_roa.dirty) { + /* ROA validation state update */ + vstate = rde_roa_validity(&conf->rde_roa, + &prefix, pt->prefixlen, asp->source_as); + if (vstate != p->validation_state) { + force_eval = 1; + p->validation_state = vstate; + } + } /* skip announced networks, they are never filtered */ if (asp->flags & F_PREFIX_ANNOUNCED) continue; for (i = RIB_LOC_START; i < rib_size; i++) { + if (!rib_valid(i)) + continue; + rib = &ribs[i]; - if (rib->state != RECONF_RELOAD) + if (rib->state != RECONF_RELOAD && !force_eval) continue; - if (!rib_valid(i)) - break; rde_filterstate_prep(&state, asp, prefix_nexthop(p), prefix_nhflags(p)); @@ -3111,11 +3152,11 @@ rde_softreconfig_in(struct rib_entry *re, void *bula) if (action == ACTION_ALLOW) { /* update Local-RIB */ - path_update(&rib->rib, peer, &state, &addr, - pt->prefixlen); + path_update(&rib->rib, peer, &state, &prefix, + pt->prefixlen, vstate); } else if (action == ACTION_DENY) { /* remove from Local-RIB */ - prefix_remove(&rib->rib, peer, &addr, + prefix_remove(&rib->rib, peer, &prefix, pt->prefixlen); } @@ -3627,6 +3668,7 @@ network_add(struct network_config *nc, int flagstatic) struct rde_aspath *asp; struct filter_set_head *vpnset = NULL; in_addr_t prefix4; + u_int8_t vstate; u_int16_t i; if (nc->rtableid != conf->default_tableid) { @@ -3671,6 +3713,7 @@ network_add(struct network_config *nc, int flagstatic) asp = path_get(); asp->aspath = aspath_get(NULL, 0); asp->origin = ORIGIN_IGP; + asp->source_as = aspath_origin(asp->aspath); asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH | F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED; /* the nexthop is unset unless a default set overrides it */ @@ -3683,8 +3726,10 @@ network_add(struct network_config *nc, int flagstatic) rde_apply_set(vpnset, &state, nc->prefix.aid, peerself, peerself); + vstate = rde_roa_validity(&conf->rde_roa, &nc->prefix, + nc->prefixlen, asp->source_as); if (path_update(&ribs[RIB_ADJ_IN].rib, peerself, &state, &nc->prefix, - nc->prefixlen)) + nc->prefixlen, vstate)) peerself->prefix_cnt++; for (i = RIB_LOC_START; i < rib_size; i++) { if (!rib_valid(i)) @@ -3693,7 +3738,7 @@ network_add(struct network_config *nc, int flagstatic) state.nexthop ? &state.nexthop->exit_nexthop : NULL, &nc->prefix, nc->prefixlen); path_update(&ribs[i].rib, peerself, &state, &nc->prefix, - nc->prefixlen); + nc->prefixlen, vstate); } rde_filterstate_clean(&state); path_put(asp); @@ -3903,3 +3948,13 @@ rde_mark_prefixsets_dirty(struct rde_prefixset_head *psold, } } } + +u_int8_t +rde_roa_validity(struct rde_prefixset *ps, struct bgpd_addr *prefix, + u_int8_t plen, u_int32_t as) +{ + int r; + + r = trie_roa_check(&ps->th, prefix, plen, as); + return (r & ROA_MASK); +} diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 83ede7b83e3..69962d83585 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.194 2018/09/29 07:43:36 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.195 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -36,11 +36,10 @@ enum peer_state { PEER_ERR /* error occurred going to PEER_DOWN state */ }; -enum roa_state { - ROA_UNKNOWN, - ROA_INVALID, - ROA_VALID -}; +#define ROA_NOTFOUND 0x0 /* default */ +#define ROA_INVALID 0x1 +#define ROA_VALID 0x2 +#define ROA_MASK 0x3 /* * How do we identify peers between the session handler and the rde? @@ -193,6 +192,7 @@ struct rde_aspath { struct aspath *aspath; u_int64_t hash; u_int32_t flags; /* internally used */ + u_int32_t source_as; /* cached source_as */ u_int32_t med; /* multi exit disc */ u_int32_t lpref; /* local pref */ u_int32_t weight; /* low prio lpref */ @@ -313,6 +313,7 @@ struct prefix { struct rde_peer *peer; struct nexthop *nexthop; /* may be NULL */ time_t lastchange; + u_int8_t validation_state; u_int8_t nhflags; }; @@ -327,24 +328,6 @@ struct filterstate { u_int8_t nhflags; }; -struct tentry_v4; -struct tentry_v6; -struct trie_head { - struct tentry_v4 *root_v4; - struct tentry_v6 *root_v6; - int match_default_v4; - int match_default_v6; -}; - -struct rde_prefixset { - char name[SET_NAME_LEN]; - struct trie_head th; - SIMPLEQ_ENTRY(rde_prefixset) entry; - int dirty; - int roa; -}; -SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset); - extern struct rde_memstats rdemem; /* prototypes */ @@ -398,6 +381,7 @@ u_char *aspath_dump(struct aspath *); u_int16_t aspath_length(struct aspath *); u_int16_t aspath_count(const void *, u_int16_t); u_int32_t aspath_neighbor(struct aspath *); +u_int32_t aspath_origin(struct aspath *); int aspath_loopfree(struct aspath *, u_int32_t); int aspath_compare(struct aspath *, struct aspath *); u_char *aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *); @@ -503,7 +487,7 @@ void path_init(u_int32_t); void path_shutdown(void); void path_hash_stats(struct rde_hashstats *); int path_update(struct rib *, struct rde_peer *, - struct filterstate *, struct bgpd_addr *, int); + struct filterstate *, struct bgpd_addr *, int, u_int8_t); int path_compare(struct rde_aspath *, struct rde_aspath *); void path_remove(struct rde_aspath *); u_int32_t path_remove_stale(struct rde_aspath *, u_int8_t, time_t); @@ -553,6 +537,12 @@ prefix_nhflags(struct prefix *p) return (p->nhflags); } +static inline u_int8_t +prefix_vstate(struct prefix *p) +{ + return (p->validation_state & ROA_MASK); +} + void nexthop_init(u_int32_t); void nexthop_shutdown(void); void nexthop_modify(struct nexthop *, enum action_types, u_int8_t, diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index 5b2b4688f03..9e12e8de042 100644 --- a/usr.sbin/bgpd/rde_attr.c +++ b/usr.sbin/bgpd/rde_attr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_attr.c,v 1.110 2018/09/20 11:06:04 benno Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.111 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -586,7 +586,7 @@ aspath_deflate(u_char *data, u_int16_t *len, int *flagnew) nlen += 2 + sizeof(u_int16_t) * seg_len; if (seg_size > olen) - fatalx("aspath_deflate: would overflow"); + fatalx("%s: would overflow", __func__); } if ((ndata = malloc(nlen)) == NULL) @@ -687,7 +687,7 @@ aspath_count(const void *data, u_int16_t len) cnt += seg_len; if (seg_size > len) - fatalx("aspath_count: would overflow"); + fatalx("%s: would overflow", __func__); } return (cnt); } @@ -719,7 +719,7 @@ aspath_countlength(struct aspath *aspath, u_int16_t cnt, int headcnt) clen += seg_size; if (seg_size > len) - fatalx("aspath_countlength: would overflow"); + fatalx("%s: would overflow", __func__); } if (headcnt > 0 && seg_type == AS_SEQUENCE && headcnt + seg_len < 256) /* no need for additional header from the new aspath. */ @@ -766,7 +766,7 @@ aspath_countcopy(struct aspath *aspath, u_int16_t cnt, u_int8_t *buf, buf[1] = seg_len; buf += seg_size; if (size < seg_size) - fatalx("aspath_countlength: would overflow"); + fatalx("%s: would overflow", __func__); size -= seg_size; } } @@ -780,6 +780,41 @@ aspath_neighbor(struct aspath *aspath) return (aspath_extract(aspath->data, 0)); } +/* + * The origin AS number derived from a Route as follows: + * o the rightmost AS in the final segment of the AS_PATH attribute + * in the Route if that segment is of type AS_SEQUENCE, or + * o the BGP speaker's own AS number if that segment is of type + * AS_CONFED_SEQUENCE or AS_CONFED_SET or if the AS_PATH is empty, + * o the distinguished value "NONE" if the final segment of the + * AS_PATH attribute is of any other type. + */ +u_int32_t +aspath_origin(struct aspath *aspath) +{ + u_int8_t *seg; + u_int32_t as = AS_NONE; + u_int16_t len, seg_size; + u_int8_t seg_len; + + /* AS_PATH is empty */ + if (aspath->len == 0) + return (rde_local_as()); + + seg = aspath->data; + for (len = aspath->len; len > 0; len -= seg_size, seg += seg_size) { + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + + if (len == seg_size && seg[0] == AS_SEQUENCE) { + as = aspath_extract(seg, seg_len - 1); + } + if (seg_size > len) + fatalx("%s: would overflow", __func__); + } + return (as); +} + int aspath_loopfree(struct aspath *aspath, u_int32_t myAS) { @@ -798,7 +833,7 @@ aspath_loopfree(struct aspath *aspath, u_int32_t myAS) } if (seg_size > len) - fatalx("aspath_loopfree: would overflow"); + fatalx("%s: would overflow", __func__); } return (1); } diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 7682e42cf9e..3f2b9e85605 100644 --- a/usr.sbin/bgpd/rde_filter.c +++ b/usr.sbin/bgpd/rde_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_filter.c,v 1.111 2018/09/26 15:48:01 claudio Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.112 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -360,6 +360,11 @@ rde_filter_match(struct filter_rule *f, struct rde_peer *peer, if (f->peer.ibgp && peer->conf.ebgp) return (0); + if (f->match.ovs.is_set) { + if (prefix_vstate(p) != f->match.ovs.validity) + return (0); + } + if (asp != NULL && f->match.as.type != AS_UNDEF) { if (aspath_match(asp->aspath->data, asp->aspath->len, &f->match.as, peer->conf.remote_as) == 0) @@ -408,8 +413,7 @@ rde_filter_match(struct filter_rule *f, struct rde_peer *peer, if (community_ext_match(asp, &f->match.ext_community, peer->conf.remote_as) == 0) return (0); - if (asp != NULL && f->match.large_community.as != - COMMUNITY_UNSET) { + if (asp != NULL && f->match.large_community.as != COMMUNITY_UNSET) { switch (f->match.large_community.as) { case COMMUNITY_ERROR: fatalx("rde_filter_match bad community string"); @@ -485,6 +489,18 @@ rde_filter_match(struct filter_rule *f, struct rde_peer *peer, } } + /* origin-set lookups match only on ROA_VALID */ + if (asp != NULL && f->match.originset.ps != NULL) { + struct bgpd_addr addr, *prefix = &addr; + u_int8_t plen; + + pt_getaddr(p->re->prefix, prefix); + plen = p->re->prefix->prefixlen; + if (trie_roa_check(&f->match.originset.ps->th, prefix, plen, + asp->source_as) != ROA_VALID) + return (0); + } + /* * prefixset and prefix filter rules are mutual exclusive */ @@ -578,7 +594,7 @@ rde_filter_equal(struct filter_head *a, struct filter_head *b, struct rde_peer *peer) { struct filter_rule *fa, *fb; - struct rde_prefixset *psa, *psb; + struct rde_prefixset *psa, *psb, *osa, *osb; struct as_set *asa, *asb; int r; @@ -609,26 +625,35 @@ rde_filter_equal(struct filter_head *a, struct filter_head *b, /* compare filter_rule.match without the prefixset pointer */ psa = fa->match.prefixset.ps; psb = fb->match.prefixset.ps; + osa = fa->match.originset.ps; + osb = fb->match.originset.ps; asa = fa->match.as.aset; asb = fb->match.as.aset; fa->match.prefixset.ps = fb->match.prefixset.ps = NULL; + fa->match.originset.ps = fb->match.originset.ps = NULL; fa->match.as.aset = fb->match.as.aset = NULL; r = memcmp(&fa->match, &fb->match, sizeof(fa->match)); /* fixup the struct again */ fa->match.prefixset.ps = psa; fb->match.prefixset.ps = psb; + fa->match.originset.ps = osa; + fb->match.originset.ps = osb; fa->match.as.aset = asa; fb->match.as.aset = asb; if (r != 0) return (0); - if (fa->match.prefixset.flags != 0 && - fa->match.prefixset.ps != NULL && + if (fa->match.prefixset.ps != NULL && fa->match.prefixset.ps->dirty) { log_debug("%s: prefixset %s has changed", __func__, fa->match.prefixset.name); return (0); } - + if (fa->match.originset.ps != NULL && + fa->match.originset.ps->dirty) { + log_debug("%s: originset %s has changed", + __func__, fa->match.originset.name); + return (0); + } if ((fa->match.as.flags & AS_FLAG_AS_SET) && fa->match.as.aset->dirty) { log_debug("%s: as-set %s has changed", diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index 18e5f7a0b34..f6200a7641a 100644 --- a/usr.sbin/bgpd/rde_rib.c +++ b/usr.sbin/bgpd/rde_rib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_rib.c,v 1.178 2018/09/20 11:06:04 benno Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.179 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> @@ -48,11 +48,11 @@ struct rib_entry *rib_restart(struct rib_context *); RB_PROTOTYPE(rib_tree, rib_entry, rib_e, rib_compare); RB_GENERATE(rib_tree, rib_entry, rib_e, rib_compare); -int prefix_add(struct bgpd_addr *, int, struct rib *, +static int prefix_add(struct bgpd_addr *, int, struct rib *, struct rde_peer *, struct rde_aspath *, - struct filterstate *); -int prefix_move(struct prefix *, struct rde_peer *, - struct rde_aspath *, struct filterstate *); + struct filterstate *, u_int8_t); +static int prefix_move(struct prefix *, struct rde_peer *, + struct rde_aspath *, struct filterstate *, u_int8_t); static inline void re_lock(struct rib_entry *re) @@ -419,7 +419,7 @@ path_hash_stats(struct rde_hashstats *hs) int path_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, - struct bgpd_addr *prefix, int prefixlen) + struct bgpd_addr *prefix, int prefixlen, u_int8_t vstate) { struct rde_aspath *asp, *nasp = &state->aspath; struct prefix *p; @@ -438,6 +438,7 @@ path_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, prefix_nhflags(p) == state->nhflags) { /* no change, update last change */ p->lastchange = time(NULL); + p->validation_state = vstate; return (0); } } @@ -455,10 +456,10 @@ path_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, /* If the prefix was found move it else add it to the aspath. */ if (p != NULL) - return (prefix_move(p, peer, asp, state)); + return (prefix_move(p, peer, asp, state, vstate)); else return (prefix_add(prefix, prefixlen, rib, peer, asp, - state)); + state, vstate)); } int @@ -500,6 +501,10 @@ path_compare(struct rde_aspath *a, struct rde_aspath *b) return (1); if (a->pftableid < b->pftableid) return (-1); + if (a->source_as > b->source_as) + return (1); + if (a->source_as < b->source_as) + return (-1); r = aspath_compare(a->aspath, b->aspath); if (r > 0) @@ -673,6 +678,7 @@ path_copy(struct rde_aspath *dst, const struct rde_aspath *src) dst->lpref = src->lpref; dst->weight = src->weight; dst->origin = src->origin; + dst->source_as = src->source_as; dst->rtlabelid = rtlabel_ref(src->rtlabelid); dst->pftableid = pftable_ref(src->pftableid); @@ -739,7 +745,7 @@ static struct prefix *prefix_alloc(void); static void prefix_free(struct prefix *); static void prefix_link(struct prefix *, struct rib_entry *, struct rde_peer *, struct rde_aspath *, - struct filterstate *); + struct filterstate *, u_int8_t); static void prefix_unlink(struct prefix *); /* @@ -760,9 +766,10 @@ prefix_get(struct rib *rib, struct rde_peer *peer, struct bgpd_addr *prefix, /* * Adds or updates a prefix. */ -int +static int prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, - struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state) + struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state, + u_int8_t vstate) { struct prefix *p; struct rib_entry *re; @@ -774,16 +781,17 @@ prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, p = prefix_bypeer(re, asp->peer); if (p == NULL) { p = prefix_alloc(); - prefix_link(p, re, peer, asp, state); + prefix_link(p, re, peer, asp, state, vstate); return (1); } else { if (prefix_aspath(p) != asp || prefix_nexthop(p) != state->nexthop || prefix_nhflags(p) != state->nhflags) { /* prefix metadata changed therefor move */ - return (prefix_move(p, peer, asp, state)); + return (prefix_move(p, peer, asp, state, vstate)); } p->lastchange = time(NULL); + p->validation_state = vstate; return (0); } } @@ -791,9 +799,9 @@ prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, /* * Move the prefix to the specified as path, removes the old asp if needed. */ -int +static int prefix_move(struct prefix *p, struct rde_peer *peer, - struct rde_aspath *asp, struct filterstate *state) + struct rde_aspath *asp, struct filterstate *state, u_int8_t vstate) { struct prefix *np; struct rde_aspath *oasp; @@ -809,6 +817,7 @@ prefix_move(struct prefix *p, struct rde_peer *peer, np->re = p->re; np->lastchange = time(NULL); np->nhflags = state->nhflags; + np->validation_state = vstate; /* add to new as path */ TAILQ_INSERT_HEAD(&asp->prefixes, np, path_l); @@ -1057,7 +1066,7 @@ prefix_network_clean(struct rde_peer *peer) */ static void prefix_link(struct prefix *pref, struct rib_entry *re, struct rde_peer *peer, - struct rde_aspath *asp, struct filterstate *state) + struct rde_aspath *asp, struct filterstate *state, u_int8_t vstate) { TAILQ_INSERT_HEAD(&asp->prefixes, pref, path_l); @@ -1068,6 +1077,7 @@ prefix_link(struct prefix *pref, struct rib_entry *re, struct rde_peer *peer, pref->re = re; pref->lastchange = time(NULL); pref->nhflags = state->nhflags; + pref->validation_state = vstate; /* make route decision */ prefix_evaluate(pref, re); diff --git a/usr.sbin/bgpd/rde_trie.c b/usr.sbin/bgpd/rde_trie.c index 539b2a458a0..72ba4f534be 100644 --- a/usr.sbin/bgpd/rde_trie.c +++ b/usr.sbin/bgpd/rde_trie.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_trie.c,v 1.8 2018/09/26 14:47:20 claudio Exp $ */ +/* $OpenBSD: rde_trie.c,v 1.9 2018/09/29 08:11:11 claudio Exp $ */ /* * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org> @@ -543,7 +543,7 @@ trie_roa_check_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen, { struct tentry_v4 *n; struct roa_set *rs; - int validity = ROA_UNKNOWN; + int validity = ROA_NOTFOUND; /* ignore possible default route since it does not make sense */ @@ -565,8 +565,8 @@ trie_roa_check_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen, */ validity = ROA_INVALID; - /* Treat AS 0 as NONE which can never be matched */ - if (as != 0) { + /* AS_NONE can never match, so don't try */ + if (as != AS_NONE) { if ((rs = set_match(n->set, as)) != NULL) { if (plen == n->plen || plen <= rs->maxlen) return ROA_VALID; @@ -591,7 +591,7 @@ trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen, { struct tentry_v6 *n; struct roa_set *rs; - int validity = ROA_UNKNOWN; + int validity = ROA_NOTFOUND; /* ignore possible default route since it does not make sense */ @@ -613,8 +613,8 @@ trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen, */ validity = ROA_INVALID; - /* Treat AS 0 as NONE which can never be matched */ - if (as != 0) { + /* AS_NONE can never match, so don't try */ + if (as != AS_NONE) { if ((rs = set_match(n->set, as)) != NULL) if (plen == n->plen || plen <= rs->maxlen) return ROA_VALID; @@ -635,9 +635,9 @@ trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen, /* * Do a ROA (Route Origin Validation) check. Look for elements in the trie * which cover prefix "prefix/plen" and match the source-as as. - * AS 0 is treated here like AS NONE and should be used when the source-as - * is unknown (e.g. AS_SET). In other words the check will then only return - * ROA_UNKNOWN or ROA_INVALID depending if the prefix is covered by the ROA. + * AS_NONE can be used when the source-as is unknown (e.g. AS_SET). + * The check will then only return ROA_NOTFOUND or ROA_INVALID depending if + * the prefix is covered by the ROA. */ int trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen, @@ -650,8 +650,8 @@ trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen, case AID_INET6: return trie_roa_check_v6(th, &prefix->v6, plen, as); default: - /* anything else is unknown */ - return ROA_UNKNOWN; + /* anything else is not-found */ + return ROA_NOTFOUND; } } |