diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 13da55ffb7..4a1f5959fc 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1598,6 +1598,15 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi, return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]); } + if (is_bgp_static_route && pi->nexthop->nexthop->type == NEXTHOP_TYPE_IFINDEX) { + /* "network" imported prefixes from vrf + * fallback to per-vrf label. + */ + + bgp_mplsvpn_path_nh_label_unlink(pi); + return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]); + } + return _vpn_leak_from_vrf_get_per_nexthop_label(pi, to_bgp, from_bgp, afi); } diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py index 146687d588..104ba85be5 100644 --- a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -347,10 +347,37 @@ def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): return topotest.json_cmp(output, expected) -def check_show_mpls_table_entry_label_found(router, inlabel, interface): - output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) +def check_show_mpls_table_entry_label_found_nexthop( + expected_router, get_router, network, nexthop +): + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) expected = { - "inLabel": inlabel, + "inLabel": label, + "installed": True, + "nexthops": [{"nexthop": nexthop}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found( + expected_router, get_router, interface, network=None, label=None +): + if not label: + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) + expected = { + "inLabel": label, "installed": True, "nexthops": [{"interface": interface}], } @@ -395,6 +422,17 @@ def mpls_entry_get_interface(router, label): return outgoing_interface +def get_mpls_label(router, network): + label = router.vtysh_cmd( + "show ip route vrf vrf1 %s json" % network, + isjson=True, + ) + label = label.get(f"{network}", [{}])[0].get("nexthops", [{}])[0] + label = int(label.get("labels", [-1])[0]) + + return label + + def test_protocols_convergence(): """ Assert that all protocols have converged @@ -719,7 +757,11 @@ def test_changing_default_label_value(): "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" ) test_func = functools.partial( - check_show_mpls_table_entry_label_found, router, 222, "vrf1" + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + router, + "vrf1", + label=222, ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "r1, mpls entry with label 222 not found" @@ -837,6 +879,126 @@ def test_reconfigure_allocation_mode_nexthop(): mpls_table_check(router, label_list=label_list) +def test_network_command(): + """ + Test with network declaration + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Disabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno redistribute static\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + for p in ["172.31.0.24/32", "172.31.0.15/32"]: + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, tgen.gears["r2"], "ipv4", p, "444:1" + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + + logger.info("Use network command for host networks declared in static instead") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.14/32\n", + isjson=False, + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.15/32\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + bgp_vpnv4_table_check(tgen.gears["r2"], group=["172.31.0.12/32", "172.31.0.15/32"]) + bgp_vpnv4_table_check(tgen.gears["r2"], group=["172.31.0.14/32"]) + mpls_table_check(tgen.gears["r1"], whitelist=["192.0.2.14"]) + + logger.info(" Remove network to 172.31.0.14/32") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno network 172.31.0.14/32\n", + isjson=False, + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.14/32", + "444:1", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network 172.31.0.14/32 should not present on r2" + mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.14"]) + + logger.info("Disabling redistribute connected and enabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "redistribute static\n no redistribute connected", + isjson=False, + ) + logger.info("Use network command for connect network 192.168.255.0/24 in vrf") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "network 192.168.255.0/24\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + bgp_vpnv4_table_check(tgen.gears["r2"], group=["192.168.255.0/24"]) + logger.info("Checking no mpls entry associated to 192.168.255.0/24") + mpls_table_check(tgen.gears["r1"], blacklist=["192.168.255.0"]) + logger.info("Checking 192.168.255.0/24 fallback to vrf") + test_func = functools.partial( + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + tgen.gears["r2"], + "vrf1", + network="192.168.255.0/24", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "192.168.255.0/24 does not fallback to vrf" + + logger.info( + "Use network command for statically routed network 192.168.3.0/24 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\n" "ip route 192.168.3.0/24 192.0.2.11\n" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "network 192.168.3.0/24\n", + isjson=False, + ) + logger.info("Checking 192.168.3.0/24 route on r2") + test_func = functools.partial( + check_show_mpls_table_entry_label_found_nexthop, + tgen.gears["r1"], + tgen.gears["r2"], + "192.168.3.0/24", + "192.0.2.11", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "label from r2 is not present on r1 for 192.168.3.0/24" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping bgp network import-check-table") + tgen.gears["r1"].vtysh_cmd( + "show bgp vrf vrf1 import-check-table detail", isjson=False + ) + + logger.info("Restoring 172.31.0.14 prefix on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.14/32\n", + isjson=False, + ) + logger.info("Restoring redistribute connected") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "no redistribute static\n redistribute connected", + isjson=False, + ) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py index 5fef6e9e60..edee8176a4 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -342,10 +342,37 @@ def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): return topotest.json_cmp(output, expected) -def check_show_mpls_table_entry_label_found(router, inlabel, interface): - output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) +def check_show_mpls_table_entry_label_found_nexthop( + expected_router, get_router, network, nexthop +): + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) expected = { - "inLabel": inlabel, + "inLabel": label, + "installed": True, + "nexthops": [{"nexthop": nexthop}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found( + expected_router, get_router, interface, network=None, label=None +): + if not label: + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) + expected = { + "inLabel": label, "installed": True, "nexthops": [{"interface": interface}], } @@ -366,6 +393,17 @@ def get_table_version(router): return table["tableVersion"] +def get_mpls_label(router, network): + label = router.vtysh_cmd( + "show ipv6 route vrf vrf1 %s json" % network, + isjson=True, + ) + label = label.get(f"{network}", [{}])[0].get("nexthops", [{}])[0] + label = int(label.get("labels", [-1])[0]) + + return label + + def mpls_entry_get_interface(router, label): """ Assert that the label is in MPLS table @@ -724,7 +762,11 @@ def test_changing_default_label_value(): "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" ) test_func = functools.partial( - check_show_mpls_table_entry_label_found, router, 222, "vrf1" + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + router, + "vrf1", + label=222, ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "r1, mpls entry with label 222 not found" @@ -837,6 +879,133 @@ def test_reconfigure_allocation_mode_nexthop(): mpls_table_check(router, label_list=label_list) +def test_network_command(): + """ + Test with network declaration + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Disabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno redistribute static\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + for p in ["172:31::14/128", "172:31::15/128"]: + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, tgen.gears["r2"], "ipv6", p, "444:1" + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + + logger.info("Use network command for host networks declared in static instead") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::14/128\n", + isjson=False, + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::15/128\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + # 172:31::12 uses LL as nexthop + # 172:31::16 uses GA as nexthop + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::12/128"]) + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::15/128"]) + + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::14/128"]) + mpls_table_check(tgen.gears["r1"], whitelist=["192:2::14"]) + + logger.info(" Remove network to 172:31::14/128") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno network 172:31::14/128\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv6", + "172:31::14/128", + "444:1", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + mpls_table_check(tgen.gears["r1"], blacklist=["192:2::14"]) + + logger.info("Disabling redistribute connected and enabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "redistribute static\n no redistribute connected", + isjson=False, + ) + logger.info( + "Use network command for connected host network 192:168::255:0/112 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "network 192:168::255:0/112\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + bgp_vpnv6_table_check(tgen.gears["r2"], group=["192:168::255:0/112"]) + logger.info("Checking no mpls entry associated to 192:168::255:0/112") + mpls_table_check(tgen.gears["r1"], blacklist=["192:168:255::0"]) + logger.info("Checking 192:168::255:0/112 fallback to vrf") + test_func = functools.partial( + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + tgen.gears["r2"], + "vrf1", + network="192:168::255:0/112", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "192:168::255:0/112 does not fallback to vrf" + + logger.info( + "Use network command for statically routed network 192:168::3:0/112 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\n" "ipv6 route 192:168::3:0/112 192:2::11\n" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "network 192:168::3:0/112\n", + isjson=False, + ) + logger.info("Checking 192:168::3:0/112 route on r2") + test_func = functools.partial( + check_show_mpls_table_entry_label_found_nexthop, + tgen.gears["r1"], + tgen.gears["r2"], + "192:168::3:0/112", + "192:2::11", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "label from r2 is not present on r1 for 192:168::3:0/112" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping bgp network import-check-table") + tgen.gears["r1"].vtysh_cmd( + "show bgp vrf vrf1 import-check-table detail", isjson=False + ) + + logger.info("Restoring 172:31::14 prefix on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::14/128\n", + isjson=False, + ) + logger.info("Restoring redistribute connected") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "no redistribute static\n redistribute connected", + isjson=False, + ) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen()