[V1,4/4] tests/offload_common: Add offload_common module.

Message ID 20230712193126.1994361-5-ke1.xu@intel.com (mailing list archive)
State New
Headers
Series Updating packet module and introducing a new common module. |

Checks

Context Check Description
ci/Intel-dts-format-test success Testing OK
ci/Intel-dts-pylama-test success Testing OK
ci/Intel-dts-suite-test success Testing OK

Commit Message

Ke Xu July 12, 2023, 7:31 p.m. UTC
  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
  

Patch

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