On Tue, May 18, 2021 at 05:35:01PM +0200, Claudio Jeker wrote:
> bgpd(8) will soon support ADD-PATH (RFC7911) and enhanced route refresh
> (RFC7313). This is the frist step toward this.
> It add the capability parsers, extends the capability struct and adds the
> capability negotiation bits. The route refresh parser and generator are
> extended to support the BoRR and EoRR message and last but not least
> bgpctl is adjusted to print the new capabilities.
> 
> Now since the system has no way of enabling the two new capabilities.
> bgpctl will only show if the peer sends the capability but there should
> be no other effect.
> 
> The RDE bits for enahnced route refresh are almost ready, add-path will
> take a bit more since the RDE needs to grow an extra indirection.
> 
> I decided to split this work up to simplify review. Lets see if this
> works :)

Since this does not change any behaviour I consider to commit this soon
unles someone objects.

-- 
:wq Claudio


Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.267
diff -u -p -r1.267 bgpctl.c
--- bgpctl/bgpctl.c     3 May 2021 14:01:56 -0000       1.267
+++ bgpctl/bgpctl.c     18 May 2021 08:49:34 -0000
@@ -1351,6 +1351,13 @@ print_capability(u_int8_t capa_code, u_c
                } else
                        printf("bad length");
                break;
+       case CAPA_ADD_PATH:
+               printf("add-path capability");
+               /* XXX there is more needed here */
+               break;
+       case CAPA_ENHANCED_RR:
+               printf("enhanced route refresh capability");
+               break;
        default:
                printf("unknown capability %u length %u", capa_code, len);
                break;
Index: bgpctl/output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
retrieving revision 1.16
diff -u -p -r1.16 output.c
--- bgpctl/output.c     26 Apr 2021 18:23:20 -0000      1.16
+++ bgpctl/output.c     18 May 2021 15:17:39 -0000
@@ -147,6 +147,34 @@ show_neighbor_capa_mp(struct capabilitie
 }
 
 static void
+show_neighbor_capa_add_path(struct capabilities *capa)
+{
+       const char      *mode;
+       int             comma;
+       u_int8_t        i;
+
+       printf("    Add-path: ");
+       for (i = 0, comma = 0; i < AID_MAX; i++) {
+               switch (capa->add_path[i]) {
+               case 0:
+               default:
+                       continue;
+               case CAPA_AP_RECV:
+                       mode = "recv";
+                       break;
+               case CAPA_AP_SEND:
+                       mode = "send";
+                       break;
+               case CAPA_AP_BIDIR:
+                       mode = "bidir";
+               }
+               printf("%s%s %s", comma ? ", " : "", aid2str(i), mode);
+               comma = 1;
+       }
+       printf("\n");
+}
+
+static void
 show_neighbor_capa_restart(struct capabilities *capa)
 {
        int             comma;
@@ -202,6 +230,13 @@ show_neighbor_msgstats(struct peer *p)
            p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw);
        printf("  %-15s %10llu %10llu\n", "End-of-Rib",
            p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor);
+       printf("  Route Refresh statistics:\n");
+       printf("  %-15s %10llu %10llu\n", "Request",
+           p->stats.refresh_sent_req, p->stats.refresh_rcvd_req);
+       printf("  %-15s %10llu %10llu\n", "Begin-of-RR",
+           p->stats.refresh_sent_borr, p->stats.refresh_rcvd_borr);
+       printf("  %-15s %10llu %10llu\n", "End-of-RR",
+           p->stats.refresh_sent_eorr, p->stats.refresh_rcvd_eorr);
 }
 
 static void
