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)