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.

-- 
: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)

Reply via email to