From 397aca73d35e9203feffb68a36a2c150c0a72751 Mon Sep 17 00:00:00 2001 From: jbao Date: Fri, 19 Sep 2025 06:03:54 +0300 Subject: [PATCH 1/2] Update qos sai case for ipv6 only topo 1. Update the method to get the test port and ip for v6 2. Update the method to generate the arp for v6 3. Update the method to get the dst port for v6 4. Update the method to send v6 packet --- tests/common/devices/sonic.py | 32 ++- tests/common/devices/sonic_asic.py | 12 +- tests/qos/qos_sai_base.py | 109 +++++-- tests/qos/test_qos_sai.py | 12 +- tests/saitests/py3/sai_qos_tests.py | 424 +++++++++++++++++++++++----- 5 files changed, 481 insertions(+), 108 deletions(-) diff --git a/tests/common/devices/sonic.py b/tests/common/devices/sonic.py index b9bed2ba05b..e1d691df92a 100644 --- a/tests/common/devices/sonic.py +++ b/tests/common/devices/sonic.py @@ -2405,7 +2405,7 @@ def is_backend_portchannel(self, port_channel, mg_facts): def is_backend_port(self, port, mg_facts): return True if "Ethernet-BP" in port else False - def active_ip_interfaces(self, ip_ifs, tbinfo, ns_arg=DEFAULT_NAMESPACE, intf_num="all"): + def active_ip_interfaces(self, ip_ifs, tbinfo, ns_arg=DEFAULT_NAMESPACE, intf_num="all", ip_type="ipv4"): """ Return a dict of active IP (Ethernet or PortChannel) interfaces, with interface and peer IPv4 address. @@ -2421,16 +2421,26 @@ def active_ip_interfaces(self, ip_ifs, tbinfo, ns_arg=DEFAULT_NAMESPACE, intf_nu if ((k.startswith("Ethernet") and config_facts_ports.get(k, {}).get("role", "") != "Dpc" and (not k.startswith("Ethernet-BP")) and not is_inband_port(k)) or (k.startswith("PortChannel") and not self.is_backend_portchannel(k, mg_facts))): - # Ping for some time to get ARP Re-learnt. - # We might have to tune it further if needed. - if (v["admin"] == "up" and v["oper_state"] == "up" and - self.ping_v4(v["peer_ipv4"], count=3, ns_arg=ns_arg)): - ip_ifaces[k] = { - "ipv4": v["ipv4"], - "peer_ipv4": v["peer_ipv4"], - "bgp_neighbor": v["bgp_neighbor"] - } - active_ip_intf_cnt += 1 + if ip_type == "ipv4": + # Ping for some time to get ARP Re-learnt. + # We might have to tune it further if needed. + if (v["admin"] == "up" and v["oper_state"] == "up" and + self.ping_v4(v["peer_ipv4"], count=3, ns_arg=ns_arg)): + ip_ifaces[k] = { + "ipv4": v["ipv4"], + "peer_ipv4": v["peer_ipv4"], + "bgp_neighbor": v["bgp_neighbor"] + } + active_ip_intf_cnt += 1 + elif ip_type == "ipv6": + if (v["admin"] == "up" and v["oper_state"] == "up" and + self.ping_v6(v["peer_ipv6"], count=3, ns_arg=ns_arg)): + ip_ifaces[k] = { + "ipv6": v["ipv6"], + "peer_ipv6": v["peer_ipv6"], + "bgp_neighbor": v["bgp_neighbor"] + } + active_ip_intf_cnt += 1 if isinstance(intf_num, int) and intf_num > 0 and active_ip_intf_cnt == intf_num: break diff --git a/tests/common/devices/sonic_asic.py b/tests/common/devices/sonic_asic.py index 9f68647f056..c5c39bdcd01 100644 --- a/tests/common/devices/sonic_asic.py +++ b/tests/common/devices/sonic_asic.py @@ -321,7 +321,7 @@ def is_backend_portchannel(self, port_channel): return False return True - def get_active_ip_interfaces(self, tbinfo, intf_num="all"): + def get_active_ip_interfaces(self, tbinfo, intf_num="all", ip_type="ipv4"): """ Return a dict of active IP (Ethernet or PortChannel) interfaces, with interface and peer IPv4 address. @@ -329,9 +329,15 @@ def get_active_ip_interfaces(self, tbinfo, intf_num="all"): Returns: Dict of Interfaces and their IPv4 address """ - ip_ifs = self.show_ip_interface()["ansible_facts"]["ip_interfaces"] + if ip_type == "ipv4": + ip_ifs = self.show_ip_interface()["ansible_facts"]["ip_interfaces"] + elif ip_type == "ipv6": + ip_ifs = self.show_ipv6_interface()["ansible_facts"]["ipv6_interfaces"] + else: + raise ValueError("Invalid IP type: {}".format(ip_type)) + return self.sonichost.active_ip_interfaces( - ip_ifs, tbinfo, self.namespace, intf_num=intf_num + ip_ifs, tbinfo, self.namespace, intf_num=intf_num, ip_type=ip_type ) def bgp_drop_rule(self, ip_version, state="present"): diff --git a/tests/qos/qos_sai_base.py b/tests/qos/qos_sai_base.py index 38fa7784862..379ca6a10a7 100644 --- a/tests/qos/qos_sai_base.py +++ b/tests/qos/qos_sai_base.py @@ -35,6 +35,8 @@ from tests.common.snappi_tests.qos_fixtures import get_pfcwd_config, reapply_pfcwd from tests.common.snappi_tests.common_helpers import \ stop_pfcwd, disable_packet_aging, enable_packet_aging +from tests.common.utilities import is_ipv6_only_topology + logger = logging.getLogger(__name__) @@ -150,8 +152,11 @@ def runPtfTest(self, ptfhost, testCase='', testParams={}, relax=False, pdb=False Raises: RunAnsibleModuleFail if ptf test fails """ - custom_options = " --disable-ipv6 --disable-vxlan --disable-geneve" \ + ip_type = testParams.get("ip_type", "ipv4") + custom_options = " --disable-vxlan --disable-geneve" \ " --disable-erspan --disable-mpls --disable-nvgre" + if ip_type != "ipv6": + custom_options += " --disable-ipv6" # Append a suffix to the logfile name if log_suffix is present in testParams log_suffix = testParams.get("log_suffix", "") logfile_suffix = "_{0}".format(log_suffix) if log_suffix else "" @@ -959,7 +964,7 @@ def configure_ip_on_ptf_intfs(self, ptfhost, get_src_dst_asic_and_duts, tbinfo): def dutConfig( self, request, duthosts, configure_ip_on_ptf_intfs, get_src_dst_asic_and_duts, lower_tor_host, tbinfo, dualtor_ports_for_duts, dut_qos_maps, # noqa: F811 - is_supported_per_dir, lossy_queue_traffic_direction + is_supported_per_dir, lossy_queue_traffic_direction, ip_type ): """ Build DUT host config pertaining to QoS SAI tests @@ -1006,6 +1011,9 @@ def dutConfig( for key, value in src_mgFacts['minigraph_ports'].items() if not key.startswith("Ethernet-BP") } + bgp_peer_ip_key = "peer_ipv6" if ip_type == "ipv6" else "peer_ipv4" + ip_version = 6 if ip_type == "ipv6" else 4 + vlan_info = {} # LAG ports in T1 TOPO need to be removed in Mellanox devices if topo in self.SUPPORTED_T0_TOPOS or (topo in self.SUPPORTED_PTF_TOPOS and isMellanoxDevice(src_dut)): @@ -1021,6 +1029,7 @@ def dutConfig( dutLagInterfaces.append(src_mgFacts["minigraph_ptf_indices"][intf]) config_facts = duthosts.config_facts(host=src_dut.hostname, source="running") + vlan_info = config_facts[src_dut.hostname]['VLAN'] port_speeds = self.__buildPortSpeeds(config_facts[src_dut.hostname]) low_speed_portIds = [] if src_dut.facts['hwsku'] in self.BREAKOUT_SKUS and 'backend' not in topo: @@ -1054,7 +1063,7 @@ def dutConfig( for portConfig in intf_map: intf = portConfig["attachto"].split(".")[0] portIndex = src_mgFacts["minigraph_ptf_indices"][intf] - if ipaddress.ip_interface(portConfig['peer_addr']).ip.version == 4: + if ipaddress.ip_interface(portConfig['peer_addr']).ip.version == ip_version: if portIndex in testPortIds[src_dut_index][src_asic_index]: portIpMap = {'peer_addr': portConfig["peer_addr"]} if 'vlan' in portConfig: @@ -1114,14 +1123,14 @@ def dutConfig( testPortIds[src_dut_index] = {} for dut_asic in get_src_dst_asic_and_duts['all_asics']: dutPortIps[src_dut_index][dut_asic.asic_index] = {} - for iface, addr in dut_asic.get_active_ip_interfaces(tbinfo).items(): + for iface, addr in dut_asic.get_active_ip_interfaces(tbinfo, ip_type=ip_type).items(): vlan_id = None if iface.startswith("Ethernet"): portName = iface if "." in iface: portName, vlan_id = iface.split(".") portIndex = src_mgFacts["minigraph_ptf_indices"][portName] - portIpMap = {'peer_addr': addr["peer_ipv4"]} + portIpMap = {'peer_addr': addr[bgp_peer_ip_key]} if vlan_id is not None: portIpMap['vlan_id'] = vlan_id dutPortIps[src_dut_index][dut_asic.asic_index].update({portIndex: portIpMap}) @@ -1130,7 +1139,7 @@ def dutConfig( iter(src_mgFacts["minigraph_portchannels"][iface]["members"]) ) portIndex = src_mgFacts["minigraph_ptf_indices"][portName] - portIpMap = {'peer_addr': addr["peer_ipv4"]} + portIpMap = {'peer_addr': addr[bgp_peer_ip_key]} dutPortIps[src_dut_index][dut_asic.asic_index].update({portIndex: portIpMap}) # If the leaf router is using separated DSCP_TO_TC_MAP on uplink/downlink ports. # we also need to test them separately @@ -1142,11 +1151,11 @@ def dutConfig( neighName = src_mgFacts["minigraph_neighbors"].get(portName, {}).get("name", "").lower() if 't0' in neighName: downlinkPortIds.append(portIndex) - downlinkPortIps.append(addr["peer_ipv4"]) + downlinkPortIps.append(addr[bgp_peer_ip_key]) downlinkPortNames.append(portName) elif 't2' in neighName: uplinkPortIds.append(portIndex) - uplinkPortIps.append(addr["peer_ipv4"]) + uplinkPortIps.append(addr[bgp_peer_ip_key]) uplinkPortNames.append(portName) testPortIds[src_dut_index][dut_asic.asic_index] = sorted( @@ -1387,7 +1396,9 @@ def dutConfig( "srcDutInstance": src_dut, "dstDutInstance": dst_dut, "dualTor": request.config.getoption("--qos_dual_tor"), - "dualTorScenario": len(dualtor_ports_for_duts) != 0 and "dualtor" not in tbinfo["topo"]["name"] + "dualTorScenario": len(dualtor_ports_for_duts) != 0 and "dualtor" not in tbinfo["topo"]["name"], + "ip_type": ip_type, + "vlan_info": vlan_info } @pytest.fixture(scope='class') @@ -1936,6 +1947,42 @@ def handleFdbAging(self, duthosts, get_src_dst_asic_and_duts): self.__loadSwssConfig(duthost) self.__deleteTmpSwitchConfig(duthost) + @pytest.fixture(scope='class', autouse=True) + def update_delay_first_probe_time_for_v6_top(self, get_src_dst_asic_and_duts, tbinfo, dutConfig): + """ + Update delay first probe time for v6 t0 topology. + Because when generating arp by sending NS packet, the default delay first probe time is 5 seconds, + which is too short to let the ip neighbor status changed from delay to probe, then to fail. + Therefore, we need to update the delay first probe time to a very large value + so that the arp entries can work during executing the qos sai case. + """ + ip_type = dutConfig.get('ip_type', 'ipv4') + if ip_type != 'ipv6' or 't0' not in tbinfo["topo"]["type"]: + yield + return + + Vlan_name = list(dutConfig['vlan_info'].keys())[0] + dut_asic = get_src_dst_asic_and_duts['src_asic'] + file_path_v6_delay_first_probe_time = f"/proc/sys/net/ipv6/neigh/{Vlan_name}/delay_first_probe_time" + cmd_get_v6_delay_first_probe_time = f"cat {file_path_v6_delay_first_probe_time}" + + # a very large value 100000 seconds which far exceeds the qos sai case execution time + # so that the status of tested ip v6 neighbor item not be changed from delay to probe, then to fail. + new_v6_delay_first_probe_time = 100000 + + original_v6_delay_first_probe_time = dut_asic.shell(cmd_get_v6_delay_first_probe_time)['stdout'] + + cmd_update_v6_delay_first_probe_time = \ + f"echo {new_v6_delay_first_probe_time} | sudo tee {file_path_v6_delay_first_probe_time}" + dut_asic.shell(cmd_update_v6_delay_first_probe_time) + + yield + + cmd_restore_v6_delay_first_probe_time = \ + f"echo {original_v6_delay_first_probe_time} | sudo tee {file_path_v6_delay_first_probe_time}" + dut_asic.shell(cmd_restore_v6_delay_first_probe_time) + return + @pytest.fixture(scope='function', autouse=True) def populateArpEntries_T2( self, duthosts, get_src_dst_asic_and_duts, ptfhost, dutTestParams, dutConfig): @@ -1983,7 +2030,8 @@ def populateArpEntries_T2( @pytest.fixture(scope='class', autouse=True) def populateArpEntries( self, duthosts, get_src_dst_asic_and_duts, lossy_queue_traffic_direction, - ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host # noqa: F811 + ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, + ip_type, update_delay_first_probe_time_for_v6_top # noqa: F811 ): """ Update ARP entries of QoS SAI test ports @@ -2012,13 +2060,19 @@ def populateArpEntries( self.populate_arp_entries( get_src_dst_asic_and_duts, ptfhost, dutTestParams, - dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host) + dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, ip_type) yield return @pytest.fixture(scope='module', autouse=True) - def dut_disable_ipv6(self, duthosts, tbinfo, lower_tor_host, swapSyncd_on_selected_duts): # noqa: F811 + def dut_disable_ipv6(self, duthosts, tbinfo, lower_tor_host, swapSyncd_on_selected_duts, ip_type): # noqa: F811 + + if ip_type == "ipv6": + logger.info("skip dut_disable_ipv6 fixture for ipv6") + yield + return + if 'dualtor' in tbinfo['topo']['name']: dut_list = [lower_tor_host] else: @@ -2677,7 +2731,7 @@ def skip_pacific_dst_asic(self, dutConfig): def populate_arp_entries( self, get_src_dst_asic_and_duts, - ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host # noqa: F811 + ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, ip_type='ipv4' # noqa: F811 ): """ Update ARP entries of QoS SAI test ports @@ -2688,16 +2742,19 @@ def populate_arp_entries( dut_asic.command('sonic-clear arp') saiQosTest = None - if dutTestParams["topo"] in self.SUPPORTED_T0_TOPOS: - saiQosTest = "sai_qos_tests.ARPpopulate" - elif dutTestParams["topo"] in self.SUPPORTED_PTF_TOPOS: - saiQosTest = "sai_qos_tests.ARPpopulatePTF" + if ip_type == "ipv6": + saiQosTest = "sai_qos_tests.ARPpopulateIPv6" else: - for dut_asic in get_src_dst_asic_and_duts['all_asics']: - result = dut_asic.command("arp -n") - pytest_assert(result["rc"] == 0, "failed to run arp command on {0}".format(dut_asic.sonichost.hostname)) - if result["stdout"].find("incomplete") == -1: - saiQosTest = "sai_qos_tests.ARPpopulate" + if dutTestParams["topo"] in self.SUPPORTED_T0_TOPOS: + saiQosTest = "sai_qos_tests.ARPpopulate" + elif dutTestParams["topo"] in self.SUPPORTED_PTF_TOPOS: + saiQosTest = "sai_qos_tests.ARPpopulatePTF" + else: + for dut_asic in get_src_dst_asic_and_duts['all_asics']: + result = dut_asic.command("arp -n") + pytest_assert(result["rc"] == 0, "failed to run arp command on {0}".format(dut_asic.sonichost.hostname)) + if result["stdout"].find("incomplete") == -1: + saiQosTest = "sai_qos_tests.ARPpopulate" if saiQosTest: testParams = dutTestParams["basicParams"] @@ -2705,7 +2762,8 @@ def populate_arp_entries( testParams.update({ "testPortIds": dutConfig["testPortIds"], "testPortIps": dutConfig["testPortIps"], - "testbed_type": dutTestParams["topo"] + "testbed_type": dutTestParams["topo"], + "ip_type": ip_type }) self.runPtfTest( ptfhost, testCase=saiQosTest, testParams=testParams @@ -3013,6 +3071,11 @@ def lossy_queue_traffic_direction(self, is_supported_per_dir): logging.info(f"Device not support per dir: {lossy_queue_dir_test}") yield lossy_queue_dir_test + @pytest.fixture(scope='module', autouse=True) + def ip_type(self, tbinfo): + ip_type = "ipv6" if is_ipv6_only_topology(tbinfo) else "ipv4" + yield ip_type + def get_src_and_dst_ports_when_support_per_dir(self, uplinkPortIds, downlinkPortIds, lossy_queue_traffic_direction): if 'src_uplink_dst_downlink' == lossy_queue_traffic_direction: src_port_ids = uplinkPortIds diff --git a/tests/qos/test_qos_sai.py b/tests/qos/test_qos_sai.py index 8bb174814e6..d0f99cd7cef 100644 --- a/tests/qos/test_qos_sai.py +++ b/tests/qos/test_qos_sai.py @@ -1308,7 +1308,8 @@ def testQosSaiLossyQueue( "src_port_vlan": dutConfig["testPorts"]["src_port_vlan"], "pkts_num_leak_out": dutQosConfig["param"][portSpeedCableLength]["pkts_num_leak_out"], "pkts_num_trig_egr_drp": qosConfig["lossy_queue_1"]["pkts_num_trig_egr_drp"], - "hwsku": dutTestParams['hwsku'] + "hwsku": dutTestParams['hwsku'], + "ip_type": dutConfig["ip_type"] }) if "platform_asic" in dutTestParams["basicParams"]: @@ -1476,7 +1477,8 @@ def testQosSaiDscpQueueMapping( "hwsku": dutTestParams['hwsku'], "dual_tor": dutConfig['dualTor'], "dual_tor_scenario": dutConfig['dualTorScenario'], - "tc_to_dscp_count_map": tc_to_dscp_count + "tc_to_dscp_count_map": tc_to_dscp_count, + 'ip_type': dutConfig["ip_type"] }) if "platform_asic" in dutTestParams["basicParams"]: @@ -1783,7 +1785,8 @@ def testQosSaiPgSharedWatermark( "pkts_num_fill_min": qosConfig[pgProfile]["pkts_num_fill_min"], "pkts_num_fill_shared": pktsNumFillShared, "cell_size": qosConfig[pgProfile]["cell_size"], - "hwsku": dutTestParams['hwsku'] + "hwsku": dutTestParams['hwsku'], + "ip_type": dutConfig["ip_type"] }) if "platform_asic" in dutTestParams["basicParams"]: @@ -1992,7 +1995,8 @@ def testQosSaiQSharedWatermark( "pkts_num_trig_drp": triggerDrop, "cell_size": qosConfig[queueProfile]["cell_size"], "hwsku": dutTestParams['hwsku'], - "dut_asic": dutConfig["dutAsic"] + "dut_asic": dutConfig["dutAsic"], + "ip_type": dutConfig["ip_type"] }) if "platform_asic" in dutTestParams["basicParams"]: diff --git a/tests/saitests/py3/sai_qos_tests.py b/tests/saitests/py3/sai_qos_tests.py index db7866df4d3..e386880b199 100755 --- a/tests/saitests/py3/sai_qos_tests.py +++ b/tests/saitests/py3/sai_qos_tests.py @@ -5,7 +5,7 @@ import time import logging import ptf.packet as scapy -from scapy.all import Ether, IP +from scapy.all import Ether, IP, IPv6 import socket import sai_base_test import operator @@ -25,7 +25,8 @@ simple_ipv4ip_packet, hex_dump_buffer, verify_packet_any_port, - port_to_tuple) + port_to_tuple, + simple_udpv6_packet) from ptf.mask import Mask from switch import (switch_init, sai_thrift_create_scheduler_profile, @@ -46,6 +47,7 @@ from switch_sai_thrift.ttypes import (sai_thrift_attribute_value_t, sai_thrift_attribute_t) from switch_sai_thrift.sai_headers import SAI_PORT_ATTR_QOS_SCHEDULER_PROFILE_ID +from scapy.layers.inet6 import ICMPv6ND_NS, ICMPv6NDOptDstLLAddr # Counters @@ -523,6 +525,42 @@ def construct_ip_pkt(pkt_len, dst_mac, src_mac, src_ip, dst_ip, dscp, src_vlan, return pkt +def construct_ipv6_pkt(pkt_len, dst_mac, src_mac, src_ip, dst_ip, dscp, src_vlan, **kwargs): + ipv6_ecn = kwargs.get('ecn', 1) + ipv6_hlim = kwargs.get('ttl', None) + exp_pkt = kwargs.get('exp_pkt', False) + + pkt_args = { + 'pktlen': pkt_len, + 'eth_dst': dst_mac, + 'eth_src': src_mac, + 'ipv6_src': src_ip, + 'ipv6_dst': dst_ip, + 'ipv6_dscp': dscp, + 'ipv6_ecn': ipv6_ecn + } + + if ipv6_hlim is not None: + pkt_args['ipv6_hlim'] = ipv6_hlim + + if src_vlan is not None: + pkt_args['dl_vlan_enable'] = True + pkt_args['vlan_vid'] = int(src_vlan) + pkt_args['vlan_pcp'] = dscp + + pkt = simple_udpv6_packet(**pkt_args) + + if exp_pkt: + masked_exp_pkt = Mask(pkt, ignore_extra_bytes=True) + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") + masked_exp_pkt.set_do_not_care_scapy(scapy.IPv6, "hlim") + if src_vlan is not None: + masked_exp_pkt.set_do_not_care_scapy(scapy.Dot1Q, "vlan") + return masked_exp_pkt + else: + return pkt + def construct_arp_pkt(eth_dst, eth_src, arp_op, src_ip, dst_ip, hw_dst, src_vlan): pkt_args = { 'eth_dst': eth_dst, @@ -542,6 +580,88 @@ def construct_arp_pkt(eth_dst, eth_src, arp_op, src_ip, dst_ip, hw_dst, src_vlan return pkt +def create_solicited_node_multicast(ipv6_binary): + """ + Create solicited-node multicast address from IPv6 binary. + + Args: + ipv6_binary (bytes): IPv6 address in binary format (16 bytes) + + Returns: + bytes: Solicited-node multicast address in binary format + """ + # Create ff02::1:ff00:0/104 + last 24 bits of target IP + multicast_bytes = bytearray(16) + multicast_bytes[0] = 0xff # ff + multicast_bytes[1] = 0x02 # 02 + multicast_bytes[11] = 0x01 # 1 + multicast_bytes[12] = 0xff # ff + # Copy last 24 bits (3 bytes) from the target IPv6 + multicast_bytes[13:16] = ipv6_binary[13:16] + + return bytes(multicast_bytes) + + +def create_multicast_mac(multicast_ipv6_binary): + """ + Create multicast MAC address from IPv6 multicast address. + + Args: + multicast_ipv6_binary (bytes): IPv6 multicast address in binary format + + Returns: + str: Multicast MAC address in format "33:33:xx:xx:xx:xx" + """ + # Create 33:33 + last 32 bits of multicast IPv6 + mac_bytes = bytearray(6) + mac_bytes[0] = 0x33 # 33 + mac_bytes[1] = 0x33 # 33 + # Copy last 32 bits (4 bytes) from multicast IPv6 + mac_bytes[2:6] = multicast_ipv6_binary[12:16] + + return ':'.join(f'{b:02x}' for b in mac_bytes) + + +def convert_to_ns_multicast(ipv6_address): + """ + Convert an IPv6 address to the corresponding multicast MAC and IP + addresses used in Neighbor Solicitation (NS) protocol. + + Args: + ipv6_address (str): The target IPv6 address (e.g., "fc02:1000::1") + Returns: + dict: Dictionary containing: + - solicited_node_multicast_ip: The solicited-node multicast IPv6 address + - solicited_node_multicast_mac: The corresponding multicast MAC address + e.g. + when ipv6 address is fc02:1000::1, + return solicited_node_multicast_ip is FF02::1:FF00:0001, solicited_node_multicast_mac is 33:33:ff:00:00:01 + """ + + # Step 1: Convert IPv6 address to binary format + ipv6_binary = socket.inet_pton(socket.AF_INET6, ipv6_address) + + # Step 2: Create solicited-node multicast address manually + # Extract last 24 bits and append to ff02::1:ff00:0/104 + multicast_addr_binary = create_solicited_node_multicast(ipv6_binary) + solicited_node_multicast_ip = socket.inet_ntop(socket.AF_INET6, multicast_addr_binary) + + # Step 3: Create corresponding multicast MAC address + # Convert multicast IPv6 to MAC (33:33:xx:xx:xx:xx) + solicited_node_multicast_mac = create_multicast_mac(multicast_addr_binary) + + return solicited_node_multicast_ip, solicited_node_multicast_mac + +def construct_ns_pkt(eth_src, src_ip, dst_ip='fc02:1000::1'): + dst_multicast_ip, dst_multicast_mac = convert_to_ns_multicast(dst_ip) + ether = Ether(src=eth_src, dst=dst_multicast_mac) + ipv6 = IPv6(src=src_ip, dst=dst_multicast_ip) + icmpv6 = ICMPv6ND_NS(tgt=dst_ip) + lladdr = ICMPv6NDOptDstLLAddr(type=1, lladdr=eth_src) + pkt = ether/ipv6/icmpv6/lladdr + return pkt + + def get_rx_port(dp, device_number, src_port_id, dst_mac, dst_ip, src_ip, src_vlan=None): ip_id = 0xBABE src_port_mac = dp.dataplane.get_mac(device_number, src_port_id) @@ -571,6 +691,34 @@ def get_rx_port(dp, device_number, src_port_id, dst_mac, dst_ip, src_ip, src_vla return result.port +def get_rx_port_ipv6(dp, device_number, src_port_id, dst_mac, dst_ip, src_ip, src_vlan=None): + src_port_mac = dp.dataplane.get_mac(device_number, src_port_id) + pkt = construct_ipv6_pkt(64, dst_mac, src_port_mac, + src_ip, dst_ip, 0, src_vlan) + # Send initial packet for any potential ARP resolution, which may cause the LAG + # destination to change. Can occur especially when running tests in isolation on a + # first test attempt. + send_packet(dp, src_port_id, pkt, 1) + # Observed experimentally this sleep needs to be at least 0.02 seconds. Setting higher. + time.sleep(1) + send_packet(dp, src_port_id, pkt, 1) + + masked_exp_pkt = construct_ipv6_pkt( + 64, dst_mac, src_port_mac, src_ip, dst_ip, 0, src_vlan, exp_pkt=True) + + pre_result = dp.dataplane.poll( + device_number=0, exp_pkt=masked_exp_pkt, timeout=3) + result = dp.dataplane.poll( + device_number=0, exp_pkt=masked_exp_pkt, timeout=3) + if pre_result.port != result.port: + logging.debug("During get_rx_port, corrected LAG destination from {} to {}".format( + pre_result.port, result.port)) + if isinstance(result, dp.dataplane.PollFailure): + dp.fail("Expected packet was not received. Received on port:{} {}".format( + result.port, result.format())) + + return result.port + def get_counter_names(sonic_version): ingress_counters = [INGRESS_DROP] @@ -898,6 +1046,40 @@ def runTest(self): time.sleep(8) +class ARPpopulateIPv6(ARPpopulate): + + def runTest(self): + # ARP Populate + # Ping only required for testports + arpreq_pkt = construct_ns_pkt( self.src_port_mac, self.src_port_ip) + + send_packet(self, self.src_port_id, arpreq_pkt) + arpreq_pkt = construct_ns_pkt( self.dst_port_mac, self.dst_port_ip) + send_packet(self, self.dst_port_id, arpreq_pkt) + arpreq_pkt = construct_ns_pkt( self.dst_port_2_mac, self.dst_port_2_ip) + send_packet(self, self.dst_port_2_id, arpreq_pkt) + arpreq_pkt = construct_ns_pkt( self.dst_port_3_mac, self.dst_port_3_ip) + send_packet(self, self.dst_port_3_id, arpreq_pkt) + + for dut_i in self.test_port_ids: + for asic_i in self.test_port_ids[dut_i]: + for dst_port_id in self.test_port_ids[dut_i][asic_i]: + dst_port_ip = self.test_port_ips[dut_i][asic_i][dst_port_id] + dst_port_mac = self.dataplane.get_mac(0, dst_port_id) + arpreq_pkt = construct_ns_pkt( dst_port_mac, dst_port_ip['peer_addr']) + send_packet(self, dst_port_id, arpreq_pkt) + + # ptf don't know the address of neighbor, use ping to learn relevant arp entries instead of send arp request + if self.test_port_ips and not self.t0_src_uplink_dst_downlink: + ips = [ip for ip in get_peer_addresses(self.test_port_ips)] + if ips: + cmd = 'for ip in {}; do ping -6 -c 4 -i 0.2 -W 1 -q $ip > /dev/null 2>&1 & done'.format(' '.join(ips)) + self.exec_cmd_on_dut(self.server, self.test_params['dut_username'], + self.test_params['dut_password'], cmd) + + time.sleep(8) + + class ARPpopulatePTF(sai_base_test.ThriftInterfaceDataPlane): def runTest(self): # ARP Populate @@ -938,6 +1120,93 @@ def get_port_id(self, client, port_name): ), file=sys.stderr) return sai_port_id + def verify_v4_pkt( + self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, \ + exp_ip_id, exp_ttl, ip_ttl): + for dscp in range(0, 64): + tos = (dscp << 2) + tos |= 1 + pkt = simple_ip_packet(pktlen=64, + eth_dst=pkt_dst_mac, + eth_src=src_port_mac, + ip_src=src_port_ip, + ip_dst=dst_port_ip, + ip_tos=tos, + ip_id=exp_ip_id, + ip_ttl=ip_ttl) + send_packet(self, src_port_id, pkt, 1) + print("dscp: %d, calling send_packet()" % + (tos >> 2), file=sys.stderr) + + cnt = 0 + dscp_received = False + while not dscp_received: + result = self.dataplane.poll( + device_number=0, port_number=dst_port_id, timeout=3) + if isinstance(result, self.dataplane.PollFailure): + self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( + dst_port_id, cnt, result.format())) + + recv_pkt = scapy.Ether(result.packet) + cnt += 1 + + # Verify dscp flag + try: + if (recv_pkt.payload.tos == tos and + recv_pkt.payload.src == src_port_ip and + recv_pkt.payload.dst == dst_port_ip and + recv_pkt.payload.ttl == exp_ttl and + recv_pkt.payload.id == exp_ip_id): + dscp_received = True + print("dscp: %d, total received: %d" % + (tos >> 2, cnt), file=sys.stderr) + except AttributeError: + print("dscp: %d, total received: %d, attribute error!" % ( + tos >> 2, cnt), file=sys.stderr) + continue + + def verify_v6_pkt( + self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, \ + exp_ttl, ip_ttl): + for dscp in range(0, 64): + pkt = simple_udpv6_packet(pktlen=64, + eth_dst=pkt_dst_mac, + eth_src=src_port_mac, + ipv6_src=src_port_ip, + ipv6_dst=dst_port_ip, + ipv6_dscp=dscp, + ipv6_hlim=ip_ttl) + send_packet(self, src_port_id, pkt, 1) + print("dscp: %d, calling send_packet()" % + (dscp), file=sys.stderr) + + cnt = 0 + dscp_received = False + while not dscp_received: + result = self.dataplane.poll( + device_number=0, port_number=dst_port_id, timeout=3) + if isinstance(result, self.dataplane.PollFailure): + self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( + dst_port_id, cnt, result.format())) + + recv_pkt = scapy.Ether(result.packet) + cnt += 1 + + # Verify dscp flag + try: + tc = dscp << 2 + if (recv_pkt.payload.tc == tc and + recv_pkt.payload.src == src_port_ip and + recv_pkt.payload.dst == dst_port_ip and + recv_pkt.payload.hlim == exp_ttl ): + dscp_received = True + print("dscp: %d, total received: %d" % + (dscp, cnt), file=sys.stderr) + except AttributeError: + print("dscp: %d, total received: %d, attribute error!" % ( + dscp, cnt), file=sys.stderr) + continue + def runTest(self): switch_init(self.clients) @@ -953,6 +1222,7 @@ def runTest(self): leaf_downstream = self.test_params.get('leaf_downstream', None) asic_type = self.test_params['sonic_asic_type'] tc_to_dscp_count_map = self.test_params.get('tc_to_dscp_count_map', None) + ip_type = self.test_params.get('ip_type', 'ipv4') exp_ip_id = 101 exp_ttl = 63 pkt_dst_mac = router_mac if router_mac != '' else dst_port_mac @@ -961,9 +1231,14 @@ def runTest(self): # in case dst_port_id is part of LAG, find out the actual dst port # for given IP parameters - dst_port_id = get_rx_port( - self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip - ) + if ip_type == 'ipv6': + dst_port_id = get_rx_port_ipv6( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip + ) + else: + dst_port_id = get_rx_port( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip + ) print("actual dst_port_id: %d" % (dst_port_id), file=sys.stderr) print("dst_port_mac: %s, src_port_mac: %s, src_port_ip: %s, dst_port_ip: %s" % ( dst_port_mac, src_port_mac, src_port_ip, dst_port_ip), file=sys.stderr) @@ -991,48 +1266,14 @@ def runTest(self): ip_ttl = ip_ttl if test_dst_port_name is None else ip_ttl + 2 if asic_type in ["cisco-8000"] and masic: ip_ttl = ip_ttl + 1 if masic else ip_ttl - - for dscp in range(0, 64): - tos = (dscp << 2) - tos |= 1 - pkt = simple_ip_packet(pktlen=64, - eth_dst=pkt_dst_mac, - eth_src=src_port_mac, - ip_src=src_port_ip, - ip_dst=dst_port_ip, - ip_tos=tos, - ip_id=exp_ip_id, - ip_ttl=ip_ttl) - send_packet(self, src_port_id, pkt, 1) - print("dscp: %d, calling send_packet()" % - (tos >> 2), file=sys.stderr) - - cnt = 0 - dscp_received = False - while not dscp_received: - result = self.dataplane.poll( - device_number=0, port_number=dst_port_id, timeout=3) - if isinstance(result, self.dataplane.PollFailure): - self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( - dst_port_id, cnt, result.format())) - - recv_pkt = scapy.Ether(result.packet) - cnt += 1 - - # Verify dscp flag - try: - if (recv_pkt.payload.tos == tos and - recv_pkt.payload.src == src_port_ip and - recv_pkt.payload.dst == dst_port_ip and - recv_pkt.payload.ttl == exp_ttl and - recv_pkt.payload.id == exp_ip_id): - dscp_received = True - print("dscp: %d, total received: %d" % - (tos >> 2, cnt), file=sys.stderr) - except AttributeError: - print("dscp: %d, total received: %d, attribute error!" % ( - tos >> 2, cnt), file=sys.stderr) - continue + if ip_type == 'ipv4': + self.verify_v4_pkt( + pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, \ + src_port_id, exp_ip_id, exp_ttl, ip_ttl) + else: + self.verify_v6_pkt( + pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, \ + src_port_id, exp_ttl, ip_ttl) # Read Counters time.sleep(3) @@ -4430,6 +4671,7 @@ def runTest(self): asic_type = self.test_params['sonic_asic_type'] hwsku = self.test_params['hwsku'] platform_asic = self.test_params['platform_asic'] + ip_type = self.test_params.get('ip_type', 'ipv4') # get counter names to query ingress_counters, egress_counters = get_counter_names(sonic_version) @@ -4453,22 +4695,36 @@ def runTest(self): packet_length = 64 pkt_dst_mac = router_mac if router_mac != '' else dst_port_mac - pkt = construct_ip_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + if ip_type == 'ipv6': + pkt = construct_ipv6_pkt(packet_length, + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) + else: + pkt = construct_ip_pkt(packet_length, + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) log_message("dst_port_id: {}, src_port_id: {} src_port_vlan: {}".format( dst_port_id, src_port_id, src_port_vlan), to_stderr=True) # in case dst_port_id is part of LAG, find out the actual dst port # for given IP parameters - dst_port_id = get_rx_port( - self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan - ) + if ip_type == 'ipv6': + dst_port_id = get_rx_port_ipv6( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan) + else: + dst_port_id = get_rx_port( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan) log_message("actual dst_port_id: {}".format(dst_port_id), to_stderr=True) capture_diag_counter(self, 'GetRxPort') @@ -4896,6 +5152,7 @@ def runTest(self): hwsku = self.test_params['hwsku'] internal_hdr_size = self.test_params.get('internal_hdr_size', 0) platform_asic = self.test_params['platform_asic'] + ip_type = self.test_params.get('ip_type', 'ipv4') if 'packet_size' in list(self.test_params.keys()): packet_length = int(self.test_params['packet_size']) @@ -4921,7 +5178,18 @@ def runTest(self): [(src_port_id, src_port_ip)], packets_per_port=1)[src_port_id][0][0] else: - pkt = construct_ip_pkt(packet_length, + if ip_type == 'ipv6': + pkt = construct_ipv6_pkt(packet_length, + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) + else: + pkt = construct_ip_pkt(packet_length, pkt_dst_mac, src_port_mac, src_port_ip, @@ -4935,9 +5203,14 @@ def runTest(self): (dst_port_id, src_port_id, src_port_vlan), file=sys.stderr) # in case dst_port_id is part of LAG, find out the actual dst port # for given IP parameters - dst_port_id = get_rx_port( - self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan - ) + if ip_type == 'ipv6': + dst_port_id = get_rx_port_ipv6( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan + ) + else: + dst_port_id = get_rx_port( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan + ) print("actual dst_port_id: %d" % (dst_port_id), file=sys.stderr) # Add slight tolerance in threshold characterization to consider @@ -5533,6 +5806,7 @@ def runTest(self): hwsku = self.test_params['hwsku'] platform_asic = self.test_params['platform_asic'] dut_asic = self.test_params['dut_asic'] + ip_type = self.test_params.get('ip_type', 'ipv4') if 'packet_size' in list(self.test_params.keys()): packet_length = int(self.test_params['packet_size']) @@ -5550,7 +5824,18 @@ def runTest(self): if is_dualtor and def_vlan_mac is not None: pkt_dst_mac = def_vlan_mac - pkt = construct_ip_pkt(packet_length, + if ip_type == 'ipv6': + pkt = construct_ipv6_pkt(packet_length, + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) + else: + pkt = construct_ip_pkt(packet_length, pkt_dst_mac, src_port_mac, src_port_ip, @@ -5564,9 +5849,14 @@ def runTest(self): (dst_port_id, src_port_id, src_port_vlan), file=sys.stderr) # in case dst_port_id is part of LAG, find out the actual dst port # for given IP parameters - dst_port_id = get_rx_port( - self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan - ) + if ip_type == 'ipv6': + dst_port_id = get_rx_port_ipv6( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan + ) + else: + dst_port_id = get_rx_port( + self, 0, src_port_id, pkt_dst_mac, dst_port_ip, src_port_ip, src_port_vlan + ) print("actual dst_port_id: %d" % (dst_port_id), file=sys.stderr) # Add slight tolerance in threshold characterization to consider From 0521ed34dbdc8e5eab8d6732a65544b8f6e53ed8 Mon Sep 17 00:00:00 2001 From: jbao Date: Wed, 24 Sep 2025 13:35:52 +0300 Subject: [PATCH 2/2] fix pre-commit issue --- tests/common/devices/sonic.py | 4 +- tests/qos/qos_sai_base.py | 10 +- tests/saitests/py3/sai_qos_tests.py | 274 ++++++++++++++-------------- 3 files changed, 146 insertions(+), 142 deletions(-) diff --git a/tests/common/devices/sonic.py b/tests/common/devices/sonic.py index e1d691df92a..b74d1681b13 100644 --- a/tests/common/devices/sonic.py +++ b/tests/common/devices/sonic.py @@ -2425,7 +2425,7 @@ def active_ip_interfaces(self, ip_ifs, tbinfo, ns_arg=DEFAULT_NAMESPACE, intf_nu # Ping for some time to get ARP Re-learnt. # We might have to tune it further if needed. if (v["admin"] == "up" and v["oper_state"] == "up" and - self.ping_v4(v["peer_ipv4"], count=3, ns_arg=ns_arg)): + self.ping_v4(v["peer_ipv4"], count=3, ns_arg=ns_arg)): ip_ifaces[k] = { "ipv4": v["ipv4"], "peer_ipv4": v["peer_ipv4"], @@ -2434,7 +2434,7 @@ def active_ip_interfaces(self, ip_ifs, tbinfo, ns_arg=DEFAULT_NAMESPACE, intf_nu active_ip_intf_cnt += 1 elif ip_type == "ipv6": if (v["admin"] == "up" and v["oper_state"] == "up" and - self.ping_v6(v["peer_ipv6"], count=3, ns_arg=ns_arg)): + self.ping_v6(v["peer_ipv6"], count=3, ns_arg=ns_arg)): ip_ifaces[k] = { "ipv6": v["ipv6"], "peer_ipv6": v["peer_ipv6"], diff --git a/tests/qos/qos_sai_base.py b/tests/qos/qos_sai_base.py index 379ca6a10a7..ccae1fc1b9e 100644 --- a/tests/qos/qos_sai_base.py +++ b/tests/qos/qos_sai_base.py @@ -2030,7 +2030,7 @@ def populateArpEntries_T2( @pytest.fixture(scope='class', autouse=True) def populateArpEntries( self, duthosts, get_src_dst_asic_and_duts, lossy_queue_traffic_direction, - ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, + ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, # noqa: F811 ip_type, update_delay_first_probe_time_for_v6_top # noqa: F811 ): """ @@ -2731,7 +2731,8 @@ def skip_pacific_dst_asic(self, dutConfig): def populate_arp_entries( self, get_src_dst_asic_and_duts, - ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, ip_type='ipv4' # noqa: F811 + ptfhost, dutTestParams, dutConfig, releaseAllPorts, handleFdbAging, tbinfo, lower_tor_host, # noqa: F811 + ip_type='ipv4' ): """ Update ARP entries of QoS SAI test ports @@ -2752,7 +2753,8 @@ def populate_arp_entries( else: for dut_asic in get_src_dst_asic_and_duts['all_asics']: result = dut_asic.command("arp -n") - pytest_assert(result["rc"] == 0, "failed to run arp command on {0}".format(dut_asic.sonichost.hostname)) + pytest_assert(result["rc"] == 0, "failed to run arp command on {0}".format( + dut_asic.sonichost.hostname)) if result["stdout"].find("incomplete") == -1: saiQosTest = "sai_qos_tests.ARPpopulate" @@ -3132,7 +3134,7 @@ def get_queue_weights_based_dynamic_th(self, duthost, queue_table_postfix_list): queue_dynamic_th_map = {} weights_list = [] for queue in queue_table_postfix_list: - key_str = f"BUFFER_PROFILE_TABLE:queue{queue}_downlink_lossy_profile" + key_str = f"BUFFER_PROFILE_TABLE:queue{queue}_downlink_lossy_profile" # noqa: E231 dynamic_th_res = duthost.run_redis_cmd(argv=["redis-cli", "-n", 0, "HGET", key_str, "dynamic_th"]) if dynamic_th_res: queue_dynamic_th_map[queue] = dynamic_th_res[0] diff --git a/tests/saitests/py3/sai_qos_tests.py b/tests/saitests/py3/sai_qos_tests.py index e386880b199..1df354a9cf8 100755 --- a/tests/saitests/py3/sai_qos_tests.py +++ b/tests/saitests/py3/sai_qos_tests.py @@ -561,6 +561,7 @@ def construct_ipv6_pkt(pkt_len, dst_mac, src_mac, src_ip, dst_ip, dscp, src_vlan else: return pkt + def construct_arp_pkt(eth_dst, eth_src, arp_op, src_ip, dst_ip, hw_dst, src_vlan): pkt_args = { 'eth_dst': eth_dst, @@ -619,7 +620,7 @@ def create_multicast_mac(multicast_ipv6_binary): # Copy last 32 bits (4 bytes) from multicast IPv6 mac_bytes[2:6] = multicast_ipv6_binary[12:16] - return ':'.join(f'{b:02x}' for b in mac_bytes) + return ':'.join(f'{b:02x}' for b in mac_bytes) # noqa: E231 def convert_to_ns_multicast(ipv6_address): @@ -652,6 +653,7 @@ def convert_to_ns_multicast(ipv6_address): return solicited_node_multicast_ip, solicited_node_multicast_mac + def construct_ns_pkt(eth_src, src_ip, dst_ip='fc02:1000::1'): dst_multicast_ip, dst_multicast_mac = convert_to_ns_multicast(dst_ip) ether = Ether(src=eth_src, dst=dst_multicast_mac) @@ -691,10 +693,10 @@ def get_rx_port(dp, device_number, src_port_id, dst_mac, dst_ip, src_ip, src_vla return result.port + def get_rx_port_ipv6(dp, device_number, src_port_id, dst_mac, dst_ip, src_ip, src_vlan=None): src_port_mac = dp.dataplane.get_mac(device_number, src_port_id) - pkt = construct_ipv6_pkt(64, dst_mac, src_port_mac, - src_ip, dst_ip, 0, src_vlan) + pkt = construct_ipv6_pkt(64, dst_mac, src_port_mac, src_ip, dst_ip, 0, src_vlan) # Send initial packet for any potential ARP resolution, which may cause the LAG # destination to change. Can occur especially when running tests in isolation on a # first test attempt. @@ -1051,14 +1053,14 @@ class ARPpopulateIPv6(ARPpopulate): def runTest(self): # ARP Populate # Ping only required for testports - arpreq_pkt = construct_ns_pkt( self.src_port_mac, self.src_port_ip) + arpreq_pkt = construct_ns_pkt(self.src_port_mac, self.src_port_ip) send_packet(self, self.src_port_id, arpreq_pkt) - arpreq_pkt = construct_ns_pkt( self.dst_port_mac, self.dst_port_ip) + arpreq_pkt = construct_ns_pkt(self.dst_port_mac, self.dst_port_ip) send_packet(self, self.dst_port_id, arpreq_pkt) - arpreq_pkt = construct_ns_pkt( self.dst_port_2_mac, self.dst_port_2_ip) + arpreq_pkt = construct_ns_pkt(self.dst_port_2_mac, self.dst_port_2_ip) send_packet(self, self.dst_port_2_id, arpreq_pkt) - arpreq_pkt = construct_ns_pkt( self.dst_port_3_mac, self.dst_port_3_ip) + arpreq_pkt = construct_ns_pkt(self.dst_port_3_mac, self.dst_port_3_ip) send_packet(self, self.dst_port_3_id, arpreq_pkt) for dut_i in self.test_port_ids: @@ -1066,7 +1068,7 @@ def runTest(self): for dst_port_id in self.test_port_ids[dut_i][asic_i]: dst_port_ip = self.test_port_ips[dut_i][asic_i][dst_port_id] dst_port_mac = self.dataplane.get_mac(0, dst_port_id) - arpreq_pkt = construct_ns_pkt( dst_port_mac, dst_port_ip['peer_addr']) + arpreq_pkt = construct_ns_pkt(dst_port_mac, dst_port_ip['peer_addr']) send_packet(self, dst_port_id, arpreq_pkt) # ptf don't know the address of neighbor, use ping to learn relevant arp entries instead of send arp request @@ -1075,7 +1077,7 @@ def runTest(self): if ips: cmd = 'for ip in {}; do ping -6 -c 4 -i 0.2 -W 1 -q $ip > /dev/null 2>&1 & done'.format(' '.join(ips)) self.exec_cmd_on_dut(self.server, self.test_params['dut_username'], - self.test_params['dut_password'], cmd) + self.test_params['dut_password'], cmd) time.sleep(8) @@ -1121,91 +1123,91 @@ def get_port_id(self, client, port_name): return sai_port_id def verify_v4_pkt( - self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, \ + self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, exp_ip_id, exp_ttl, ip_ttl): for dscp in range(0, 64): - tos = (dscp << 2) - tos |= 1 - pkt = simple_ip_packet(pktlen=64, - eth_dst=pkt_dst_mac, - eth_src=src_port_mac, - ip_src=src_port_ip, - ip_dst=dst_port_ip, - ip_tos=tos, - ip_id=exp_ip_id, - ip_ttl=ip_ttl) - send_packet(self, src_port_id, pkt, 1) - print("dscp: %d, calling send_packet()" % - (tos >> 2), file=sys.stderr) - - cnt = 0 - dscp_received = False - while not dscp_received: - result = self.dataplane.poll( - device_number=0, port_number=dst_port_id, timeout=3) - if isinstance(result, self.dataplane.PollFailure): - self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( - dst_port_id, cnt, result.format())) - - recv_pkt = scapy.Ether(result.packet) - cnt += 1 + tos = (dscp << 2) + tos |= 1 + pkt = simple_ip_packet(pktlen=64, + eth_dst=pkt_dst_mac, + eth_src=src_port_mac, + ip_src=src_port_ip, + ip_dst=dst_port_ip, + ip_tos=tos, + ip_id=exp_ip_id, + ip_ttl=ip_ttl) + send_packet(self, src_port_id, pkt, 1) + print("dscp: %d, calling send_packet()" % + (tos >> 2), file=sys.stderr) - # Verify dscp flag - try: - if (recv_pkt.payload.tos == tos and - recv_pkt.payload.src == src_port_ip and - recv_pkt.payload.dst == dst_port_ip and - recv_pkt.payload.ttl == exp_ttl and - recv_pkt.payload.id == exp_ip_id): - dscp_received = True - print("dscp: %d, total received: %d" % - (tos >> 2, cnt), file=sys.stderr) - except AttributeError: - print("dscp: %d, total received: %d, attribute error!" % ( - tos >> 2, cnt), file=sys.stderr) - continue + cnt = 0 + dscp_received = False + while not dscp_received: + result = self.dataplane.poll( + device_number=0, port_number=dst_port_id, timeout=3) + if isinstance(result, self.dataplane.PollFailure): + self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( + dst_port_id, cnt, result.format())) + + recv_pkt = scapy.Ether(result.packet) + cnt += 1 + + # Verify dscp flag + try: + if (recv_pkt.payload.tos == tos and + recv_pkt.payload.src == src_port_ip and + recv_pkt.payload.dst == dst_port_ip and + recv_pkt.payload.ttl == exp_ttl and + recv_pkt.payload.id == exp_ip_id): + dscp_received = True + print("dscp: %d, total received: %d" % + (tos >> 2, cnt), file=sys.stderr) + except AttributeError: + print("dscp: %d, total received: %d, attribute error!" % ( + tos >> 2, cnt), file=sys.stderr) + continue def verify_v6_pkt( - self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, \ + self, pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, exp_ttl, ip_ttl): for dscp in range(0, 64): - pkt = simple_udpv6_packet(pktlen=64, - eth_dst=pkt_dst_mac, - eth_src=src_port_mac, - ipv6_src=src_port_ip, - ipv6_dst=dst_port_ip, - ipv6_dscp=dscp, - ipv6_hlim=ip_ttl) - send_packet(self, src_port_id, pkt, 1) - print("dscp: %d, calling send_packet()" % - (dscp), file=sys.stderr) + pkt = simple_udpv6_packet(pktlen=64, + eth_dst=pkt_dst_mac, + eth_src=src_port_mac, + ipv6_src=src_port_ip, + ipv6_dst=dst_port_ip, + ipv6_dscp=dscp, + ipv6_hlim=ip_ttl) + send_packet(self, src_port_id, pkt, 1) + print("dscp: %d, calling send_packet()" % + (dscp), file=sys.stderr) - cnt = 0 - dscp_received = False - while not dscp_received: - result = self.dataplane.poll( - device_number=0, port_number=dst_port_id, timeout=3) - if isinstance(result, self.dataplane.PollFailure): - self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( - dst_port_id, cnt, result.format())) - - recv_pkt = scapy.Ether(result.packet) - cnt += 1 - - # Verify dscp flag - try: - tc = dscp << 2 - if (recv_pkt.payload.tc == tc and - recv_pkt.payload.src == src_port_ip and - recv_pkt.payload.dst == dst_port_ip and - recv_pkt.payload.hlim == exp_ttl ): - dscp_received = True - print("dscp: %d, total received: %d" % - (dscp, cnt), file=sys.stderr) - except AttributeError: - print("dscp: %d, total received: %d, attribute error!" % ( - dscp, cnt), file=sys.stderr) - continue + cnt = 0 + dscp_received = False + while not dscp_received: + result = self.dataplane.poll( + device_number=0, port_number=dst_port_id, timeout=3) + if isinstance(result, self.dataplane.PollFailure): + self.fail("Expected packet was not received on port %d. Total received: %d.\n%s" % ( + dst_port_id, cnt, result.format())) + + recv_pkt = scapy.Ether(result.packet) + cnt += 1 + + # Verify dscp flag + try: + tc = dscp << 2 + if (recv_pkt.payload.tc == tc and + recv_pkt.payload.src == src_port_ip and + recv_pkt.payload.dst == dst_port_ip and + recv_pkt.payload.hlim == exp_ttl): + dscp_received = True + print("dscp: %d, total received: %d" % + (dscp, cnt), file=sys.stderr) + except AttributeError: + print("dscp: %d, total received: %d, attribute error!" % ( + dscp, cnt), file=sys.stderr) + continue def runTest(self): switch_init(self.clients) @@ -1268,11 +1270,11 @@ def runTest(self): ip_ttl = ip_ttl + 1 if masic else ip_ttl if ip_type == 'ipv4': self.verify_v4_pkt( - pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, \ + pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, exp_ip_id, exp_ttl, ip_ttl) else: self.verify_v6_pkt( - pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, \ + pkt_dst_mac, src_port_mac, src_port_ip, dst_port_ip, dst_port_id, src_port_id, exp_ttl, ip_ttl) # Read Counters @@ -4697,24 +4699,24 @@ def runTest(self): pkt_dst_mac = router_mac if router_mac != '' else dst_port_mac if ip_type == 'ipv6': pkt = construct_ipv6_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) else: pkt = construct_ip_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) log_message("dst_port_id: {}, src_port_id: {} src_port_vlan: {}".format( dst_port_id, src_port_id, src_port_vlan), to_stderr=True) # in case dst_port_id is part of LAG, find out the actual dst port @@ -5180,24 +5182,24 @@ def runTest(self): else: if ip_type == 'ipv6': pkt = construct_ipv6_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) else: pkt = construct_ip_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) print("dst_port_id: %d, src_port_id: %d src_port_vlan: %s" % (dst_port_id, src_port_id, src_port_vlan), file=sys.stderr) @@ -5826,24 +5828,24 @@ def runTest(self): if ip_type == 'ipv6': pkt = construct_ipv6_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) else: pkt = construct_ip_pkt(packet_length, - pkt_dst_mac, - src_port_mac, - src_port_ip, - dst_port_ip, - dscp, - src_port_vlan, - ecn=ecn, - ttl=ttl) + pkt_dst_mac, + src_port_mac, + src_port_ip, + dst_port_ip, + dscp, + src_port_vlan, + ecn=ecn, + ttl=ttl) print("dst_port_id: %d, src_port_id: %d, src_port_vlan: %s" % (dst_port_id, src_port_id, src_port_vlan), file=sys.stderr)