Commit dacf6806f5 for frr
commit dacf6806f52679152ad176ee44eebed6aba8499b
Author: Donatas Abraitis <donatas@opensourcerouting.org>
Date: Wed Oct 28 16:40:30 2026 +0200
bgpd: Honor PATHS-LIMIT capability's limits received via dynamic capability
[afi][safi].received never reset when the session goes down or when the dynamic
capability removed. An old/stale limit can be applied...
Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 8f390e6adf..faf3578124 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -2346,6 +2346,7 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection)
/* peer address family capability flags*/
peer->af_cap[afi][safi] = 0;
+ peer->addpath_paths_limit[afi][safi].receive = 0;
/* peer address family status flags*/
/* Don't clear WAIT_EOR */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 8053861052..f20dcf53dc 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -3321,12 +3321,17 @@ ignore:
data += CAPABILITY_CODE_PATHS_LIMIT_LEN;
}
} else {
- FOREACH_AFI_SAFI (afi, safi)
+ FOREACH_AFI_SAFI (afi, safi) {
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_PATHS_LIMIT_AF_RCV);
+ peer->addpath_paths_limit[afi][safi].receive = 0;
+ }
UNSET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
}
+
+ update_group_adjust_peer_afs(peer);
+ bgp_announce_route_all(peer);
}
static void bgp_dynamic_capability_enhe(uint8_t *pnt, int action, struct capability_header *hdr,
diff --git a/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf
index 796b4d0ba7..98be901ef4 100644
--- a/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf
+++ b/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf
@@ -14,6 +14,7 @@ router bgp 65002
neighbor 192.168.1.1 remote-as external
neighbor 192.168.7.7 remote-as external
neighbor 192.168.7.7 timers connect 5
+ neighbor 192.168.7.7 capability dynamic
neighbor 192.168.2.3 remote-as external
neighbor 192.168.2.3 timers connect 5
neighbor 192.168.2.3 weight 3
diff --git a/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf
index 8c44566b2f..54f2b632ee 100644
--- a/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf
+++ b/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf
@@ -7,6 +7,7 @@ router bgp 65007
no bgp ebgp-requires-policy
neighbor 192.168.7.2 remote-as external
neighbor 192.168.7.2 timers connect 5
+ neighbor 192.168.7.2 capability dynamic
address-family ipv4 unicast
neighbor 192.168.7.2 addpath-rx-paths-limit 3
exit-address-family
diff --git a/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py b/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py
index f342971e1a..37981c1bff 100644
--- a/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py
+++ b/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py
@@ -338,6 +338,74 @@ def test_bgp_addpath_paths_limit_best_selected_filtered():
assert result is None, "r1 did not receive the two best paths: {}".format(result)
+def test_bgp_addpath_paths_limit_dynamic_capability():
+ """
+ Paths-Limit changed via dynamic capability must be honored without reset.
+ """
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r7 = tgen.gears["r7"]
+
+ def _set_paths_limit(limit):
+ if limit:
+ cmd = "neighbor 192.168.7.2 addpath-rx-paths-limit {}".format(limit)
+ else:
+ cmd = "no neighbor 192.168.7.2 addpath-rx-paths-limit"
+
+ r7.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65007
+ address-family ipv4 unicast
+ {}
+ """.format(
+ cmd
+ )
+ )
+
+ def _bgp_check_paths_limit(limit, aspaths):
+ output = json.loads(r2.vtysh_cmd("show bgp neighbor 192.168.7.7 json"))
+ expected = {
+ "192.168.7.7": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "pathsLimit": {"ipv4Unicast": {"receivedPathsLimit": limit}},
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "notificationsSent": 0,
+ },
+ }
+ }
+ result = topotest.json_cmp(output, expected)
+ if result is not None:
+ return result
+
+ return _bgp_check_received_aspaths(r7, aspaths)
+
+ r2.vtysh_cmd("clear bgp 192.168.7.7 message-stats")
+
+ steps = [
+ (1, ["65002 65006"]),
+ (0, ["65002 65006", "65002 65005", "65002 65004", "65002 65003"]),
+ (3, ["65002 65006", "65002 65005", "65002 65004"]),
+ ]
+
+ for limit, aspaths in steps:
+ _set_paths_limit(limit)
+
+ test_func = functools.partial(_bgp_check_paths_limit, limit, aspaths)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Dynamic Paths-Limit {} is not honored: {}".format(
+ limit, result
+ )
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))