On Fri, Sep 21, 2018 at 05:29:24PM +0200, Claudio Jeker wrote:
> This diff adds the rest needed to do ROA validation.
> 
> It does:
> - add the filter logic for roa validation check
>    deny from any roa-set RPKI invalid
>    match from any roa-set RPKI valid set community local-as:42
> - makes the RDE do the roa validation check whenever a prefix is added to
>   the RIB (both via UPDATE or via network statement)
> - adds some magic for reloads (currently a big hammer that needs to be
>   optimized but lets start easy)
> - various bug fixes
> - introduces a new funciton aspath_origin() to get the origin AS from an
>   AS path. This info may later be used for source-as checks as well but
>   they currently behave a bit different when it comes to pathes not ending
>   with a AS SEQUENCE segement.
> 
> I currently use the RIPE RPKI validator to grab a JSON file (e.g.
> http://localcert.ripe.net:8088/export.json) and feed that to this perl
> script to convert it into bgpd syntax:
> 
> #!/usr/bin/perl
> use strict;
> use warnings;
> use JSON::PP;
> my $json = do { local $/; <> };
> my $roa = decode_json $json;
> print "roa-set RPKI {\n";
> foreach (@{$roa->{'roas'}}) {
>         my $as = substr $_->{'asn'}, 2;
>         print "\t$_->{'prefix'} maxlen $_->{'maxLength'} source-as $as\n";
> }
> print "}\n";
> 
> With that configs like this work:
> include "/etc/bgpd/rpki.conf"
> 
> deny from any roa-set RPKI invalid
> match from any roa-set RPKI valid set community local-as:42
> match from any roa-set RPKI unknown set community local-as:43
> 
> In my setup I get these numbers:
>     5895 invalid prefixes
>     67478 valid prefixes
>     638299 unknown prefixes
> This is from a single IPv4 only full feed.
> 
> Disclaimer: works for me but I did not test it thoroughly especially no
> comparison was done to other implementations. Still wanted to share it
> now so other people can help.

Updated diff, fixes an issue with IPv6 sessions which was found by benno@

-- 
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.201
diff -u -p -r1.201 bgpd.c
--- bgpd.c      21 Sep 2018 04:55:27 -0000      1.201
+++ bgpd.c      21 Sep 2018 13:56:16 -0000
@@ -529,15 +529,15 @@ reconfigure(char *conffile, struct bgpd_
                    ps->name, sizeof(ps->name)) == -1)
                        return (-1);
                RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) {
-                       u_int32_t *as;
+                       struct roa_set *rs;
                        size_t i, l, n;
                        RB_REMOVE(prefixset_tree, &ps->psitems, psi);
-                       as = set_get(psi->set, &n);
+                       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_AS_SET_ITEMS,
-                                   0, 0, -1, as + i, l) == -1)
+                                   0, 0, -1, rs + i, l * sizeof(*rs)) == -1)
                                        return -1;
                        }
                        if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSETITEM, 0,
@@ -569,7 +569,7 @@ reconfigure(char *conffile, struct bgpd_
                for (i = 0; i < n; i += l) {
                        l = (n - i > 1024 ? 1024 : n - i);
                        if (imsg_compose(ibuf_rde, IMSG_RECONF_AS_SET_ITEMS,
-                           0, 0, -1, as + i, l) == -1)
+                           0, 0, -1, as + i, l * sizeof(*as)) == -1)
                                return -1;
                }
 
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.344
diff -u -p -r1.344 bgpd.h
--- bgpd.h      21 Sep 2018 04:55:27 -0000      1.344
+++ bgpd.h      21 Sep 2018 12:20:16 -0000
@@ -695,6 +695,12 @@ struct filter_prefixset {
        struct rde_prefixset    *ps;
 };
 
