Implement reception of multiple paths per BGP session. This is one
authorclaudio <claudio@openbsd.org>
Mon, 9 Aug 2021 08:15:34 +0000 (08:15 +0000)
committerclaudio <claudio@openbsd.org>
Mon, 9 Aug 2021 08:15:34 +0000 (08:15 +0000)
side of RFC7911 and the send portion will follow.

The path-id is extracted from the NLRI encoding an put into struct
prefix.  To do this the prefix_by_peer() function gets a path-id
argument.  If a session is not path-id enabled this argument will
be always 0. If a session is path-id enabled the value is taken
from the NLRI and can be anything, including 0. The value has no
meaning in itself. Still to make sure the decision process is able
to break a tie the path-id is checked as the last step (this is not
part of the RFC but required).

OK benno@

usr.sbin/bgpd/bgpd.conf.5
usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/parse.y
usr.sbin/bgpd/rde.c
usr.sbin/bgpd/rde.h
usr.sbin/bgpd/rde_decide.c
usr.sbin/bgpd/rde_peer.c
usr.sbin/bgpd/rde_rib.c
usr.sbin/bgpd/rde_update.c

index 6567216..6ee7bc2 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.conf.5,v 1.212 2021/07/13 08:44:18 claudio Exp $
+.\" $OpenBSD: bgpd.conf.5,v 1.213 2021/08/09 08:15:34 claudio Exp $
 .\"
 .\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
 .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,7 +16,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 13 2021 $
+.Dd $Mdocdate: August 9 2021 $
 .Dt BGPD.CONF 5
 .Os
 .Sh NAME
@@ -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 4c0952d..e897e07 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpd.h,v 1.416 2021/07/27 07:32:08 claudio Exp $ */
+/*     $OpenBSD: bgpd.h,v 1.417 2021/08/09 08:15:34 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 82ea228..37f1862 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.417 2021/06/17 16:05:26 claudio Exp $ */
+/*     $OpenBSD: parse.y,v 1.418 2021/08/09 08:15:34 claudio Exp $ */
 
 /*
  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -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 24dc671..65696e2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde.c,v 1.531 2021/07/27 07:50:01 claudio Exp $ */
+/*     $OpenBSD: rde.c,v 1.532 2021/08/09 08:15:34 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -50,10 +50,10 @@ void                 rde_dispatch_imsg_parent(struct imsgbuf *);
 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 *peer, struct imsg *imsg)
        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 *peer, struct imsg *imsg)
                        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 *peer, struct imsg *imsg)
                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 *peer, struct imsg *imsg)
                }
 
                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 *peer, struct imsg *imsg)
                        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 *peer, struct imsg *imsg)
                        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 *peer, struct imsg *imsg)
                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 *peer, struct imsg *imsg)
                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 *peer, struct imsg *imsg)
                        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, struct filterstate *in,
            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, struct filterstate *in,
                        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, struct filterstate *in,
 }
 
 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 *peer, struct bgpd_addr *prefix,
                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, struct rde_aspath *asp)
  * 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 rde_aspath *asp, pid_t pid, int flags)
        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 rde_aspath *asp, pid_t pid, int flags)
                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, struct ctl_neighbor *n)
 }
 
 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 ctl_show_rib_request *req)
        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 ctl_show_rib_request *req)
        }
        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, void *ptr)
        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 *re, void *ptr)
                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, void *ptr)
 
        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 prefix *p, void *ptr)
                        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, void *bula)
 
                        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, void *bula)
 
                        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, struct filterstate *state)
 
        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, struct filterstate *state)
                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 *re, void *ptr)
        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 *re, void *ptr)
                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 6088026..54f718d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde.h,v 1.241 2021/07/27 07:50:02 claudio Exp $ */
+/*     $OpenBSD: rde.h,v 1.242 2021/08/09 08:15:34 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
@@ -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 *, struct ctl_neighbor *);
 
 /* 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 *, u_int8_t, unsigned int,
                    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 d071beb..f77f615 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_decide.c,v 1.85 2021/05/04 09:21:05 claudio Exp $ */
+/*     $OpenBSD: rde_decide.c,v 1.86 2021/08/09 08:15:34 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -281,6 +281,13 @@ prefix_cmp(struct prefix *p1, struct prefix *p2, int *testall)
        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 502826d..fafc1c8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_peer.c,v 1.11 2021/06/17 16:05:26 claudio Exp $ */
+/*     $OpenBSD: rde_peer.c,v 1.12 2021/08/09 08:15:35 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
@@ -53,6 +53,14 @@ peer_has_as4byte(struct rde_peer *peer)
        return (peer->capa.as4byte);
 }
 
+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)
 {
@@ -250,7 +258,8 @@ peer_flush_upcall(struct rib_entry *re, void *arg)
                        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, void *arg)
 
                prefix_destroy(p);
                peer->prefix_cnt--;
-               break;  /* optimization, only one match per peer possible */
        }
 }
 
index dc85251..9cca822 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_rib.c,v 1.223 2021/07/27 07:50:02 claudio Exp $ */
+/*     $OpenBSD: rde_rib.c,v 1.224 2021/08/09 08:15:35 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -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 rde_peer *,
                    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 prefix *b)
                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, entry.tree.index, prefix_index_cmp)
  * 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, struct bgpd_addr *addr)
  * 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 rde_peer *peer, struct filterstate *state,
        /*
         * 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 rde_peer *peer, struct filterstate *state,
                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 rde_peer *peer, struct filterstate *state,
  */
 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 prefixlen, struct rib *rib,
                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_peer *peer,
        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_peer *peer,
  * 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 bgpd_addr *prefix, u_int8_t plen)
  * 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_entry *re, struct rde_peer *peer,
        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 3296227..3c4c8b0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_update.c,v 1.130 2021/06/17 08:14:50 claudio Exp $ */
+/*     $OpenBSD: rde_update.c,v 1.131 2021/08/09 08:15:35 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -625,9 +625,18 @@ up_dump_prefix(u_char *buf, int len, struct prefix_tree *prefix_head,
 {
        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)