diff --git a/tests/topotests/pim_mrib/__init__.py b/tests/topotests/pim_mrib/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/pim_mrib/r1/frr.conf b/tests/topotests/pim_mrib/r1/frr.conf new file mode 100644 index 000000000000..28cf2b2c460f --- /dev/null +++ b/tests/topotests/pim_mrib/r1/frr.conf @@ -0,0 +1,28 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +! +!debug pim nht +!debug pim nht detail +!debug pim nht rp +! +interface r1-eth0 + ip address 10.0.0.1/24 + ip igmp + ip pim +! +interface r1-eth1 + ip address 10.0.1.1/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.2.0/24 10.0.0.2 50 +ip route 10.0.3.0/24 10.0.1.3 50 +! +router pim + rpf-lookup-mode mrib-then-urib + rp 10.0.0.1 224.0.0.0/4 +! \ No newline at end of file diff --git a/tests/topotests/pim_mrib/r2/frr.conf b/tests/topotests/pim_mrib/r2/frr.conf new file mode 100644 index 000000000000..3e647f6795d2 --- /dev/null +++ b/tests/topotests/pim_mrib/r2/frr.conf @@ -0,0 +1,28 @@ +! +hostname r2 +password zebra +log file /tmp/r2-frr.log +! +!debug pim nht +!debug pim nht detail +!debug pim nht rp +! +interface r2-eth0 + ip address 10.0.0.2/24 + ip igmp + ip pim +! +interface r2-eth1 + ip address 10.0.2.2/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.1.0/24 10.0.0.1 50 +ip route 10.0.3.0/24 10.0.2.4 50 +! +router pim + rpf-lookup-mode mrib-then-urib + rp 10.0.0.1 224.0.0.0/4 +! \ No newline at end of file diff --git a/tests/topotests/pim_mrib/r3/frr.conf b/tests/topotests/pim_mrib/r3/frr.conf new file mode 100644 index 000000000000..9815484d02d5 --- /dev/null +++ b/tests/topotests/pim_mrib/r3/frr.conf @@ -0,0 +1,28 @@ +! +hostname r3 +password zebra +log file /tmp/r3-frr.log +! +!debug pim nht +!debug pim nht detail +!debug pim nht rp +! +interface r3-eth0 + ip address 10.0.1.3/24 + ip igmp + ip pim +! +interface r3-eth1 + ip address 10.0.3.3/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.0.0/24 10.0.1.1 50 +ip route 10.0.2.0/24 10.0.3.4 50 +! +router pim + rpf-lookup-mode mrib-then-urib + rp 10.0.0.1 224.0.0.0/4 +! \ No newline at end of file diff --git a/tests/topotests/pim_mrib/r4/frr.conf b/tests/topotests/pim_mrib/r4/frr.conf new file mode 100644 index 000000000000..8432a7a35044 --- /dev/null +++ b/tests/topotests/pim_mrib/r4/frr.conf @@ -0,0 +1,29 @@ +! +hostname r4 +password zebra +log file /tmp/r4-frr.log +! +debug pim nht +debug pim nht detail +debug pim nht rp +debug zebra rib detail +! +interface r4-eth0 + ip address 10.0.2.4/24 + ip igmp + ip pim +! +interface r4-eth1 + ip address 10.0.3.4/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.0.0/24 10.0.2.2 50 +ip route 10.0.1.0/24 10.0.3.3 50 +! +router pim + rpf-lookup-mode mrib-then-urib + rp 10.0.0.1 224.0.0.0/4 +! \ No newline at end of file diff --git a/tests/topotests/pim_mrib/test_pim_mrib.py b/tests/topotests/pim_mrib/test_pim_mrib.py new file mode 100644 index 000000000000..355c503e3b83 --- /dev/null +++ b/tests/topotests/pim_mrib/test_pim_mrib.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_pim_mrib.py +# +# Copyright (c) 2024 ATCorp +# Nathan Bahr +# + +import os +import sys +import pytest +from functools import partial + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger +from lib.pim import ( + verify_pim_rp_info, +) +from lib.common_config import step, write_test_header + +""" +test_pim_mrib.py: Test PIM MRIB overrides and RPF modes +""" + +TOPOLOGY = """ + Test PIM MRIB overrides and RPF modes + + +---+---+ +---+---+ + | | 10.0.0.0/24 | | + + R1 +----------------------+ R2 | + | | .1 .2 | | + +---+---+ r1-eth0 r2-eth0 +---+---+ + .1 | r1-eth1 r2-eth1 | .2 + | | + 10.0.1.0/24 | | 10.0.2.0/24 + | | + .3 | r3-eth0 r4-eth0 | .4 + +---+---+ r3-eth1 r4-eth1 +---+---+ + | | .3 .4 | | + + R3 +----------------------+ R4 | + | | 10.0.3.0/24 | | + +---+---+ +---+---+ +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. +pytestmark = [pytest.mark.pimd] + + +def build_topo(tgen): + '''Build function''' + + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + + # Create topology links + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth0", "r2-eth0") + tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "r1-eth1", "r3-eth0") + tgen.add_link(tgen.gears["r2"], tgen.gears["r4"], "r2-eth1", "r4-eth0") + tgen.add_link(tgen.gears["r3"], tgen.gears["r4"], "r3-eth1", "r4-eth1") + + +def setup_module(mod): + logger.info("PIM MRIB/RPF functionality:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + 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_pim_mrib_init(request): + '''Test boot in MRIB-than-URIB with the default MRIB''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Verify rp-info using default URIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth0", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_override(request): + '''Test MRIB override nexthop''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Install a MRIB route that has a shorter prefix length and lower cost. + # In MRIB-than-URIB mode, it should use this route + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + ip mroute 10.0.0.0/16 10.0.3.3 25 + ''' + ) + + step("Verify rp-info using MRIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth1", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_prefix_mode(request): + '''Test longer prefix lookup mode''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Switch to longer prefix match, should switch back to the URIB route + # even with the lower cost, the longer prefix match will win because of the mode + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + router pim + rpf-lookup-mode longer-prefix + ''' + ) + + step("Verify rp-info using URIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth0", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_dist_mode(request): + '''Test lower distance lookup mode''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Switch to lower distance match, should switch back to the MRIB route + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + router pim + rpf-lookup-mode lower-distance + ''' + ) + + step("Verify rp-info using MRIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth1", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_urib_mode(request): + '''Test URIB only lookup mode''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Switch to urib only match, should switch back to the URIB route + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + router pim + rpf-lookup-mode urib-only + ''' + ) + + step("Verify rp-info using URIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth0", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_mrib_mode(request): + '''Test MRIB only lookup mode''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Switch to mrib only match, should switch back to the MRIB route + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + router pim + rpf-lookup-mode mrib-only + ''' + ) + + step("Verify rp-info using MRIB nexthop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "r4-eth1", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +def test_pim_mrib_mrib_mode_no_route(request): + '''Test MRIB only with no route''' + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Remove the MRIB route, in mrib-only mode, it should switch to no path for the RP + tgen.routers()["r4"].vtysh_cmd( + ''' + conf term + no ip mroute 10.0.0.0/16 10.0.3.3 25 + ''' + ) + + step("Verify rp-info with Unknown next hop") + result = verify_pim_rp_info( + tgen, + None, + "r4", + "224.0.0.0/4", + "Unknown", + "10.0.0.1", + "Static", + False, + "ipv4", + True, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + +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))