def test_proxy_arp(duthosts, enum_rand_one_per_hwsku_frontend_hostname, setup_ptf_arp, intfs_for_test, ptfhost, config_facts, ip_version, tbinfo): ''' Send an ARP request or neighbor solicitation (NS) to the DUT for an IP address within the subnet of the DUT's VLAN. DUT should reply with an ARP reply or neighbor advertisement (NA) containing the DUT's own MAC ''' duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name = setup_ptf_arp pytest_require(duthost.has_config_subcommand('config vlan proxy_arp'), "Proxy ARP command does not exist on device") if ip_version == 'v4': pytest_require(ptf_intf_ipv4_addr is not None, 'No IPv4 VLAN address configured on device') elif ip_version == 'v6': pytest_require(ptf_intf_ipv6_addr is not None, 'No IPv6 VLAN address configured on device') proxy_arp_config_cmd = 'config vlan proxy_arp {} {}' # We are leveraging the fact that ping will automatically send a neighbor solicitation/ARP request for us # However, we expect the ping itself to always fail since no interface is configured with the pinged IP, so add '|| true' so we can continue ping_cmd = 'ping {} -I {} -c 1 || true' # Enable proxy ARP/NDP for the VLANs on the DUT vlans = config_facts['VLAN'] vlan_ids =[vlans[vlan]['vlanid'] for vlan in vlans.keys()] for vid in vlan_ids: duthost.shell(proxy_arp_config_cmd.format(vid, 'enabled')) time.sleep(3) logger.info("Enabled proxy ARP for VLAN {}".format(vid)) clear_dut_arp_cache(ptfhost) ping_addr = None if ip_version == 'v4': ping_addr = increment_ipv4_addr(ptf_intf_ipv4_addr) elif ip_version == 'v6': ping_addr = increment_ipv6_addr(ptf_intf_ipv6_addr) logger.info("Pinging {} using PTF interface {}".format(ping_addr, ptf_intf_name)) ptfhost.shell(ping_cmd.format(ping_addr, ptf_intf_name)) time.sleep(2) neighbor_table = ptfhost.switch_arptable()['ansible_facts']['arptable'][ip_version] topology = tbinfo['topo']['name'] if 'dualtor' in topology: dut_macs = [] for vlan_details in vlans.values(): dut_macs.append(vlan_details['mac'].lower()) else: router_mac = duthost.shell('sonic-cfggen -d -v \'DEVICE_METADATA.localhost.mac\'')["stdout_lines"][0].decode("utf-8") dut_macs = [router_mac] pytest_assert(ping_addr in neighbor_table.keys()) pytest_assert(neighbor_table[ping_addr]['macaddress'].lower() in dut_macs) pytest_assert(neighbor_table[ping_addr]['interface'] == ptf_intf_name) pytest_assert(neighbor_table[ping_addr]['state'].lower() not in ['failed', 'incomplete'])
def test_proxy_arp(common_setup_teardown, setup_ptf_arp, intfs_for_test, ptfhost, config_facts, ip_version, tbinfo): ''' Send an ARP request or neighbor solicitation (NS) to the DUT for an IP address within the subnet of the DUT's VLAN. DUT should reply with an ARP reply or neighbor advertisement (NA) containing the DUT's own MAC ''' duthost, _, router_mac = common_setup_teardown ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name = setup_ptf_arp proxy_arp_config_cmd = 'config vlan proxy_arp {} {}' # We are leveraging the fact that ping will automatically send a neighbor solicitation/ARP request for us # However, we expect the ping itself to always fail since no interface is configured with the pinged IP, so add '|| true' so we can continue ping_cmd = 'ping {} -I {} -c 1 || true' # Enable proxy ARP/NDP for the VLANs on the DUT vlans = config_facts['VLAN'] vlan_ids = [vlans[vlan]['vlanid'] for vlan in vlans.keys()] for vid in vlan_ids: duthost.shell(proxy_arp_config_cmd.format(vid, 'enabled')) time.sleep(3) logger.info("Enabled proxy ARP for VLAN {}".format(vid)) clear_dut_arp_cache(ptfhost) ping_addr = None if ip_version == 'v4': ping_addr = increment_ipv4_addr(ptf_intf_ipv4_addr) elif ip_version == 'v6': ping_addr = increment_ipv6_addr(ptf_intf_ipv6_addr) logger.info("Pinging {} using PTF interface {}".format( ping_addr, ptf_intf_name)) ptfhost.shell(ping_cmd.format(ping_addr, ptf_intf_name)) time.sleep(2) neighbor_table = ptfhost.switch_arptable( )['ansible_facts']['arptable'][ip_version] topology = tbinfo['topo']['name'] if 'dualtor' in topology: dut_macs = [] for vlan_details in vlans.values(): dut_macs.append(vlan_details['mac']) else: dut_macs = [router_mac] pytest_assert(ping_addr in neighbor_table.keys()) pytest_assert(neighbor_table[ping_addr]['macaddress'] in dut_macs) pytest_assert(neighbor_table[ping_addr]['interface'] == ptf_intf_name) pytest_assert(neighbor_table[ping_addr]['state'].lower() not in ['failed', 'incomplete'])
def setup_ptf_arp(config_facts, ptfhost, intfs_for_test): _, _, intf1_index, _, = intfs_for_test ip_addr_config_cmd = 'ip addr {} {}/{} dev {}' # Calculate the IPv6 address to assign to the PTF port vlan_addrs = config_facts['VLAN_INTERFACE'].items()[0][1].keys() intf_ipv6_addr = None intf_ipv4_addr = None for addr in vlan_addrs: if type(ip_network(addr, strict=False)) is IPv6Network: intf_ipv6_addr = ip_network(addr, strict=False) elif type(ip_network(addr, strict=False)) is IPv4Network: intf_ipv4_addr = ip_network(addr, strict=False) # The VLAN interface on the DUT has an x.x.x.1 address assigned (or x::1 in the case of IPv6) # But the network_address property returns an x.x.x.0 address (or x::0 for IPv6) so we increment by two to avoid conflict ptf_intf_name = "eth{}".format(intf1_index) if intf_ipv4_addr is not None: ptf_intf_ipv4_addr = increment_ipv4_addr( intf_ipv4_addr.network_address, incr=2) ptfhost.shell( ip_addr_config_cmd.format('replace', ptf_intf_ipv4_addr, intf_ipv4_addr.prefixlen, ptf_intf_name)) else: ptf_intf_ipv4_addr = None if intf_ipv6_addr is not None: ptf_intf_ipv6_addr = increment_ipv6_addr( intf_ipv6_addr.network_address, incr=2) ptfhost.shell( ip_addr_config_cmd.format('replace', ptf_intf_ipv6_addr, intf_ipv6_addr.prefixlen, ptf_intf_name)) else: ptf_intf_ipv6_addr = None logger.info("Configured {} and {} on PTF interface {}".format( ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name)) yield ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name logger.info("Removing {} and {} from PTF interface {}".format( ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name)) if intf_ipv4_addr is not None: ptfhost.shell( ip_addr_config_cmd.format('del', ptf_intf_ipv4_addr, intf_ipv4_addr.prefixlen, ptf_intf_name)) if intf_ipv6_addr is not None: ptfhost.shell( ip_addr_config_cmd.format('del', ptf_intf_ipv6_addr, intf_ipv6_addr.prefixlen, ptf_intf_name))
def packets_for_test(request, ptfadapter, duthost, config_facts, tbinfo, ip_and_intf_info): ip_version = request.param src_addr_v4, src_addr_v6, ptf_intf = ip_and_intf_info ptf_intf_index = int(ptf_intf.replace('eth', '')) ptf_intf_mac = ptfadapter.dataplane.get_mac(0, ptf_intf_index) vlans = config_facts['VLAN'] topology = tbinfo['topo']['name'] dut_mac = '' for vlan_details in vlans.values(): if 'dualtor' in topology: dut_mac = vlan_details['mac'].lower() else: dut_mac = duthost.shell( 'sonic-cfggen -d -v \'DEVICE_METADATA.localhost.mac\'' )["stdout_lines"][0].decode("utf-8") break if ip_version == 'v4': tgt_addr = increment_ipv4_addr(src_addr_v4) out_pkt = testutils.simple_arp_packet(eth_dst='ff:ff:ff:ff:ff:ff', eth_src=ptf_intf_mac, ip_snd=src_addr_v4, ip_tgt=tgt_addr, arp_op=1, hw_snd=ptf_intf_mac) exp_pkt = testutils.simple_arp_packet(eth_dst=ptf_intf_mac, eth_src=dut_mac, ip_snd=tgt_addr, ip_tgt=src_addr_v4, arp_op=2, hw_snd=dut_mac, hw_tgt=ptf_intf_mac) elif ip_version == 'v6': tgt_addr = increment_ipv6_addr(src_addr_v6) ll_src_addr = generate_link_local_addr(ptf_intf_mac) multicast_tgt_addr = in6_getnsma(inet_pton(socket.AF_INET6, tgt_addr)) multicast_tgt_mac = in6_getnsmac(multicast_tgt_addr) out_pkt = Ether(src=ptf_intf_mac, dst=multicast_tgt_mac) out_pkt /= IPv6(dst=inet_ntop(socket.AF_INET6, multicast_tgt_addr), src=ll_src_addr) out_pkt /= ICMPv6ND_NS(tgt=tgt_addr) out_pkt /= ICMPv6NDOptSrcLLAddr(lladdr=ptf_intf_mac) exp_pkt = Ether(src=dut_mac, dst=ptf_intf_mac) exp_pkt /= IPv6(dst=ll_src_addr, src=generate_link_local_addr(dut_mac)) exp_pkt /= ICMPv6ND_NA(tgt=tgt_addr, S=1, R=1, O=0) exp_pkt /= ICMPv6NDOptSrcLLAddr(type=2, lladdr=dut_mac) exp_pkt = mask.Mask(exp_pkt) exp_pkt.set_do_not_care_scapy(packet.IPv6, 'fl') return ip_version, out_pkt, exp_pkt
def ip_and_intf_info(config_facts, intfs_for_test, ptfhost, ptfadapter): """ Calculate IP addresses and interface to use for test """ ptf_ports_available_in_topo = ptfhost.host.options[ 'variable_manager'].extra_vars.get("ifaces_map") _, _, intf1_index, _, = intfs_for_test ptf_intf_name = ptf_ports_available_in_topo[intf1_index] # Calculate the IPv6 address to assign to the PTF port vlan_addrs = config_facts['VLAN_INTERFACE'].items()[0][1].keys() intf_ipv6_addr = None intf_ipv4_addr = None for addr in vlan_addrs: try: if type(ip_network(addr, strict=False)) is IPv6Network: intf_ipv6_addr = ip_network(addr, strict=False) elif type(ip_network(addr, strict=False)) is IPv4Network: intf_ipv4_addr = ip_network(addr, strict=False) except ValueError: continue # The VLAN interface on the DUT has an x.x.x.1 address assigned (or x::1 in the case of IPv6) # But the network_address property returns an x.x.x.0 address (or x::0 for IPv6) so we increment by two to avoid conflict if intf_ipv4_addr is not None: ptf_intf_ipv4_addr = increment_ipv4_addr( intf_ipv4_addr.network_address, incr=2) ptf_intf_ipv4_hosts = intf_ipv4_addr.hosts() else: ptf_intf_ipv4_addr = None ptf_intf_ipv4_hosts = None if intf_ipv6_addr is not None: ptf_intf_ipv6_addr = increment_ipv6_addr( intf_ipv6_addr.network_address, incr=2) else: ptf_intf_ipv6_addr = None logger.info("Using {}, {}, and PTF interface {}".format( ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name)) return ptf_intf_ipv4_addr, ptf_intf_ipv4_hosts, ptf_intf_ipv6_addr, ptf_intf_name, intf1_index
def ip_and_intf_info(config_facts, intfs_for_test): """ Calculate IP addresses and interface to use for test """ _, _, intf1_index, _, = intfs_for_test ptf_intf_name = "eth{}".format(intf1_index) # Calculate the IPv6 address to assign to the PTF port vlan_addrs = config_facts['VLAN_INTERFACE'].items()[0][1].keys() intf_ipv6_addr = None intf_ipv4_addr = None for addr in vlan_addrs: try: if type(ip_network(addr, strict=False)) is IPv6Network: intf_ipv6_addr = ip_network(addr, strict=False) elif type(ip_network(addr, strict=False)) is IPv4Network: intf_ipv4_addr = ip_network(addr, strict=False) except ValueError: continue # The VLAN interface on the DUT has an x.x.x.1 address assigned (or x::1 in the case of IPv6) # But the network_address property returns an x.x.x.0 address (or x::0 for IPv6) so we increment by two to avoid conflict if intf_ipv4_addr is not None: ptf_intf_ipv4_addr = increment_ipv4_addr( intf_ipv4_addr.network_address, incr=2) else: ptf_intf_ipv4_addr = None if intf_ipv6_addr is not None: ptf_intf_ipv6_addr = increment_ipv6_addr( intf_ipv6_addr.network_address, incr=2) else: ptf_intf_ipv6_addr = None logger.info("Using {}, {}, and PTF interface {}".format( ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name)) return ptf_intf_ipv4_addr, ptf_intf_ipv6_addr, ptf_intf_name