From 63e1ff1fe96ce757cca92c44e3faa55f0b509971 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 26 Nov 2024 19:36:40 +0100 Subject: [PATCH] test: vlan_iface_termination: Add test This will fail on a system running a vanilla kernel, where the dut's ports are backed by mv88e6xxx, because: 1. The ports are attached to the same bridge, and are thus in the same PVT group. 2. Creation of the VLAN uppers causes the DSA layer to add both ports to the same VTU entry. As a result, hardware behaves as if both ports had been configured as tagged members of VLAN 10, instead of just terminating incoming traffic locally. Add this test to catch hardware which behaves in this way. --- .../case/ietf_interfaces/ietf_interfaces.yaml | 3 + .../vlan_iface_termination/Readme.adoc | 49 ++++++ .../vlan_iface_termination/test.py | 142 ++++++++++++++++++ .../vlan_iface_termination/topology.dot | 24 +++ .../vlan_iface_termination/topology.svg | 51 +++++++ 5 files changed, 269 insertions(+) create mode 100644 test/case/ietf_interfaces/vlan_iface_termination/Readme.adoc create mode 100755 test/case/ietf_interfaces/vlan_iface_termination/test.py create mode 100644 test/case/ietf_interfaces/vlan_iface_termination/topology.dot create mode 100644 test/case/ietf_interfaces/vlan_iface_termination/topology.svg diff --git a/test/case/ietf_interfaces/ietf_interfaces.yaml b/test/case/ietf_interfaces/ietf_interfaces.yaml index 9118da66..b1418d10 100644 --- a/test/case/ietf_interfaces/ietf_interfaces.yaml +++ b/test/case/ietf_interfaces/ietf_interfaces.yaml @@ -61,3 +61,6 @@ - name: iface_enable_disable case: iface_enable_disable/test.py + +- name: vlan_iface_termination + case: vlan_iface_termination/test.py diff --git a/test/case/ietf_interfaces/vlan_iface_termination/Readme.adoc b/test/case/ietf_interfaces/vlan_iface_termination/Readme.adoc new file mode 100644 index 00000000..18669f86 --- /dev/null +++ b/test/case/ietf_interfaces/vlan_iface_termination/Readme.adoc @@ -0,0 +1,49 @@ +=== VLAN Interface Termination +==== Description +Verify that VLANs stacked on top of an interfaces that are also +attached to a VLAN filtering bridge are always locally terminated. + +.... +.-------------------. +| dut | +| | +| a.10 br0 b.10 | +| \ / \ / | +'------a-----b------' + | | + | | +.------a-----b------. +| | +| host | +| | +'-------------------' +.... + +In this setup, even though VLAN 10 is allowed to ingress and egress on +both `a` and `b`, _bridging_ of packets from one to the other must +_not_ be allowed. + +==== Topology +ifdef::topdoc[] +image::../../test/case/ietf_interfaces/vlan_iface_termination/topology.svg[VLAN Interface Termination topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::vlan_iface_termination/topology.svg[VLAN Interface Termination topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[VLAN Interface Termination topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to dut +. Configure bridge and VLAN interfaces on dut +. Configure IP addresses and VLAN interfaces on host +. Verify that host:a reaches host:b with untagged packets +. Verify that traffic on VLAN 10 from host:a does not reach host:b +. Verify that host:a can reach dut on VLAN 10 +. Verify that host:b can reach dut on VLAN 10 + + +<<< + diff --git a/test/case/ietf_interfaces/vlan_iface_termination/test.py b/test/case/ietf_interfaces/vlan_iface_termination/test.py new file mode 100755 index 00000000..5b57f4e5 --- /dev/null +++ b/test/case/ietf_interfaces/vlan_iface_termination/test.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +r"""VLAN Interface Termination + +Verify that VLANs stacked on top of an interfaces that are also +attached to a VLAN filtering bridge are always locally terminated. + +.... +.-------------------. +| dut | +| | +| a.10 br0 b.10 | +| \ / \ / | +'------a-----b------' + | | + | | +.------a-----b------. +| | +| host | +| | +'-------------------' +.... + +In this setup, even though VLAN 10 is allowed to ingress and egress on +both `a` and `b`, _bridging_ of packets from one to the other must +_not_ be allowed. + +""" +import infamy + +with infamy.Test() as test: + with test.step("Set up topology and attach to dut"): + env = infamy.Env() + dut = env.attach("dut", "mgmt") + + with test.step("Configure bridge and VLAN interfaces on dut"): + _, hporta = env.ltop.xlate("host", "a") + _, hportb = env.ltop.xlate("host", "b") + _, dporta = env.ltop.xlate( "dut", "a") + _, dportb = env.ltop.xlate( "dut", "b") + + dut.put_config_dicts({ + "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": "br0", + "type": "infix-if-type:bridge", + "enabled": True, + "bridge": { + "vlans": { + "vlan": [ + { + "vid": 1, + "untagged": [dporta, dportb] + }, + ] + } + } + }, + { + "name": dporta, + "infix-interfaces:bridge-port": { + "pvid": 1, + "bridge": "br0" + } + }, + { + "name": f"{dporta}.10", + "type": "infix-if-type:vlan", + "vlan": { + "id": 10, + "lower-layer-if": dporta, + }, + "ipv4": { + "address": [ + { + "ip": "10.10.1.2", + "prefix-length": 24, + } + ] + } + }, + { + "name": dportb, + "infix-interfaces:bridge-port": { + "pvid": 1, + "bridge": "br0" + } + }, + { + "name": f"{dportb}.10", + "type": "infix-if-type:vlan", + "vlan": { + "id": 10, + "lower-layer-if": dportb, + }, + "ipv4": { + "address": [ + { + "ip": "10.10.2.2", + "prefix-length": 24, + } + ] + } + }, + ] + } + } + }) + + with infamy.IsolatedMacVlan(hporta) as nsa, \ + infamy.IsolatedMacVlan(hportb) as nsb: + + with test.step("Configure IP addresses and VLAN interfaces on host"): + nsa.addip("10.0.1.1") + nsa.runsh(""" + set -ex + ip link add dev vlan10 link iface up type vlan id 10 + ip addr add 10.10.1.1/24 dev vlan10 + """) + + nsb.addip("10.0.1.2") + nsb.runsh(""" + set -ex + ip link add dev vlan10 link iface up type vlan id 10 + ip addr add 10.10.2.1/24 dev vlan10 + """) + + with test.step("Verify that host:a reaches host:b with untagged packets"): + nsa.must_reach("10.0.1.2") + + with test.step("Verify that traffic on VLAN 10 from host:a does not reach host:b"): + infamy.parallel(lambda: nsa.runsh("timeout -s INT 5 ping -i 0.2 -b 10.10.1.255 || true"), + lambda: nsb.must_not_receive("ip src 10.10.1.1")) + + with test.step("Verify that host:a can reach dut on VLAN 10"): + nsa.must_reach("10.10.1.2") + + with test.step("Verify that host:b can reach dut on VLAN 10"): + nsb.must_reach("10.10.2.2") + + test.succeed() diff --git a/test/case/ietf_interfaces/vlan_iface_termination/topology.dot b/test/case/ietf_interfaces/vlan_iface_termination/topology.dot new file mode 100644 index 00000000..8c349d35 --- /dev/null +++ b/test/case/ietf_interfaces/vlan_iface_termination/topology.dot @@ -0,0 +1,24 @@ +graph "vlan-iface-termination" { + layout="neato"; + overlap="false"; + esep="+22"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt | a | b }", + pos="0,0!", + kind="controller", + ]; + + dut [ + label="{ mgmt | a | b } | dut", + pos="6,0!", + kind="infix", + ]; + + host:mgmt -- dut:mgmt [kind=mgmt, color="lightgray"] + host:a -- dut:a + host:b -- dut:b +} diff --git a/test/case/ietf_interfaces/vlan_iface_termination/topology.svg b/test/case/ietf_interfaces/vlan_iface_termination/topology.svg new file mode 100644 index 00000000..aebf6a0c --- /dev/null +++ b/test/case/ietf_interfaces/vlan_iface_termination/topology.svg @@ -0,0 +1,51 @@ + + + + + + +vlan-iface-termination + + + +host + +host + +mgmt + +a + +b + + + +dut + +mgmt + +a + +b + +dut + + + +host:mgmt--dut:mgmt + + + + +host:a--dut:a + + + + +host:b--dut:b + + + +