diff options
Diffstat (limited to 'tests')
36 files changed, 1089 insertions, 2 deletions
diff --git a/tests/ospfd/common.c b/tests/ospfd/common.c index eb30c4016e..ef5e3ed177 100644 --- a/tests/ospfd/common.c +++ b/tests/ospfd/common.c @@ -74,7 +74,8 @@ void print_route_table(struct vty *vty, struct route_table *rt) label_stack = path->srni.backup_label_stack; mpls_label2str(label_stack->num_labels, label_stack->label, buf, - MPLS_LABEL_STRLEN, true); + MPLS_LABEL_STRLEN, + ZEBRA_LSP_NONE, true); vty_out(vty, " and backup path %s", buf); } vty_out(vty, "\n"); diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c index 73f2e29834..b8a2aef69e 100644 --- a/tests/ospfd/test_ospf_spf.c +++ b/tests/ospfd/test_ospf_spf.c @@ -107,7 +107,7 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf, ->num_labels, q_space->label_stack->label, label_buf, MPLS_LABEL_STRLEN, - true); + ZEBRA_LSP_NONE, true); vty_out(vty, "\nLabel stack: %s\n", label_buf); } else { diff --git a/tests/topotests/bgp_default_originate_withdraw/__init__.py b/tests/topotests/bgp_default_originate_withdraw/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/__init__.py diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf new file mode 100644 index 0000000000..6813b02be7 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 default-originate + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf new file mode 100644 index 0000000000..3692361fb3 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf new file mode 100644 index 0000000000..60e6236801 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r2/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + network 192.168.2.0/24 + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf new file mode 100644 index 0000000000..0c95656663 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf new file mode 100644 index 0000000000..547cf86b3f --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + network 0.0.0.0/0 + exit-address-family +! diff --git a/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf new file mode 100644 index 0000000000..7ccdcfd0d8 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/r3/zebra.conf @@ -0,0 +1,5 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +ip route 0.0.0.0/0 Null0 diff --git a/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py new file mode 100644 index 0000000000..e25f85af85 --- /dev/null +++ b/tests/topotests/bgp_default_originate_withdraw/test_bgp_default_originate_withdraw.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Check if bgpd do not crash if we use default-originate while +received a default route from the neighbor as well. 0.0.0.0/0 +MUST be kept in RIB even if we remove default-originate from +the neighbor. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_with_default_received(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_default_received_from_r3(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json")) + expected = { + "paths": [ + { + "nexthops": [ + { + "hostname": "r3", + "ip": "192.168.2.2", + } + ], + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_default_received_from_r3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default route received from r3" + + def _bgp_advertised_default_originate_to_r2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "bgpOriginatingDefaultNetwork": "0.0.0.0/0", + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_advertised_default_originate_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default-originate route advertised to r2" + + step("Disable default-originate for r2") + r1.vtysh_cmd( + """ + configure + router bgp + address-family ipv4 unicast + no neighbor 192.168.1.2 default-originate + """ + ) + + def _bgp_advertised_default_from_r3_to_r2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "bgpOriginatingDefaultNetwork": None, + "advertisedRoutes": { + "0.0.0.0/0": { + "valid": True, + } + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_advertised_default_from_r3_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default route advertised to r2" + + step("Enable default-originate for r2") + r1.vtysh_cmd( + """ + configure + router bgp + address-family ipv4 unicast + neighbor 192.168.1.2 default-originate + do clear ip bgp * + """ + ) + + step("Check if default-originate route advertised to r2") + test_func = functools.partial(_bgp_advertised_default_originate_to_r2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see default-originate route advertised to r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf new file mode 100644 index 0000000000..2db7edb806 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/ospfd.conf @@ -0,0 +1,13 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf new file mode 100644 index 0000000000..95b5da8402 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/P1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.20.20.20/32 +interface P1-eth0 + ip address 10.20.1.2/24 +interface P1-eth1 + ip address 10.20.2.2/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf new file mode 100644 index 0000000000..39ac8ca69c --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65000 + timers 3 9 + bgp router-id 10.10.10.10 + no bgp default ipv4-unicast + neighbor 10.30.30.30 remote-as 65000 + neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 + address-family l2vpn evpn + neighbor 10.30.30.30 activate + advertise-all-vni + advertise-svi-ip +! +router bgp 65000 vrf vrf-red + ! + address-family l2vpn evpn + route-target import *:300 + route-target import auto + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json new file mode 100644 index 0000000000..98ae92ce55 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/evpn.vni.json @@ -0,0 +1,16 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan0", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "remoteVteps":[ + { + "ip":"10.30.30.30", + "flood":"HER" + } + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf new file mode 100644 index 0000000000..f1c2b42dc1 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf new file mode 100644 index 0000000000..8c6cf3e6d4 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE1/zebra.conf @@ -0,0 +1,13 @@ +! +log file zebra.log +! +vrf vrf-red + vni 100 + exit-vrf +! +! +interface lo + ip address 10.10.10.10/32 +interface PE1-eth1 + ip address 10.20.1.1/24 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf new file mode 100644 index 0000000000..10809da283 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/bgpd.conf @@ -0,0 +1,24 @@ +! +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.30.30.30 + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as 65000 + neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.10.10.10 activate + advertise-all-vni + advertise-svi-ip +! +router bgp 65000 vrf vrf-blue + ! + address-family ipv4 unicast + redistribute static + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json new file mode 100644 index 0000000000..5c059786b2 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/evpn.vni.json @@ -0,0 +1,15 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan0", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "remoteVteps":[ + { + "ip":"10.10.10.10", + "flood":"HER" + } + ] +} diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf new file mode 100644 index 0000000000..065c993303 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf new file mode 100644 index 0000000000..cee4355e40 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/PE2/zebra.conf @@ -0,0 +1,17 @@ +vrf vrf-blue + vni 300 + exit-vrf +! +vrf vrf-red + vni 100 + exit-vrf +! +interface lo + ip address 10.30.30.30/32 +interface PE2-eth0 + ip address 10.20.2.3/24 +! +interface vrf-blue + ip address 30.0.0.3/24 +! +ip route 4.4.4.1/32 30.0.0.100 vrf vrf-blue diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/__init__.py diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf new file mode 100644 index 0000000000..91fae9eeba --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host1/zebra.conf @@ -0,0 +1,3 @@ +! +int host1-eth0 + ip address 10.10.1.55/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf new file mode 100644 index 0000000000..df9adeb3b5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/host2/zebra.conf @@ -0,0 +1,3 @@ +! +interface host2-eth0 + ip address 10.10.1.56/24 diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py new file mode 100755 index 0000000000..f8af210ed7 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -0,0 +1,534 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn_vxlan_svd.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 NVIDIA Corporation +# +# 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" +test_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC and route signalling over BGP +using Single Vxlan Device Configurtion +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import required_linux_kernel_version + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("P1") + tgen.add_router("PE1") + tgen.add_router("PE2") + tgen.add_router("host1") + tgen.add_router("host2") + + # Host1-PE1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["host1"]) + switch.add_link(tgen.gears["PE1"]) + + # PE1-P1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["PE1"]) + switch.add_link(tgen.gears["P1"]) + + # P1-PE2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["P1"]) + switch.add_link(tgen.gears["PE2"]) + + # PE2-host2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["PE2"]) + switch.add_link(tgen.gears["host2"]) + +def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): + pe = tgen.gears[pe_name] + + # configure vlan aware bridge + pe.run("ip link add name bridge type bridge stp_state 0") + pe.run("ip link set dev bridge type bridge vlan_filtering 1") + pe.run("bridge vlan add vid 1 dev bridge self") + pe.run("ip link set dev bridge up") + + # setup svi + pe.run("ip link add link bridge name vlan1 type vlan id 1 protocol 802.1q") + pe.run("ip link set dev vlan1 up") + pe.run("ip addr add {0} dev vlan1".format(svi_ip)) + pe.run("/sbin/sysctl net.ipv4.conf.vlan1.arp_accept=1") + + # setup single vxlan device + pe.run( + "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(tunnel_local_ip) + ) + pe.run("ip link set dev vxlan0 master bridge") + pe.run("bridge link set dev vxlan0 vlan_tunnel on") + pe.run("bridge link set dev vxlan0 neigh_suppress on") + pe.run("bridge link set dev vxlan0 learning off") + pe.run("bridge vlan add dev vxlan0 vid 1") + pe.run("bridge vlan add dev vxlan0 vid 1 tunnel_info id 101") + pe.run("ip link set up dev vxlan0") + + # setup PE interface + pe.run("ip link set dev {0}-{1} master bridge".format(pe_name, intf)) + pe.run("bridge vlan del vid 1 dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan del vid 1 untagged pvid dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan add vid 1 dev {0}-{1}".format(pe_name, intf)) + pe.run("bridge vlan add vid 1 pvid untagged dev {0}-{1}".format(pe_name, intf)) + + # l3vni 100 + pe.run("ip link add vrf-red type vrf table 1400") + pe.run("ip link add link bridge name vlan100 type vlan id 100 protocol 802.1q") + pe.run("ip link set dev vlan100 master vrf-blue") + pe.run("ip link set dev vlan100 up") + pe.run("bridge vlan add vid 100 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 100") + pe.run("bridge vlan add dev vxlan0 vid 100 tunnel_info id 100") + + # add a vrf for testing DVNI + if pe_name == "PE2": + pe.run("ip link add vrf-blue type vrf table 2400") + pe.run("ip link add link bridge name vlan300 type vlan id 300 protocol 802.1q") + pe.run("ip link set dev vlan300 master vrf-blue") + pe.run("ip link set dev vlan300 up") + pe.run("bridge vlan add vid 300 dev bridge self") + pe.run("bridge vlan add dev vxlan0 vid 300") + pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300") + +def setup_p_router(tgen, p_name): + p1 = tgen.gears[p_name] + p1.run("sysctl -w net.ipv4.ip_forward=1") + +def setup_module(mod): + "Sets up the pytest environment" + + result = required_linux_kernel_version("5.7") + if result is not True: + pytest.skip("Kernel requirements are not met, kernel version should be >= 5.7") + + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + setup_pe_router(tgen, "PE1", "10.10.10.10", "10.10.1.1/24", "eth0") + setup_pe_router(tgen, "PE2", "10.30.30.30", "10.10.1.3/24", "eth1") + setup_p_router(tgen, "P1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + #tgen.mininet_cli() + # This function tears down the whole topology. + tgen.stop_topology() + + +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + +def check_vni_macs_present(tgen, router, vni, maclist): + result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) + for rname, ifname in maclist: + m = tgen.net.macs[(rname, ifname)] + if m not in result["macs"]: + return "MAC ({}) for interface {} on {} missing on {} from {}".format( + m, ifname, rname, router.name, json.dumps(result, indent=4) + ) + return None + +def check_flood_entry_present(pe, vni, vtep): + if not topotest.iproute2_is_fdb_get_capable(): + return None + + output = pe.run("bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)) + + if str(vtep) not in output: + return output + + return None + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + + test_func = partial( + check_vni_macs_present, + tgen, + pe1, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe1.name) + + vtep = "10.30.30.30" + test_func = partial(check_flood_entry_present, pe1, 101, vtep) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep) + assert result is None, assertmsg + +def test_pe2_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() +#Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe2 = tgen.gears["PE2"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + + test_func = partial( + check_vni_macs_present, + tgen, + pe2, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe2.name) + + vtep = "10.10.10.10" + test_func = partial(check_flood_entry_present, pe2, 101, vtep) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep) + assert result is None, assertmsg + +def mac_learn_test(host, local): + "check the host MAC gets learned by the VNI" + + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + + mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + mac_output_json = json.loads(mac_output) + assertmsg = "Local MAC output does not match interface mac {}".format(mac) + assert mac_output_json[mac]["type"] == "local", assertmsg + + +def mac_test_local_remote(local, remote): + "test MAC transfer between local and remote" + + local_output = local.vtysh_cmd("show evpn mac vni all json") + remote_output = remote.vtysh_cmd("show evpn mac vni all json") + local_output_vni = local.vtysh_cmd("show evpn vni detail json") + local_output_json = json.loads(local_output) + remote_output_json = json.loads(remote_output) + local_output_vni_json = json.loads(local_output_vni) + + for vni in local_output_json: + if vni not in remote_output_json: + continue + + mac_list = local_output_json[vni]["macs"] + for mac in mac_list: + if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101": + assertmsg = "JSON output mismatches local: {} remote: {}".format( + local_output_vni_json[0]["vtepIp"], + remote_output_json[vni]["macs"][mac]["remoteVtep"], + ) + assert ( + remote_output_json[vni]["macs"][mac]["remoteVtep"] + == local_output_vni_json[0]["vtepIp"] + ), assertmsg + + +def test_learning_pe1(): + "test MAC learning on PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + mac_learn_test(host1, pe1) + + +def test_learning_pe2(): + "test MAC learning on PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe2 = tgen.gears["PE2"] + mac_learn_test(host2, pe2) + + +def test_local_remote_mac_pe1(): + "Test MAC transfer PE1 local and PE2 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe1, pe2) + + +def test_local_remote_mac_pe2(): + "Test MAC transfer PE2 local and PE1 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe2, pe1) + + +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + #print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + #print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = ( + "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + #print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + #print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + #print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe2.vtysh_cmd("debug zebra vxlan") + pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe1.vtysh_cmd("debug zebra vxlan") + pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + +def show_dvni_route(pe, vni, prefix, vrf): + output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix)) + + if str(vni) not in output: + return output + + output = pe.run("ip route show vrf {} {}".format(vrf, prefix)) + + if str(vni) not in output: + return output + + return None + +def test_dvni(): + "test Downstream VNI works as expected importing into PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + + prefix = "4.4.4.1/32" + test_func = partial(show_dvni_route, pe1, 300, prefix, "vrf-red") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix) + assert result is None, assertmsg + #tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/__init__.py diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf new file mode 100644 index 0000000000..4286b98409 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/bgpd.conf @@ -0,0 +1,14 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 + address-family ipv4 unicast + network 10.10.10.10/32 route-map atomic + network 10.10.10.20/32 + exit-address-family +! +route-map atomic permit 10 + set atomic-aggregate +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf new file mode 100644 index 0000000000..51a1b2657c --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf new file mode 100644 index 0000000000..2e63fd8c44 --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.1 remote-as external + neighbor 10.0.0.1 timers 3 10 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf new file mode 100644 index 0000000000..12d3731b3a --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 10.0.0.2/24 +! diff --git a/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py new file mode 100644 index 0000000000..4aa297511a --- /dev/null +++ b/tests/topotests/bgp_path_attribute_treat_as_withdraw/test_bgp_path_attribute_treat_as_withdraw.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2022 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# +# 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" +Test if `neighbor path-attribute treat-as-withdraw` command works correctly, +can withdraw unwanted prefixes from BGP table. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(r2) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + r1 = tgen.gears["r1"] + r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + r1.start() + + r2 = tgen.gears["r2"] + r2.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r2/zebra.conf")) + r2.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r2/bgpd.conf")) + r2.start() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_path_attribute_treat_as_withdraw(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "10.10.10.10/32": [ + { + "valid": True, + "atomicAggregate": True, + } + ], + "10.10.10.20/32": [ + { + "valid": True, + } + ], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed bgp convergence" + + step("Withdraw prefixes with atomic-aggregate from r1") + r2.vtysh_cmd( + """ + configure terminal + router bgp + neighbor 10.0.0.1 path-attribute treat-as-withdraw 6 + """ + ) + + def _bgp_check_if_route_withdrawn(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "10.10.10.10/32": None, + "10.10.10.20/32": [ + { + "valid": True, + } + ], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_if_route_withdrawn) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to withdraw prefixes with atomic-aggregate attribute" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 7f68b4ccf3..699b53303a 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -595,6 +595,29 @@ def iproute2_is_vrf_capable(): pass return False +def iproute2_is_fdb_get_capable(): + """ + Checks if the iproute2 version installed on the system is capable of + handling `bridge fdb get` commands to query neigh table resolution. + + Returns True if capability can be detected, returns False otherwise. + """ + + if is_linux(): + try: + subp = subprocess.Popen( + ["bridge", "fdb", "get", "help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + iproute2_out = subp.communicate()[1].splitlines()[0].split()[0] + + if "Usage" in str(iproute2_out): + return True + except Exception: + pass + return False def module_present_linux(module, load): """ |
