Claudio Jeker([email protected]) on 2018.09.27 10:57:12 +0200:
> On Thu, Sep 27, 2018 at 09:39:36AM +0200, Claudio Jeker wrote:
> > 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.
> >
>
> And the moment I sent this benno@ reported that I forgot to fixup 'set
> ext-community ovs' in parse.y since ovs is now a keyword. Adjusted patch
> below.
I'm still unsure about the origin-sets. However i dont think they hurt: even
if they are in 6.4, if we later want to change things in this regard, we
still can do that.
ok benno@
> 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 08:46:26 -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($$)); }
> @@ -2641,6 +2696,22 @@ filter_set_opt : LOCALPREF NUMBER {
> free($3);
> free($4);
> }
> + | EXTCOMMUNITY delete OVS STRING {
> + if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
> + fatal(NULL);
> + if ($2)
> + $$->type = ACTION_DEL_EXT_COMMUNITY;
> + else
> + $$->type = ACTION_SET_EXT_COMMUNITY;
> +
> + if (parseextcommunity(&$$->action.ext_community,
> + "ovs", $4) == -1) {
> + free($4);
> + free($$);
> + YYERROR;
> + }
> + free($4);
> + }
> | ORIGIN origincode {
> if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
> fatal(NULL);
> @@ -2649,7 +2720,7 @@ filter_set_opt : LOCALPREF NUMBER {
> }
> ;
>
> -origincode : string {
> +origincode : STRING {
> if (!strcmp($1, "egp"))
> $$ = ORIGIN_EGP;
> else if (!strcmp($1, "igp"))
> @@ -2664,6 +2735,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 +2878,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 +4369,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 +4403,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 08:44:46 -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 08:44:14 -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)
> @@ -408,8 +413,7 @@ rde_filter_match(struct filter_rule *f,
> if (community_ext_match(asp, &f->match.ext_community,
> peer->conf.remote_as) == 0)
> return (0);
> - if (asp != NULL && f->match.large_community.as !=
> - COMMUNITY_UNSET) {
> + if (asp != NULL && f->match.large_community.as != COMMUNITY_UNSET) {
> switch (f->match.large_community.as) {
> case COMMUNITY_ERROR:
> fatalx("rde_filter_match bad community string");
> @@ -485,6 +489,18 @@ rde_filter_match(struct filter_rule *f,
> }
> }
>
> + /* origin-set lookups match only on ROA_VALID */
> + if (asp != NULL && f->match.originset.ps != NULL) {
> + struct bgpd_addr addr, *prefix = &addr;
> + u_int8_t plen;
> +
> + pt_getaddr(p->re->prefix, prefix);
> + plen = p->re->prefix->prefixlen;
> + if (trie_roa_check(&f->match.originset.ps->th, prefix, plen,
> + asp->source_as) != ROA_VALID)
> + return (0);
> + }
> +
> /*
> * prefixset and prefix filter rules are mutual exclusive
> */
> @@ -578,7 +594,7 @@ rde_filter_equal(struct filter_head *a,
> struct rde_peer *peer)
> {
> struct filter_rule *fa, *fb;
> - struct rde_prefixset *psa, *psb;
> + struct rde_prefixset *psa, *psb, *osa, *osb;
> struct as_set *asa, *asb;
> int r;
>
> @@ -609,26 +625,35 @@ rde_filter_equal(struct filter_head *a,
> /* 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;
> }
> }
>
>