From fc51cb501dd3aa311d9b11d159f41d7b1e5cf33e Mon Sep 17 00:00:00 2001 From: claudio Date: Wed, 1 Sep 2021 12:39:52 +0000 Subject: [PATCH] Implement roa-set data expiry. Every prefix in a roa-set can specify an optional expires timestamp. The rtr process is walking the roa-set every 5min and removes every prefix that is expired. With this stale RPKI data will slowly disapear and not linger around. OK job@ --- usr.sbin/bgpd/bgpd.conf.5 | 9 +++++--- usr.sbin/bgpd/bgpd.h | 3 ++- usr.sbin/bgpd/parse.y | 42 ++++++++++++++++++++++++++--------- usr.sbin/bgpd/printconf.c | 4 +++- usr.sbin/bgpd/rtr.c | 46 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 87 insertions(+), 17 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 6ee7bc244b3..5fb003fb6ea 100644 --- a/usr.sbin/bgpd/bgpd.conf.5 +++ b/usr.sbin/bgpd/bgpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.conf.5,v 1.213 2021/08/09 08:15:34 claudio Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.214 2021/09/01 12:39:52 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker .\" Copyright (c) 2003, 2004 Henning Brauer @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 9 2021 $ +.Dd $Mdocdate: September 1 2021 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -489,7 +489,8 @@ prefix-set as64496set { 192.0.2.0/24 prefixlen >= 26, .Pp .It Xo .Ic roa-set -.Ic { Ar address Ns Li / Ns Ar len Ic maxlen Ar mlen Ic source-as Ar asn ... Ic } +.Ic { Ar address Ns Li / Ns Ar len Ic maxlen Ar mlen Ic source-as Ar asn +.Oo Ic expires Ar seconds Oc ... Ic } .Xc The .Ic roa-set @@ -499,6 +500,8 @@ Payloads (VRP). Each received prefix is checked against the .Ic roa-set , and the Origin Validation State (OVS) is set. +.Ic expires +can be set to the seconds since Epoch until when this VRP is valid. .Bd -literal -offset indent roa-set { 192.0.2.0/24 maxlen 24 source-as 64511 203.0.113.0/24 source-as 64496 } diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index e897e077c36..435198ad2ea 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.417 2021/08/09 08:15:34 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.418 2021/09/01 12:39:52 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -257,6 +257,7 @@ struct roa { uint8_t maxlen; uint8_t pad; uint32_t asnum; + time_t expires; union { struct in_addr inet; struct in6_addr inet6; diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 37f1862c922..46a5c4d9670 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.418 2021/08/09 08:15:34 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.419 2021/09/01 12:39:52 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -102,6 +102,7 @@ static struct filter_head *peerfilter_l; static struct filter_head *groupfilter_l; static struct filter_rule *curpeer_filter[2]; static struct filter_rule *curgroup_filter[2]; +static int noexpires; struct filter_rib_l { struct filter_rib_l *next; @@ -163,7 +164,8 @@ static int new_as_set(char *); static void add_as_set(u_int32_t); static void done_as_set(void); static struct prefixset *new_prefix_set(char *, int); -static void add_roa_set(struct prefixset_item *, u_int32_t, u_int8_t); +static void add_roa_set(struct prefixset_item *, u_int32_t, u_int8_t, + time_t); static struct rtr_config *get_rtr(struct bgpd_addr *); static int insert_rtr(struct rtr_config *); @@ -216,7 +218,7 @@ typedef struct { %token CONNECTED STATIC %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE %token PREFIX PREFIXLEN PREFIXSET -%token ROASET ORIGINSET OVS +%token ROASET ORIGINSET OVS EXPIRES %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 @@ -229,7 +231,7 @@ typedef struct { %token NUMBER %type asnumber as4number as4number_any optnumber %type espah family safi restart origincode nettype -%type yesno inout restricted validity +%type yesno inout restricted validity expires %type string %type address %type prefix addrspec @@ -505,8 +507,10 @@ prefixset_item : prefix prefixlenop { roa_set : ROASET '{' optnl { curroatree = &conf->roa; + noexpires = 0; } roa_set_l optnl '}' { curroatree = NULL; + noexpires = 1; } | ROASET '{' optnl '}' /* nothing */ ; @@ -517,6 +521,7 @@ origin_set : ORIGINSET STRING '{' optnl { YYERROR; } curroatree = &curoset->roaitems; + noexpires = 1; free($2); } roa_set_l optnl '}' { SIMPLEQ_INSERT_TAIL(&conf->originsets, curoset, entry); @@ -535,24 +540,35 @@ origin_set : ORIGINSET STRING '{' optnl { } ; -roa_set_l : prefixset_item SOURCEAS as4number_any { +expires : /* empty */ { + $$ = 0; + } + | EXPIRES NUMBER { + if (noexpires) { + yyerror("syntax error, expires not allowed"); + YYERROR; + } + $$ = $2; + } + +roa_set_l : prefixset_item SOURCEAS as4number_any expires { if ($1->p.len_min != $1->p.len) { yyerror("unsupported prefixlen operation in " "roa-set"); free($1); YYERROR; } - add_roa_set($1, $3, $1->p.len_max); + add_roa_set($1, $3, $1->p.len_max, $4); free($1); } - | roa_set_l comma prefixset_item SOURCEAS as4number_any { + | roa_set_l comma prefixset_item SOURCEAS as4number_any expires { if ($3->p.len_min != $3->p.len) { yyerror("unsupported prefixlen operation in " "roa-set"); free($3); YYERROR; } - add_roa_set($3, $5, $3->p.len_max); + add_roa_set($3, $5, $3->p.len_max, $6); free($3); } ; @@ -2916,6 +2932,7 @@ lookup(char *s) { "enhanced", ENHANCED }, { "esp", ESP}, { "evaluate", EVALUATE}, + { "expires", EXPIRES}, { "export", EXPORT}, { "export-target", EXPORTTRGT}, { "ext-community", EXTCOMMUNITY}, @@ -4670,7 +4687,8 @@ new_prefix_set(char *name, int is_roa) } static void -add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max) +add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max, + time_t expires) { struct roa *roa, *r; @@ -4681,6 +4699,7 @@ add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max) roa->prefixlen = npsi->p.len; roa->maxlen = max; roa->asnum = as; + roa->expires = expires; switch (roa->aid) { case AID_INET: roa->prefix.inet = npsi->p.addr.v4; @@ -4693,9 +4712,12 @@ add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max) } r = RB_INSERT(roa_tree, curroatree, roa); - if (r != NULL) + if (r != NULL) { /* just ignore duplicates */ + if (r->expires != 0 && expires != 0 && expires > r->expires) + r->expires = expires; free(roa); + } } static struct rtr_config * diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 2940b27fc11..4981840a863 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.147 2021/03/02 09:45:07 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.148 2021/09/01 12:39:52 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -576,6 +576,8 @@ print_roa(struct roa_tree *r) if (roa->prefixlen != roa->maxlen) printf(" maxlen %u", roa->maxlen); printf(" source-as %u", roa->asnum); + if (roa->expires != 0) + printf(" expires %lld", (long long)roa->expires); } printf("\n}\n\n"); } diff --git a/usr.sbin/bgpd/rtr.c b/usr.sbin/bgpd/rtr.c index 7ed317a4422..11f2741c532 100644 --- a/usr.sbin/bgpd/rtr.c +++ b/usr.sbin/bgpd/rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtr.c,v 1.3 2021/05/11 12:09:19 claudio Exp $ */ +/* $OpenBSD: rtr.c,v 1.4 2021/09/01 12:39:52 claudio Exp $ */ /* * Copyright (c) 2020 Claudio Jeker @@ -38,6 +38,7 @@ volatile sig_atomic_t rtr_quit; static struct imsgbuf *ibuf_main; static struct imsgbuf *ibuf_rde; static struct bgpd_config *conf, *nconf; +static struct timer_head expire_timer; static void rtr_sighdlr(int sig) @@ -54,6 +55,31 @@ rtr_sighdlr(int sig) #define PFD_PIPE_RDE 1 #define PFD_PIPE_COUNT 2 +#define EXPIRE_TIMEOUT 300 + +/* + * Every EXPIRE_TIMEOUT seconds traverse the static roa-set table and expire + * all elements where the expires timestamp is smaller or equal to now. + * If any change is done recalculate the RTR table. + */ +static unsigned int +rtr_expire_roas(time_t now) +{ + struct roa *roa, *nr; + unsigned int recalc = 0; + + RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nr) { + if (roa->expires != 0 && roa->expires <= now) { + recalc++; + RB_REMOVE(roa_tree, &conf->roa, roa); + free(roa); + } + } + if (recalc != 0) + log_warnx("%u roa-set entries expired", recalc); + return recalc; +} + void roa_insert(struct roa_tree *rt, struct roa *in) { @@ -113,6 +139,9 @@ rtr_main(int debug, int verbose) conf = new_config(); log_info("rtr engine ready"); + TAILQ_INIT(&expire_timer); + timer_set(&expire_timer, Timer_Rtr_Expire, EXPIRE_TIMEOUT); + while (rtr_quit == 0) { i = rtr_count(); if (pfd_elms < PFD_PIPE_COUNT + i) { @@ -123,7 +152,12 @@ rtr_main(int debug, int verbose) pfd = newp; pfd_elms = PFD_PIPE_COUNT + i; } - timeout = 240; /* loop every 240s at least */ + + /* run the expire timeout every EXPIRE_TIMEOUT seconds */ + timeout = timer_nextduein(&expire_timer, getmonotime()); + if (timeout == -1) + fatalx("roa-set expire timer no longer runnning"); + bzero(pfd, sizeof(struct pollfd) * pfd_elms); set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main); @@ -153,6 +187,13 @@ rtr_main(int debug, int verbose) i = PFD_PIPE_COUNT; rtr_check_events(pfd + i, pfd_elms - i); + + if (timer_nextisdue(&expire_timer, getmonotime()) != NULL) { + timer_set(&expire_timer, Timer_Rtr_Expire, + EXPIRE_TIMEOUT); + if (rtr_expire_roas(time(NULL)) != 0) + rtr_recalc(); + } } rtr_shutdown(); @@ -257,6 +298,7 @@ rtr_dispatch_imsg_parent(struct imsgbuf *ibuf) RB_ROOT(&nconf->roa) = NULL; /* finally merge the rtr session */ rtr_config_merge(); + rtr_expire_roas(time(NULL)); rtr_recalc(); log_info("RTR engine reconfigured"); imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0, -- 2.20.1