new file mode 100644
@@ -0,0 +1,281 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 Intel Corporation
+#
+import re
+import time
+
+from framework.packet import Packet
+from framework.pmd_output import PmdOutput
+from framework.test_case import TestCase
+
+match_pkts = {
+ "rule_1_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:20:24")/IP(src="10.2.29.100", dst="10.2.29.2")/UDP()',
+ "rule_2_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:06:00:00:02:18")/IP(src="133.133.10.20", dst="133.133.10.10")/UDP()',
+ "rule_3_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:05:34")/IP(src="10.2.28.10", dst="10.2.28.20")/UDP()',
+ "rule_4_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:10:14")/IP(src="10.2.28.10", dst="10.2.28.20")/TCP()',
+ "rule_5_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:05:00:00:03:17")/IP(src="133.133.10.60", dst="133.133.10.20")/UDP()',
+ "rule_6_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:08:84")/IP(src="10.2.28.19", dst="10.2.28.29")/UDP()',
+ "rule_7_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:05:00:00:03:18")/IP(src="133.133.10.60", dst="133.133.10.10")/TCP()',
+ "rule_8_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="dc:ad:be:ef:04:29")/IP(src="10.2.28.39", dst="10.2.28.49")/UDP()',
+ "rule_9_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:10:14")/IP(src="10.2.28.10", dst="10.2.28.20")/TCP()',
+ "rule_10_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:08:00:00:03:19")/IP(src="133.133.10.60", dst="133.133.10.30")/TCP()',
+ "rule_11_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:06:00:00:02:18")/IP(src="10.2.29.100", dst="10.2.29.2")/TCP()',
+ "rule_12_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:07:00:00:05:19")/IP(src="133.133.10.20", dst="133.133.10.10")/TCP()',
+ "rule_13_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="00:05:00:00:03:17")/IP(src="133.133.10.60", dst="133.133.10.20")/TCP()',
+ "rule_14_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="dc:ad:be:ef:04:29")/IP(src="10.2.28.39", dst="10.2.28.49")/TCP()',
+ "rule_15_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="de:ad:be:ef:08:84")/IP(src="10.2.28.19", dst="10.2.28.29")/TCP()',
+ "rule_16_match_pkt": 'Ether(src="00:11:22:33:44:55", dst="da:ad:be:ef:06:19")/IP(src="10.2.28.29", dst="10.2.28.39")/TCP()',
+}
+
+hairpin_rules = {
+ "rule_1": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x20,0x24,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1d,0x64,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1d,0x2,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_2": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,00,0x06,0x00,0x00,0x02,0x18,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x14,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0xa,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_3": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x05,0x34,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0xa,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x14,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_4": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x10,0x14,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0xa,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x14,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_5": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,00,0x05,0x00,0x00,0x03,0x17,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x3c,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0x14,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_6": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x08,0x84,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0x13,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x1d,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_7": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,00,0x05,0x00,0x00,0x03,0x18,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x3c,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0xa,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_8": "opcode=0x1303 prof_id=0x34 sub_prof_id=0x0 cookie=0xa2b87 key=0x18,0x0,00,00,00,00,0xdc,0xad,0xbe,0xef,0x04,0x29,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0x27,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x31,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_9": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x10,0x14,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0xa,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x14,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_10": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,00,0x08,0x00,0x00,0x03,0x19,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x3c,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0x1e,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_11": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,00,0x06,0x00,0x00,0x02,0x18,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1d,0x64,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1d,0x2,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_12": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,00,0x07,0x00,0x00,0x05,0x19,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x14,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0xa,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_13": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,00,0x05,0x00,0x00,0x03,0x17,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0x85,0x85,0xa,0x3c,00,00,00,00,00,00,00,00,00,00,00,00,\
+0x85,0x85,0xa,0x14,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_14": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,0xdc,0xad,0xbe,0xef,0x04,0x29,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0x27,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x31,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_15": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,0xde,0xad,0xbe,0xef,0x08,0x84,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0x13,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x1d,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+ "rule_16": "opcode=0x1303 prof_id=0x32 sub_prof_id=0x0 cookie=0xa2b87 key=0x1a,0x0,00,00,00,00,0xda,0xad,0xbe,0xef,0x06,0x19,0x0,0x0,0x0,0x0,00,00,00,00,00,00,0xa,0x2,0x1c,0x1d,00,00,00,00,00,00,00,00,00,00,00,00,\
+0xa,0x2,0x1c,0x27,00,00,00,00,00,00,00,00,00,00,00,00 act=set_vsi{act_val=0 val_type=2 dst_pe=0 slot=0x0}",
+}
+
+mismatch_pkts = {
+ "mac_ipv4": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IP(src="192.168.0.2",dst="192.168.0.3")/("X"*480)',
+ "mac_ipv6": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IPv6(src="2001::2",dst="2001::3")/("X"*480)',
+ "mac_ipv4_udp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IP(src="10.2.28.12", dst="10.2.28.22")/UDP(sport=1026, dport=1027)/("X"*480)',
+ "mac_ipv4_tcp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IP(src="10.2.28.12", dst="10.2.28.22")/TCP(sport=1026, dport=1027)/("X"*480)',
+ "mac_ipv4_sctp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IP(src="10.2.28.12", dst="10.2.28.22")/SCTP(sport=1026, dport=1027)/("X"*480)',
+ "mac_ipv6_udp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IPv6(src="2001::2",dst="2001::3")/UDP(sport=1026, dport=1027)/("X"*480)',
+ "mac_ipv6_tcp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IPv6(src="2001::2",dst="2001::3")/TCP(sport=1026, dport=1027)/("X"*480)',
+ "mac_ipv6_sctp": 'Ether(src="00:11:22:33:44:55", dst="02:70:80:C6:67:CE")/IPv6(src="2001::2",dst="2001::3")/SCTP(sport=1026, dport=1027)/("X"*480)',
+}
+
+
+class TestHairpinqueue(TestCase):
+ def set_up_all(self):
+ """
+ Run at the start of each test suite.
+ Generic filter Prerequistites
+ """
+ self.verify(
+ self.nic in ["mev-cpfl"],
+ "%s nic not support hairpin queue" % self.nic,
+ )
+ self.dut_ports = self.dut.get_ports(self.nic)
+ self.ports_socket = self.dut.get_numa_id(self.dut_ports[0])
+ # Verify that enough ports are available
+ self.verify(len(self.dut_ports) >= 1, "Insufficient ports")
+ self.tester_port0 = self.tester.get_local_port(self.dut_ports[0])
+ self.tester_iface0 = self.tester.get_interface(self.tester_port0)
+ self.pkt = Packet()
+ self.pmdout = PmdOutput(self.dut)
+ self.pf_pci = self.dut.ports_info[self.dut_ports[0]]["pci"]
+ self.imc_rule_dir = "/home/rule/"
+
+ def set_up(self):
+ """
+ Run before each test case.
+ """
+ pass
+
+ def launch_testpmd(self, rxq, txq, hairpinq):
+ """
+ start testpmd
+ """
+ # Prepare testpmd EAL and parameters
+ out = self.pmdout.start_testpmd(
+ "Default",
+ param="--rxq=%d --txq=%d --hairpinq=%d --hairpin-mode=0"
+ % (rxq, txq, hairpinq),
+ )
+ qnum = re.findall("hairpin: vport 0, Rxq id (\w+)", out)
+ print(qnum)
+ return qnum
+
+ def start_testpmd(self):
+ self.dut.send_expect("set verbose 1", "testpmd>")
+ self.dut.send_expect("start", "testpmd>", 10)
+ # test link status
+ res = self.pmdout.wait_link_status_up("all", timeout=15)
+ self.verify(res is True, "There have port link is down")
+
+ def create_imc_session(self):
+ self.imc_test_session = self.dut.new_session(suite="imc")
+ self.imc_test_session.send_expect("export TERM=linux-c-nc", "#")
+ out = self.imc_test_session.send_command(
+ "sudo minicom -D /dev/ttyUSB2 -b 460800 -w"
+ )
+ if "Device /dev/ttyUSB2 is locked" in out:
+ self.imc_test_session.send_command(
+ "ps -ef | grep 'minicom -D /dev/ttyUSB2' | grep -v grep | awk '{print $2}' | xargs kill -9"
+ )
+ self.imc_test_session.send_command(
+ "sudo minicom -D /dev/ttyUSB2 -b 460800 -w"
+ )
+ if "mev-imc login:" in self.imc_test_session.send_command("\n"):
+ self.imc_test_session.send_expect("root", "mev-imc")
+ else:
+ self.imc_test_session.send_expect("\n", "mev-imc")
+ self.logger.info("create imc test session successfully")
+ return self.imc_test_session
+
+ def create_imc_rule(self, count, rules):
+ self.create_imc_session()
+ self.imc_test_session.send_expect(
+ "mkdir -p %s" % (self.imc_rule_dir), "mev-imc"
+ )
+ self.imc_test_session.send_expect("cd %s" % (self.imc_rule_dir), "mev-imc")
+ self.imc_test_session.send_expect("rm *.txt", "mev-imc")
+ for i in range(count):
+ rule = rules["rule_%d" % (i + 1)]
+ print(rule)
+ time.sleep(2)
+ self.imc_test_session.send_command("cat > rule_%d.txt" % (i + 1))
+ self.imc_test_session.send_command("%s" % rule)
+ self.imc_test_session.send_command("^d")
+ print(self.imc_test_session.send_expect("ls", "#"))
+ output = self.imc_test_session.send_command(
+ "cli_client -x -f rule_%d.txt" % (i + 1), 10
+ )
+ print(output)
+ time.sleep(2)
+ self.verify("WARNING" not in output, "Found bad key in rule")
+ self.verify("File successfully processed" in output, "Create rule failed")
+
+ def delete_imc_rule(self, count):
+ self.imc_test_session.send_expect("cd %s" % (self.imc_rule_dir), "mev-imc")
+ print(self.imc_test_session.send_expect("ls", "#"))
+ for i in range(count):
+ self.imc_test_session.send_command(
+ "sed -i 's/opcode=0x1303/opcode=0x1305/g' rule_%d.txt" % (i + 1), 2
+ )
+ output = self.imc_test_session.send_command(
+ "cli_client -x -f rule_%d.txt" % (i + 1), 5
+ )
+ self.verify("WARNING" not in output, "Found bad key in rule")
+ self.verify("File successfully processed" in output, "Create rule failed")
+
+ def send_packets(self, packets, tx_port, count=1):
+ self.pkt.update_pkt(packets)
+ self.pkt.send_pkt(crb=self.tester, tx_port=tx_port, count=count)
+
+ def check_tcpdump_pkts(self, pkts, count):
+ filters = [{"layer": "ether", "config": {"src": "00:11:22:33:44:55"}}]
+ inst = self.tester.tcpdump_sniff_packets(self.tester_iface0, filters=filters)
+ self.dut.send_expect("clear port stats all", "testpmd>", 10)
+ for i in range(len(pkts)):
+ pkt = pkts[i]
+ self.pkt.update_pkt(pkt)
+ self.pkt.send_pkt(crb=self.tester, tx_port=self.tester_iface0, count=1)
+ self.dut.send_expect("show port stats all", "testpmd>", 10)
+ time.sleep(2)
+ p = self.tester.load_tcpdump_sniff_packets(inst)
+ print(len(p))
+ self.verify(
+ len(p) == count,
+ "Send %d packets but received %d packets, not match" % (count, len(p)),
+ )
+
+ def validate_hairpin_queue(self, count, rules, match_pkts):
+ mismatch_pkt = list(mismatch_pkts.values())
+ self.create_imc_rule(count, rules)
+ self.check_tcpdump_pkts(match_pkts, count)
+ self.start_testpmd()
+ self.check_tcpdump_pkts(mismatch_pkt, count=8)
+ self.delete_imc_rule(count)
+
+ def test_single_hairpin_queue(self):
+ """
+ Test Case 1: Launch 1 hairpin queue
+ """
+ self.logger.info(
+ "===================Test subcase 1: single data queue and single hairpin queue ================"
+ )
+ qnum = self.launch_testpmd(rxq=1, txq=1, hairpinq=1)
+ rules = {}
+ match_pkt = [match_pkts["rule_4_match_pkt"]]
+ rules["rule_1"] = (
+ hairpin_rules["rule_4"]
+ + " act=set_q{qnum=%s no_implicit_vsi=1 prec=5}" % qnum[0]
+ )
+ self.validate_hairpin_queue(count=1, rules=rules, match_pkts=match_pkt)
+ self.dut.send_expect("stop", "testpmd> ", 10)
+ self.dut.send_expect("quit", "# ", 10)
+
+ self.logger.info(
+ "===================Test subcase 2: multi data queues and single hairpin queue ================"
+ )
+ qnum = self.launch_testpmd(rxq=16, txq=16, hairpinq=1)
+ match_pkt = [match_pkts["rule_1_match_pkt"]]
+ rules["rule_1"] = (
+ hairpin_rules["rule_1"]
+ + " act=set_q{qnum=%s no_implicit_vsi=1 prec=5}" % qnum[0]
+ )
+ self.validate_hairpin_queue(count=1, rules=rules, match_pkts=match_pkt)
+
+ def test_multi_hairpin_queues(self):
+ """
+ Test Case 2: Launch 16 hairpin queues
+ """
+ self.logger.info(
+ "===================Test subcase 1: single data queue and multi hairpin queues ================"
+ )
+ qnum = self.launch_testpmd(rxq=1, txq=1, hairpinq=16)
+ match_pkt = list(match_pkts.values())
+ rules = {}
+ for i in range(16):
+ rules["rule_%d" % (i + 1)] = (
+ hairpin_rules["rule_%d" % (i + 1)]
+ + " act=set_q{qnum=%s no_implicit_vsi=1 prec=5}" % qnum[i]
+ )
+ self.validate_hairpin_queue(count=16, rules=rules, match_pkts=match_pkt)
+ self.dut.send_expect("stop", "testpmd> ", 10)
+ self.dut.send_expect("quit", "# ", 10)
+
+ self.logger.info(
+ "===================Test subcase 2: multi data queues and multi hairpin queues ================"
+ )
+ qnum = self.launch_testpmd(rxq=16, txq=16, hairpinq=16)
+ for i in range(16):
+ rules["rule_%d" % (i + 1)] = (
+ hairpin_rules["rule_%d" % (i + 1)]
+ + " act=set_q{qnum=%s no_implicit_vsi=1 prec=5}" % qnum[i]
+ )
+ self.validate_hairpin_queue(count=16, rules=rules, match_pkts=match_pkt)
+
+ def tear_down(self):
+ """
+ Run after each test case.
+ """
+ self.dut.send_expect("stop", "testpmd> ", 10)
+ self.dut.send_expect("quit", "# ", 10)
+ self.dut.kill_all()
+
+ def tear_down_all(self):
+ """
+ Run after each test suite.
+ """
+ self.dut.kill_all()