<div dir="ltr"><div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, May 11, 2026 at 11:46 AM 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">Port over the IP GRE test suite from old DTS to next DTS. This test<br>
suite covers GRE tunneling and checksum offload verification using this<br>
protocol.<br>
<br>
Bugzilla ID: 1480<br>
<br>
Signed-off-by: Andrew Bailey <<a href="mailto:abailey@iol.unh.edu" target="_blank">abailey@iol.unh.edu</a>><br>
---<br>
 doc/api/dts/tests.TestSuite_ip_gre.rst |   8 +<br>
 dts/api/testpmd/__init__.py            |  23 ++<br>
 dts/tests/TestSuite_ip_gre.py          | 301 +++++++++++++++++++++++++<br>
 3 files changed, 332 insertions(+)<br>
 create mode 100644 doc/api/dts/tests.TestSuite_ip_gre.rst<br>
 create mode 100644 dts/tests/TestSuite_ip_gre.py<br>
<br>
diff --git a/doc/api/dts/tests.TestSuite_ip_gre.rst b/doc/api/dts/tests.TestSuite_ip_gre.rst<br>
new file mode 100644<br>
index 0000000000..e8ce01dc0b<br>
--- /dev/null<br>
+++ b/doc/api/dts/tests.TestSuite_ip_gre.rst<br>
@@ -0,0 +1,8 @@<br>
+.. SPDX-License-Identifier: BSD-3-Clause<br>
+<br>
+ip_gre Test Suite<br>
+=================<br>
+<br>
+.. automodule:: tests.TestSuite_ip_gre<br>
+   :members:<br>
+   :show-inheritance:<br>
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py<br>
index e9187440bb..bb5cbd6725 100644<br>
--- a/dts/api/testpmd/__init__.py<br>
+++ b/dts/api/testpmd/__init__.py<br>
@@ -951,6 +951,29 @@ def set_flow_control(<br>
                     f"Testpmd failed to set the {flow_ctrl} in port {port}."<br>
                 )<br>
