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