new file mode 100644
@@ -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.")