summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2018-09-29 08:11:12 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2018-09-29 08:11:12 +0000
commit3e707678d2170dade0d8c727c0d4467b3334a779 (patch)
treec48e07dd99920136ec89a730561e4182e0c2bab1
parentd036d37464c59cd3ed85183b9251d7de0622e405 (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.c50
-rw-r--r--usr.sbin/bgpd/bgpd.h56
-rw-r--r--usr.sbin/bgpd/config.c60
-rw-r--r--usr.sbin/bgpd/parse.y136
-rw-r--r--usr.sbin/bgpd/printconf.c70
-rw-r--r--usr.sbin/bgpd/rde.c169
-rw-r--r--usr.sbin/bgpd/rde.h40
-rw-r--r--usr.sbin/bgpd/rde_attr.c47
-rw-r--r--usr.sbin/bgpd/rde_filter.c39
-rw-r--r--usr.sbin/bgpd/rde_rib.c42
-rw-r--r--usr.sbin/bgpd/rde_trie.c24
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;
}
}