@@ -210,7 +245,7 @@ show_neighbor_full(struct peer *p, struc
        const char              *errstr;
        struct in_addr           ina;
        char                    *s;
-       int                      hascapamp = 0;
+       int                      hascapamp, hascapaap;
        u_int8_t                 i;
 
        if ((p->conf.remote_addr.aid == AID_INET &&
@@ -279,35 +314,57 @@ show_neighbor_full(struct peer *p, struc
            fmt_monotime(p->stats.last_read),
            p->holdtime, p->holdtime/3);
        printf("  Last write %s\n", fmt_monotime(p->stats.last_write));
-       for (i = 0; i < AID_MAX; i++)
+
+       hascapamp = 0;
+       hascapaap = 0;
+       for (i = AID_MIN; i < AID_MAX; i++) {
                if (p->capa.peer.mp[i])
                        hascapamp = 1;
-       if (hascapamp || p->capa.peer.refresh ||
-           p->capa.peer.grestart.restart || p->capa.peer.as4byte) {
+               if (p->capa.peer.add_path[i])
+                       hascapaap = 1;
+       }
+       if (hascapamp || hascapaap || p->capa.peer.grestart.restart ||
+           p->capa.peer.refresh || p->capa.peer.enhanced_rr ||
+           p->capa.peer.as4byte) {
                printf("  Neighbor capabilities:\n");
                if (hascapamp)
                        show_neighbor_capa_mp(&p->capa.peer);
+               if (p->capa.peer.as4byte)
+                       printf("    4-byte AS numbers\n");
                if (p->capa.peer.refresh)
                        printf("    Route Refresh\n");
+               if (p->capa.peer.enhanced_rr)
+                       printf("    Enhanced Route Refresh\n");
                if (p->capa.peer.grestart.restart)
                        show_neighbor_capa_restart(&p->capa.peer);
-               if (p->capa.peer.as4byte)
-                       printf("    4-byte AS numbers\n");
+               if (hascapaap)
+                       show_neighbor_capa_add_path(&p->capa.peer);
        }
-       for (i = 0; i < AID_MAX; i++)
+
+       hascapamp = 0;
+       hascapaap = 0;
+       for (i = AID_MIN; i < AID_MAX; i++) {
                if (p->capa.neg.mp[i])
                        hascapamp = 1;
-       if (hascapamp || p->capa.neg.refresh ||
-           p->capa.neg.grestart.restart || p->capa.neg.as4byte) {
+               if (p->capa.neg.add_path[i])
+                       hascapaap = 1;
+       }
+       if (hascapamp || hascapaap || p->capa.neg.grestart.restart ||
+           p->capa.neg.refresh || p->capa.neg.enhanced_rr ||
+           p->capa.neg.as4byte) {
                printf("  Negotiated capabilities:\n");
                if (hascapamp)
                        show_neighbor_capa_mp(&p->capa.neg);
+               if (p->capa.neg.as4byte)
+                       printf("    4-byte AS numbers\n");
                if (p->capa.neg.refresh)
                        printf("    Route Refresh\n");
+               if (p->capa.neg.enhanced_rr)
+                       printf("    Enhanced Route Refresh\n");
                if (p->capa.neg.grestart.restart)
                        show_neighbor_capa_restart(&p->capa.neg);
-               if (p->capa.neg.as4byte)
-                       printf("    4-byte AS numbers\n");
+               if (hascapaap)
+                       show_neighbor_capa_add_path(&p->capa.neg);
        }
        printf("\n");
 
Index: bgpctl/output_json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v
retrieving revision 1.10
diff -u -p -r1.10 output_json.c
--- bgpctl/output_json.c        3 May 2021 14:01:56 -0000       1.10
+++ bgpctl/output_json.c        18 May 2021 15:22:56 -0000
@@ -40,19 +40,23 @@ json_head(struct parse_result *res)
 static void
 json_neighbor_capabilities(struct capabilities *capa)
 {
-       int hascapamp;
+       int hascapamp = 0, hascapaap = 0;
        uint8_t i;
 
-       for (i = 0; i < AID_MAX; i++)
+       for (i = 0; i < AID_MAX; i++) {
                if (capa->mp[i])
                        hascapamp = 1;
-       if (!hascapamp && !capa->refresh && !capa->grestart.restart &&
-           !capa->as4byte)
+               if (capa->add_path[i])
+                       hascapaap = 1;
+       }
+       if (!hascapamp && !hascapaap && !capa->grestart.restart &&
+           !capa->refresh && !capa->enhanced_rr && !capa->as4byte)
                return;
 
        json_do_object("capabilities");
        json_do_bool("as4byte", capa->as4byte);
        json_do_bool("refresh", capa->refresh);
+       json_do_bool("enhanced_refresh", capa->enhanced_rr);
 
        if (hascapamp) {
                json_do_array("multiprotocol");
@@ -95,6 +99,31 @@ json_neighbor_capabilities(struct capabi
 
                json_do_end();
        }
+       if (hascapaap) {
+               json_do_array("add-path");
+               for (i = 0; i < AID_MAX; i++)
+                       if (capa->add_path[i]) {
+                               json_do_object("add-path-elm");
+                               json_do_printf("family", "%s", aid2str(i));
+                               switch (capa->add_path[i]) {
+                               case CAPA_AP_RECV:
+                                       json_do_printf("mode", "recv");
+                                       break;
+                               case CAPA_AP_SEND:
+                                       json_do_printf("mode", "send");
+                                       break;
+                               case CAPA_AP_BIDIR:
+                                       json_do_printf("mode", "bidir");
+                                       break;
+                               default:
+                                       json_do_printf("mode", "unknown %d",
+                                           capa->add_path[i]);
+                                       break;
+                               }
+                               json_do_end();
+                       }
+               json_do_end();
+       }
 
        json_do_end();
 }
@@ -153,6 +182,22 @@ json_neighbor_stats(struct peer *p)
        json_do_uint("updates", p->stats.prefix_rcvd_update);
        json_do_uint("withdraws", p->stats.prefix_rcvd_withdraw);
        json_do_uint("eor", p->stats.prefix_rcvd_eor);
+       json_do_end();
+
+       json_do_end();
+
+       json_do_object("route-refresh");
+
+       json_do_object("sent");
+       json_do_uint("request", p->stats.refresh_sent_req);
+       json_do_uint("borr", p->stats.refresh_sent_borr);
+       json_do_uint("eorr", p->stats.refresh_sent_eorr);
+       json_do_end();
+
+       json_do_object("received");
+       json_do_uint("request", p->stats.refresh_rcvd_req);
+       json_do_uint("borr", p->stats.refresh_rcvd_borr);
+       json_do_uint("eorr", p->stats.refresh_rcvd_eorr);
        json_do_end();
 
        json_do_end();
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.413
diff -u -p -r1.413 bgpd.h
--- bgpd/bgpd.h 2 Mar 2021 09:45:07 -0000       1.413
+++ bgpd/bgpd.h 18 May 2021 08:41:54 -0000
@@ -352,17 +352,24 @@ struct capabilities {
        int8_t  mp[AID_MAX];            /* multiprotocol extensions, RFC 4760 */
        int8_t  refresh;                /* route refresh, RFC 2918 */
        int8_t  as4byte;                /* 4-byte ASnum, RFC 4893 */
+       int8_t  enhanced_rr;            /* enhanced route refresh, RFC 7313 */
+       int8_t  add_path[AID_MAX];      /* ADD_PATH, RFC 7911 */
 };
 
+/* flags for RFC4724 - graceful restart */
 #define        CAPA_GR_PRESENT         0x01
 #define        CAPA_GR_RESTART         0x02
 #define        CAPA_GR_FORWARD         0x04
 #define        CAPA_GR_RESTARTING      0x08
-
 #define        CAPA_GR_TIMEMASK        0x0fff
 #define        CAPA_GR_R_FLAG          0x8000
 #define        CAPA_GR_F_FLAG          0x80
 
+/* flags for RFC7911 - enhanced router refresh */
+#define        CAPA_AP_RECV            0x01
+#define        CAPA_AP_SEND            0x02
+#define        CAPA_AP_BIDIR           0x03
+
 struct peer_config {
        struct bgpd_addr         remote_addr;
        struct bgpd_addr         local_addr_v4;
@@ -595,7 +602,8 @@ enum err_codes {
        ERR_UPDATE,
        ERR_HOLDTIMEREXPIRED,
        ERR_FSM,
-       ERR_CEASE
+       ERR_CEASE,
+       ERR_RREFRESH
 };
 
 enum suberr_update {
@@ -626,6 +634,10 @@ enum suberr_cease {
        ERR_CEASE_MAX_SENT_PREFIX
 };
 
+enum suberr_rrefresh {
+       ERR_RR_INV_LEN = 1
+};
+
 struct kroute_node;
 struct kroute6_node;
 struct knexthop_node;
@@ -713,6 +725,14 @@ struct session_up {
        u_int16_t               short_as;
 };
 
+struct route_refresh {
+       u_int8_t                aid;
+       u_int8_t                subtype;
+};
+#define ROUTE_REFRESH_REQUEST  0
+#define ROUTE_REFRESH_BEGIN_RR 1
+#define ROUTE_REFRESH_END_RR   2
+
 struct pftable_msg {
        struct bgpd_addr        addr;
        char                    pftable[PFTABLE_LEN];
@@ -1457,7 +1477,8 @@ static const char * const errnames[] = {
        "error in UPDATE message",
        "HoldTimer expired",
        "Finite State Machine error",
-       "Cease"
+       "Cease",
+       "error in ROUTE-REFRESH message"
 };
 
 static const char * const suberr_header_names[] = {
@@ -1514,6 +1535,11 @@ static const char * const suberr_cease_n
        "resource exhaustion",
        "hard reset",
        "sent max-prefix exceeded"
+};
+
+static const char * const suberr_rrefresh_names[] = {
+       "none",
+       "invalid message length"
 };
 
 static const char * const ctl_res_strerror[] = {
Index: bgpd/logmsg.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/logmsg.c,v
retrieving revision 1.4
diff -u -p -r1.4 logmsg.c
--- bgpd/logmsg.c       18 Feb 2019 09:43:57 -0000      1.4
+++ bgpd/logmsg.c       18 May 2021 08:41:54 -0000
@@ -176,6 +176,12 @@ log_notification(const struct peer *peer
                else
                        suberrname = suberr_fsm_names[subcode];
                break;
+       case ERR_RREFRESH:
+               if (subcode >= sizeof(suberr_rrefresh_names)/sizeof(char *))
+                       uk = 1;
+               else
+                       suberrname = suberr_rrefresh_names[subcode];
+               break;
        default:
                logit(LOG_ERR, "%s: %s notification, unknown errcode "
                    "%u, subcode %u", p, dir, errcode, subcode);
Index: bgpd/session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.415
diff -u -p -r1.415 session.c
--- bgpd/session.c      16 May 2021 09:09:11 -0000      1.415
+++ bgpd/session.c      18 May 2021 09:34:52 -0000
@@ -68,7 +68,7 @@ void  session_tcp_established(struct peer
 void   session_capa_ann_none(struct peer *);
 int    session_capa_add(struct ibuf *, u_int8_t, u_int8_t);
 int    session_capa_add_mp(struct ibuf *, u_int8_t);
-int    session_capa_add_gr(struct peer *, struct ibuf *, u_int8_t);
+int    session_capa_add_afi(struct peer *, struct ibuf *, u_int8_t, u_int8_t);
 struct bgp_msg *session_newmsg(enum msg_type, u_int16_t);
 int    session_sendmsg(struct bgp_msg *, struct peer *);
 void   session_open(struct peer *);
@@ -76,7 +76,7 @@ void  session_keepalive(struct peer *);
 void   session_update(u_int32_t, void *, size_t);
 void   session_notification(struct peer *, u_int8_t, u_int8_t, void *,
            ssize_t);
-void   session_rrefresh(struct peer *, u_int8_t);
+void   session_rrefresh(struct peer *, u_int8_t, u_int8_t);
 int    session_graceful_restart(struct peer *);
 int    session_graceful_stop(struct peer *);
 int    session_dispatch_msg(struct pollfd *, struct peer *);
@@ -84,7 +84,7 @@ void  session_process_msg(struct peer *);
 int    parse_header(struct peer *, u_char *, u_int16_t *, u_int8_t *);
 int    parse_open(struct peer *);
 int    parse_update(struct peer *);
-int    parse_refresh(struct peer *);
+int    parse_rrefresh(struct peer *);
 int    parse_notification(struct peer *);
 int    parse_capabilities(struct peer *, u_char *, u_int16_t, u_int32_t *);
 int    capa_neg_calc(struct peer *);
@@ -1329,20 +1329,17 @@ session_capa_add_mp(struct ibuf *buf, u_
 }
 
 int
-session_capa_add_gr(struct peer *p, struct ibuf *b, u_int8_t aid)
+session_capa_add_afi(struct peer *p, struct ibuf *b, u_int8_t aid,
+    u_int8_t flags)
 {
        u_int           errs = 0;
        u_int16_t       afi;
-       u_int8_t        flags, safi;
+       u_int8_t        safi;
 
        if (aid2afi(aid, &afi, &safi)) {
-               log_warn("session_capa_add_gr: bad AID");
+               log_warn("session_capa_add_afi: bad AID");
                return (1);
        }
-       if (p->capa.neg.grestart.flags[aid] & CAPA_GR_RESTARTING)
-               flags = CAPA_GR_F_FLAG;
-       else
-               flags = 0;
 
        afi = htons(afi);
        errs += ibuf_add(b, &afi, sizeof(afi));
@@ -1445,6 +1442,7 @@ session_open(struct peer *p)
                int             rst = 0;
                u_int16_t       hdr;
                u_int8_t        grlen;
+               u_int8_t        flags;
 
                if (mpcapa) {
                        grlen = 2 + 4 * mpcapa;
@@ -1472,15 +1470,25 @@ session_open(struct peer *p)
                if (mpcapa) {
                        for (i = 0; i < AID_MAX; i++) {
                                if (p->capa.ann.mp[i]) {
-                                       errs += session_capa_add_gr(p, opb, i);
+                                       if (p->capa.neg.grestart.flags[i] &
+                                           CAPA_GR_RESTARTING)
+                                               flags = CAPA_GR_F_FLAG;
+                                       else
+                                               flags = 0;
+                                       errs += session_capa_add_afi(p, opb,
+                                           i, flags);
                                }
                        }
                } else {        /* AID_INET */
-                       errs += session_capa_add_gr(p, opb, AID_INET);
+                       if (p->capa.neg.grestart.flags[i] & CAPA_GR_RESTARTING)
+                               flags = CAPA_GR_F_FLAG;
+                       else
+                               flags = 0;
+                       errs += session_capa_add_afi(p, opb, AID_INET, flags);
                }
        }
 
-       /* 4-bytes AS numbers, draft-ietf-idr-as4bytes-13 */
+       /* 4-bytes AS numbers, RFC6793 */
        if (p->capa.ann.as4byte) {      /* 4 bytes data */
                u_int32_t       nas;
 
@@ -1489,6 +1497,32 @@ session_open(struct peer *p)
                errs += ibuf_add(opb, &nas, sizeof(nas));
        }
 
+       /* advertisement of multiple paths, RFC7911 */
+       if (p->capa.ann.add_path[0]) {  /* variable */
+               u_int8_t        aplen;
+
+               if (mpcapa)
+                       aplen = 2 + 4 * mpcapa;
+               else    /* AID_INET */
+                       aplen = 2 + 4;
+               errs += session_capa_add(opb, CAPA_ADD_PATH, aplen);
+               if (mpcapa) {
+                       for (i = AID_MIN; i < AID_MAX; i++) {
+                               if (p->capa.ann.mp[i]) {
+                                       errs += session_capa_add_afi(p, opb,
+                                           i, p->capa.ann.add_path[i]);
+                               }
+                       }
+               } else {        /* AID_INET */
+                       errs += session_capa_add_afi(p, opb, AID_INET,
+                           p->capa.ann.add_path[AID_INET]);
+               }
+       }
+
+       /* enhanced route-refresh, RFC6793 */
+       if (p->capa.ann.enhanced_rr)    /* no data */
+               errs += session_capa_add(opb, CAPA_ENHANCED_RR, 0);
+
        if (ibuf_size(opb))
                optparamlen = ibuf_size(opb) + sizeof(op_type) +
                    sizeof(optparamlen);
@@ -1602,6 +1636,13 @@ session_notification(struct peer *p, u_i
 
        log_notification(p, errcode, subcode, data, datalen, "sending");
 
+       /* cap to maximum size */
+       if (datalen > MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN) {
+               log_peer_warnx(&p->conf,
+                   "oversized notification, data trunkated");
+               datalen = MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN;
+       }
+
        if ((buf = session_newmsg(NOTIFICATION,
            MSGSIZE_NOTIFICATION_MIN + datalen)) == NULL) {
                bgp_fsm(p, EVNT_CON_FATAL);
@@ -1636,24 +1677,42 @@ session_neighbor_rrefresh(struct peer *p
 {
        u_int8_t        i;
 
-       if (!p->capa.neg.refresh)
+       if (!p->capa.neg.refresh && !p->capa.peer.enhanced_rr)
                return (-1);
 
        for (i = 0; i < AID_MAX; i++) {
                if (p->capa.peer.mp[i] != 0)
-                       session_rrefresh(p, i);
+                       session_rrefresh(p, i, ROUTE_REFRESH_REQUEST);
        }
 
        return (0);
 }
 
 void
-session_rrefresh(struct peer *p, u_int8_t aid)
+session_rrefresh(struct peer *p, u_int8_t aid, u_int8_t subtype)
 {
        struct bgp_msg          *buf;
        int                      errs = 0;
        u_int16_t                afi;
-       u_int8_t                 safi, null8 = 0;
+       u_int8_t                 safi;
+
+       switch (subtype) {
+       case ROUTE_REFRESH_REQUEST:
+               p->stats.refresh_sent_req++;
+               break;
+       case ROUTE_REFRESH_BEGIN_RR:
+       case ROUTE_REFRESH_END_RR:
+               /* requires enhanced route refresh */
+               if (!p->capa.neg.enhanced_rr)
+                       return;
+               if (subtype == ROUTE_REFRESH_BEGIN_RR)
+                       p->stats.refresh_sent_borr++;
+               else
+                       p->stats.refresh_sent_eorr++;
+               break;
+       default:
+               fatalx("session_rrefresh: bad subtype %d", subtype);
+       }
 
        if (aid2afi(aid, &afi, &safi) == -1)
                fatalx("session_rrefresh: bad afi/safi pair");
@@ -1665,7 +1724,7 @@ session_rrefresh(struct peer *p, u_int8_
 
        afi = htons(afi);
        errs += ibuf_add(buf->buf, &afi, sizeof(afi));
-       errs += ibuf_add(buf->buf, &null8, sizeof(null8));
+       errs += ibuf_add(buf->buf, &subtype, sizeof(subtype));
        errs += ibuf_add(buf->buf, &safi, sizeof(safi));
 
        if (errs) {
@@ -1886,7 +1945,7 @@ session_process_msg(struct peer *p)
                        p->stats.msg_rcvd_keepalive++;
                        break;
                case RREFRESH:
-                       parse_refresh(p);
+                       parse_rrefresh(p);
                        p->stats.msg_rcvd_rrefresh++;
                        break;
                default:        /* cannot happen */
@@ -1987,7 +2046,7 @@ parse_header(struct peer *peer, u_char *
                }
                break;
        case RREFRESH:
-               if (*len != MSGSIZE_RREFRESH) {
+               if (*len < MSGSIZE_RREFRESH_MIN) {
                        log_peer_warnx(&peer->conf,
                            "received RREFRESH: illegal len: %u byte", *len);
                        session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
@@ -2227,11 +2286,16 @@ parse_update(struct peer *peer)
 }
 
 int
-parse_refresh(struct peer *peer)
+parse_rrefresh(struct peer *peer)
 {
-       u_char          *p;
-       u_int16_t        afi;
-       u_int8_t         aid, safi;
+       u_int16_t afi, datalen;
+       u_int8_t aid, safi, subtype;
+       u_char *p;
+
+       p = peer->rbuf->rptr;
+       p += MSGSIZE_HEADER_MARKER;
+       memcpy(&datalen, p, sizeof(datalen));
+       datalen = ntohs(datalen);
 
        p = peer->rbuf->rptr;
        p += MSGSIZE_HEADER;    /* header is already checked */
@@ -2245,10 +2309,59 @@ parse_refresh(struct peer *peer)
        memcpy(&afi, p, sizeof(afi));
        afi = ntohs(afi);
        p += 2;
-       /* reserved, 1 byte */
+       /* subtype, 1 byte */
+       subtype = *p;
        p += 1;
        /* safi, 1 byte */
-       memcpy(&safi, p, sizeof(safi));
+       safi = *p;
+
+       /* check subtype if peer announced enhanced route refresh */
+       if (peer->capa.neg.enhanced_rr) {
+               switch (subtype) {
+               case ROUTE_REFRESH_REQUEST:
+                       /* no ORF support, so no oversized RREFRESH msgs */
+                       if (datalen != MSGSIZE_RREFRESH) {
+                               log_peer_warnx(&peer->conf,
+                                   "received RREFRESH: illegal len: %u byte",
+                                   datalen);
+                               datalen = htons(datalen);
+                               session_notification(peer, ERR_HEADER,
+                                   ERR_HDR_LEN, &datalen, sizeof(datalen));
+                               bgp_fsm(peer, EVNT_CON_FATAL);
+                               return (-1);
+                       }
+                       peer->stats.refresh_rcvd_req++;
+                       break;
+               case ROUTE_REFRESH_BEGIN_RR:
+               case ROUTE_REFRESH_END_RR:
+                       /* special handling for RFC7313 */
+                       if (datalen != MSGSIZE_RREFRESH) {
+                               log_peer_warnx(&peer->conf,
+                                   "received RREFRESH: illegal len: %u byte",
+                                   datalen);
+                               p = peer->rbuf->rptr;
+                               p += MSGSIZE_HEADER;
+                               datalen -= MSGSIZE_HEADER;
+                               session_notification(peer, ERR_RREFRESH,
+                                   ERR_RR_INV_LEN, p, datalen);
+                               bgp_fsm(peer, EVNT_CON_FATAL);
+                               return (-1);
+                       }
+                       if (subtype == ROUTE_REFRESH_BEGIN_RR)
+                               peer->stats.refresh_rcvd_borr++;
+                       else
+                               peer->stats.refresh_rcvd_eorr++;
+                       break;
+               default:
+                       log_peer_warnx(&peer->conf, "peer sent bad refresh, "
+                           "bad subtype %d", subtype);
+                       return (0);
+               }
+       } else {
+               /* force subtype to default */
+               subtype = ROUTE_REFRESH_REQUEST;
+               peer->stats.refresh_rcvd_req++;
+       }
 
        /* afi/safi unchecked - unrecognized values will be ignored anyway */
        if (afi2aid(afi, safi, &aid) == -1) {
@@ -2257,7 +2370,7 @@ parse_refresh(struct peer *peer)
                return (0);
        }
 
-       if (!peer->capa.neg.refresh) {
+       if (!peer->capa.neg.refresh && !peer->capa.neg.enhanced_rr) {
                log_peer_warnx(&peer->conf, "peer sent unexpected refresh");
                return (0);
        }
@@ -2355,6 +2468,18 @@ parse_notification(struct peer *peer)
                                log_peer_warnx(&peer->conf,
                                    "disabling 4-byte AS num capability");
                                break;
+                       case CAPA_ADD_PATH:
+                               memset(peer->capa.ann.add_path, 0,
+                                   sizeof(peer->capa.ann.add_path));
+                               log_peer_warnx(&peer->conf,
+                                   "disabling ADD-PATH capability");
+                               break;
+                       case CAPA_ENHANCED_RR:
+                               peer->capa.ann.enhanced_rr = 0;
+                               log_peer_warnx(&peer->conf,
+                                   "disabling enhanced route refresh "
+                                   "capability");
+                               break;
                        default:        /* should not happen... */
                                log_peer_warnx(&peer->conf, "received "
                                    "\"unsupported capability\" notification "
@@ -2413,7 +2538,7 @@ parse_capabilities(struct peer *peer, u_
        u_int16_t        gr_header;
        u_int8_t         safi;
        u_int8_t         aid;
-       u_int8_t         gr_flags;
+       u_int8_t         flags;
        u_int8_t         capa_code;
        u_int8_t         capa_len;
        u_int8_t         i;
@@ -2497,7 +2622,8 @@ parse_capabilities(struct peer *peer, u_
                        for (i = 2; i <= capa_len - 4; i += 4) {
                                memcpy(&afi, capa_val + i, sizeof(afi));
                                afi = ntohs(afi);
-                               memcpy(&safi, capa_val + i + 2, sizeof(safi));
+                               safi = capa_val[i + 2];
+                               flags = capa_val[i + 3];
                                if (afi2aid(afi, safi, &aid) == -1) {
                                        log_peer_warnx(&peer->conf,
                                            "Received graceful restart capa: "
@@ -2505,11 +2631,9 @@ parse_capabilities(struct peer *peer, u_
                                            afi, safi);
                                        continue;
                                }
-                               memcpy(&gr_flags, capa_val + i + 3,
-                                   sizeof(gr_flags));
                                peer->capa.peer.grestart.flags[aid] |=
                                    CAPA_GR_PRESENT;
-                               if (gr_flags & CAPA_GR_F_FLAG)
+                               if (flags & CAPA_GR_F_FLAG)
                                        peer->capa.peer.grestart.flags[aid] |=
                                            CAPA_GR_FORWARD;
                                if (gr_header & CAPA_GR_R_FLAG)
@@ -2538,6 +2662,43 @@ parse_capabilities(struct peer *peer, u_
                        }
                        peer->capa.peer.as4byte = 1;
                        break;
+               case CAPA_ADD_PATH:
+                       if (capa_len % 4 != 0) {
+                               log_peer_warnx(&peer->conf,
+                                   "Bad ADD-PATH capability length: "
+                                   "%u", capa_len);
+                               memset(peer->capa.peer.add_path, 0,
+                                   sizeof(peer->capa.peer.add_path));
+                               break;
+                       }
+                       for (i = 0; i <= capa_len - 4; i += 4) {
+                               memcpy(&afi, capa_val + i, sizeof(afi));
+                               afi = ntohs(afi);
+                               safi = capa_val[i + 2];
+                               flags = capa_val[i + 3];
+                               if (afi2aid(afi, safi, &aid) == -1) {
+                                       log_peer_warnx(&peer->conf,
+                                           "Received ADD-PATH capa: "
+                                           " unknown AFI %u, safi %u pair",
+                                           afi, safi);
+                                       memset(peer->capa.peer.add_path, 0,
+                                           sizeof(peer->capa.peer.add_path));
+                                       break;
+                               }
+                               if (flags & ~CAPA_AP_BIDIR) {
+                                       log_peer_warnx(&peer->conf,
+                                           "Received ADD-PATH capa: "
+                                           " bad flags %x", flags);
+                                       memset(peer->capa.peer.add_path, 0,
+                                           sizeof(peer->capa.peer.add_path));
+                                       break;
+                               }
+                               peer->capa.peer.add_path[aid] = flags;
+                       }
+                       break;
+               case CAPA_ENHANCED_RR:
+                       peer->capa.peer.enhanced_rr = 1;
+                       break;
                default:
                        break;
                }
@@ -2555,6 +2716,8 @@ capa_neg_calc(struct peer *p)
 
        p->capa.neg.refresh =
            (p->capa.ann.refresh && p->capa.peer.refresh) != 0;
+       p->capa.neg.enhanced_rr =
+           (p->capa.ann.enhanced_rr && p->capa.peer.enhanced_rr) != 0;
 
        p->capa.neg.as4byte =
            (p->capa.ann.as4byte && p->capa.peer.as4byte) != 0;
@@ -2585,30 +2748,49 @@ capa_neg_calc(struct peer *p)
                int8_t  negflags;
 
                /* disable GR if the AFI/SAFI is not present */
-               if (p->capa.ann.grestart.restart == 0 ||
-                   (p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT &&
+               if ((p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT &&
                    p->capa.neg.mp[i] == 0))
                        p->capa.peer.grestart.flags[i] = 0;     /* disable */
                /* look at current GR state and decide what to do */
                negflags = p->capa.neg.grestart.flags[i];
                p->capa.neg.grestart.flags[i] = p->capa.peer.grestart.flags[i];
                if (negflags & CAPA_GR_RESTARTING) {
-                       if (!(p->capa.peer.grestart.flags[i] &
-                           CAPA_GR_FORWARD)) {
+                       if (p->capa.ann.grestart.restart != 0 && 
+                           p->capa.peer.grestart.flags[i] & CAPA_GR_FORWARD) {
+                               p->capa.neg.grestart.flags[i] |=
+                                   CAPA_GR_RESTARTING;
+                       } else {
                                if (imsg_rde(IMSG_SESSION_FLUSH, p->conf.id,
                                    &i, sizeof(i)) == -1)
                                        return (-1);
                                log_peer_warnx(&p->conf, "graceful restart of "
                                    "%s, not restarted, flushing", aid2str(i));
-                       } else
-                               p->capa.neg.grestart.flags[i] |=
-                                   CAPA_GR_RESTARTING;
+                       }
                }
        }
        p->capa.neg.grestart.timeout = p->capa.peer.grestart.timeout;
        p->capa.neg.grestart.restart = p->capa.peer.grestart.restart;
        if (p->capa.ann.grestart.restart == 0)
                p->capa.neg.grestart.restart = 0;
+
+
+       /*
+        * ADD-PATH: set only those bits where both sides agree.
+        * For this compare our send bit with the recv bit from the peer
+        * and vice versa.
+        * The flags are stored from this systems view point.
+        */
+       memset(p->capa.neg.add_path, 0, sizeof(p->capa.neg.add_path));
+       if (p->capa.ann.add_path[0]) {
+               for (i = AID_MIN; i < AID_MAX; i++) {
+                       if ((p->capa.ann.add_path[i] & CAPA_AP_RECV) &&
+                           (p->capa.peer.add_path[i] & CAPA_AP_SEND))
+                               p->capa.neg.add_path[i] |= CAPA_AP_RECV;
+                       if ((p->capa.ann.add_path[i] & CAPA_AP_SEND) &&
+                           (p->capa.peer.add_path[i] & CAPA_AP_RECV))
+                               p->capa.neg.add_path[i] |= CAPA_AP_SEND;
+               }
+       }
 
        return (0);
 }
Index: bgpd/session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.150
diff -u -p -r1.150 session.h
--- bgpd/session.h      16 Feb 2021 08:29:16 -0000      1.150
+++ bgpd/session.h      18 May 2021 08:41:54 -0000
@@ -34,7 +34,8 @@
 #define        MSGSIZE_OPEN_MIN                29
 #define        MSGSIZE_UPDATE_MIN              23
 #define        MSGSIZE_KEEPALIVE               MSGSIZE_HEADER
-#define        MSGSIZE_RREFRESH                MSGSIZE_HEADER + 4
+#define        MSGSIZE_RREFRESH                (MSGSIZE_HEADER + 4)
+#define        MSGSIZE_RREFRESH_MIN            MSGSIZE_RREFRESH
 #define        MSG_PROCESS_LIMIT               25
 #define        SESSION_CLEAR_DELAY             5
 
@@ -106,11 +107,13 @@ enum opt_params {
 };
 
 enum capa_codes {
-       CAPA_NONE,
-       CAPA_MP,
-       CAPA_REFRESH,
+       CAPA_NONE = 0,
+       CAPA_MP = 1,
+       CAPA_REFRESH = 2,
        CAPA_RESTART = 64,
-       CAPA_AS4BYTE = 65
+       CAPA_AS4BYTE = 65,
+       CAPA_ADD_PATH = 69,
+       CAPA_ENHANCED_RR = 70,
 };
 
 struct bgp_msg {
@@ -158,6 +161,12 @@ struct peer_stats {
        unsigned long long       msg_sent_notification;
        unsigned long long       msg_sent_keepalive;
        unsigned long long       msg_sent_rrefresh;
+       unsigned long long       refresh_rcvd_req;
+       unsigned long long       refresh_rcvd_borr;
+       unsigned long long       refresh_rcvd_eorr;
+       unsigned long long       refresh_sent_req;
+       unsigned long long       refresh_sent_borr;
+       unsigned long long       refresh_sent_eorr;
        unsigned long long       prefix_rcvd_update;
        unsigned long long       prefix_rcvd_withdraw;
        unsigned long long       prefix_rcvd_eor;

Reply via email to