bgpd ROA validation

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

bgpd ROA validation

Claudio Jeker
This diff adds the rest needed to do ROA validation.

It does:
- add the filter logic for roa validation check
   deny from any roa-set RPKI invalid
   match from any roa-set RPKI valid set community local-as:42
- makes the RDE do the roa validation check whenever a prefix is added to
  the RIB (both via UPDATE or via network statement)
- adds some magic for reloads (currently a big hammer that needs to be
  optimized but lets start easy)
- various bug fixes
- introduces a new funciton aspath_origin() to get the origin AS from an
  AS path. This info may later be used for source-as checks as well but
  they currently behave a bit different when it comes to pathes not ending
  with a AS SEQUENCE segement.

I currently use the RIPE RPKI validator to grab a JSON file (e.g.
http://localcert.ripe.net:8088/export.json) and feed that to this perl
script to convert it into bgpd syntax:

#!/usr/bin/perl
use strict;
use warnings;
use JSON::PP;
my $json = do { local $/; <> };
my $roa = decode_json $json;
print "roa-set RPKI {\n";
foreach (@{$roa->{'roas'}}) {
        my $as = substr $_->{'asn'}, 2;
        print "\t$_->{'prefix'} maxlen $_->{'maxLength'} source-as $as\n";
}
print "}\n";

With that configs like this work:
include "/etc/bgpd/rpki.conf"

deny from any roa-set RPKI invalid
match from any roa-set RPKI valid set community local-as:42
match from any roa-set RPKI unknown set community local-as:43

In my setup I get these numbers:
    5895 invalid prefixes
    67478 valid prefixes
    638299 unknown prefixes
This is from a single IPv4 only full feed.

Disclaimer: works for me but I did not test it thoroughly especially no
comparison was done to other implementations. Still wanted to share it
now so other people can help.
--
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.201
diff -u -p -r1.201 bgpd.c
--- bgpd.c 21 Sep 2018 04:55:27 -0000 1.201
+++ bgpd.c 21 Sep 2018 13:56:16 -0000
@@ -529,15 +529,15 @@ reconfigure(char *conffile, struct bgpd_
     ps->name, sizeof(ps->name)) == -1)
  return (-1);
  RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) {
- u_int32_t *as;
+ struct roa_set *rs;
  size_t i, l, n;
  RB_REMOVE(prefixset_tree, &ps->psitems, psi);
- as = set_get(psi->set, &n);
+ rs = set_get(psi->set, &n);
  for (i = 0; i < n; i += l) {
  l = (n - i > 1024 ? 1024 : n - i);
  if (imsg_compose(ibuf_rde,
     IMSG_RECONF_ROA_AS_SET_ITEMS,
-    0, 0, -1, as + i, l) == -1)
+    0, 0, -1, rs + i, l * sizeof(*rs)) == -1)
  return -1;
  }
  if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSETITEM, 0,
@@ -569,7 +569,7 @@ reconfigure(char *conffile, struct bgpd_
  for (i = 0; i < n; i += l) {
  l = (n - i > 1024 ? 1024 : n - i);
  if (imsg_compose(ibuf_rde, IMSG_RECONF_AS_SET_ITEMS,
-    0, 0, -1, as + i, l) == -1)
+    0, 0, -1, as + i, l * sizeof(*as)) == -1)
  return -1;
  }
 
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.344
diff -u -p -r1.344 bgpd.h
--- bgpd.h 21 Sep 2018 04:55:27 -0000 1.344
+++ bgpd.h 21 Sep 2018 12:20:16 -0000
@@ -695,6 +695,12 @@ struct filter_prefixset {
  struct rde_prefixset *ps;
 };
 
