On Wed, Sep 26, 2018 at 06:24:36PM +0200, Claudio Jeker wrote:
> On Tue, Sep 25, 2018 at 12:23:48PM +0200, Claudio Jeker wrote:
> > On Sat, Sep 22, 2018 at 09:48:24PM +0000, Job Snijders wrote:
> > > Hi claudio,
> > > 
> > > Seems we are getting very close. Some suggestions to simplify the
> > > experience for the end user.
> > > 
> > > Let's start with supporting just one (unnamed) roa-set, so far I've
> > > really not come across a use case where multiple ROA tables are useful.
> > > I say this having implemented origin validation on both ISPs and IXPs.
> > > 
> > > The semantics of the Origin Validation procedure that apply to
> > > "Validated ROA Payloads" are not compatible with how the ARIN WHOIS
> > > OriginAS semantics, so roa-set will never be used for ARIN WHOIS data.
> > 
> > Please explain further, because honestly both the ruleset that
> > arouteserver generates for ARIN WHOIS OriginAS and ROA are doing
> > the same. They connect a source-as with a prefix. This is what a
> > roa-set is giving you. It implements a way to quickly lookup a 2 key
> > element (AS + prefix). Depending on how this table is used it can be used
> > for both cases. This is the technical view based on looking at rulesets and
> > thinking on how to write them in a way that is fast and understandable.
> > I understand that from an administration point of view RPKI ROA is much
> > stronger and can therefor be used more strictly (e.g. with the simple rule
> > deny quick from ebgp roa-set RPKI invalid). The generic origin validatiation
> > as a supporting measure to IRR based prefixset filtering is only allowing
> > additional prefixes based on the roa-set (e.g. match from ebgp \
> > large-community $INTCOMM_ORIGIN_OK roa-set ARINDB valid \
> > set large-community $INTCOMM_PREF_OK). They are two different things but
> > can use the same filter building blocks.
> > Maybe naming it roa-set is wrong (since it is too much connected with
> > RPKI) but the lookup table is usable for more than just RPKI. This is why
> > I think supporting multiple tables is benefitial. I also agree that a
> > simplified user experience for RPKI is a good thing, it should be simple
> > to use.
> > 
> > > There currently is 1 RPKI, and therefor we should have just 1 roa-set.
> > 
> > I would fully agree here if ARIN would make their trust anchor freely
> > distributable. But yes in general only one RPKI table is needed.
> > My point is that roa-set is more generic than RPKI. It is not an RPKI only
> > thing. It can be used for more than that and we should support this as
> > well since it makes for much better and efficient configs.
> >  
> > > The advantage of having only one roa-set is that it does not need to be
> > > referenced in the policies.
> > > 
> > > I propose the patch is amended to accomodate the following:
> > > 
> > >   roa-set {
> > >           165.254.255.0/24 source-as 15562
> > >           193.0.0.0/21 source-as 3333
> > >   }
> > > 
> > >   deny from any ovs invalid
> > >   match from any ovs valid set community local-as:42
> > >   match from any ovs unknown set community local-as:43
> > > 
> > > I suggest the filter keyword 'ovs' (origin validation state) is
> > > introduced to allow easy matching. I think this looks better. If the
> > > roa-set is not defined or empty, all route announcements are 'unknown'.
> > 
> > If we want to use ovs then the naming scheme should be the same as for the
> > extended community. At least if we reuse this keyword it should work the
> > same. Also the side-effect is that the two configs look fairly similar
> > which can be good or bad:
> > match from any ovs valid set community local-as:42 
> > match from any ext-community ovs valid set community local-as:42 
> > 
> > I'm not against this, just wanted to bring it up.
> > 
> > Also I think unknown should be renamed not-found. I will do that since
> > not-found is what the RFC is using.
> > 
> > I will rework the diff considering the input.
> 
> Here we go. This changes the diff so there is only one roa-set like
> mentioned above:
> 
>       roa-set {
>               165.254.255.0/24 source-as 15562
>               193.0.0.0/21 source-as 3333
>       }
>  
>       deny from any ovs invalid
>       match from any ovs valid set community local-as:42
>       match from any ovs unknown set community local-as:43
> 
> Additionally I left the old sets around but used a bit different:
> 
>       origin-set FOO {
>               ...
>       }
> 
>       match from any community $ORIGIN_AS_OK origin-set FOO valid \
>               set community $ORIGIN_PREF_OK
> 
> I may even consider to drop the 'valid' in the above rule since that is
> the only check that kind of matters in this case.
> 

New version removing the validity argument from origin-set FOO filter
rules as mentioned above. Apart from that it should be the same.

-- 
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.202
diff -u -p -r1.202 bgpd.c
--- bgpd.c      25 Sep 2018 07:58:11 -0000      1.202
+++ bgpd.c      26 Sep 2018 10:34:53 -0000
@@ -506,15 +506,15 @@ reconfigure(char *conffile, struct bgpd_
                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_
                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,17 +536,41 @@ 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_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);
                }
                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 */
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.345
diff -u -p -r1.345 bgpd.h
--- bgpd.h      26 Sep 2018 15:48:01 -0000      1.345
+++ bgpd.h      27 Sep 2018 07:03:12 -0000
@@ -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_DONE,
        IMSG_UPDATE,
        IMSG_UPDATE_ERR,
