diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 85cc8f3596b6..b96c287f8600 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -4079,6 +4079,35 @@ void bgp_vpn_leak_export(struct bgp *from_bgp) } } +/* It releases the label from labelpool which + * was previously assigned and unsets the flag based on reset arg + * This also used in vty to release the label and to change the allocation mode as well + */ +void bgp_vpn_release_label(struct bgp *bgp, afi_t afi, bool reset) +{ + if (!CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) + return; + /* + * label has previously been automatically + * assigned by labelpool: release it + * + * NB if tovpn_label == MPLS_LABEL_NONE it + * means the automatic assignment is in flight + * and therefore the labelpool callback must + * detect that the auto label is not needed. + */ + if (bgp->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE) + return; + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) + return; + + bgp_lp_release(LP_TYPE_VRF, &bgp->vpn_policy[afi], bgp->vpn_policy[afi].tovpn_label); + bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; + + if (reset) + UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO); +} + /* The nexthops values are compared to * find in the tree the appropriate cache entry */ diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 39fed667818a..18639fc69b23 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -352,6 +352,7 @@ extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, bool is_config); extern void bgp_vpn_leak_unimport(struct bgp *from_bgp); extern void bgp_vpn_leak_export(struct bgp *from_bgp); +extern void bgp_vpn_release_label(struct bgp *bgp, afi_t afi, bool reset); extern bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi); extern int diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 22fbb1bb21cd..e336b54eb46d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9948,26 +9948,9 @@ DEFPY (af_label_vpn_export, UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_MANUAL_REG); - } else if (CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + } else if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) /* release any previous auto label */ - if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { - - /* - * label has previously been automatically - * assigned by labelpool: release it - * - * NB if tovpn_label == MPLS_LABEL_NONE it - * means the automatic assignment is in flight - * and therefore the labelpool callback must - * detect that the auto label is not needed. - */ - - bgp_lp_release(LP_TYPE_VRF, - &bgp->vpn_policy[afi], - bgp->vpn_policy[afi].tovpn_label); - } - } + bgp_vpn_release_label(bgp, afi, false); if (yes) { if (label_auto) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a244e15fb8ec..74f79cdd72d6 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4037,6 +4037,9 @@ int bgp_delete(struct bgp *bgp) bgp_vpn_leak_unimport(bgp); + bgp_vpn_release_label(bgp, AFI_IP, true); + bgp_vpn_release_label(bgp, AFI_IP6, true); + hook_call(bgp_inst_delete, bgp); FOREACH_AFI_SAFI (afi, safi) diff --git a/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/__init__.py b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r1/frr.conf b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r1/frr.conf new file mode 100644 index 000000000000..7daf335aabc7 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r1/frr.conf @@ -0,0 +1,117 @@ +interface r1-eth1 vrf vrf1 + ip address 173.31.1.1/32 +! +interface r1-eth2 vrf vrf2 + ip address 173.31.1.2/32 +! +interface r1-eth3 vrf vrf3 + ip address 173.31.1.3/32 +! +interface r1-eth4 vrf vrf4 + ip address 173.31.1.4/32 +! +interface r1-eth5 vrf vrf5 + ip address 173.31.1.5/32 +! + +interface r1-eth0 + ip address 192.168.0.1/24 +! + +interface r1-eth6 + ip address 193.170.0.1/24 + +interface lo + ip address 11.11.11.11/32 +! +router ospf + ospf router-id 11.11.11.11 + network 193.170.0.0/24 area 0.0.0.0 + network 11.11.11.11/32 area 0.0.0.0 + redistribute connected +exit +! +mpls ldp + router-id 11.11.11.11 + ! + address-family ipv4 + discovery transport-address 11.11.11.11 + ! + interface r1-eth6 + exit + ! + exit-address-family + ! +exit +! +bgp route-map delay-timer 1 +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + address-family ipv4 unicast + no neighbor 192.168.0.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.2 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:1 + rt vpn both 53:1 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf2 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:2 + rt vpn both 53:2 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf3 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:3 + rt vpn both 53:3 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf4 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:4 + rt vpn both 53:4 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf5 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:5 + rt vpn both 53:5 + export vpn + import vpn + exit-address-family +! + +interface r1-eth0 + mpls bgp forwarding +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r2/frr.conf b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r2/frr.conf new file mode 100644 index 000000000000..6facebe40e05 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r2/frr.conf @@ -0,0 +1,88 @@ +interface r2-eth1 vrf vrf1 + ip address 173.31.0.1/32 +! +interface r2-eth2 vrf vrf2 + ip address 173.31.0.2/32 +! +interface r2-eth3 vrf vrf3 + ip address 173.31.0.3/32 +! +interface r2-eth4 vrf vrf4 + ip address 173.31.0.4/32 +! +interface r2-eth5 vrf vrf5 + ip address 173.31.0.5/32 +! +interface r2-eth0 + ip address 192.168.0.2/24 +! +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:1 + rt vpn both 53:1 + export vpn + import vpn + exit-address-family +! +router bgp 65501 vrf vrf2 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:2 + rt vpn both 53:2 + export vpn + import vpn + exit-address-family +! +router bgp 65501 vrf vrf3 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:3 + rt vpn both 53:3 + export vpn + import vpn + exit-address-family +! +router bgp 65501 vrf vrf4 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:4 + rt vpn both 53:4 + export vpn + import vpn + exit-address-family +! +router bgp 65501 vrf vrf5 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export auto + rd vpn export 445:5 + rt vpn both 53:5 + export vpn + import vpn + exit-address-family +! + +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r3/frr.conf b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r3/frr.conf new file mode 100644 index 000000000000..8f49cdfe0c85 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/r3/frr.conf @@ -0,0 +1,32 @@ +interface r3-eth0 + ip address 193.170.0.2/24 +! +interface lo + ip address 33.33.33.33/32 +! +interface r3-eth1 + ip address 180.170.0.2/32 +! +interface r3-eth2 + ip address 180.170.0.3/32 +! +router ospf + ospf router-id 33.33.33.33 + network 193.170.0.0/24 area 0.0.0.0 + network 33.33.33.33/32 area 0.0.0.0 + redistribute connected +exit +! +mpls ldp + router-id 33.33.33.33 + ! + address-family ipv4 + discovery transport-address 33.33.33.33 + ! + interface r3-eth0 + exit + ! + exit-address-family + ! +exit +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/test_bgp_vpnv4_vpn_auto.py b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/test_bgp_vpnv4_vpn_auto.py new file mode 100644 index 000000000000..ed3cdca2f93c --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp_vpn_auto/test_bgp_vpnv4_vpn_auto.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_vpn_auto.py +# +# Copyright (c) 2024 by Varun Hegde +# + +""" + test_bgp_vpnv4_vpn_auto.py: Test the FRR BGP daemon with BGP VPN session with label export auto +""" + +import os +import sys +import json +import functools +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.bgpcheck import ( + check_show_bgp_vpn_prefix_found, + check_show_bgp_vpn_prefix_not_found, +) +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 3 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + + + for i in range(6): + switch = tgen.add_switch("s{0}".format(i)) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + #create a singiluar link between R2 -- R3 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + for i in range(7, 9): + switch = tgen.add_switch("s{0}".format(i)) + switch.add_link(tgen.gears["r3"]) + + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf{} type vrf table {}", + "ip link set dev vrf{} up", + "ip link set dev r1-eth{} master vrf{}", + "echo 1 > /proc/sys/net/mpls/conf/r1-eth{}/input", + ] + cmds_list2 = [ + "ip link add vrf{} type vrf table {}", + "ip link set dev vrf{} up", + "ip link set dev r2-eth{} master vrf{}", + "echo 1 > /proc/sys/net/mpls/conf/r2-eth{}/input", + ] + + for i in range(1, 6): + for cmd in cmds_list: + input = cmd.format(i, i) + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format(i, i)) + logger.info("output: " + output) + + for cmd in cmds_list2: + input = cmd.format(i, i) + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format(i, i)) + logger.info("output: " + output) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_labelpool_release(): + """ + Check that once we remove BGP VPN sesson + label pool structure ( allocated_map ) gets released properly or not + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Just waiting for BGP VPN session to converge + logger.info("Waiting for BGP VPN sessions to converge and label pools to get initialised") + router = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + router.vtysh_cmd("show bgp labelpool summary json") + ) + expected = {"ledger":5,"inUse":5,"requests":0,"labelChunks":1,"pending":0,"reconnects":1} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to see BGP Labelpool initialised" + + + # checking the initial label pool chunk's free labels + logger.info("checking the initial label pool chunk's free labels") + expected = [{"first":80,"last":207,"size":128,"numberFree":123}] + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show bgp label chunks json", + expected, + ) + + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + + # Test case : check whether label got released or not + logger.info( + "Remove multiple vpn session and check whether label got released or no" + ) + router.vtysh_cmd( + """ + configure terminal + no router bgp 65500 vrf vrf1 + no router bgp 65500 vrf vrf2 + """ + ) + expected = [{"first":80,"last":207,"size":128,"numberFree":125}] + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show bgp label chunks json", + expected, + ) + + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))