+struct filter_roaset {
+ u_int32_t validity;
+ char name[SET_NAME_LEN];
+ struct rde_prefixset *ps;
+};
+
 struct filter_community {
  int as;
  int type;
@@ -886,6 +892,7 @@ struct filter_match {
  struct filter_largecommunity large_community;
  struct filter_extcommunity ext_community;
  struct filter_prefixset prefixset;
+ struct filter_roaset roaset;
 };
 
 union filter_rule_ptr {
@@ -1015,6 +1022,8 @@ extern struct rib_names ribnames;
 
 /* 4-byte magic AS number */
 #define AS_TRANS 23456
+/* AS_NONE for origin validation */
+#define AS_NONE 0
 
 struct rde_memstats {
  int64_t path_cnt;
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.359
diff -u -p -r1.359 parse.y
--- parse.y 21 Sep 2018 08:17:15 -0000 1.359
+++ parse.y 21 Sep 2018 11:18:44 -0000
@@ -100,6 +100,7 @@ static struct filter_head *groupfilter_l
 static struct filter_rule *curpeer_filter[2];
 static struct filter_rule *curgroup_filter[2];
 static u_int32_t id;
+static int roacount;
 
 struct filter_rib_l {
  struct filter_rib_l *next;
@@ -213,7 +214,8 @@ typedef struct {
 %token FROM TO ANY
 %token CONNECTED STATIC
 %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE
-%token PREFIX PREFIXLEN PREFIXSET ROASET
+%token PREFIX PREFIXLEN PREFIXSET
+%token ROASET INVALID UNKNOWN VALID
 %token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ
 %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
 %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY
@@ -226,7 +228,7 @@ typedef struct {
 %token <v.number> NUMBER
 %type <v.number> asnumber as4number as4number_any optnumber
 %type <v.number> espah family restart origincode nettype
-%type <v.number> yesno inout restricted
+%type <v.number> yesno inout restricted validity
 %type <v.string> string
 %type <v.addr> address
 %type <v.prefix> prefix addrspec
@@ -493,6 +495,12 @@ prefixset_item : prefix prefixlenop {
  ;
 
 roa_set : ROASET STRING '{' optnl {
+ if (roacount++ >= MAX_ROASETS) {
+ yyerror("too many roa-sets defined: max %d",
+    MAX_ROASETS);
+ free($2);
+ YYERROR;
+ }
  if ((curroaset = new_prefix_set($2, 1)) == NULL) {
  free($2);
  YYERROR;
@@ -503,6 +511,12 @@ roa_set : ROASET STRING '{' optnl {
  curroaset = NULL;
  }
  | ROASET STRING '{' optnl '}' {
+ if (roacount++ >= MAX_ROASETS) {
+ yyerror("too many roa-sets defined: max %d",
+    MAX_ROASETS);
+ free($2);
+ YYERROR;
+ }
  if ((curroaset = new_prefix_set($2, 1)) == NULL) {
  free($2);
  YYERROR;
@@ -2234,6 +2248,28 @@ filter_elm : filter_prefix_h {
  fmopts.m.prefixset.ps = NULL;
  free($2);
  }
+ | ROASET STRING validity {
+ struct prefixset *ps;
+ if (fmopts.m.roaset.name[0] != '\0') {
+ yyerror("roa-set filter already specified");
+ free($2);
+ YYERROR;
+ }
+ if ((ps = find_prefixset($2, conf->roasets)) == NULL) {
+ yyerror("roa-set not defined");
+ free($2);
+ YYERROR;
+ }
+ if (strlcpy(fmopts.m.roaset.name, $2,
+    sizeof(fmopts.m.roaset.name)) >=
+    sizeof(fmopts.m.roaset.name)) {
+ yyerror("roa-set name too long");
+ free($2);
+ YYERROR;
+ }
+ fmopts.m.roaset.validity = $3;
+ free($2);
+ }
  ;
 
 prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); }
@@ -2649,7 +2685,7 @@ filter_set_opt : LOCALPREF NUMBER {
  }
  ;
 
-origincode : string {
+origincode : STRING {
  if (!strcmp($1, "egp"))
  $$ = ORIGIN_EGP;
  else if (!strcmp($1, "igp"))
@@ -2664,6 +2700,21 @@ origincode : string {
  free($1);
  };
 
+validity : STRING {
+ if (!strcmp($1, "unknown"))
+ $$ = ROA_UNKNOWN;
+ else if (!strcmp($1, "invalid"))
+ $$ = ROA_INVALID;
+ else if (!strcmp($1, "valid"))
+ $$ = ROA_VALID;
+ else {
+ yyerror("unknown origin \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ };
+
 optnl : /* empty */
  | '\n' optnl
  ;
@@ -3220,6 +3271,7 @@ parse_config(char *filename, struct bgpd
  curpeer = NULL;
  curgroup = NULL;
  id = 1;
+ roacount = 0;
 
  netconf = &conf->networks;
 
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.122
diff -u -p -r1.122 printconf.c
--- printconf.c 21 Sep 2018 04:55:27 -0000 1.122
+++ printconf.c 21 Sep 2018 04:55:53 -0000
@@ -86,11 +86,11 @@ print_prefix(struct filter_prefix *p)
  printf(" prefixlen %u >< %u ", p->len_min, p->len_max);
  break;
  case OP_RANGE:
- if (p->len_min == p->len_max)
+ if (p->len_min == p->len_max && p->len != p->len_min)
  printf(" prefixlen = %u", p->len_min);
  else if (p->len == p->len_min && p->len_max == max_len)
  printf(" or-longer");
- else if (p->len == p->len_min)
+ else if (p->len == p->len_min && p->len != p->len_max)
  printf(" maxlen %u", p->len_max);
  else if (p->len_max == max_len)
  printf(" prefixlen >= %u", p->len_min);
@@ -751,6 +751,23 @@ print_rule(struct peer *peer_l, struct f
  printf("prefix-set \"%s\" ", r->match.prefixset.name);
  if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER)
  printf("or-longer ");
+
+ if (r->match.roaset.name[0] != '\0') {
+ printf("roa-set \"%s\" ", r->match.roaset.name);
+ switch (r->match.roaset.validity) {
+ case ROA_VALID:
+ printf("valid ");
+ break;
+ case ROA_INVALID:
+ printf("invalid ");
+ break;
+ case ROA_UNKNOWN:
+ printf("unknown ");
+ break;
+ default:
+ printf("??? %d ??? ", r->match.roaset.validity);
+ }
+ }
 
  if (r->match.nexthop.flags) {
  if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.426
diff -u -p -r1.426 rde.c
--- rde.c 21 Sep 2018 04:55:27 -0000 1.426
+++ rde.c 21 Sep 2018 15:10:36 -0000
@@ -103,7 +103,9 @@ void rde_update6_queue_runner(u_int8_t
 struct rde_prefixset *rde_find_prefixset(char *, struct rde_prefixset_head *);
 void rde_free_prefixsets(struct rde_prefixset_head *);
 void rde_mark_prefixsets_dirty(struct rde_prefixset_head *,
- struct rde_prefixset_head *);
+     struct rde_prefixset_head *);
+u_int32_t rde_set_roa_validity(struct rde_prefixset_head *,
+     struct bgpd_addr *, u_int8_t, u_int32_t);
 
 void peer_init(u_int32_t);
 void peer_shutdown(void);
@@ -500,6 +502,7 @@ rde_dispatch_imsg_session(struct imsgbuf
  asp->origin = csr.origin;
  asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC;
  asp->aspath = aspath_get(asdata, csr.aspath_len);
+ asp->source_as = aspath_origin(asp->aspath);
  netconf_s.asp = asp;
  break;
  case IMSG_NETWORK_ATTR:
@@ -692,6 +695,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
  static struct as_set *last_as_set;
  static struct set_table *last_set;
  static struct rdomain *rd;
+ static int roa_index;
  struct imsg imsg;
  struct mrt xmrt;
  struct rde_rib rn;
@@ -781,6 +785,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
  if (roasets_tmp == NULL)
  fatal(NULL);
  SIMPLEQ_INIT(roasets_tmp);
+ roa_index = 0;
  as_sets_tmp = calloc(1,
     sizeof(struct as_set_head));
  if (as_sets_tmp == NULL)
@@ -846,6 +851,14 @@ rde_dispatch_imsg_parent(struct imsgbuf
  log_warnx("%s: no prefixset for %s",
     __func__, r->match.prefixset.name);
  }
+ if (r->match.roaset.name[0] != '\0') {
+ r->match.roaset.ps =
+    rde_find_prefixset(r->match.roaset.name,
+ roasets_tmp);
+ if (r->match.roaset.ps == NULL)
+ log_warnx("%s: no roa-set for %s",
+    __func__, r->match.roaset.name);
+ }
  if (r->match.as.flags & AS_FLAG_AS_SET_NAME) {
  struct as_set * aset;
 
@@ -894,17 +907,21 @@ rde_dispatch_imsg_parent(struct imsgbuf
  memcpy(ps->name, imsg.data, sizeof(ps->name));
  if (imsg.hdr.type == IMSG_RECONF_ROA_SET) {
  SIMPLEQ_INSERT_TAIL(roasets_tmp, ps, entry);
- ps->roa = 1;
- last_set = set_new(1, sizeof(struct roa_set));
- if (last_set == NULL)
- fatal(NULL);
- } else
+ ps->roa_index = roa_index++;
+ } else {
  SIMPLEQ_INSERT_TAIL(prefixsets_tmp, ps, entry);
+ }
+ last_set = NULL;
  last_prefixset = ps;
  break;
  case IMSG_RECONF_ROA_AS_SET_ITEMS:
  nmemb = imsg.hdr.len - IMSG_HEADER_SIZE;
  nmemb /= sizeof(struct roa_set);
+ if (last_set == NULL) {
+ last_set = set_new(1, sizeof(struct roa_set));
+ if (last_set == NULL)
+ fatal(NULL);
+ }
  if (set_add(last_set, imsg.data, nmemb) != 0)
  fatal(NULL);
  break;
@@ -915,10 +932,11 @@ rde_dispatch_imsg_parent(struct imsgbuf
  memcpy(&psi, imsg.data, sizeof(psi));
  if (last_prefixset == NULL)
  fatalx("King Bula has no prefixset");
- if (last_prefixset->roa) {
+ if (last_set) {
  set_prep(last_set);
  rv = trie_roa_add(&last_prefixset->th,
     &psi.p.addr, psi.p.len, last_set);
+ last_set = NULL;
  } else {
  rv = trie_add(&last_prefixset->th,
     &psi.p.addr, psi.p.len,
@@ -1122,6 +1140,8 @@ rde_update_dispatch(struct imsg *imsg)
  }
  }
 
+ state.aspath.source_as = aspath_origin(state.aspath.aspath);
+
  rde_reflector(peer, &state.aspath);
  }
 
@@ -1380,12 +1400,17 @@ rde_update_update(struct rde_peer *peer,
  struct filterstate state;
  struct prefix *p;
  enum filter_actions action;
+ u_int32_t vstate;
  u_int16_t i;
  const char *wmsg = "filtered, withdraw";
 
  peer->prefix_rcvd_update++;
+ vstate = rde_set_roa_validity(conf->rde_roasets, prefix, prefixlen,
+    in->aspath.source_as);
+
  /* add original path to the Adj-RIB-In */
- if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen))
+ if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen,
+    vstate))
  peer->prefix_cnt++;
 
  /* max prefix checker */
@@ -1416,7 +1441,7 @@ rde_update_update(struct rde_peer *peer,
     &state.nexthop->exit_nexthop, prefix,
     prefixlen);
  path_update(&ribs[i].rib, peer, &state, prefix,
-    prefixlen);
+    prefixlen, vstate);
  } else if (prefix_remove(&ribs[i].rib, peer, prefix,
     prefixlen)) {
  rde_update_log(wmsg, i, peer,
@@ -3077,25 +3102,37 @@ rde_softreconfig_in(struct rib_entry *re
  struct rde_peer *peer;
  struct rde_aspath *asp;
  enum filter_actions action;
- struct bgpd_addr addr;
+ struct bgpd_addr prefix;
+ int force_reval;
+ u_int32_t vstate;
  u_int16_t i;
 
  pt = re->prefix;
- pt_getaddr(pt, &addr);
+ pt_getaddr(pt, &prefix);
  LIST_FOREACH(p, &re->prefix_h, rib_l) {
+ force_reval = 0;
  asp = prefix_aspath(p);
  peer = prefix_peer(p);
 
+ /* ROA validation state update, BIG HAMMER */
+ vstate = rde_set_roa_validity(conf->rde_roasets, &prefix,
+    pt->prefixlen, asp->source_as);
+ if (vstate != p->validation_state) {
+ force_reval = 1;
+ p->validation_state = vstate;
+ }
+
  /* skip announced networks, they are never filtered */
  if (asp->flags & F_PREFIX_ANNOUNCED)
  continue;
 
  for (i = RIB_LOC_START; i < rib_size; i++) {
+ if (!rib_valid(i))
+ continue;
+
  rib = &ribs[i];
- if (rib->state != RECONF_RELOAD)
+ if (rib->state != RECONF_RELOAD && !force_reval)
  continue;
- if (!rib_valid(i))
- break;
 
  rde_filterstate_prep(&state, asp, prefix_nexthop(p),
     prefix_nhflags(p));
@@ -3103,11 +3140,11 @@ rde_softreconfig_in(struct rib_entry *re
 
  if (action == ACTION_ALLOW) {
  /* update Local-RIB */
- path_update(&rib->rib, peer, &state, &addr,
-    pt->prefixlen);
+ path_update(&rib->rib, peer, &state, &prefix,
+    pt->prefixlen, vstate);
  } else if (action == ACTION_DENY) {
  /* remove from Local-RIB */
- prefix_remove(&rib->rib, peer, &addr,
+ prefix_remove(&rib->rib, peer, &prefix,
     pt->prefixlen);
  }
 
@@ -3616,6 +3653,7 @@ network_add(struct network_config *nc, i
  struct rde_aspath *asp;
  struct filter_set_head *vpnset = NULL;
  in_addr_t prefix4;
+ u_int32_t vstate;
  u_int16_t i;
 
  if (nc->rtableid != conf->default_tableid) {
@@ -3660,6 +3698,7 @@ network_add(struct network_config *nc, i
  asp = path_get();
  asp->aspath = aspath_get(NULL, 0);
  asp->origin = ORIGIN_IGP;
+ asp->source_as = aspath_origin(asp->aspath);
  asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH |
     F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED;
  /* the nexthop is unset unless a default set overrides it */
@@ -3672,8 +3711,10 @@ network_add(struct network_config *nc, i
  rde_apply_set(vpnset, &state, nc->prefix.aid, peerself,
     peerself);
 
+ vstate = rde_set_roa_validity(conf->rde_roasets, &nc->prefix,
+    nc->prefixlen, asp->source_as);
  if (path_update(&ribs[RIB_ADJ_IN].rib, peerself, &state, &nc->prefix,
-    nc->prefixlen))
+    nc->prefixlen, vstate))
  peerself->prefix_cnt++;
  for (i = RIB_LOC_START; i < rib_size; i++) {
  if (!rib_valid(i))
@@ -3682,7 +3723,7 @@ network_add(struct network_config *nc, i
     state.nexthop ? &state.nexthop->exit_nexthop : NULL,
     &nc->prefix, nc->prefixlen);
  path_update(&ribs[i].rib, peerself, &state, &nc->prefix,
-    nc->prefixlen);
+    nc->prefixlen, vstate);
  }
  rde_filterstate_clean(&state);
  path_put(asp);
@@ -3891,4 +3932,20 @@ rde_mark_prefixsets_dirty(struct rde_pre
  new->dirty = 1;
  }
  }
+}
+
+u_int32_t
+rde_set_roa_validity(struct rde_prefixset_head *p, struct bgpd_addr *prefix,
+    u_int8_t plen, u_int32_t as)
+{
+ struct rde_prefixset *ps;
+ u_int32_t validity = 0;
+ int r;
+
+ SIMPLEQ_FOREACH(ps, p, entry) {
+ r = trie_roa_check(&ps->th, prefix, plen, as);
+ validity |= (r << (2 * ps->roa_index)) & ROA_MASK;
+ }
+
+ return validity;
 }
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.193
diff -u -p -r1.193 rde.h
--- rde.h 20 Sep 2018 11:45:59 -0000 1.193
+++ rde.h 21 Sep 2018 12:13:53 -0000
@@ -36,11 +36,12 @@ enum peer_state {
  PEER_ERR /* error occurred going to PEER_DOWN state */
 };
 
-enum roa_state {
- ROA_UNKNOWN,
- ROA_INVALID,
- ROA_VALID
-};
+#define ROA_UNKNOWN 0x0
+#define ROA_INVALID 0x1
+#define ROA_VALID 0x2
+#define ROA_MASK 0x3
+
+#define MAX_ROASETS 16
 
 /*
  * How do we identify peers between the session handler and the rde?
@@ -191,6 +192,7 @@ struct rde_aspath {
  struct aspath *aspath;
  u_int64_t hash;
  u_int32_t flags; /* internally used */
+ u_int32_t source_as; /* chached source_as */
  u_int32_t med; /* multi exit disc */
  u_int32_t lpref; /* local pref */
  u_int32_t weight; /* low prio lpref */
@@ -311,6 +313,7 @@ struct prefix {
  struct rde_peer *peer;
  struct nexthop *nexthop; /* may be NULL */
  time_t lastchange;
+ u_int32_t validation_state;
  u_int8_t nhflags;
 };
 
@@ -339,7 +342,7 @@ struct rde_prefixset {
  struct trie_head th;
  SIMPLEQ_ENTRY(rde_prefixset) entry;
  int dirty;
- int roa;
+ int roa_index;
 };
 SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset);
 
@@ -396,6 +399,7 @@ u_char *aspath_dump(struct aspath *);
 u_int16_t aspath_length(struct aspath *);
 u_int16_t aspath_count(const void *, u_int16_t);
 u_int32_t aspath_neighbor(struct aspath *);
+u_int32_t aspath_origin(struct aspath *);
 int aspath_loopfree(struct aspath *, u_int32_t);
 int aspath_compare(struct aspath *, struct aspath *);
 u_char *aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *);
@@ -501,7 +505,7 @@ void path_init(u_int32_t);
 void path_shutdown(void);
 void path_hash_stats(struct rde_hashstats *);
 int path_update(struct rib *, struct rde_peer *,
-     struct filterstate *, struct bgpd_addr *, int);
+     struct filterstate *, struct bgpd_addr *, int, u_int32_t);
 int path_compare(struct rde_aspath *, struct rde_aspath *);
 void path_remove(struct rde_aspath *);
 u_int32_t path_remove_stale(struct rde_aspath *, u_int8_t, time_t);
@@ -549,6 +553,12 @@ static inline u_int8_t
 prefix_nhflags(struct prefix *p)
 {
  return (p->nhflags);
+}
+
+static inline u_int32_t
+prefix_vstate(struct prefix *p, int index)
+{
+ return (p->validation_state >> (2 * index)) & ROA_MASK;
 }
 
 void nexthop_init(u_int32_t);
Index: rde_attr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v
retrieving revision 1.110
diff -u -p -r1.110 rde_attr.c
--- rde_attr.c 20 Sep 2018 11:06:04 -0000 1.110
+++ rde_attr.c 21 Sep 2018 12:26:46 -0000
@@ -586,7 +586,7 @@ aspath_deflate(u_char *data, u_int16_t *
  nlen += 2 + sizeof(u_int16_t) * seg_len;
 
  if (seg_size > olen)
- fatalx("aspath_deflate: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
 
  if ((ndata = malloc(nlen)) == NULL)
@@ -687,7 +687,7 @@ aspath_count(const void *data, u_int16_t
  cnt += seg_len;
 
  if (seg_size > len)
- fatalx("aspath_count: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  return (cnt);
 }
@@ -719,7 +719,7 @@ aspath_countlength(struct aspath *aspath
  clen += seg_size;
 
  if (seg_size > len)
- fatalx("aspath_countlength: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  if (headcnt > 0 && seg_type == AS_SEQUENCE && headcnt + seg_len < 256)
  /* no need for additional header from the new aspath. */
@@ -766,7 +766,7 @@ aspath_countcopy(struct aspath *aspath,
  buf[1] = seg_len;
  buf += seg_size;
  if (size < seg_size)
- fatalx("aspath_countlength: would overflow");
+ fatalx("%s: would overflow", __func__);
  size -= seg_size;
  }
 }
@@ -780,6 +780,41 @@ aspath_neighbor(struct aspath *aspath)
  return (aspath_extract(aspath->data, 0));
 }
 
+/*
+ * The origin AS number derived from a Route as follows:
+ * o  the rightmost AS in the final segment of the AS_PATH attribute
+ *    in the Route if that segment is of type AS_SEQUENCE, or
+ * o  the BGP speaker's own AS number if that segment is of type
+ *    AS_CONFED_SEQUENCE or AS_CONFED_SET or if the AS_PATH is empty,
+ * o  the distinguished value "NONE" if the final segment of the
+ *   AS_PATH attribute is of any other type.
+ */
+u_int32_t
+aspath_origin(struct aspath *aspath)
+{
+ u_int8_t *seg;
+ u_int32_t as = AS_NONE;
+ u_int16_t len, seg_size;
+ u_int8_t seg_len;
+
+ /* AS_PATH is empty */
+ if (aspath->len == 0)
+ return (rde_local_as());
+
+ seg = aspath->data;
+ for (len = aspath->len; len > 0; len -= seg_size, seg += seg_size) {
+ seg_len = seg[1];
+ seg_size = 2 + sizeof(u_int32_t) * seg_len;
+
+ if (len == seg_size && seg[0] == AS_SEQUENCE) {
+ as = aspath_extract(seg, seg_len - 1);
+ }
+ if (seg_size > len)
+ fatalx("%s: would overflow", __func__);
+ }
+ return (as);
+}
+
 int
 aspath_loopfree(struct aspath *aspath, u_int32_t myAS)
 {
@@ -798,7 +833,7 @@ aspath_loopfree(struct aspath *aspath, u
  }
 
  if (seg_size > len)
- fatalx("aspath_loopfree: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  return (1);
 }
Index: rde_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v
retrieving revision 1.108
diff -u -p -r1.108 rde_filter.c
--- rde_filter.c 20 Sep 2018 11:45:59 -0000 1.108
+++ rde_filter.c 20 Sep 2018 13:42:13 -0000
@@ -360,6 +360,12 @@ rde_filter_match(struct filter_rule *f,
  if (f->peer.ibgp && peer->conf.ebgp)
  return (0);
 
+ if (f->match.roaset.ps != NULL) {
+ struct rde_prefixset *ps = f->match.roaset.ps;
+ if (prefix_vstate(p, ps->roa_index) != f->match.roaset.validity)
+ return (0);
+ }
+
  if (asp != NULL && f->match.as.type != AS_NONE) {
  if (aspath_match(asp->aspath->data, asp->aspath->len,
     &f->match.as, peer->conf.remote_as) == 0)
Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.178
diff -u -p -r1.178 rde_rib.c
--- rde_rib.c 20 Sep 2018 11:06:04 -0000 1.178
+++ rde_rib.c 21 Sep 2018 12:02:27 -0000
@@ -48,11 +48,11 @@ struct rib_entry *rib_restart(struct rib
 RB_PROTOTYPE(rib_tree, rib_entry, rib_e, rib_compare);
 RB_GENERATE(rib_tree, rib_entry, rib_e, rib_compare);
 
-int prefix_add(struct bgpd_addr *, int, struct rib *,
+static int prefix_add(struct bgpd_addr *, int, struct rib *,
     struct rde_peer *, struct rde_aspath *,
-    struct filterstate *);
-int prefix_move(struct prefix *, struct rde_peer *,
-    struct rde_aspath *, struct filterstate *);
+    struct filterstate *, u_int32_t);
+static int prefix_move(struct prefix *, struct rde_peer *,
+    struct rde_aspath *, struct filterstate *, u_int32_t);
 
 static inline void
 re_lock(struct rib_entry *re)
@@ -419,7 +419,7 @@ path_hash_stats(struct rde_hashstats *hs
 
 int
 path_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state,
-    struct bgpd_addr *prefix, int prefixlen)
+    struct bgpd_addr *prefix, int prefixlen, u_int32_t vstate)
 {
  struct rde_aspath *asp, *nasp = &state->aspath;
  struct prefix *p;
@@ -438,6 +438,7 @@ path_update(struct rib *rib, struct rde_
     prefix_nhflags(p) == state->nhflags) {
  /* no change, update last change */
  p->lastchange = time(NULL);
+ p->validation_state = vstate;
  return (0);
  }
  }
@@ -455,10 +456,10 @@ path_update(struct rib *rib, struct rde_
 
  /* If the prefix was found move it else add it to the aspath. */
  if (p != NULL)
- return (prefix_move(p, peer, asp, state));
+ return (prefix_move(p, peer, asp, state, vstate));
  else
  return (prefix_add(prefix, prefixlen, rib, peer, asp,
-    state));
+    state, vstate));
 }
 
 int
@@ -500,6 +501,10 @@ path_compare(struct rde_aspath *a, struc
  return (1);
  if (a->pftableid < b->pftableid)
  return (-1);
+ if (a->source_as > b->source_as)
+ return (1);
+ if (a->source_as < b->source_as)
+ return (-1);
 
  r = aspath_compare(a->aspath, b->aspath);
  if (r > 0)
@@ -673,6 +678,7 @@ path_copy(struct rde_aspath *dst, const
  dst->lpref = src->lpref;
  dst->weight = src->weight;
  dst->origin = src->origin;
+ dst->source_as = src->source_as;
  dst->rtlabelid = rtlabel_ref(src->rtlabelid);
  dst->pftableid = pftable_ref(src->pftableid);
 
@@ -739,7 +745,7 @@ static struct prefix *prefix_alloc(void)
 static void prefix_free(struct prefix *);
 static void prefix_link(struct prefix *, struct rib_entry *,
      struct rde_peer *, struct rde_aspath *,
-     struct filterstate *);
+     struct filterstate *, u_int32_t);
 static void prefix_unlink(struct prefix *);
 
 /*
@@ -760,9 +766,10 @@ prefix_get(struct rib *rib, struct rde_p
 /*
  * Adds or updates a prefix.
  */
-int
+static int
 prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib,
-    struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state)
+    struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state,
+    u_int32_t vstate)
 {
  struct prefix *p;
  struct rib_entry *re;
@@ -774,16 +781,17 @@ prefix_add(struct bgpd_addr *prefix, int
  p = prefix_bypeer(re, asp->peer);
  if (p == NULL) {
  p = prefix_alloc();
- prefix_link(p, re, peer, asp, state);
+ prefix_link(p, re, peer, asp, state, vstate);
  return (1);
  } else {
  if (prefix_aspath(p) != asp ||
     prefix_nexthop(p) != state->nexthop ||
     prefix_nhflags(p) != state->nhflags) {
  /* prefix metadata changed therefor move */
- return (prefix_move(p, peer, asp, state));
+ return (prefix_move(p, peer, asp, state, vstate));
  }
  p->lastchange = time(NULL);
+ p->validation_state = vstate;
  return (0);
  }
 }
@@ -791,9 +799,9 @@ prefix_add(struct bgpd_addr *prefix, int
 /*
  * Move the prefix to the specified as path, removes the old asp if needed.
  */
-int
+static int
 prefix_move(struct prefix *p, struct rde_peer *peer,
-    struct rde_aspath *asp, struct filterstate *state)
+    struct rde_aspath *asp, struct filterstate *state, u_int32_t vstate)
 {
  struct prefix *np;
  struct rde_aspath *oasp;
@@ -809,6 +817,7 @@ prefix_move(struct prefix *p, struct rde
  np->re = p->re;
  np->lastchange = time(NULL);
  np->nhflags = state->nhflags;
+ np->validation_state = vstate;
 
  /* add to new as path */
  TAILQ_INSERT_HEAD(&asp->prefixes, np, path_l);
@@ -1057,7 +1066,7 @@ prefix_network_clean(struct rde_peer *pe
  */
 static void
 prefix_link(struct prefix *pref, struct rib_entry *re, struct rde_peer *peer,
-    struct rde_aspath *asp, struct filterstate *state)
+    struct rde_aspath *asp, struct filterstate *state, u_int32_t vstate)
 {
  TAILQ_INSERT_HEAD(&asp->prefixes, pref, path_l);
 
@@ -1068,6 +1077,7 @@ prefix_link(struct prefix *pref, struct
  pref->re = re;
  pref->lastchange = time(NULL);
  pref->nhflags = state->nhflags;
+ pref->validation_state = vstate;
 
  /* make route decision */
  prefix_evaluate(pref, re);
Index: rde_trie.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_trie.c,v
retrieving revision 1.7
diff -u -p -r1.7 rde_trie.c
--- rde_trie.c 20 Sep 2018 11:45:59 -0000 1.7
+++ rde_trie.c 21 Sep 2018 15:08:21 -0000
@@ -565,11 +565,12 @@ trie_roa_check_v4(struct trie_head *th,
  */
  validity = ROA_INVALID;
 
- /* Treat AS 0 as NONE which can never be matched */
- if (as != 0) {
- rs = set_match(n->set, as);
- if (rs && plen <= rs->maxlen)
+ /* AS_NONE can never match, so don't try */
+ if (as != AS_NONE) {
+ if ((rs = set_match(n->set, as)) != NULL) {
+    if (plen == n->plen || plen <= rs->maxlen)
  return ROA_VALID;
+ }
  }
  }
 
@@ -612,8 +613,8 @@ trie_roa_check_v6(struct trie_head *th,
  */
  validity = ROA_INVALID;
 
- /* Treat AS 0 as NONE which can never be matched */
- if (as != 0) {
+ /* AS_NONE can never match, so don't try */
+ if (as != AS_NONE) {
  if ((rs = set_match(n->set, as)) != NULL)
     if (plen == n->plen || plen <= rs->maxlen)
  return ROA_VALID;
@@ -634,9 +635,9 @@ trie_roa_check_v6(struct trie_head *th,
 /*
  * Do a ROA (Route Origin Validation) check.  Look for elements in the trie
  * which cover prefix "prefix/plen" and match the source-as as.
- * AS 0 is treated here like AS NONE and should be used when the source-as
- * is unknown (e.g. AS_SET). In other words the check will then only return
- * ROA_UNKNOWN or ROA_INVALID depending if the prefix is covered by the ROA.
+ * AS_NONE can be used when the source-as is unknown (e.g. AS_SET).
+ * The check will then only return ROA_UNKNOWN or ROA_INVALID depending if
+ * the prefix is covered by the ROA.
  */
 int
 trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen,
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.39
diff -u -p -r1.39 util.c
--- util.c 20 Sep 2018 11:45:59 -0000 1.39
+++ util.c 21 Sep 2018 12:17:52 -0000
@@ -390,7 +390,7 @@ aspath_match(void *data, u_int16_t len,
  * AS of a previous non AS_SET segment should be used.
  * Because of that simply skip AS_SET segments.
  */
- if (seg[0] != AS_SET)
+ if (seg[0] == AS_SEQUENCE)
  as = aspath_extract(seg, seg_len - 1);
  /* not yet in the final segment */
  if (!final)

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Claudio Jeker
On Fri, Sep 21, 2018 at 05:29:24PM +0200, Claudio Jeker wrote:

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

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

--
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.201
diff -u -p -r1.201 bgpd.c
--- bgpd.c 21 Sep 2018 04:55:27 -0000 1.201
+++ bgpd.c 21 Sep 2018 13:56:16 -0000
@@ -529,15 +529,15 @@ reconfigure(char *conffile, struct bgpd_
     ps->name, sizeof(ps->name)) == -1)
  return (-1);
  RB_FOREACH_SAFE(psi, prefixset_tree, &ps->psitems, npsi) {
- u_int32_t *as;
+ struct roa_set *rs;
  size_t i, l, n;
  RB_REMOVE(prefixset_tree, &ps->psitems, psi);
- as = set_get(psi->set, &n);
+ rs = set_get(psi->set, &n);
  for (i = 0; i < n; i += l) {
  l = (n - i > 1024 ? 1024 : n - i);
  if (imsg_compose(ibuf_rde,
     IMSG_RECONF_ROA_AS_SET_ITEMS,
-    0, 0, -1, as + i, l) == -1)
+    0, 0, -1, rs + i, l * sizeof(*rs)) == -1)
  return -1;
  }
  if (imsg_compose(ibuf_rde, IMSG_RECONF_PREFIXSETITEM, 0,
@@ -569,7 +569,7 @@ reconfigure(char *conffile, struct bgpd_
  for (i = 0; i < n; i += l) {
  l = (n - i > 1024 ? 1024 : n - i);
  if (imsg_compose(ibuf_rde, IMSG_RECONF_AS_SET_ITEMS,
-    0, 0, -1, as + i, l) == -1)
+    0, 0, -1, as + i, l * sizeof(*as)) == -1)
  return -1;
  }
 
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.344
diff -u -p -r1.344 bgpd.h
--- bgpd.h 21 Sep 2018 04:55:27 -0000 1.344
+++ bgpd.h 21 Sep 2018 12:20:16 -0000
@@ -695,6 +695,12 @@ struct filter_prefixset {
  struct rde_prefixset *ps;
 };
 
+struct filter_roaset {
+ u_int32_t validity;
+ char name[SET_NAME_LEN];
+ struct rde_prefixset *ps;
+};
+
 struct filter_community {
  int as;
  int type;
@@ -886,6 +892,7 @@ struct filter_match {
  struct filter_largecommunity large_community;
  struct filter_extcommunity ext_community;
  struct filter_prefixset prefixset;
+ struct filter_roaset roaset;
 };
 
 union filter_rule_ptr {
@@ -1015,6 +1022,8 @@ extern struct rib_names ribnames;
 
 /* 4-byte magic AS number */
 #define AS_TRANS 23456
+/* AS_NONE for origin validation */
+#define AS_NONE 0
 
 struct rde_memstats {
  int64_t path_cnt;
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.359
diff -u -p -r1.359 parse.y
--- parse.y 21 Sep 2018 08:17:15 -0000 1.359
+++ parse.y 21 Sep 2018 11:18:44 -0000
@@ -100,6 +100,7 @@ static struct filter_head *groupfilter_l
 static struct filter_rule *curpeer_filter[2];
 static struct filter_rule *curgroup_filter[2];
 static u_int32_t id;
+static int roacount;
 
 struct filter_rib_l {
  struct filter_rib_l *next;
@@ -213,7 +214,8 @@ typedef struct {
 %token FROM TO ANY
 %token CONNECTED STATIC
 %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE
-%token PREFIX PREFIXLEN PREFIXSET ROASET
+%token PREFIX PREFIXLEN PREFIXSET
+%token ROASET INVALID UNKNOWN VALID
 %token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ
 %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
 %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY
@@ -226,7 +228,7 @@ typedef struct {
 %token <v.number> NUMBER
 %type <v.number> asnumber as4number as4number_any optnumber
 %type <v.number> espah family restart origincode nettype
-%type <v.number> yesno inout restricted
+%type <v.number> yesno inout restricted validity
 %type <v.string> string
 %type <v.addr> address
 %type <v.prefix> prefix addrspec
@@ -493,6 +495,12 @@ prefixset_item : prefix prefixlenop {
  ;
 
 roa_set : ROASET STRING '{' optnl {
+ if (roacount++ >= MAX_ROASETS) {
+ yyerror("too many roa-sets defined: max %d",
+    MAX_ROASETS);
+ free($2);
+ YYERROR;
+ }
  if ((curroaset = new_prefix_set($2, 1)) == NULL) {
  free($2);
  YYERROR;
@@ -503,6 +511,12 @@ roa_set : ROASET STRING '{' optnl {
  curroaset = NULL;
  }
  | ROASET STRING '{' optnl '}' {
+ if (roacount++ >= MAX_ROASETS) {
+ yyerror("too many roa-sets defined: max %d",
+    MAX_ROASETS);
+ free($2);
+ YYERROR;
+ }
  if ((curroaset = new_prefix_set($2, 1)) == NULL) {
  free($2);
  YYERROR;
@@ -2234,6 +2248,28 @@ filter_elm : filter_prefix_h {
  fmopts.m.prefixset.ps = NULL;
  free($2);
  }
+ | ROASET STRING validity {
+ struct prefixset *ps;
+ if (fmopts.m.roaset.name[0] != '\0') {
+ yyerror("roa-set filter already specified");
+ free($2);
+ YYERROR;
+ }
+ if ((ps = find_prefixset($2, conf->roasets)) == NULL) {
+ yyerror("roa-set not defined");
+ free($2);
+ YYERROR;
+ }
+ if (strlcpy(fmopts.m.roaset.name, $2,
+    sizeof(fmopts.m.roaset.name)) >=
+    sizeof(fmopts.m.roaset.name)) {
+ yyerror("roa-set name too long");
+ free($2);
+ YYERROR;
+ }
+ fmopts.m.roaset.validity = $3;
+ free($2);
+ }
  ;
 
 prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); }
@@ -2649,7 +2685,7 @@ filter_set_opt : LOCALPREF NUMBER {
  }
  ;
 
-origincode : string {
+origincode : STRING {
  if (!strcmp($1, "egp"))
  $$ = ORIGIN_EGP;
  else if (!strcmp($1, "igp"))
@@ -2664,6 +2700,21 @@ origincode : string {
  free($1);
  };
 
+validity : STRING {
+ if (!strcmp($1, "unknown"))
+ $$ = ROA_UNKNOWN;
+ else if (!strcmp($1, "invalid"))
+ $$ = ROA_INVALID;
+ else if (!strcmp($1, "valid"))
+ $$ = ROA_VALID;
+ else {
+ yyerror("unknown origin \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ };
+
 optnl : /* empty */
  | '\n' optnl
  ;
@@ -3220,6 +3271,7 @@ parse_config(char *filename, struct bgpd
  curpeer = NULL;
  curgroup = NULL;
  id = 1;
+ roacount = 0;
 
  netconf = &conf->networks;
 
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.122
diff -u -p -r1.122 printconf.c
--- printconf.c 21 Sep 2018 04:55:27 -0000 1.122
+++ printconf.c 21 Sep 2018 04:55:53 -0000
@@ -86,11 +86,11 @@ print_prefix(struct filter_prefix *p)
  printf(" prefixlen %u >< %u ", p->len_min, p->len_max);
  break;
  case OP_RANGE:
- if (p->len_min == p->len_max)
+ if (p->len_min == p->len_max && p->len != p->len_min)
  printf(" prefixlen = %u", p->len_min);
  else if (p->len == p->len_min && p->len_max == max_len)
  printf(" or-longer");
- else if (p->len == p->len_min)
+ else if (p->len == p->len_min && p->len != p->len_max)
  printf(" maxlen %u", p->len_max);
  else if (p->len_max == max_len)
  printf(" prefixlen >= %u", p->len_min);
@@ -751,6 +751,23 @@ print_rule(struct peer *peer_l, struct f
  printf("prefix-set \"%s\" ", r->match.prefixset.name);
  if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER)
  printf("or-longer ");
+
+ if (r->match.roaset.name[0] != '\0') {
+ printf("roa-set \"%s\" ", r->match.roaset.name);
+ switch (r->match.roaset.validity) {
+ case ROA_VALID:
+ printf("valid ");
+ break;
+ case ROA_INVALID:
+ printf("invalid ");
+ break;
+ case ROA_UNKNOWN:
+ printf("unknown ");
+ break;
+ default:
+ printf("??? %d ??? ", r->match.roaset.validity);
+ }
+ }
 
  if (r->match.nexthop.flags) {
  if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.426
diff -u -p -r1.426 rde.c
--- rde.c 21 Sep 2018 04:55:27 -0000 1.426
+++ rde.c 21 Sep 2018 20:18:17 -0000
@@ -103,7 +103,9 @@ void rde_update6_queue_runner(u_int8_t
 struct rde_prefixset *rde_find_prefixset(char *, struct rde_prefixset_head *);
 void rde_free_prefixsets(struct rde_prefixset_head *);
 void rde_mark_prefixsets_dirty(struct rde_prefixset_head *,
- struct rde_prefixset_head *);
+     struct rde_prefixset_head *);
+u_int32_t rde_set_roa_validity(struct rde_prefixset_head *,
+     struct bgpd_addr *, u_int8_t, u_int32_t);
 
 void peer_init(u_int32_t);
 void peer_shutdown(void);
@@ -500,6 +502,7 @@ rde_dispatch_imsg_session(struct imsgbuf
  asp->origin = csr.origin;
  asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC;
  asp->aspath = aspath_get(asdata, csr.aspath_len);
+ asp->source_as = aspath_origin(asp->aspath);
  netconf_s.asp = asp;
  break;
  case IMSG_NETWORK_ATTR:
@@ -692,6 +695,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
  static struct as_set *last_as_set;
  static struct set_table *last_set;
  static struct rdomain *rd;
+ static int roa_index;
  struct imsg imsg;
  struct mrt xmrt;
  struct rde_rib rn;
@@ -781,6 +785,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
  if (roasets_tmp == NULL)
  fatal(NULL);
  SIMPLEQ_INIT(roasets_tmp);
+ roa_index = 0;
  as_sets_tmp = calloc(1,
     sizeof(struct as_set_head));
  if (as_sets_tmp == NULL)
@@ -846,6 +851,14 @@ rde_dispatch_imsg_parent(struct imsgbuf
  log_warnx("%s: no prefixset for %s",
     __func__, r->match.prefixset.name);
  }
+ if (r->match.roaset.name[0] != '\0') {
+ r->match.roaset.ps =
+    rde_find_prefixset(r->match.roaset.name,
+ roasets_tmp);
+ if (r->match.roaset.ps == NULL)
+ log_warnx("%s: no roa-set for %s",
+    __func__, r->match.roaset.name);
+ }
  if (r->match.as.flags & AS_FLAG_AS_SET_NAME) {
  struct as_set * aset;
 
@@ -894,17 +907,21 @@ rde_dispatch_imsg_parent(struct imsgbuf
  memcpy(ps->name, imsg.data, sizeof(ps->name));
  if (imsg.hdr.type == IMSG_RECONF_ROA_SET) {
  SIMPLEQ_INSERT_TAIL(roasets_tmp, ps, entry);
- ps->roa = 1;
- last_set = set_new(1, sizeof(struct roa_set));
- if (last_set == NULL)
- fatal(NULL);
- } else
+ ps->roa_index = roa_index++;
+ } else {
  SIMPLEQ_INSERT_TAIL(prefixsets_tmp, ps, entry);
+ }
+ last_set = NULL;
  last_prefixset = ps;
  break;
  case IMSG_RECONF_ROA_AS_SET_ITEMS:
  nmemb = imsg.hdr.len - IMSG_HEADER_SIZE;
  nmemb /= sizeof(struct roa_set);
+ if (last_set == NULL) {
+ last_set = set_new(1, sizeof(struct roa_set));
+ if (last_set == NULL)
+ fatal(NULL);
+ }
  if (set_add(last_set, imsg.data, nmemb) != 0)
  fatal(NULL);
  break;
@@ -915,10 +932,11 @@ rde_dispatch_imsg_parent(struct imsgbuf
  memcpy(&psi, imsg.data, sizeof(psi));
  if (last_prefixset == NULL)
  fatalx("King Bula has no prefixset");
- if (last_prefixset->roa) {
+ if (last_set) {
  set_prep(last_set);
  rv = trie_roa_add(&last_prefixset->th,
     &psi.p.addr, psi.p.len, last_set);
+ last_set = NULL;
  } else {
  rv = trie_add(&last_prefixset->th,
     &psi.p.addr, psi.p.len,
@@ -1122,6 +1140,10 @@ rde_update_dispatch(struct imsg *imsg)
  }
  }
 
+ if (state.aspath.flags & F_ATTR_ASPATH)
+ state.aspath.source_as =
+    aspath_origin(state.aspath.aspath);
+
  rde_reflector(peer, &state.aspath);
  }
 
@@ -1380,12 +1402,17 @@ rde_update_update(struct rde_peer *peer,
  struct filterstate state;
  struct prefix *p;
  enum filter_actions action;
+ u_int32_t vstate;
  u_int16_t i;
  const char *wmsg = "filtered, withdraw";
 
  peer->prefix_rcvd_update++;
+ vstate = rde_set_roa_validity(conf->rde_roasets, prefix, prefixlen,
+    in->aspath.source_as);
+
  /* add original path to the Adj-RIB-In */
- if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen))
+ if (path_update(&ribs[RIB_ADJ_IN].rib, peer, in, prefix, prefixlen,
+    vstate))
  peer->prefix_cnt++;
 
  /* max prefix checker */
@@ -1416,7 +1443,7 @@ rde_update_update(struct rde_peer *peer,
     &state.nexthop->exit_nexthop, prefix,
     prefixlen);
  path_update(&ribs[i].rib, peer, &state, prefix,
-    prefixlen);
+    prefixlen, vstate);
  } else if (prefix_remove(&ribs[i].rib, peer, prefix,
     prefixlen)) {
  rde_update_log(wmsg, i, peer,
@@ -3077,25 +3104,37 @@ rde_softreconfig_in(struct rib_entry *re
  struct rde_peer *peer;
  struct rde_aspath *asp;
  enum filter_actions action;
- struct bgpd_addr addr;
+ struct bgpd_addr prefix;
+ int force_reval;
+ u_int32_t vstate;
  u_int16_t i;
 
  pt = re->prefix;
- pt_getaddr(pt, &addr);
+ pt_getaddr(pt, &prefix);
  LIST_FOREACH(p, &re->prefix_h, rib_l) {
+ force_reval = 0;
  asp = prefix_aspath(p);
  peer = prefix_peer(p);
 
+ /* ROA validation state update, BIG HAMMER */
+ vstate = rde_set_roa_validity(conf->rde_roasets, &prefix,
+    pt->prefixlen, asp->source_as);
+ if (vstate != p->validation_state) {
+ force_reval = 1;
+ p->validation_state = vstate;
+ }
+
  /* skip announced networks, they are never filtered */
  if (asp->flags & F_PREFIX_ANNOUNCED)
  continue;
 
  for (i = RIB_LOC_START; i < rib_size; i++) {
+ if (!rib_valid(i))
+ continue;
+
  rib = &ribs[i];
- if (rib->state != RECONF_RELOAD)
+ if (rib->state != RECONF_RELOAD && !force_reval)
  continue;
- if (!rib_valid(i))
- break;
 
  rde_filterstate_prep(&state, asp, prefix_nexthop(p),
     prefix_nhflags(p));
@@ -3103,11 +3142,11 @@ rde_softreconfig_in(struct rib_entry *re
 
  if (action == ACTION_ALLOW) {
  /* update Local-RIB */
- path_update(&rib->rib, peer, &state, &addr,
-    pt->prefixlen);
+ path_update(&rib->rib, peer, &state, &prefix,
+    pt->prefixlen, vstate);
  } else if (action == ACTION_DENY) {
  /* remove from Local-RIB */
- prefix_remove(&rib->rib, peer, &addr,
+ prefix_remove(&rib->rib, peer, &prefix,
     pt->prefixlen);
  }
 
@@ -3616,6 +3655,7 @@ network_add(struct network_config *nc, i
  struct rde_aspath *asp;
  struct filter_set_head *vpnset = NULL;
  in_addr_t prefix4;
+ u_int32_t vstate;
  u_int16_t i;
 
  if (nc->rtableid != conf->default_tableid) {
@@ -3660,6 +3700,7 @@ network_add(struct network_config *nc, i
  asp = path_get();
  asp->aspath = aspath_get(NULL, 0);
  asp->origin = ORIGIN_IGP;
+ asp->source_as = aspath_origin(asp->aspath);
  asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH |
     F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED;
  /* the nexthop is unset unless a default set overrides it */
@@ -3672,8 +3713,10 @@ network_add(struct network_config *nc, i
  rde_apply_set(vpnset, &state, nc->prefix.aid, peerself,
     peerself);
 
+ vstate = rde_set_roa_validity(conf->rde_roasets, &nc->prefix,
+    nc->prefixlen, asp->source_as);
  if (path_update(&ribs[RIB_ADJ_IN].rib, peerself, &state, &nc->prefix,
-    nc->prefixlen))
+    nc->prefixlen, vstate))
  peerself->prefix_cnt++;
  for (i = RIB_LOC_START; i < rib_size; i++) {
  if (!rib_valid(i))
@@ -3682,7 +3725,7 @@ network_add(struct network_config *nc, i
     state.nexthop ? &state.nexthop->exit_nexthop : NULL,
     &nc->prefix, nc->prefixlen);
  path_update(&ribs[i].rib, peerself, &state, &nc->prefix,
-    nc->prefixlen);
+    nc->prefixlen, vstate);
  }
  rde_filterstate_clean(&state);
  path_put(asp);
@@ -3891,4 +3934,20 @@ rde_mark_prefixsets_dirty(struct rde_pre
  new->dirty = 1;
  }
  }
+}
+
+u_int32_t
+rde_set_roa_validity(struct rde_prefixset_head *p, struct bgpd_addr *prefix,
+    u_int8_t plen, u_int32_t as)
+{
+ struct rde_prefixset *ps;
+ u_int32_t validity = 0;
+ int r;
+
+ SIMPLEQ_FOREACH(ps, p, entry) {
+ r = trie_roa_check(&ps->th, prefix, plen, as);
+ validity |= (r << (2 * ps->roa_index)) & ROA_MASK;
+ }
+
+ return validity;
 }
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.193
diff -u -p -r1.193 rde.h
--- rde.h 20 Sep 2018 11:45:59 -0000 1.193
+++ rde.h 21 Sep 2018 12:13:53 -0000
@@ -36,11 +36,12 @@ enum peer_state {
  PEER_ERR /* error occurred going to PEER_DOWN state */
 };
 
-enum roa_state {
- ROA_UNKNOWN,
- ROA_INVALID,
- ROA_VALID
-};
+#define ROA_UNKNOWN 0x0
+#define ROA_INVALID 0x1
+#define ROA_VALID 0x2
+#define ROA_MASK 0x3
+
+#define MAX_ROASETS 16
 
 /*
  * How do we identify peers between the session handler and the rde?
@@ -191,6 +192,7 @@ struct rde_aspath {
  struct aspath *aspath;
  u_int64_t hash;
  u_int32_t flags; /* internally used */
+ u_int32_t source_as; /* chached source_as */
  u_int32_t med; /* multi exit disc */
  u_int32_t lpref; /* local pref */
  u_int32_t weight; /* low prio lpref */
@@ -311,6 +313,7 @@ struct prefix {
  struct rde_peer *peer;
  struct nexthop *nexthop; /* may be NULL */
  time_t lastchange;
+ u_int32_t validation_state;
  u_int8_t nhflags;
 };
 
@@ -339,7 +342,7 @@ struct rde_prefixset {
  struct trie_head th;
  SIMPLEQ_ENTRY(rde_prefixset) entry;
  int dirty;
- int roa;
+ int roa_index;
 };
 SIMPLEQ_HEAD(rde_prefixset_head, rde_prefixset);
 
@@ -396,6 +399,7 @@ u_char *aspath_dump(struct aspath *);
 u_int16_t aspath_length(struct aspath *);
 u_int16_t aspath_count(const void *, u_int16_t);
 u_int32_t aspath_neighbor(struct aspath *);
+u_int32_t aspath_origin(struct aspath *);
 int aspath_loopfree(struct aspath *, u_int32_t);
 int aspath_compare(struct aspath *, struct aspath *);
 u_char *aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *);
@@ -501,7 +505,7 @@ void path_init(u_int32_t);
 void path_shutdown(void);
 void path_hash_stats(struct rde_hashstats *);
 int path_update(struct rib *, struct rde_peer *,
-     struct filterstate *, struct bgpd_addr *, int);
+     struct filterstate *, struct bgpd_addr *, int, u_int32_t);
 int path_compare(struct rde_aspath *, struct rde_aspath *);
 void path_remove(struct rde_aspath *);
 u_int32_t path_remove_stale(struct rde_aspath *, u_int8_t, time_t);
@@ -549,6 +553,12 @@ static inline u_int8_t
 prefix_nhflags(struct prefix *p)
 {
  return (p->nhflags);
+}
+
+static inline u_int32_t
+prefix_vstate(struct prefix *p, int index)
+{
+ return (p->validation_state >> (2 * index)) & ROA_MASK;
 }
 
 void nexthop_init(u_int32_t);
Index: rde_attr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v
retrieving revision 1.110
diff -u -p -r1.110 rde_attr.c
--- rde_attr.c 20 Sep 2018 11:06:04 -0000 1.110
+++ rde_attr.c 21 Sep 2018 12:26:46 -0000
@@ -586,7 +586,7 @@ aspath_deflate(u_char *data, u_int16_t *
  nlen += 2 + sizeof(u_int16_t) * seg_len;
 
  if (seg_size > olen)
- fatalx("aspath_deflate: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
 
  if ((ndata = malloc(nlen)) == NULL)
@@ -687,7 +687,7 @@ aspath_count(const void *data, u_int16_t
  cnt += seg_len;
 
  if (seg_size > len)
- fatalx("aspath_count: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  return (cnt);
 }
@@ -719,7 +719,7 @@ aspath_countlength(struct aspath *aspath
  clen += seg_size;
 
  if (seg_size > len)
- fatalx("aspath_countlength: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  if (headcnt > 0 && seg_type == AS_SEQUENCE && headcnt + seg_len < 256)
  /* no need for additional header from the new aspath. */
@@ -766,7 +766,7 @@ aspath_countcopy(struct aspath *aspath,
  buf[1] = seg_len;
  buf += seg_size;
  if (size < seg_size)
- fatalx("aspath_countlength: would overflow");
+ fatalx("%s: would overflow", __func__);
  size -= seg_size;
  }
 }
@@ -780,6 +780,41 @@ aspath_neighbor(struct aspath *aspath)
  return (aspath_extract(aspath->data, 0));
 }
 
+/*
+ * The origin AS number derived from a Route as follows:
+ * o  the rightmost AS in the final segment of the AS_PATH attribute
+ *    in the Route if that segment is of type AS_SEQUENCE, or
+ * o  the BGP speaker's own AS number if that segment is of type
+ *    AS_CONFED_SEQUENCE or AS_CONFED_SET or if the AS_PATH is empty,
+ * o  the distinguished value "NONE" if the final segment of the
+ *   AS_PATH attribute is of any other type.
+ */
+u_int32_t
+aspath_origin(struct aspath *aspath)
+{
+ u_int8_t *seg;
+ u_int32_t as = AS_NONE;
+ u_int16_t len, seg_size;
+ u_int8_t seg_len;
+
+ /* AS_PATH is empty */
+ if (aspath->len == 0)
+ return (rde_local_as());
+
+ seg = aspath->data;
+ for (len = aspath->len; len > 0; len -= seg_size, seg += seg_size) {
+ seg_len = seg[1];
+ seg_size = 2 + sizeof(u_int32_t) * seg_len;
+
+ if (len == seg_size && seg[0] == AS_SEQUENCE) {
+ as = aspath_extract(seg, seg_len - 1);
+ }
+ if (seg_size > len)
+ fatalx("%s: would overflow", __func__);
+ }
+ return (as);
+}
+
 int
 aspath_loopfree(struct aspath *aspath, u_int32_t myAS)
 {
@@ -798,7 +833,7 @@ aspath_loopfree(struct aspath *aspath, u
  }
 
  if (seg_size > len)
- fatalx("aspath_loopfree: would overflow");
+ fatalx("%s: would overflow", __func__);
  }
  return (1);
 }
Index: rde_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v
retrieving revision 1.108
diff -u -p -r1.108 rde_filter.c
--- rde_filter.c 20 Sep 2018 11:45:59 -0000 1.108
+++ rde_filter.c 20 Sep 2018 13:42:13 -0000
@@ -360,6 +360,12 @@ rde_filter_match(struct filter_rule *f,
  if (f->peer.ibgp && peer->conf.ebgp)
  return (0);
 
+ if (f->match.roaset.ps != NULL) {
+ struct rde_prefixset *ps = f->match.roaset.ps;
+ if (prefix_vstate(p, ps->roa_index) != f->match.roaset.validity)
+ return (0);
+ }
+
  if (asp != NULL && f->match.as.type != AS_NONE) {
  if (aspath_match(asp->aspath->data, asp->aspath->len,
     &f->match.as, peer->conf.remote_as) == 0)
Index: rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.178
diff -u -p -r1.178 rde_rib.c
--- rde_rib.c 20 Sep 2018 11:06:04 -0000 1.178
+++ rde_rib.c 21 Sep 2018 12:02:27 -0000
@@ -48,11 +48,11 @@ struct rib_entry *rib_restart(struct rib
 RB_PROTOTYPE(rib_tree, rib_entry, rib_e, rib_compare);
 RB_GENERATE(rib_tree, rib_entry, rib_e, rib_compare);
 
-int prefix_add(struct bgpd_addr *, int, struct rib *,
+static int prefix_add(struct bgpd_addr *, int, struct rib *,
     struct rde_peer *, struct rde_aspath *,
-    struct filterstate *);
-int prefix_move(struct prefix *, struct rde_peer *,
-    struct rde_aspath *, struct filterstate *);
+    struct filterstate *, u_int32_t);
+static int prefix_move(struct prefix *, struct rde_peer *,
+    struct rde_aspath *, struct filterstate *, u_int32_t);
 
 static inline void
 re_lock(struct rib_entry *re)
@@ -419,7 +419,7 @@ path_hash_stats(struct rde_hashstats *hs
 
 int
 path_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state,
-    struct bgpd_addr *prefix, int prefixlen)
+    struct bgpd_addr *prefix, int prefixlen, u_int32_t vstate)
 {
  struct rde_aspath *asp, *nasp = &state->aspath;
  struct prefix *p;
@@ -438,6 +438,7 @@ path_update(struct rib *rib, struct rde_
     prefix_nhflags(p) == state->nhflags) {
  /* no change, update last change */
  p->lastchange = time(NULL);
+ p->validation_state = vstate;
  return (0);
  }
  }
@@ -455,10 +456,10 @@ path_update(struct rib *rib, struct rde_
 
  /* If the prefix was found move it else add it to the aspath. */
  if (p != NULL)
- return (prefix_move(p, peer, asp, state));
+ return (prefix_move(p, peer, asp, state, vstate));
  else
  return (prefix_add(prefix, prefixlen, rib, peer, asp,
-    state));
+    state, vstate));
 }
 
 int
@@ -500,6 +501,10 @@ path_compare(struct rde_aspath *a, struc
  return (1);
  if (a->pftableid < b->pftableid)
  return (-1);
+ if (a->source_as > b->source_as)
+ return (1);
+ if (a->source_as < b->source_as)
+ return (-1);
 
  r = aspath_compare(a->aspath, b->aspath);
  if (r > 0)
@@ -673,6 +678,7 @@ path_copy(struct rde_aspath *dst, const
  dst->lpref = src->lpref;
  dst->weight = src->weight;
  dst->origin = src->origin;
+ dst->source_as = src->source_as;
  dst->rtlabelid = rtlabel_ref(src->rtlabelid);
  dst->pftableid = pftable_ref(src->pftableid);
 
@@ -739,7 +745,7 @@ static struct prefix *prefix_alloc(void)
 static void prefix_free(struct prefix *);
 static void prefix_link(struct prefix *, struct rib_entry *,
      struct rde_peer *, struct rde_aspath *,
-     struct filterstate *);
+     struct filterstate *, u_int32_t);
 static void prefix_unlink(struct prefix *);
 
 /*
@@ -760,9 +766,10 @@ prefix_get(struct rib *rib, struct rde_p
 /*
  * Adds or updates a prefix.
  */
-int
+static int
 prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib,
-    struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state)
+    struct rde_peer *peer, struct rde_aspath *asp, struct filterstate *state,
+    u_int32_t vstate)
 {
  struct prefix *p;
  struct rib_entry *re;
@@ -774,16 +781,17 @@ prefix_add(struct bgpd_addr *prefix, int
  p = prefix_bypeer(re, asp->peer);
  if (p == NULL) {
  p = prefix_alloc();
- prefix_link(p, re, peer, asp, state);
+ prefix_link(p, re, peer, asp, state, vstate);
  return (1);
  } else {
  if (prefix_aspath(p) != asp ||
     prefix_nexthop(p) != state->nexthop ||
     prefix_nhflags(p) != state->nhflags) {
  /* prefix metadata changed therefor move */
- return (prefix_move(p, peer, asp, state));
+ return (prefix_move(p, peer, asp, state, vstate));
  }
  p->lastchange = time(NULL);
+ p->validation_state = vstate;
  return (0);
  }
 }
@@ -791,9 +799,9 @@ prefix_add(struct bgpd_addr *prefix, int
 /*
  * Move the prefix to the specified as path, removes the old asp if needed.
  */
-int
+static int
 prefix_move(struct prefix *p, struct rde_peer *peer,
-    struct rde_aspath *asp, struct filterstate *state)
+    struct rde_aspath *asp, struct filterstate *state, u_int32_t vstate)
 {
  struct prefix *np;
  struct rde_aspath *oasp;
@@ -809,6 +817,7 @@ prefix_move(struct prefix *p, struct rde
  np->re = p->re;
  np->lastchange = time(NULL);
  np->nhflags = state->nhflags;
+ np->validation_state = vstate;
 
  /* add to new as path */
  TAILQ_INSERT_HEAD(&asp->prefixes, np, path_l);
@@ -1057,7 +1066,7 @@ prefix_network_clean(struct rde_peer *pe
  */
 static void
 prefix_link(struct prefix *pref, struct rib_entry *re, struct rde_peer *peer,
-    struct rde_aspath *asp, struct filterstate *state)
+    struct rde_aspath *asp, struct filterstate *state, u_int32_t vstate)
 {
  TAILQ_INSERT_HEAD(&asp->prefixes, pref, path_l);
 
@@ -1068,6 +1077,7 @@ prefix_link(struct prefix *pref, struct
  pref->re = re;
  pref->lastchange = time(NULL);
  pref->nhflags = state->nhflags;
+ pref->validation_state = vstate;
 
  /* make route decision */
  prefix_evaluate(pref, re);
Index: rde_trie.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_trie.c,v
retrieving revision 1.7
diff -u -p -r1.7 rde_trie.c
--- rde_trie.c 20 Sep 2018 11:45:59 -0000 1.7
+++ rde_trie.c 21 Sep 2018 15:08:21 -0000
@@ -565,11 +565,12 @@ trie_roa_check_v4(struct trie_head *th,
  */
  validity = ROA_INVALID;
 
- /* Treat AS 0 as NONE which can never be matched */
- if (as != 0) {
- rs = set_match(n->set, as);
- if (rs && plen <= rs->maxlen)
+ /* AS_NONE can never match, so don't try */
+ if (as != AS_NONE) {
+ if ((rs = set_match(n->set, as)) != NULL) {
+    if (plen == n->plen || plen <= rs->maxlen)
  return ROA_VALID;
+ }
  }
  }
 
@@ -612,8 +613,8 @@ trie_roa_check_v6(struct trie_head *th,
  */
  validity = ROA_INVALID;
 
- /* Treat AS 0 as NONE which can never be matched */
- if (as != 0) {
+ /* AS_NONE can never match, so don't try */
+ if (as != AS_NONE) {
  if ((rs = set_match(n->set, as)) != NULL)
     if (plen == n->plen || plen <= rs->maxlen)
  return ROA_VALID;
@@ -634,9 +635,9 @@ trie_roa_check_v6(struct trie_head *th,
 /*
  * Do a ROA (Route Origin Validation) check.  Look for elements in the trie
  * which cover prefix "prefix/plen" and match the source-as as.
- * AS 0 is treated here like AS NONE and should be used when the source-as
- * is unknown (e.g. AS_SET). In other words the check will then only return
- * ROA_UNKNOWN or ROA_INVALID depending if the prefix is covered by the ROA.
+ * AS_NONE can be used when the source-as is unknown (e.g. AS_SET).
+ * The check will then only return ROA_UNKNOWN or ROA_INVALID depending if
+ * the prefix is covered by the ROA.
  */
 int
 trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen,
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.39
diff -u -p -r1.39 util.c
--- util.c 20 Sep 2018 11:45:59 -0000 1.39
+++ util.c 21 Sep 2018 12:17:52 -0000
@@ -390,7 +390,7 @@ aspath_match(void *data, u_int16_t len,
  * AS of a previous non AS_SET segment should be used.
  * Because of that simply skip AS_SET segments.
  */
- if (seg[0] != AS_SET)
+ if (seg[0] == AS_SEQUENCE)
  as = aspath_extract(seg, seg_len - 1);
  /* not yet in the final segment */
  if (!final)

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Sebastian Benoit-3
Claudio Jeker([hidden email]) on 2018.09.21 22:30:17 +0200:
> > In my setup I get these numbers:
> >     5895 invalid prefixes
> >     67478 valid prefixes
> >     638299 unknown prefixes
> > This is from a single IPv4 only full feed.
> >
> > Disclaimer: works for me but I did not test it thoroughly especially no
> > comparison was done to other implementations. Still wanted to share it
> > now so other people can help.

fwiw, the numbers you are seeing and what i get seem to agree with the stats
on https://rpki-monitor.antd.nist.gov/

(Of course more tests and reality checks/comparisions are still welcome)

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Job Snijders-2
In reply to this post by Claudio Jeker
On Fri, Sep 21, 2018 at 05:29:24PM +0200, Claudio Jeker wrote:
> I currently use the RIPE RPKI validator to grab a JSON file (e.g.
> http://localcert.ripe.net:8088/export.json) and feed that to this perl
> script to convert it into bgpd syntax:

For now I recommend using https://rpki.gin.ntt.net/api/export.json -
should be reliable.

The localcert.ripe.net instance is not run as a production service, more
as a sort of public facing demonstration of an old version of the
validator.

Of course best operational practise is to run your own RPKI validator.
Currently there are two implementations available that can provide the
'export.json' file:

    https://github.com/RIPE-NCC/rpki-validator-3 (java, more mature)
    https://github.com/nlnetlabs/routinator (rust, beta)

We'll have to use one of these until we have our own openbsd validator.

Kind regards,

Job

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Job Snijders-2
In reply to this post by Claudio Jeker
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.
There currently is 1 RPKI, and therefor we should have just 1 roa-set.

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'.

I'm working on a patch to introduce the 'ovs' keyword to bgpctl too,
folks can do stuff like "bgpctl show rib ovs valid". I'll share that
with you privately.

Kind regards,

Job

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Claudio Jeker
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.
--
:wq Claudio

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Job Snijders-2
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:
> > 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).

The big difference between IRR, ARIN WHOIS and RPKI is that the former
two carry no exclusivity, and the latter (RPKI) semantically means
someting different than IRR or WHOIS data can convey.

When an object from IRR or WHOIS indicates that something like
"192.0.2.0/24 source-as 65001", it does *not* mean that 192.0.2.0/24
can't be originated by other ASNs. There may be other sources (other
IRRs) that also have route objects covering the prefix, or not. It just
means that 65001 is one of the valid source ASNs, it is a rather weak
statement.

However, if the same information "192.0.2.0/24 source-as 65001" comes
from a RPKI ROA, it means that *ALL OTHER* announcements from different
source ASNs or with different prefix lengths are invalid.

You can't apply the origin validation procedure if the information fed
into the procedure has no proof of termination.

Phrased differently: this is why you can do RPKI Origin Validation on
all your EBGP sessions, including your transit providers. But with IRR
based filters you can't really apply them on you transit providers.
IRR/WHOIS are only an allowlist, where as RPKI ROAs is a combined allow
+ blocklist.

> 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.

At this point in time I do not thing we should support multiple tables.
Let's finish RPKI ROA origin validation and later, separately, look if
we can optimise for IRR/WHOIS data.

> > 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.

That is entirely unrelated, and being worked on outside of OpenBSD
scope.

> 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.

I disagree, roa-set is an RPKI only thing. There currently is nothing
else to support. No other type of published routing information has the
same semantics as a ROA and are thus suitable for the origin validation
procedure.

> > 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.

Ah, so instead of what I proposed 'unknown' we should use 'not-found'.
OK that makes sense.

> 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 think this is fine. 'ext-community ovs' is a non-transitive extended
community and is not that widely used.

> 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.

OK.

> I will rework the diff considering the input.

Kind regards,

Job

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Claudio Jeker
In reply to this post by Claudio Jeker
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.

--
: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 16:14:05 -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 26 Sep 2018 16:14:06 -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,17 @@ struct filter_prefixset {
  struct rde_prefixset *ps;
 };
 
+struct filter_originset {
+ u_int8_t validity;
+ 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 +917,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 +1000,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 +1047,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 +1133,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 16:14:06 -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 26 Sep 2018 16:14:07 -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,37 @@ 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 validity {
+ 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;
+ }
+ fmopts.m.originset.validity = $3;
+ 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 +2705,7 @@ filter_set_opt : LOCALPREF NUMBER {
  }
  ;
 
-origincode : string {
+origincode : STRING {
  if (!strcmp($1, "egp"))
  $$ = ORIGIN_EGP;
  else if (!strcmp($1, "igp"))
@@ -2664,6 +2720,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 +2863,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 +4354,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 +4388,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 26 Sep 2018 16:14:07 -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,50 @@ 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);
+ switch (r->match.originset.validity) {
+ case ROA_VALID:
+ printf("valid ");
+ break;
+ case ROA_INVALID:
+ printf("invalid ");
+ break;
+ case ROA_NOTFOUND:
+ printf("not-found ");
+ break;
+ default:
+ printf("??? %d ??? ", r->match.originset.validity);
+ }
+ }
+
  if (r->match.nexthop.flags) {
  if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
  printf("nexthop neighbor ");
@@ -919,9 +972,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:14:08 -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 16:14:08 -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 26 Sep 2018 16:14:08 -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 26 Sep 2018 16:14:08 -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 */
+ 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) != f->match.originset.validity)
+ 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 16:14:08 -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 16:14:08 -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 | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Claudio Jeker
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 | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Claudio Jeker
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.

--
: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 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;
  }
 }
 

Reply | Threaded
Open this post in threaded view
|

Re: bgpd ROA validation

Sebastian Benoit-3
Claudio Jeker([hidden email]) 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;
>   }
>  }
>  
>