<br>
+    def set_csum_parse_tunnel(self, port: int, on: bool, verify: bool = True) -> None:<br>
+        """Set parse tunnel on or of in testpmd for a given port.<br></blockquote><div><br></div><div>Should be “off” not “of”<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+        Args:<br>
+            port: The ID of the requested port<br>
+            on: set parse tunnel on if `on` is :data:`True`, otherwise off<br>
+            verify: if :data:`True`, the output of the command is scanned to verify that<br>
+                parse tunnel was set successfully<br>
+<br>
+        Raises:<br>
+            InteractiveCommandExecutionError: If `verify` is :data:`True` and the command<br>
+                fails to execute.<br>
+<br>
+        """<br>
+        output = self.send_command(f"csum parse-tunnel {'on' if on else 'off'} {port}")<br>
+        if verify and f"Parse tunnel is {'on' if on else'off'}" not in output:<br></blockquote><div><br></div><div>Missing a space after "else".</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+            self._logger.debug(<br>
+                f"Testpmd failed to set csum parse-tunnel {'on' if on else 'off'} in port {port}"<br>
+            )<br>
+            raise InteractiveCommandExecutionError(<br>
+                f"Testpmd failed to set csum parse-tunnel {'on' if on else 'off'} in port {port}"<br>
+            )<br>
+<br>
     def show_port_flow_info(self, port: int) -> TestPmdPortFlowCtrl | None:<br>
         """Show port info flow.<br>
<br>
diff --git a/dts/tests/TestSuite_ip_gre.py b/dts/tests/TestSuite_ip_gre.py<br>
new file mode 100644<br>
index 0000000000..fc51eef181<br>
--- /dev/null<br>
+++ b/dts/tests/TestSuite_ip_gre.py<br>
@@ -0,0 +1,301 @@<br>
+# SPDX-License-Identifier: BSD-3-Clause<br>
+# Copyright(c) 2026 University of New Hampshire<br>
+<br>
+"""DPDK IP GRE test suite."""<br>
+<br>
+from scapy.layers.inet import GRE, IP, TCP, UDP<br>
+from scapy.layers.inet6 import IPv6<br>
+from scapy.layers.l2 import Dot1Q, Ether<br>
+from scapy.layers.sctp import SCTP<br>
+from scapy.packet import Packet<br>
+<br>
+from api.capabilities import (<br>
+    NicCapability,<br>
+    requires_nic_capability,<br>
+)<br>
+from api.packet import send_packet_and_capture<br>
+from api.test import verify<br>
+from api.testpmd import TestPmd<br>
+from api.testpmd.config import SimpleForwardingModes<br>
+from api.testpmd.types import (<br>
+    ChecksumOffloadOptions,<br>
+    PacketOffloadFlag,<br>
+    RtePTypes,<br>
+    TestPmdVerbosePacket,<br>
+)<br>
+from framework.test_suite import TestSuite, func_test<br>
+<br>
+SRC_ID = "FF:FF:FF:FF:FF:FF"<br></blockquote><div><br></div><div>Why did you choose the broadcast address instead of a "normal" address? Just want to hear your reasoning. </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+<br>
+class TestIpGre(TestSuite):<br>
+    """IP GRE test suite."""<br>
+<br>
+    def _check_for_matching_packet(<br>
+        self, output: list[TestPmdVerbosePacket], flags: RtePTypes<br>
+    ) -> bool:<br>
+        """Returns :data:`True` if the packet in verbose output contains all specified flags."""<br>
+        for packet in output:<br>
+            if packet.src_mac == SRC_ID:<br>
+                if flags not in packet.hw_ptype and flags not in packet.sw_ptype:<br>
+                    return False<br>
+        return True<br>
+<br>
+    def _send_packet_and_verify_flags(<br>
+        self, expected_flag: RtePTypes, packet: Packet, testpmd: TestPmd<br>
+    ) -> None:<br>
+        """Sends a packet to the DUT and verifies the verbose ptype flags."""<br>
+        send_packet_and_capture(packet=packet)<br>
+        verbose_output = testpmd.extract_verbose_output(testpmd.stop(verify=True))<br>
+        valid = self._check_for_matching_packet(output=verbose_output, flags=expected_flag)<br>
+        verify(valid, f"Packet type flag did not match the expected flag: {expected_flag}.")<span style="background-color:transparent"> </span></blockquote><div><br></div><div>Is it worth logging a packet transmission issue vs a flag setting / flag processing issue in different ways? And re the previous 2 functions if verbose_output is None will _check_for_matching_packet return True in error? Just checking that this is sound.</div><div><span style="background-color:transparent"> </span></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+    def _setup_session(<br>
+        self, testpmd: TestPmd, expected_flags: list[RtePTypes], packet_list=list[Packet]<br>
+    ) -> None:<br>
+        """Sets the forwarding and verbose mode of each test case interactive shell session."""<br>
+        testpmd.set_forward_mode(SimpleForwardingModes.rxonly)<br>
+        testpmd.set_verbose(level=1)<br>
+        for i in range(0, len(packet_list)):<br>
+            testpmd.start(verify=True)<br>
+            self._send_packet_and_verify_flags(<br>
+                expected_flag=expected_flags[i], packet=packet_list[i], testpmd=testpmd<br>
+            )<br></blockquote><div><br></div><div>If expected_flags and packet_list are related lists of the same length, is it better if we use 1 list of tuples [(expected flag, packet)]?</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+    def _send_packet_and_verify_checksum(<br>
+        self, packet: Packet, good_L4: bool, good_IP: bool, testpmd: TestPmd<br></blockquote><div><br></div><div>If we are going to say good_L4 can we say good_L3 or does that reduce clarity to you? Figure we should stay consistent.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    ) -> None:<br>
+        """Send packet and verify verbose output matches expected output."""<br>
+        testpmd.start()<br>
+        send_packet_and_capture(packet=packet)<br>
+        verbose_output = testpmd.extract_verbose_output(testpmd.stop())<br>
+        is_IP = is_L4 = None<br>
+        for testpmd_packet in verbose_output:<br>
+            if testpmd_packet.src_mac == SRC_ID:<br>
+                is_IP = PacketOffloadFlag.RTE_MBUF_F_RX_IP_CKSUM_GOOD in testpmd_packet.ol_flags<br>
+                is_L4 = PacketOffloadFlag.RTE_MBUF_F_RX_L4_CKSUM_GOOD in testpmd_packet.ol_flags<br></blockquote><div><br></div><div>is_IP -> good_L3_checksum? If the meaning of the is_IP variable is that there is an IP layer, I disagree with the name because it is possible for there to be an IP layer and a bad checksum. I do see below the verify logic is for if the checksum is correct.</div><div><br></div><div>Do we have any logic here to verify we are acting on the correct packet, given that it may not be a quiet wire?</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+        verify(<br>
+            is_IP is not None and is_L4 is not None,<br>
+            "Test packet was dropped when it should have been received.",<br></blockquote><div><br></div><div>Reviewed-by: Patrick Robb <<a href="mailto:patrickrobb1997@gmail.com">patrickrobb1997@gmail.com</a>> </div></div></div>
</div>