Show a patch.

GET /api/patches/74497/
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 74497,
    "url": "https://patches.dpdk.org/api/patches/74497/",
    "web_url": "https://patches.dpdk.org/patch/74497/",
    "project": {
        "id": 3,
        "url": "https://patches.dpdk.org/api/projects/3/",
        "name": "DTS",
        "link_name": "dts",
        "list_id": "dts.dpdk.org",
        "list_email": "dts@dpdk.org",
        "web_url": "",
        "scm_url": "git://dpdk.org/tools/dts",
        "webscm_url": "http://git.dpdk.org/tools/dts/"
    },
    "msgid": "<20200720140744.210132-1-ohilyard@iol.unh.edu>",
    "date": "2020-07-20T14:07:44",
    "name": "checksum checks: Add hardware offload l3 and l4 cases",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "9a6833d2e221d6a8a2d8cc159ce60a786c752282",
    "submitter": {
        "id": 1829,
        "url": "https://patches.dpdk.org/api/people/1829/",
        "name": "Owen Hilyard",
        "email": "ohilyard@iol.unh.edu"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/patch/74497/mbox/",
    "series": [
        {
            "id": 11172,
            "url": "https://patches.dpdk.org/api/series/11172/",
            "web_url": "https://patches.dpdk.org/project/dts/list/?series=11172",
            "date": "2020-07-20T14:07:44",
            "name": "checksum checks: Add hardware offload l3 and l4 cases",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/11172/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/74497/comments/",
    "check": "pending",
    "checks": "https://patches.dpdk.org/api/patches/74497/checks/",
    "tags": {},
    "headers": {
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dts>,\n <mailto:dts-request@dpdk.org?subject=subscribe>",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "X-Gm-Message-State": "AOAM533mxmgbRKuphT0amaCVMvIKm7+s1e3HYnNe4qTHYvgLMks8JUi3\n hBBx7cpERk89fS/l7hcZ0zxGIcCSRHE3X4I9SgJo3I+BBVL5Ka7UDaqxgiKl1pW17r8BiyTNaOz\n XOJ2l905UqEiiEyEv+YxwTZGLlM7+vUbsCeoXU6UjcK9/eW6h1RtREDT3+A==",
        "X-Google-Smtp-Source": "\n ABdhPJzwNtkDYEspkC/g+q+cR7h60fKzwVMvRmt28LrrQN82wHz7SaQjhAtprl1J3GE0WyOp3iHdeA==",
        "List-Post": "<mailto:dts@dpdk.org>",
        "MIME-Version": "1.0",
        "List-Id": "test suite reviews and discussions <dts.dpdk.org>",
        "X-BeenThere": "dts@dpdk.org",
        "References": "<20200714145355.18410-1-ohilyard@iol.unh.edu>",
        "Subject": "[dts] [PATCH] checksum checks: Add hardware offload l3 and l4 cases",
        "Sender": "\"dts\" <dts-bounces@dpdk.org>",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 741A8A0540;\n\tMon, 20 Jul 2020 16:07:53 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 319DD1DBB;\n\tMon, 20 Jul 2020 16:07:53 +0200 (CEST)",
            "from mail-qt1-f182.google.com (mail-qt1-f182.google.com\n [209.85.160.182]) by dpdk.org (Postfix) with ESMTP id DE3BF1AFB\n for <dts@dpdk.org>; Mon, 20 Jul 2020 16:07:51 +0200 (CEST)",
            "by mail-qt1-f182.google.com with SMTP id g13so13034964qtv.8\n for <dts@dpdk.org>; Mon, 20 Jul 2020 07:07:51 -0700 (PDT)",
            "from ohilyard-Alienware-m17.iol.unh.edu\n ([2606:4100:3880:1254::1033])\n by smtp.gmail.com with ESMTPSA id a20sm20178021qtw.54.2020.07.20.07.07.46\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 20 Jul 2020 07:07:47 -0700 (PDT)"
        ],
        "List-Archive": "<http://mails.dpdk.org/archives/dts/>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "X-Mailer": "git-send-email 2.25.1",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dts>,\n <mailto:dts-request@dpdk.org?subject=unsubscribe>",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20161025;\n h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n :references:mime-version:content-transfer-encoding;\n bh=X9IVoRlh6w4belSqb1wmOYoy+pfxCshOg7gXowFHgh0=;\n b=Op5FG/fXJDXCqH1CuSzWnU/VBLgqxmXRcuc7LQV5E7G6aYwhZlDMTOiBl5hSgUShCX\n +j8aU9U1MiEq71oGfDsGp58eNvv2rrlrbf73o+1hlUcs+RythWrB+jG1lEUa+PvnUu1d\n elJDA9BBdSfCsd26gXS1YhRl47pa7IobqBnh/YEvaEuvsvjU/kwz3UE01hv7M1CyThx7\n LiQCDJB/krtxBiW3JIZC/ZweGCU24J3ErcglaNuJIhNsIiVxYTgTQLwDm1rMv/7oOfjz\n TJiIakwaUFiVRvoK5SK9kREmp9+ZP13piw9/YN3hwyIcQ6CXoSbY7lDuFl/YqUoap4iF\n Y0nw==",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu;\n s=unh-iol;\n h=from:to:cc:subject:date:message-id:in-reply-to:references\n :mime-version:content-transfer-encoding;\n bh=X9IVoRlh6w4belSqb1wmOYoy+pfxCshOg7gXowFHgh0=;\n b=b8UhLxNQBufWoUGhRV/rf4y4bCeEDqPbhqkkmyTWurn7Mv+7fc3mpYOp+x9lCmj/f3\n OcLdZjez7FNnuDWLH2VMXQVJhZH31+HZ/ZBXPqdsgvsq5yiXav073rJ5ArwnLXQq35sP\n xJbAW5MyOynL1MpsSukUSOQmsIT5W7cNbPxgU=",
        "Date": "Mon, 20 Jul 2020 10:07:44 -0400",
        "Content-Transfer-Encoding": "8bit",
        "X-Received": "by 2002:aed:2684:: with SMTP id q4mr23722426qtd.208.1595254067780;\n Mon, 20 Jul 2020 07:07:47 -0700 (PDT)",
        "To": "dts@dpdk.org",
        "From": "Owen Hilyard <ohilyard@iol.unh.edu>",
        "In-Reply-To": "<20200714145355.18410-1-ohilyard@iol.unh.edu>",
        "Cc": "ferruh.yigit@intel.com, arybchenko@solarflare.com, olivier.matz@6wind.com,\n david.marchand@redhat.com, ivan.malov@oktetlabs.ru,\n bruce.richardson@intel.com, lylavoie@iol.unh.edu, rasland@mellanox.com,\n j.hendergart@f5.com, ohilyard@iol.unh.edu, thomas@monjalon.net",
        "List-Help": "<mailto:dts-request@dpdk.org?subject=help>",
        "Errors-To": "dts-bounces@dpdk.org",
        "Message-Id": "<20200720140744.210132-1-ohilyard@iol.unh.edu>",
        "Return-Path": "<dts-bounces@dpdk.org>"
    },
    "content": "add rx and tx l3 test cases\nadd tx l4 test case\nadd documentation for new test cases\n\nSigned-off-by: Owen Hilyard <ohilyard@iol.unh.edu>\n---\n test_plans/checksum_offload_test_plan.rst | 122 +++++++-\n tests/TestSuite_checksum_offload.py       | 331 +++++++++++++++++++---\n 2 files changed, 412 insertions(+), 41 deletions(-)",
    "diff": "diff --git a/test_plans/checksum_offload_test_plan.rst b/test_plans/checksum_offload_test_plan.rst\nindex 4fd8c5a..de2e1a9 100644\n--- a/test_plans/checksum_offload_test_plan.rst\n+++ b/test_plans/checksum_offload_test_plan.rst\n@@ -221,7 +221,7 @@ combination: good/bad ip checksum + good/bad udp/tcp checksum.\n \n Check the Rx checksum flags consistent with expected flags.\n \n-Test Case: Hardware Checksum Check L4\n+Test Case: Hardware Checksum Check L4 RX\n ===========================================\n This test involves testing many different scenarios with a L4 checksum.\n A variety of tunneling protocols, L3 protocols and L4 protocols are combined\n@@ -256,4 +256,122 @@ Send a packet with a bad checksum::\n    rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n    tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n \n-Verify flags are as expected.\n\\ No newline at end of file\n+Verify flags are as expected.\n+\n+Test Case: Hardware Checksum Check L3 RX\n+===========================================\n+This test involves testing L3 checksum hardware offload.\n+Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and IPv6's\n+lack of a checksum, only IPv4's checksum is tested.\n+\n+Setup the ``csum`` forwarding mode::\n+\n+  testpmd> set fwd csum\n+  Set csum packet forwarding mode\n+\n+Start the packet forwarding::\n+\n+  testpmd> start\n+    csum packet forwarding - CRC stripping disabled - packets/burst=32\n+    nb forwarding cores=1 - nb forwarding ports=10\n+    RX queues=1 - RX desc=128 - RX free threshold=64\n+    RX threshold registers: pthresh=8 hthresh=8 wthresh=4\n+    TX queues=1 - TX desc=512 - TX free threshold=0\n+    TX threshold registers: pthresh=32 hthresh=8 wthresh=8\n+\n+Send a packet with a good checksum::\n+\n+   port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD  PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Send a packet with a bad checksum::\n+\n+   port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Verify flags are as expected.\n+\n+Test Case: Hardware Checksum Check L4 TX\n+===========================================\n+This test involves testing many different scenarios with a L4 checksum.\n+A variety of tunneling protocols, L3 protocols and L4 protocols are combined\n+to test as many scenarios as possible. Currently, UDP, TCP and SCTP are used\n+as L4 protocols, with IP and IPv6 being used at level 3. The tested tunneling\n+protocols are VXLAN and GRE. This test is used to determine whether the\n+hardware offloading of checksums works properly.\n+\n+Setup the ``csum`` forwarding mode::\n+\n+  testpmd> set fwd csum\n+  Set csum packet forwarding mode\n+\n+Start the packet forwarding::\n+\n+  testpmd> start\n+    csum packet forwarding - CRC stripping disabled - packets/burst=32\n+    nb forwarding cores=1 - nb forwarding ports=10\n+    RX queues=1 - RX desc=128 - RX free threshold=64\n+    RX threshold registers: pthresh=8 hthresh=8 wthresh=4\n+    TX queues=1 - TX desc=512 - TX free threshold=0\n+    TX threshold registers: pthresh=32 hthresh=8 wthresh=8\n+\n+\n+Start a packet capture on the tester in the backround::\n+\n+   # tcpdump -i <iface> -s 65535 -w /tmp/tester/test_hardware_checksum_check_l4_tx_capture.pcap &\n+\n+Send a packet with a good checksum::\n+\n+   port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD  PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Send a packet with a bad checksum::\n+\n+   port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_GOOD PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Inspect the pcap file from the packet capture and verify the checksums.\n+\n+Test Case: Hardware Checksum Check L3 TX\n+===========================================\n+This test involves testing L3 checksum hardware offload.\n+Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and IPv6's\n+lack of a checksum, only IPv4's checksum is tested.\n+\n+Setup the ``csum`` forwarding mode::\n+\n+  testpmd> set fwd csum\n+  Set csum packet forwarding mode\n+\n+Start the packet forwarding::\n+\n+  testpmd> start\n+    csum packet forwarding - CRC stripping disabled - packets/burst=32\n+    nb forwarding cores=1 - nb forwarding ports=10\n+    RX queues=1 - RX desc=128 - RX free threshold=64\n+    RX threshold registers: pthresh=8 hthresh=8 wthresh=4\n+    TX queues=1 - TX desc=512 - TX free threshold=0\n+    TX threshold registers: pthresh=32 hthresh=8 wthresh=8\n+\n+\n+Start a packet capture on the tester in the backround::\n+\n+   # tcpdump -i <iface> -s 65535 -w /tmp/tester/test_hardware_checksum_check_l3_tx_capture.pcap &\n+\n+Send a packet with a good checksum with a 1 in it's payload::\n+\n+   port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD  PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Send a packet with a bad checksum with a 0 in it's payload::\n+\n+   port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1:\n+   rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN\n+   tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4\n+\n+Inspect the pcap file from the packet capture and verify the checksums.\ndiff --git a/tests/TestSuite_checksum_offload.py b/tests/TestSuite_checksum_offload.py\nindex 9ff5a41..c4a877d 100644\n--- a/tests/TestSuite_checksum_offload.py\n+++ b/tests/TestSuite_checksum_offload.py\n@@ -47,8 +47,18 @@ import time\n \n from rst import RstReport\n import utils\n-\n+from exception import VerifyFailure\n+from pktgen import PacketGeneratorHelper\n+from scapy.layers.inet import UDP, TCP, IP\n+from scapy.layers.inet6 import IPv6\n+from scapy.layers.l2 import Ether, GRE\n+from scapy.layers.sctp import SCTP\n+from scapy.layers.vxlan import VXLAN\n+from scapy.packet import Raw\n+from scapy.utils import wrpcap, rdpcap\n+from test_capabilities import DRIVER_TEST_LACK_CAPA\n from test_case import TestCase\n+\n from framework.pmd_output import PmdOutput\n from test_capabilities import DRIVER_TEST_LACK_CAPA\n from pktgen import PacketGeneratorHelper\n@@ -57,6 +67,33 @@ import packet\n \n from settings import FOLDERS\n \n+l3_proto_classes = [\n+    IP,\n+    IPv6\n+]\n+\n+l4_proto_classes = [\n+    UDP,\n+    TCP,\n+    SCTP\n+]\n+\n+tunnelling_proto_classes = [\n+    VXLAN,\n+    GRE,\n+]\n+\n+l3_protos = [\n+    \"IP\",\n+    \"IPv6\"\n+]\n+\n+l4_protos = [\n+    \"UDP\",\n+    \"TCP\",\n+    \"SCTP\",\n+]\n+\n \n class TestChecksumOffload(TestCase):\n \n@@ -79,8 +116,6 @@ class TestChecksumOffload(TestCase):\n             cur_path = os.path.dirname(\n                 os.path.dirname(os.path.realpath(__file__)))\n             self.output_path = os.sep.join([cur_path, self.logger.log_path])\n-        # create an instance to set stream field setting\n-        self.pktgen_helper = PacketGeneratorHelper()\n \n     def set_up(self):\n         \"\"\"\n@@ -243,7 +278,6 @@ class TestChecksumOffload(TestCase):\n     def send_pkt_expect_good_bad_from_flag(self, pkt_str: str, flag: str, test_name: str, should_pass: bool = True):\n         self.pmdout.get_output(timeout=5)  # Remove any old output\n         self.scapy_exec(f\"sendp({pkt_str}, iface=iface)\")\n-        time.sleep(1)\n         testpmd_output: str = self.pmdout.get_output(timeout=5)\n         self.verify(flag in testpmd_output,\n                     f\"Flag {flag[:-1]} not found for test {test_name}, please run test_rx_checksum_valid_flags.\")\n@@ -269,11 +303,147 @@ class TestChecksumOffload(TestCase):\n \n         return None\n \n-    def scapy_exec(self, cmd: str):\n-        return self.tester.send_expect(cmd, \">>>\")\n+    def validate_checksum(self, pkt, layer) -> bool:\n+        \"\"\"\n+        @param pkt: The packet to validate the checksum of.\n+        @return: Whether the checksum was valid.\n+        \"\"\"\n+        if pkt is None:\n+            return False\n+\n+        csum = pkt[layer].chksum\n+        del pkt[layer].chksum\n+        # Converting it to raw will calculate the checksum\n+        return layer(Raw(pkt[layer])).chksum == csum\n \n-    def scapy_send_append(self, packet: str):\n-        return self.tester.scapy_append(f'sendp({packet}, iface=iface)')\n+    def scapy_exec(self, cmd: str, timeout=1) -> str:\n+        return self.tester.send_expect(cmd, \">>>\", timeout=timeout)\n+\n+    def get_packets(self, dut_mac, tester_mac):\n+        eth = Ether(dst=dut_mac, src=tester_mac)\n+        packets = []\n+        checksum_options = ({}, {'chksum': 0xf},)\n+        # Untunneled\n+        for l3 in l3_proto_classes:\n+            for l4 in l4_proto_classes:\n+                for chksum in checksum_options:\n+                    # The packet's data can be used to identify how the packet was constructed so avoid any issues with\n+                    # ordering\n+                    pkt = eth / l3() / l4(**chksum) / (\n+                        f'UNTUNNELED,{l3.__name__},{l4.__name__},{\" \" if len(chksum.values()) == 0 else chksum[\"chksum\"]}'\n+                    )\n+\n+                    # Prevents the default behavior which adds DNS headers\n+                    if l4 == UDP:\n+                        pkt[UDP].dport, pkt[UDP].sport = 1001, 1001\n+\n+                    packets.append(pkt)\n+\n+        # Tunneled\n+        # VXLAN\n+        for l3 in l3_proto_classes:\n+            for l4 in l4_proto_classes:\n+                for outer_arg in checksum_options:\n+                    for inner_arg in checksum_options:\n+                        pkt = eth / l3() / UDP(**outer_arg) / VXLAN() / Ether() / l3() / l4(**inner_arg) / (\n+                            f'VXLAN,{l3.__name__},{l4.__name__},'\n+                            f'{\" \" if len(outer_arg.values()) == 0 else outer_arg[\"chksum\"]},'\n+                            f'{\" \" if len(inner_arg.values()) == 0 else inner_arg[\"chksum\"]}'\n+                        )\n+                        # Prevents the default behavior which adds DNS headers\n+                        if l4 == UDP:\n+                            pkt[VXLAN][UDP].dport, pkt[VXLAN][UDP].sport = 1001, 1001\n+\n+                        packets.append(pkt)\n+        # GRE\n+        for l3 in l3_proto_classes:\n+            for l4 in l4_proto_classes:\n+                for chksum in checksum_options:\n+                    pkt = eth / l3() / GRE() / l3() / l4(**chksum) / (\n+                        f'GRE,{l3.__name__},{l4.__name__},{\" \" if len(chksum.values()) == 0 else chksum[\"chksum\"]}'\n+                    )\n+\n+                    # Prevents the default behavior which adds DNS headers\n+                    if l4 == UDP:\n+                        pkt[GRE][UDP].dport, pkt[GRE][UDP].sport = 1001, 1001\n+\n+                    packets.append(pkt)\n+\n+        return packets\n+\n+    def replay_pcap_file_on_tester(self, iface, packet_file_path):\n+        self.tester.send_expect(\"scapy\", \">>>\")\n+        self.scapy_exec(f\"packets = rdpcap('{packet_file_path}')\")\n+        self.scapy_exec(f\"sendp(packets, iface={iface})\")\n+        self.tester.send_expect(\"quit()\", \"# \")\n+\n+    def validate_packet_list_checksums(self, packets):\n+        name_to_class_dict = {\n+            'UDP': UDP,\n+            'TCP': TCP,\n+            'SCTP': SCTP,\n+            'IP': IP,\n+            'IPv6': IPv6,\n+            'VXLAN': VXLAN,\n+            'GRE': GRE,\n+        }\n+\n+        error_messages = []\n+\n+        untunnelled_error_message = f\"Invalid untunneled checksum state for %s/%s with a %s checksum.\"\n+\n+        vxlan_error_message = f\"Invalid VXLAN tunnelled %s checksum state for %s/%s\" \\\n+                              f\" with a %s inner checksum and a %s outer checksum.\"\n+\n+        gre_error_message = f\"Invalid GRE tunnelled checksum state for %s/%s with a %s checksum.\"\n+\n+        for packet in packets:\n+            payload: str\n+            # try:\n+            payload = packet[Raw].load.decode('utf-8').split(\",\")\n+            # # This error usually happens with tunneling protocols, and means that an additional cast is needed\n+            # except UnicodeDecodeError:\n+            #     for proto in tunnelling_proto_classes:\n+\n+            l3 = name_to_class_dict[payload[1]]\n+            l4 = name_to_class_dict[payload[2]]\n+            if payload[0] == \"UNTUNNELED\":\n+                chksum_should_be_valid = payload[3] == \" \"\n+                if self.validate_checksum(packet, l4) != chksum_should_be_valid:\n+                    error_messages.append(\n+                        untunnelled_error_message % (\n+                            l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid == '' else 'invalid'\n+                        )\n+                    )\n+            elif payload[0] == \"VXLAN\":\n+                outer_chksum_should_be_valid = payload[3] == \" \"\n+                inner_chksum_should_be_valid = payload[4] == \" \"\n+                if self.validate_checksum(packet[VXLAN], l4) != inner_chksum_should_be_valid:\n+                    error_messages.append(\n+                        vxlan_error_message % (\n+                            \"inner\", l4.__name__, l3.__name__,\n+                            'valid' if inner_chksum_should_be_valid == '' else 'invalid',\n+                            'valid' if outer_chksum_should_be_valid == '' else 'invalid'\n+                        )\n+                    )\n+                if self.validate_checksum(packet, l4) != outer_chksum_should_be_valid:\n+                    error_messages.append(\n+                        vxlan_error_message % (\n+                            \"outer\", l3.__name__, l4.__name__,\n+                            'valid' if inner_chksum_should_be_valid == '' else 'invalid',\n+                            'valid' if outer_chksum_should_be_valid == '' else 'invalid'\n+                        )\n+                    )\n+            elif payload[0] == \"GRE\":\n+                chksum_should_be_valid = payload[3] == \" \"\n+                if self.validate_checksum(packet, l4) != chksum_should_be_valid:\n+                    error_messages.append(\n+                        gre_error_message % (\n+                            l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid == '' else 'invalid'\n+                        )\n+                    )\n+\n+        return error_messages\n \n     #\n     #\n@@ -448,6 +618,9 @@ class TestChecksumOffload(TestCase):\n \n             # clear streams before add new streams\n             self.tester.pktgen.clear_streams()\n+            # create an instance to set stream field setting\n+            # Moved here because it messes with the ability of the functional tests to use scapy.\n+            self.pktgen_helper = PacketGeneratorHelper()\n             # run packet generator\n             streams = self.pktgen_helper.prepare_stream_from_tginput(tgenInput, 100,\n                     None, self.tester.pktgen)\n@@ -511,53 +684,93 @@ class TestChecksumOffload(TestCase):\n             self.dut.send_expect(\"quit\", \"#\", 10)\n             self.result_table_print()\n \n-    def test_hardware_checksum_check_ip(self):\n+    def test_hardware_checksum_check_ip_rx(self):\n         self.dut.send_expect(\"start\", \"testpmd>\")\n+        self.tester.send_expect(\"scapy\", \">>>\")\n         self.checksum_enablehw(self.dut_ports[0])\n+        self.dut.send_expect(\"start\", \"testpmd>\")\n \n         verification_errors: List[VerifyFailure] = []\n \n-        # untunnelled\n-        vf = self.try_helper_hardware_checksum_check_catch_failure('Ether(dst=\"%s\", src=\"%s\")/IP(%s)/UDP()/(\"X\"*50)',\n-                                                                   \"PKT_RX_IP_CKSUM_\")\n-        if vf is not None:\n-            verification_errors.append(vf)\n+        iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0]))\n+        dut_mac = self.dut.get_mac_address(self.dut_ports[0])\n+        tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0]))\n \n-        # tunneled inner\n-        vf = self.try_helper_hardware_checksum_check_catch_failure(\n-            'Ether(dst=\"%s\", src=\"%s\")/IP()/UDP()/IP(%s)/(\"X\"*50)',\n-            \"PKT_RX_OUTER_IP_CKSUM_\")\n-        if vf is not None:\n-            verification_errors.append(vf)\n+        self.scapy_exec(f\"eth = Ether(dst='{dut_mac}', src='{tester_mac}')\")\n+        self.scapy_exec(f\"iface = '{iface}'\")\n \n-        # tunneled outer\n-        vf = self.try_helper_hardware_checksum_check_catch_failure(\n-            'Ether(dst=\"%s\", src=\"%s\")/IP()/UDP()/IP(%s)/(\"X\"*50)',\n-            \"PKT_RX_OUTER_IP_CKSUM_\")\n-        if vf is not None:\n-            verification_errors.append(vf)\n+        # Untunnelled\n+        for l4 in l4_protos:\n+            for chksum in \"\", \"chksum=0xf\":\n+                vf = self.send_pkt_expect_good_bad_from_flag_catch_failure(\n+                    f\"eth/IP({chksum})/{l4}()/(X'*50)\",\n+                    \"PKT_RX_IP_CKSUM_\", f\"{l4}\",\n+                    should_pass=(chksum == \"\"))\n+                if vf is not None:\n+                    verification_errors.append(vf)\n \n-        self.verify(len(verification_errors) == 0, \"\\n\".join(verification_errors))\n+        for err in verification_errors:\n+            self.logger.error(str(err))\n+        self.verify(len(verification_errors) == 0, \"See previous output\")\n \n+        self.tester.send_expect(\"quit()\", \"# \")\n         self.dut.send_expect(\"stop\", \"testpmd>\")\n \n-    def test_hardware_checksum_check_l4(self):\n+    def test_hardware_checksum_check_ip_tx(self):\n         self.checksum_enablehw(self.dut_ports[0])\n         self.dut.send_expect(\"start\", \"testpmd>\")\n \n         verification_errors: List[VerifyFailure] = []\n \n-        l3_protos: List[str] = [\n-            \"IP\",\n-            \"IPv6\"\n-        ]\n+        iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0]))\n+        dut_mac = self.dut.get_mac_address(self.dut_ports[0])\n+        tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0]))\n+        eth = Ether(dst=dut_mac, src=tester_mac)\n \n-        l4_protos: List[str] = [\n-            \"UDP\",\n-            \"TCP\",\n-            \"SCTP\",\n+        checksum_options = ({}, {'chksum': 0xf},)\n+\n+        packets = [\n+            eth / IP(**chksum) / TCP() / Raw(load=str(int(len(chksum) != 1))) for chksum in checksum_options\n         ]\n \n+        capture_file_name = \"test_hardware_checksum_check_l3_tx_capture.pcap\"\n+\n+        packet_file_path = \"/tmp/test_hardware_checksum_check_l3_tx_packets.pcap\"\n+        capture_file_path = \"/tmp/tester/\" + capture_file_name\n+\n+        self.tester.send_expect(f\"tcpdump -i {iface} -s 65535 -w {capture_file_path} &\", \"# \")\n+\n+        wrpcap(packet_file_path, packets)\n+        self.tester.session.copy_file_to(packet_file_path, packet_file_path)\n+\n+        self.replay_pcap_file_on_tester(iface, packet_file_path)\n+\n+        self.tester.session.copy_file_from(packet_file_path, \"output/tmp/pcap/\" + capture_file_name)\n+\n+        captured_packets = rdpcap(\"output/tmp/pcap/\" + capture_file_name)\n+\n+        self.verify(len(packets) == len(captured_packets), \"Not all packets were received\")\n+\n+        error_messages = []\n+        for pkt in captured_packets:\n+            should_pass = pkt[TCP].payload.build() == b'1'\n+            if not (self.validate_checksum(pkt, IP) == should_pass):\n+                error_messages.append(f\"A packet was marked as having a\"\n+                                      f\"{' valid' if should_pass == '' else 'n invalid'}\"\n+                                      f\" checksum when it should have had the opposite.\")\n+\n+        self.dut.send_expect(\"stop\", \"testpmd>\")\n+        if len(error_messages) != 0:\n+            for error_msg in error_messages:\n+                self.logger.error(error_msg)\n+            self.verify(False, \"See prior output\")\n+\n+    def test_hardware_checksum_check_l4_rx(self):\n+        self.checksum_enablehw(self.dut_ports[0])\n+        self.dut.send_expect(\"start\", \"testpmd>\")\n+\n+        verification_errors: List[VerifyFailure] = []\n+\n         iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0]))\n         dut_mac = self.dut.get_mac_address(self.dut_ports[0])\n         tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0]))\n@@ -589,7 +802,7 @@ class TestChecksumOffload(TestCase):\n                                 should_pass = outer_arg == \"\"\n                             vf = self.send_pkt_expect_good_bad_from_flag_catch_failure(\n                                 f\"eth/{l3}()/{l4}({outer_arg})/VXLAN()/{l3}()/\"\n-                                f\"{l4}({inner_arg})/('X'*50)\",\n+                                f\"{l4}(chksum={inner_arg})/('X'*50)\",\n                                 flag, f\"{l3}/{l4}/VXLAN/{l3}/{l4}\",\n                                 should_pass=should_pass)\n \n@@ -602,8 +815,7 @@ class TestChecksumOffload(TestCase):\n                 for inner_arg in \"\", \"chksum=0xf\":\n                     should_pass: bool = inner_arg == \"\"\n                     vf = self.send_pkt_expect_good_bad_from_flag_catch_failure(\n-                        f\"eth/{l3}()/GRE()/{l3}()/\"\n-                        f\"{l4}({inner_arg})/('X'*50)\",\n+                        f\"eth/{l3}()/GRE()/{l3}()/{l4}({inner_arg})/('X'*50)\",\n                         \"PKT_RX_L4_CKSUM_\", f\"{l3}/GRE/{l3}/{l4}\",\n                         should_pass=should_pass)\n \n@@ -615,6 +827,8 @@ class TestChecksumOffload(TestCase):\n         # updated this test case can easily take advantage of the new functionality.\n \n         # # GENEVE\n+        # # This import is over here so that it is not forgotten when the update happens\n+        # from scapy.contrib.geneve import GENEVE\n         # for l3_outer in l3_protos:\n         #     for l4_outer in l4_protos:\n         #         for l3_inner in l3_protos:\n@@ -638,7 +852,46 @@ class TestChecksumOffload(TestCase):\n             self.logger.error(str(err))\n         self.verify(len(verification_errors) == 0, \"See previous output\")\n \n+        self.tester.send_expect(\"quit\", \"#\")\n+        self.dut.send_expect(\"stop\", \"testpmd>\")\n+\n+    def test_hardware_checksum_check_l4_tx(self):\n+        self.checksum_enablehw(self.dut_ports[0])\n+        self.dut.send_expect(\"start\", \"testpmd>\")\n+\n+        verification_errors: List[VerifyFailure] = []\n+\n+        iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0]))\n+        dut_mac = self.dut.get_mac_address(self.dut_ports[0])\n+        tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0]))\n+\n+        packets = self.get_packets(dut_mac, tester_mac)\n+\n+        capture_file_name = \"test_hardware_checksum_check_l4_tx_capture.pcap\"\n+\n+        packet_file_path = \"/tmp/test_hardware_checksum_check_l4_tx_packets.pcap\"\n+        capture_file_path = \"/tmp/tester/\" + capture_file_name\n+\n+        self.tester.send_expect(f\"tcpdump -i {iface} -s 65535 -w {capture_file_path} &\", \"# \")\n+\n+        wrpcap(packet_file_path, packets)\n+        self.tester.session.copy_file_to(packet_file_path, packet_file_path)\n+\n+        self.replay_pcap_file_on_tester(iface, packet_file_path)\n+\n+        self.tester.session.copy_file_from(packet_file_path, \"output/tmp/pcap/\" + capture_file_name)\n+\n+        captured_packets = rdpcap(\"output/tmp/pcap/\" + capture_file_name)\n+\n+        self.verify(len(packets) == len(captured_packets), \"Not all packets were received\")\n+\n+        error_messages = self.validate_packet_list_checksums(captured_packets)\n+\n         self.dut.send_expect(\"stop\", \"testpmd>\")\n+        if len(error_messages) != 0:\n+            for error_msg in error_messages:\n+                self.logger.error(error_msg)\n+            self.verify(False, \"See prior output\")\n \n     def tear_down(self):\n         \"\"\"\n",
    "prefixes": []
}