diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf new file mode 100644 index 000000000000..d3ababde3a81 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf @@ -0,0 +1,75 @@ +int dummy0 + ip address 10.0.4.1/24 + no shut +! +int dummy1 + ip address 10.0.0.1/24 + no shut +! +int dummy2 + ip address 10.0.1.1/24 + no shut +! +int dummy3 + ip address 10.0.2.1/24 + no shut +! +int dummy4 + ip address 10.0.3.1/24 + no shut +! +int EVA + no shut +! +int DONNA + no shut +! +ip router-id 10.0.4.1 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + rd vpn export 10.0.4.1:1 + rt vpn export 10.0.4.1:1 + rt vpn import 10.0.4.1:101 + export vpn + import vpn + ! +! +router bgp 99 vrf DONNA + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 10.0.4.1:1 + rt vpn export 10.0.4.1:101 + rt vpn import 10.0.4.1:1 10.0.4.1:102 10.0.4.1:103 + export vpn + import vpn + ! +! +router bgp 99 vrf EVA + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 10.0.4.1:1 + rt vpn export 10.0.4.1:102 + rt vpn import 10.0.4.1:101 10.0.4.1:103 + export vpn + import vpn + ! +! +router bgp 99 vrf ZITA + no bgp ebgp-requires-policy + no bgp network import-check + address-family ipv4 unicast + network 172.16.101.0/24 + label vpn export 103 + rd vpn export 10.0.4.1:1 + rt vpn export 10.0.4.1:103 + export vpn + import vpn + ! +! diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/setup_vrfs b/tests/topotests/bgp_vpnv4_route_leak_basic/setup_vrfs new file mode 100644 index 000000000000..f62c5cd21115 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_route_leak_basic/setup_vrfs @@ -0,0 +1,17 @@ +#!/bin/bash + +ip link add DONNA type vrf table 1001 +ip link add EVA type vrf table 1002 + +ip link add dummy0 type dummy # vrf default +ip link add dummy1 type dummy +ip link add dummy2 type dummy +ip link add dummy3 type dummy +ip link add dummy4 type dummy + +ip link set dummy1 master DONNA +ip link set dummy2 master EVA +ip link set dummy3 master DONNA +ip link set dummy4 master EVA + + diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py new file mode 100644 index 000000000000..a44f07b5600e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_route_leak_basic.py +# +# Copyright (c) 2018 Cumulus Networks, Inc. +# Donald Sharp +# Copyright (c) 2024 6WIND SAS +# + +""" +Test basic VPNv4 route leaking +""" + +import os +import sys +from functools import partial +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the unified configuration file + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_vrf_route_leak_donna(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test DONNA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.1.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [{"protocol": "connected"}], + "10.0.3.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "dummy0", + "vrf": "default", + "active": True, + }, + ], + }, + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "selected": None, + "nexthops": [ + { + "fib": None, + "interfaceName": "unknown", + "vrf": "Unknown", + "active": None, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_eva(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test EVA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], + "172.16.101.0/24": [ + { + "protocol": "bgp", + "selected": None, + "nexthops": [ + { + "fib": None, + "interfaceName": "unknown", + "vrf": "Unknown", + "active": None, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf EVA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF EVA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_default(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test default VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.2.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "DONNA", + "vrf": "DONNA", + "active": True, + }, + ], + }, + ], + "10.0.4.0/24": [ + { + "protocol": "connected", + } + ], + } + + test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF default check failed:\n{}".format(diff) + + +def test_ping(): + "Simple ping tests" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + logger.info("Ping from default to DONNA") + check_ping("r1", "10.0.0.1", True, 10, 0.5, source_addr="10.0.4.1") + + +def test_vrf_route_leak_donna_after_eva_down(): + logger.info("Ensure that route states change after EVA interface goes down") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r1.vtysh_cmd( + """ +configure +interface EVA + shutdown +""" + ) + + # Test DONNA VRF. + expect = { + "10.0.1.0/24": [ + { + "protocol": "bgp", + "selected": None, + "nexthops": [ + { + "fib": None, + "interfaceName": "EVA", + "vrf": "EVA", + "active": None, + }, + ], + }, + ], + "10.0.3.0/24": [ + { + "protocol": "bgp", + "selected": None, + "nexthops": [ + { + "fib": None, + "interfaceName": "EVA", + "vrf": "EVA", + "active": None, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + """ + Check that "show ip route vrf DONNA json" and the JSON at key "DONNA" of + "show ip route vrf all json" gives the same result. + """ + + def check_vrf_table(router, vrf, expect): + output = router.vtysh_cmd("show ip route vrf all json", isjson=True) + vrf_table = output.get(vrf, {}) + + return topotest.json_cmp(vrf_table, expect) + + test_func = partial(check_vrf_table, r1, "DONNA", expect) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_donna_after_eva_up(): + logger.info("Ensure that route states change after EVA interface goes up") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r1.vtysh_cmd( + """ +configure +interface EVA + no shutdown +""" + ) + + # Test DONNA VRF. + expect = { + "10.0.1.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + "10.0.3.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "EVA", + "vrf": "EVA", + "active": True, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_donna_add_vrf_zita(): + logger.info("Add VRF ZITA and ensure that the route from VRF ZITA is updated") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r1.cmd("ip link add ZITA type vrf table 1003") + + # Test DONNA VRF. + expect = { + "172.16.101.0/24": [ + { + "protocol": "bgp", + "selected": None, + "nexthops": [ + { + "fib": None, + "interfaceName": "ZITA", + "vrf": "ZITA", + "active": None, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_donna_set_zita_up(): + logger.info("Set VRF ZITA up and ensure that the route from VRF ZITA is updated") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r1.vtysh_cmd( + """ +configure +interface ZITA + no shutdown +""" + ) + + # Test DONNA VRF. + expect = { + "172.16.101.0/24": [ + { + "protocol": "bgp", + "selected": True, + "nexthops": [ + { + "fib": True, + "interfaceName": "ZITA", + "vrf": "ZITA", + "active": True, + }, + ], + }, + ], + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +def test_vrf_route_leak_donna_delete_vrf_zita(): + logger.info("Delete VRF ZITA and ensure that the route from VRF ZITA is deleted") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r1.cmd("ip link delete ZITA") + + # Test DONNA VRF. + expect = { + "172.16.101.0/24": None, + } + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + +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))