From 7432ff10aadebbc871f3e512d5ce85a8d0f12330 Mon Sep 17 00:00:00 2001 From: Ahmed Karic Date: Tue, 15 Aug 2023 16:14:40 +0200 Subject: [PATCH] test/case: add bridge forwarding tests (single-DUT, dual-DUT, VLAN separation) --- test/case/infix_interfaces/all.yaml | 4 +- .../infix_interfaces/bridge_fwd_dual_dut.py | 150 ++++++++++++++++ .../infix_interfaces/bridge_fwd_sgl_dut.py | 69 +++++++ .../bridge_vlan_separation.py | 168 ++++++++++++++++++ test/infamy/netns.py | 24 ++- test/infamy/topologies/1x3.dot | 25 +++ test/infamy/topologies/2x4.dot | 38 ++++ 7 files changed, 475 insertions(+), 3 deletions(-) create mode 100755 test/case/infix_interfaces/bridge_fwd_dual_dut.py create mode 100755 test/case/infix_interfaces/bridge_fwd_sgl_dut.py create mode 100755 test/case/infix_interfaces/bridge_vlan_separation.py create mode 100644 test/infamy/topologies/1x3.dot create mode 100644 test/infamy/topologies/2x4.dot diff --git a/test/case/infix_interfaces/all.yaml b/test/case/infix_interfaces/all.yaml index 1280b119..22cb848f 100644 --- a/test/case/infix_interfaces/all.yaml +++ b/test/case/infix_interfaces/all.yaml @@ -4,4 +4,6 @@ - case: dual_bridge.py - case: bridge_vlan.py - case: ipv4_autoconf.py - +- case: bridge_fwd_sgl_dut.py +- case: bridge_fwd_dual_dut.py +- case: bridge_vlan_separation.py diff --git a/test/case/infix_interfaces/bridge_fwd_dual_dut.py b/test/case/infix_interfaces/bridge_fwd_dual_dut.py new file mode 100755 index 00000000..99876e88 --- /dev/null +++ b/test/case/infix_interfaces/bridge_fwd_dual_dut.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# ,-------------------------------------, ,-------------------------------------, +# | dut1:data2 | | dut2:data2 | +# | br0 ----------|-------|--------- br0 | +# | / \ | | / \ | +# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt | +# '-------------------------------------' '-------------------------------------' +# | | | | | | +# | | | | | | +# ,-----------------------------------------------------------------------------------------, +# | host:mgmt0 host:data0 host:data1 host:data2 host:data3 host:mgmt1 | +# | [10.0.0.2] [10.0.0.3] [10.0.0.4] | +# | (ns11) (ns20) (ns21) | +# | | +# | [ HOST ] | +# '-----------------------------------------------------------------------------------------' + +import infamy + +with infamy.Test() as test: + with test.step("Initialize"): + env = infamy.Env(infamy.std_topology("2x4")) + dut1 = env.attach("dut1", "mgmt") + dut2 = env.attach("dut2", "mgmt") + + with test.step("Configure a bridge with triple physical port"): + _, tport10 = env.ltop.xlate("dut1", "data0") + _, tport11 = env.ltop.xlate("dut1", "data1") + _, tport12 = env.ltop.xlate("dut1", "data2") + _, tport20 = env.ltop.xlate("dut2", "data0") + _, tport21 = env.ltop.xlate("dut2", "data1") + _, tport22 = env.ltop.xlate("dut2", "data2") + + dut1.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "iana-if-type:bridge", + "enabled": True, + "bridge": { + "vlans": { + "vlan": [ + { + "vid": 10, + "untagged": [ tport10, tport11 ], + "tagged": [ "br0", tport12 ] + } + ] + } + } + }, + { + "name": tport10, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport11, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport12, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0", + } + } + ] + } + }) + + dut2.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "iana-if-type:bridge", + "enabled": True, + "bridge": { + "vlans": { + "vlan": [ + { + "vid": 10, + "untagged": [ tport20, tport21 ], + "tagged": [ "br0", tport22 ] + } + ] + } + } + }, + { + "name": tport20, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport21, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport22, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0", + } + } + ] + } + }) + + with test.step("Ping host:data20 [10.0.0.3] and host:data21 [10.0.0.4]"\ + "from host:data11 [10.0.0.2]"): + + _, hport11 = env.ltop.xlate("host", "data11") + _, hport20 = env.ltop.xlate("host", "data20") + _, hport21 = env.ltop.xlate("host", "data21") + + with infamy.IsolatedMacVlan(hport11) as ns11, \ + infamy.IsolatedMacVlan(hport20) as ns20, \ + infamy.IsolatedMacVlan(hport21) as ns21: + + ns11.addip("10.0.0.2") + ns20.addip("10.0.0.3") + ns21.addip("10.0.0.4") + + ns11.must_reach("10.0.0.3") + ns11.must_reach("10.0.0.4") + + test.succeed() diff --git a/test/case/infix_interfaces/bridge_fwd_sgl_dut.py b/test/case/infix_interfaces/bridge_fwd_sgl_dut.py new file mode 100755 index 00000000..ff7802c1 --- /dev/null +++ b/test/case/infix_interfaces/bridge_fwd_sgl_dut.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# ,-----------------------------------------, +# | | +# | br0 | +# | / \ | +# | target:mgmt tgt:data0 tgt:data1 | +# '-----------------------------------------' +# | | | +# | | | +# ,------------------------------------------, +# | host:mgmt host:data0 host:data1 | +# | [10.0.0.1] [10.0.0.2] | +# | (ns0) (ns1) | +# | | +# | [ HOST ] | +# '------------------------------------------' + +import infamy + +with infamy.Test() as test: + with test.step("Initialize"): + env = infamy.Env(infamy.std_topology("1x3")) + target = env.attach("target", "mgmt") + + with test.step("Configure a bridge with dual physical port"): + _, tport0 = env.ltop.xlate("target", "data0") + _, tport1 = env.ltop.xlate("target", "data1") + + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "iana-if-type:bridge", + "enabled": True, + }, + { + "name": tport0, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0" + } + }, + { + "name": tport1, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0" + } + } + ] + } + }) + + with test.step("Ping host:data1 [10.0.0.2] from host:data0 [10.0.0.1]"): + _, hport0 = env.ltop.xlate("host", "data0") + _, hport1 = env.ltop.xlate("host", "data1") + + with infamy.IsolatedMacVlan(hport0) as ns0, \ + infamy.IsolatedMacVlan(hport1) as ns1 : + + ns1.addip("10.0.0.2") + ns0.addip("10.0.0.1") + + ns0.must_reach("10.0.0.2") + + test.succeed() diff --git a/test/case/infix_interfaces/bridge_vlan_separation.py b/test/case/infix_interfaces/bridge_vlan_separation.py new file mode 100755 index 00000000..5c66a953 --- /dev/null +++ b/test/case/infix_interfaces/bridge_vlan_separation.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# ,-------------------------------------, ,-------------------------------------, +# | dut1:data2 | | dut2:data2 | +# | br0 ----------|-------|--------- br0 | +# | / \ | | / \ | +# |dut1:mgmt dut1:data0 dut1:data1 | | dut2:data0 dut2:data1 dut2:mgmt | +# '-------------------------------------' '-------------------------------------' +# | | | | | | +# | | | | | | +# ,-----------------------------------------------------------------------------------------, +# | host:mgmt0 host:data0 host:data1 host:data2 host:data3 host:mgmt1 | +# | [10.0.0.1] [10.0.0.2] [10.0.0.3] [10.0.0.4] | +# | (ns10) (ns11) (ns20) (ns21) | +# | | +# | [ HOST ] | +# '-----------------------------------------------------------------------------------------' + +import infamy + +with infamy.Test() as test: + with test.step("Initialize"): + env = infamy.Env(infamy.std_topology("2x4")) + dut1 = env.attach("dut1", "mgmt") + dut2 = env.attach("dut2", "mgmt") + + with test.step("Configure a bridge with triple physical port"): + _, tport10 = env.ltop.xlate("dut1", "data0") + _, tport11 = env.ltop.xlate("dut1", "data1") + _, tport12 = env.ltop.xlate("dut1", "data2") + _, tport20 = env.ltop.xlate("dut2", "data0") + _, tport21 = env.ltop.xlate("dut2", "data1") + _, tport22 = env.ltop.xlate("dut2", "data2") + + dut1.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "iana-if-type:bridge", + "enabled": True, + "bridge": { + "vlans": { + "vlan": [ + { + "vid": 10, + "untagged": [ tport10 ], + "tagged": [ "br0", tport12 ] + }, + { + "vid": 20, + "untagged": [ tport11 ], + "tagged": [ "br0", tport12 ] + } + ] + } + } + }, + { + "name": tport10, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport11, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 20, + "bridge": "br0" + } + }, + { + "name": tport12, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0", + } + } + ] + } + }) + + dut2.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "iana-if-type:bridge", + "enabled": True, + "bridge": { + "vlans": { + "vlan": [ + { + "vid": 10, + "untagged": [ tport20 ], + "tagged": [ "br0", tport22 ] + }, + { + "vid": 20, + "untagged": [ tport21 ], + "tagged": [ "br0", tport22 ] + } + ] + } + } + }, + { + "name": tport20, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 10, + "bridge": "br0" + } + }, + { + "name": tport21, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "pvid": 20, + "bridge": "br0" + } + }, + { + "name": tport22, + "type": "iana-if-type:ethernetCsmacd", + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0", + } + } + ] + } + }) + + with test.step("Ping host:data20 [10.0.0.3] from host:data10 [10.0.0.1] through " \ + " and host:data21 [10.0.0.4] from host:data11 [10.0.0.2] through "): + + _, hport10 = env.ltop.xlate("host", "data10") + _, hport11 = env.ltop.xlate("host", "data11") + _, hport20 = env.ltop.xlate("host", "data20") + _, hport21 = env.ltop.xlate("host", "data21") + + with infamy.IsolatedMacVlan(hport10) as ns10, \ + infamy.IsolatedMacVlan(hport11) as ns11, \ + infamy.IsolatedMacVlan(hport20) as ns20, \ + infamy.IsolatedMacVlan(hport21) as ns21: + + ns10.addip("10.0.0.1") + ns11.addip("10.0.0.2") + ns20.addip("10.0.0.3") + ns21.addip("10.0.0.4") + + ns10.must_reach("10.0.0.3") + ns11.must_reach("10.0.0.4") + + ns10.must_not_reach("10.0.0.4") + ns11.must_not_reach("10.0.0.3") + ns10.must_not_reach("10.0.0.2") + ns11.must_not_reach("10.0.0.1") + + test.succeed() diff --git a/test/infamy/netns.py b/test/infamy/netns.py index 671906e9..1e987e6f 100644 --- a/test/infamy/netns.py +++ b/test/infamy/netns.py @@ -63,6 +63,26 @@ class IsolatedMacVlan: args, kwargs = self._mangle_subprocess_args(args, kwargs) return subprocess.Popen(*args, **kwargs) - def runsh(self, script): + def runsh(self, script, *args, **kwargs): return self.run("/bin/sh", text=True, input=script, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *args, **kwargs) + + def addip(self, addr, subnet=24): + self.runsh(f""" + set -ex + ip link set iface up + ip addr add {addr}/{subnet} dev iface + """, check=True) + + def ping(self, daddr, count=1, timeout=2, check=False): + return self.runsh(f"""set -ex; ping -c {count} -w {timeout} {daddr}""", check=check) + + def must_reach(self, daddr): + res = self.ping(daddr) + if res.returncode != 0: + raise Exception(res.stdout) + + def must_not_reach(self, daddr): + res = self.ping(daddr) + if res.returncode == 0: + raise Exception(res.stdout) diff --git a/test/infamy/topologies/1x3.dot b/test/infamy/topologies/1x3.dot new file mode 100644 index 00000000..3e13689c --- /dev/null +++ b/test/infamy/topologies/1x3.dot @@ -0,0 +1,25 @@ +graph "1x3" { + layout="neato"; + overlap="false"; + esep="+20"; + + node [shape=record, fontname="monospace"]; + edge [color="cornflowerblue", penwidth="2"]; + + host [ + label="host | { tgt | data0 | data1 }", + pos="0,12!", + kind="controller", + ]; + + target [ + label="{ mgmt | data0 | data1 } | target", + pos="10,12!", + + kind="infix", + ]; + + host:tgt -- target:mgmt [kind=mgmt] + host:data0 -- target:data0 + host:data1 -- target:data1 +} \ No newline at end of file diff --git a/test/infamy/topologies/2x4.dot b/test/infamy/topologies/2x4.dot new file mode 100644 index 00000000..720baa3b --- /dev/null +++ b/test/infamy/topologies/2x4.dot @@ -0,0 +1,38 @@ +graph "2x4" { + layout="neato"; + overlap="false"; + esep="+20"; + + node [shape=record, fontname="monospace"]; + edge [color="cornflowerblue", penwidth="2"]; + + host [ + label="host | { mgmt1 | data10 | data11 | mgmt2 | data20 | data21 }", + pos="0,15!", + kind="controller", + ]; + + dut1 [ + label="{ mgmt | data0 | data1 } | dut1 | { data2 }", + pos="10,18!", + + kind="infix", + ]; + + dut2 [ + label="{ mgmt | data0 | data1 } | dut2 | { data2 }", + pos="10,12!", + + kind="infix", + ]; + + host:mgmt1 -- dut1:mgmt [kind=mgmt] + host:data10 -- dut1:data0 + host:data11 -- dut1:data1 + + host:mgmt2 -- dut2:mgmt [kind=mgmt] + host:data20 -- dut2:data0 + host:data21 -- dut2:data1 + + dut1:data2 -- dut2:data2 +} \ No newline at end of file