+struct filter_roaset {
+       u_int32_t                validity;
+       char                     name[SET_NAME_LEN];
+       struct rde_prefixset    *ps;
+};
+
 struct filter_community {
        int             as;
        int             type;
@@ -886,6 +892,7 @@ struct filter_match {
        struct filter_largecommunity    large_community;
        struct filter_extcommunity      ext_community;
        struct filter_prefixset         prefixset;
+       struct filter_roaset            roaset;
 };
 
 union filter_rule_ptr {
@@ -1015,6 +1022,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;
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.359
diff -u -p -r1.359 parse.y
--- parse.y     21 Sep 2018 08:17:15 -0000      1.359
+++ parse.y     21 Sep 2018 11:18:44 -0000
@@ -100,6 +100,7 @@ static struct filter_head   *groupfilter_l
 static struct filter_rule      *curpeer_filter[2];
 static struct filter_rule      *curgroup_filter[2];
 static u_int32_t                id;
+static int                      roacount;
 
 struct filter_rib_l {
        struct filter_rib_l     *next;
@@ -213,7 +214,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 INVALID UNKNOWN VALID
 %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 +228,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
@@ -493,6 +495,12 @@ prefixset_item     : prefix prefixlenop                    
{
                ;
 
 roa_set                : ROASET STRING '{' optnl               {
+                       if (roacount++ >= MAX_ROASETS) {
+                               yyerror("too many roa-sets defined: max %d",
+                                   MAX_ROASETS);
+                               free($2);
+                               YYERROR;
+                       }
                        if ((curroaset = new_prefix_set($2, 1)) == NULL) {
                                free($2);
                                YYERROR;
@@ -503,6 +511,12 @@ roa_set            : ROASET STRING '{' optnl               
{
                        curroaset = NULL;
                }
                | ROASET STRING '{' optnl '}'           {
+                       if (roacount++ >= MAX_ROASETS) {
+                               yyerror("too many roa-sets defined: max %d",
+                                   MAX_ROASETS);
+                               free($2);
+                               YYERROR;
+                       }
                        if ((curroaset = new_prefix_set($2, 1)) == NULL) {
                                free($2);
                                YYERROR;
@@ -2234,6 +2248,28 @@ filter_elm       : filter_prefix_h       {
                        fmopts.m.prefixset.ps = NULL;
                        free($2);
                }
+               | ROASET STRING validity                {
+                       struct prefixset *ps;
+                       if (fmopts.m.roaset.name[0] != '\0') {
+                               yyerror("roa-set filter already specified");
+                               free($2);
+                               YYERROR;
+                       }
+                       if ((ps = find_prefixset($2, conf->roasets)) == NULL) {
+                               yyerror("roa-set not defined");
+                               free($2);
+                               YYERROR;
+                       }
+                       if (strlcpy(fmopts.m.roaset.name, $2,
+                           sizeof(fmopts.m.roaset.name)) >=
+                           sizeof(fmopts.m.roaset.name)) {
+                               yyerror("roa-set name too long");
+                               free($2);
+                               YYERROR;
+                       }
+                       fmopts.m.roaset.validity = $3;
+                       free($2);
+               }
                ;
 
 prefixlenop    : /* empty */                   { bzero(&$$, sizeof($$)); }
@@ -2649,7 +2685,7 @@ filter_set_opt    : LOCALPREF NUMBER              {
                }
                ;
 
-origincode     : string {
+origincode     : STRING        {
                        if (!strcmp($1, "egp"))
                                $$ = ORIGIN_EGP;
                        else if (!strcmp($1, "igp"))
@@ -2664,6 +2700,21 @@ origincode       : string {
                        free($1);
                };
 
+validity       : STRING        {
+                       if (!strcmp($1, "unknown"))
+                               $$ = ROA_UNKNOWN;
+                       else if (!strcmp($1, "invalid"))
+                               $$ = ROA_INVALID;
+                       else if (!strcmp($1, "valid"))
+                               $$ = ROA_VALID;
+                       else {
+                               yyerror("unknown origin \"%s\"", $1);
+                               free($1);
+                               YYERROR;
+                       }
+                       free($1);
+               };
+
 optnl          : /* empty */
                | '\n' optnl
                ;
@@ -3220,6 +3271,7 @@ parse_config(char *filename, struct bgpd
        curpeer = NULL;
        curgroup = NULL;
        id = 1;
+       roacount = 0;
 
        netconf = &conf->networks;
 
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.122
diff -u -p -r1.122 printconf.c
--- printconf.c 21 Sep 2018 04:55:27 -0000      1.122
+++ printconf.c 21 Sep 2018 04:55:53 -0000
@@ -86,11 +86,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);
@@ -751,6 +751,23 @@ print_rule(struct peer *peer_l, struct f
                printf("prefix-set \"%s\" ", r->match.prefixset.name);
        if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER)
                printf("or-longer ");
+
+       if (r->match.roaset.name[0] != '\0') {
+               printf("roa-set \"%s\" ", r->match.roaset.name);
+               switch (r->match.roaset.validity) {
+               case ROA_VALID:
+                       printf("valid ");
+                       break;
+               case ROA_INVALID:
+                       printf("invalid ");
+                       break;
+               case ROA_UNKNOWN:
+                       printf("unknown ");
+                       break;
+               default:
+                       printf("??? %d ??? ", r->match.roaset.validity);
+               }
+       }
 
        if (r->match.nexthop.flags) {
                if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.426
diff -u -p -r1.426 rde.c
--- rde.c       21 Sep 2018 04:55:27 -0000      1.426
+++ rde.c       21 Sep 2018 20:18:17 -0000
@@ -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_int32_t       rde_set_roa_validity(struct rde_prefixset_head *,
+                    struct bgpd_addr *, u_int8_t, u_int32_t);
 
 void            peer_init(u_int32_t);
 void            peer_shutdown(void);
@@ -500,6 +502,7 @@ rde_dispatch_imsg_session(struct imsgbuf
                        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:
@@ -692,6 +695,7 @@ rde_dispatch_imsg_parent(struct imsgbuf 
        static struct as_set    *last_as_set;
        static struct set_table *last_set;
        static struct rdomain   *rd;
+       static int               roa_index;
        struct imsg              imsg;
        struct mrt               xmrt;
        struct rde_rib           rn;
@@ -781,6 +785,7 @@ rde_dispatch_imsg_parent(struct imsgbuf 
                        if (roasets_tmp == NULL)
                                fatal(NULL);
                        SIMPLEQ_INIT(roasets_tmp);
+                       roa_index = 0;
                        as_sets_tmp = calloc(1,
                            sizeof(struct as_set_head));
                        if (as_sets_tmp == NULL)
@@ -846,6 +851,14 @@ rde_dispatch_imsg_parent(struct imsgbuf 
                                        log_warnx("%s: no prefixset for %s",
                                            __func__, r->match.prefixset.name);
                        }
+                       if (r->match.roaset.name[0] != '\0') {
+                               r->match.roaset.ps =
+                                   rde_find_prefixset(r->match.roaset.name,
+                                       roasets_tmp);
+                               if (r->match.roaset.ps == NULL)
+                                       log_warnx("%s: no roa-set for %s",
+                                           __func__, r->match.roaset.name);
+                       }
                        if (r->match.as.flags & AS_FLAG_AS_SET_NAME) {
                                struct as_set * aset;
 
@@ -894,17 +907,21 @@ rde_dispatch_imsg_parent(struct imsgbuf 
                        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;
-                               last_set = set_new(1, sizeof(struct roa_set));
-                               if (last_set == NULL)
-                                       fatal(NULL);
-                       } else
+                               ps->roa_index = roa_index++;
+                       } else {
                                SIMPLEQ_INSERT_TAIL(prefixsets_tmp, ps, entry);
+                       }
+                       last_set = NULL;
                        last_prefixset = ps;
                        break;
                case IMSG_RECONF_ROA_AS_SET_ITEMS:
                        nmemb = imsg.hdr.len - IMSG_HEADER_SIZE;
                        nmemb /= sizeof(struct roa_set);
+                       if (last_set == NULL) {
+                               last_set = set_new(1, sizeof(struct roa_set));
+                               if (last_set == NULL)
+                                       fatal(NULL);
+                       }
                        if (set_add(last_set, imsg.data, nmemb) != 0)
                                fatal(NULL);
                        break;
@@ -915,10 +932,11 @@ rde_dispatch_imsg_parent(struct imsgbuf 
                        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);
+                               last_set = NULL;
                        } else {
                                rv = trie_add(&last_prefixset->th,
                                    &psi.p.addr, psi.p.len,
@@ -1122,6 +1140,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);
        }
 
@@ -1380,12 +1402,17 @@ rde_update_update(struct rde_peer *peer,
        struct filterstate       state;
        struct prefix           *p;
        enum filter_actions      action;
+       u_int32_t                vstate;
        u_int16_t                i;
        const char              *wmsg = "filtered, withdraw";
 
        peer->prefix_rcvd_update++;
+       vstate = rde_set_roa_validity(conf->rde_roasets, 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 */
@@ -1416,7 +1443,7 @@ rde_update_update(struct rde_peer *peer,
                            &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,
@@ -3077,25 +3104,37 @@ rde_softreconfig_in(struct rib_entry *re
        struct rde_peer         *peer;
        struct rde_aspath       *asp;
        enum filter_actions      action;
-       struct bgpd_addr         addr;
+       struct bgpd_addr         prefix;
+       int                      force_reval;
+       u_int32_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) {
+               force_reval = 0;
                asp = prefix_aspath(p);
                peer = prefix_peer(p);
 
+               /* ROA validation state update, BIG HAMMER */
+               vstate = rde_set_roa_validity(conf->rde_roasets, &prefix,
+                   pt->prefixlen, asp->source_as);
+               if (vstate != p->validation_state) {
+                       force_reval = 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_reval)
                                continue;
-                       if (!rib_valid(i))
-                               break;
 
                        rde_filterstate_prep(&state, asp, prefix_nexthop(p),
                            prefix_nhflags(p));
@@ -3103,11 +3142,11 @@ rde_softreconfig_in(struct rib_entry *re
 
                        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);
                        }
 
@@ -3616,6 +3655,7 @@ network_add(struct network_config *nc, i
        struct rde_aspath       *asp;
        struct filter_set_head  *vpnset = NULL;
        in_addr_t                prefix4;
+       u_int32_t                vstate;
        u_int16_t                i;
 
        if (nc->rtableid != conf->default_tableid) {
@@ -3660,6 +3700,7 @@ network_add(struct network_config *nc, i
                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 */
@@ -3672,8 +3713,10 @@ network_add(struct network_config *nc, i
                rde_apply_set(vpnset, &state, nc->prefix.aid, peerself,
                    peerself);
 
+       vstate = rde_set_roa_validity(conf->rde_roasets, &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))
@@ -3682,7 +3725,7 @@ network_add(struct network_config *nc, i
                    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);
@@ -3891,4 +3934,20 @@ rde_mark_prefixsets_dirty(struct rde_pre
                                new->dirty = 1;
                }
        }
+}
+
+u_int32_t
+rde_set_roa_validity(struct rde_prefixset_head *p, struct bgpd_addr *prefix,
+    u_int8_t plen, u_int32_t as)
+{
+       struct rde_prefixset *ps;
+       u_int32_t validity = 0;
+       int r;
+
+       SIMPLEQ_FOREACH(ps, p, entry) {
+               r = trie_roa_check(&ps->th, prefix, plen, as);
+               validity |= (r << (2 * ps->roa_index)) & ROA_MASK;
+       }
+
+       return validity;
 }
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.193
diff -u -p -r1.193 rde.h
--- rde.h       20 Sep 2018 11:45:59 -0000      1.193
+++ rde.h       21 Sep 2018 12:13:53 -0000
@@ -36,11 +36,12 @@ enum peer_state {
        PEER_ERR        /* error occurred going to PEER_DOWN state */
 };
 
-enum roa_state {
-       ROA_UNKNOWN,
-       ROA_INVALID,
-       ROA_VALID
-};
+#define        ROA_UNKNOWN     0x0
+#define        ROA_INVALID     0x1
+#define        ROA_VALID       0x2
+#define        ROA_MASK        0x3
+
+#define MAX_ROASETS    16
 
 /*
  * How do we identify peers between the session handler and the rde?
@@ -191,6 +192,7 @@ struct rde_aspath {
        struct aspath                   *aspath;
        u_int64_t                        hash;
        u_int32_t                        flags;         /* internally used */
+       u_int32_t                        source_as;     /* chached source_as */
        u_int32_t                        med;           /* multi exit disc */
        u_int32_t                        lpref;         /* local pref */
        u_int32_t                        weight;        /* low prio lpref */
@@ -311,6 +313,7 @@ struct prefix {
        struct rde_peer                 *peer;
        struct nexthop                  *nexthop;       /* may be NULL */
        time_t                           lastchange;
+       u_int32_t                        validation_state;
        u_int8_t                         nhflags;
 };
 
@@ -339,7 +342,7 @@ struct rde_prefixset {
        struct trie_head                th;
        SIMPLEQ_ENTRY(rde_prefixset)    entry;
        int                             dirty;
-       int                             roa;
+       int                             roa_index;
 };
 SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset);
 
@@ -396,6 +399,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 *);
@@ -501,7 +505,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_int32_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);
@@ -549,6 +553,12 @@ static inline u_int8_t
 prefix_nhflags(struct prefix *p)
 {
        return (p->nhflags);
+}
+
+static inline u_int32_t
+prefix_vstate(struct prefix *p, int index)
+{
+       return (p->validation_state >> (2 * index)) & ROA_MASK;
 }
 
 void            nexthop_init(u_int32_t);
Index: rde_attr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v
retrieving revision 1.110
diff -u -p -r1.110 rde_attr.c
--- rde_attr.c  20 Sep 2018 11:06:04 -0000      1.110
+++ rde_attr.c  21 Sep 2018 12:26:46 -0000
@@ -586,7 +586,7 @@ aspath_deflate(u_char *data, u_int16_t *
                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
                        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
                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, 
                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
                }
 
                if (seg_size > len)
-                       fatalx("aspath_loopfree: would overflow");
+                       fatalx("%s: would overflow", __func__);
        }
        return (1);
 }
Index: rde_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v
retrieving revision 1.108
diff -u -p -r1.108 rde_filter.c
--- rde_filter.c        20 Sep 2018 11:45:59 -0000      1.108
+++ rde_filter.c        20 Sep 2018 13:42:13 -0000
@@ -360,6 +360,12 @@ rde_filter_match(struct filter_rule *f, 
        if (f->peer.ibgp && peer->conf.ebgp)
                return (0);
 
+       if (f->match.roaset.ps != NULL) {
+               struct rde_prefixset *ps = f->match.roaset.ps;
+               if (prefix_vstate(p, ps->roa_index) != f->match.roaset.validity)
+                       return (0);
+       }
+
        if (asp != NULL && f->match.as.type != AS_NONE) {
                if (aspath_match(asp->aspath->data, asp->aspath->len,
                    &f->match.as, peer->conf.remote_as) == 0)
Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.178
diff -u -p -r1.178 rde_rib.c
--- rde_rib.c   20 Sep 2018 11:06:04 -0000      1.178
+++ rde_rib.c   21 Sep 2018 12:02:27 -0000
@@ -48,11 +48,11 @@ struct rib_entry *rib_restart(struct rib
 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_int32_t);
+static int      prefix_move(struct prefix *, struct rde_peer *,
+                   struct rde_aspath *, struct filterstate *, u_int32_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_int32_t vstate)
 {
        struct rde_aspath       *asp, *nasp = &state->aspath;
        struct prefix           *p;
@@ -438,6 +438,7 @@ path_update(struct rib *rib, struct rde_
                    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_
 
        /* 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, struc
                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 
        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_int32_t);
 static void             prefix_unlink(struct prefix *);
 
 /*
@@ -760,9 +766,10 @@ prefix_get(struct rib *rib, struct rde_p
 /*
  * 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_int32_t vstate)
 {
        struct prefix           *p;
        struct rib_entry        *re;
@@ -774,16 +781,17 @@ prefix_add(struct bgpd_addr *prefix, int
        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
 /*
  * 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_int32_t vstate)
 {
        struct prefix           *np;
        struct rde_aspath       *oasp;
@@ -809,6 +817,7 @@ prefix_move(struct prefix *p, struct rde
        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 *pe
  */
 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_int32_t vstate)
 {
        TAILQ_INSERT_HEAD(&asp->prefixes, pref, path_l);
 
@@ -1068,6 +1077,7 @@ prefix_link(struct prefix *pref, struct 
        pref->re = re;
        pref->lastchange = time(NULL);
        pref->nhflags = state->nhflags;
+       pref->validation_state = vstate;
 
        /* make route decision */
        prefix_evaluate(pref, re);
Index: rde_trie.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_trie.c,v
retrieving revision 1.7
diff -u -p -r1.7 rde_trie.c
--- rde_trie.c  20 Sep 2018 11:45:59 -0000      1.7
+++ rde_trie.c  21 Sep 2018 15:08:21 -0000
@@ -565,11 +565,12 @@ trie_roa_check_v4(struct trie_head *th, 
                         */
                        validity = ROA_INVALID;
 
-                       /* Treat AS 0 as NONE which can never be matched */
-                       if (as != 0) {
-                               rs = set_match(n->set, as);
-                               if (rs && plen <= rs->maxlen)
+                       /* 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;
+                               }
                        }
                }
 
@@ -612,8 +613,8 @@ trie_roa_check_v6(struct trie_head *th, 
                         */
                        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;
@@ -634,9 +635,9 @@ trie_roa_check_v6(struct trie_head *th, 
 /*
  * 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_UNKNOWN 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,
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.39
diff -u -p -r1.39 util.c
--- util.c      20 Sep 2018 11:45:59 -0000      1.39
+++ util.c      21 Sep 2018 12:17:52 -0000
@@ -390,7 +390,7 @@ aspath_match(void *data, u_int16_t len, 
                         * AS of a previous non AS_SET segment should be used.
                         * Because of that simply skip AS_SET segments.
                         */
-                       if (seg[0] != AS_SET)
+                       if (seg[0] == AS_SEQUENCE)
                                as = aspath_extract(seg, seg_len - 1);
                        /* not yet in the final segment */
                        if (!final)

Reply via email to