]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: Handle ENHE capability via dynamic capability 17855/head
authorDonatas Abraitis <donatas@opensourcerouting.org>
Tue, 14 Jan 2025 16:57:45 +0000 (18:57 +0200)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Tue, 14 Jan 2025 20:46:53 +0000 (22:46 +0200)
FRR supports dynamic capability which is useful to exchange the capabilities
without tearing down the session. ENHE capability was missed to be included
handling via dynamic capability. Let's add it too.

This was missed and asked in Slack that it would be useful.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
bgpd/bgp_packet.c
bgpd/bgp_packet.h
bgpd/bgp_vty.c
bgpd/bgpd.c

index c5e390b04564b1981b2df1a6e0c2c0458e38d470..ca2e8de041739372fce1b7eb7cd36fb11d1534f0 100644 (file)
@@ -1620,9 +1620,38 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
        case CAPABILITY_CODE_AS4:
        case CAPABILITY_CODE_DYNAMIC:
        case CAPABILITY_CODE_ENHANCED_RR:
-       case CAPABILITY_CODE_ENHE:
        case CAPABILITY_CODE_EXT_MESSAGE:
                break;
+       case CAPABILITY_CODE_ENHE:
+               FOREACH_AFI_SAFI (afi, safi) {
+                       if (!peer->afc[afi][safi])
+                               continue;
+
+                       bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+                       if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) &&
+                           peer->connection->su.sa.sa_family == AF_INET6 && afi == AFI_IP &&
+                           (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN ||
+                            safi == SAFI_LABELED_UNICAST)) {
+                               stream_putc(s, action);
+                               stream_putc(s, CAPABILITY_CODE_ENHE);
+                               stream_putc(s, CAPABILITY_CODE_ENHE_LEN);
+                               stream_putw(s, pkt_afi);
+                               stream_putw(s, pkt_safi);
+                               stream_putw(s, afi_int2iana(AFI_IP6));
+
+                               COND_FLAG(peer->af_cap[AFI_IP][safi], PEER_CAP_ENHE_AF_ADV,
+                                         action == CAPABILITY_ACTION_SET);
+
+                               if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV))
+                                       COND_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_NEGO,
+                                                 action == CAPABILITY_ACTION_SET);
+                       }
+               }
+               COND_FLAG(peer->cap, PEER_CAP_ENHE_ADV, action == CAPABILITY_ACTION_SET);
+               update_group_adjust_peer_afs(peer);
+               bgp_announce_route_all(peer);
+               break;
        case CAPABILITY_CODE_ROLE:
                stream_putc(s, action);
                stream_putc(s, CAPABILITY_CODE_ROLE);
@@ -3321,6 +3350,81 @@ ignore:
        }
 }
 
+static void bgp_dynamic_capability_enhe(uint8_t *pnt, int action, struct capability_header *hdr,
+                                       struct peer *peer)
+{
+       uint8_t *data = pnt + 3;
+       uint8_t *end = data + hdr->length;
+       size_t len = end - data;
+
+       if (data + CAPABILITY_CODE_ENHE_LEN > end) {
+               flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+                         "Extended NH: Received invalid length %zu, less than %d", len,
+                         CAPABILITY_CODE_ENHE_LEN);
+               return;
+       }
+
+       if (action == CAPABILITY_ACTION_SET) {
+               if (hdr->length % CAPABILITY_CODE_ENHE_LEN) {
+                       flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+                                 "Extended NH: Received invalid length %d, non-multiple of %d",
+                                 hdr->length, CAPABILITY_CODE_ENHE_LEN);
+                       return;
+               }
+
+               while (data + CAPABILITY_CODE_ENHE_LEN <= end) {
+                       afi_t afi;
+                       safi_t safi;
+                       afi_t nh_afi;
+                       struct bgp_enhe_capability bec = {};
+
+                       memcpy(&bec, data, sizeof(bec));
+                       afi = ntohs(bec.afi);
+                       safi = ntohs(bec.safi);
+                       nh_afi = afi_iana2int(ntohs(bec.nh_afi));
+
+                       /* RFC 5549 specifies use of this capability only for IPv4 AFI,
+                        * with the Nexthop AFI being IPv6. A future spec may introduce
+                        * other possibilities, so we ignore other values with a log.
+                        * Also, only SAFI_UNICAST and SAFI_LABELED_UNICAST are currently
+                        * supported (and expected).
+                        */
+                       if (afi != AFI_IP || nh_afi != AFI_IP6 ||
+                           !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN ||
+                             safi == SAFI_LABELED_UNICAST)) {
+                               flog_warn(EC_BGP_CAPABILITY_INVALID_DATA,
+                                         "%s Unexpected afi/safi/next-hop afi: %s/%s/%u in Extended Next-hop capability, ignoring",
+                                         peer->host, afi2str(afi), safi2str(safi), nh_afi);
+                               goto ignore;
+                       }
+
+                       SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV);
+
+                       if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_ADV))
+                               SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_NEGO);
+
+ignore:
+                       data += CAPABILITY_CODE_ENHE_LEN;
+               }
+
+               SET_FLAG(peer->cap, PEER_CAP_ENHE_RCV);
+               update_group_adjust_peer_afs(peer);
+               bgp_announce_route_all(peer);
+       } else {
+               afi_t afi;
+               safi_t safi;
+
+               UNSET_FLAG(peer->cap, PEER_CAP_ENHE_RCV);
+
+               FOREACH_AFI_SAFI (afi, safi) {
+                       UNSET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV);
+
+                       if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_ADV))
+                               UNSET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_NEGO);
+               }
+       }
+}
+
 static void bgp_dynamic_capability_orf(uint8_t *pnt, int action,
                                       struct capability_header *hdr,
                                       struct peer *peer)
