diff options
| author | Rafael Zalamena <rzalamena@users.noreply.github.com> | 2022-07-22 14:12:17 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-22 14:12:17 +0000 | 
| commit | b8443f7ad39ce0429f53c286d04a84b6faaeef83 (patch) | |
| tree | cbe8317eb6858926883caf6fd2bae15a4b0f2b76 | |
| parent | efb6140a9bf38ac9ccea43bd7e3da46a21b776ba (diff) | |
| parent | 97413ed7786ebcc59e1b86d19bb03d50f9feb9f1 (diff) | |
Merge pull request #11565 from pguibert6WIND/bfd_vrf_lite_support
bfdd: allow l3vrf bfd sessions without udp leaking
| -rw-r--r-- | bfdd/bfd.c | 66 | ||||
| -rw-r--r-- | bfdd/bfd_packet.c | 45 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/__init__.py | 0 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json | 96 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf | 26 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/r1/zebra.conf | 24 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf | 26 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/r2/zebra.conf | 24 | ||||
| -rw-r--r-- | tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py | 153 | ||||
| -rw-r--r-- | tests/topotests/lib/micronet.py | 54 | 
10 files changed, 480 insertions, 34 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_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_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 (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 26c7174f63..82b3f09b0c 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", 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 --- /dev/null +++ b/tests/topotests/bfd_vrflite_topo1/__init__.py 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)  | 