@@ -695,6 +715,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;
@@ -886,6 +916,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 {
@@ -967,7 +999,6 @@ struct prefixset_item {
        RB_ENTRY(prefixset_item)        entry;
        struct set_table                *set;
 };
-RB_HEAD(prefixset_tree, prefixset_item);
 
 struct prefixset {
        int                              sflags;
@@ -1015,6 +1046,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;
@@ -1099,6 +1132,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 *);
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.76
diff -u -p -r1.76 config.c
--- config.c    21 Sep 2018 20:45:50 -0000      1.76
+++ config.c    26 Sep 2018 14:15:49 -0000
@@ -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, 
        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;
-
-       /* switch the roasets, first remove the old ones */
-       free_prefixsets(xconf->roasets);
-       xconf->roasets = conf->roasets;
-       conf->roasets = NULL;
+       free_prefixsets(&xconf->prefixsets);
+       SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets);
+
+       /* 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);
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     27 Sep 2018 07:02:48 -0000
@@ -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);
@@ -2169,6 +2182,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");
@@ -2198,9 +2226,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;
                        }
@@ -2222,7 +2250,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;
                        }
@@ -2231,9 +2259,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($$)); }
@@ -2649,7 +2704,7 @@ filter_set_opt    : LOCALPREF NUMBER              {
                }
                ;
 
-origincode     : string {
+origincode     : STRING        {
                        if (!strcmp($1, "egp"))
                                $$ = ORIGIN_EGP;
                        else if (!strcmp($1, "igp"))
@@ -2664,6 +2719,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
                ;
@@ -2792,7 +2862,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},
@@ -4281,12 +4353,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)  {
@@ -4315,7 +4387,7 @@ add_roa_set(struct prefixset_item *npsi,
        /* 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;
 
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 27 Sep 2018 07:04:49 -0000
@@ -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 *
 }
 
 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 f
        } 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, s
        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))
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.427
diff -u -p -r1.427 rde.c
--- rde.c       25 Sep 2018 08:08:38 -0000      1.427
+++ rde.c       26 Sep 2018 16:13:52 -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_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
                        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 
                            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 
                                        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 
                        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 
                        } 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 
                        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);
@@ -1126,6 +1139,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);
        }
 
@@ -1384,12 +1401,17 @@ rde_update_update(struct rde_peer *peer,
        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 */
@@ -1420,7 +1442,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,
@@ -2827,16 +2849,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;
 
@@ -2860,17 +2887,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;
 
@@ -3059,10 +3088,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;
 
@@ -3081,25 +3108,39 @@ 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_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));
@@ -3107,11 +3148,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);
                        }
 
@@ -3620,6 +3661,7 @@ network_add(struct network_config *nc, i
        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) {
@@ -3664,6 +3706,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 */
@@ -3676,8 +3719,10 @@ network_add(struct network_config *nc, i
                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))
@@ -3686,7 +3731,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);
@@ -3895,4 +3940,14 @@ rde_mark_prefixsets_dirty(struct rde_pre
                                new->dirty = 1;
                }
        }
+}
+
+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);
 }
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       26 Sep 2018 14:30:41 -0000
@@ -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?
@@ -191,6 +190,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 */
@@ -311,6 +311,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;
 };
 
@@ -325,24 +326,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 */
@@ -396,6 +379,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 +485,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);
@@ -549,6 +533,12 @@ static inline u_int8_t
 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);
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.111
diff -u -p -r1.111 rde_filter.c
--- rde_filter.c        26 Sep 2018 15:48:01 -0000      1.111
+++ rde_filter.c        27 Sep 2018 07:04:06 -0000
@@ -360,6 +360,11 @@ rde_filter_match(struct filter_rule *f, 
        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)
@@ -485,6 +490,18 @@ rde_filter_match(struct filter_rule *f, 
                }
        }
 
+       /* origin-set lookups match only on ROA_VALID */
+       if (state != 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,
+                   state->aspath.source_as) != ROA_VALID)
+                       return (0);
+       }
+
        /*
         * prefixset and prefix filter rules are mutual exclusive
         */
@@ -578,7 +595,7 @@ rde_filter_equal(struct filter_head *a, 
     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 +626,35 @@ rde_filter_equal(struct filter_head *a, 
                /* 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",
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   26 Sep 2018 14:20:41 -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_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_
                    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_int8_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_int8_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_int8_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_int8_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.8
diff -u -p -r1.8 rde_trie.c
--- rde_trie.c  26 Sep 2018 14:47:20 -0000      1.8
+++ rde_trie.c  26 Sep 2018 14:49:31 -0000
@@ -543,7 +543,7 @@ trie_roa_check_v4(struct trie_head *th, 
 {
        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, 
                         */
                        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 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, 
                         */
                        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, 
 /*
  * 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, str
        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;
        }
 }
 

Reply via email to