Claudio Jeker(cje...@diehard.n-r-g.com) on 2021.08.04 17:55:45 +0200:
> On Fri, Jul 30, 2021 at 12:02:12PM +0200, Claudio Jeker wrote:
> > This diff implements the bit to support the receive side of
> > RFC7911 - Advertisement of Multiple Paths in BGP.
> > 
> > I did some basic tests and it works for me. People running route
> > collectors should give this a try. The interaction of Add-Path and bgpctl
> > probably needs some work. Also the MRT dumper needs to be updated to
> > support RFC8050. I have a partial diff for that ready as well.
> > 
> > Sending out multiple paths will follow in a later step since that is a
> > bit more complex. I still need to decide how stable I want to make the
> > assigned path_ids for the multiple paths and then changes to the decision
> > process and adjrib-out are required to allow multipe paths there.
> 
> Updated diff that includes some minimal support for bgpctl.
> This add 'bgpctl show rib nei foo path-id 42' as a way to limit which
> paths to show. Now the RFC itself is very flexible in how path-ids are
> assigned (it is possible that different prefixes have different path-ids)
> but the assumtion is that most systems assign path-id in a stable way and
> so it makes sense to allow to filter on path-id.
> Apart from that not much changed.

ok benno@

Only one thing, I worry that using this while the sending side is not working 
can lead to
blackholing of prefixes.

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

Reply via email to