Claudio Jeker(cje...@diehard.n-r-g.com) on 2021.08.04 17:55:45 +0200: > On Fri, Jul 30, 2021 at 12:02:12PM +0200, Claudio Jeker wrote: > > This diff implements the bit to support the receive side of > > RFC7911 - Advertisement of Multiple Paths in BGP. > > > > I did some basic tests and it works for me. People running route > > collectors should give this a try. The interaction of Add-Path and bgpctl > > probably needs some work. Also the MRT dumper needs to be updated to > > support RFC8050. I have a partial diff for that ready as well. > > > > Sending out multiple paths will follow in a later step since that is a > > bit more complex. I still need to decide how stable I want to make the > > assigned path_ids for the multiple paths and then changes to the decision > > process and adjrib-out are required to allow multipe paths there. > > Updated diff that includes some minimal support for bgpctl. > This add 'bgpctl show rib nei foo path-id 42' as a way to limit which > paths to show. Now the RFC itself is very flexible in how path-ids are > assigned (it is possible that different prefixes have different path-ids) > but the assumtion is that most systems assign path-id in a stable way and > so it makes sense to allow to filter on path-id. > Apart from that not much changed.
ok benno@ Only one thing, I worry that using this while the sending side is not working can lead to blackholing of prefixes. > > -- > :wq Claudio > > Index: bgpctl/bgpctl.8 > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.8,v > retrieving revision 1.99 > diff -u -p -r1.99 bgpctl.8 > --- bgpctl/bgpctl.8 16 Jun 2021 16:24:12 -0000 1.99 > +++ bgpctl/bgpctl.8 4 Aug 2021 13:15:53 -0000 > @@ -348,6 +348,13 @@ Show RIB memory statistics. > Show only entries from the specified peer. > .It Cm neighbor group Ar description > Show only entries from the specified peer group. > +.It Cm path-id Ar pathid > +Show only entries which match the specified > +.Ar pathid . > +Must be used together with either > +.Cm neighbor > +or > +.Cm out . > .It Cm peer-as Ar as > Show all entries with > .Ar as > Index: bgpctl/bgpctl.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v > retrieving revision 1.272 > diff -u -p -r1.272 bgpctl.c > --- bgpctl/bgpctl.c 2 Aug 2021 16:51:39 -0000 1.272 > +++ bgpctl/bgpctl.c 4 Aug 2021 15:54:25 -0000 > @@ -249,6 +249,7 @@ main(int argc, char *argv[]) > ribreq.neighbor = neighbor; > strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); > ribreq.aid = res->aid; > + ribreq.path_id = res->pathid; > ribreq.flags = res->flags; > imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq)); > break; > Index: bgpctl/parser.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v > retrieving revision 1.106 > diff -u -p -r1.106 parser.c > --- bgpctl/parser.c 16 Feb 2021 08:30:21 -0000 1.106 > +++ bgpctl/parser.c 4 Aug 2021 13:08:31 -0000 > @@ -61,7 +61,8 @@ enum token_type { > RD, > FAMILY, > RTABLE, > - FILENAME > + FILENAME, > + PATHID, > }; > > struct token { > @@ -114,6 +115,7 @@ static const struct token t_log[]; > static const struct token t_fib_table[]; > static const struct token t_show_fib_table[]; > static const struct token t_communication[]; > +static const struct token t_show_rib_path[]; > > static const struct token t_main[] = { > { KEYWORD, "reload", RELOAD, t_communication}, > @@ -178,10 +180,11 @@ static const struct token t_show_rib[] = > { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, > { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib}, > { KEYWORD, "neighbor", NONE, t_show_rib_neigh}, > + { KEYWORD, "ovs", NONE, t_show_ovs}, > + { KEYWORD, "path-id", NONE, t_show_rib_path}, > { KEYWORD, "table", NONE, t_show_rib_rib}, > { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, > { KEYWORD, "memory", SHOW_RIB_MEM, NULL}, > - { KEYWORD, "ovs", NONE, t_show_ovs}, > { FAMILY, "", NONE, t_show_rib}, > { PREFIX, "", NONE, t_show_prefix}, > { ENDTOKEN, "", NONE, NULL} > @@ -479,6 +482,11 @@ static const struct token t_show_fib_tab > { ENDTOKEN, "", NONE, NULL} > }; > > +static const struct token t_show_rib_path[] = { > + { PATHID, "", NONE, t_show_rib}, > + { ENDTOKEN, "", NONE, NULL} > +}; > + > static struct parse_result res; > > const struct token *match_token(int *argc, char **argv[], > @@ -748,6 +756,7 @@ match_token(int *argc, char **argv[], co > case PREPSELF: > case WEIGHT: > case RTABLE: > + case PATHID: > if (word != NULL && wordlen > 0 && > parse_number(word, &res, table[i].type)) { > match++; > @@ -863,6 +872,7 @@ show_valid_args(const struct token table > case PREPNBR: > case PREPSELF: > case WEIGHT: > + case PATHID: > fprintf(stderr, " <number>\n"); > break; > case RTABLE: > @@ -1019,9 +1029,16 @@ parse_number(const char *word, struct pa > errx(1, "number is %s: %s", errstr, word); > > /* number was parseable */ > - if (type == RTABLE) { > + switch (type) { > + case RTABLE: > r->rtableid = uval; > return (1); > + case PATHID: > + r->pathid = uval; > + r->flags |= F_CTL_HAS_PATHID; > + return (1); > + default: > + break; > } > > if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) > Index: bgpctl/parser.h > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v > retrieving revision 1.40 > diff -u -p -r1.40 parser.h > --- bgpctl/parser.h 16 Feb 2021 08:30:21 -0000 1.40 > +++ bgpctl/parser.h 4 Aug 2021 13:08:11 -0000 > @@ -71,9 +71,10 @@ struct parse_result { > u_int64_t rd; > int flags; > int is_group; > - u_int8_t validation_state; > u_int rtableid; > + u_int32_t pathid; > enum actions action; > + u_int8_t validation_state; > u_int8_t prefixlen; > u_int8_t aid; > int mrtfd; > Index: bgpd/bgpd.conf.5 > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v > retrieving revision 1.212 > diff -u -p -r1.212 bgpd.conf.5 > --- bgpd/bgpd.conf.5 13 Jul 2021 08:44:18 -0000 1.212 > +++ bgpd/bgpd.conf.5 4 Aug 2021 15:53:42 -0000 > @@ -809,6 +809,17 @@ The default is > for the same address family of the session. > .Pp > .It Xo > +.Ic announce add-path recv > +.Pq Ic yes Ns | Ns Ic no > +.Xc > +If set to > +.Ic yes , > +the receive add-path capability is announced which allows reception of > multiple > +paths per prefix. > +The default is > +.Ic no . > +.Pp > +.It Xo > .Ic announce as-4byte > .Pq Ic yes Ns | Ns Ic no > .Xc > Index: bgpd/bgpd.h > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v > retrieving revision 1.416 > diff -u -p -r1.416 bgpd.h > --- bgpd/bgpd.h 27 Jul 2021 07:32:08 -0000 1.416 > +++ bgpd/bgpd.h 4 Aug 2021 13:07:11 -0000 > @@ -95,6 +95,7 @@ > #define F_CTL_OVS_INVALID 0x100000 > #define F_CTL_OVS_NOTFOUND 0x200000 > #define F_CTL_NEIGHBORS 0x400000 /* only used by bgpctl */ > +#define F_CTL_HAS_PATHID 0x800000 /* only set on requests */ > > #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ > __attribute__((__unused__)) > @@ -891,9 +892,10 @@ struct ctl_show_rib_request { > struct filter_as as; > struct community community; > u_int32_t flags; > - u_int8_t validation_state; > + u_int32_t path_id; > pid_t pid; > enum imsg_type type; > + u_int8_t validation_state; > u_int8_t prefixlen; > u_int8_t aid; > }; > Index: bgpd/parse.y > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v > retrieving revision 1.417 > diff -u -p -r1.417 parse.y > --- bgpd/parse.y 17 Jun 2021 16:05:26 -0000 1.417 > +++ bgpd/parse.y 22 Jun 2021 10:48:50 -0000 > @@ -204,7 +204,8 @@ typedef struct { > %token GROUP NEIGHBOR NETWORK > %token EBGP IBGP > %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX > RESTART > -%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED > +%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED > ADDPATH > +%token SEND RECV > %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN > %token DUMP IN OUT SOCKET RESTRICTED > %token LOG TRANSPARENT > @@ -1455,6 +1456,16 @@ peeropts : REMOTEAS as4number { > | ANNOUNCE AS4BYTE yesno { > curpeer->conf.capabilities.as4byte = $3; > } > + | ANNOUNCE ADDPATH RECV yesno { > + int8_t *ap = curpeer->conf.capabilities.add_path; > + u_int8_t i; > + > + for (i = 0; i < AID_MAX; i++) > + if ($4) > + *ap++ |= CAPA_AP_RECV; > + else > + *ap++ &= ~CAPA_AP_RECV; > + } > | EXPORT NONE { > curpeer->conf.export_type = EXPORT_NONE; > } > @@ -2878,6 +2889,7 @@ lookup(char *s) > { "AS", AS}, > { "IPv4", IPV4}, > { "IPv6", IPV6}, > + { "add-path", ADDPATH}, > { "ah", AH}, > { "allow", ALLOW}, > { "announce", ANNOUNCE}, > @@ -2965,6 +2977,7 @@ lookup(char *s) > { "quick", QUICK}, > { "rd", RD}, > { "rde", RDE}, > + { "recv", RECV}, > { "refresh", REFRESH }, > { "reject", REJECT}, > { "remote-as", REMOTEAS}, > @@ -2978,6 +2991,7 @@ lookup(char *s) > { "rtlabel", RTLABEL}, > { "rtr", RTR}, > { "self", SELF}, > + { "send", SEND}, > { "set", SET}, > { "socket", SOCKET }, > { "source-as", SOURCEAS}, > Index: bgpd/rde.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v > retrieving revision 1.531 > diff -u -p -r1.531 rde.c > --- bgpd/rde.c 27 Jul 2021 07:50:01 -0000 1.531 > +++ bgpd/rde.c 4 Aug 2021 15:31:02 -0000 > @@ -50,10 +50,10 @@ void rde_dispatch_imsg_parent(struct i > void rde_dispatch_imsg_rtr(struct imsgbuf *); > void rde_dispatch_imsg_peer(struct rde_peer *, void *); > void rde_update_dispatch(struct rde_peer *, struct imsg *); > -int rde_update_update(struct rde_peer *, struct filterstate *, > +int rde_update_update(struct rde_peer *, u_int32_t, > + struct filterstate *, struct bgpd_addr *, u_int8_t); > +void rde_update_withdraw(struct rde_peer *, u_int32_t, > struct bgpd_addr *, u_int8_t); > -void rde_update_withdraw(struct rde_peer *, struct bgpd_addr *, > - u_int8_t); > int rde_attr_parse(u_char *, u_int16_t, struct rde_peer *, > struct filterstate *, struct mpattr *); > int rde_attr_add(struct filterstate *, u_char *, u_int16_t); > @@ -1183,7 +1183,7 @@ rde_update_dispatch(struct rde_peer *pee > u_int16_t attrpath_len; > u_int16_t nlri_len; > u_int8_t aid, prefixlen, safi, subtype; > - u_int32_t fas; > + u_int32_t fas, pathid; > > p = imsg->data; > > @@ -1288,6 +1288,21 @@ rde_update_dispatch(struct rde_peer *pee > goto done; > } > > + if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) { > + if (len <= sizeof(pathid)) { > + log_peer_warnx(&peer->conf, > + "bad withdraw prefix"); > + rde_update_err(peer, ERR_UPDATE, > + ERR_UPD_NETWORK, NULL, 0); > + goto done; > + } > + memcpy(&pathid, p, sizeof(pathid)); > + pathid = ntohl(pathid); > + p += sizeof(pathid); > + len -= sizeof(pathid); > + } else > + pathid = 0; > + > if ((pos = nlri_get_prefix(p, len, &prefix, > &prefixlen)) == -1) { > /* > @@ -1302,7 +1317,7 @@ rde_update_dispatch(struct rde_peer *pee > p += pos; > len -= pos; > > - rde_update_withdraw(peer, &prefix, prefixlen); > + rde_update_withdraw(peer, pathid, &prefix, prefixlen); > } > > /* withdraw MP_UNREACH_NLRI if available */ > @@ -1339,6 +1354,23 @@ rde_update_dispatch(struct rde_peer *pee > } > > while (mplen > 0) { > + if (peer_has_add_path(peer, aid, CAPA_AP_RECV)) { > + if (mplen <= sizeof(pathid)) { > + log_peer_warnx(&peer->conf, > + "bad %s withdraw prefix", > + aid2str(aid)); > + rde_update_err(peer, ERR_UPDATE, > + ERR_UPD_OPTATTR, > + mpa.unreach, mpa.unreach_len); > + goto done; > + } > + memcpy(&pathid, mpp, sizeof(pathid)); > + pathid = ntohl(pathid); > + mpp += sizeof(pathid); > + mplen -= sizeof(pathid); > + } else > + pathid = 0; > + > switch (aid) { > case AID_INET6: > if ((pos = nlri_get_prefix6(mpp, mplen, > @@ -1381,7 +1413,7 @@ rde_update_dispatch(struct rde_peer *pee > mpp += pos; > mplen -= pos; > > - rde_update_withdraw(peer, &prefix, prefixlen); > + rde_update_withdraw(peer, pathid, &prefix, prefixlen); > } > > if ((state.aspath.flags & ~F_ATTR_MP_UNREACH) == 0) > @@ -1401,6 +1433,21 @@ rde_update_dispatch(struct rde_peer *pee > goto done; > } > > + if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) { > + if (nlri_len <= sizeof(pathid)) { > + log_peer_warnx(&peer->conf, > + "bad nlri prefix"); > + rde_update_err(peer, ERR_UPDATE, > + ERR_UPD_NETWORK, NULL, 0); > + goto done; > + } > + memcpy(&pathid, p, sizeof(pathid)); > + pathid = ntohl(pathid); > + p += sizeof(pathid); > + nlri_len -= sizeof(pathid); > + } else > + pathid = 0; > + > if ((pos = nlri_get_prefix(p, nlri_len, &prefix, > &prefixlen)) == -1) { > log_peer_warnx(&peer->conf, "bad nlri prefix"); > @@ -1411,7 +1458,8 @@ rde_update_dispatch(struct rde_peer *pee > p += pos; > nlri_len -= pos; > > - if (rde_update_update(peer, &state, &prefix, prefixlen) == -1) > + if (rde_update_update(peer, pathid, &state, > + &prefix, prefixlen) == -1) > goto done; > > } > @@ -1456,6 +1504,22 @@ rde_update_dispatch(struct rde_peer *pee > mplen -= pos; > > while (mplen > 0) { > + if (peer_has_add_path(peer, aid, CAPA_AP_RECV)) { > + if (mplen <= sizeof(pathid)) { > + log_peer_warnx(&peer->conf, > + "bad %s nlri prefix", aid2str(aid)); > + rde_update_err(peer, ERR_UPDATE, > + ERR_UPD_OPTATTR, > + mpa.reach, mpa.reach_len); > + goto done; > + } > + memcpy(&pathid, mpp, sizeof(pathid)); > + pathid = ntohl(pathid); > + mpp += sizeof(pathid); > + mplen -= sizeof(pathid); > + } else > + pathid = 0; > + > switch (aid) { > case AID_INET6: > if ((pos = nlri_get_prefix6(mpp, mplen, > @@ -1498,7 +1562,7 @@ rde_update_dispatch(struct rde_peer *pee > mpp += pos; > mplen -= pos; > > - if (rde_update_update(peer, &state, > + if (rde_update_update(peer, pathid, &state, > &prefix, prefixlen) == -1) > goto done; > } > @@ -1509,8 +1573,8 @@ done: > } > > int > -rde_update_update(struct rde_peer *peer, struct filterstate *in, > - struct bgpd_addr *prefix, u_int8_t prefixlen) > +rde_update_update(struct rde_peer *peer, u_int32_t path_id, > + struct filterstate *in, struct bgpd_addr *prefix, u_int8_t prefixlen) > { > struct filterstate state; > enum filter_actions action; > @@ -1523,8 +1587,8 @@ rde_update_update(struct rde_peer *peer, > aspath_origin(in->aspath.aspath)); > > /* add original path to the Adj-RIB-In */ > - if (prefix_update(rib_byid(RIB_ADJ_IN), peer, in, prefix, prefixlen, > - vstate) == 1) > + if (prefix_update(rib_byid(RIB_ADJ_IN), peer, path_id, in, > + prefix, prefixlen, vstate) == 1) > peer->prefix_cnt++; > > /* max prefix checker */ > @@ -1552,9 +1616,9 @@ rde_update_update(struct rde_peer *peer, > rde_update_log("update", i, peer, > &state.nexthop->exit_nexthop, prefix, > prefixlen); > - prefix_update(rib, peer, &state, prefix, > + prefix_update(rib, peer, path_id, &state, prefix, > prefixlen, vstate); > - } else if (prefix_withdraw(rib, peer, prefix, > + } else if (prefix_withdraw(rib, peer, path_id, prefix, > prefixlen)) { > rde_update_log(wmsg, i, peer, > NULL, prefix, prefixlen); > @@ -1567,8 +1631,8 @@ rde_update_update(struct rde_peer *peer, > } > > void > -rde_update_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix, > - u_int8_t prefixlen) > +rde_update_withdraw(struct rde_peer *peer, u_int32_t path_id, > + struct bgpd_addr *prefix, u_int8_t prefixlen) > { > u_int16_t i; > > @@ -1576,13 +1640,14 @@ rde_update_withdraw(struct rde_peer *pee > struct rib *rib = rib_byid(i); > if (rib == NULL) > continue; > - if (prefix_withdraw(rib, peer, prefix, prefixlen)) > + if (prefix_withdraw(rib, peer, path_id, prefix, prefixlen)) > rde_update_log("withdraw", i, peer, NULL, prefix, > prefixlen); > } > > /* remove original path form the Adj-RIB-In */ > - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peer, prefix, prefixlen)) > + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peer, path_id, > + prefix, prefixlen)) > peer->prefix_cnt--; > > peer->prefix_rcvd_withdraw++; > @@ -2292,28 +2357,31 @@ rde_reflector(struct rde_peer *peer, str > * control specific functions > */ > static void > -rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int > flags) > +rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int > flags, > + int adjout) > { > struct ctl_show_rib rib; > struct ibuf *wbuf; > struct attr *a; > struct nexthop *nexthop; > struct rib_entry *re; > + struct rde_peer *peer; > void *bp; > time_t staletime; > size_t aslen; > u_int8_t l; > > nexthop = prefix_nexthop(p); > + peer = prefix_peer(p); > bzero(&rib, sizeof(rib)); > rib.age = getmonotime() - p->lastchange; > rib.local_pref = asp->lpref; > rib.med = asp->med; > rib.weight = asp->weight; > - strlcpy(rib.descr, prefix_peer(p)->conf.descr, sizeof(rib.descr)); > - memcpy(&rib.remote_addr, &prefix_peer(p)->remote_addr, > + strlcpy(rib.descr, peer->conf.descr, sizeof(rib.descr)); > + memcpy(&rib.remote_addr, &peer->remote_addr, > sizeof(rib.remote_addr)); > - rib.remote_id = prefix_peer(p)->remote_bgpid; > + rib.remote_id = peer->remote_bgpid; > if (nexthop != NULL) { > memcpy(&rib.true_nexthop, &nexthop->true_nexthop, > sizeof(rib.true_nexthop)); > @@ -2334,7 +2402,7 @@ rde_dump_rib_as(struct prefix *p, struct > re = prefix_re(p); > if (re != NULL && re->active == p) > rib.flags |= F_PREF_ACTIVE; > - if (!prefix_peer(p)->conf.ebgp) > + if (!peer->conf.ebgp) > rib.flags |= F_PREF_INTERNAL; > if (asp->flags & F_PREFIX_ANNOUNCED) > rib.flags |= F_PREF_ANNOUNCE; > @@ -2344,9 +2412,20 @@ rde_dump_rib_as(struct prefix *p, struct > rib.flags &= ~F_PREF_ELIGIBLE; > if (asp->flags & F_ATTR_PARSE_ERR) > rib.flags |= F_PREF_INVALID; > - staletime = prefix_peer(p)->staletime[p->pt->aid]; > + staletime = peer->staletime[p->pt->aid]; > if (staletime && p->lastchange <= staletime) > rib.flags |= F_PREF_STALE; > + if (!adjout) { > + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_RECV)) { > + rib.path_id = p->path_id; > + rib.flags |= F_PREF_PATH_ID; > + } > + } else { > + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { > + rib.path_id = 0; /* XXX add-path send */ > + rib.flags |= F_PREF_PATH_ID; > + } > + } > aslen = aspath_length(asp->aspath); > > if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, > @@ -2411,7 +2490,7 @@ rde_match_peer(struct rde_peer *p, struc > } > > static void > -rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) > +rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req, int > adjout) > { > struct rde_aspath *asp; > struct rib_entry *re; > @@ -2428,6 +2507,12 @@ rde_dump_filter(struct prefix *p, struct > if ((req->flags & F_CTL_INVALID) && > (asp->flags & F_ATTR_PARSE_ERR) == 0) > return; > + /* > + * XXX handle out specially since then we want to match against our > + * path ids. > + */ > + if ((req->flags & F_CTL_HAS_PATHID) && req->path_id != p->path_id) > + return; > if (req->as.type != AS_UNDEF && > !aspath_match(asp->aspath, &req->as, 0)) > return; > @@ -2438,7 +2523,7 @@ rde_dump_filter(struct prefix *p, struct > } > if (!ovs_match(p, req->flags)) > return; > - rde_dump_rib_as(p, asp, req->pid, req->flags); > + rde_dump_rib_as(p, asp, req->pid, req->flags, adjout); > } > > static void > @@ -2448,7 +2533,7 @@ rde_dump_upcall(struct rib_entry *re, vo > struct prefix *p; > > LIST_FOREACH(p, &re->prefix_h, entry.list.rib) > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 0); > } > > static void > @@ -2469,14 +2554,14 @@ rde_dump_prefix_upcall(struct rib_entry > if (!prefix_compare(&ctx->req.prefix, &addr, > ctx->req.prefixlen)) > LIST_FOREACH(p, &re->prefix_h, entry.list.rib) > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 0); > } else { > if (ctx->req.prefixlen < pt->prefixlen) > return; > if (!prefix_compare(&addr, &ctx->req.prefix, > pt->prefixlen)) > LIST_FOREACH(p, &re->prefix_h, entry.list.rib) > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 0); > } > } > > @@ -2487,7 +2572,7 @@ rde_dump_adjout_upcall(struct prefix *p, > > if (p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) > return; > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 1); > } > > static void > @@ -2507,13 +2592,13 @@ rde_dump_adjout_prefix_upcall(struct pre > return; > if (!prefix_compare(&ctx->req.prefix, &addr, > ctx->req.prefixlen)) > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 1); > } else { > if (ctx->req.prefixlen < p->pt->prefixlen) > return; > if (!prefix_compare(&addr, &ctx->req.prefix, > p->pt->prefixlen)) > - rde_dump_filter(p, &ctx->req); > + rde_dump_filter(p, &ctx->req, 1); > } > } > > @@ -3580,11 +3665,12 @@ rde_softreconfig_in(struct rib_entry *re > > if (action == ACTION_ALLOW) { > /* update Local-RIB */ > - prefix_update(rib, peer, &state, &prefix, > - pt->prefixlen, p->validation_state); > + prefix_update(rib, peer, p->path_id, &state, > + &prefix, pt->prefixlen, > + p->validation_state); > } else if (action == ACTION_DENY) { > /* remove from Local-RIB */ > - prefix_withdraw(rib, peer, &prefix, > + prefix_withdraw(rib, peer, p->path_id, &prefix, > pt->prefixlen); > } > > @@ -3724,11 +3810,12 @@ rde_roa_softreload(struct rib_entry *re, > > if (action == ACTION_ALLOW) { > /* update Local-RIB */ > - prefix_update(rib, peer, &state, &prefix, > - pt->prefixlen, p->validation_state); > + prefix_update(rib, peer, p->path_id, &state, > + &prefix, pt->prefixlen, > + p->validation_state); > } else if (action == ACTION_DENY) { > /* remove from Local-RIB */ > - prefix_withdraw(rib, peer, &prefix, > + prefix_withdraw(rib, peer, p->path_id, &prefix, > pt->prefixlen); > } > > @@ -3952,7 +4039,7 @@ network_add(struct network_config *nc, s > > vstate = rde_roa_validity(&rde_roa, &nc->prefix, > nc->prefixlen, aspath_origin(state->aspath.aspath)); > - if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, state, &nc->prefix, > + if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, 0, state, &nc->prefix, > nc->prefixlen, vstate) == 1) > peerself->prefix_cnt++; > for (i = RIB_LOC_START; i < rib_size; i++) { > @@ -3962,7 +4049,7 @@ network_add(struct network_config *nc, s > rde_update_log("announce", i, peerself, > state->nexthop ? &state->nexthop->exit_nexthop : NULL, > &nc->prefix, nc->prefixlen); > - prefix_update(rib, peerself, state, &nc->prefix, > + prefix_update(rib, peerself, 0, state, &nc->prefix, > nc->prefixlen, vstate); > } > filterset_free(&nc->attrset); > @@ -4022,12 +4109,12 @@ network_delete(struct network_config *nc > struct rib *rib = rib_byid(i); > if (rib == NULL) > continue; > - if (prefix_withdraw(rib, peerself, &nc->prefix, > + if (prefix_withdraw(rib, peerself, 0, &nc->prefix, > nc->prefixlen)) > rde_update_log("withdraw announce", i, peerself, > NULL, &nc->prefix, nc->prefixlen); > } > - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, &nc->prefix, > + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, 0, &nc->prefix, > nc->prefixlen)) > peerself->prefix_cnt--; > } > @@ -4074,7 +4161,7 @@ network_flush_upcall(struct rib_entry *r > u_int32_t i; > u_int8_t prefixlen; > > - p = prefix_bypeer(re, peerself); > + p = prefix_bypeer(re, peerself, 0); > if (p == NULL) > return; > if ((prefix_aspath(p)->flags & F_ANN_DYNAMIC) != F_ANN_DYNAMIC) > @@ -4087,12 +4174,12 @@ network_flush_upcall(struct rib_entry *r > struct rib *rib = rib_byid(i); > if (rib == NULL) > continue; > - if (prefix_withdraw(rib, peerself, &addr, prefixlen) == 1) > + if (prefix_withdraw(rib, peerself, 0, &addr, prefixlen) == 1) > rde_update_log("flush announce", i, peerself, > NULL, &addr, prefixlen); > } > > - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, &addr, > + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, 0, &addr, > prefixlen) == 1) > peerself->prefix_cnt--; > } > Index: bgpd/rde.h > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v > retrieving revision 1.241 > diff -u -p -r1.241 rde.h > --- bgpd/rde.h 27 Jul 2021 07:50:02 -0000 1.241 > +++ bgpd/rde.h 30 Jul 2021 09:16:53 -0000 > @@ -329,6 +329,8 @@ struct prefix { > struct rde_peer *peer; > struct nexthop *nexthop; /* may be NULL */ > time_t lastchange; > + u_int32_t path_id; > + u_int32_t path_id_tx; > u_int8_t validation_state; > u_int8_t nhflags; > u_int8_t eor; > @@ -386,6 +388,7 @@ int rde_match_peer(struct rde_peer *, s > > /* rde_peer.c */ > int peer_has_as4byte(struct rde_peer *); > +int peer_has_add_path(struct rde_peer *, u_int8_t, int); > int peer_accept_no_as_set(struct rde_peer *); > void peer_init(u_int32_t); > void peer_shutdown(void); > @@ -577,13 +580,13 @@ void path_clean(struct rde_aspath *); > void path_put(struct rde_aspath *); > > #define PREFIX_SIZE(x) (((x) + 7) / 8 + 1) > -struct prefix *prefix_get(struct rib *, struct rde_peer *, > +struct prefix *prefix_get(struct rib *, struct rde_peer *, u_int32_t, > struct bgpd_addr *, int); > struct prefix *prefix_lookup(struct rde_peer *, struct bgpd_addr *, > int); > struct prefix *prefix_match(struct rde_peer *, struct bgpd_addr *); > -int prefix_update(struct rib *, struct rde_peer *, > +int prefix_update(struct rib *, struct rde_peer *, u_int32_t, > struct filterstate *, struct bgpd_addr *, int, u_int8_t); > -int prefix_withdraw(struct rib *, struct rde_peer *, > +int prefix_withdraw(struct rib *, struct rde_peer *, u_int32_t, > struct bgpd_addr *, int); > void prefix_add_eor(struct rde_peer *, u_int8_t); > int prefix_adjout_update(struct rde_peer *, struct filterstate *, > @@ -598,7 +601,8 @@ int prefix_dump_new(struct rde_peer *, > void (*)(void *, u_int8_t), int (*)(void *)); > int prefix_write(u_char *, int, struct bgpd_addr *, u_int8_t, int); > int prefix_writebuf(struct ibuf *, struct bgpd_addr *, u_int8_t); > -struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *); > +struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *, > + u_int32_t); > void prefix_destroy(struct prefix *); > void prefix_relink(struct prefix *, struct rde_aspath *, int); > > Index: bgpd/rde_decide.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v > retrieving revision 1.85 > diff -u -p -r1.85 rde_decide.c > --- bgpd/rde_decide.c 4 May 2021 09:21:05 -0000 1.85 > +++ bgpd/rde_decide.c 22 Jun 2021 16:52:02 -0000 > @@ -281,6 +281,13 @@ prefix_cmp(struct prefix *p1, struct pre > if (i > 0) > return -1; > > + /* XXX RFC7911 does not specify this but it is needed. */ > + /* 13. lowest path identifier wins */ > + if (p1->path_id < p2->path_id) > + return 1; > + if (p1->path_id > p2->path_id) > + return -1; > + > fatalx("Uh, oh a politician in the decision process"); > } > > Index: bgpd/rde_peer.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v > retrieving revision 1.11 > diff -u -p -r1.11 rde_peer.c > --- bgpd/rde_peer.c 17 Jun 2021 16:05:26 -0000 1.11 > +++ bgpd/rde_peer.c 22 Jun 2021 14:47:42 -0000 > @@ -54,6 +54,14 @@ peer_has_as4byte(struct rde_peer *peer) > } > > int > +peer_has_add_path(struct rde_peer *peer, u_int8_t aid, int mode) > +{ > + if (aid > AID_MAX) > + return 0; > + return (peer->capa.add_path[aid] & mode); > +} > + > +int > peer_accept_no_as_set(struct rde_peer *peer) > { > return (peer->flags & PEERFLAG_NO_AS_SET); > @@ -250,7 +258,8 @@ peer_flush_upcall(struct rib_entry *re, > struct rib *rib = rib_byid(i); > if (rib == NULL) > continue; > - rp = prefix_get(rib, peer, &addr, prefixlen); > + rp = prefix_get(rib, peer, p->path_id, > + &addr, prefixlen); > if (rp) { > asp = prefix_aspath(rp); > if (asp && asp->pftableid) > @@ -264,7 +273,6 @@ peer_flush_upcall(struct rib_entry *re, > > prefix_destroy(p); > peer->prefix_cnt--; > - break; /* optimization, only one match per peer possible */ > } > } > > Index: bgpd/rde_rib.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v > retrieving revision 1.223 > diff -u -p -r1.223 rde_rib.c > --- bgpd/rde_rib.c 27 Jul 2021 07:50:02 -0000 1.223 > +++ bgpd/rde_rib.c 30 Jul 2021 09:37:10 -0000 > @@ -842,7 +842,7 @@ path_put(struct rde_aspath *asp) > /* prefix specific functions */ > > static int prefix_add(struct bgpd_addr *, int, struct rib *, > - struct rde_peer *, struct rde_aspath *, > + struct rde_peer *, u_int32_t, struct rde_aspath *, > struct rde_community *, struct nexthop *, > u_int8_t, u_int8_t); > static int prefix_move(struct prefix *, struct rde_peer *, > @@ -850,7 +850,7 @@ static int prefix_move(struct prefix *, > struct nexthop *, u_int8_t, u_int8_t); > > static void prefix_link(struct prefix *, struct rib_entry *, > - struct rde_peer *, struct rde_aspath *, > + struct rde_peer *, u_int32_t, struct rde_aspath *, > struct rde_community *, struct nexthop *, > u_int8_t, u_int8_t); > static void prefix_unlink(struct prefix *); > @@ -876,12 +876,14 @@ prefix_cmp(struct prefix *a, struct pref > return (a->nexthop > b->nexthop ? 1 : -1); > if (a->nhflags != b->nhflags) > return (a->nhflags > b->nhflags ? 1 : -1); > + /* XXX path_id ??? */ > return pt_prefix_cmp(a->pt, b->pt); > } > > static inline int > prefix_index_cmp(struct prefix *a, struct prefix *b) > { > + /* XXX path_id ??? */ > return pt_prefix_cmp(a->pt, b->pt); > } > > @@ -892,15 +894,15 @@ RB_GENERATE_STATIC(prefix_index, prefix, > * search for specified prefix of a peer. Returns NULL if not found. > */ > struct prefix * > -prefix_get(struct rib *rib, struct rde_peer *peer, struct bgpd_addr *prefix, > - int prefixlen) > +prefix_get(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, > + struct bgpd_addr *prefix, int prefixlen) > { > struct rib_entry *re; > > re = rib_get(rib, prefix, prefixlen); > if (re == NULL) > return (NULL); > - return (prefix_bypeer(re, peer)); > + return (prefix_bypeer(re, peer, path_id)); > } > > /* > @@ -954,8 +956,9 @@ prefix_match(struct rde_peer *peer, stru > * Return 1 if prefix was newly added, 0 if it was just changed. > */ > int > -prefix_update(struct rib *rib, struct rde_peer *peer, struct filterstate > *state, > - struct bgpd_addr *prefix, int prefixlen, u_int8_t vstate) > +prefix_update(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, > + struct filterstate *state, struct bgpd_addr *prefix, int prefixlen, > + u_int8_t vstate) > { > struct rde_aspath *asp, *nasp = &state->aspath; > struct rde_community *comm, *ncomm = &state->communities; > @@ -964,7 +967,7 @@ prefix_update(struct rib *rib, struct rd > /* > * First try to find a prefix in the specified RIB. > */ > - if ((p = prefix_get(rib, peer, prefix, prefixlen)) != NULL) { > + if ((p = prefix_get(rib, peer, path_id, prefix, prefixlen)) != NULL) { > if (prefix_nexthop(p) == state->nexthop && > prefix_nhflags(p) == state->nhflags && > communities_equal(ncomm, prefix_communities(p)) && > @@ -997,8 +1000,8 @@ prefix_update(struct rib *rib, struct rd > return (prefix_move(p, peer, asp, comm, state->nexthop, > state->nhflags, vstate)); > else > - return (prefix_add(prefix, prefixlen, rib, peer, asp, comm, > - state->nexthop, state->nhflags, vstate)); > + return (prefix_add(prefix, prefixlen, rib, peer, path_id, asp, > + comm, state->nexthop, state->nhflags, vstate)); > } > > /* > @@ -1006,8 +1009,9 @@ prefix_update(struct rib *rib, struct rd > */ > static int > prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, > - struct rde_peer *peer, struct rde_aspath *asp, struct rde_community > *comm, > - struct nexthop *nexthop, u_int8_t nhflags, u_int8_t vstate) > + struct rde_peer *peer, u_int32_t path_id, struct rde_aspath *asp, > + struct rde_community *comm, struct nexthop *nexthop, u_int8_t nhflags, > + u_int8_t vstate) > { > struct prefix *p; > struct rib_entry *re; > @@ -1017,7 +1021,7 @@ prefix_add(struct bgpd_addr *prefix, int > re = rib_add(rib, prefix, prefixlen); > > p = prefix_alloc(); > - prefix_link(p, re, peer, asp, comm, nexthop, nhflags, vstate); > + prefix_link(p, re, peer, path_id, asp, comm, nexthop, nhflags, vstate); > return (1); > } > > @@ -1045,6 +1049,7 @@ prefix_move(struct prefix *p, struct rde > np->peer = peer; > np->entry.list.re = prefix_re(p); > np->pt = p->pt; /* skip refcnt update since ref is moved */ > + np->path_id = p->path_id; > np->validation_state = vstate; > np->nhflags = nhflags; > np->nexthop = nexthop_ref(nexthop); > @@ -1086,13 +1091,13 @@ prefix_move(struct prefix *p, struct rde > * or pt_entry -- become empty remove them too. > */ > int > -prefix_withdraw(struct rib *rib, struct rde_peer *peer, > +prefix_withdraw(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, > struct bgpd_addr *prefix, int prefixlen) > { > struct prefix *p; > struct rde_aspath *asp; > > - p = prefix_get(rib, peer, prefix, prefixlen); > + p = prefix_get(rib, peer, path_id, prefix, prefixlen); > if (p == NULL) /* Got a dummy withdrawn request. */ > return (0); > > @@ -1487,12 +1492,12 @@ prefix_writebuf(struct ibuf *buf, struct > * belonging to the peer peer. Returns NULL if no match found. > */ > struct prefix * > -prefix_bypeer(struct rib_entry *re, struct rde_peer *peer) > +prefix_bypeer(struct rib_entry *re, struct rde_peer *peer, u_int32_t path_id) > { > struct prefix *p; > > LIST_FOREACH(p, &re->prefix_h, entry.list.rib) > - if (prefix_peer(p) == peer) > + if (prefix_peer(p) == peer && p->path_id == path_id) > return (p); > return (NULL); > } > @@ -1544,7 +1549,7 @@ prefix_destroy(struct prefix *p) > */ > static void > prefix_link(struct prefix *p, struct rib_entry *re, struct rde_peer *peer, > - struct rde_aspath *asp, struct rde_community *comm, > + u_int32_t path_id, struct rde_aspath *asp, struct rde_community *comm, > struct nexthop *nexthop, u_int8_t nhflags, u_int8_t vstate) > { > if (p->flags & PREFIX_FLAG_ADJOUT) > @@ -1555,6 +1560,7 @@ prefix_link(struct prefix *p, struct rib > p->communities = communities_ref(comm); > p->peer = peer; > p->pt = pt_ref(re->prefix); > + p->path_id = path_id; > p->validation_state = vstate; > p->nhflags = nhflags; > p->nexthop = nexthop_ref(nexthop); > Index: bgpd/rde_update.c > =================================================================== > RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v > retrieving revision 1.130 > diff -u -p -r1.130 rde_update.c > --- bgpd/rde_update.c 17 Jun 2021 08:14:50 -0000 1.130 > +++ bgpd/rde_update.c 22 Jun 2021 10:10:07 -0000 > @@ -625,9 +625,18 @@ up_dump_prefix(u_char *buf, int len, str > { > struct prefix *p, *np; > struct bgpd_addr addr; > + u_int32_t pathid; > int r, wpos = 0, done = 0; > > RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) { > + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { > + if (len <= wpos + (int)sizeof(pathid)) > + break; > + /* XXX add-path send side */ > + pathid = 0; > + memcpy(buf + wpos, &pathid, sizeof(pathid)); > + wpos += sizeof(pathid); > + } > pt_getaddr(p->pt, &addr); > if ((r = prefix_write(buf + wpos, len - wpos, > &addr, p->pt->prefixlen, withdraw)) == -1) >