test suite reviews and discussions
 help / color / mirror / Atom feed
* [DTS][Patch V1 0/4] Updating packet module and introducing a new common module.
@ 2023-07-12 19:31 Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 1/4] framework/packet: Add GTPU and GENEVE support for packet module Ke Xu
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Ke Xu @ 2023-07-12 19:31 UTC (permalink / raw)
  To: dts; +Cc: ke1.xu, tarcadia

This patch is updating packet module for new util functions and packet types
 and introducing a new common module for util functions and common executions
 for offload cases. This patch also added a new decorator to test_case module.

Packet module is designed to support packet organizing, analyzing and
 processing of different packet types. This patch introduced several
 new functions and supported new packet types in this module.

Added support for GTPU and GENEVE packet. Packet module only supports
 limited tunneling layers like VxLAN, GRE. New packet types are required
 in recent DPDK releases and GTPU and GENEVE are required to be added.

Added new util functions. Packet module introduced several util functions
 for writing packets into files, getting and increasing IP addresses,
 getting ether types and so on. As more packet types are introduced, some
 other util functions are required. This patch implemented a set of packet
 layer indexing, packet payload, segment and checksum checking. This patch
 also fixed some bad doc strings.

New Offload Common module is designed to support common checksum offload, TSO
 and related cases that requires varied packet types and massive packet-type
 traversal. This new module provides prepared packets, packet modification
 methods, packet and verbose analyzing methods and common execution steps.

New decorator `skip_unsupported` is designed to mark not supported cases. It
 provides two inputs for labeling a case unsupported in condition and labeling
 cases not supported with a reason.

Ke Xu (4):
  framework/packet: Add GTPU and GENEVE support for packet module.
  framework/packet: Update packet module for new methods.
  framework/test_case: Add skip_unsupported decorator.
  tests/offload_common: Add offload_common module.

 framework/packet.py     | 866 ++++++++++++++++++++++++++++++++++++-
 framework/test_case.py  |  21 +
 tests/offload_common.py | 926 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1809 insertions(+), 4 deletions(-)
 create mode 100644 tests/offload_common.py

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [DTS][Patch V1 1/4] framework/packet: Add GTPU and GENEVE support for packet module.
  2023-07-12 19:31 [DTS][Patch V1 0/4] Updating packet module and introducing a new common module Ke Xu
@ 2023-07-12 19:31 ` Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 2/4] framework/packet: Update packet module for new methods Ke Xu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Ke Xu @ 2023-07-12 19:31 UTC (permalink / raw)
  To: dts; +Cc: ke1.xu, tarcadia

Partially implemented GTPU and GENEVE layer in packet. Allowing limited usage of GTPU and GENEVE packets.

Signed-off-by: Ke Xu <ke1.xu@intel.com>
---
 framework/packet.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/framework/packet.py b/framework/packet.py
index 2c16e7cb..c44c955f 100644
--- a/framework/packet.py
+++ b/framework/packet.py
@@ -17,6 +17,8 @@ import time
 from importlib import import_module
 from socket import AF_INET6
 
+from scapy.contrib.geneve import GENEVE
+from scapy.contrib.gtp import GTP_U_Header, GTPPDUSessionContainer
 from scapy.contrib.lldp import LLDPDU, LLDPDUManagementAddress
 from scapy.contrib.mpls import MPLS
 from scapy.contrib.nsh import NSH
@@ -51,6 +53,7 @@ scapy_modules_required = {
         "GTPEchoRequest",
         "GTPEchoResponse",
     ],
+    "scapy.contrib.geneve": ["GENEVE"],
     "scapy.contrib.lldp": ["LLDPDU", "LLDPDUManagementAddress"],
     "scapy.contrib.pfcp": ["PFCP"],
     "scapy.contrib.nsh": ["NSH"],
@@ -90,7 +93,7 @@ LayersTypes = {
     # <'ether type'=0x0800 'version'=4, 'protocol'=17 'destination port'=4789>
     # or
     # <'ether type'=0x86DD 'version'=6, 'next header'=17 'destination port'=4789>
-    "TUNNEL": ["ip", "gre", "vxlan", "nvgre", "geneve", "grenat"],
+    "TUNNEL": ["ip", "gre", "vxlan", "nvgre", "gtpu", "geneve", "grenat"],
     "INNER L2": ["inner_mac", "inner_vlan"],
     # inner_ipv4_unknown, inner_ipv6_unknown
     "INNER L3": ["inner_ipv4", "inner_ipv4_ext", "inner_ipv6", "inner_ipv6_ext"],
@@ -167,7 +170,8 @@ class scapy(object):
         "ipv6_in_ip": IP() / IPv6(src="::1"),
         "ipv6_frag_in_ip": IP() / IPv6(src="::1", nh=44) / IPv6ExtHdrFragment(),
         "nvgre": GRE(key_present=1, proto=0x6558, key=0x00000100),
-        "geneve": "Not Implement",
+        "gtpu": GTP_U_Header(gtp_type=255, teid=0x123456),
+        "geneve": GENEVE(),
     }
 
     def __init__(self):
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [DTS][Patch V1 2/4] framework/packet: Update packet module for new methods.
  2023-07-12 19:31 [DTS][Patch V1 0/4] Updating packet module and introducing a new common module Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 1/4] framework/packet: Add GTPU and GENEVE support for packet module Ke Xu
@ 2023-07-12 19:31 ` Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 3/4] framework/test_case: Add skip_unsupported decorator Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 4/4] tests/offload_common: Add offload_common module Ke Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Ke Xu @ 2023-07-12 19:31 UTC (permalink / raw)
  To: dts; +Cc: ke1.xu, tarcadia

1. Add util method segment_bytes.

2. Add methods get_packet_layer_index_oip_ol4_tun_ip_l4, get_packet_layer_index_to_tso_seg, get_packet_layer_index_tunnel for packet layer locating.

3. Add doc string for strip_pktload.

4. Add methods for packet payload getting, payload segmentation and checking.

5. Add methods for packet checksum getting, correction, stating and offload flag checking.

Signed-off-by: Ke Xu <ke1.xu@intel.com>
---
 framework/packet.py | 858 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 856 insertions(+), 2 deletions(-)

diff --git a/framework/packet.py b/framework/packet.py
index c44c955f..02b9f885 100644
--- a/framework/packet.py
+++ b/framework/packet.py
@@ -27,9 +27,9 @@ from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, IPv6ExtHdrRouting
 from scapy.layers.l2 import ARP, GRE, Dot1Q, Ether
 from scapy.layers.sctp import SCTP
 from scapy.layers.vxlan import VXLAN
-from scapy.packet import Raw
+from scapy.packet import Padding, Raw
 from scapy.sendrecv import sendp
-from scapy.utils import hexstr, rdpcap, wrpcap
+from scapy.utils import hexstr, randstring, rdpcap, wrpcap
 
 from .utils import convert_int2ip, convert_ip2int, get_module_path
 
@@ -127,6 +127,152 @@ def write_raw_pkt(pkt_str, file_name):
         w.close()
 
 
