Message ID | 20200720140744.210132-1-ohilyard@iol.unh.edu (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | checksum checks: Add hardware offload l3 and l4 cases | expand |
Is the patch based on the latest DTS ? sorry, I can't merge it because of code conflict. Could you please rework it. Btw, all patches, v1/v2... should be based on latest DTS. Thanks, Lijuan. > -----Original Message----- > From: dts <dts-bounces@dpdk.org> On Behalf Of Owen Hilyard > Sent: 2020年7月20日 22:08 > To: dts@dpdk.org > Cc: Yigit, Ferruh <ferruh.yigit@intel.com>; arybchenko@solarflare.com; > olivier.matz@6wind.com; david.marchand@redhat.com; > ivan.malov@oktetlabs.ru; Richardson, Bruce <bruce.richardson@intel.com>; > lylavoie@iol.unh.edu; rasland@mellanox.com; j.hendergart@f5.com; > ohilyard@iol.unh.edu; thomas@monjalon.net > Subject: [dts] [PATCH] checksum checks: Add hardware offload l3 and l4 cases > > add rx and tx l3 test cases > add tx l4 test case > add documentation for new test cases > > Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu> > --- > test_plans/checksum_offload_test_plan.rst | 122 +++++++- > tests/TestSuite_checksum_offload.py | 331 +++++++++++++++++++--- > 2 files changed, 412 insertions(+), 41 deletions(-) > > diff --git a/test_plans/checksum_offload_test_plan.rst > b/test_plans/checksum_offload_test_plan.rst > index 4fd8c5a..de2e1a9 100644 > --- a/test_plans/checksum_offload_test_plan.rst > +++ b/test_plans/checksum_offload_test_plan.rst > @@ -221,7 +221,7 @@ combination: good/bad ip checksum + good/bad > udp/tcp checksum. > > Check the Rx checksum flags consistent with expected flags. > > -Test Case: Hardware Checksum Check L4 > +Test Case: Hardware Checksum Check L4 RX > =========================================== > This test involves testing many different scenarios with a L4 checksum. > A variety of tunneling protocols, L3 protocols and L4 protocols are combined > @@ -256,4 +256,122 @@ Send a packet with a bad checksum:: > rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > -Verify flags are as expected. > \ No newline at end of file > +Verify flags are as expected. > + > +Test Case: Hardware Checksum Check L3 RX > +=========================================== > +This test involves testing L3 checksum hardware offload. > +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and > +IPv6's lack of a checksum, only IPv4's checksum is tested. > + > +Setup the ``csum`` forwarding mode:: > + > + testpmd> set fwd csum > + Set csum packet forwarding mode > + > +Start the packet forwarding:: > + > + testpmd> start > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > + nb forwarding cores=1 - nb forwarding ports=10 > + RX queues=1 - RX desc=128 - RX free threshold=64 > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > + TX queues=1 - TX desc=512 - TX free threshold=0 > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > + > +Send a packet with a good checksum:: > + > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Send a packet with a bad checksum:: > + > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Verify flags are as expected. > + > +Test Case: Hardware Checksum Check L4 TX > +=========================================== > +This test involves testing many different scenarios with a L4 checksum. > +A variety of tunneling protocols, L3 protocols and L4 protocols are > +combined to test as many scenarios as possible. Currently, UDP, TCP and > +SCTP are used as L4 protocols, with IP and IPv6 being used at level 3. > +The tested tunneling protocols are VXLAN and GRE. This test is used to > +determine whether the hardware offloading of checksums works properly. > + > +Setup the ``csum`` forwarding mode:: > + > + testpmd> set fwd csum > + Set csum packet forwarding mode > + > +Start the packet forwarding:: > + > + testpmd> start > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > + nb forwarding cores=1 - nb forwarding ports=10 > + RX queues=1 - RX desc=128 - RX free threshold=64 > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > + TX queues=1 - TX desc=512 - TX free threshold=0 > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > + > + > +Start a packet capture on the tester in the backround:: > + > + # tcpdump -i <iface> -s 65535 -w > + /tmp/tester/test_hardware_checksum_check_l4_tx_capture.pcap & > + > +Send a packet with a good checksum:: > + > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Send a packet with a bad checksum:: > + > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_GOOD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Inspect the pcap file from the packet capture and verify the checksums. > + > +Test Case: Hardware Checksum Check L3 TX > +=========================================== > +This test involves testing L3 checksum hardware offload. > +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and > +IPv6's lack of a checksum, only IPv4's checksum is tested. > + > +Setup the ``csum`` forwarding mode:: > + > + testpmd> set fwd csum > + Set csum packet forwarding mode > + > +Start the packet forwarding:: > + > + testpmd> start > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > + nb forwarding cores=1 - nb forwarding ports=10 > + RX queues=1 - RX desc=128 - RX free threshold=64 > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > + TX queues=1 - TX desc=512 - TX free threshold=0 > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > + > + > +Start a packet capture on the tester in the backround:: > + > + # tcpdump -i <iface> -s 65535 -w > + /tmp/tester/test_hardware_checksum_check_l3_tx_capture.pcap & > + > +Send a packet with a good checksum with a 1 in it's payload:: > + > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Send a packet with a bad checksum with a 0 in it's payload:: > + > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_BAD > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > + > +Inspect the pcap file from the packet capture and verify the checksums. > diff --git a/tests/TestSuite_checksum_offload.py > b/tests/TestSuite_checksum_offload.py > index 9ff5a41..c4a877d 100644 > --- a/tests/TestSuite_checksum_offload.py > +++ b/tests/TestSuite_checksum_offload.py > @@ -47,8 +47,18 @@ import time > > from rst import RstReport > import utils > - > +from exception import VerifyFailure > +from pktgen import PacketGeneratorHelper from scapy.layers.inet import > +UDP, TCP, IP from scapy.layers.inet6 import IPv6 from scapy.layers.l2 > +import Ether, GRE from scapy.layers.sctp import SCTP from > +scapy.layers.vxlan import VXLAN from scapy.packet import Raw from > +scapy.utils import wrpcap, rdpcap from test_capabilities import > +DRIVER_TEST_LACK_CAPA > from test_case import TestCase > + > from framework.pmd_output import PmdOutput from test_capabilities import > DRIVER_TEST_LACK_CAPA from pktgen import PacketGeneratorHelper @@ - > 57,6 +67,33 @@ import packet > > from settings import FOLDERS > > +l3_proto_classes = [ > + IP, > + IPv6 > +] > + > +l4_proto_classes = [ > + UDP, > + TCP, > + SCTP > +] > + > +tunnelling_proto_classes = [ > + VXLAN, > + GRE, > +] > + > +l3_protos = [ > + "IP", > + "IPv6" > +] > + > +l4_protos = [ > + "UDP", > + "TCP", > + "SCTP", > +] > + > > class TestChecksumOffload(TestCase): > > @@ -79,8 +116,6 @@ class TestChecksumOffload(TestCase): > cur_path = os.path.dirname( > os.path.dirname(os.path.realpath(__file__))) > self.output_path = os.sep.join([cur_path, self.logger.log_path]) > - # create an instance to set stream field setting > - self.pktgen_helper = PacketGeneratorHelper() > > def set_up(self): > """ > @@ -243,7 +278,6 @@ class TestChecksumOffload(TestCase): > def send_pkt_expect_good_bad_from_flag(self, pkt_str: str, flag: str, > test_name: str, should_pass: bool = True): > self.pmdout.get_output(timeout=5) # Remove any old output > self.scapy_exec(f"sendp({pkt_str}, iface=iface)") > - time.sleep(1) > testpmd_output: str = self.pmdout.get_output(timeout=5) > self.verify(flag in testpmd_output, > f"Flag {flag[:-1]} not found for test {test_name}, please run > test_rx_checksum_valid_flags.") @@ -269,11 +303,147 @@ class > TestChecksumOffload(TestCase): > > return None > > - def scapy_exec(self, cmd: str): > - return self.tester.send_expect(cmd, ">>>") > + def validate_checksum(self, pkt, layer) -> bool: > + """ > + @param pkt: The packet to validate the checksum of. > + @return: Whether the checksum was valid. > + """ > + if pkt is None: > + return False > + > + csum = pkt[layer].chksum > + del pkt[layer].chksum > + # Converting it to raw will calculate the checksum > + return layer(Raw(pkt[layer])).chksum == csum > > - def scapy_send_append(self, packet: str): > - return self.tester.scapy_append(f'sendp({packet}, iface=iface)') > + def scapy_exec(self, cmd: str, timeout=1) -> str: > + return self.tester.send_expect(cmd, ">>>", timeout=timeout) > + > + def get_packets(self, dut_mac, tester_mac): > + eth = Ether(dst=dut_mac, src=tester_mac) > + packets = [] > + checksum_options = ({}, {'chksum': 0xf},) > + # Untunneled > + for l3 in l3_proto_classes: > + for l4 in l4_proto_classes: > + for chksum in checksum_options: > + # The packet's data can be used to identify how the packet was > constructed so avoid any issues with > + # ordering > + pkt = eth / l3() / l4(**chksum) / ( > + f'UNTUNNELED,{l3.__name__},{l4.__name__},{" " if > len(chksum.values()) == 0 else chksum["chksum"]}' > + ) > + > + # Prevents the default behavior which adds DNS headers > + if l4 == UDP: > + pkt[UDP].dport, pkt[UDP].sport = 1001, 1001 > + > + packets.append(pkt) > + > + # Tunneled > + # VXLAN > + for l3 in l3_proto_classes: > + for l4 in l4_proto_classes: > + for outer_arg in checksum_options: > + for inner_arg in checksum_options: > + pkt = eth / l3() / UDP(**outer_arg) / VXLAN() / Ether() / l3() / > l4(**inner_arg) / ( > + f'VXLAN,{l3.__name__},{l4.__name__},' > + f'{" " if len(outer_arg.values()) == 0 else outer_arg["chksum"]},' > + f'{" " if len(inner_arg.values()) == 0 else inner_arg["chksum"]}' > + ) > + # Prevents the default behavior which adds DNS headers > + if l4 == UDP: > + pkt[VXLAN][UDP].dport, > + pkt[VXLAN][UDP].sport = 1001, 1001 > + > + packets.append(pkt) > + # GRE > + for l3 in l3_proto_classes: > + for l4 in l4_proto_classes: > + for chksum in checksum_options: > + pkt = eth / l3() / GRE() / l3() / l4(**chksum) / ( > + f'GRE,{l3.__name__},{l4.__name__},{" " if len(chksum.values()) == > 0 else chksum["chksum"]}' > + ) > + > + # Prevents the default behavior which adds DNS headers > + if l4 == UDP: > + pkt[GRE][UDP].dport, pkt[GRE][UDP].sport = > + 1001, 1001 > + > + packets.append(pkt) > + > + return packets > + > + def replay_pcap_file_on_tester(self, iface, packet_file_path): > + self.tester.send_expect("scapy", ">>>") > + self.scapy_exec(f"packets = rdpcap('{packet_file_path}')") > + self.scapy_exec(f"sendp(packets, iface={iface})") > + self.tester.send_expect("quit()", "# ") > + > + def validate_packet_list_checksums(self, packets): > + name_to_class_dict = { > + 'UDP': UDP, > + 'TCP': TCP, > + 'SCTP': SCTP, > + 'IP': IP, > + 'IPv6': IPv6, > + 'VXLAN': VXLAN, > + 'GRE': GRE, > + } > + > + error_messages = [] > + > + untunnelled_error_message = f"Invalid untunneled checksum state > for %s/%s with a %s checksum." > + > + vxlan_error_message = f"Invalid VXLAN tunnelled %s checksum state > for %s/%s" \ > + f" with a %s inner checksum and a %s outer checksum." > + > + gre_error_message = f"Invalid GRE tunnelled checksum state for %s/%s > with a %s checksum." > + > + for packet in packets: > + payload: str > + # try: > + payload = packet[Raw].load.decode('utf-8').split(",") > + # # This error usually happens with tunneling protocols, and means that > an additional cast is needed > + # except UnicodeDecodeError: > + # for proto in tunnelling_proto_classes: > + > + l3 = name_to_class_dict[payload[1]] > + l4 = name_to_class_dict[payload[2]] > + if payload[0] == "UNTUNNELED": > + chksum_should_be_valid = payload[3] == " " > + if self.validate_checksum(packet, l4) != chksum_should_be_valid: > + error_messages.append( > + untunnelled_error_message % ( > + l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid > == '' else 'invalid' > + ) > + ) > + elif payload[0] == "VXLAN": > + outer_chksum_should_be_valid = payload[3] == " " > + inner_chksum_should_be_valid = payload[4] == " " > + if self.validate_checksum(packet[VXLAN], l4) != > inner_chksum_should_be_valid: > + error_messages.append( > + vxlan_error_message % ( > + "inner", l4.__name__, l3.__name__, > + 'valid' if inner_chksum_should_be_valid == '' else 'invalid', > + 'valid' if outer_chksum_should_be_valid == '' else 'invalid' > + ) > + ) > + if self.validate_checksum(packet, l4) != > outer_chksum_should_be_valid: > + error_messages.append( > + vxlan_error_message % ( > + "outer", l3.__name__, l4.__name__, > + 'valid' if inner_chksum_should_be_valid == '' else 'invalid', > + 'valid' if outer_chksum_should_be_valid == '' else 'invalid' > + ) > + ) > + elif payload[0] == "GRE": > + chksum_should_be_valid = payload[3] == " " > + if self.validate_checksum(packet, l4) != chksum_should_be_valid: > + error_messages.append( > + gre_error_message % ( > + l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid > == '' else 'invalid' > + ) > + ) > + > + return error_messages > > # > # > @@ -448,6 +618,9 @@ class TestChecksumOffload(TestCase): > > # clear streams before add new streams > self.tester.pktgen.clear_streams() > + # create an instance to set stream field setting > + # Moved here because it messes with the ability of the functional tests > to use scapy. > + self.pktgen_helper = PacketGeneratorHelper() > # run packet generator > streams = self.pktgen_helper.prepare_stream_from_tginput(tgenInput, > 100, > None, self.tester.pktgen) @@ -511,53 +684,93 @@ class > TestChecksumOffload(TestCase): > self.dut.send_expect("quit", "#", 10) > self.result_table_print() > > - def test_hardware_checksum_check_ip(self): > + def test_hardware_checksum_check_ip_rx(self): > self.dut.send_expect("start", "testpmd>") > + self.tester.send_expect("scapy", ">>>") > self.checksum_enablehw(self.dut_ports[0]) > + self.dut.send_expect("start", "testpmd>") > > verification_errors: List[VerifyFailure] = [] > > - # untunnelled > - vf = > self.try_helper_hardware_checksum_check_catch_failure('Ether(dst="%s", > src="%s")/IP(%s)/UDP()/("X"*50)', > - "PKT_RX_IP_CKSUM_") > - if vf is not None: > - verification_errors.append(vf) > + iface = > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > + tester_mac = > + self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > > - # tunneled inner > - vf = self.try_helper_hardware_checksum_check_catch_failure( > - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', > - "PKT_RX_OUTER_IP_CKSUM_") > - if vf is not None: > - verification_errors.append(vf) > + self.scapy_exec(f"eth = Ether(dst='{dut_mac}', src='{tester_mac}')") > + self.scapy_exec(f"iface = '{iface}'") > > - # tunneled outer > - vf = self.try_helper_hardware_checksum_check_catch_failure( > - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', > - "PKT_RX_OUTER_IP_CKSUM_") > - if vf is not None: > - verification_errors.append(vf) > + # Untunnelled > + for l4 in l4_protos: > + for chksum in "", "chksum=0xf": > + vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( > + f"eth/IP({chksum})/{l4}()/(X'*50)", > + "PKT_RX_IP_CKSUM_", f"{l4}", > + should_pass=(chksum == "")) > + if vf is not None: > + verification_errors.append(vf) > > - self.verify(len(verification_errors) == 0, "\n".join(verification_errors)) > + for err in verification_errors: > + self.logger.error(str(err)) > + self.verify(len(verification_errors) == 0, "See previous > + output") > > + self.tester.send_expect("quit()", "# ") > self.dut.send_expect("stop", "testpmd>") > > - def test_hardware_checksum_check_l4(self): > + def test_hardware_checksum_check_ip_tx(self): > self.checksum_enablehw(self.dut_ports[0]) > self.dut.send_expect("start", "testpmd>") > > verification_errors: List[VerifyFailure] = [] > > - l3_protos: List[str] = [ > - "IP", > - "IPv6" > - ] > + iface = > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > + tester_mac = > self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > + eth = Ether(dst=dut_mac, src=tester_mac) > > - l4_protos: List[str] = [ > - "UDP", > - "TCP", > - "SCTP", > + checksum_options = ({}, {'chksum': 0xf},) > + > + packets = [ > + eth / IP(**chksum) / TCP() / Raw(load=str(int(len(chksum) > + != 1))) for chksum in checksum_options > ] > > + capture_file_name = > "test_hardware_checksum_check_l3_tx_capture.pcap" > + > + packet_file_path = > "/tmp/test_hardware_checksum_check_l3_tx_packets.pcap" > + capture_file_path = "/tmp/tester/" + capture_file_name > + > + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w > + {capture_file_path} &", "# ") > + > + wrpcap(packet_file_path, packets) > + self.tester.session.copy_file_to(packet_file_path, > + packet_file_path) > + > + self.replay_pcap_file_on_tester(iface, packet_file_path) > + > + self.tester.session.copy_file_from(packet_file_path, > + "output/tmp/pcap/" + capture_file_name) > + > + captured_packets = rdpcap("output/tmp/pcap/" + > + capture_file_name) > + > + self.verify(len(packets) == len(captured_packets), "Not all > + packets were received") > + > + error_messages = [] > + for pkt in captured_packets: > + should_pass = pkt[TCP].payload.build() == b'1' > + if not (self.validate_checksum(pkt, IP) == should_pass): > + error_messages.append(f"A packet was marked as having a" > + f"{' valid' if should_pass == '' else 'n invalid'}" > + f" checksum when it should have > + had the opposite.") > + > + self.dut.send_expect("stop", "testpmd>") > + if len(error_messages) != 0: > + for error_msg in error_messages: > + self.logger.error(error_msg) > + self.verify(False, "See prior output") > + > + def test_hardware_checksum_check_l4_rx(self): > + self.checksum_enablehw(self.dut_ports[0]) > + self.dut.send_expect("start", "testpmd>") > + > + verification_errors: List[VerifyFailure] = [] > + > iface = > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > tester_mac = > self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > @@ -589,7 +802,7 @@ class TestChecksumOffload(TestCase): > should_pass = outer_arg == "" > vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( > f"eth/{l3}()/{l4}({outer_arg})/VXLAN()/{l3}()/" > - f"{l4}({inner_arg})/('X'*50)", > + f"{l4}(chksum={inner_arg})/('X'*50)", > flag, f"{l3}/{l4}/VXLAN/{l3}/{l4}", > should_pass=should_pass) > > @@ -602,8 +815,7 @@ class TestChecksumOffload(TestCase): > for inner_arg in "", "chksum=0xf": > should_pass: bool = inner_arg == "" > vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( > - f"eth/{l3}()/GRE()/{l3}()/" > - f"{l4}({inner_arg})/('X'*50)", > + > + f"eth/{l3}()/GRE()/{l3}()/{l4}({inner_arg})/('X'*50)", > "PKT_RX_L4_CKSUM_", f"{l3}/GRE/{l3}/{l4}", > should_pass=should_pass) > > @@ -615,6 +827,8 @@ class TestChecksumOffload(TestCase): > # updated this test case can easily take advantage of the new functionality. > > # # GENEVE > + # # This import is over here so that it is not forgotten when the update > happens > + # from scapy.contrib.geneve import GENEVE > # for l3_outer in l3_protos: > # for l4_outer in l4_protos: > # for l3_inner in l3_protos: > @@ -638,7 +852,46 @@ class TestChecksumOffload(TestCase): > self.logger.error(str(err)) > self.verify(len(verification_errors) == 0, "See previous output") > > + self.tester.send_expect("quit", "#") > + self.dut.send_expect("stop", "testpmd>") > + > + def test_hardware_checksum_check_l4_tx(self): > + self.checksum_enablehw(self.dut_ports[0]) > + self.dut.send_expect("start", "testpmd>") > + > + verification_errors: List[VerifyFailure] = [] > + > + iface = > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > + tester_mac = > + self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > + > + packets = self.get_packets(dut_mac, tester_mac) > + > + capture_file_name = > "test_hardware_checksum_check_l4_tx_capture.pcap" > + > + packet_file_path = > "/tmp/test_hardware_checksum_check_l4_tx_packets.pcap" > + capture_file_path = "/tmp/tester/" + capture_file_name > + > + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w > + {capture_file_path} &", "# ") > + > + wrpcap(packet_file_path, packets) > + self.tester.session.copy_file_to(packet_file_path, > + packet_file_path) > + > + self.replay_pcap_file_on_tester(iface, packet_file_path) > + > + self.tester.session.copy_file_from(packet_file_path, > + "output/tmp/pcap/" + capture_file_name) > + > + captured_packets = rdpcap("output/tmp/pcap/" + > + capture_file_name) > + > + self.verify(len(packets) == len(captured_packets), "Not all > + packets were received") > + > + error_messages = > + self.validate_packet_list_checksums(captured_packets) > + > self.dut.send_expect("stop", "testpmd>") > + if len(error_messages) != 0: > + for error_msg in error_messages: > + self.logger.error(error_msg) > + self.verify(False, "See prior output") > > def tear_down(self): > """ > -- > 2.25.1
Unfortunately I had to start a new email thread since intel's server didn't like the self-signed certificate on my local mail server. I merged all of the patches into one patch so it's a little easier to review for the second time. On Thu, Jul 23, 2020 at 10:45 PM Tu, Lijuan <lijuan.tu@intel.com> wrote: > Is the patch based on the latest DTS ? sorry, I can't merge it because of > code conflict. Could you please rework it. Btw, all patches, v1/v2... > should be based on latest DTS. > > Thanks, Lijuan. > > > -----Original Message----- > > From: dts <dts-bounces@dpdk.org> On Behalf Of Owen Hilyard > > Sent: 2020年7月20日 22:08 > > To: dts@dpdk.org > > Cc: Yigit, Ferruh <ferruh.yigit@intel.com>; arybchenko@solarflare.com; > > olivier.matz@6wind.com; david.marchand@redhat.com; > > ivan.malov@oktetlabs.ru; Richardson, Bruce <bruce.richardson@intel.com>; > > lylavoie@iol.unh.edu; rasland@mellanox.com; j.hendergart@f5.com; > > ohilyard@iol.unh.edu; thomas@monjalon.net > > Subject: [dts] [PATCH] checksum checks: Add hardware offload l3 and l4 > cases > > > > add rx and tx l3 test cases > > add tx l4 test case > > add documentation for new test cases > > > > Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu> > > --- > > test_plans/checksum_offload_test_plan.rst | 122 +++++++- > > tests/TestSuite_checksum_offload.py | 331 +++++++++++++++++++--- > > 2 files changed, 412 insertions(+), 41 deletions(-) > > > > diff --git a/test_plans/checksum_offload_test_plan.rst > > b/test_plans/checksum_offload_test_plan.rst > > index 4fd8c5a..de2e1a9 100644 > > --- a/test_plans/checksum_offload_test_plan.rst > > +++ b/test_plans/checksum_offload_test_plan.rst > > @@ -221,7 +221,7 @@ combination: good/bad ip checksum + good/bad > > udp/tcp checksum. > > > > Check the Rx checksum flags consistent with expected flags. > > > > -Test Case: Hardware Checksum Check L4 > > +Test Case: Hardware Checksum Check L4 RX > > =========================================== > > This test involves testing many different scenarios with a L4 checksum. > > A variety of tunneling protocols, L3 protocols and L4 protocols are > combined > > @@ -256,4 +256,122 @@ Send a packet with a bad checksum:: > > rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > > > -Verify flags are as expected. > > \ No newline at end of file > > +Verify flags are as expected. > > + > > +Test Case: Hardware Checksum Check L3 RX > > +=========================================== > > +This test involves testing L3 checksum hardware offload. > > +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and > > +IPv6's lack of a checksum, only IPv4's checksum is tested. > > + > > +Setup the ``csum`` forwarding mode:: > > + > > + testpmd> set fwd csum > > + Set csum packet forwarding mode > > + > > +Start the packet forwarding:: > > + > > + testpmd> start > > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > > + nb forwarding cores=1 - nb forwarding ports=10 > > + RX queues=1 - RX desc=128 - RX free threshold=64 > > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > > + TX queues=1 - TX desc=512 - TX free threshold=0 > > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > > + > > +Send a packet with a good checksum:: > > + > > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Send a packet with a bad checksum:: > > + > > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Verify flags are as expected. > > + > > +Test Case: Hardware Checksum Check L4 TX > > +=========================================== > > +This test involves testing many different scenarios with a L4 checksum. > > +A variety of tunneling protocols, L3 protocols and L4 protocols are > > +combined to test as many scenarios as possible. Currently, UDP, TCP and > > +SCTP are used as L4 protocols, with IP and IPv6 being used at level 3. > > +The tested tunneling protocols are VXLAN and GRE. This test is used to > > +determine whether the hardware offloading of checksums works properly. > > + > > +Setup the ``csum`` forwarding mode:: > > + > > + testpmd> set fwd csum > > + Set csum packet forwarding mode > > + > > +Start the packet forwarding:: > > + > > + testpmd> start > > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > > + nb forwarding cores=1 - nb forwarding ports=10 > > + RX queues=1 - RX desc=128 - RX free threshold=64 > > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > > + TX queues=1 - TX desc=512 - TX free threshold=0 > > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > > + > > + > > +Start a packet capture on the tester in the backround:: > > + > > + # tcpdump -i <iface> -s 65535 -w > > + /tmp/tester/test_hardware_checksum_check_l4_tx_capture.pcap & > > + > > +Send a packet with a good checksum:: > > + > > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Send a packet with a bad checksum:: > > + > > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_GOOD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Inspect the pcap file from the packet capture and verify the checksums. > > + > > +Test Case: Hardware Checksum Check L3 TX > > +=========================================== > > +This test involves testing L3 checksum hardware offload. > > +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and > > +IPv6's lack of a checksum, only IPv4's checksum is tested. > > + > > +Setup the ``csum`` forwarding mode:: > > + > > + testpmd> set fwd csum > > + Set csum packet forwarding mode > > + > > +Start the packet forwarding:: > > + > > + testpmd> start > > + csum packet forwarding - CRC stripping disabled - packets/burst=32 > > + nb forwarding cores=1 - nb forwarding ports=10 > > + RX queues=1 - RX desc=128 - RX free threshold=64 > > + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 > > + TX queues=1 - TX desc=512 - TX free threshold=0 > > + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 > > + > > + > > +Start a packet capture on the tester in the backround:: > > + > > + # tcpdump -i <iface> -s 65535 -w > > + /tmp/tester/test_hardware_checksum_check_l3_tx_capture.pcap & > > + > > +Send a packet with a good checksum with a 1 in it's payload:: > > + > > + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Send a packet with a bad checksum with a 0 in it's payload:: > > + > > + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: > > + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 > > flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_BAD > > PKT_RX_OUTER_L4_CKSUM_UNKNOWN > > + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 > > + > > +Inspect the pcap file from the packet capture and verify the checksums. > > diff --git a/tests/TestSuite_checksum_offload.py > > b/tests/TestSuite_checksum_offload.py > > index 9ff5a41..c4a877d 100644 > > --- a/tests/TestSuite_checksum_offload.py > > +++ b/tests/TestSuite_checksum_offload.py > > @@ -47,8 +47,18 @@ import time > > > > from rst import RstReport > > import utils > > - > > +from exception import VerifyFailure > > +from pktgen import PacketGeneratorHelper from scapy.layers.inet import > > +UDP, TCP, IP from scapy.layers.inet6 import IPv6 from scapy.layers.l2 > > +import Ether, GRE from scapy.layers.sctp import SCTP from > > +scapy.layers.vxlan import VXLAN from scapy.packet import Raw from > > +scapy.utils import wrpcap, rdpcap from test_capabilities import > > +DRIVER_TEST_LACK_CAPA > > from test_case import TestCase > > + > > from framework.pmd_output import PmdOutput from test_capabilities > import > > DRIVER_TEST_LACK_CAPA from pktgen import PacketGeneratorHelper @@ - > > 57,6 +67,33 @@ import packet > > > > from settings import FOLDERS > > > > +l3_proto_classes = [ > > + IP, > > + IPv6 > > +] > > + > > +l4_proto_classes = [ > > + UDP, > > + TCP, > > + SCTP > > +] > > + > > +tunnelling_proto_classes = [ > > + VXLAN, > > + GRE, > > +] > > + > > +l3_protos = [ > > + "IP", > > + "IPv6" > > +] > > + > > +l4_protos = [ > > + "UDP", > > + "TCP", > > + "SCTP", > > +] > > + > > > > class TestChecksumOffload(TestCase): > > > > @@ -79,8 +116,6 @@ class TestChecksumOffload(TestCase): > > cur_path = os.path.dirname( > > os.path.dirname(os.path.realpath(__file__))) > > self.output_path = os.sep.join([cur_path, > self.logger.log_path]) > > - # create an instance to set stream field setting > > - self.pktgen_helper = PacketGeneratorHelper() > > > > def set_up(self): > > """ > > @@ -243,7 +278,6 @@ class TestChecksumOffload(TestCase): > > def send_pkt_expect_good_bad_from_flag(self, pkt_str: str, flag: > str, > > test_name: str, should_pass: bool = True): > > self.pmdout.get_output(timeout=5) # Remove any old output > > self.scapy_exec(f"sendp({pkt_str}, iface=iface)") > > - time.sleep(1) > > testpmd_output: str = self.pmdout.get_output(timeout=5) > > self.verify(flag in testpmd_output, > > f"Flag {flag[:-1]} not found for test {test_name}, > please run > > test_rx_checksum_valid_flags.") @@ -269,11 +303,147 @@ class > > TestChecksumOffload(TestCase): > > > > return None > > > > - def scapy_exec(self, cmd: str): > > - return self.tester.send_expect(cmd, ">>>") > > + def validate_checksum(self, pkt, layer) -> bool: > > + """ > > + @param pkt: The packet to validate the checksum of. > > + @return: Whether the checksum was valid. > > + """ > > + if pkt is None: > > + return False > > + > > + csum = pkt[layer].chksum > > + del pkt[layer].chksum > > + # Converting it to raw will calculate the checksum > > + return layer(Raw(pkt[layer])).chksum == csum > > > > - def scapy_send_append(self, packet: str): > > - return self.tester.scapy_append(f'sendp({packet}, iface=iface)') > > + def scapy_exec(self, cmd: str, timeout=1) -> str: > > + return self.tester.send_expect(cmd, ">>>", timeout=timeout) > > + > > + def get_packets(self, dut_mac, tester_mac): > > + eth = Ether(dst=dut_mac, src=tester_mac) > > + packets = [] > > + checksum_options = ({}, {'chksum': 0xf},) > > + # Untunneled > > + for l3 in l3_proto_classes: > > + for l4 in l4_proto_classes: > > + for chksum in checksum_options: > > + # The packet's data can be used to identify how the > packet was > > constructed so avoid any issues with > > + # ordering > > + pkt = eth / l3() / l4(**chksum) / ( > > + f'UNTUNNELED,{l3.__name__},{l4.__name__},{" " if > > len(chksum.values()) == 0 else chksum["chksum"]}' > > + ) > > + > > + # Prevents the default behavior which adds DNS > headers > > + if l4 == UDP: > > + pkt[UDP].dport, pkt[UDP].sport = 1001, 1001 > > + > > + packets.append(pkt) > > + > > + # Tunneled > > + # VXLAN > > + for l3 in l3_proto_classes: > > + for l4 in l4_proto_classes: > > + for outer_arg in checksum_options: > > + for inner_arg in checksum_options: > > + pkt = eth / l3() / UDP(**outer_arg) / VXLAN() / > Ether() / l3() / > > l4(**inner_arg) / ( > > + f'VXLAN,{l3.__name__},{l4.__name__},' > > + f'{" " if len(outer_arg.values()) == 0 else > outer_arg["chksum"]},' > > + f'{" " if len(inner_arg.values()) == 0 else > inner_arg["chksum"]}' > > + ) > > + # Prevents the default behavior which adds DNS > headers > > + if l4 == UDP: > > + pkt[VXLAN][UDP].dport, > > + pkt[VXLAN][UDP].sport = 1001, 1001 > > + > > + packets.append(pkt) > > + # GRE > > + for l3 in l3_proto_classes: > > + for l4 in l4_proto_classes: > > + for chksum in checksum_options: > > + pkt = eth / l3() / GRE() / l3() / l4(**chksum) / ( > > + f'GRE,{l3.__name__},{l4.__name__},{" " if > len(chksum.values()) == > > 0 else chksum["chksum"]}' > > + ) > > + > > + # Prevents the default behavior which adds DNS > headers > > + if l4 == UDP: > > + pkt[GRE][UDP].dport, pkt[GRE][UDP].sport = > > + 1001, 1001 > > + > > + packets.append(pkt) > > + > > + return packets > > + > > + def replay_pcap_file_on_tester(self, iface, packet_file_path): > > + self.tester.send_expect("scapy", ">>>") > > + self.scapy_exec(f"packets = rdpcap('{packet_file_path}')") > > + self.scapy_exec(f"sendp(packets, iface={iface})") > > + self.tester.send_expect("quit()", "# ") > > + > > + def validate_packet_list_checksums(self, packets): > > + name_to_class_dict = { > > + 'UDP': UDP, > > + 'TCP': TCP, > > + 'SCTP': SCTP, > > + 'IP': IP, > > + 'IPv6': IPv6, > > + 'VXLAN': VXLAN, > > + 'GRE': GRE, > > + } > > + > > + error_messages = [] > > + > > + untunnelled_error_message = f"Invalid untunneled checksum state > > for %s/%s with a %s checksum." > > + > > + vxlan_error_message = f"Invalid VXLAN tunnelled %s checksum > state > > for %s/%s" \ > > + f" with a %s inner checksum and a %s > outer checksum." > > + > > + gre_error_message = f"Invalid GRE tunnelled checksum state for > %s/%s > > with a %s checksum." > > + > > + for packet in packets: > > + payload: str > > + # try: > > + payload = packet[Raw].load.decode('utf-8').split(",") > > + # # This error usually happens with tunneling protocols, > and means that > > an additional cast is needed > > + # except UnicodeDecodeError: > > + # for proto in tunnelling_proto_classes: > > + > > + l3 = name_to_class_dict[payload[1]] > > + l4 = name_to_class_dict[payload[2]] > > + if payload[0] == "UNTUNNELED": > > + chksum_should_be_valid = payload[3] == " " > > + if self.validate_checksum(packet, l4) != > chksum_should_be_valid: > > + error_messages.append( > > + untunnelled_error_message % ( > > + l3.__name__, l4.__name__, 'valid' if > chksum_should_be_valid > > == '' else 'invalid' > > + ) > > + ) > > + elif payload[0] == "VXLAN": > > + outer_chksum_should_be_valid = payload[3] == " " > > + inner_chksum_should_be_valid = payload[4] == " " > > + if self.validate_checksum(packet[VXLAN], l4) != > > inner_chksum_should_be_valid: > > + error_messages.append( > > + vxlan_error_message % ( > > + "inner", l4.__name__, l3.__name__, > > + 'valid' if inner_chksum_should_be_valid == > '' else 'invalid', > > + 'valid' if outer_chksum_should_be_valid == > '' else 'invalid' > > + ) > > + ) > > + if self.validate_checksum(packet, l4) != > > outer_chksum_should_be_valid: > > + error_messages.append( > > + vxlan_error_message % ( > > + "outer", l3.__name__, l4.__name__, > > + 'valid' if inner_chksum_should_be_valid == > '' else 'invalid', > > + 'valid' if outer_chksum_should_be_valid == > '' else 'invalid' > > + ) > > + ) > > + elif payload[0] == "GRE": > > + chksum_should_be_valid = payload[3] == " " > > + if self.validate_checksum(packet, l4) != > chksum_should_be_valid: > > + error_messages.append( > > + gre_error_message % ( > > + l3.__name__, l4.__name__, 'valid' if > chksum_should_be_valid > > == '' else 'invalid' > > + ) > > + ) > > + > > + return error_messages > > > > # > > # > > @@ -448,6 +618,9 @@ class TestChecksumOffload(TestCase): > > > > # clear streams before add new streams > > self.tester.pktgen.clear_streams() > > + # create an instance to set stream field setting > > + # Moved here because it messes with the ability of the > functional tests > > to use scapy. > > + self.pktgen_helper = PacketGeneratorHelper() > > # run packet generator > > streams = > self.pktgen_helper.prepare_stream_from_tginput(tgenInput, > > 100, > > None, self.tester.pktgen) @@ -511,53 +684,93 @@ > class > > TestChecksumOffload(TestCase): > > self.dut.send_expect("quit", "#", 10) > > self.result_table_print() > > > > - def test_hardware_checksum_check_ip(self): > > + def test_hardware_checksum_check_ip_rx(self): > > self.dut.send_expect("start", "testpmd>") > > + self.tester.send_expect("scapy", ">>>") > > self.checksum_enablehw(self.dut_ports[0]) > > + self.dut.send_expect("start", "testpmd>") > > > > verification_errors: List[VerifyFailure] = [] > > > > - # untunnelled > > - vf = > > self.try_helper_hardware_checksum_check_catch_failure('Ether(dst="%s", > > src="%s")/IP(%s)/UDP()/("X"*50)', > > - > "PKT_RX_IP_CKSUM_") > > - if vf is not None: > > - verification_errors.append(vf) > > + iface = > > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > > + tester_mac = > > + self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > > > > - # tunneled inner > > - vf = self.try_helper_hardware_checksum_check_catch_failure( > > - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', > > - "PKT_RX_OUTER_IP_CKSUM_") > > - if vf is not None: > > - verification_errors.append(vf) > > + self.scapy_exec(f"eth = Ether(dst='{dut_mac}', > src='{tester_mac}')") > > + self.scapy_exec(f"iface = '{iface}'") > > > > - # tunneled outer > > - vf = self.try_helper_hardware_checksum_check_catch_failure( > > - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', > > - "PKT_RX_OUTER_IP_CKSUM_") > > - if vf is not None: > > - verification_errors.append(vf) > > + # Untunnelled > > + for l4 in l4_protos: > > + for chksum in "", "chksum=0xf": > > + vf = > self.send_pkt_expect_good_bad_from_flag_catch_failure( > > + f"eth/IP({chksum})/{l4}()/(X'*50)", > > + "PKT_RX_IP_CKSUM_", f"{l4}", > > + should_pass=(chksum == "")) > > + if vf is not None: > > + verification_errors.append(vf) > > > > - self.verify(len(verification_errors) == 0, > "\n".join(verification_errors)) > > + for err in verification_errors: > > + self.logger.error(str(err)) > > + self.verify(len(verification_errors) == 0, "See previous > > + output") > > > > + self.tester.send_expect("quit()", "# ") > > self.dut.send_expect("stop", "testpmd>") > > > > - def test_hardware_checksum_check_l4(self): > > + def test_hardware_checksum_check_ip_tx(self): > > self.checksum_enablehw(self.dut_ports[0]) > > self.dut.send_expect("start", "testpmd>") > > > > verification_errors: List[VerifyFailure] = [] > > > > - l3_protos: List[str] = [ > > - "IP", > > - "IPv6" > > - ] > > + iface = > > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > > + tester_mac = > > self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > > + eth = Ether(dst=dut_mac, src=tester_mac) > > > > - l4_protos: List[str] = [ > > - "UDP", > > - "TCP", > > - "SCTP", > > + checksum_options = ({}, {'chksum': 0xf},) > > + > > + packets = [ > > + eth / IP(**chksum) / TCP() / Raw(load=str(int(len(chksum) > > + != 1))) for chksum in checksum_options > > ] > > > > + capture_file_name = > > "test_hardware_checksum_check_l3_tx_capture.pcap" > > + > > + packet_file_path = > > "/tmp/test_hardware_checksum_check_l3_tx_packets.pcap" > > + capture_file_path = "/tmp/tester/" + capture_file_name > > + > > + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w > > + {capture_file_path} &", "# ") > > + > > + wrpcap(packet_file_path, packets) > > + self.tester.session.copy_file_to(packet_file_path, > > + packet_file_path) > > + > > + self.replay_pcap_file_on_tester(iface, packet_file_path) > > + > > + self.tester.session.copy_file_from(packet_file_path, > > + "output/tmp/pcap/" + capture_file_name) > > + > > + captured_packets = rdpcap("output/tmp/pcap/" + > > + capture_file_name) > > + > > + self.verify(len(packets) == len(captured_packets), "Not all > > + packets were received") > > + > > + error_messages = [] > > + for pkt in captured_packets: > > + should_pass = pkt[TCP].payload.build() == b'1' > > + if not (self.validate_checksum(pkt, IP) == should_pass): > > + error_messages.append(f"A packet was marked as having a" > > + f"{' valid' if should_pass == '' > else 'n invalid'}" > > + f" checksum when it should have > > + had the opposite.") > > + > > + self.dut.send_expect("stop", "testpmd>") > > + if len(error_messages) != 0: > > + for error_msg in error_messages: > > + self.logger.error(error_msg) > > + self.verify(False, "See prior output") > > + > > + def test_hardware_checksum_check_l4_rx(self): > > + self.checksum_enablehw(self.dut_ports[0]) > > + self.dut.send_expect("start", "testpmd>") > > + > > + verification_errors: List[VerifyFailure] = [] > > + > > iface = > > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > > dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > > tester_mac = > > self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > > @@ -589,7 +802,7 @@ class TestChecksumOffload(TestCase): > > should_pass = outer_arg == "" > > vf = > self.send_pkt_expect_good_bad_from_flag_catch_failure( > > > f"eth/{l3}()/{l4}({outer_arg})/VXLAN()/{l3}()/" > > - f"{l4}({inner_arg})/('X'*50)", > > + f"{l4}(chksum={inner_arg})/('X'*50)", > > flag, f"{l3}/{l4}/VXLAN/{l3}/{l4}", > > should_pass=should_pass) > > > > @@ -602,8 +815,7 @@ class TestChecksumOffload(TestCase): > > for inner_arg in "", "chksum=0xf": > > should_pass: bool = inner_arg == "" > > vf = > self.send_pkt_expect_good_bad_from_flag_catch_failure( > > - f"eth/{l3}()/GRE()/{l3}()/" > > - f"{l4}({inner_arg})/('X'*50)", > > + > > + f"eth/{l3}()/GRE()/{l3}()/{l4}({inner_arg})/('X'*50)", > > "PKT_RX_L4_CKSUM_", f"{l3}/GRE/{l3}/{l4}", > > should_pass=should_pass) > > > > @@ -615,6 +827,8 @@ class TestChecksumOffload(TestCase): > > # updated this test case can easily take advantage of the new > functionality. > > > > # # GENEVE > > + # # This import is over here so that it is not forgotten when > the update > > happens > > + # from scapy.contrib.geneve import GENEVE > > # for l3_outer in l3_protos: > > # for l4_outer in l4_protos: > > # for l3_inner in l3_protos: > > @@ -638,7 +852,46 @@ class TestChecksumOffload(TestCase): > > self.logger.error(str(err)) > > self.verify(len(verification_errors) == 0, "See previous > output") > > > > + self.tester.send_expect("quit", "#") > > + self.dut.send_expect("stop", "testpmd>") > > + > > + def test_hardware_checksum_check_l4_tx(self): > > + self.checksum_enablehw(self.dut_ports[0]) > > + self.dut.send_expect("start", "testpmd>") > > + > > + verification_errors: List[VerifyFailure] = [] > > + > > + iface = > > self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) > > + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) > > + tester_mac = > > + self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) > > + > > + packets = self.get_packets(dut_mac, tester_mac) > > + > > + capture_file_name = > > "test_hardware_checksum_check_l4_tx_capture.pcap" > > + > > + packet_file_path = > > "/tmp/test_hardware_checksum_check_l4_tx_packets.pcap" > > + capture_file_path = "/tmp/tester/" + capture_file_name > > + > > + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w > > + {capture_file_path} &", "# ") > > + > > + wrpcap(packet_file_path, packets) > > + self.tester.session.copy_file_to(packet_file_path, > > + packet_file_path) > > + > > + self.replay_pcap_file_on_tester(iface, packet_file_path) > > + > > + self.tester.session.copy_file_from(packet_file_path, > > + "output/tmp/pcap/" + capture_file_name) > > + > > + captured_packets = rdpcap("output/tmp/pcap/" + > > + capture_file_name) > > + > > + self.verify(len(packets) == len(captured_packets), "Not all > > + packets were received") > > + > > + error_messages = > > + self.validate_packet_list_checksums(captured_packets) > > + > > self.dut.send_expect("stop", "testpmd>") > > + if len(error_messages) != 0: > > + for error_msg in error_messages: > > + self.logger.error(error_msg) > > + self.verify(False, "See prior output") > > > > def tear_down(self): > > """ > > -- > > 2.25.1 > >
diff --git a/test_plans/checksum_offload_test_plan.rst b/test_plans/checksum_offload_test_plan.rst index 4fd8c5a..de2e1a9 100644 --- a/test_plans/checksum_offload_test_plan.rst +++ b/test_plans/checksum_offload_test_plan.rst @@ -221,7 +221,7 @@ combination: good/bad ip checksum + good/bad udp/tcp checksum. Check the Rx checksum flags consistent with expected flags. -Test Case: Hardware Checksum Check L4 +Test Case: Hardware Checksum Check L4 RX =========================================== This test involves testing many different scenarios with a L4 checksum. A variety of tunneling protocols, L3 protocols and L4 protocols are combined @@ -256,4 +256,122 @@ Send a packet with a bad checksum:: rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 -Verify flags are as expected. \ No newline at end of file +Verify flags are as expected. + +Test Case: Hardware Checksum Check L3 RX +=========================================== +This test involves testing L3 checksum hardware offload. +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and IPv6's +lack of a checksum, only IPv4's checksum is tested. + +Setup the ``csum`` forwarding mode:: + + testpmd> set fwd csum + Set csum packet forwarding mode + +Start the packet forwarding:: + + testpmd> start + csum packet forwarding - CRC stripping disabled - packets/burst=32 + nb forwarding cores=1 - nb forwarding ports=10 + RX queues=1 - RX desc=128 - RX free threshold=64 + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 + TX queues=1 - TX desc=512 - TX free threshold=0 + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 + +Send a packet with a good checksum:: + + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Send a packet with a bad checksum:: + + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Verify flags are as expected. + +Test Case: Hardware Checksum Check L4 TX +=========================================== +This test involves testing many different scenarios with a L4 checksum. +A variety of tunneling protocols, L3 protocols and L4 protocols are combined +to test as many scenarios as possible. Currently, UDP, TCP and SCTP are used +as L4 protocols, with IP and IPv6 being used at level 3. The tested tunneling +protocols are VXLAN and GRE. This test is used to determine whether the +hardware offloading of checksums works properly. + +Setup the ``csum`` forwarding mode:: + + testpmd> set fwd csum + Set csum packet forwarding mode + +Start the packet forwarding:: + + testpmd> start + csum packet forwarding - CRC stripping disabled - packets/burst=32 + nb forwarding cores=1 - nb forwarding ports=10 + RX queues=1 - RX desc=128 - RX free threshold=64 + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 + TX queues=1 - TX desc=512 - TX free threshold=0 + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 + + +Start a packet capture on the tester in the backround:: + + # tcpdump -i <iface> -s 65535 -w /tmp/tester/test_hardware_checksum_check_l4_tx_capture.pcap & + +Send a packet with a good checksum:: + + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Send a packet with a bad checksum:: + + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_BAD PKT_RX_IP_CKSUM_GOOD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Inspect the pcap file from the packet capture and verify the checksums. + +Test Case: Hardware Checksum Check L3 TX +=========================================== +This test involves testing L3 checksum hardware offload. +Due to the relative dominance of IPv4 and IPv6 as L3 protocols, and IPv6's +lack of a checksum, only IPv4's checksum is tested. + +Setup the ``csum`` forwarding mode:: + + testpmd> set fwd csum + Set csum packet forwarding mode + +Start the packet forwarding:: + + testpmd> start + csum packet forwarding - CRC stripping disabled - packets/burst=32 + nb forwarding cores=1 - nb forwarding ports=10 + RX queues=1 - RX desc=128 - RX free threshold=64 + RX threshold registers: pthresh=8 hthresh=8 wthresh=4 + TX queues=1 - TX desc=512 - TX free threshold=0 + TX threshold registers: pthresh=32 hthresh=8 wthresh=8 + + +Start a packet capture on the tester in the backround:: + + # tcpdump -i <iface> -s 65535 -w /tmp/tester/test_hardware_checksum_check_l3_tx_capture.pcap & + +Send a packet with a good checksum with a 1 in it's payload:: + + port=0, mbuf=0x2269df8780, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_GOOD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Send a packet with a bad checksum with a 0 in it's payload:: + + port=0, mbuf=0x2269df7e40, pkt_len=96, nb_segs=1: + rx: l2_len=18 ethertype=800 l3_len=20 l4_proto=17 l4_len=8 flags=PKT_RX_L4_CKSUM_GOOD PKT_RX_IP_CKSUM_BAD PKT_RX_OUTER_L4_CKSUM_UNKNOWN + tx: flags=PKT_TX_L4_NO_CKSUM PKT_TX_IPV4 + +Inspect the pcap file from the packet capture and verify the checksums. diff --git a/tests/TestSuite_checksum_offload.py b/tests/TestSuite_checksum_offload.py index 9ff5a41..c4a877d 100644 --- a/tests/TestSuite_checksum_offload.py +++ b/tests/TestSuite_checksum_offload.py @@ -47,8 +47,18 @@ import time from rst import RstReport import utils - +from exception import VerifyFailure +from pktgen import PacketGeneratorHelper +from scapy.layers.inet import UDP, TCP, IP +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, GRE +from scapy.layers.sctp import SCTP +from scapy.layers.vxlan import VXLAN +from scapy.packet import Raw +from scapy.utils import wrpcap, rdpcap +from test_capabilities import DRIVER_TEST_LACK_CAPA from test_case import TestCase + from framework.pmd_output import PmdOutput from test_capabilities import DRIVER_TEST_LACK_CAPA from pktgen import PacketGeneratorHelper @@ -57,6 +67,33 @@ import packet from settings import FOLDERS +l3_proto_classes = [ + IP, + IPv6 +] + +l4_proto_classes = [ + UDP, + TCP, + SCTP +] + +tunnelling_proto_classes = [ + VXLAN, + GRE, +] + +l3_protos = [ + "IP", + "IPv6" +] + +l4_protos = [ + "UDP", + "TCP", + "SCTP", +] + class TestChecksumOffload(TestCase): @@ -79,8 +116,6 @@ class TestChecksumOffload(TestCase): cur_path = os.path.dirname( os.path.dirname(os.path.realpath(__file__))) self.output_path = os.sep.join([cur_path, self.logger.log_path]) - # create an instance to set stream field setting - self.pktgen_helper = PacketGeneratorHelper() def set_up(self): """ @@ -243,7 +278,6 @@ class TestChecksumOffload(TestCase): def send_pkt_expect_good_bad_from_flag(self, pkt_str: str, flag: str, test_name: str, should_pass: bool = True): self.pmdout.get_output(timeout=5) # Remove any old output self.scapy_exec(f"sendp({pkt_str}, iface=iface)") - time.sleep(1) testpmd_output: str = self.pmdout.get_output(timeout=5) self.verify(flag in testpmd_output, f"Flag {flag[:-1]} not found for test {test_name}, please run test_rx_checksum_valid_flags.") @@ -269,11 +303,147 @@ class TestChecksumOffload(TestCase): return None - def scapy_exec(self, cmd: str): - return self.tester.send_expect(cmd, ">>>") + def validate_checksum(self, pkt, layer) -> bool: + """ + @param pkt: The packet to validate the checksum of. + @return: Whether the checksum was valid. + """ + if pkt is None: + return False + + csum = pkt[layer].chksum + del pkt[layer].chksum + # Converting it to raw will calculate the checksum + return layer(Raw(pkt[layer])).chksum == csum - def scapy_send_append(self, packet: str): - return self.tester.scapy_append(f'sendp({packet}, iface=iface)') + def scapy_exec(self, cmd: str, timeout=1) -> str: + return self.tester.send_expect(cmd, ">>>", timeout=timeout) + + def get_packets(self, dut_mac, tester_mac): + eth = Ether(dst=dut_mac, src=tester_mac) + packets = [] + checksum_options = ({}, {'chksum': 0xf},) + # Untunneled + for l3 in l3_proto_classes: + for l4 in l4_proto_classes: + for chksum in checksum_options: + # The packet's data can be used to identify how the packet was constructed so avoid any issues with + # ordering + pkt = eth / l3() / l4(**chksum) / ( + f'UNTUNNELED,{l3.__name__},{l4.__name__},{" " if len(chksum.values()) == 0 else chksum["chksum"]}' + ) + + # Prevents the default behavior which adds DNS headers + if l4 == UDP: + pkt[UDP].dport, pkt[UDP].sport = 1001, 1001 + + packets.append(pkt) + + # Tunneled + # VXLAN + for l3 in l3_proto_classes: + for l4 in l4_proto_classes: + for outer_arg in checksum_options: + for inner_arg in checksum_options: + pkt = eth / l3() / UDP(**outer_arg) / VXLAN() / Ether() / l3() / l4(**inner_arg) / ( + f'VXLAN,{l3.__name__},{l4.__name__},' + f'{" " if len(outer_arg.values()) == 0 else outer_arg["chksum"]},' + f'{" " if len(inner_arg.values()) == 0 else inner_arg["chksum"]}' + ) + # Prevents the default behavior which adds DNS headers + if l4 == UDP: + pkt[VXLAN][UDP].dport, pkt[VXLAN][UDP].sport = 1001, 1001 + + packets.append(pkt) + # GRE + for l3 in l3_proto_classes: + for l4 in l4_proto_classes: + for chksum in checksum_options: + pkt = eth / l3() / GRE() / l3() / l4(**chksum) / ( + f'GRE,{l3.__name__},{l4.__name__},{" " if len(chksum.values()) == 0 else chksum["chksum"]}' + ) + + # Prevents the default behavior which adds DNS headers + if l4 == UDP: + pkt[GRE][UDP].dport, pkt[GRE][UDP].sport = 1001, 1001 + + packets.append(pkt) + + return packets + + def replay_pcap_file_on_tester(self, iface, packet_file_path): + self.tester.send_expect("scapy", ">>>") + self.scapy_exec(f"packets = rdpcap('{packet_file_path}')") + self.scapy_exec(f"sendp(packets, iface={iface})") + self.tester.send_expect("quit()", "# ") + + def validate_packet_list_checksums(self, packets): + name_to_class_dict = { + 'UDP': UDP, + 'TCP': TCP, + 'SCTP': SCTP, + 'IP': IP, + 'IPv6': IPv6, + 'VXLAN': VXLAN, + 'GRE': GRE, + } + + error_messages = [] + + untunnelled_error_message = f"Invalid untunneled checksum state for %s/%s with a %s checksum." + + vxlan_error_message = f"Invalid VXLAN tunnelled %s checksum state for %s/%s" \ + f" with a %s inner checksum and a %s outer checksum." + + gre_error_message = f"Invalid GRE tunnelled checksum state for %s/%s with a %s checksum." + + for packet in packets: + payload: str + # try: + payload = packet[Raw].load.decode('utf-8').split(",") + # # This error usually happens with tunneling protocols, and means that an additional cast is needed + # except UnicodeDecodeError: + # for proto in tunnelling_proto_classes: + + l3 = name_to_class_dict[payload[1]] + l4 = name_to_class_dict[payload[2]] + if payload[0] == "UNTUNNELED": + chksum_should_be_valid = payload[3] == " " + if self.validate_checksum(packet, l4) != chksum_should_be_valid: + error_messages.append( + untunnelled_error_message % ( + l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid == '' else 'invalid' + ) + ) + elif payload[0] == "VXLAN": + outer_chksum_should_be_valid = payload[3] == " " + inner_chksum_should_be_valid = payload[4] == " " + if self.validate_checksum(packet[VXLAN], l4) != inner_chksum_should_be_valid: + error_messages.append( + vxlan_error_message % ( + "inner", l4.__name__, l3.__name__, + 'valid' if inner_chksum_should_be_valid == '' else 'invalid', + 'valid' if outer_chksum_should_be_valid == '' else 'invalid' + ) + ) + if self.validate_checksum(packet, l4) != outer_chksum_should_be_valid: + error_messages.append( + vxlan_error_message % ( + "outer", l3.__name__, l4.__name__, + 'valid' if inner_chksum_should_be_valid == '' else 'invalid', + 'valid' if outer_chksum_should_be_valid == '' else 'invalid' + ) + ) + elif payload[0] == "GRE": + chksum_should_be_valid = payload[3] == " " + if self.validate_checksum(packet, l4) != chksum_should_be_valid: + error_messages.append( + gre_error_message % ( + l3.__name__, l4.__name__, 'valid' if chksum_should_be_valid == '' else 'invalid' + ) + ) + + return error_messages # # @@ -448,6 +618,9 @@ class TestChecksumOffload(TestCase): # clear streams before add new streams self.tester.pktgen.clear_streams() + # create an instance to set stream field setting + # Moved here because it messes with the ability of the functional tests to use scapy. + self.pktgen_helper = PacketGeneratorHelper() # run packet generator streams = self.pktgen_helper.prepare_stream_from_tginput(tgenInput, 100, None, self.tester.pktgen) @@ -511,53 +684,93 @@ class TestChecksumOffload(TestCase): self.dut.send_expect("quit", "#", 10) self.result_table_print() - def test_hardware_checksum_check_ip(self): + def test_hardware_checksum_check_ip_rx(self): self.dut.send_expect("start", "testpmd>") + self.tester.send_expect("scapy", ">>>") self.checksum_enablehw(self.dut_ports[0]) + self.dut.send_expect("start", "testpmd>") verification_errors: List[VerifyFailure] = [] - # untunnelled - vf = self.try_helper_hardware_checksum_check_catch_failure('Ether(dst="%s", src="%s")/IP(%s)/UDP()/("X"*50)', - "PKT_RX_IP_CKSUM_") - if vf is not None: - verification_errors.append(vf) + iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) + tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) - # tunneled inner - vf = self.try_helper_hardware_checksum_check_catch_failure( - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', - "PKT_RX_OUTER_IP_CKSUM_") - if vf is not None: - verification_errors.append(vf) + self.scapy_exec(f"eth = Ether(dst='{dut_mac}', src='{tester_mac}')") + self.scapy_exec(f"iface = '{iface}'") - # tunneled outer - vf = self.try_helper_hardware_checksum_check_catch_failure( - 'Ether(dst="%s", src="%s")/IP()/UDP()/IP(%s)/("X"*50)', - "PKT_RX_OUTER_IP_CKSUM_") - if vf is not None: - verification_errors.append(vf) + # Untunnelled + for l4 in l4_protos: + for chksum in "", "chksum=0xf": + vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( + f"eth/IP({chksum})/{l4}()/(X'*50)", + "PKT_RX_IP_CKSUM_", f"{l4}", + should_pass=(chksum == "")) + if vf is not None: + verification_errors.append(vf) - self.verify(len(verification_errors) == 0, "\n".join(verification_errors)) + for err in verification_errors: + self.logger.error(str(err)) + self.verify(len(verification_errors) == 0, "See previous output") + self.tester.send_expect("quit()", "# ") self.dut.send_expect("stop", "testpmd>") - def test_hardware_checksum_check_l4(self): + def test_hardware_checksum_check_ip_tx(self): self.checksum_enablehw(self.dut_ports[0]) self.dut.send_expect("start", "testpmd>") verification_errors: List[VerifyFailure] = [] - l3_protos: List[str] = [ - "IP", - "IPv6" - ] + iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) + tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) + eth = Ether(dst=dut_mac, src=tester_mac) - l4_protos: List[str] = [ - "UDP", - "TCP", - "SCTP", + checksum_options = ({}, {'chksum': 0xf},) + + packets = [ + eth / IP(**chksum) / TCP() / Raw(load=str(int(len(chksum) != 1))) for chksum in checksum_options ] + capture_file_name = "test_hardware_checksum_check_l3_tx_capture.pcap" + + packet_file_path = "/tmp/test_hardware_checksum_check_l3_tx_packets.pcap" + capture_file_path = "/tmp/tester/" + capture_file_name + + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w {capture_file_path} &", "# ") + + wrpcap(packet_file_path, packets) + self.tester.session.copy_file_to(packet_file_path, packet_file_path) + + self.replay_pcap_file_on_tester(iface, packet_file_path) + + self.tester.session.copy_file_from(packet_file_path, "output/tmp/pcap/" + capture_file_name) + + captured_packets = rdpcap("output/tmp/pcap/" + capture_file_name) + + self.verify(len(packets) == len(captured_packets), "Not all packets were received") + + error_messages = [] + for pkt in captured_packets: + should_pass = pkt[TCP].payload.build() == b'1' + if not (self.validate_checksum(pkt, IP) == should_pass): + error_messages.append(f"A packet was marked as having a" + f"{' valid' if should_pass == '' else 'n invalid'}" + f" checksum when it should have had the opposite.") + + self.dut.send_expect("stop", "testpmd>") + if len(error_messages) != 0: + for error_msg in error_messages: + self.logger.error(error_msg) + self.verify(False, "See prior output") + + def test_hardware_checksum_check_l4_rx(self): + self.checksum_enablehw(self.dut_ports[0]) + self.dut.send_expect("start", "testpmd>") + + verification_errors: List[VerifyFailure] = [] + iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) dut_mac = self.dut.get_mac_address(self.dut_ports[0]) tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) @@ -589,7 +802,7 @@ class TestChecksumOffload(TestCase): should_pass = outer_arg == "" vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( f"eth/{l3}()/{l4}({outer_arg})/VXLAN()/{l3}()/" - f"{l4}({inner_arg})/('X'*50)", + f"{l4}(chksum={inner_arg})/('X'*50)", flag, f"{l3}/{l4}/VXLAN/{l3}/{l4}", should_pass=should_pass) @@ -602,8 +815,7 @@ class TestChecksumOffload(TestCase): for inner_arg in "", "chksum=0xf": should_pass: bool = inner_arg == "" vf = self.send_pkt_expect_good_bad_from_flag_catch_failure( - f"eth/{l3}()/GRE()/{l3}()/" - f"{l4}({inner_arg})/('X'*50)", + f"eth/{l3}()/GRE()/{l3}()/{l4}({inner_arg})/('X'*50)", "PKT_RX_L4_CKSUM_", f"{l3}/GRE/{l3}/{l4}", should_pass=should_pass) @@ -615,6 +827,8 @@ class TestChecksumOffload(TestCase): # updated this test case can easily take advantage of the new functionality. # # GENEVE + # # This import is over here so that it is not forgotten when the update happens + # from scapy.contrib.geneve import GENEVE # for l3_outer in l3_protos: # for l4_outer in l4_protos: # for l3_inner in l3_protos: @@ -638,7 +852,46 @@ class TestChecksumOffload(TestCase): self.logger.error(str(err)) self.verify(len(verification_errors) == 0, "See previous output") + self.tester.send_expect("quit", "#") + self.dut.send_expect("stop", "testpmd>") + + def test_hardware_checksum_check_l4_tx(self): + self.checksum_enablehw(self.dut_ports[0]) + self.dut.send_expect("start", "testpmd>") + + verification_errors: List[VerifyFailure] = [] + + iface = self.tester.get_interface(self.tester.get_local_port(self.dut_ports[0])) + dut_mac = self.dut.get_mac_address(self.dut_ports[0]) + tester_mac = self.tester.get_mac(self.tester.get_local_port(self.dut_ports[0])) + + packets = self.get_packets(dut_mac, tester_mac) + + capture_file_name = "test_hardware_checksum_check_l4_tx_capture.pcap" + + packet_file_path = "/tmp/test_hardware_checksum_check_l4_tx_packets.pcap" + capture_file_path = "/tmp/tester/" + capture_file_name + + self.tester.send_expect(f"tcpdump -i {iface} -s 65535 -w {capture_file_path} &", "# ") + + wrpcap(packet_file_path, packets) + self.tester.session.copy_file_to(packet_file_path, packet_file_path) + + self.replay_pcap_file_on_tester(iface, packet_file_path) + + self.tester.session.copy_file_from(packet_file_path, "output/tmp/pcap/" + capture_file_name) + + captured_packets = rdpcap("output/tmp/pcap/" + capture_file_name) + + self.verify(len(packets) == len(captured_packets), "Not all packets were received") + + error_messages = self.validate_packet_list_checksums(captured_packets) + self.dut.send_expect("stop", "testpmd>") + if len(error_messages) != 0: + for error_msg in error_messages: + self.logger.error(error_msg) + self.verify(False, "See prior output") def tear_down(self): """
add rx and tx l3 test cases add tx l4 test case add documentation for new test cases Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu> --- test_plans/checksum_offload_test_plan.rst | 122 +++++++- tests/TestSuite_checksum_offload.py | 331 +++++++++++++++++++--- 2 files changed, 412 insertions(+), 41 deletions(-)