@@ -3943,9 +4047,11 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
                case CAPABILITY_CODE_AS4:
                case CAPABILITY_CODE_DYNAMIC:
                case CAPABILITY_CODE_ENHANCED_RR:
-               case CAPABILITY_CODE_ENHE:
                case CAPABILITY_CODE_EXT_MESSAGE:
                        break;
+               case CAPABILITY_CODE_ENHE:
+                       bgp_dynamic_capability_enhe(pnt, action, hdr, peer);
+                       break;
                case CAPABILITY_CODE_ROLE:
                        bgp_dynamic_capability_role(pnt, action, peer);
                        break;
index c266b17266ec9c89625d550612d9bb9dd52ea488..866b8f617dbbb9ebbc81ce97a51b3ddae88e7fef 100644 (file)
@@ -8,6 +8,12 @@
 
 #include "hook.h"
 
+struct bgp_enhe_capability {
+       uint16_t afi;
+       uint16_t safi;
+       uint16_t nh_afi;
+};
+
 DECLARE_HOOK(bgp_packet_dump,
                (struct peer *peer, uint8_t type, bgp_size_t size,
                        struct stream *s),
index 56b06106f3341e02cd8832ddf93c3d5dd395a2f8..30b633416b68b77166b6bc8c2728bf1263932a5d 100644 (file)
@@ -5977,13 +5977,17 @@ DEFUN (neighbor_capability_enhe,
 {
        int idx_peer = 1;
        struct peer *peer;
+       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (peer && peer->conf_if)
                return CMD_SUCCESS;
 
-       return peer_flag_set_vty(vty, argv[idx_peer]->arg,
-                                PEER_FLAG_CAPABILITY_ENHE);
+       ret = peer_flag_set_vty(vty, argv[idx_peer]->arg, PEER_FLAG_CAPABILITY_ENHE);
+
+       bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, CAPABILITY_CODE_ENHE, CAPABILITY_ACTION_SET);
+
+       return ret;
 }
 
 DEFUN (no_neighbor_capability_enhe,
@@ -5997,6 +6001,7 @@ DEFUN (no_neighbor_capability_enhe,
 {
        int idx_peer = 2;
        struct peer *peer;
+       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (peer && peer->conf_if) {
@@ -6006,8 +6011,12 @@ DEFUN (no_neighbor_capability_enhe,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
-                                  PEER_FLAG_CAPABILITY_ENHE);
+       ret = peer_flag_unset_vty(vty, argv[idx_peer]->arg, PEER_FLAG_CAPABILITY_ENHE);
+
+       bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, CAPABILITY_CODE_ENHE,
+                           CAPABILITY_ACTION_UNSET);
+
+       return ret;
 }
 
 /* neighbor capability software-version */
index 02333db1c267c9dbdbf86bd968f69aea45e0a772..977980dc49ea377fc88b20006c48dede35b208ae 100644 (file)
@@ -4953,6 +4953,10 @@ static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
                        peer->v_start = BGP_INIT_START_TIMER;
                        BGP_EVENT_ADD(peer->connection, BGP_Stop);
                }
+       } else if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
+                  CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV) &&
+                  flag == PEER_FLAG_CAPABILITY_ENHE) {
+               peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
        } else if (!peer_notify_config_change(peer->connection))
                bgp_session_reset(peer);
 }