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'])
Exemple #3
0
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))
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
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