Claudio Jeker([email protected]) 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)
>