On Fri, Aug 06, 2021 at 08:34:18PM +0200, Sebastian Benoit wrote: > 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@
Thanks a lot :) > Only one thing, I worry that using this while the sending side is not working > can lead to > blackholing of prefixes. > Add-path allows for send / recv to be independent and so this is would me more of a common issue with add-path. In general having more paths to select from should help to stop oscilation (e.g. with route reflectors) but I think this is frequently used to collect routes and still get full views. Not sure if blackholing is possible (in the end there are more routes to select from available) but route loops could be an issue. By default add-path is disabled and that will remain. I agree that operators need to evaluate carefully what add-path will give them. Also plan is to finish the MRT support for add-path and then work on add-path send. So this should arrive soon as well :) -- :wq Claudio > > > > -- > > :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) > > >