blob: e71ee401d0ffdd2c8c2d1b5aa020e483dba29c0e [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 Wang8531f622018-05-03 13:50:16 -07006from autotest_lib.server import site_linux_system
Matthew Wangbefae5f2018-02-27 17:42:51 -08007from autotest_lib.client.common_lib import error
Kirtika Ruchandani41be6a92018-04-18 17:01:22 -07008from autotest_lib.client.bin import utils
Matthew Wangbefae5f2018-02-27 17:42:51 -08009from autotest_lib.client.common_lib.cros.network import iw_runner
10from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
Matthew Wangf597e852018-08-17 14:41:08 -070011from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
Matthew Wangbefae5f2018-02-27 17:42:51 -080012from autotest_lib.server.cros.network import wifi_cell_test_base
13from autotest_lib.server.cros.network import hostap_config
14
15class network_WiFi_RoamFT(wifi_cell_test_base.WiFiCellTestBase):
16 """Tests roam on low signal using FT-PSK between APs
17
18 This test seeks to associate the DUT with an AP with a set of
19 association parameters, create a second AP with a second set of
20 parameters but the same SSID, and lower the transmission power of
21 the first AP. We seek to observe that the DUT successfully
22 connects to the second AP in a reasonable amount of time.
23
24 Roaming using FT-PSK is different from standard roaming in that
25 there is a special key exchange protocol that needs to occur
26 between the APs prior to a successful roam. In order for this
27 communication to work, we need to construct a specific interface
28 architecture as shown below:
29 _________ _________
30 | | | |
31 | br0 | | br1 |
32 |_________| |_________|
33 | | | |
34 ____| |____ ____| |____
35 _____|____ ____|____ ____|____ ____|_____
36 | | | | | | | |
37 | managed0 | | veth0 | <---> | veth1 | | managed1 |
38 |__________| |_________| |_________| |__________|
39
40 The managed0 and managed1 interfaces cannot communicate with each
41 other without a bridge. However, the same bridge cannot be used
42 to bridge the two interfaces either (you can't read from a bridge
43 that you write to as well without putting the bridge in
44 promiscuous mode). Thus, we create a virtual ethernet interface
45 with one peer on either bridge to allow the bridges to forward
46 traffic between managed0 and managed1.
47 """
48
49 version = 1
50 TIMEOUT_SECONDS = 15
51
52 def dut_sees_bss(self, bssid):
53 """
54 Check if a DUT can see a BSS in scan results.
55
56 @param bssid: string bssid of AP we expect to see in scan results.
57 @return True iff scan results from DUT include the specified BSS.
58
59 """
60 runner = iw_runner.IwRunner(remote_host=self.context.client.host)
61 is_requested_bss = lambda iw_bss: iw_bss.bss == bssid
62 scan_results = runner.scan(self.context.client.wifi_if)
63 return scan_results and filter(is_requested_bss, scan_results)
64
65
Matthew Wangbefae5f2018-02-27 17:42:51 -080066 def parse_additional_arguments(self, commandline_args, additional_params):
67 """Hook into super class to take control files parameters.
68
69 @param commandline_args dict of parsed parameters from the autotest.
70 @param additional_params xmlrpc_security_types security config.
71
72 """
73 self._security_config = additional_params
74
Matthew Wang371e0f52018-11-15 14:45:07 -080075 def test_body(self):
Matthew Wangbefae5f2018-02-27 17:42:51 -080076 """Test body."""
77
Matthew Wangf597e852018-08-17 14:41:08 -070078 if self._security_config.ft_mode == \
79 xmlrpc_security_types.WPAConfig.FT_MODE_PURE:
80 self.context.client.require_capabilities(
81 [site_linux_system.LinuxSystem.CAPABILITY_SME])
Matthew Wang8531f622018-05-03 13:50:16 -070082
Matthew Wangbefae5f2018-02-27 17:42:51 -080083 mac0 = '02:00:00:00:03:00'
84 mac1 = '02:00:00:00:04:00'
85 id0 = '020000000300'
86 id1 = '020000000400'
87 key0 = '0f0e0d0c0b0a09080706050403020100'
88 key1 = '000102030405060708090a0b0c0d0e0f'
89 mdid = 'a1b2'
Matthew Wangbefae5f2018-02-27 17:42:51 -080090 router0_conf = hostap_config.HostapConfig(channel=1,
91 mode=hostap_config.HostapConfig.MODE_11G,
92 security_config=self._security_config,
93 bssid=mac0,
94 mdid=mdid,
95 nas_id=id0,
96 r1kh_id=id0,
97 r0kh='%s %s %s' % (mac1, id1, key0),
98 r1kh='%s %s %s' % (mac1, mac1, key1),
Matthew Wang08e868d2018-04-19 12:04:54 -070099 use_bridge=True)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800100 router1_conf = hostap_config.HostapConfig(channel=48,
101 mode=hostap_config.HostapConfig.MODE_11A,
102 security_config=self._security_config,
103 bssid=mac1,
104 mdid=mdid,
105 nas_id=id1,
106 r1kh_id=id1,
107 r0kh='%s %s %s' % (mac0, id0, key1),
108 r1kh='%s %s %s' % (mac0, mac0, key0),
Matthew Wang08e868d2018-04-19 12:04:54 -0700109 use_bridge=True)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800110 client_conf = xmlrpc_datatypes.AssociationParameters(
111 security_config=self._security_config)
112
113 # Configure the inital AP.
114 self.context.configure(router0_conf)
115 router_ssid = self.context.router.get_ssid()
116
117 # Connect to the inital AP.
118 client_conf.ssid = router_ssid
119 self.context.assert_connect_wifi(client_conf)
120
121 # Setup a second AP with the same SSID.
122 router1_conf.ssid = router_ssid
123 self.context.configure(router1_conf, multi_interface=True)
124
125 # Get BSSIDs of the two APs
126 bssid0 = self.context.router.get_hostapd_mac(0)
127 bssid1 = self.context.router.get_hostapd_mac(1)
128
129 # Wait for DUT to see the second AP
Kirtika Ruchandani41be6a92018-04-18 17:01:22 -0700130 utils.poll_for_condition(
131 condition=lambda: self.dut_sees_bss(bssid1),
132 exception=error.TestFail('Timed out waiting for DUT'
133 'to see second AP'),
134 timeout=self.TIMEOUT_SECONDS,
135 sleep_interval=1)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800136
137 # Check which AP we are currently connected.
138 # This is to include the case that wpa_supplicant
139 # automatically roam to AP2 during the scan.
140 interface = self.context.client.wifi_if
141 curr_bssid = self.context.client.iw_runner.get_current_bssid(interface)
142 if curr_bssid == bssid0:
143 current_if = self.context.router.get_hostapd_interface(0)
144 roam_to_bssid = bssid1
145 else:
146 current_if = self.context.router.get_hostapd_interface(1)
147 roam_to_bssid = bssid0
148
Matthew Wang08e868d2018-04-19 12:04:54 -0700149 br0 = router0_conf.bridge
150 br1 = router1_conf.bridge
151 self.veth0 = 'veth0'
152 self.veth1 = 'veth1'
Matthew Wang6d1b9612018-04-27 10:40:13 -0700153
154 # Cleanup veth interfaces from previous runs
Matthew Wang08e868d2018-04-19 12:04:54 -0700155 self.context.router.delete_link(self.veth0)
156 self.context.router.delete_link(self.veth1)
157
Matthew Wang6d1b9612018-04-27 10:40:13 -0700158 # Set up virtual ethernet interface so APs can talk to each other
Matthew Wangbefae5f2018-02-27 17:42:51 -0800159 try:
160 self.context.router.router.run('ip link add %s type veth peer name '
Matthew Wang08e868d2018-04-19 12:04:54 -0700161 '%s' % (self.veth0, self.veth1))
162 self.context.router.router.run('ifconfig %s up' % self.veth0)
163 self.context.router.router.run('ifconfig %s up' % self.veth1)
Matthew Wangbefae5f2018-02-27 17:42:51 -0800164 self.context.router.router.run('ip link set %s master %s' %
Matthew Wang08e868d2018-04-19 12:04:54 -0700165 (self.veth0, br0))
Matthew Wangbefae5f2018-02-27 17:42:51 -0800166 self.context.router.router.run('ip link set %s master %s' %
Matthew Wang08e868d2018-04-19 12:04:54 -0700167 (self.veth1, br1))
Matthew Wangbefae5f2018-02-27 17:42:51 -0800168 except Exception as e:
169 raise error.TestFail('veth configuration failed: %s' % e)
170
171
172 # Set the tx power of the current interface
173 # This should fix the tx power at 100mBm == 1dBm. It turns out that
174 # set_tx_power does not actually change the signal level seen from the
175 # DUT sufficiently to force a roam (It might vary from -45 to -30), so
176 # this autotest takes advantage of wpa_supplicant's preference for
177 # 5GHz channels.
178 self.context.router.iw_runner.set_tx_power(current_if, 'fixed 100')
179
180 # Expect that the DUT will re-connect to the new AP.
181 self.context.client._wpa_cli_proxy.run_wpa_cli_cmd('scan')
182 logging.info("Attempting to roam.")
183 if not self.context.client.wait_for_roam(
184 roam_to_bssid, timeout_seconds=self.TIMEOUT_SECONDS):
185 self.context.client._wpa_cli_proxy.run_wpa_cli_cmd('scan')
186 logging.info("Attempting to roam again.")
187 if not self.context.client.wait_for_roam(
188 roam_to_bssid, timeout_seconds=self.TIMEOUT_SECONDS):
189 raise error.TestFail('Failed to roam.')
190
Matthew Wang371e0f52018-11-15 14:45:07 -0800191 def run_once(self,host):
192 """Set global FT switch and call test_body"""
193
194 with self.context.client.set_global_ft_enabled(True):
195 self.test_body()
196
Matthew Wang08e868d2018-04-19 12:04:54 -0700197 def cleanup(self):
198 """Cleanup function."""
199
Brian Norris4d040db2018-07-26 12:31:05 -0700200 if hasattr(self, "veth0"):
201 self.context.router.delete_link(self.veth0)
Matthew Wang08e868d2018-04-19 12:04:54 -0700202 super(network_WiFi_RoamFT, self).cleanup()