Home | History | Annotate | Download | only in network_WiFi_TDLSPing
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 import time
      7 
      8 from autotest_lib.client.bin import utils
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.client.common_lib.cros.network import ping_runner
     11 from autotest_lib.client.common_lib.cros.network import tcpdump_analyzer
     12 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
     13 from autotest_lib.server import site_linux_system
     14 from autotest_lib.server.cros.network import hostap_config
     15 from autotest_lib.server.cros.network import wifi_cell_test_base
     16 
     17 
     18 class network_WiFi_TDLSPing(wifi_cell_test_base.WiFiCellTestBase):
     19     """Tests that the DUT can establish a TDLS link to a connected peer.
     20 
     21     This test associates the DUT with an AP, then attaches a peer
     22     client to the same AP.  After enabling a TDLS link from the DUT
     23     to the attached peer, we should see in the over-the-air packets
     24     that a ping between these devices does not use the AP as a relay
     25     any more.
     26 
     27     """
     28 
     29     version = 1
     30 
     31     def ping_and_check_for_tdls(self, frequency, expected):
     32         """
     33         Use an over-the-air packet capture to check whether we see
     34         ICMP packets from the DUT that indicate it is using a TDLS
     35         link to transmit its requests.  Raise an exception if this
     36         was not what was |expected|.
     37 
     38         @param frequency: int frequency on which to perform the packet capture.
     39         @param expected: bool set to true if we expect the sender to use TDLS.
     40 
     41         """
     42         self.context.capture_host.start_capture(frequency)
     43 
     44         # Since we don't wait for the TDLS link to come up, it's possible
     45         # that we'll have some fairly drastic packet loss as the link is
     46         # being established.  We don't care about that in this test, except
     47         # that we should see at least a few packets by the end of the ping
     48         # we can use to test for TDLS.  Therefore we ignore the statistical
     49         # result of the ping.
     50         ping_config = ping_runner.PingConfig(
     51                 self.context.router.local_peer_ip_address(0),
     52                 ignore_result=True)
     53         self.context.assert_ping_from_dut(ping_config=ping_config)
     54 
     55         results = self.context.capture_host.stop_capture()
     56         if len(results) != 1:
     57             raise error.TestError('Expected to generate one packet '
     58                                   'capture but got %d captures instead.' %
     59                                   len(results))
     60         pcap_result = results[0]
     61 
     62         logging.info('Analyzing packet capture...')
     63 
     64         # Filter for packets from the DUT.
     65         client_mac_filter = 'wlan.sa==%s' % self.context.client.wifi_mac
     66 
     67         # In this test we only care that the outgoing ICMP requests are
     68         # sent over IBSS, so we filter for ICMP echo requests explicitly.
     69         icmp_filter = 'icmp.type==0x08'
     70 
     71         # This filter requires a little explaining. DS status is the second byte
     72         # of the frame control field. This field contains the "tods" and
     73         # "fromds" bits in bit 0 and 1 respsectively. These bits have the
     74         # following interpretation:
     75         #
     76         #   ToDS  FromDS
     77         #     0     0      Ad-Hoc (IBSS)
     78         #     0     1      Traffic from client to the AP
     79         #     1     0      Traffic from AP to the client
     80         #     1     1      4-address mode for wireless distribution system
     81         #
     82         # TDLS co-opts the ToDS=0, FromDS=0 (IBSS) mode when transferring
     83         # data directly between peers. Therefore, to detect TDLS, we compare it
     84         # with 0.
     85         tdls_filter = 'wlan.fc.ds==0x00'
     86 
     87         dut_icmp_display_filter = ' and '.join(
     88                 [client_mac_filter, icmp_filter, tdls_filter])
     89         frames = tcpdump_analyzer.get_frames(pcap_result.local_pcap_path,
     90                 dut_icmp_display_filter, reject_bad_fcs=False)
     91         if expected and not frames:
     92             raise error.TestFail('Packet capture did not contain any IBSS '
     93                                  'frames from the DUT!')
     94         elif not expected and frames:
     95             raise error.TestFail('Packet capture contains an IBSS frame '
     96                                  'from the DUT, but we did not expect them!')
     97 
     98 
     99     def run_once(self):
    100         """Test body."""
    101         client_caps = self.context.client.capabilities
    102         if site_linux_system.LinuxSystem.CAPABILITY_TDLS not in client_caps:
    103             raise error.TestNAError('DUT is incapable of TDLS')
    104         router_caps = self.context.router.capabilities
    105         if site_linux_system.LinuxSystem.CAPABILITY_TDLS not in router_caps:
    106             raise error.TestNAError('Router is incapable of TDLS')
    107 
    108         # Configure the AP.
    109         frequency = 2412
    110         self.context.configure(hostap_config.HostapConfig(
    111                 frequency=frequency, force_wmm=True))
    112         router_ssid = self.context.router.get_ssid()
    113 
    114         # Connect the DUT to the AP.
    115         self.context.assert_connect_wifi(
    116                 xmlrpc_datatypes.AssociationParameters(ssid=router_ssid))
    117 
    118         # Connect a client instance to the AP so the DUT has a peer to which
    119         # it can send TDLS traffic.
    120         self.context.router.add_connected_peer()
    121 
    122         # Test for TDLS connectivity to the peer, using the IP address.
    123         # We expect the first attempt to fail since the client does not
    124         # have the IP address of the peer in its ARP cache.
    125         peer_ip = self.context.router.local_peer_ip_address(0)
    126         link_state = self.context.client.query_tdls_link(peer_ip)
    127         if link_state is not False:
    128             raise error.TestError(
    129                     'First query of TDLS link succeeded: %r' % link_state)
    130 
    131         # Wait a reasonable time for the ARP triggered by the first TDLS
    132         # command to succeed.
    133         time.sleep(1)
    134 
    135         try:
    136             # One of the next few attempts should succeed, since by now the ARP
    137             # cache should be populated. However at this time there should be
    138             # no link.
    139             utils.poll_for_condition(
    140                     lambda: ( self.context.client.query_tdls_link(peer_ip) ==
    141                     'Nonexistent'), sleep_interval=1)
    142         except utils.TimeoutError:
    143             link_state = self.context.client.query_tdls_link(peer_ip)
    144             raise error.TestError('DUT does not report a missing TDLS link: %r'
    145                                   % link_state)
    146 
    147         # Perform TDLS discover and check the status after waiting for response.
    148         self.context.client.discover_tdls_link(peer_ip)
    149         try:
    150             utils.poll_for_condition(
    151                     lambda: (self.context.client.query_tdls_link(peer_ip) ==
    152                     'Disconnected'), timeout=1)
    153         except utils.TimeoutError:
    154             link_state = self.context.client.query_tdls_link(peer_ip)
    155             logging.error('DUT does not report TDLS link is disconnected: %r',
    156                           link_state)
    157 
    158         # Ping from DUT to the associated peer without TDLS.
    159         self.ping_and_check_for_tdls(frequency, expected=False)
    160 
    161         # Ping from DUT to the associated peer with TDLS.
    162         self.context.client.establish_tdls_link(peer_ip)
    163         self.ping_and_check_for_tdls(frequency, expected=True)
    164 
    165         # Ensure that the DUT reports the TDLS link as being active.
    166         # Use the MAC address to ensure we can perform TDLS requests
    167         # against either IP or MAC addresses.
    168         peer_mac = self.context.router.local_peer_mac_address()
    169         link_state = self.context.client.query_tdls_link(peer_mac)
    170         if link_state != 'Connected':
    171             raise error.TestError(
    172                     'DUT does not report TDLS link is active: %r' % link_state)
    173