diff options
Diffstat (limited to 'tests/topotests/bgp_ipv4_over_ipv6')
10 files changed, 4153 insertions, 0 deletions
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json new file mode 100644 index 0000000..7f928b9 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json @@ -0,0 +1,85 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + } + }}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}} + }}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json new file mode 100644 index 0000000..8e0f448 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json new file mode 100644 index 0000000..72d3a93 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json new file mode 100644 index 0000000..a7ea0c8 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json new file mode 100644 index 0000000..5e90d6b --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto"}, + "r1-link2": {"ipv4": "auto"}, + "r1-link3": {"ipv4": "auto"}, + "r1-link4": {"ipv4": "auto"}, + "r1-link5": {"ipv4": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto"}, + "r0-link2": {"ipv4": "auto"}, + "r0-link3": {"ipv4": "auto"}, + "r0-link4": {"ipv4": "auto"}, + "r0-link5": {"ipv4": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py new file mode 100644 index 0000000..9f01287 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py @@ -0,0 +1,963 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + get_frr_ipv6_linklocal, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify Ipv4 route next hop is changed when advertised using +next hop -self command +2. Verify IPv4 route advertised to peer when IPv6 BGP session established + using peer-group +3. Verify IPv4 routes received with IPv6 nexthop are getting advertised + to another IBGP peer without changing the nexthop +4. Verify IPv4 routes advertised with correct nexthop when nexthop +unchange is configure on EBGP peers + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_ibgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## +def test_ibgp_to_ibgp_p1(request): + """ + + Test Capability extended nexthop. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo23 = deepcopy(topo) + build_config_from_json(tgen, topo23, save_bkup=False) + + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + # verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family. + bgp_convergence = verify_bgp_convergence(tgen, topo23) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo23, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes installed on R3 with global address without " + "changing the nexthop ( nexthop should IPv6 link local which is" + " received from R1)" + ) + gipv6 = get_glipv6("r1", "r2-link0") + dut = "r3" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + write_test_footer(tc_name) + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test Extended capability next hop, with ibgp peer. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + " Configure IPv6 EBGP session between R1 & R2 with global IPv6 address" + " Enable capability extended-nexthop on the nbr from both the routers" + " Activate same IPv6 nbr from IPv4 unicast family" + ) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "capability": "extended-nexthop", + "activate": "ipv4", + "next_hop_self": True, + "activate": "ipv4", + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_glipv6("r2", "r3") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_peer_group_p1(request): + """ + Test extended capability next hop with peer groups. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo1 = deepcopy(topo) + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py new file mode 100644 index 0000000..48f308e --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py @@ -0,0 +1,630 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +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, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + get_frr_ipv6_linklocal, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +BGP_CONVERGENCE_TIMEOUT = 10 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +TC6. Verify BGP speaker advertise IPv4 route to peer only if "extended + nexthop capability" is negotiated +TC7. Verify ipv4 route nexthop updated dynamically when in route-map is + applied on receiving BGP peer +TC8. Verify IPv4 routes advertise using "redistribute static" and "network + command" are received on EBGP peer with IPv6 nexthop +TC10. Verify IPv4 routes are deleted after un-configuring of "network +command" and "redistribute static knob" +TC18. Verify IPv4 routes installed with correct nexthop after deactivate + and activate neighbor from address family +TC19. Verify IPv4 route ping is working fine and nexhop installed in kernel + as IPv4 link-local address +TC24. Verify IPv4 prefix-list routes advertised to peer when prefix -list + applied in out direction +TC27. Verify IPv4 routes are intact after BGPd process restart +TC30. Verify Ipv4 route installed with correct next hop when same route + is advertised via IPV4 and IPv6 BGP peers +TC32. Verify IPv4 route received with IPv6 nexthop can be advertised to + another IPv4 BGP peers + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_tc8_p0(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step("Configure IPv6 EBGP session between R1 and R2 with global" " IPv6 address") + reset_config_on_routers(tgen) + + step( + "Enable capability extended-nexthop on the nbr from both the " + " routers Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static " + "routes on R1 (nexthop for static route exists on different " + "link of R0" + ) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv6"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv6"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command " + "are received on R2 BGP & routing table , verify using show ip bgp " + "show ip route for IPv4 routes and show bgp ipv6,show ipv6 routes " + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 2, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, addr_type, dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + addr_type, + dut, + verify_nh_for_static_rtes, + next_hop=glip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + " R1 to R2 connected link" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + +def test_ext_nh_cap_remove_red_static_network_ebgp_peer_tc10_p1(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes are deleted after un-configuring of + network command and redistribute static knob + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers , Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static routes " + " on R1 nexthop for static route exists on different link of R0" + ) + reset_config_on_routers(tgen) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1. Configure loopback on R1 with IPv4 " + "address Advertise loobak from IPv4 unicast family using network " + "command from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp, show ipv6 routes" + " for IPv6 routes ." + ) + + glipv6 = get_llip("r1", "r2-link0") + assert glipv6 is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": get_glipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=get_glipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=get_glipv6, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1 " + " R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glipv6, + } + ] + } + } + + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glipv6, expected=False + ) + assert ( + bgp_rib is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in BGP rib".format( + tc_name + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes " "still present in RIB".format(tc_name) + + step( + "After removing IPv4 routes from redistribute static those routes" + " are removed from R2, after re-advertising routes which are " + " advertised using network are still present in the on R2 with " + " IPv6 global nexthop, verify using show ip bgp and show ip routes" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_nw_cmd_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Error: Routes still present in BGP rib".format( + tc_name + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py new file mode 100644 index 0000000..c070904 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py @@ -0,0 +1,779 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +import functools +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + + +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +from lib.common_config import ( + write_test_header, + start_topology, + write_test_footer, + start_router, + stop_router, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import create_router_bgp, verify_bgp_convergence, verify_bgp_rib + +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None + +# Global variables +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +INTF_LIST = [ + "r2-link0", + "r2-link1", + "r2-link2", + "r2-link3", + "r2-link4", + "r2-link5", + "r2-link6", + "r2-link7", +] +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are advertised when IPv6 EBGP loopback session + established using Unnumbered interface +2. Verify IPv4 routes are installed with correct nexthop after +shut / no shut of nexthop and BGP peer interfaces +3. Verify IPv4 routes are intact after stop and start the FRR services + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ebgp_unnumbered_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_unnumbered_loopback_ebgp_nbr_p0(request): + """ + + Test extended capability nexthop with un numbered ebgp. + + Verify IPv4 routes are advertised when IPv6 EBGP loopback + session established using Unnumbered interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip) + + dut = "r2" + protocol = "bgp" + for rte in range(0, NO_OF_RTES): + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][rte], "no_of_ip": 1, "next_hop": llip} + ] + } + } + """ interface_list = ['r1-link0','r1-link1'] + nh_list =[] + for i in range(NO_OF_RTES): + nh_list.append(topo['routers']['r2']['links'][i][ + 'interface']) """ + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=llip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_restart_frr_p2(request): + """ + + Test extended capability nexthop , restart frr. + + Verify IPv4 routes are intact after stop and start the FRR services + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_configure_gua_on_unnumbered_intf(request): + """ + Configure a global V6 address on an unnumbered interface on R1 + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + r2 = tgen.gears["r2"] + + def bgp_prefix_received_gua_nh(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json")) + expected = { + "prefix": "11.0.20.1/32", + "paths": [ + { + "nexthops": [ + { + "ip": "5001:dead:beef::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + } + ] + } + ], + } + return topotest.json_cmp(output, expected) + + def bgp_prefix_received_v4_mapped_v6_nh(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json")) + expected = { + "prefix": "11.0.20.1/32", + "paths": [ + { + "nexthops": [ + { + "ip": "::ffff:a00:501", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + } + ] + } + ], + } + return topotest.json_cmp(output, expected) + + step("Configure a global V6 address on an unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + ipv6 address 5001:dead:beef::1/126 + ! + """ + ) + + # verify that r2 has received prefix with GUA as nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Configure a secondary global V6 address on an unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + ipv6 address 7771:dead:beef::1/126 + ! + """ + ) + # verify that r1 did not readvertise the prefix with secondary V6 address as the nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Unconfigure the secondary global V6 address from unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + no ipv6 address 7771:dead:beef::1/126 + ! + """ + ) + # verify that r1 still has the prefix with primary GUA as the nexthop + test_func = functools.partial(bgp_prefix_received_gua_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not 5001:dead:beef::1".format( + tc_name + ) + + step("Unconfigure the primary global V6 address from unnumbered interface on R1") + output = tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + interface r1-r2-eth5 + no ipv6 address 5001:dead:beef::1/126 + ! + """ + ) + # verify that r1 has rcvd the prefix with v4-mapped-v6 address as the nexthop + test_func = functools.partial(bgp_prefix_received_v4_mapped_v6_nh, r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert ( + result is None + ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ + is not ::ffff:a00:501".format( + tc_name + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py new file mode 100644 index 0000000..a9e6d21 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py @@ -0,0 +1,988 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + addKernelRoute, + write_test_footer, + create_prefix_lists, + verify_rib, + create_static_routes, + reset_config_on_routers, + step, + create_route_maps, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +topo = None +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NETWORK_CMD_IP = "1.0.1.17/32" +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop +2. Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP session + established using loopback interface +3. Verify IPv4 routes are advertised to peer when static routes are + configured with ADMIN distance and tag option +4. Verify IPv4 routes advertised to peer when BGP session established + using link-local address + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ibgp_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with ibgp peer. + + Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 addr" + " & Advertise loopback from IPv4 unicast family using network cmd " + " from R1" + ) + # this test case needs ipv6 routes to be configured + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network command are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + "R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ext_nh_cap_admin_dist_tag_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with admin distance and route tag. + + Verify IPv4 routes are advertised to peer when static routes + are configured with ADMIN distance and tag option + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 1 + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family & IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 " + "address & Advertise loopback from IPv4 unicast family " + "using network cmd from R1" + ) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network cmd are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + count = 0 + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 10 + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 0 + count, + "action": "permit", + "network": NETWORK["ipv4"][rte], + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n " "Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format("ipv4"): [ + { + "action": "deny", + "match": { + "ipv4": {"prefix_lists": "pf_list_1_{}".format("ipv4")} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ibgp_loopback_nbr_p1(request): + """ + Verify Extended capability nexthop with loopback interface. + + Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP + session established using loopback interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + global topo + topo1 = deepcopy(topo) + reset_config_on_routers(tgen) + step("Configure IPv6 global address between R1 and R2") + step( + "Configure loopback on R1 and R2 and establish EBGP session " + "between R1 and R2 over loopback global ip" + ) + step("Configure static route on R1 and R2 for loopback reachability") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + + for routerN in ["r1", "r2"]: + for addr_type in ["ipv6"]: + for bgp_neighbor in topo1["routers"][routerN]["bgp"]["address_family"][ + addr_type + ]["unicast"]["neighbor"].keys(): + # Adding ['source_link'] = 'lo' key:value pair + if bgp_neighbor == "r1" or bgp_neighbor == "r2": + topo1["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": { + "source_link": "lo", + "ebgp_multihop": 2, + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + # Creating configuration from JSON + build_config_from_json(tgen, topo1, save_bkup=False) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + ipv4_list = [("r1", r1_r2_intf, [r2_lo_v4]), ("r2", r2_r1_intf, [r1_lo_v4])] + + ipv6_list = [ + ("r1", r1_r2_intf, [r2_lo_v6], r2_r1_v6_nh), + ("r2", r2_r1_intf, [r1_lo_v6], r1_r2_v6_nh), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + r1_r2_v4_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0] + r2_r1_v4_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv4"].split("/")[0] + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_lo_v4, "next_hop": r2_r1_v4_nh}, + {"network": r2_lo_v6, "next_hop": r2_r1_v6_nh}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_lo_v4, "next_hop": r1_r2_v4_nh}, + {"network": r1_lo_v6, "next_hop": r1_r2_v6_nh}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence.") + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = (topo1["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]).lower() + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using network command" + " from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from network command , routes which are " + "advertised using redistribute static are still present in the on " + "R2 , verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using redistribute static" + " command from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from redistribute static , routes which" + " are advertised using network are still present in the on R2 , " + "verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py new file mode 100644 index 0000000..9a0fc44 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +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, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + step, + reset_config_on_routers, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import create_router_bgp, verify_bgp_convergence +from lib.topojson import build_config_from_json + +# Global variables +topo = None + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +ADDR_TYPES = check_address_types() +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 2links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 + + R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are deleted after un-configuring "network command +" and "redistribute static knob" with Unnumbered IPv6 IBGP session + """ + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/rfc5549_ibgp_unnumbered_nbr.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a particular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a particular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_unnumbered_nbr_p1(request): + """ + + Test extended capability nexthop. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 IBGP Unnumbered session between R1 and R2 and enable " + "ipv6 nd ra-interval 10 in the interface" + ) + + step( + "Enable capability extended-nexthop" + "on the neighbor from both the routers and " + "ipv6 nd ra-interval 10 on link connected between R1 and R2" + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast " + "family respectively from R1 " + "Configure loopback on R1 with IPv4 address Advertise loopback " + "from IPv4 unicast family using network cmd from R1 " + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + " IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp show ipv6 routes" + " for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": llip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) |