blob: 8ddc627ccffb0c2c5a8326c30662a83226f8f080 [file] [log] [blame]
Matthew Wangbefae5f2018-02-27 17:42:51 -08001# Copyright (c) 2015 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
5import logging
Matthew Wangbefae5f2018-02-27 17:42:51 -08006from autotest_lib.client.common_lib import error
Kirtika Ruchandani41be6a92018-04-18 17:01:22 -07007from autotest_lib.client.bin import utils
Matthew Wangbefae5f2018-02-27 17:42:51 -08008from autotest_lib.client.common_lib.cros.network import iw_runner
9from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
10from autotest_lib.server.cros.network import wifi_cell_test_base
11from autotest_lib.server.cros.network import hostap_config
12
13class network_WiFi_RoamFT(wifi_cell_test_base.WiFiCellTestBase):
14 """Tests roam on low signal using FT-PSK between APs
15
16 This test seeks to associate the DUT with an AP with a set of
17 association parameters, create a second AP with a second set of
18 parameters but the same SSID, and lower the transmission power of
19 the first AP. We seek to observe that the DUT successfully
20 connects to the second AP in a reasonable amount of time.
21
22 Roaming using FT-PSK is different from standard roaming in that
23 there is a special key exchange protocol that needs to occur
24 between the APs prior to a successful roam. In order for this
25 communication to work, we need to construct a specific interface
26 architecture as shown below:
27 _________ _________
28 | | | |
29 | br0 | | br1 |
30 |_________| |_________|
31 | | | |
32 ____| |____ ____| |____
33 _____|____ ____|____ ____|____ ____|_____
34 | | | | | | | |
35 | managed0 | | veth0 | <---> | veth1 | | managed1 |
36 |__________| |_________| |_________| |__________|
37
38 The managed0 and managed1 interfaces cannot communicate with each
39 other without a bridge. However, the same bridge cannot be used
40 to bridge the two interfaces either (you can't read from a bridge
41 that you write to as well without putting the bridge in
42 promiscuous mode). Thus, we create a virtual ethernet interface
43 with one peer on either bridge to allow the bridges to forward
44 traffic between managed0 and managed1.
45 """
46
47 version = 1
48 TIMEOUT_SECONDS = 15
49
50 def dut_sees_bss(self, bssid):
51 """
52 Check if a DUT can see a BSS in scan results.
53
54 @param bssid: string bssid of AP we expect to see in scan results.
55 @return True iff scan results from DUT include the specified BSS.
56
57 """
58 runner = iw_runner.IwRunner(remote_host=self.context.client.host)
59 is_requested_bss = lambda iw_bss: iw_bss.bss == bssid
60 scan_results = runner.scan(self.context.client.wifi_if)
61 return scan_results and filter(is_requested_bss, scan_results)
62
63
Matthew Wangbefae5f2018-02-27 17:42:51 -080064 def parse_additional_arguments(self, commandline_args, additional_params):
65 """Hook into super class to take control files parameters.
66
67 @param commandline_args dict of parsed parameters from the autotest.
68 @param additional_params xmlrpc_security_types security config.
69
70 """
71 self._security_config = additional_params
72
73 def run_once(self,host):
74 """Test body."""
75
76 mac0 = '02:00:00:00:03:00'
77 mac1 = '02:00:00:00:04:00'
78 id0 = '020000000300'
79 id1 = '020000000400'
80 key0 = '0f0e0d0c0b0a09080706050403020100'
81 key1 = '000102030405060708090a0b0c0d0e0f'
82 mdid = 'a1b2'
Matthew Wangbefae5f2018-02-27 17:42:51 -080083 router0_conf = hostap_config.HostapConfig(channel=1,
84 mode=hostap_config.HostapConfig.MODE_11G,
85 security_config=self._security_config,
86 bssid=mac0,
87 mdid=mdid,
88 nas_id=id0,
89 r1kh_id=id0,
90 r0kh='%s %s %s' % (mac1, id1, key0),
91 r1kh='%s %s %s' % (mac1, mac1, key1),
Matthew Wang08e868d2018-04-19 12:04:54 -070092 use_bridge=True)
Matthew Wangbefae5f2018-02-27 17:42:51 -080093 router1_conf = hostap_config.HostapConfig(channel=48,
94 mode=hostap_config.HostapConfig.MODE_11A,
95 security_config=self._security_config,
96 bssid=mac1,
97 mdid=mdid,
98 nas_id=id1,
99 r1kh_id=id1,
100 r0kh='%s %s %s' % (mac0, id0, key1),
101 r1kh='%s %s %s' % (mac0, mac0, key0),
Matthew Wang08e868d2018-04-19 12:04:54 -0700102 use_bridge=True)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800103 client_conf = xmlrpc_datatypes.AssociationParameters(
104 security_config=self._security_config)
105
106 # Configure the inital AP.
107 self.context.configure(router0_conf)
108 router_ssid = self.context.router.get_ssid()
109
110 # Connect to the inital AP.
111 client_conf.ssid = router_ssid
112 self.context.assert_connect_wifi(client_conf)
113
114 # Setup a second AP with the same SSID.
115 router1_conf.ssid = router_ssid
116 self.context.configure(router1_conf, multi_interface=True)
117
118 # Get BSSIDs of the two APs
119 bssid0 = self.context.router.get_hostapd_mac(0)
120 bssid1 = self.context.router.get_hostapd_mac(1)
121
122 # Wait for DUT to see the second AP
Kirtika Ruchandani41be6a92018-04-18 17:01:22 -0700123 utils.poll_for_condition(
124 condition=lambda: self.dut_sees_bss(bssid1),
125 exception=error.TestFail('Timed out waiting for DUT'
126 'to see second AP'),
127 timeout=self.TIMEOUT_SECONDS,
128 sleep_interval=1)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800129
130 # Check which AP we are currently connected.
131 # This is to include the case that wpa_supplicant
132 # automatically roam to AP2 during the scan.
133 interface = self.context.client.wifi_if
134 curr_bssid = self.context.client.iw_runner.get_current_bssid(interface)
135 if curr_bssid == bssid0:
136 current_if = self.context.router.get_hostapd_interface(0)
137 roam_to_bssid = bssid1
138 else:
139 current_if = self.context.router.get_hostapd_interface(1)
140 roam_to_bssid = bssid0
141
Matthew Wang08e868d2018-04-19 12:04:54 -0700142 br0 = router0_conf.bridge
143 br1 = router1_conf.bridge
144 self.veth0 = 'veth0'
145 self.veth1 = 'veth1'
Matthew Wang6d1b9612018-04-27 10:40:13 -0700146
147 # Cleanup veth interfaces from previous runs
Matthew Wang08e868d2018-04-19 12:04:54 -0700148 self.context.router.delete_link(self.veth0)
149 self.context.router.delete_link(self.veth1)
150
Matthew Wang6d1b9612018-04-27 10:40:13 -0700151 # Set up virtual ethernet interface so APs can talk to each other
Matthew Wangbefae5f2018-02-27 17:42:51 -0800152 try:
153 self.context.router.router.run('ip link add %s type veth peer name '
Matthew Wang08e868d2018-04-19 12:04:54 -0700154 '%s' % (self.veth0, self.veth1))
155 self.context.router.router.run('ifconfig %s up' % self.veth0)
156 self.context.router.router.run('ifconfig %s up' % self.veth1)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800157 self.context.router.router.run('ip link set %s master %s' %
Matthew Wang08e868d2018-04-19 12:04:54 -0700158 (self.veth0, br0))
Matthew Wangbefae5f2018-02-27 17:42:51 -0800159 self.context.router.router.run('ip link set %s master %s' %
Matthew Wang08e868d2018-04-19 12:04:54 -0700160 (self.veth1, br1))
Matthew Wangbefae5f2018-02-27 17:42:51 -0800161 except Exception as e:
162 raise error.TestFail('veth configuration failed: %s' % e)
163
164
165 # Set the tx power of the current interface
166 # This should fix the tx power at 100mBm == 1dBm. It turns out that
167 # set_tx_power does not actually change the signal level seen from the
168 # DUT sufficiently to force a roam (It might vary from -45 to -30), so
169 # this autotest takes advantage of wpa_supplicant's preference for
170 # 5GHz channels.
171 self.context.router.iw_runner.set_tx_power(current_if, 'fixed 100')
172
173 # Expect that the DUT will re-connect to the new AP.
174 self.context.client._wpa_cli_proxy.run_wpa_cli_cmd('scan')
175 logging.info("Attempting to roam.")
176 if not self.context.client.wait_for_roam(
177 roam_to_bssid, timeout_seconds=self.TIMEOUT_SECONDS):
178 self.context.client._wpa_cli_proxy.run_wpa_cli_cmd('scan')
179 logging.info("Attempting to roam again.")
180 if not self.context.client.wait_for_roam(
181 roam_to_bssid, timeout_seconds=self.TIMEOUT_SECONDS):
182 raise error.TestFail('Failed to roam.')
183
Matthew Wang08e868d2018-04-19 12:04:54 -0700184 def cleanup(self):
185 """Cleanup function."""
186
Brian Norris4d040db2018-07-26 12:31:05 -0700187 if hasattr(self, "veth0"):
188 self.context.router.delete_link(self.veth0)
Matthew Wang08e868d2018-04-19 12:04:54 -0700189 super(network_WiFi_RoamFT, self).cleanup()