<div dir="ltr"><div>The current DTS dev systems you have access to still are:</div><div><br></div><div>iol-dts-tester and iol-dts-dut<br><br></div><div>with mlnx cx5 and broadcom NetXtreme-E.<br><br></div><div>Let us know if you have any further questions.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Oct 6, 2026 at 3:56 PM Patrick Robb <<a href="mailto:patrickrobb1997@gmail.com" target="_blank">patrickrobb1997@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="auto">I will test this tonight.</div><div dir="auto"><br></div><div dir="auto">What do we have right now in terms of DTS dev systems and NICs for non-iol people like me. Is it still:</div><div dir="auto"><br></div><div dir="auto">iol-dts-tester</div><div dir="auto"><br></div><div dir="auto">And </div><div dir="auto"><br></div><div dir="auto">iol-dts-dut</div><div dir="auto"><br></div><div dir="auto">With mlnx cx5 and intel xl710?</div><div dir="auto"><br></div><div dir="auto">Just checking in. </div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Aug 11, 2026 at 1:04 PM Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Refactor current flow test suite to utilize a generator<br>
method, which dynamically creates flow rules from a<br>
dictionary of pattern and action fields during runtime.<br>
This allows for much more extensive testing of the flow<br>
API without exponential code growth.<br>
<br>
Signed-off-by: Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>><br>
Signed-off-by: Dean Marx <<a href="mailto:dmarx@iol.unh.edu" target="_blank">dmarx@iol.unh.edu</a>><br>
Reviewed-by: Patrick Robb <<a href="mailto:probb@iol.unh.edu" target="_blank">probb@iol.unh.edu</a>><br>
---<br>
<br>
v7:<br>
* Resolve merge conflicts.<br>
dts/tests/TestSuite_flow_offload.py | 206 ++++++++++++++++++++++++----<br>
1 file changed, 182 insertions(+), 24 deletions(-)<br>
<br>
diff --git a/dts/tests/TestSuite_flow_offload.py b/dts/tests/TestSuite_flow_offload.py<br>
index be11d09875..d1a6c2fa74 100644<br>
--- a/dts/tests/TestSuite_flow_offload.py<br>
+++ b/dts/tests/TestSuite_flow_offload.py<br>
@@ -1,7 +1,7 @@<br>
# SPDX-License-Identifier: BSD-3-Clause<br>
# Copyright(c) 2025 University of New Hampshire<br>
<br>
-"""RTE Flow testing suite.<br>
+"""Flow offload testing suite.<br>
<br>
This suite verifies a range of flow rules built using patterns<br>
and actions from the RTE Flow API. It would be impossible to cover<br>
@@ -10,7 +10,7 @@<br>
<br>
"""<br>
<br>
-from dataclasses import dataclass<br>
+from dataclasses import dataclass, field<br>
from itertools import product<br>
from typing import Any, Callable, Optional<br>
<br>
@@ -18,6 +18,7 @@<br>
from scapy.layers.inet6 import IPv6<br>
from scapy.layers.l2 import ARP, Dot1Q, Ether<br>
from scapy.layers.sctp import SCTP<br>
+from scapy.layers.vxlan import VXLAN<br>
from scapy.packet import Packet, Raw<br>
<br>
from api.capabilities import NicCapability, requires_nic_capability<br>
@@ -50,10 +51,16 @@ class Protocol:<br>
scapy_class: type[Packet]<br>
pattern_name: str<br>
fields: list[PatternField]<br>
+ default_values: dict[str, Any] = field(default_factory=dict)<br>
<br>
def build_scapy_layer(self, field_values: dict[str, Any]) -> Packet:<br>
- """Construct a Scapy layer with the given field values."""<br>
- return self.scapy_class(**field_values)<br>
+ """Construct a Scapy layer with the given field values.<br>
+<br>
+ Default values are applied first, then overridden by any<br>
+ explicit field values so test parameters always win.<br>
+ """<br>
+ merged = {**self.default_values, **field_values}<br>
+ return self.scapy_class(**merged)<br>
<br>
<br>
@dataclass<br>
@@ -125,6 +132,7 @@ class JumpTest:<br>
<br>
<br>
PROTOCOLS: dict[str, Protocol] = {<br>
+ # -------------------- Base Protocols --------------------<br>
"eth": Protocol(<br>
name="eth",<br>
scapy_class=Ether,<br>
@@ -215,6 +223,89 @@ class JumpTest:<br>
PatternField("op", "opcode", [1, 2]),<br>
],<br>
),<br>
+ # -------------------- VXLAN Outer Protocols --------------------<br>
+ "eth_outer": Protocol(<br>
+ name="eth_outer",<br>
+ scapy_class=Ether,<br>
+ pattern_name="eth",<br>
+ fields=[<br>
+ PatternField("src", "src", ["02:00:00:00:00:00"]),<br>
+ PatternField("dst", "dst", ["02:00:00:00:00:02"]),<br>
+ ],<br>
+ ),<br>
+ "ipv4_outer": Protocol(<br>
+ name="ipv4_outer",<br>
+ scapy_class=IP,<br>
+ pattern_name="ipv4",<br>
+ fields=[<br>
+ PatternField("src", "src", ["10.0.0.1"]),<br>
+ PatternField("dst", "dst", ["10.0.0.2"]),<br>
+ PatternField("ttl", "ttl", [64, 128]),<br>
+ PatternField("tos", "tos", [0, 4]),<br>
+ ],<br>
+ ),<br>
+ "udp_outer": Protocol(<br>
+ name="udp_outer",<br>
+ scapy_class=UDP,<br>
+ pattern_name="udp",<br>
+ fields=[],<br>
+ default_values={"dport": 4789},<br>
+ ),<br>
+ # -------------------- VXLAN Tunnel Header --------------------<br>
+ "vxlan": Protocol(<br>
+ name="vxlan",<br>
+ scapy_class=VXLAN,<br>
+ pattern_name="vxlan",<br>
+ fields=[<br>
+ PatternField("vni", "vni", [42, 100]),<br>
+ ],<br>
+ ),<br>
+ # -------------------- VXLAN Inner Protocols --------------------<br>
+ "eth_inner": Protocol(<br>
+ name="eth_inner",<br>
+ scapy_class=Ether,<br>
+ pattern_name="eth",<br>
+ fields=[<br>
+ PatternField("src", "src", ["02:00:00:00:00:00"]),<br>
+ PatternField("dst", "dst", ["02:00:00:00:00:02"]),<br>
+ ],<br>
+ ),<br>
+ "ipv4_inner": Protocol(<br>
+ name="ipv4_inner",<br>
+ scapy_class=IP,<br>
+ pattern_name="ipv4",<br>
+ fields=[<br>
+ PatternField("src", "src", ["192.168.1.1"]),<br>
+ PatternField("dst", "dst", ["192.168.1.2"]),<br>
+ ],<br>
+ ),<br>
+ "ipv6_inner": Protocol(<br>
+ name="ipv6_inner",<br>
+ scapy_class=IPv6,<br>
+ pattern_name="ipv6",<br>
+ fields=[<br>
+ PatternField("src", "src", ["2001:db8::1"]),<br>
+ PatternField("dst", "dst", ["2001:db8::2"]),<br>
+ ],<br>
+ ),<br>
+ "tcp_inner": Protocol(<br>
+ name="tcp_inner",<br>
+ scapy_class=TCP,<br>
+ pattern_name="tcp",<br>
+ fields=[<br>
+ PatternField("sport", "src", [1234]),<br>
+ PatternField("dport", "dst", [80]),<br>
+ ],<br>
+ ),<br>
+ "udp_inner": Protocol(<br>
+ name="udp_inner",<br>
+ scapy_class=UDP,<br>
+ pattern_name="udp",<br>
+ fields=[<br>
+ PatternField("sport", "src", [5000]),<br>
+ PatternField("dport", "dst", [53]),<br>
+ ],<br>
+ ),<br>
}<br>
<br>
<br>
@@ -234,6 +325,7 @@ class JumpTest:<br>
}<br>
<br>
PROTOCOL_STACKS = [<br>
+ # -------------------- Non-tunnel stacks --------------------<br>
[("eth", True)],<br>
[("eth", False), ("ipv4", True)],<br>
[("eth", False), ("ipv4", True), ("tcp", True)],<br>
@@ -253,6 +345,66 @@ class JumpTest:<br>
[("eth", False), ("vlan", False), ("ipv6", True), ("tcp", True)],<br>
[("eth", False), ("vlan", False), ("ipv6", True), ("udp", True)],<br>
[("eth", False), ("arp", True)],<br>
+ # -------------------- VXLAN tunnel stacks --------------------<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv4_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv4_inner", False),<br>
+ ("tcp_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv4_inner", False),<br>
+ ("udp_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv6_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv6_inner", False),<br>
+ ("tcp_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ("ipv6_inner", False),<br>
+ ("udp_inner", False),<br>
+ ],<br>
+ [<br>
+ ("eth_outer", False),<br>
+ ("ipv4_outer", True),<br>
+ ("udp_outer", False),<br>
+ ("vxlan", True),<br>
+ ("eth_inner", False),<br>
+ ],<br>
]<br>
<br>
<br>
@@ -304,6 +456,10 @@ def generate(<br>
) -> list[FlowTest]:<br>
"""Generate test cases for patterns matching fields across multiple protocols.<br>
<br>
+ Pattern parts are assembled in stack order to preserve positional<br>
+ correctness, which is required for tunnel encapsulations where<br>
+ inner and outer layers share the same pattern_name.<br>
+<br>
Args:<br>
protocol_stack: List of (protocol_name, test_fields) tuples.<br>
If test_fields is True, iterate through field combinations.<br>
@@ -316,15 +472,12 @@ def generate(<br>
List of FlowTest objects ready for execution.<br>
"""<br>
action_spec = self.actions[action_name]<br>
-<br>
- wildcard_protocols = [name for name, test_fields in protocol_stack if not test_fields]<br>
- field_test_protocols = [name for name, test_fields in protocol_stack if test_fields]<br>
all_protocol_names = [name for name, _ in protocol_stack]<br>
-<br>
- wildcard_pattern_parts = [self.protocols[name].pattern_name for name in wildcard_protocols]<br>
+ field_test_protocols = [name for name, test_fields in protocol_stack if test_fields]<br>
<br>
if not field_test_protocols:<br>
- pattern = " / ".join(wildcard_pattern_parts)<br>
+ pattern_parts = [self.protocols[name].pattern_name for name, _ in protocol_stack]<br>
+ pattern = " / ".join(pattern_parts)<br>
flow_rule = FlowRule(<br>
direction="ingress",<br>
pattern=[pattern],<br>
@@ -339,7 +492,7 @@ def generate(<br>
packet=packet,<br>
verification_type=action_spec.verification_type,<br>
verification_params=action_spec.build_verification_params(action_value),<br>
- description=" / ".join(wildcard_pattern_parts) + f" -> {<a href="http://action_spec.name" rel="noreferrer" target="_blank">action_spec.name</a>}",<br>
+ description=pattern + f" -> {<a href="http://action_spec.name" rel="noreferrer" target="_blank">action_spec.name</a>}",<br>
)<br>
]<br>
<br>
@@ -354,24 +507,32 @@ def generate(<br>
max_vals = max(len(f_spec.test_parameters) for _, f_spec in field_combo)<br>
<br>
for i in range(max_vals):<br>
- field_pattern_parts = []<br>
+ field_value_map: dict[str, tuple[Protocol, PatternField, Any]] = {}<br>
all_field_values: dict[str, dict[str, Any]] = {}<br>
- desc_parts = []<br>
<br>
for protocol_spec, field_spec in field_combo:<br>
val = field_spec.test_parameters[i % len(field_spec.test_parameters)]<br>
-<br>
- field_pattern_parts.append(<br>
- f"{protocol_spec.pattern_name} {field_spec.pattern_field} is {val}"<br>
- )<br>
+ field_value_map[<a href="http://protocol_spec.name" rel="noreferrer" target="_blank">protocol_spec.name</a>] = (protocol_spec, field_spec, val)<br>
<br>
if <a href="http://protocol_spec.name" rel="noreferrer" target="_blank">protocol_spec.name</a> not in all_field_values:<br>
all_field_values[<a href="http://protocol_spec.name" rel="noreferrer" target="_blank">protocol_spec.name</a>] = {}<br>
all_field_values[<a href="http://protocol_spec.name" rel="noreferrer" target="_blank">protocol_spec.name</a>][field_spec.scapy_field] = val<br>
<br>
- desc_parts.append(f"{<a href="http://protocol_spec.name" rel="noreferrer" target="_blank">protocol_spec.name</a>}[{field_spec.scapy_field}={val}]")<br>
+ pattern_parts = []<br>
+ desc_parts = []<br>
+<br>
+ for proto_name, test_fields in protocol_stack:<br>
+ proto_spec = self.protocols[proto_name]<br>
+ if test_fields and proto_name in field_value_map:<br>
+ _, f_spec, val = field_value_map[proto_name]<br>
+ pattern_parts.append(<br>
+ f"{proto_spec.pattern_name} {f_spec.pattern_field} is {val}"<br>
+ )<br>
+ desc_parts.append(f"{<a href="http://proto_spec.name" rel="noreferrer" target="_blank">proto_spec.name</a>}[{f_spec.scapy_field}={val}]")<br>
+ else:<br>
+ pattern_parts.append(proto_spec.pattern_name)<br>
<br>
- full_pattern = " / ".join(wildcard_pattern_parts + field_pattern_parts)<br>
+ full_pattern = " / ".join(pattern_parts)<br>
<br>
flow_rule = FlowRule(<br>
direction="ingress",<br>
@@ -384,10 +545,7 @@ def generate(<br>
all_protocol_names, all_field_values, add_payload=True<br>
)<br>
<br>
- wildcard_desc = " / ".join(wildcard_pattern_parts)<br>
- field_desc = " / ".join(desc_parts)<br>
- full_desc = f"{wildcard_desc} / {field_desc}" if wildcard_desc else field_desc<br>
-<br>
+ full_desc = " / ".join(desc_parts)<br>
test_cases.append(<br>
FlowTest(<br>
flow_rule=flow_rule,<br>
@@ -403,7 +561,7 @@ def generate(<br>
<br>
@requires_nic_capability(NicCapability.FLOW_CTRL)<br>
class TestFlowOffload(TestSuite):<br>
- """Flow offload test suite.<br>
+ """Flow Offload test suite.<br>
<br>
This suite consists of 4 test cases:<br>
1. Queue Action: Verifies queue actions with multi-protocol patterns<br>
--<br>
2.55.0<br>
<br>
</blockquote></div></div>
</blockquote></div>