From edc3f63167fd95e4e70287743c9b252415c9336e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Jul 2022 14:33:48 +0200 Subject: [PATCH 1/2] bfdd: allow l3vrf bfd sessions without udp leaking Until now, when in vrf-lite mode, the BFD implementation creates a single UDP socket and relies on the following sysctl value to 1: echo 1 > /proc/sys/net/ipv4/udp_l3mdev_accept With this setting, the incoming BFD packets from a given vrf, would leak to the default vrf, and would match the UDP socket. The drawback of this solution is that udp packets received on a given vrf may leak to an other vrf. This may be a security concern. The commit addresses this issue by avoiding this leak mechanism. An UDP socket is created for each vrf, and each socket uses new setsockopt option: SO_REUSEADDR + SO_REUSEPORT. With this option, the incoming UDP packets are distributed on the available sockets. The impact of those options with l3mdev devices is unknown. It has been observed that this option is not needed, until the default vrf sockets are created. To ensure the BFD packets are correctly routed to the appropriate socket, a BPF filter has been put in place and attached to the sockets : SO_ATTACH_REUSEPORT_CBPF. This option adds a criterium to force the packet to choose a given socket. If initial criteria from the default distribution algorithm were not good, at least two sockets would be available, and the CBPF would force the selection to the same socket. This would come to the situation where an incoming packet would be processed on a different vrf. The bpf code is the following one: struct sock_filter code[] = { { BPF_RET | BPF_K, 0, 0, 0 }, }; struct sock_fprog p = { .len = sizeof(code)/sizeof(struct sock_filter), .filter = code, }; if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p))) { zlog_warn("unable to set SO_ATTACH_REUSEPORT_CBPF on socket: %s", strerror(errno)); return -1; } Some tests have been done with by creating vrf contexts, and by using the below vtysh configuration: ip route 2.2.2.2/32 10.126.0.2 vrf vrf2 ip route 2.2.2.2/32 10.126.0.2 ! interface ntfp2 ip address 10.126.0.1/24 ! interface ntfp3 vrf vrf4 ip address 10.126.0.1/24 ! interface ntfp2 vrf vrf1 ip address 10.126.0.1/24 ! interface ntfp2.100 vrf vrf2 ip address 10.126.0.1/24 ! interface ntfp2.200 vrf vrf3 ip address 10.126.0.1/24 ! line vty ! bfd peer 10.126.0.2 vrf vrf2 ! peer 10.126.0.2 vrf vrf3 ! peer 10.126.0.2 ! peer 10.126.0.2 vrf vrf4 ! peer 2.2.2.2 multihop local-address 1.1.1.1 ! peer 2.2.2.2 multihop local-address 1.1.1.1 vrf vrf2 transmit-interval 1500 receive-interval 1500 ! The results showed no issue related to packets received by the wrong vrf. Even changing the udp_l3mdev_accept flag to 1 did not change the test results. Signed-off-by: Philippe Guibert --- bfdd/bfd.c | 64 +++++++++++++++++++++++------------------------ bfdd/bfd_packet.c | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 483beb1b17..a161926358 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -1950,40 +1950,38 @@ static int bfd_vrf_enable(struct vrf *vrf) if (bglobal.debug_zebra) zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id); - if (vrf->vrf_id == VRF_DEFAULT || - vrf_get_backend() == VRF_BACKEND_NETNS) { - if (!bvrf->bg_shop) - bvrf->bg_shop = bp_udp_shop(vrf); - if (!bvrf->bg_mhop) - bvrf->bg_mhop = bp_udp_mhop(vrf); - if (!bvrf->bg_shop6) - bvrf->bg_shop6 = bp_udp6_shop(vrf); - if (!bvrf->bg_mhop6) - bvrf->bg_mhop6 = bp_udp6_mhop(vrf); - if (!bvrf->bg_echo) - bvrf->bg_echo = bp_echo_socket(vrf); - if (!bvrf->bg_echov6) - bvrf->bg_echov6 = bp_echov6_socket(vrf); + if (!bvrf->bg_shop) + bvrf->bg_shop = bp_udp_shop(vrf); + if (!bvrf->bg_mhop) + bvrf->bg_mhop = bp_udp_mhop(vrf); + if (!bvrf->bg_shop6) + bvrf->bg_shop6 = bp_udp6_shop(vrf); + if (!bvrf->bg_mhop6) + bvrf->bg_mhop6 = bp_udp6_mhop(vrf); + if (!bvrf->bg_echo) + bvrf->bg_echo = bp_echo_socket(vrf); + if (!bvrf->bg_echov6) + bvrf->bg_echov6 = bp_echov6_socket(vrf); + + if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); + if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, + &bvrf->bg_ev[1]); + if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, + &bvrf->bg_ev[2]); + if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); + if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, + &bvrf->bg_ev[4]); + if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); - if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_shop, &bvrf->bg_ev[0]); - if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_mhop, &bvrf->bg_ev[1]); - if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_shop6, &bvrf->bg_ev[2]); - if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_mhop6, &bvrf->bg_ev[3]); - if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_echo, &bvrf->bg_ev[4]); - if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1) - thread_add_read(master, bfd_recv_cb, bvrf, - bvrf->bg_echov6, &bvrf->bg_ev[5]); - } if (vrf->vrf_id != VRF_DEFAULT) { bfdd_zclient_register(vrf->vrf_id); bfdd_sessions_enable_vrf(vrf); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index d34d642762..054a9bfbf2 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -876,6 +876,14 @@ void bfd_recv_cb(struct thread *t) "no session found"); return; } + /* + * We may have a situation where received packet is on wrong vrf + */ + if (bfd && bfd->vrf && bfd->vrf != bvrf->vrf) { + cp_debug(is_mhop, &peer, &local, ifindex, vrfid, + "wrong vrfid."); + return; + } /* Ensure that existing good sessions are not overridden. */ if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN && @@ -1208,10 +1216,41 @@ int bp_set_tos(int sd, uint8_t value) return 0; } +static bool bp_set_reuse_addr(int sd) +{ + int one = 1; + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) { + zlog_warn("set-reuse-addr: setsockopt(SO_REUSEADDR, %d): %s", + one, strerror(errno)); + return false; + } + return true; +} + +static bool bp_set_reuse_port(int sd) +{ + int one = 1; + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) { + zlog_warn("set-reuse-port: setsockopt(SO_REUSEPORT, %d): %s", + one, strerror(errno)); + return false; + } + return true; +} + + static void bp_set_ipopts(int sd) { int rcvttl = BFD_RCV_TTL_VAL; + if (!bp_set_reuse_addr(sd)) + zlog_fatal("set-reuse-addr: failed"); + + if (!bp_set_reuse_port(sd)) + zlog_fatal("set-reuse-port: failed"); + if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) zlog_fatal("set-ipopts: TTL configuration failed"); @@ -1453,6 +1492,12 @@ static void bp_set_ipv6opts(int sd) int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL; int ipv6_only = BFD_IPV6_ONLY_VAL; + if (!bp_set_reuse_addr(sd)) + zlog_fatal("set-reuse-addr: failed"); + + if (!bp_set_reuse_port(sd)) + zlog_fatal("set-reuse-port: failed"); + if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1) zlog_fatal( "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s", From 97413ed7786ebcc59e1b86d19bb03d50f9feb9f1 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 12 Jul 2022 12:12:01 +0200 Subject: [PATCH 2/2] topotests: add bfd_vrflite_topo1 test This tests checks that there are no errors when receiving BFD packets over the various linux vrf interfaces. For example, if an incoming packet is received by the wrong socket, a VRF mismatch error would occur, and BFD flapping would be observed. Signed-off-by: Philippe Guibert --- tests/topotests/bfd_vrflite_topo1/__init__.py | 0 .../r1/bfd_peers_status.json | 96 +++++++++++ .../topotests/bfd_vrflite_topo1/r1/bfdd.conf | 26 +++ .../topotests/bfd_vrflite_topo1/r1/zebra.conf | 24 +++ .../topotests/bfd_vrflite_topo1/r2/bfdd.conf | 26 +++ .../topotests/bfd_vrflite_topo1/r2/zebra.conf | 24 +++ .../test_bfd_vrflite_topo1.py | 153 ++++++++++++++++++ tests/topotests/lib/micronet.py | 54 +++++++ 8 files changed, 403 insertions(+) create mode 100644 tests/topotests/bfd_vrflite_topo1/__init__.py create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r1/zebra.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/r2/zebra.conf create mode 100644 tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py diff --git a/tests/topotests/bfd_vrflite_topo1/__init__.py b/tests/topotests/bfd_vrflite_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json new file mode 100644 index 0000000000..07e96d74bd --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json @@ -0,0 +1,96 @@ +[ + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"vrf1", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":true, + "peer":"192.0.2.2", + "local":"192.0.2.1", + "vrf":"vrf2", + "passive-mode":false, + "minimum-ttl":254, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"vrf2", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":false, + "peer":"192.168.0.2", + "vrf":"default", + "passive-mode":false, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + }, + { + "multihop":true, + "peer":"192.0.2.2", + "local":"192.0.2.1", + "vrf":"vrf1", + "passive-mode":false, + "minimum-ttl":254, + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":1000, + "transmit-interval":1000, + "echo-receive-interval":50, + "echo-transmit-interval":0, + "detect-multiplier":3, + "remote-receive-interval":1000, + "remote-transmit-interval":1000, + "remote-echo-receive-interval":50, + "remote-detect-multiplier":3 + } +] diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf new file mode 100644 index 0000000000..96e8ff4b12 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf @@ -0,0 +1,26 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.2 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.2 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf new file mode 100644 index 0000000000..ebb4e63be7 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf @@ -0,0 +1,24 @@ +vrf vrf1 + ip route 192.0.2.2/32 192.168.0.2 +! +vrf vrf2 + ip route 192.0.2.2/32 192.168.0.2 +! +interface r1-eth0 vrf vrf3 + ip address 192.168.0.1/24 +! +interface r1-eth0.100 vrf vrf1 + ip address 192.168.0.1/24 +! +interface r1-eth0.200 vrf vrf2 + ip address 192.168.0.1/24 +! +interface r1-eth0.300 + ip address 192.168.0.1/24 +! +interface r1-loop1 vrf vrf1 + ip address 192.0.2.1/32 +! +interface r1-loop2 vrf vrf2 + ip address 192.0.2.1/32 +! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf new file mode 100644 index 0000000000..7b11a4785a --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf @@ -0,0 +1,26 @@ +! +! debug bfd network +! debug bfd peer +! debug bfd zebra +! +bfd + peer 192.168.0.1 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.1 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.168.0.1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf1 + transmit-interval 1000 + receive-interval 1000 + ! + peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf2 + transmit-interval 1000 + receive-interval 1000 + ! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf new file mode 100644 index 0000000000..d8b996e9ed --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf @@ -0,0 +1,24 @@ +vrf vrf1 + ip route 192.0.2.1/32 192.168.0.1 +! +vrf vrf2 + ip route 192.0.2.1/32 192.168.0.1 +! +interface r2-eth0 vrf vrf3 + ip address 192.168.0.2/24 +! +interface r2-eth0.100 vrf vrf1 + ip address 192.168.0.2/24 +! +interface r2-eth0.200 vrf vrf2 + ip address 192.168.0.2/24 +! +interface r2-eth0.300 + ip address 192.168.0.2/24 +! +interface r2-loop1 vrf vrf1 + ip address 192.0.2.2/32 +! +interface r2-loop2 vrf vrf2 + ip address 192.0.2.2/32 +! \ No newline at end of file diff --git a/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py new file mode 100644 index 0000000000..b7afb8e3b9 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +# +# test_bfd_vrflite_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Copyright (c) 2022 by 6WIND +# +# 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_bfd_vrflite_topo1.py: Test the FRR BFD daemon. +""" + +import os +import sys +import json +from functools import partial +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 + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd] + + +def build_topo(tgen): + # Create 2 routers + for routern in range(1, 3): + 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["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info("Testing with Linux VRF support and udp_l3mdev=0") + if os.system("echo 0 > /proc/sys/net/ipv4/udp_l3mdev_accept") != 0: + return pytest.skip( + "Skipping BFD vrflite Topo1 Test. Linux VRF not available on System" + ) + + for rname, router in router_list.items(): + router.net.add_l3vrf("vrf1", 10) + router.net.add_l3vrf("vrf2", 20) + router.net.add_l3vrf("vrf3", 30) + router.net.add_vlan(rname + "-eth0.100", rname + "-eth0", 100) + router.net.add_vlan(rname + "-eth0.200", rname + "-eth0", 200) + router.net.add_vlan(rname + "-eth0.300", rname + "-eth0", 300) + router.net.attach_iface_to_l3vrf(rname + "-eth0.100", "vrf1") + router.net.attach_iface_to_l3vrf(rname + "-eth0.200", "vrf2") + router.net.add_loop(rname + "-loop1") + router.net.add_loop(rname + "-loop2") + router.net.attach_iface_to_l3vrf(rname + "-loop1", "vrf1") + router.net.attach_iface_to_l3vrf(rname + "-loop2", "vrf2") + router.net.attach_iface_to_l3vrf(rname + "-eth0", "vrf3") + + 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_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # Move interfaces out of vrf namespace and delete the namespace + router_list = tgen.routers() + for rname, router in router_list.items(): + router.net.del_iface(rname + "-eth0.100") + router.net.del_iface(rname + "-eth0.200") + router.net.del_iface(rname + "-eth0.300") + router.net.del_iface(rname + "-loop1") + router.net.del_iface(rname + "-loop2") + + tgen.stop_topology() + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("waiting for bfd peers to go up") + router = tgen.gears['r1'] + json_file = "{}/{}/bfd_peers_status.json".format(CWD, 'r1') + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +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/micronet.py b/tests/topotests/lib/micronet.py index 02f66e9c26..dfa10ccb2f 100644 --- a/tests/topotests/lib/micronet.py +++ b/tests/topotests/lib/micronet.py @@ -599,6 +599,60 @@ class LinuxNamespace(Commander): self.cmd_raises("mkdir -p " + inner) self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) + def add_vlan(self, vlanname, linkiface, vlanid): + self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ + ip_path, + "link", + "add", + "link", + linkiface, + "name", + vlanname, + "type", + "vlan", + "id", + vlanid, + ] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) + + def add_loop(self, loopname): + self.logger.debug("Adding Linux iface: %s", loopname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) + self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) + + def add_l3vrf(self, vrfname, tableid): + self.logger.debug("Adding Linux VRF: %s", vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) + + def del_iface(self, iface): + self.logger.debug("Removing Linux Iface: %s", iface) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "del", iface]) + + def attach_iface_to_l3vrf(self, ifacename, vrfname): + self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + if vrfname: + self.cmd_raises( + [ip_path, "link", "set", "dev", ifacename, "master", vrfname] + ) + else: + self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) + def add_netns(self, ns): self.logger.debug("Adding network namespace %s", ns)