<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 gmail_quote_container"><div dir="ltr" class="gmail_attr">On Tue, Aug 11, 2026 at 1:04 PM Andrew Bailey <<a href="mailto:abailey@iol.unh.edu">abailey@iol.unh.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;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>