+def segment_bytes(payload: bytes, seg_len: int) -> list:
+    """
+    Segment a bytes according to the seg_len.
+    payload: bytes, a sequence to be segmented.
+    seg_len: int, the length of segmentation.
+    return: list, a list of segmented bytes.
+    """
+    segments = []
+    if seg_len > 0:
+        while payload:
+            _s = payload[:seg_len]
+            payload = payload[seg_len:]
+            segments.append(_s)
+    else:
+        segments = [payload]
+    return segments
+
+
+def get_packet_layer_index_oip_ol4_tun_ip_l4(
+    pkt, support_rx_tunnel: bool = True
+) -> tuple:
+    """
+    Get the layer index of outer-ip, outer-l4, tunnel, inner-ip, inner-l4 in a packet.
+    The index counting follows the DPDK rule, that if a non-tunnel packet is counted,
+    the ip / l4 layer are considered the inner ip / l4 layers.
+    If a layer is not found, it will return None for that index.
+    This method asserts that a tunnel layer is to transfer a L3 tunneled packet over a L3
+    or higher network, which means a tunnel packet is laid between an outer IP layer and an
+    inner IP layer.
+    pkt: Packet, the Scapy Packet object to be counted.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: tuple, indexes of outer IP, outer L4, tunnel, inner IP, inner L4.
+    """
+    _LAYERS_L3 = {IP, IPv6}
+    _LAYERS_L4 = {UDP, TCP, SCTP}
+    _layers = pkt.layers()
+    _index_tunnel = get_packet_layer_index_tunnel(pkt)
+    _index_oip = None
+    _index_ol4 = None
+    _index_ip = None
+    _index_l4 = None
+    if _index_tunnel is None:
+        for _i, _l in enumerate(_layers):
+            if _l in _LAYERS_L3:
+                _index_ip = _i
+            if _l in _LAYERS_L4:
+                _index_l4 = _i
+    else:
+        for _i in range(0, _index_tunnel + 1):
+            _l = _layers[_i]
+            if _l in _LAYERS_L3:
+                _index_oip = _i
+            if _l in _LAYERS_L4:
+                _index_ol4 = _i
+        for _i in range(_index_tunnel + 1, len(_layers)):
+            _l = _layers[_i]
+            if _l in _LAYERS_L3:
+                _index_ip = _i
+            if _l in _LAYERS_L4:
+                _index_l4 = _i
+    if not support_rx_tunnel and not _index_tunnel is None:
+        (_index_oip, _index_ol4, _index_tunnel, _index_ip, _index_l4) = (
+            None,
+            None,
+            None,
+            _index_oip,
+            _index_ol4,
+        )
+    return _index_oip, _index_ol4, _index_tunnel, _index_ip, _index_l4
+
+
+def get_packet_layer_index_to_tso_seg(
+    pkt,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> int:
+    """
+    Get the layer index of TSO in a packet.
+    In DPDK TSO is considered Transmittion Segmentation Offload. That both TCP
+    and UDP are considered to be segemeted.
+    pkt: Packt, Scapy Packet object to be segmeted.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    support_seg_non_tunnel: bool, flag indicating if a non-tunnel packet is to segment.
+    support_seg_tunnel: bool, flag indicating if a tunnel packet is to segment.
+    support_tso: bool, flag indicating if TCP Segmentation is enabled.
+    support_ufd: bool, flag indicating if UDP Fragmentation is enabled.
+    return: int, the index of the layer to be segmented in the packet.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    if support_seg_tunnel and not _index_tunnel is None:
+        if not _index_l4 is None and support_tso and isinstance(pkt[_index_l4], TCP):
+            return _index_l4
+        if not _index_l4 is None and support_ufo and isinstance(pkt[_index_l4], UDP):
+            return _index_l4
+    if support_seg_non_tunnel and _index_tunnel is None:
+        if not _index_l4 is None and support_tso and isinstance(pkt[_index_l4], TCP):
+            return _index_l4
+        if not _index_l4 is None and support_ufo and isinstance(pkt[_index_l4], UDP):
+            return _index_l4
+    return None
+
+
+def get_packet_layer_index_tunnel(pkt):
+    """
+    Get the layer index of tunnel layer in a packet.
+    If a layer is not found, it will return None for that index.
+    This method asserts that a tunnel layer is to transfer a L3 tunneled packet over a L3
+    or higher network, which means a tunnel packet is laid between an outer IP layer and an
+    inner IP layer.
+    pkt: Packet, Scapy Packet object to be indexed.
+    return: int | None, the index of the tunnel layer in the packet.
+    """
+    # packet types
+    _LAYERS_TUNNEL = {VXLAN, GRE, GTP_U_Header, GENEVE}
+
+    _layers = pkt.layers()
+    for _i, _l in enumerate(_layers):
+        _layers_outer = _layers[: _i + 1]
+        _layers_inner = _layers[_i + 1 :]
+        if (
+            (_l in _LAYERS_TUNNEL)
+            and (IP in _layers_outer or IPv6 in _layers_outer)
+            and (IP in _layers_inner or IPv6 in _layers_inner)
+        ):
+            return _i
+    for _i, _l in enumerate(_layers):
+        if (_l is IP or _l is IPv6) and (
+            _layers[_i + 1] is IP or _layers[_i + 1] is IPv6
+        ):
+            return _i
+    return None
+
+
 class scapy(object):
     SCAPY_LAYERS = {
         "ether": Ether(dst="ff:ff:ff:ff:ff:ff"),
@@ -1188,6 +1334,12 @@ def compare_pktload(pkt1=None, pkt2=None, layer="L2"):
 
 
 def strip_pktload(pkt=None, layer="L2", p_index=0):
+    """
+    Strip the payload of a specific layer indicated by `layer` in a pakcet.
+    pkt: Packet, DTS Packet object to be stripped.
+    layer: str, the layer to be stripped.
+    return: str, the stripped layer formatted in hex string.
+    """
     if layer == "L2":
         l_idx = 0
     elif layer == "L3":
@@ -1205,6 +1357,708 @@ def strip_pktload(pkt=None, layer="L2", p_index=0):
     return load
 
 
+def get_packet_payload_of(pkt, layer: int) -> bytes:
+    """
+    Get the payload of a layer in a packet.
+    pkt: Packet, Scapy Packet object to get the payload of.
+    layer: int, the layer index to get the payload in the packet.
+    return: bytes, the payload of a layer.
+    """
+    if layer is None:
+        return None
+    pkt_payload = pkt.copy()
+    if Padding in pkt_payload:
+        _index_padding = pkt_payload.layers().index(Padding)
+        pkt_payload[_index_padding - 1].remove_payload()
+        if layer >= _index_padding:
+            return None
+    return bytes(pkt_payload[layer].payload)
+
+
+def get_packet_payload(
+    pkt,
+    support_rx_tunnel: bool = True,
+) -> bytes:
+    """
+    Get the payload of a packet.
+    pkt: Packet, Scapy Packet object to get the payload of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: bytes, the payload of a packet. If the packet has an L4, the payload is the load of
+    L4, or it will be the load of L3 if L3 exists, else it returns None.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    if not _index_l4 is None:
+        return get_packet_payload_of(pkt, _index_l4)
+    elif not _index_ip is None:
+        return get_packet_payload_of(pkt, _index_ip)
+    else:
+        return None
+
+
+def get_packet_payload_no_tso_seg(
+    pkt,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> bytes:
+    """
+    Get the payload of the layer to be segmented of a packet, not segmented.
+    pkt: Packet, Scapy Packet object to get the payload of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    support_seg_non_tunnel: bool, flag indicating if a non-tunnel packet is to segment.
+    support_seg_tunnel: bool, flag indicating if a tunnel packet is to segment.
+    support_tso: bool, flag indicating if TCP Segmentation is enabled.
+    support_ufd: bool, flag indicating if UDP Fragmentation is enabled.
+    return: bytes, the payload of the layer to be segmented of a packet. If no layer is to be
+    segmented, it returns None.
+    """
+    _index_seg = get_packet_layer_index_to_tso_seg(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    if not _index_seg is None:
+        return get_packet_payload_of(pkt, _index_seg)
+    else:
+        return None
+
+
+def get_packet_payload_pre_tso_seg(
+    pkt,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> bytes:
+    """
+    Get the payload of a packet before considering it is segmented.
+    pkt: Packet, Scapy Packet object to get the payload of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    support_seg_non_tunnel: bool, flag indicating if a non-tunnel packet is to segment.
+    support_seg_tunnel: bool, flag indicating if a tunnel packet is to segment.
+    support_tso: bool, flag indicating if TCP Segmentation is enabled.
+    support_ufd: bool, flag indicating if UDP Fragmentation is enabled.
+    return: bytes, the payload of the packet. If it can be segmented then returns the not
+    segmented payload, else it returns the payload despite the segmentation flags.
+    """
+    _payload_no_seg = get_packet_payload_no_tso_seg(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    _payload = get_packet_payload(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+    )
+    if not _payload_no_seg is None:
+        return _payload_no_seg
+    else:
+        return _payload
+
+
+def get_packet_payload_post_tso_seg(
+    pkt,
+    seg_len: int = 800,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> list:
+    """
+    Get the list of payloads of a packet after it is segmented.
+    pkt: Packet, Scapy Packet object to get the payload of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    support_seg_non_tunnel: bool, flag indicating if a non-tunnel packet is to segment.
+    support_seg_tunnel: bool, flag indicating if a tunnel packet is to segment.
+    support_tso: bool, flag indicating if TCP Segmentation is enabled.
+    support_ufd: bool, flag indicating if UDP Fragmentation is enabled.
+    return: list, sequence of the payloads after applying TSO to a packet. If a packet is not
+    to segment, it returns a list of only the payload. If the packet is not a proper packet, it
+    returns an empty list.
+    """
+    _payload_no_seg = get_packet_payload_no_tso_seg(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    _payload = get_packet_payload(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+    )
+    if not _payload_no_seg is None:
+        _segments = segment_bytes(_payload_no_seg, seg_len)
+    elif not _payload is None:
+        _segments = segment_bytes(_payload, -1)
+    else:
+        _segments = []
+    return _segments
+
+
+def get_packet_tso_seg_tx_flag(
+    pkt,
+    seg_len: int = 800,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> list:
+    """
+    Get the list of TSO TX offload flags of a packet.
+    pkt: Packet, Scapy Packet object to get the offload flag of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    support_seg_non_tunnel: bool, flag indicating if a non-tunnel packet is to segment.
+    support_seg_tunnel: bool, flag indicating if a tunnel packet is to segment.
+    support_tso: bool, flag indicating if TCP Segmentation is enabled.
+    support_ufd: bool, flag indicating if UDP Fragmentation is enabled.
+    return: list, set of offload flags formatted in a list of str.
+    """
+    _index_seg = get_packet_layer_index_to_tso_seg(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    _flags = []
+    if not _index_seg is None and len(get_packet_payload_of(pkt, _index_seg)) > seg_len:
+        if isinstance(pkt[_index_seg], TCP):
+            _flags.append("RTE_MBUF_F_TX_TCP_SEG")
+        elif isinstance(pkt[_index_seg], UDP):
+            _flags.append("RTE_MBUF_F_TX_UDP_SEG")
+    return _flags
+
+
+def get_packet_checksum_of(
+    pkt,
+    layer: int,
+    good_checksum: bool = False,
+) -> int:
+    """
+    Get the checksum of a specific layer in a packet.
+    pkt: Packet, Scapy Packet object to get the checksum of.
+    layer: int, the layer index to get the checksum in the packet.
+    good_checksum: bool, flag indicating if to get the corrected checksum or to get the raw one.
+    return: int, the checksum of the packet at the layer.
+    """
+    _re_checksum_pattern = re.compile(r"chksum\s*=\s*(0x[0-9a-fA-F]+|None)")
+    if layer is None:
+        return None
+    p_chksum = pkt[layer].copy()
+    if "chksum" in p_chksum.fieldtype:
+        if not good_checksum:
+            checksum_list_str = _re_checksum_pattern.findall(p_chksum.show2(dump=True))
+            return int(checksum_list_str[0], 16)
+        else:
+            p_chksum.chksum = None
+            checksum_list_str = _re_checksum_pattern.findall(p_chksum.show2(dump=True))
+            return int(checksum_list_str[0], 16)
+    else:
+        return None
+
+
+def get_packet_checksums(
+    pkt,
+    good_checksum: bool = False,
+    support_rx_tunnel: bool = True,
+) -> tuple:
+    """
+    Get the checksum list of a packet.
+    This method calculates the checksum of the whole packet. This means if an inner layer has a
+    bad checksum, it will be corrected then to calculate the outer checksum if `good_checksum` is
+    set.
+    If a layer is not found, it will return None for that layer.
+    pkt: Packet, Scapy Packet object to get the checksum of.
+    good_checksum: bool, flag indicating if to get the corrected checksum or to get the raw one.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: tuple, checksums of outer IP, outer L4, inner IP, inner L4 in the packet.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    p_chksum = pkt.copy()
+    if good_checksum:
+        for _i in [_index_oip, _index_ol4, _index_ip, _index_l4]:
+            if not _i is None and "chksum" in p_chksum[_i].fieldtype:
+                p_chksum[_i].chksum = None
+    return (
+        get_packet_checksum_of(p_chksum, _index_oip),
+        get_packet_checksum_of(p_chksum, _index_ol4),
+        get_packet_checksum_of(p_chksum, _index_ip),
+        get_packet_checksum_of(p_chksum, _index_l4),
+    )
+
+
+def get_packet_checksum_of_each(
+    pkt,
+    good_checksum: bool = False,
+    support_rx_tunnel: bool = True,
+) -> tuple:
+    """
+    Get the checksum list of a packet.
+    This method calculates the checksums of each layer seperately. This means if an inner layer
+    has a bad checksum, an outer checksum will be calculated based on the bad inner checksum if
+    `good_checksum` is set.
+    If a layer is not found, it will return None for that layer.
+    pkt: Packet, Scapy Packet object to get the checksum of.
+    good_checksum: bool, flag indicating if to get the corrected checksum or to get the raw one.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: tuple, checksums of outer IP, outer L4, inner IP, inner L4 in the packet.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    return (
+        get_packet_checksum_of(pkt, _index_oip, good_checksum=good_checksum),
+        get_packet_checksum_of(pkt, _index_ol4, good_checksum=good_checksum),
+        get_packet_checksum_of(pkt, _index_ip, good_checksum=good_checksum),
+        get_packet_checksum_of(pkt, _index_l4, good_checksum=good_checksum),
+    )
+
+
+def get_packet_checksum_rx_stat(pkt, support_rx_tunnel: bool = True) -> tuple:
+    """
+    Get the checksum stats of a packet.
+    pkt: Packet, Scapy Packet object to get the checksum stats of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: tuple, checksum stats of bad outer IP, bad outer L4, bad inner IP, bad inner L4.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    _bad_oip, _bad_ol4, _bad_ip, _bad_l4 = 0, 0, 0, 0
+    if not _index_oip is None and get_packet_checksum_of(
+        pkt, _index_oip
+    ) != get_packet_checksum_of(pkt, _index_oip, good_checksum=True):
+        _bad_oip = 1
+    if not _index_oip is None and get_packet_checksum_of(
+        pkt, _index_ol4
+    ) != get_packet_checksum_of(pkt, _index_ol4, good_checksum=True):
+        _bad_ol4 = 1
+    if not _index_ip is None and get_packet_checksum_of(
+        pkt, _index_ip
+    ) != get_packet_checksum_of(pkt, _index_ip, good_checksum=True):
+        _bad_ip = 1
+    if not _index_l4 is None and get_packet_checksum_of(
+        pkt, _index_l4
+    ) != get_packet_checksum_of(pkt, _index_l4, good_checksum=True):
+        _bad_l4 = 1
+    return _bad_oip, _bad_ol4, _bad_ip, _bad_l4
+
+
+def get_packet_checksum_rx_olflag(pkt, support_rx_tunnel: bool = True) -> list:
+    """
+    Get the checksum RX offload flags of a packet.
+    pkt: Packet, Scapy Packet object to get the checksum RX offload flag of.
+    support_rx_tunnel: bool, flag indicating if to parse the tunnel. If false, the outer L3 /
+    L4 layers will be considered the only L3 / L4 layers.
+    return: list, set of offload flags formated in a list of str.
+    """
+    _FLAG_PAT = "RTE_MBUF_F_RX_%s_CKSUM_%s"
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    _flags = []
+    if not _index_oip is None and get_packet_checksum_of(
+        pkt, _index_oip
+    ) != get_packet_checksum_of(pkt, _index_oip, good_checksum=True):
+        _flags.append(_FLAG_PAT % ("OUTER_IP", "BAD"))
+    else:
+        ## OUTER_IP_CKSUM_GOOD is default not shown
+        pass
+    if not _index_ol4 is None and get_packet_checksum_of(
+        pkt, _index_ol4
+    ) != get_packet_checksum_of(pkt, _index_ol4, good_checksum=True):
+        _flags.append(_FLAG_PAT % ("OUTER_L4", "BAD"))
+    elif not _index_ol4 is None:
+        _flags.append(_FLAG_PAT % ("OUTER_L4", "GOOD"))
+    elif _index_ol4 is None:
+        pass
+    else:
+        pass
+    if get_packet_checksum_of(pkt, _index_ip) != get_packet_checksum_of(
+        pkt, _index_ip, good_checksum=True
+    ):
+        _flags.append(_FLAG_PAT % ("IP", "BAD"))
+    else:
+        _flags.append(_FLAG_PAT % ("IP", "GOOD"))
+    if get_packet_checksum_of(pkt, _index_l4) != get_packet_checksum_of(
+        pkt, _index_l4, good_checksum=True
+    ):
+        _flags.append(_FLAG_PAT % ("L4", "BAD"))
+    else:
+        _flags.append(_FLAG_PAT % ("L4", "GOOD"))
+    return _flags
+
+
+def get_packets_payload_pre_seg_list(
+    pkts: list,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> list:
+    """
+    Get the payload of a seq of packets before considering it is segmented.
+    """
+    return [
+        get_packet_payload_pre_tso_seg(
+            _p,
+            support_rx_tunnel=support_rx_tunnel,
+            support_seg_non_tunnel=support_seg_non_tunnel,
+            support_seg_tunnel=support_seg_tunnel,
+            support_tso=support_tso,
+            support_ufo=support_ufo,
+        )
+        for _p in pkts
+    ]
+
+
+def get_packets_payload_post_seg_list(
+    pkts: list,
+    seg_len: int = 800,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> list:
+    """
+    Get the list of payloads of a seq of packets after it is segmented.
+    """
+    return [
+        get_packet_payload_post_tso_seg(
+            _p,
+            seg_len=seg_len,
+            support_rx_tunnel=support_rx_tunnel,
+            support_seg_non_tunnel=support_seg_non_tunnel,
+            support_seg_tunnel=support_seg_tunnel,
+            support_tso=support_tso,
+            support_ufo=support_ufo,
+        )
+        for _p in pkts
+    ]
+
+
+def get_packets_checksum_rx_stat_list(
+    pkts: list, support_rx_tunnel: bool = True
+) -> list:
+    """
+    Get the checksum stats of a set of packet.
+    """
+    k_bad_ip = "Bad-ipcsum"
+    k_bad_l4 = "Bad-l4csum"
+    k_bad_oip = "Bad-outer-ipcsum"
+    k_bad_ol4 = "Bad-outer-l4csum"
+    k_rx = "RX-total"
+    stats = {
+        k_bad_ip: 0,
+        k_bad_l4: 0,
+        k_bad_oip: 0,
+        k_bad_ol4: 0,
+        k_rx: 0,
+    }
+    for pkt in pkts:
+        _bad_oip, _bad_ol4, _bad_ip, _bad_l4 = get_packet_checksum_rx_stat(
+            pkt, support_rx_tunnel=support_rx_tunnel
+        )
+        stats[k_bad_oip] += _bad_oip
+        stats[k_bad_ol4] += _bad_ol4
+        stats[k_bad_ip] += _bad_ip
+        stats[k_bad_l4] += _bad_l4
+        stats[k_rx] += 1
+    return stats
+
+
+def get_packets_tso_seg_tx_flag_list(
+    pkts: list,
+    seg_len: int = 800,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> list:
+    """
+    Get the list of TSO TX offload flags of a seq of packet.
+    """
+    flag_list = []
+    for pkt in pkts:
+        flag_list.append(
+            get_packet_tso_seg_tx_flag(
+                pkt,
+                seg_len=seg_len,
+                support_rx_tunnel=support_rx_tunnel,
+                support_seg_non_tunnel=support_seg_non_tunnel,
+                support_seg_tunnel=support_seg_tunnel,
+                support_tso=support_tso,
+                support_ufo=support_ufo,
+            )
+        )
+    return flag_list
+
+
+def get_packets_checksum_rx_olflag_list(
+    pkts: list, support_rx_tunnel: bool = True
+) -> list:
+    """
+    Get the checksum RX offload flags of a seq of packets.
+    """
+    flag_list = []
+    for pkt in pkts:
+        flag_list.append(
+            get_packet_checksum_rx_olflag(pkt, support_rx_tunnel=support_rx_tunnel)
+        )
+    return flag_list
+
+
+def check_packet_segment(
+    pkt,
+    segments: list,
+    seg_len: int,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+) -> bool:
+    """
+    Check the packet segmentation.
+    """
+    payload_expected = get_packet_payload_pre_tso_seg(
+        pkt,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    segments_expected = get_packet_payload_post_tso_seg(
+        pkt,
+        seg_len=seg_len,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    if not segments:
+        return False
+    if segments == segments_expected:
+        return True
+    else:
+        payload_segmented = b""
+        for _i, _seg in enumerate(segments, 1):
+            if len(segments_expected) > 1 and len(_seg) > seg_len:
+                return False
+            payload_segmented += _seg
+        return payload_expected == payload_segmented
+
+
+def check_packet_checksum_of(pkt: Packet, layer: int, checksum_ref: int = None) -> bool:
+    """
+    Check the packet checksum of a specific layer indicated by `layer`.
+    """
+    if checksum_ref is None:
+        checksum_ref = get_packet_checksum_of(pkt, layer, good_checksum=True)
+    return get_packet_checksum_of(pkt, layer) == checksum_ref
+
+
+def check_packet_checksums(
+    pkt,
+    checksum_ref_list: list = None,
+    support_rx_tunnel: bool = True,
+) -> bool:
+    """
+    Check the packet checksum.
+    pkt: checked packet;
+    checksum_ref_list: refering checksum list, recalculate the whole packet for correct checksum in default;
+    return: if this packet is in good checksum.
+    """
+    if checksum_ref_list is None:
+        checksum_ref_list = get_packet_checksums(
+            pkt, support_rx_tunnel=support_rx_tunnel, good_checksum=True
+        )
+    return (
+        get_packet_checksums(pkt, support_rx_tunnel=support_rx_tunnel)
+        == checksum_ref_list
+    )
+
+
+def check_packet_checksum_of_each(
+    pkt,
+    checksum_ref_list: list = None,
+    support_rx_tunnel: bool = True,
+    allow_zero_outer_ip: bool = False,
+    allow_zero_outer_udp: bool = False,
+    allow_zero_inner_ip: bool = False,
+    allow_zero_inner_udp: bool = False,
+    allow_zero_inner_tcp: bool = False,
+    allow_zero_inner_sctp: bool = False,
+) -> tuple:
+    """
+    Check the packet checksum of each layer.
+    pkt: checked packet;
+    checksum_ref_list: refering checksum list, recalculate each layer seperately for correct checksum in default;
+    allow_zero_*: allowing zero checksum validated passed on specific layer;
+    return: if this packet is validated passed on outer-ip, outer-l4, inner-ip, inner-l4 layer checksum.
+    """
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(
+        pkt, support_rx_tunnel=support_rx_tunnel
+    )
+    if checksum_ref_list and len(checksum_ref_list) == 2:
+        (_checksum_ref_ip, _checksum_ref_l4) = (
+            checksum_ref_list[0],
+            checksum_ref_list[1],
+        )
+        return (
+            True,
+            True,
+            check_packet_checksum_of(pkt, _index_ip, _checksum_ref_ip),
+            check_packet_checksum_of(pkt, _index_l4, _checksum_ref_l4),
+        )
+    elif checksum_ref_list and len(checksum_ref_list) == 4:
+        (_checksum_ref_oip, _checksum_ref_ol4, _checksum_ref_ip, _checksum_ref_l4) = (
+            checksum_ref_list[0],
+            checksum_ref_list[1],
+            checksum_ref_list[2],
+            checksum_ref_list[3],
+        )
+        return (
+            check_packet_checksum_of(pkt, _index_oip, _checksum_ref_oip),
+            check_packet_checksum_of(pkt, _index_ol4, _checksum_ref_ol4),
+            check_packet_checksum_of(pkt, _index_ip, _checksum_ref_ip),
+            check_packet_checksum_of(pkt, _index_l4, _checksum_ref_l4),
+        )
+    elif checksum_ref_list:
+        raise ValueError("Wrong checksum ref list length.")
+    else:
+        _verified_l4 = (
+            _index_l4 is None
+            or check_packet_checksum_of(pkt, _index_l4)
+            or (
+                not _index_l4 is None
+                and allow_zero_inner_udp
+                and isinstance(pkt[_index_l4], UDP)
+                and check_packet_checksum_of(pkt, _index_l4, 0x00)
+            )
+            or (
+                not _index_l4 is None
+                and allow_zero_inner_tcp
+                and isinstance(pkt[_index_l4], TCP)
+                and check_packet_checksum_of(pkt, _index_l4, 0x00)
+            )
+            or (
+                not _index_l4 is None
+                and allow_zero_inner_sctp
+                and isinstance(pkt[_index_l4], SCTP)
+                and check_packet_checksum_of(pkt, _index_l4, 0x0000)
+            )
+        )
+        _verified_ip = (
+            _index_ip is None
+            or check_packet_checksum_of(pkt, _index_ip)
+            or (
+                not _index_ip is None
+                and allow_zero_inner_ip
+                and isinstance(pkt[_index_ip], IP)
+                and check_packet_checksum_of(pkt, _index_ip, 0x00)
+            )
+        )
+        _verified_ol4 = (
+            _index_ol4 is None
+            or check_packet_checksum_of(pkt, _index_ol4)
+            or (
+                not _index_ol4 is None
+                and allow_zero_outer_udp
+                and isinstance(pkt[_index_ol4], UDP)
+                and get_packet_checksum_of(pkt, _index_ol4) == 0x00
+            )
+        )
+        _verified_oip = (
+            _index_oip is None
+            or check_packet_checksum_of(pkt, _index_oip)
+            or (
+                not _index_oip is None
+                and allow_zero_outer_ip
+                and isinstance(pkt[_index_oip], IP)
+                and get_packet_checksum_of(pkt, _index_oip) == 0x00
+            )
+        )
+        return _verified_oip, _verified_ol4, _verified_ip, _verified_l4
+
+
+def check_packet_verbose(verbose: str, verbose_ref_list: list) -> bool:
+    """
+    Check the verbose with the reference list.
+    """
+    for _ref in verbose_ref_list:
+        if not _ref in verbose:
+            return False
+    return True
+
+
 ###############################################################################
 ###############################################################################
 if __name__ == "__main__":
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [DTS][Patch V1 3/4] framework/test_case: Add skip_unsupported decorator.
  2023-07-12 19:31 [DTS][Patch V1 0/4] Updating packet module and introducing a new common module Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 1/4] framework/packet: Add GTPU and GENEVE support for packet module Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 2/4] framework/packet: Update packet module for new methods Ke Xu
@ 2023-07-12 19:31 ` Ke Xu
  2023-07-12 19:31 ` [DTS][Patch V1 4/4] tests/offload_common: Add offload_common module Ke Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Ke Xu @ 2023-07-12 19:31 UTC (permalink / raw)
  To: dts; +Cc: ke1.xu, tarcadia

Add a new decorator for marking unsupported cases.

Signed-off-by: Ke Xu <ke1.xu@intel.com>
---
 framework/test_case.py | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/framework/test_case.py b/framework/test_case.py
index 68f89407..35342de0 100644
--- a/framework/test_case.py
+++ b/framework/test_case.py
@@ -628,3 +628,24 @@ def skip_unsupported_host_driver(drivers):
         return wrapper
 
     return decorator
+
+
+def skip_unsupported(do_skip: bool = True, reason: str = None):
+    """
+    Skip case which are not supported. This means a case may be supported in future or in other branches.
+    """
+
+    def decorator(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            test_case = args[0]
+            if do_skip:
+                raise VerifySkip(
+                    "DPDK currently do not support this case"
+                    + (" for %s" % reason if reason else "")
+                )
+            return func(*args, **kwargs)
+
+        return wrapper
+
+    return decorator
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [DTS][Patch V1 4/4] tests/offload_common: Add offload_common module.
  2023-07-12 19:31 [DTS][Patch V1 0/4] Updating packet module and introducing a new common module Ke Xu
                   ` (2 preceding siblings ...)
  2023-07-12 19:31 ` [DTS][Patch V1 3/4] framework/test_case: Add skip_unsupported decorator Ke Xu
@ 2023-07-12 19:31 ` Ke Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Ke Xu @ 2023-07-12 19:31 UTC (permalink / raw)
  To: dts; +Cc: ke1.xu, tarcadia

Add new module for common packet sending, validation and test steps for stateless offload cases.

1. Add common parameters and prepared packets for checksum offload and TSO cases.

2. Add packet organizing methods to get packets with expected inner and outer, l3 and l4 checksums.

3. Add methods get_verbose_line_key_value and get_verbose_list for verbose parsing.

4. Add method get_table_stats for port stats and fwd stats parsing.

5. Add method execute_fwd_scapy for common packet sending, capturing, verbose getting and fwd stats getting.

6. Add method execute_test_checksum for common checksum case execution.

7. Add method execute_test_tso for common TSO case execution.

Signed-off-by: Ke Xu <ke1.xu@intel.com>
---
 tests/offload_common.py | 926 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 926 insertions(+)
 create mode 100644 tests/offload_common.py

diff --git a/tests/offload_common.py b/tests/offload_common.py
new file mode 100644
index 00000000..6905aa1e
--- /dev/null
+++ b/tests/offload_common.py
@@ -0,0 +1,926 @@
+import re
+from math import ceil
+from typing import List
+
+from framework.crb import Crb
+from framework.dut import Dut
+from framework.packet import *
+from framework.pmd_output import PmdOutput
+from framework.test_case import TestCase
+from framework.tester import Tester
+
+TX_INTERVAL = 0.01
+TX_REPEAT = 10
+
+CSUM_PAYLOAD_SIZE = 1024
+
+TSO_MTU = 9000
+TSO_SEGMENT_LENGTH = 800
+TSO_PAYLOAD_SIZE_LIST = [128, 800, 801, 1700, 2500, 8500]
+
+CSUM_INNER_PACKET_PART = {
+    "IP/UDP": IP(src="10.0.0.1")
+    / UDP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "IP/TCP": IP(src="10.0.0.1")
+    / TCP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "IP/SCTP": IP(src="10.0.0.1")
+    / SCTP(sport=29999, dport=30000, chksum=0x0000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "IPv6/UDP": IPv6(src="::1")
+    / UDP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "IPv6/TCP": IPv6(src="::1")
+    / TCP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "IPv6/SCTP": IPv6(src="::1")
+    / SCTP(sport=29999, dport=30000, chksum=0x0000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IP/UDP": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IP/TCP": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / TCP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IP/SCTP": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / SCTP(sport=29999, dport=30000, chksum=0x0000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IPv6/UDP": Dot1Q(vlan=100)
+    / IPv6(src="::1")
+    / UDP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IPv6/TCP": Dot1Q(vlan=100)
+    / IPv6(src="::1")
+    / TCP(sport=29999, dport=30000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+    "VLAN/IPv6/SCTP": Dot1Q(vlan=100)
+    / IPv6(src="::1")
+    / SCTP(sport=29999, dport=30000, chksum=0x0000)
+    / Raw(randstring(CSUM_PAYLOAD_SIZE)),
+}
+
+TSO_INNER_PACKET_PART = {
+    "IP/TCP": IP(src="10.0.0.1") / TCP(sport=29999, dport=30000),
+    "IP/UDP": IP(src="10.0.0.1") / UDP(sport=29999, dport=30000),
+    "IPv6/TCP": IPv6(src="::1") / TCP(sport=29999, dport=30000),
+    "IPv6/UDP": IPv6(src="::1") / UDP(sport=29999, dport=30000),
+    "VLAN/IP/TCP": Dot1Q(vlan=100) / IP(src="10.0.0.1") / TCP(sport=29999, dport=30000),
+    "VLAN/IP/UDP": Dot1Q(vlan=100) / IP(src="10.0.0.1") / UDP(sport=29999, dport=30000),
+    "VLAN/IPv6/TCP": Dot1Q(vlan=100) / IPv6(src="::1") / TCP(sport=29999, dport=30000),
+    "VLAN/IPv6/UDP": Dot1Q(vlan=100) / IPv6(src="::1") / UDP(sport=29999, dport=30000),
+}
+
+OUTER_PACKET_PART = {
+    "IP/VXLAN": IP(src="10.0.0.1") / UDP(dport=4789) / VXLAN() / Ether(),
+    "IPv6/VXLAN": IPv6() / UDP(dport=4789) / VXLAN() / Ether(),
+    "IP/VXLAN-GPE": IP(src="10.0.0.1") / UDP(sport=4790, dport=4790) / VXLAN(),
+    "IPv6/VXLAN-GPE": IPv6() / UDP(sport=4790, dport=4790) / VXLAN(),
+    "IP/VXLAN-GPE/Ether": IP(src="10.0.0.1")
+    / UDP(sport=4790, dport=4790)
+    / VXLAN()
+    / Ether(),
+    "IPv6/VXLAN-GPE/Ether": IPv6() / UDP(sport=4790, dport=4790) / VXLAN() / Ether(),
+    "IP/GRE": IP(src="10.0.0.1", proto=47) / GRE(),
+    "IPv6/GRE": IPv6(nh=47) / GRE(),
+    "IP/GRE/Ether": IP(src="10.0.0.1", proto=47) / GRE() / Ether(),
+    "IPv6/GRE/Ether": IPv6(nh=47) / GRE() / Ether(),
+    "IP/NVGRE": IP(src="10.0.0.1", proto=47)
+    / GRE(key_present=1, proto=0x6558, key=0x00000100)
+    / Ether(),
+    "IPv6/NVGRE": IPv6(nh=47)
+    / GRE(key_present=1, proto=0x6558, key=0x00000100)
+    / Ether(),
+    "IP/GTPU": IP(src="10.0.0.1")
+    / UDP(dport=2152)
+    / GTP_U_Header(gtp_type=255, teid=0x123456),
+    "IPv6/GTPU": IPv6() / UDP(dport=2152) / GTP_U_Header(gtp_type=255, teid=0x123456),
+    "IP/GENEVE": IP(src="10.0.0.1") / UDP(dport=6081, sport=29999) / GENEVE(),
+    "IPv6/GENEVE": IPv6() / UDP(dport=6081, sport=29999) / GENEVE(),
+    "IP": IP(src="10.0.0.1"),
+    "IPv6": IPv6(),
+    "VLAN/IP/VXLAN": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(dport=4789)
+    / VXLAN()
+    / Ether(),
+    "VLAN/IPv6/VXLAN": Dot1Q(vlan=100) / IPv6() / UDP(dport=4789) / VXLAN() / Ether(),
+    "VLAN/IP/VXLAN-GPE": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(sport=4790, dport=4790)
+    / VXLAN(),
+    "VLAN/IPv6/VXLAN-GPE": Dot1Q(vlan=100)
+    / IPv6()
+    / UDP(sport=4790, dport=4790)
+    / VXLAN(),
+    "VLAN/IP/VXLAN-GPE/Ether": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(sport=4790, dport=4790)
+    / VXLAN()
+    / Ether(),
+    "VLAN/IPv6/VXLAN-GPE/Ether": Dot1Q(vlan=100)
+    / IPv6()
+    / UDP(sport=4790, dport=4790)
+    / VXLAN()
+    / Ether(),
+    "VLAN/IP/GRE": Dot1Q(vlan=100) / IP(src="10.0.0.1", proto=47) / GRE(),
+    "VLAN/IPv6/GRE": Dot1Q(vlan=100) / IPv6(nh=47) / GRE(),
+    "VLAN/IP/GRE/Ether": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1", proto=47)
+    / GRE()
+    / Ether(),
+    "VLAN/IPv6/GRE/Ether": Dot1Q(vlan=100) / IPv6(nh=47) / GRE() / Ether(),
+    "VLAN/IP/NVGRE": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1", proto=47)
+    / GRE(key_present=1, proto=0x6558, key=0x00000100)
+    / Ether(),
+    "VLAN/IPv6/NVGRE": Dot1Q(vlan=100)
+    / IPv6(nh=47)
+    / GRE(key_present=1, proto=0x6558, key=0x00000100)
+    / Ether(),
+    "VLAN/IP/GTPU": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(dport=2152)
+    / GTP_U_Header(gtp_type=255, teid=0x123456),
+    "VLAN/IPv6/GTPU": Dot1Q(vlan=100)
+    / IPv6()
+    / UDP(dport=2152)
+    / GTP_U_Header(gtp_type=255, teid=0x123456),
+    "VLAN/IP/GENEVE": Dot1Q(vlan=100)
+    / IP(src="10.0.0.1")
+    / UDP(dport=6081, sport=29999)
+    / GENEVE(),
+    "VLAN/IPv6/GENEVE": Dot1Q(vlan=100)
+    / IPv6()
+    / UDP(dport=6081, sport=29999)
+    / GENEVE(),
+    "VLAN/IP": Dot1Q(vlan=100) / IP(src="10.0.0.1"),
+    "VLAN/IPv6": Dot1Q(vlan=100) / IPv6(),
+}
+
+
+def filter_packets(pkts: list):
+    return [
+        p
+        for p in pkts
+        if len(p.layers()) >= 3
+        and p.layers()[1] in {IP, IPv6, Dot1Q}
+        and p.layers()[2] in {IP, IPv6, Dot1Q, UDP, TCP, SCTP, GRE, MPLS}
+        and Raw in p
+    ]
+
+
+def get_packet_with_bad_no_checksum(pkt):
+    pkt = pkt.copy()
+    return pkt
+
+
+def get_packet_with_bad_all_checksum(pkt):
+    pkt = pkt.copy()
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(pkt)
+    if not _index_oip is None and "chksum" in pkt[_index_oip].fieldtype:
+        pkt[_index_oip].chksum = 0xFF
+    if not _index_ol4 is None and "chksum" in pkt[_index_ol4].fieldtype:
+        if isinstance(pkt[_index_ol4], UDP):
+            pkt[_index_ol4].chksum = 0xFF
+        else:
+            pkt[_index_ol4].chksum = 0xFF
+    if not _index_ip is None and "chksum" in pkt[_index_ip].fieldtype:
+        pkt[_index_ip].chksum = 0xFF
+    if not _index_l4 is None and "chksum" in pkt[_index_l4].fieldtype:
+        if isinstance(pkt[_index_l4], UDP) or isinstance(pkt[_index_l4], TCP):
+            pkt[_index_l4].chksum = 0xFF
+        elif isinstance(pkt[_index_l4], SCTP):
+            pkt[_index_l4].chksum = 0x0000
+        else:
+            pkt[_index_l4].chksum = 0xFF
+    return pkt
+
+
+def get_packet_with_bad_ip_checksum(pkt):
+    pkt = pkt.copy()
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(pkt)
+    if not _index_ip is None and "chksum" in pkt[_index_ip].fieldtype:
+        pkt[_index_ip].chksum = 0xFF
+    return pkt
+
+
+def get_packet_with_bad_l4_checksum(pkt):
+    pkt = pkt.copy()
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(pkt)
+    if not _index_l4 is None and "chksum" in pkt[_index_l4].fieldtype:
+        if isinstance(pkt[_index_l4], UDP) or isinstance(pkt[_index_l4], TCP):
+            pkt[_index_l4].chksum = 0xFF
+        elif isinstance(pkt[_index_l4], SCTP):
+            pkt[_index_l4].chksum = 0x0000
+        else:
+            pkt[_index_l4].chksum = 0xFF
+    return pkt
+
+
+def get_packet_with_bad_outer_ip_checksum(pkt):
+    pkt = pkt.copy()
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(pkt)
+    if not _index_oip is None and "chksum" in pkt[_index_oip].fieldtype:
+        pkt[_index_oip].chksum = 0xFF
+    return pkt
+
+
+def get_packet_with_bad_outer_l4_checksum(pkt):
+    pkt = pkt.copy()
+    (
+        _index_oip,
+        _index_ol4,
+        _index_tunnel,
+        _index_ip,
+        _index_l4,
+    ) = get_packet_layer_index_oip_ol4_tun_ip_l4(pkt)
+    if not _index_ol4 is None and "chksum" in pkt[_index_ol4].fieldtype:
+        if isinstance(pkt[_index_ol4], UDP):
+            pkt[_index_ol4].chksum = 0xFF
+        else:
+            pkt[_index_ol4].chksum = 0xFF
+    return pkt
+
+
+def get_verbose_line_key_value(line: str):
+    _re_key_value = re.compile(r"(.*)=(.*)")
+    _re_key_flag = re.compile(r"(.*):([A-Z0-9\_\s]+)")
+    line_key_value = {}
+    keys = line.split(" - ")
+    for _k in keys:
+        _f_kv = _re_key_value.findall(_k)
+        _f_kf = _re_key_flag.findall(_k)
+        if _f_kv:
+            _key = _f_kv[0][0]
+            _value = _f_kv[0][1]
+            try:
+                if _value.startswith("0x"):
+                    _value = int(_value[2:], 16)
+                else:
+                    _value = int(_value)
+            except:
+                pass
+            if _key:
+                line_key_value[_key] = _value
+        elif _f_kf:
+            _key = _f_kf[0][0]
+            _flags = {_f.strip() for _f in _f_kf[0][1].split(" ") if _f}
+            if _key:
+                line_key_value[_key] = _flags
+        else:
+            _key = _k.strip()
+            if _key:
+                line_key_value[_key] = ""
+    return line_key_value
+
+
+def get_verbose_list(verbose_str: str):
+    _re_verbose_head = re.compile(
+        r"port (\d+)\/queue (\d+): (received|sent) (\d+) packets"
+    )
+    _re_verbose_restore_info = re.compile(r"restore info:(.*)")
+    _re_verbose_basic_info = re.compile(r"  src=([A-Z0-9:]+) - dst=([A-Z0-9:]+)(.*)")
+    _re_verbose_ol_flags = re.compile(r"  ol_flags:(.*)")
+    _re_verbose_invalid_mbuf = re.compile(r"INVALID mbuf:(.*)")
+    _lines = verbose_str.splitlines(keepends=True)
+    _lines_index = 0
+    verbose_list = []
+    verbose_index = 0
+
+    while _lines_index < len(_lines):
+        _l = _lines[_lines_index]
+        _find_head = _re_verbose_head.findall(_l)
+        if _find_head:
+            _port = int(_find_head[0][0])
+            _queue = int(_find_head[0][1])
+            _rs = _find_head[0][2]
+            _count = int(_find_head[0][3])
+            _lines_index += 1
+            if _lines_index < len(_lines):
+                _l = _lines[_lines_index]
+            verbose_sub_burst_index = 0
+            verbose_sub_extra_index = 0
+            while _lines_index < len(_lines) and verbose_sub_burst_index < _count:
+                _verbose_sub_burst = {}
+                _verbose_sub_burst["port"] = _port
+                _verbose_sub_burst["queue"] = _queue
+                _verbose_sub_burst["rs"] = _rs
+                _find_restore_info = _re_verbose_restore_info.findall(_l)
+                if _find_restore_info:
+                    _verbose_sub_burst["restore info"] = get_verbose_line_key_value(
+                        _find_restore_info[0]
+                    )
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+                _find_basic_info = _re_verbose_basic_info.findall(_l)
+                if _find_basic_info:
+                    _verbose_sub_burst["src"] = _find_basic_info[0][0]
+                    _verbose_sub_burst["dst"] = _find_basic_info[0][1]
+                    _verbose_sub_burst.update(
+                        get_verbose_line_key_value(_find_basic_info[0][2])
+                    )
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+                _find_ol_flags = _re_verbose_ol_flags.findall(_l)
+                if _find_ol_flags:
+                    _verbose_sub_burst["ol_flags"] = {
+                        _f.strip() for _f in _find_ol_flags[0].split(" ") if _f
+                    }
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+                _find_invalid_mbuf = _re_verbose_invalid_mbuf.findall(_l)
+                if _find_invalid_mbuf:
+                    _verbose_sub_burst["INVALID mbuf"] = _find_invalid_mbuf[0]
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+                if (
+                    _find_restore_info
+                    or _find_basic_info
+                    or _find_ol_flags
+                    or _find_invalid_mbuf
+                ):
+                    while verbose_index + verbose_sub_burst_index >= len(verbose_list):
+                        verbose_list.append({})
+                    verbose_list[verbose_index + verbose_sub_burst_index].update(
+                        _verbose_sub_burst
+                    )
+                    verbose_sub_burst_index += 1
+                else:
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+            while _lines_index < len(_lines) and verbose_sub_extra_index < _count:
+                _find_extra_split = ("-" * 17) in _l
+                _find_head = _re_verbose_head.findall(_l)
+                if _find_extra_split:
+                    _verbose_sub_extra = []
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+                    while _lines_index < len(_lines) and not (
+                        ("-" * 17) in _lines[_lines_index]
+                        or _re_verbose_head.findall(_lines[_lines_index])
+                    ):
+                        _verbose_sub_extra.append(_l)
+                        _lines_index += 1
+                        if _lines_index < len(_lines):
+                            _l = _lines[_lines_index]
+                    while verbose_index + verbose_sub_extra_index >= len(verbose_list):
+                        verbose_list.append({})
+                    verbose_list[verbose_index + verbose_sub_extra_index][
+                        "extra"
+                    ] = _verbose_sub_extra
+                    verbose_sub_extra_index += 1
+                else:
+                    _lines_index += 1
+                    if _lines_index < len(_lines):
+                        _l = _lines[_lines_index]
+            verbose_index += _count
+        else:
+            _lines_index += 1
+            if _lines_index < len(_lines):
+                _l = _lines[_lines_index]
+    return verbose_list
+
+
+def get_table_stats(stats_str: str):
+    _re_fwd_stats_table_header_port = re.compile(
+        r"-+\s*Forward statistics for ([0-9a-zA-Z\s]+)\s*-+"
+    )
+    _re_fwd_stats_table_header_total = re.compile(
+        r"\++\s*Accumulated forward statistics for (all ports)\s*\++"
+    )
+    _re_nic_stats_table_header = re.compile(
+        r"#+\s*NIC statistics for ([0-9a-zA-Z\s]+)\s*#+"
+    )
+    _re_stats_table_key_value = re.compile(r"([0-9a-zA-Z\-\_]+:\s*[0-9]+)")
+    stats_lins = stats_str.splitlines()
+    stats = {}
+    _current_table = ""
+    for line in stats_lins:
+        _h_fwd_p = _re_fwd_stats_table_header_port.findall(line)
+        _h_fwd_t = _re_fwd_stats_table_header_total.findall(line)
+        _h_nic = _re_nic_stats_table_header.findall(line)
+        if _h_fwd_p:
+            _current_table = _h_fwd_p[0].strip()
+        elif _h_fwd_t:
+            _current_table = _h_fwd_t[0].strip()
+        elif _h_nic:
+            _current_table = _h_nic[0].strip()
+        elif ("-" * 76) in line or ("+" * 76) in line or ("#" * 76) in line:
+            _current_table = ""
+        elif _current_table:
+            if not _current_table in stats:
+                stats[_current_table] = {}
+            for key_value in _re_stats_table_key_value.findall(line):
+                key, value = key_value.split(":")
+                stats[_current_table][key.strip()] = int(value)
+    return stats
+
+
+def execute_fwd_scapy(
+    pmd: PmdOutput,
+    tester: Tester,
+    packets: List[Packet],
+    packet_interval: int,
+    tester_tx_interface: str,
+    tester_rx_interface: str,
+):
+    _ONCE_TX_COUNT = 1000
+    tcpdump_inst_rx = tester.tcpdump_sniff_packets(intf=tester_rx_interface)
+    time.sleep(3)
+    pmd.execute_cmd("start")
+    pmd.execute_cmd("clear port stats all")
+    # Using sendp for a stable case execution.
+    # Failed using tester.scapy_execute and packet.send_pkt_bg
+    # for long scapy command breaking the ssh session.
+    verbose = ""
+    for i in range(ceil(len(packets) / _ONCE_TX_COUNT)):
+        _packet_obj = Packet()
+        _packets_once = packets[i * _ONCE_TX_COUNT : (i + 1) * _ONCE_TX_COUNT]
+        _packet_obj.pktgen.pkts.extend(_packets_once)
+        _packet_obj.send_pkt_bg_with_pcapfile(
+            tester, tx_port=tester_tx_interface, inter=packet_interval
+        )
+        verbose += pmd.session.get_session_output()
+    # In case tcpdump working slower than expected on very limited environments,
+    # an immediate stop sniffing causes a trimed pcap file, leading to wrong
+    # packet statistic.
+    # Uncommenting the following line helps resolving this problem.
+    time.sleep(1)
+    packets_captured = tester.load_tcpdump_sniff_packets(tcpdump_inst_rx)
+    stats_str = pmd.execute_cmd("stop")
+    stats = get_table_stats(stats_str)
+
+    return packets_captured, verbose, stats
+
+
+def execute_test_checksum(
+    case: TestCase,
+    testpmd: PmdOutput,
+    tester: Tester,
+    tester_tx_interface: str,
+    tester_rx_interface: str,
+    testpmd_port_0: str,
+    testpmd_port_1: str,
+    testpmd_enable_dcf_0: bool = False,
+    testpmd_enable_dcf_1: bool = False,
+    testpmd_bitwidth: int = None,
+    testpmd_log_level: dict = {},
+    testpmd_other_eal_param: str = "",
+    testpmd_param: str = " --enable-rx-cksum ",
+    testpmd_commands: list = [],
+    packets: list = [],
+    packet_interval: int = 0.01,
+    support_rx_tunnel: bool = True,
+    allow_tx_zero_outer_ip: bool = False,
+    allow_tx_zero_outer_udp: bool = False,
+    allow_tx_zero_inner_ip: bool = False,
+    allow_tx_zero_inner_udp: bool = False,
+    allow_tx_zero_inner_tcp: bool = False,
+    allow_tx_zero_inner_sctp: bool = False,
+    allow_tx_bad_outer_ip: bool = False,
+    allow_tx_bad_outer_udp: bool = False,
+    allow_tx_bad_inner_ip: bool = False,
+    allow_tx_bad_inner_l4: bool = False,
+    allow_rx_bad_verbose: bool = False,
+    allow_rx_bad_outer_ip: bool = False,
+    allow_rx_bad_outer_l4: bool = False,
+    allow_rx_bad_inner_ip: bool = False,
+    allow_rx_bad_inner_l4: bool = False,
+):
+    testpmd_core_mask = "all"
+    testpmd_eal_param = ""
+    testpmd_eal_param += (
+        " --force-max-simd-bitwidth=%d " % testpmd_bitwidth
+        if not testpmd_bitwidth is None
+        else ""
+    )
+    testpmd_eal_param += "".join(
+        [
+            " --log-level='%s,%d' " % (_pmd, testpmd_log_level[_pmd])
+            for _pmd in testpmd_log_level
+        ]
+    )
+    testpmd_eal_param += testpmd_other_eal_param
+    testpmd_ports = [testpmd_port_0, testpmd_port_1]
+    testpmd_port_options = {
+        testpmd_port_0: ("cap=dcf" if testpmd_enable_dcf_0 else ""),
+        testpmd_port_1: ("cap=dcf" if testpmd_enable_dcf_1 else ""),
+    }
+
+    testpmd.start_testpmd(
+        cores=testpmd_core_mask,
+        param=testpmd_param,
+        eal_param=testpmd_eal_param,
+        ports=testpmd_ports,
+        port_options=testpmd_port_options,
+    )
+
+    for command in testpmd_commands:
+        testpmd.execute_cmd(command)
+    testpmd.wait_link_status_up(0, 30)
+    testpmd.wait_link_status_up(1, 30)
+
+    eth_mac = testpmd.get_port_mac(0)
+    eth = Ether(dst=eth_mac, src="52:00:00:00:00:00")
+
+    fwd_packets, fwd_verbose, fwd_stats = execute_fwd_scapy(
+        pmd=testpmd,
+        tester=tester,
+        packets=[eth / p for p in packets],
+        packet_interval=packet_interval,
+        tester_tx_interface=tester_tx_interface,
+        tester_rx_interface=tester_rx_interface,
+    )
+
+    stats = get_packets_checksum_rx_stat_list(
+        packets, support_rx_tunnel=support_rx_tunnel
+    )
+    olflags = get_packets_checksum_rx_olflag_list(
+        packets, support_rx_tunnel=support_rx_tunnel
+    )
+    fwd_verbose_list = get_verbose_list(fwd_verbose)
+    passed = True
+
+    if not len(fwd_packets) == len(packets):
+        passed = False
+        case.logger.info(
+            "Inconsist TX packet count and RX packet count.\n"
+            + "  - TX Packet Count : %d.\n" % len(packets)
+            + "  - RX Packet Count : %d.\n" % len(fwd_packets)
+        )
+
+    if not len(fwd_verbose_list) == len(packets):
+        passed = False
+        case.logger.info(
+            "Inconsist TX packet count and verbose count.\n"
+            + "  - TX Packet Count : %d.\n" % len(packets)
+            + "  - Verbose Count : %d.\n" % len(fwd_verbose_list)
+        )
+
+    if not all([fwd_stats["port 0"][k] == stats[k] for k in stats]):
+        _logged_info = "Inconsist TX packet and fwd stats.\n"
+        for k in stats:
+            _ignored = (
+                (allow_rx_bad_outer_ip and "outer-ip" in k)
+                or (allow_rx_bad_inner_ip and "ip" in k and not "outer-ip" in k)
+                or (allow_rx_bad_outer_l4 and "outer-l4" in k)
+                or (allow_rx_bad_inner_l4 and "l4" in k and not "outer-l4" in k)
+            )
+            if not (fwd_stats["port 0"][k] == stats[k]):
+                _logged_info += "  - Stat %s=%s expecting %s%s.\n" % (
+                    k,
+                    fwd_stats["port 0"][k],
+                    stats[k],
+                    " (ignored)" if _ignored else "",
+                )
+                if not _ignored:
+                    passed = False
+        case.logger.info(_logged_info)
+
+    i_fwd_packets = 0
+    for i, packet in enumerate(packets):
+        # Here to verify if a packet is forwarded for better verifying.
+        # If the fwd_packet is not matching the sending packet in the last
+        # 64 bytes we say this two packets are not matching so we log this
+        # sending packet as not forwarded and seeking the next sending packet
+        # to check for the match.
+        if i_fwd_packets >= len(fwd_packets):
+            passed &= False
+            case.logger.info(
+                "=" * 40 + "\n" + "  - Missing packet : %s.\n" % packet.command()
+            )
+            continue
+        else:
+            _sufix64len = min(
+                64, len(bytes(packet)), len(bytes(fwd_packets[i_fwd_packets]))
+            )
+            if not (
+                bytes(packet)[-_sufix64len:]
+                == bytes(fwd_packets[i_fwd_packets])[-_sufix64len:]
+            ):
+                passed &= False
+                case.logger.info(
+                    "=" * 40 + "\n" + "  - Missing packet : %s.\n" % packet.command()
+                )
+                continue
+
+        # If a packet is considered recieved.
+        fwd_packet = fwd_packets[i_fwd_packets]
+        i_fwd_packets += 1
+        fwd_verbose = fwd_verbose_list[i] if i <= len(fwd_verbose_list) else None
+        (verify_oip, verify_ol4, verify_ip, verify_l4) = check_packet_checksum_of_each(
+            fwd_packet,
+            support_rx_tunnel=support_rx_tunnel,
+            allow_zero_outer_ip=allow_tx_zero_outer_ip,
+            allow_zero_outer_udp=allow_tx_zero_outer_udp,
+            allow_zero_inner_ip=allow_tx_zero_inner_ip,
+            allow_zero_inner_udp=allow_tx_zero_inner_udp,
+            allow_zero_inner_tcp=allow_tx_zero_inner_tcp,
+            allow_zero_inner_sctp=allow_tx_zero_inner_sctp,
+        )
+        verify_oip = allow_tx_bad_outer_ip or verify_oip
+        verify_ol4 = allow_tx_bad_outer_udp or verify_ol4
+        verify_ip = allow_tx_bad_inner_ip or verify_ip
+        verify_l4 = allow_tx_bad_inner_l4 or verify_l4
+        verify_olflags = allow_rx_bad_verbose or (
+            not (fwd_verbose is None)
+            and "ol_flags" in fwd_verbose
+            and all(
+                [
+                    (_f in fwd_verbose["ol_flags"])
+                    or (allow_rx_bad_outer_ip and "RX_OUTER_IP_CKSUM" in _f)
+                    or (allow_rx_bad_inner_ip and "RX_IP_CKSUM" in _f)
+                    or (allow_rx_bad_outer_l4 and "RX_OUTER_L4_CKSUM" in _f)
+                    or (allow_rx_bad_inner_l4 and "RX_L4_CKSUM" in _f)
+                    for _f in olflags[i]
+                ]
+            )
+        )
+        verify_payload = get_packet_payload(
+            packet, support_rx_tunnel=support_rx_tunnel
+        ) == get_packet_payload(fwd_packet, support_rx_tunnel=support_rx_tunnel)
+        verify_pass = (
+            verify_oip
+            and verify_ol4
+            and verify_ip
+            and verify_l4
+            and verify_olflags
+            and verify_payload
+        )
+        passed &= verify_pass
+        if not verify_pass:
+            case.logger.info(
+                "=" * 40
+                + "\n"
+                + "  - Failed packet : %s.\n" % packet.command()
+                + "  - Received packet : %s.\n" % fwd_packet.command()
+            )
+            if not (verify_oip and verify_ol4 and verify_ip and verify_l4):
+                case.logger.info(
+                    "  - Failed verifying "
+                    + ("IP " if not verify_ip else "")
+                    + ("L4 " if not verify_l4 else "")
+                    + ("Outer-IP " if not verify_oip else "")
+                    + ("Outer-L4 " if not verify_ol4 else "")
+                    + ".\n"
+                    + "  - Received checksum : outer-ip=%s, outer-l4=%s, ip=%s, l4=%s.\n"
+                    % get_packet_checksums(
+                        fwd_packet,
+                        support_rx_tunnel=support_rx_tunnel,
+                    )
+                    + "  - Correct checksum : outer-ip=%s, outer-l4=%s, ip=%s, l4=%s.\n"
+                    % get_packet_checksum_of_each(
+                        fwd_packet,
+                        good_checksum=True,
+                        support_rx_tunnel=support_rx_tunnel,
+                    )
+                    + "  - Perfect checksum : outer-ip=%s, outer-l4=%s, ip=%s, l4=%s.\n"
+                    % get_packet_checksums(
+                        packet,
+                        good_checksum=True,
+                        support_rx_tunnel=support_rx_tunnel,
+                    )
+                )
+            if not verify_olflags:
+                if fwd_verbose is None:
+                    case.logger.info(
+                        "  - Failed verifying ol_flags.\n" + "  - Missing verbose.\n"
+                    )
+                else:
+                    case.logger.info(
+                        "  - Failed verifying ol_flags.\n"
+                        + "  - Expected flags : %s.\n" % olflags[i]
+                        + "  - Received flags : %s.\n"
+                        % (
+                            fwd_verbose["ol_flags"]
+                            if "ol_flags" in fwd_verbose
+                            else "---"
+                        ),
+                    )
+            if not verify_payload:
+                case.logger.info(
+                    "  - Failed verifying payload.\n"
+                    + "  - Expected payload : %s.\n"
+                    % get_packet_payload(packet, support_rx_tunnel=support_rx_tunnel)
+                    + "  - Received payload : %s.\n"
+                    % get_packet_payload(
+                        fwd_packet,
+                        support_rx_tunnel=support_rx_tunnel,
+                    )
+                )
+
+    case.verify(passed, "Failed verifying. Errors logged.")
+
+
+def execute_test_tso(
+    case: TestCase,
+    testpmd: PmdOutput,
+    tester: Tester,
+    tester_tx_interface: str,
+    tester_rx_interface: str,
+    testpmd_port_0: str,
+    testpmd_port_1: str,
+    testpmd_enable_dcf_0: bool = False,
+    testpmd_enable_dcf_1: bool = False,
+    testpmd_bitwidth: int = None,
+    testpmd_log_level: dict = {},
+    testpmd_other_eal_param: str = "",
+    testpmd_param: str = " --enable-rx-cksum --max-pkt-len=9000 ",
+    testpmd_commands: list = [],
+    packets: list = [],
+    packet_interval: int = 0.01,
+    packet_mtu: int = 9000,
+    segment_length: int = 800,
+    support_rx_tunnel: bool = True,
+    support_seg_non_tunnel: bool = True,
+    support_seg_tunnel: bool = True,
+    support_tso: bool = True,
+    support_ufo: bool = True,
+):
+    testpmd_core_mask = "all"
+    testpmd_eal_param = ""
+    testpmd_eal_param += (
+        " --force-max-simd-bitwidth=%d " % testpmd_bitwidth
+        if not testpmd_bitwidth is None
+        else ""
+    )
+    testpmd_eal_param += "".join(
+        [
+            " --log-level='%s,%d' " % (_pmd, testpmd_log_level[_pmd])
+            for _pmd in testpmd_log_level
+        ]
+    )
+    testpmd_eal_param += testpmd_other_eal_param
+    testpmd_ports = [testpmd_port_0, testpmd_port_1]
+    testpmd_port_options = {
+        testpmd_port_0: ("cap=dcf" if testpmd_enable_dcf_0 else ""),
+        testpmd_port_1: ("cap=dcf" if testpmd_enable_dcf_1 else ""),
+    }
+
+    tester.send_expect(
+        "ethtool -K %s rx off tx off tso off gso off gro off lro off"
+        % tester_tx_interface,
+        "# ",
+    )
+    tester.send_expect("ip link set %s up" % tester_tx_interface, "# ")
+    tester.send_expect("ifconfig %s mtu %s" % (tester_rx_interface, packet_mtu), "# ")
+
+    testpmd.start_testpmd(
+        cores=testpmd_core_mask,
+        param=testpmd_param,
+        eal_param=testpmd_eal_param,
+        ports=testpmd_ports,
+        port_options=testpmd_port_options,
+    )
+
+    for command in testpmd_commands:
+        testpmd.execute_cmd(command)
+    testpmd.wait_link_status_up(0, 30)
+    testpmd.wait_link_status_up(1, 30)
+
+    eth_mac = testpmd.get_port_mac(0)
+    eth = Ether(dst=eth_mac, src="52:00:00:00:00:00")
+
+    fwd_packets, fwd_verbose, fwd_stats = execute_fwd_scapy(
+        pmd=testpmd,
+        tester=tester,
+        packets=[eth / p for p in packets],
+        packet_interval=packet_interval,
+        tester_tx_interface=tester_tx_interface,
+        tester_rx_interface=tester_rx_interface,
+    )
+
+    segments_list = get_packets_payload_post_seg_list(
+        packets,
+        seg_len=segment_length,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    segments_count_list = [len(_s) for _s in segments_list]
+    fwd_segments_list = get_packets_payload_pre_seg_list(
+        fwd_packets,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    txflags = get_packets_tso_seg_tx_flag_list(
+        packets,
+        seg_len=segment_length,
+        support_rx_tunnel=support_rx_tunnel,
+        support_seg_non_tunnel=support_seg_non_tunnel,
+        support_seg_tunnel=support_seg_tunnel,
+        support_tso=support_tso,
+        support_ufo=support_ufo,
+    )
+    fwd_verbose_list = get_verbose_list(fwd_verbose)
+    passed = True
+
+    if not (
+        len(fwd_packets) == sum(segments_count_list)
+        and fwd_stats["port 1"]["TX-packets"] == sum(segments_count_list)
+    ):
+        passed = False
+        case.logger.info(
+            "Inconsist expected TX packet segmented count and FWD stats and RX packet count.\n"
+            + "  - TX Packet Expected Segmented Count : %d.\n"
+            % (sum(segments_count_list))
+            + "  - FWD Packet Stats Count : %d.\n" % fwd_stats["port 1"]["TX-packets"]
+            + "  - RX Packet Count : %d.\n" % len(fwd_packets)
+            + "  - Last 3 RX Packet :\n"
+            + "".join(["  - %s.\n" % _p.command() for _p in fwd_packets[-3:]])
+        )
+    if not len(fwd_verbose_list) == len(packets):
+        passed = False
+        case.logger.info(
+            "Inconsist TX packet count and verbose count.\n"
+            + "  - TX Packet Count : %d.\n" % (len(packets))
+            + "  - Verbose Count : %d.\n" % len(fwd_verbose_list)
+            + "  - Last 3 Verbose :\n"
+            + "".join(["  - %s\n" % _v for _v in fwd_verbose_list[-3:]])
+        )
+    if passed:
+        for i, packet in enumerate(packets):
+            fwd_segments_index = sum(segments_count_list[:i])
+            segments = segments_list[i]
+            fwd_segments = fwd_segments_list[
+                fwd_segments_index : fwd_segments_index + segments_count_list[i]
+            ]
+            verify_verbose = all(
+                "extra" in fwd_verbose_list[i]
+                and any([_f in _line for _line in fwd_verbose_list[i]["extra"]])
+                for _f in txflags[i]
+            )
+            verify_segment = check_packet_segment(
+                packet,
+                fwd_segments,
+                seg_len=segment_length,
+                support_rx_tunnel=support_rx_tunnel,
+                support_seg_non_tunnel=support_seg_non_tunnel,
+                support_seg_tunnel=support_seg_tunnel,
+                support_tso=support_tso,
+                support_ufo=support_ufo,
+            )
+            verify_pass = verify_verbose and verify_segment
+            passed &= verify_pass
+            if not verify_pass:
+                case.logger.info(
+                    "=" * 40
+                    + "\n"
+                    + "  - Failed packet : %s.\n" % packet.command()
+                    + "  - Received packet : %s.\n" % fwd_packets[i].command()
+                )
+                if not verify_verbose:
+                    case.logger.info(
+                        "  - Failed verifying verbose.\n"
+                        + "  - Received verbose : %s.\n" % fwd_verbose_list[i]
+                        + "  - Expected flags : %s.\n" % txflags[i],
+                    )
+                if not verify_segment:
+                    case.logger.info(
+                        "  - Failed verifying segmentation.\n"
+                        + "  - Received segment size : %s\n"
+                        % [len(_s) for _s in fwd_segments]
+                        + "  - Expected segment size : %s\n"
+                        % [len(_s) for _s in segments]
+                    )
+
+    case.verify(passed, "Failed verifying. Errors logged.")
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-07-12 19:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-12 19:31 [DTS][Patch V1 0/4] Updating packet module and introducing a new common module Ke Xu
2023-07-12 19:31 ` [DTS][Patch V1 1/4] framework/packet: Add GTPU and GENEVE support for packet module Ke Xu
2023-07-12 19:31 ` [DTS][Patch V1 2/4] framework/packet: Update packet module for new methods Ke Xu
2023-07-12 19:31 ` [DTS][Patch V1 3/4] framework/test_case: Add skip_unsupported decorator Ke Xu
2023-07-12 19:31 ` [DTS][Patch V1 4/4] tests/offload_common: Add offload_common module Ke Xu

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).