Implement roa-set data expiry. Every prefix in a roa-set can specify an
authorclaudio <claudio@openbsd.org>
Wed, 1 Sep 2021 12:39:52 +0000 (12:39 +0000)
committerclaudio <claudio@openbsd.org>
Wed, 1 Sep 2021 12:39:52 +0000 (12:39 +0000)
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
usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/parse.y
usr.sbin/bgpd/printconf.c
usr.sbin/bgpd/rtr.c

index 6ee7bc2..5fb003f 100644 (file)
@@ -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 <claudio@openbsd.org>
 .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 }
index e897e07..435198a 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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;
index 37f1862..46a5c4d 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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 <v.number>              NUMBER
 %type  <v.number>              asnumber as4number as4number_any optnumber
 %type  <v.number>              espah family safi restart origincode nettype
-%type  <v.number>              yesno inout restricted validity
+%type  <v.number>              yesno inout restricted validity expires
 %type  <v.string>              string
 %type  <v.addr>                address
 %type  <v.prefix>              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 *
index 2940b27..4981840 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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");
 }
index 7ed317a..11f2741 100644 (file)
@@ -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 <claudio@openbsd.org>
@@ -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,