summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2025-01-14 17:57:45 +0100
committerDonatas Abraitis <donatas@opensourcerouting.org>2025-01-14 21:46:53 +0100
commitd60320c6d27e476f9e351bc5f1470197fa46623b (patch)
tree3bb37bd4a35b6fc026bd814d3224d818c795e121
parenttests: Check if ENHE capability can be handled dynamically (diff)
downloadfrr-d60320c6d27e476f9e351bc5f1470197fa46623b.tar.xz
frr-d60320c6d27e476f9e351bc5f1470197fa46623b.zip
bgpd: Handle ENHE capability via dynamic capability
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>
-rw-r--r--bgpd/bgp_packet.c110
-rw-r--r--bgpd/bgp_packet.h6
-rw-r--r--bgpd/bgp_vty.c17
-rw-r--r--bgpd/bgpd.c4
4 files changed, 131 insertions, 6 deletions
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index c5e390b04..ca2e8de04 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -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;
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index c266b1726..866b8f617 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -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),
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 56b06106f..30b633416 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -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 */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 02333db1c..977980dc4 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -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);
}