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)