From: Paul Szczepanek <paul.szczepanek@arm.com>
To: dev@dpdk.org
Cc: Jeremy Spewock <jspewock@iol.unh.edu>,
Paul Szczepanek <paul.szczepanek@arm.com>
Subject: [PATCH v4 2/2] dts: add dual VLAN test suite
Date: Thu, 9 Jan 2025 18:01:24 +0000 [thread overview]
Message-ID: <20250109180124.1678253-2-paul.szczepanek@arm.com> (raw)
In-Reply-To: <20250109180124.1678253-1-paul.szczepanek@arm.com>
From: Jeremy Spewock <jspewock@iol.unh.edu>
Test functionality of VLAN functions such as stripping,
inserting and filtering with two tags present.
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
Signed-off-by: Paul Szczepanek <paul.szczepanek@arm.com>
---
dts/tests/TestSuite_dual_vlan.py | 287 +++++++++++++++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 dts/tests/TestSuite_dual_vlan.py
diff --git a/dts/tests/TestSuite_dual_vlan.py b/dts/tests/TestSuite_dual_vlan.py
new file mode 100644
index 0000000000..bdbee7e8d1
--- /dev/null
+++ b/dts/tests/TestSuite_dual_vlan.py
@@ -0,0 +1,287 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 University of New Hampshire
+
+"""Dual VLAN functionality testing suite.
+
+The main objective of this test suite is to ensure that standard VLAN functions such as stripping
+and filtering both still carry out their expected behavior in the presence of a packet which
+contains two VLAN headers. These functions should carry out said behavior not just in isolation,
+but also when other VLAN functions are configured on the same port. In addition to this, the
+priority attributes of VLAN headers should be unchanged in the case of multiple VLAN headers
+existing on a single packet, and a packet with only a single VLAN header should be able to have one
+additional VLAN inserted into it.
+"""
+
+from enum import Flag, auto
+from typing import ClassVar
+
+from scapy.layers.l2 import Dot1Q, Ether
+from scapy.packet import Packet, Raw
+
+from framework.params.testpmd import SimpleForwardingModes
+from framework.remote_session.testpmd_shell import TestPmdShell
+from framework.test_suite import TestSuite, func_test
+
+
+class TestDualVlan(TestSuite):
+ """DPDK Dual VLAN test suite.
+
+ This suite tests the behavior of VLAN functions and properties in the presence of two VLAN
+ headers. All VLAN functions which are tested in this suite are specified using the inner class
+ :class:`TestCaseOptions` and should have cases for configuring them in
+ :meth:`configure_testpmd` as well as cases for testing their behavior in
+ :meth:`verify_vlan_functions`. Every combination of VLAN functions being enabled should be
+ tested. Additionally, attributes of VLAN headers, such as priority, are tested to ensure they
+ are not modified in the case of two VLAN headers.
+ """
+
+ class TestCaseOptions(Flag):
+ """Flag for specifying which VLAN functions to configure.
+
+ These flags are specific to configuring this testcase and are used to
+ create a matrix of all configuration combinations.
+ """
+
+ #:
+ VLAN_STRIP = auto()
+ #:
+ VLAN_FILTER_INNER = auto()
+ #:
+ VLAN_FILTER_OUTER = auto()
+
+ #: ID to set on inner VLAN tags.
+ inner_vlan_tag: ClassVar[int] = 2
+ #: ID to set on outer VLAN tags.
+ outer_vlan_tag: ClassVar[int] = 1
+ #: ID to use when inserting VLAN tags.
+ vlan_insert_tag: ClassVar[int] = 3
+ #:
+ rx_port: ClassVar[int] = 0
+ #:
+ tx_port: ClassVar[int] = 1
+
+ def is_relevant_packet(self, pkt: Packet) -> bool:
+ """Check if a packet was sent by functions in this suite.
+
+ All functions in this test suite send packets with a payload that is packed with 20 "X"
+ characters. This method, therefore, can determine if the packet was sent by this test suite
+ by just checking to see if this payload exists on the received packet.
+
+ Args:
+ pkt: Packet to check for relevancy.
+
+ Returns:
+ :data:`True` if the packet contains the expected payload, :data:`False` otherwise.
+ """
+ return hasattr(pkt, "load") and "X" * 20 in str(pkt.load)
+
+ def pkt_payload_contains_layers(self, pkt: Packet, *expected_layers: Packet) -> bool:
+ """Verify that the payload of the packet matches `expected_layers`.
+
+ The layers in the payload of `pkt` must match the type and the user-defined fields of the
+ layers in `expected_layers` in order.
+
+ Args:
+ pkt: Packet to check the payload of.
+ *expected_layers: Layers expected to be in the payload of `pkt`.
+
+ Returns:
+ :data:`True` if the payload of `pkt` matches the layers in `expected_layers` in order,
+ :data:`False` otherwise.
+ """
+ current_pkt_layer = pkt.payload
+ ret = True
+ for layer in expected_layers:
+ ret &= isinstance(current_pkt_layer, type(layer))
+ if not ret:
+ break
+ for field, val in layer.fields.items():
+ ret &= (
+ hasattr(current_pkt_layer, field) and getattr(current_pkt_layer, field) == val
+ )
+ current_pkt_layer = current_pkt_layer.payload
+ return ret
+
+ def verify_vlan_functions(self, send_packet: Packet, options: TestCaseOptions) -> None:
+ """Send packet and verify the received packet has the expected structure.
+
+ Expected structure is defined by `options` according to the following table:
+ +----------------------------------------------+-----------------------+
+ | Configure setting | Result |
+ +=======+=======+========+============+========+=======+=======+=======+
+ | Outer | Inner | VLAN | VLAN | VLAN | Pass/ | Outer | Inner |
+ | vlan | vlan | strip | filter | insert | Drop | vlan | vlan |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | no | no | no | pass | 0x1 | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | yes | no | no | pass | no | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | no | yes,0x1 | no | pass | 0x1 | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | no | yes,0x2 | no | pass | 0x1 | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | no | yes,0x1,0x2| no | pass | 0x1 | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | yes | yes,0x1 | no | pass | no | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | yes | yes,0x2 | no | pass | no | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+ | 0x1 | 0x2 | yes | yes,0x1,0x2| no | pass | no | 0x2 |
+ +-------+-------+--------+------------+--------+-------+-------+-------+
+
+ Args:
+ send_packet: Packet to send for testing.
+ options: Flag which defines the currents configured settings in testpmd.
+ """
+ recv = self.send_packet_and_capture(send_packet)
+ recv = list(filter(self.is_relevant_packet, recv))
+ expected_layers: list[Packet] = []
+
+ if self.TestCaseOptions.VLAN_STRIP not in options:
+ expected_layers.append(Dot1Q(vlan=self.outer_vlan_tag))
+ expected_layers.append(Dot1Q(vlan=self.inner_vlan_tag))
+
+ self.verify(
+ len(recv) > 0,
+ f"Expected to receive packet with the payload {expected_layers} but got nothing.",
+ )
+
+ for pkt in recv:
+ self.verify(
+ self.pkt_payload_contains_layers(pkt, *expected_layers),
+ f"Received packet ({pkt.summary()}) did not match the expected sequence of layers "
+ f"{expected_layers} with options {options}.",
+ )
+
+ def configure_testpmd(self, shell: TestPmdShell, options: TestCaseOptions, add: bool) -> None:
+ """Configure VLAN functions in testpmd based on `options`.
+
+ Args:
+ shell: Testpmd session to configure the settings on.
+ options: Settings to modify in `shell`.
+ add: If :data:`True`, turn the settings in `options` on, otherwise turn them off.
+ """
+ if (
+ self.TestCaseOptions.VLAN_FILTER_INNER in options
+ or self.TestCaseOptions.VLAN_FILTER_OUTER in options
+ ):
+ if add:
+ # If we are adding a filter, filtering has to be enabled first
+ shell.set_vlan_filter(self.rx_port, True)
+
+ if self.TestCaseOptions.VLAN_FILTER_INNER in options:
+ shell.rx_vlan(self.inner_vlan_tag, self.rx_port, add)
+ if self.TestCaseOptions.VLAN_FILTER_OUTER in options:
+ shell.rx_vlan(self.outer_vlan_tag, self.rx_port, add)
+
+ if not add:
+ # If we are removing filters then we need to remove the filters before we can
+ # disable filtering.
+ shell.set_vlan_filter(self.rx_port, False)
+ if self.TestCaseOptions.VLAN_STRIP in options:
+ shell.set_vlan_strip(self.rx_port, add)
+
+ @func_test
+ def insert_second_vlan(self) -> None:
+ """Test that a packet with a single VLAN can have an additional one inserted into it.
+
+ Steps:
+ Set VLAN tag on the tx port.
+ Create a packet with a VLAN tag.
+ Send and receive the packet.
+ Verify:
+ Packets are received.
+ Packet contains two VLAN tags.
+ """
+ with TestPmdShell(self.sut_node, forward_mode=SimpleForwardingModes.mac) as testpmd:
+ testpmd.tx_vlan_set(port=self.tx_port, enable=True, vlan=self.vlan_insert_tag)
+ testpmd.start()
+ recv = self.send_packet_and_capture(
+ Ether() / Dot1Q(vlan=self.outer_vlan_tag) / Raw(b"X" * 20)
+ )
+ self.verify(len(recv) > 0, "Did not receive any packets when testing VLAN insertion.")
+ self.verify(
+ any(
+ self.is_relevant_packet(p)
+ and self.pkt_payload_contains_layers(
+ p, Dot1Q(vlan=self.vlan_insert_tag), Dot1Q(vlan=self.outer_vlan_tag)
+ )
+ for p in recv
+ ),
+ "Packet was unable to insert a second VLAN tag.",
+ )
+
+ @func_test
+ def all_vlan_functions(self) -> None:
+ """Test that all combinations of :class:`TestCaseOptions` behave as expected.
+
+ Steps:
+ Send packets with VLAN functions disabled.
+ Send packets with a set of combinations of VLAN functions enabled.
+ Disable VLAN function to allow for a clean environment for the next test.
+ Verify:
+ Packet with two VLANs is unchanged without the VLAN modification functions enabled.
+ VLAN functions work as expected.
+ """
+ send_pkt = (
+ Ether()
+ / Dot1Q(vlan=self.outer_vlan_tag)
+ / Dot1Q(vlan=self.inner_vlan_tag)
+ / Raw(b"X" * 20)
+ )
+ with TestPmdShell(self.sut_node, forward_mode=SimpleForwardingModes.mac) as testpmd:
+ testpmd.start()
+ recv = self.send_packet_and_capture(send_pkt)
+ self.verify(len(recv) > 0, "Unmodified packet was not received.")
+ self.verify(
+ any(
+ self.is_relevant_packet(p)
+ and self.pkt_payload_contains_layers(
+ p, Dot1Q(vlan=self.outer_vlan_tag), Dot1Q(vlan=self.inner_vlan_tag)
+ )
+ for p in recv
+ ),
+ "Packet was modified without any VLAN functions applied.",
+ )
+ testpmd.stop()
+ for i in range(2 ** len(self.TestCaseOptions)):
+ options = self.TestCaseOptions(i)
+ self.configure_testpmd(testpmd, options, True)
+ testpmd.start()
+ self.verify_vlan_functions(send_pkt, options)
+ testpmd.stop()
+ self.configure_testpmd(testpmd, options, False)
+
+ @func_test
+ def maintains_priority(self) -> None:
+ """Test that priorities of multiple VLAN tags are preserved by the PMD.
+
+ Steps:
+ Create packets with VLAN tags with priorities.
+ Send and receive packets.
+ Verify:
+ Packets are received.
+ Priorities are unchanged.
+ """
+ pkt = (
+ Ether()
+ / Dot1Q(vlan=self.outer_vlan_tag, prio=1)
+ / Dot1Q(vlan=self.inner_vlan_tag, prio=2)
+ / Raw(b"X" * 20)
+ )
+ with TestPmdShell(self.sut_node, forward_mode=SimpleForwardingModes.mac) as testpmd:
+ testpmd.start()
+ recv = self.send_packet_and_capture(pkt)
+ self.verify(len(recv) > 0, "Did not receive any packets when testing VLAN priority.")
+ self.verify(
+ any(
+ self.is_relevant_packet(p)
+ and self.pkt_payload_contains_layers(
+ p,
+ Dot1Q(vlan=self.outer_vlan_tag, prio=1),
+ Dot1Q(vlan=self.inner_vlan_tag, prio=2),
+ )
+ for p in recv
+ ),
+ "VLAN headers did not maintain their priorities.",
+ )
--
2.39.2
next prev parent reply other threads:[~2025-01-09 18:01 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-15 19:58 [PATCH v1 0/3] dts: add test suite for dual VLANs jspewock
2024-07-15 19:58 ` [PATCH v1 1/3] dts: fix Testpmd function for resetting VLAN insertion jspewock
2024-07-15 19:58 ` [PATCH v1 2/3] dts: add dual_vlan testing suite jspewock
2024-07-22 17:38 ` Dean Marx
2024-07-25 13:56 ` Jeremy Spewock
2024-07-31 13:54 ` Jeremy Spewock
2024-07-15 19:58 ` [PATCH v1 3/3] dts: add dual_vlan test suite to the yaml schema jspewock
2024-07-16 21:24 ` [PATCH v2 0/2] dts: add test suite for dual VLANs jspewock
2024-07-16 21:24 ` [PATCH v2 1/2] dts: add dual_vlan testing suite jspewock
2024-08-07 19:07 ` Dean Marx
2024-07-16 21:24 ` [PATCH v2 2/2] dts: add dual_vlan test suite to the yaml schema jspewock
2024-07-24 17:13 ` [PATCH v3 0/2] dts: add test suite for dual VLANs jspewock
2024-07-24 17:13 ` [PATCH v3 1/2] dts: add dual_vlan testing suite jspewock
2024-08-13 19:47 ` Dean Marx
2024-09-12 3:46 ` Patrick Robb
2025-01-09 18:01 ` [PATCH v4 1/2] dts: add stop ports decorator for VLAN functions Paul Szczepanek
2025-01-09 18:01 ` Paul Szczepanek [this message]
2024-07-24 17:13 ` [PATCH v3 2/2] dts: add dual_vlan test suite to the yaml schema jspewock
2024-08-13 19:48 ` Dean Marx
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250109180124.1678253-2-paul.szczepanek@arm.com \
--to=paul.szczepanek@arm.com \
--cc=dev@dpdk.org \
--cc=jspewock@iol.unh.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).