blob: bb3977d0c47337d43a93a1c37f8daf7a70df8642 [file] [log] [blame]
Sam Leffler6969d1d2010-03-15 16:07:11 -07001# Copyright (c) 2010 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
Christopher Wiley14796b32013-04-03 14:53:33 -07005import logging
Christopher Wiley3166e432013-08-06 09:53:12 -07006import random
Christopher Wiley14796b32013-04-03 14:53:33 -07007import re
Christopher Wiley3166e432013-08-06 09:53:12 -07008import string
Christopher Wiley14796b32013-04-03 14:53:33 -07009
Wade Guthriee4074dd2013-10-30 11:00:48 -070010from autotest_lib.client.common_lib import base_utils
Paul Stewartc9628b32010-08-11 13:03:51 -070011from autotest_lib.client.common_lib import error
Paul Stewart6ddeba72013-11-18 10:08:23 -080012from autotest_lib.client.common_lib.cros.network import interface
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070013from autotest_lib.server import site_linux_system
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070014from autotest_lib.server.cros import wifi_test_utils
Christopher Wiley99d42c92013-07-09 16:40:16 -070015from autotest_lib.server.cros.network import hostap_config
Sam Leffler19bb0a72010-04-12 08:51:08 -070016
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070017def isLinuxRouter(host):
18 """Check if host is a linux router.
19
20 @param host Host object representing the remote machine.
21 @return True iff remote system is a Linux system.
22
23 """
24 router_uname = host.run('uname').stdout
Sam Leffler19bb0a72010-04-12 08:51:08 -070025 return re.search('Linux', router_uname)
26
Christopher Wiley1febd6a2013-06-03 13:59:48 -070027
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070028class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070029 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070030
31 This class implements test methods/steps that communicate with a
32 router implemented with Linux/mac80211. The router must
33 be pre-configured to enable ssh access and have a mac80211-based
34 wireless device. We also assume hostapd 0.7.x and iw are present
35 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070036
Sam Leffler6969d1d2010-03-15 16:07:11 -070037 """
38
Christopher Wiley3166e432013-08-06 09:53:12 -070039 KNOWN_TEST_PREFIX = 'network_WiFi'
Christopher Wiley1defc242013-09-18 10:28:37 -070040 STARTUP_POLLING_INTERVAL_SECONDS = 0.5
41 STARTUP_TIMEOUT_SECONDS = 10
Christopher Wiley3166e432013-08-06 09:53:12 -070042 SUFFIX_LETTERS = string.ascii_lowercase + string.digits
Christopher Wileyb1ade0a2013-09-16 13:09:55 -070043 SUBNET_PREFIX_OCTETS = (192, 168)
Sam Leffler6969d1d2010-03-15 16:07:11 -070044
Paul Stewart51b0f382013-06-12 09:03:02 -070045 def get_capabilities(self):
46 """@return iterable object of AP capabilities for this system."""
47 caps = set()
48 try:
49 self.cmd_send_management_frame = wifi_test_utils.must_be_installed(
50 self.router, '/usr/bin/send_management_frame')
51 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME)
52 except error.TestFail:
53 pass
54 return super(LinuxRouter, self).get_capabilities().union(caps)
55
56
Christopher Wiley3166e432013-08-06 09:53:12 -070057 def __init__(self, host, params, test_name):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070058 """Build a LinuxRouter.
59
60 @param host Host object representing the remote machine.
61 @param params dict of settings from site_wifitest based tests.
Christopher Wiley3166e432013-08-06 09:53:12 -070062 @param test_name string name of this test. Used in SSID creation.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070063
64 """
65 site_linux_system.LinuxSystem.__init__(self, host, params, 'router')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070066 self._remove_interfaces()
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070067
Wade Guthrie24d1e312012-04-24 16:53:40 -070068 # Router host.
69 self.router = host
70
Christopher Wiley4bca01e2013-09-13 13:36:39 -070071 self.cmd_dhcpd = params.get('cmd_dhcpd', '/usr/sbin/dhcpd')
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070072 self.cmd_hostapd = wifi_test_utils.must_be_installed(
73 host, params.get('cmd_hostapd', '/usr/sbin/hostapd'))
Christopher Wiley7337ff62013-10-03 17:21:46 -070074 self.cmd_hostapd_cli = wifi_test_utils.must_be_installed(
75 host, params.get('cmd_hostapd_cli', '/usr/sbin/hostapd_cli'))
Paul Stewart6ddeba72013-11-18 10:08:23 -080076 self.cmd_wpa_supplicant = wifi_test_utils.must_be_installed(
77 host, params.get('cmd_wpa_supplicant',
78 '/usr/sbin/wpa_supplicant'))
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070079 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
80 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070081
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070082 # hostapd configuration persists throughout the test, subsequent
83 # 'config' commands only modify it.
Christopher Wiley3166e432013-08-06 09:53:12 -070084 self.ssid_prefix = test_name
85 if self.ssid_prefix.startswith(self.KNOWN_TEST_PREFIX):
86 # Many of our tests start with an uninteresting prefix.
87 # Remove it so we can have more unique bytes.
88 self.ssid_prefix = self.ssid_prefix[len(self.KNOWN_TEST_PREFIX):]
89 self.ssid_prefix = self.ssid_prefix.lstrip('_')
90 self.ssid_prefix += '_'
91
Paul Stewartd5aafa92013-03-24 19:06:14 -070092 self.default_config = {
Paul Stewartd5aafa92013-03-24 19:06:14 -070093 'hw_mode': 'g',
94 'ctrl_interface': '/tmp/hostapd-test.control',
95 'logger_syslog': '-1',
96 'logger_syslog_level': '0'
97 }
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070098 self.hostapd = {
99 'configured': False,
Paul Stewart326badb2012-12-18 14:18:54 -0800100 'config_file': "/tmp/hostapd-test-%s.conf",
101 'log_file': "/tmp/hostapd-test-%s.log",
Christopher Wiley1defc242013-09-18 10:28:37 -0700102 'pid_file': "/tmp/hostapd-test-%s.pid",
Paul Stewartf854d2e2011-05-04 13:19:18 -0700103 'log_count': 0,
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700104 'driver': "nl80211",
Paul Stewartd5aafa92013-03-24 19:06:14 -0700105 'conf': self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700106 }
Paul Stewartc2b3de82011-03-03 14:45:31 -0800107 self.station = {
108 'configured': False,
Paul Stewart6ddeba72013-11-18 10:08:23 -0800109 'config_file': "/tmp/wpa-supplicant-test-%s.conf",
110 'log_file': "/tmp/wpa-supplicant-test-%s.log",
111 'pid_file': "/tmp/wpa-supplicant-test-%s.pid",
Christopher Wiley3166e432013-08-06 09:53:12 -0700112 'conf': {},
Paul Stewartc2b3de82011-03-03 14:45:31 -0800113 }
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700114 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -0800115 self.hostapd_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700116 self.force_local_server = "force_local_server" in params
117 self.dhcp_low = 1
118 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700119
Paul Stewart548cf452012-11-27 17:46:23 -0800120 # Kill hostapd and dhcp server if already running.
Thieu Le7b23a542012-01-27 15:54:48 -0800121 self.kill_hostapd()
Paul Stewart548cf452012-11-27 17:46:23 -0800122 self.stop_dhcp_servers()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700123
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700124 # Place us in the US by default
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700125 self.iw_runner.set_regulatory_domain('US')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700126
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700127
Christopher Wileyf4bc88b2013-08-29 16:45:15 -0700128 def close(self):
129 """Close global resources held by this system."""
130 self.destroy()
131 super(LinuxRouter, self).close()
132
133
Sam Leffler6969d1d2010-03-15 16:07:11 -0700134 def create(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700135 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700136
137 @param params dict containing the device type under key 'type'.
138
139 """
140 self.create_wifi_device(params['type'])
141
142
143 def create_wifi_device(self, device_type='hostap'):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700144 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700145
146 Defaults to creating a hostap managed device.
147
148 @param device_type string device type.
149
150 """
Sam Leffler6969d1d2010-03-15 16:07:11 -0700151 #
152 # AP mode is handled entirely by hostapd so we only
153 # have to setup others (mapping the bsd type to what
154 # iw wants)
155 #
156 # map from bsd types to iw types
Christopher Wiley14796b32013-04-03 14:53:33 -0700157 self.apmode = device_type in ('ap', 'hostap')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800158 if not self.apmode:
Christopher Wiley14796b32013-04-03 14:53:33 -0700159 self.station['type'] = device_type
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700160 self.phytype = {
Christopher Wiley14796b32013-04-03 14:53:33 -0700161 'sta' : 'managed',
162 'monitor' : 'monitor',
163 'adhoc' : 'adhoc',
164 'ibss' : 'ibss',
165 'ap' : 'managed', # NB: handled by hostapd
166 'hostap' : 'managed', # NB: handled by hostapd
167 'mesh' : 'mesh',
168 'wds' : 'wds',
169 }[device_type]
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700170
Sam Leffler6969d1d2010-03-15 16:07:11 -0700171
Christopher Wileyd89b5282013-04-10 15:21:26 -0700172 def destroy(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700173 """Destroy a previously created device.
174
175 @param params dict of site_wifitest parameters.
176
177 """
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700178 self.deconfig(params)
Paul Stewartd5aafa92013-03-24 19:06:14 -0700179 self.hostapd['conf'] = self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700180
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700181
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700182 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700183 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700184 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700185
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700186
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700187 def cleanup(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700188 """Clean up any resources in use.
189
190 @param params dict of site_wifitest parameters.
191
192 """
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700193 # For linux, this is a no-op
194 pass
195
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700196
Christopher Wiley7337ff62013-10-03 17:21:46 -0700197 def get_hostapd_start_command(self, log_file, pid_file, conf_file):
198 return '%s -dd -t -P %s %s &> %s &' % (
199 self.cmd_hostapd, pid_file, conf_file, log_file)
200
201
Paul Stewart548cf452012-11-27 17:46:23 -0800202 def start_hostapd(self, conf, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700203 """Start a hostapd instance described by conf.
204
205 @param conf dict of hostapd configuration parameters.
206 @param params dict of site_wifitest parameters.
207
208 """
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700209 logging.info('Starting hostapd with parameters: %r', conf)
Paul Stewart548cf452012-11-27 17:46:23 -0800210 # Figure out the correct interface.
Paul Stewart326badb2012-12-18 14:18:54 -0800211 interface = self._get_wlanif(self.hostapd['frequency'],
212 self.phytype,
213 mode=conf.get('hw_mode', 'b'))
214
215 conf_file = self.hostapd['config_file'] % interface
216 log_file = self.hostapd['log_file'] % interface
Christopher Wiley1defc242013-09-18 10:28:37 -0700217 pid_file = self.hostapd['pid_file'] % interface
Paul Stewart326badb2012-12-18 14:18:54 -0800218 conf['interface'] = interface
Paul Stewart548cf452012-11-27 17:46:23 -0800219
220 # Generate hostapd.conf.
221 self._pre_config_hook(conf)
222 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
223 (conf_file, '\n'.join(
224 "%s=%s" % kv for kv in conf.iteritems())))
225
226 # Run hostapd.
227 logging.info("Starting hostapd...")
Christopher Wiley1defc242013-09-18 10:28:37 -0700228 self.router.run('rm %s' % log_file, ignore_status=True)
229 self.router.run('rm %s' % pid_file, ignore_status=True)
Paul Stewart548cf452012-11-27 17:46:23 -0800230 self._pre_start_hook(params)
Christopher Wiley7337ff62013-10-03 17:21:46 -0700231 start_command = self.get_hostapd_start_command(
232 log_file, pid_file, conf_file)
233 self.router.run(start_command)
Paul Stewart548cf452012-11-27 17:46:23 -0800234 self.hostapd_instances.append({
Christopher Wiley3099be72013-11-06 16:49:02 -0800235 'ssid': conf['ssid'],
Paul Stewart548cf452012-11-27 17:46:23 -0800236 'conf_file': conf_file,
237 'log_file': log_file,
Christopher Wiley1defc242013-09-18 10:28:37 -0700238 'interface': interface,
Paul Stewart6ddeba72013-11-18 10:08:23 -0800239 'pid_file': pid_file,
240 'config_dict': conf.copy()
Paul Stewart548cf452012-11-27 17:46:23 -0800241 })
242
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700243
Paul Stewart326badb2012-12-18 14:18:54 -0800244 def _kill_process_instance(self, process, instance=None, wait=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700245 """Kill a process on the router.
246
Paul Stewart326badb2012-12-18 14:18:54 -0800247 Kills program named |process|, optionally only a specific
248 |instance|. If |wait| is specified, we makes sure |process| exits
249 before returning.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700250
251 @param process string name of process to kill.
252 @param instance string instance of process to kill.
253 @param wait int timeout in seconds to wait for.
254
Thieu Le7b23a542012-01-27 15:54:48 -0800255 """
Paul Stewart21737812012-12-06 11:03:32 -0800256 if instance:
Paul Stewart326badb2012-12-18 14:18:54 -0800257 search_arg = '-f "%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800258 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800259 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800260
Paul Stewart326badb2012-12-18 14:18:54 -0800261 cmd = "pkill %s >/dev/null 2>&1" % search_arg
262
263 if wait:
264 cmd += (" && while pgrep %s &> /dev/null; do sleep 1; done" %
265 search_arg)
266 self.router.run(cmd, timeout=wait, ignore_status=True)
267 else:
268 self.router.run(cmd, ignore_status=True)
269
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700270
Paul Stewart326badb2012-12-18 14:18:54 -0800271 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700272 """Kills a hostapd instance.
273
274 @param instance string instance to kill.
275
276 """
Paul Stewart326badb2012-12-18 14:18:54 -0800277 self._kill_process_instance('hostapd', instance, 30)
Thieu Le7b23a542012-01-27 15:54:48 -0800278
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700279
Paul Stewart21737812012-12-06 11:03:32 -0800280 def kill_hostapd(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700281 """Kill all hostapd instances."""
Paul Stewart21737812012-12-06 11:03:32 -0800282 self.kill_hostapd_instance(None)
283
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700284
285 def __get_default_hostap_config(self):
286 """@return dict of default options for hostapd."""
287 conf = self.hostapd['conf']
288 # default RTS and frag threshold to ``off''
289 conf['rts_threshold'] = '2347'
290 conf['fragm_threshold'] = '2346'
291 conf['driver'] = self.hostapd['driver']
Christopher Wiley0ff28ab2013-08-12 13:23:04 -0700292 conf['ssid'] = self._build_ssid('')
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700293 return conf
294
295
Christopher Wiley0ff28ab2013-08-12 13:23:04 -0700296 def _build_ssid(self, suffix):
Christopher Wiley3166e432013-08-06 09:53:12 -0700297 unique_salt = ''.join([random.choice(self.SUFFIX_LETTERS)
298 for x in range(5)])
299 return (self.ssid_prefix + unique_salt + suffix)[-32:]
300
301
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700302 def hostap_configure(self, configuration, multi_interface=None):
303 """Build up a hostapd configuration file and start hostapd.
304
305 Also setup a local server if this router supports them.
306
307 @param configuration HosetapConfig object.
308 @param multi_interface bool True iff multiple interfaces allowed.
309
310 """
311 if multi_interface is None and (self.hostapd['configured'] or
312 self.station['configured']):
313 self.deconfig()
314 # Start with the default hostapd config parameters.
315 conf = self.__get_default_hostap_config()
Christopher Wiley3166e432013-08-06 09:53:12 -0700316 conf['ssid'] = (configuration.ssid or
Christopher Wiley0ff28ab2013-08-12 13:23:04 -0700317 self._build_ssid(configuration.ssid_suffix))
Christopher Wiley9b406202013-05-06 14:07:49 -0700318 if configuration.bssid:
319 conf['bssid'] = configuration.bssid
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700320 conf['channel'] = configuration.channel
321 self.hostapd['frequency'] = configuration.frequency
322 conf['hw_mode'] = configuration.hw_mode
323 if configuration.hide_ssid:
324 conf['ignore_broadcast_ssid'] = 1
325 if configuration.is_11n:
326 conf['ieee80211n'] = 1
Christopher Wiley32fbb7a2013-09-18 14:30:50 -0700327 conf['ht_capab'] = configuration.hostapd_ht_capabilities
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700328 if configuration.wmm_enabled:
329 conf['wmm_enabled'] = 1
330 if configuration.require_ht:
331 conf['require_ht'] = 1
Christopher Wiley9fa7c632013-05-01 11:58:06 -0700332 if configuration.beacon_interval:
333 conf['beacon_int'] = configuration.beacon_interval
Christopher Wileya51258e2013-05-03 13:05:06 -0700334 if configuration.dtim_period:
335 conf['dtim_period'] = configuration.dtim_period
Christopher Wileye1235b62013-05-03 15:09:34 -0700336 if configuration.frag_threshold:
337 conf['fragm_threshold'] = configuration.frag_threshold
Christopher Wileyebdc27d2013-06-28 14:35:41 -0700338 if configuration.pmf_support:
339 conf['ieee80211w'] = configuration.pmf_support
Paul Stewart4ae471e2013-09-04 15:42:35 -0700340 if configuration.obss_interval:
341 conf['obss_interval'] = configuration.obss_interval
Christopher Wileyb8921c72013-06-13 09:51:47 -0700342 conf.update(configuration.get_security_hostapd_conf())
Christopher Wileya89706d2013-06-12 13:20:58 -0700343
Wade Guthriefcaaf022013-09-06 11:12:22 -0700344 # TODO(wiley): Remove this multi_interface flag when the bridge router
345 # class is gone.
346 params = {'multi_interface': 1} if multi_interface else {}
347 self.start_hostapd(conf, params)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700348 # Configure transmit power
349 tx_power_params = {'interface': conf['interface']}
350 # TODO(wiley) support for setting transmit power
351 self.set_txpower(tx_power_params)
352 if self.force_local_server:
353 self.start_local_server(conf['interface'])
Wade Guthriefcaaf022013-09-06 11:12:22 -0700354 self._post_start_hook(params)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700355 logging.info('AP configured.')
356 self.hostapd['configured'] = True
357
358
Paul Stewartc2b3de82011-03-03 14:45:31 -0800359 def hostap_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700360 """Configure the AP per test requirements.
Sam Leffler6969d1d2010-03-15 16:07:11 -0700361
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700362 @param params dict of site_wifitest parameters.
363
364 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700365 # keep parameter modifications local-only
366 orig_params = params
367 params = params.copy()
368
Paul Stewart45338d22010-10-21 10:57:02 -0700369 multi_interface = 'multi_interface' in params
370 if multi_interface:
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700371 # remove non-hostapd config item from params
Paul Stewart45338d22010-10-21 10:57:02 -0700372 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800373 elif self.hostapd['configured'] or self.station['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700374 self.deconfig()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700375
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700376 local_server = params.pop('local_server', False)
377
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700378 conf = self.__get_default_hostap_config()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800379 tx_power_params = {}
380 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700381
Paul Stewartc2b3de82011-03-03 14:45:31 -0800382 for k, v in params.iteritems():
383 if k == 'ssid':
384 conf['ssid'] = v
385 elif k == 'ssid_suffix':
Christopher Wiley0ff28ab2013-08-12 13:23:04 -0700386 conf['ssid'] = self._build_ssid(v)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800387 elif k == 'channel':
388 freq = int(v)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700389 self.hostapd['frequency'] = freq
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700390
Paul Stewartc2b3de82011-03-03 14:45:31 -0800391 # 2.4GHz
392 if freq <= 2484:
393 # Make sure hw_mode is set
394 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700395 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800396
397 # Freq = 5 * chan + 2407, except channel 14
398 if freq == 2484:
399 conf['channel'] = 14
400 else:
401 conf['channel'] = (freq - 2407) / 5
402 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700403 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800404 # Make sure hw_mode is set
405 conf['hw_mode'] = 'a'
406 # Freq = 5 * chan + 4000
407 if freq < 5000:
408 conf['channel'] = (freq - 4000) / 5
409 # Freq = 5 * chan + 5000
410 else:
411 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700412
Paul Stewartc2b3de82011-03-03 14:45:31 -0800413 elif k == 'country':
414 conf['country_code'] = v
415 elif k == 'dotd':
416 conf['ieee80211d'] = 1
417 elif k == '-dotd':
418 conf['ieee80211d'] = 0
419 elif k == 'mode':
420 if v == '11a':
421 conf['hw_mode'] = 'a'
422 elif v == '11g':
423 conf['hw_mode'] = 'g'
424 elif v == '11b':
425 conf['hw_mode'] = 'b'
426 elif v == '11n':
427 conf['ieee80211n'] = 1
428 elif k == 'bintval':
429 conf['beacon_int'] = v
430 elif k == 'dtimperiod':
431 conf['dtim_period'] = v
432 elif k == 'rtsthreshold':
433 conf['rts_threshold'] = v
434 elif k == 'fragthreshold':
435 conf['fragm_threshold'] = v
436 elif k == 'shortpreamble':
437 conf['preamble'] = 1
438 elif k == 'authmode':
439 if v == "open":
440 conf['auth_algs'] = 1
441 elif v == "shared":
442 conf['auth_algs'] = 2
443 elif k == 'hidessid':
444 conf['ignore_broadcast_ssid'] = 1
445 elif k == 'wme':
446 conf['wmm_enabled'] = 1
447 elif k == '-wme':
448 conf['wmm_enabled'] = 0
449 elif k == 'deftxkey':
450 conf['wep_default_key'] = v
451 elif k == 'ht20':
452 htcaps.add('') # NB: ensure 802.11n setup below
453 conf['wmm_enabled'] = 1
454 elif k == 'ht40':
455 htcaps.add('[HT40-]')
456 htcaps.add('[HT40+]')
457 conf['wmm_enabled'] = 1
Paul Stewartc1df8d62011-04-07 14:28:15 -0700458 elif k in ('ht40+', 'ht40-'):
459 htcaps.add('[%s]' % k.upper())
460 conf['wmm_enabled'] = 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800461 elif k == 'shortgi':
462 htcaps.add('[SHORT-GI-20]')
463 htcaps.add('[SHORT-GI-40]')
464 elif k == 'pureg':
465 pass # TODO(sleffler) need hostapd support
466 elif k == 'puren':
467 pass # TODO(sleffler) need hostapd support
468 elif k == 'protmode':
469 pass # TODO(sleffler) need hostapd support
470 elif k == 'ht':
471 htcaps.add('') # NB: ensure 802.11n setup below
472 elif k == 'htprotmode':
473 pass # TODO(sleffler) need hostapd support
474 elif k == 'rifs':
475 pass # TODO(sleffler) need hostapd support
476 elif k == 'wepmode':
477 pass # NB: meaningless for hostapd; ignore
478 elif k == '-ampdu':
479 pass # TODO(sleffler) need hostapd support
480 elif k == 'txpower':
481 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700482 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800483 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700484
Paul Stewartc2b3de82011-03-03 14:45:31 -0800485 # Aggregate ht_capab.
486 if htcaps:
487 conf['ieee80211n'] = 1
488 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700489
Paul Stewart548cf452012-11-27 17:46:23 -0800490 self.start_hostapd(conf, orig_params)
Paul Stewart1ae854b2011-02-08 15:10:14 -0800491
Paul Stewartc2b3de82011-03-03 14:45:31 -0800492 # Configure transmit power
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700493 tx_power_params['interface'] = conf['interface']
Paul Stewartc2b3de82011-03-03 14:45:31 -0800494 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700495
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700496 if self.force_local_server or local_server is not False:
497 self.start_local_server(conf['interface'])
Sam Leffler6969d1d2010-03-15 16:07:11 -0700498
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700499 self._post_start_hook(orig_params)
500
501 logging.info("AP configured.")
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700502 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700503
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700504
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700505 @staticmethod
506 def ip_addr(netblock, idx):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700507 """Simple IPv4 calculator.
508
509 Takes host address in "IP/bits" notation and returns netmask, broadcast
510 address as well as integer offsets into the address range.
511
512 @param netblock string host address in "IP/bits" notation.
513 @param idx string describing what to return.
514 @return string containing something you hopefully requested.
515
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700516 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700517 addr_str,bits = netblock.split('/')
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700518 addr = map(int, addr_str.split('.'))
519 mask_bits = (-1 << (32-int(bits))) & 0xffffffff
520 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
Paul Stewart5977da92011-06-01 19:14:08 -0700521 if idx == 'local':
522 return addr_str
523 elif idx == 'netmask':
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700524 return '.'.join(map(str, mask))
525 elif idx == 'broadcast':
526 offset = [m ^ 0xff for m in mask]
527 else:
528 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
529 return '.'.join(map(str, [(a & m) + o
530 for a, m, o in zip(addr, mask, offset)]))
531
532
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700533 def ibss_configure(self, config):
534 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700535
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700536 Extract relevant configuration objects from |config| despite not
537 actually being a hostap managed endpoint.
538
539 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700540
541 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700542 if self.station['configured'] or self.hostapd['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700543 self.deconfig()
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700544 interface = self._get_wlanif(config.frequency, self.phytype,
545 config.hw_mode)
Christopher Wiley3166e432013-08-06 09:53:12 -0700546 self.station['conf']['ssid'] = (config.ssid or
Christopher Wiley0ff28ab2013-08-12 13:23:04 -0700547 self._build_ssid(config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800548 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700549 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700550 self.iw_runner.ibss_join(
551 interface, self.station['conf']['ssid'], config.frequency)
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700552 # Always start a local server.
553 self.start_local_server(interface)
554 # Remember that this interface is up.
Paul Stewartc2b3de82011-03-03 14:45:31 -0800555 self.station['configured'] = True
556 self.station['interface'] = interface
557
558
Paul Stewart2bd823b2012-11-21 15:03:37 -0800559 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700560 """Get the local server address for an interface.
561
562 When we multiple local servers, we give them static IP addresses
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700563 like 192.168.*.254.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700564
565 @param index int describing which local server this is for.
566
567 """
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700568 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254))
Paul Stewart2bd823b2012-11-21 15:03:37 -0800569
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700570
Paul Stewart6ddeba72013-11-18 10:08:23 -0800571 def local_peer_ip_address(self, index):
572 """Get the IP address allocated for the peer associated to the AP.
573
574 This address is assigned to a locally associated peer device that
575 is created for the DUT to perform connectivity tests with.
576 When we have multiple local servers, we give them static IP addresses
577 like 192.168.*.253.
578
579 @param index int describing which local server this is for.
580
581 """
582 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253))
583
584
585 def local_peer_mac_address(self):
586 """Get the MAC address of the peer interface.
587
588 @return string MAC address of the peer interface.
589 """
590 iface = interface.Interface(self.station['interface'], self.router)
591 return iface.mac_address
592
593
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700594 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700595 """Start a local server on an interface.
596
597 @param interface string (e.g. wlan0)
598
599 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700600 logging.info("Starting up local server...")
601
602 if len(self.local_servers) >= 256:
603 raise error.TestFail('Exhausted available local servers')
604
Paul Stewart2bd823b2012-11-21 15:03:37 -0800605 netblock = '%s/24' % self.local_server_address(len(self.local_servers))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700606
607 params = {}
608 params['netblock'] = netblock
609 params['subnet'] = self.ip_addr(netblock, 0)
610 params['netmask'] = self.ip_addr(netblock, 'netmask')
611 params['dhcp_range'] = ' '.join(
612 (self.ip_addr(netblock, self.dhcp_low),
613 self.ip_addr(netblock, self.dhcp_high)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700614 params['interface'] = interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700615
616 params['ip_params'] = ("%s broadcast %s dev %s" %
617 (netblock,
618 self.ip_addr(netblock, 'broadcast'),
619 interface))
620 self.local_servers.append(params)
621
622 self.router.run("%s addr flush %s" %
623 (self.cmd_ip, interface))
624 self.router.run("%s addr add %s" %
625 (self.cmd_ip, params['ip_params']))
626 self.router.run("%s link set %s up" %
627 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800628 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700629
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700630
Paul Stewart548cf452012-11-27 17:46:23 -0800631 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700632 """Start a dhcp server on an interface.
633
634 @param interface string (e.g. wlan0)
635
636 """
Paul Stewart326badb2012-12-18 14:18:54 -0800637 conf_file = self.dhcpd_conf % interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700638 dhcp_conf = '\n'.join(map(
639 lambda server_conf: \
640 "subnet %(subnet)s netmask %(netmask)s {\n" \
641 " range %(dhcp_range)s;\n" \
642 "}" % server_conf,
643 self.local_servers))
644 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
Paul Stewart326badb2012-12-18 14:18:54 -0800645 (conf_file,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700646 '\n'.join(('ddns-update-style none;', dhcp_conf))))
647 self.router.run("touch %s" % self.dhcpd_leases)
648
649 self.router.run("pkill dhcpd >/dev/null 2>&1", ignore_status=True)
650 self.router.run("%s -q -cf %s -lf %s" %
Paul Stewart326badb2012-12-18 14:18:54 -0800651 (self.cmd_dhcpd, conf_file, self.dhcpd_leases))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700652
653
Paul Stewart326badb2012-12-18 14:18:54 -0800654 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700655 """Stop a dhcp server on the router.
656
657 @param instance string instance to kill.
658
659 """
Paul Stewart326badb2012-12-18 14:18:54 -0800660 self._kill_process_instance('dhcpd', instance, 0)
661
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700662
Paul Stewart548cf452012-11-27 17:46:23 -0800663 def stop_dhcp_servers(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700664 """Stop all dhcp servers on the router."""
Paul Stewart326badb2012-12-18 14:18:54 -0800665 self.stop_dhcp_server(None)
Paul Stewart548cf452012-11-27 17:46:23 -0800666
667
Paul Stewartc2b3de82011-03-03 14:45:31 -0800668 def config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700669 """Configure an AP based on site_wifitest parameters.
670
671 @param params dict of site_wifitest parameters.
672
673 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800674 if self.apmode:
675 self.hostap_config(params)
676 else:
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700677 config = hostap_config.HostapConfig(
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700678 frequency=int(params.get('channel', None)))
679 self.ibss_configure(config)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800680
681
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700682 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700683 """Return IP address on the WiFi subnet of a local server on the router.
684
685 If no local servers are configured (e.g. for an RSPro), a TestFail will
686 be raised.
687
688 @param ap_num int which local server to get an address from.
689
690 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700691 if self.local_servers:
692 return self.ip_addr(self.local_servers[ap_num]['netblock'],
693 'local')
694 else:
695 raise error.TestFail("No IP address assigned")
Paul Stewart5977da92011-06-01 19:14:08 -0700696
697
Paul Stewart17350be2012-12-14 13:34:54 -0800698 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700699 """Return the MAC address of an AP in the test.
700
701 @param ap_num int index of local server to read the MAC address from.
702 @return string MAC address like 00:11:22:33:44:55.
703
704 """
Paul Stewart17350be2012-12-14 13:34:54 -0800705 instance = self.hostapd_instances[ap_num]
706 interface = instance['interface']
707 result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
708 # Example response:
709 # 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
710 # link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
711 # inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
712 # inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
713 # we want the MAC address after the "link/ether" above.
714 parts = result.stdout.split(' ')
715 return parts[parts.index('link/ether') + 1]
716
717
Christopher Wileyd89b5282013-04-10 15:21:26 -0700718 def deconfig(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700719 """De-configure the AP (will also bring wlan down).
Sam Leffler6969d1d2010-03-15 16:07:11 -0700720
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700721 @param params dict of parameters from site_wifitest.
722
723 """
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800724 self.deconfig_aps(instance=params.get('instance', None),
725 silent='silent' in params)
726
727
728 def deconfig_aps(self, instance=None, silent=False):
729 """De-configure an AP (will also bring wlan down).
730
731 @param instance: int or None. If instance is None, will bring down all
732 instances of hostapd.
733 @param silent: True if instances should be brought without de-authing
734 the DUT.
735
736 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800737 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700738 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700739
Paul Stewartc2b3de82011-03-03 14:45:31 -0800740 if self.hostapd['configured']:
Paul Stewart326badb2012-12-18 14:18:54 -0800741 local_servers = []
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800742 if instance is not None:
743 instances = [ self.hostapd_instances.pop(instance) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800744 for server in self.local_servers:
745 if server['interface'] == instances[0]['interface']:
746 local_servers = [server]
747 self.local_servers.remove(server)
748 break
Paul Stewart21737812012-12-06 11:03:32 -0800749 else:
750 instances = self.hostapd_instances
751 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800752 local_servers = self.local_servers
753 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700754
Paul Stewart21737812012-12-06 11:03:32 -0800755 for instance in instances:
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800756 if silent:
Paul Stewart21737812012-12-06 11:03:32 -0800757 # Deconfigure without notifying DUT. Remove the interface
758 # hostapd uses to send beacon and DEAUTH packets.
759 self._remove_interface(instance['interface'], True)
760
Paul Stewart326badb2012-12-18 14:18:54 -0800761 self.kill_hostapd_instance(instance['conf_file'])
Christopher Wiley7337ff62013-10-03 17:21:46 -0700762 if wifi_test_utils.is_installed(self.host,
763 instance['log_file']):
764 self.router.get_file(instance['log_file'],
765 'debug/hostapd_router_%d_%s.log' %
766 (self.hostapd['log_count'],
767 instance['interface']))
768 else:
769 logging.error('Did not collect hostapd log file because '
770 'it was missing.')
Paul Stewart548cf452012-11-27 17:46:23 -0800771 self._release_wlanif(instance['interface'])
772# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance)
Paul Stewartf854d2e2011-05-04 13:19:18 -0700773 self.hostapd['log_count'] += 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800774 if self.station['configured']:
Christopher Wiley05262d62013-04-17 17:53:59 -0700775 local_servers = self.local_servers
776 self.local_servers = []
Paul Stewartc2b3de82011-03-03 14:45:31 -0800777 if self.station['type'] == 'ibss':
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700778 self.iw_runner.ibss_leave(self.station['interface'])
Paul Stewart6ddeba72013-11-18 10:08:23 -0800779 if self.station['type'] == 'supplicant':
780 self._kill_process_instance('wpa_supplicant',
781 self.station['interface'])
Paul Stewartc2b3de82011-03-03 14:45:31 -0800782 else:
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700783 self.iw_runner.disconnect_station(self.station['interface'])
Paul Stewartc2b3de82011-03-03 14:45:31 -0800784 self.router.run("%s link set %s down" % (self.cmd_ip,
785 self.station['interface']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700786
Paul Stewart326badb2012-12-18 14:18:54 -0800787 for server in local_servers:
788 self.stop_dhcp_server(server['interface'])
789 self.router.run("%s addr del %s" %
790 (self.cmd_ip, server['ip_params']),
791 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700792
793 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800794 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700795
796
Paul Stewart17350be2012-12-14 13:34:54 -0800797 def verify_pmksa_auth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700798 """Verify that the PMKSA auth was cached on a hostapd instance.
799
800 @param params dict with optional key 'instance' (defaults to 0).
801
802 """
Paul Stewart17350be2012-12-14 13:34:54 -0800803 instance_num = params.get('instance', 0)
804 instance = self.hostapd_instances[instance_num]
805 pmksa_match = 'PMK from PMKSA cache - skip IEEE 802.1X.EAP'
806 self.router.run('grep -q "%s" %s' % (pmksa_match, instance['log_file']))
807
808
Christopher Wileye0afecb2013-11-11 10:54:23 -0800809 def get_ssid(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700810 """@return string ssid for the network stemming from this router."""
Christopher Wileye0afecb2013-11-11 10:54:23 -0800811 if instance is None:
812 instance = 0
813 if len(self.hostapd_instances) > 1:
814 raise error.TestFail('No instance of hostapd specified with '
815 'multiple instances present.')
816
Christopher Wiley3099be72013-11-06 16:49:02 -0800817 if self.hostapd_instances:
818 return self.hostapd_instances[instance]['ssid']
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700819
Christopher Wiley3166e432013-08-06 09:53:12 -0700820 if not 'ssid' in self.station['conf']:
821 raise error.TestFail('Requested ssid of an unconfigured AP.')
822
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700823 return self.station['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700824
825
826 def set_txpower(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700827 """Set the transmission power for an interface.
828
829 Assumes that we want to refer to the first hostapd instance unless
830 'interface' is defined in params. Sets the transmission power to
831 'auto' if 'power' is not defined in params.
832
833 @param params dict of parameters as described above.
834
835 """
Paul Stewart548cf452012-11-27 17:46:23 -0800836 interface = params.get('interface',
837 self.hostapd_instances[0]['interface'])
838 power = params.get('power', 'auto')
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700839 self.iw_runner.set_tx_power(interface, power)
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700840
841
Wade Guthriee4074dd2013-10-30 11:00:48 -0700842 def deauth_client(self, client_mac):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700843 """Deauthenticates a client described in params.
844
Wade Guthriee4074dd2013-10-30 11:00:48 -0700845 @param client_mac string containing the mac address of the client to be
846 deauthenticated.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700847
848 """
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700849 self.router.run('%s -p%s deauthenticate %s' %
850 (self.cmd_hostapd_cli,
851 self.hostapd['conf']['ctrl_interface'],
Wade Guthriee4074dd2013-10-30 11:00:48 -0700852 client_mac))
853
854
855 @base_utils.deprecated
856 def deauth(self, params):
857 """Deauthenticates a client described in params.
858
859 Deprecated: Call 'deauth_client', instead.
860
861 @param params dict containing a key 'client'.
862
863 """
864 self.deauth_client(params['client'])
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700865
866
Paul Stewart51b0f382013-06-12 09:03:02 -0700867 def send_management_frame(self, frame_type, instance=0):
868 """Injects a management frame into an active hostapd session.
869
870 @param frame_type string the type of frame to send.
871 @param instance int indicating which hostapd instance to inject into.
872
873 """
874 hostap_interface = self.hostapd_instances[instance]['interface']
875 interface = self._get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
876 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
877 self.router.run('%s %s %s' %
878 (self.cmd_send_management_frame, interface, frame_type))
879 self._release_wlanif(interface)
880
881
Paul Stewart25536942013-08-15 17:33:42 -0700882 def detect_client_deauth(self, client_mac, instance=0):
883 """Detects whether hostapd has logged a deauthentication from
884 |client_mac|.
885
886 @param client_mac string the MAC address of the client to detect.
887 @param instance int indicating which hostapd instance to query.
888
889 """
890 interface = self.hostapd_instances[instance]['interface']
891 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
892 log_file = self.hostapd_instances[instance]['log_file']
893 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
894 ignore_status=True)
895 return result.exit_status == 0
896
897
Paul Stewart4ae471e2013-09-04 15:42:35 -0700898 def detect_client_coexistence_report(self, client_mac, instance=0):
899 """Detects whether hostapd has logged an action frame from
900 |client_mac| indicating information about 20/40MHz BSS coexistence.
901
902 @param client_mac string the MAC address of the client to detect.
903 @param instance int indicating which hostapd instance to query.
904
905 """
906 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): '
907 '.. .. .. .. .. .. .. .. .. .. %s '
908 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' %
909 ' '.join(client_mac.split(':')))
910 log_file = self.hostapd_instances[instance]['log_file']
911 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file),
912 ignore_status=True)
913 return result.exit_status == 0
914
915
Paul Stewart6ddeba72013-11-18 10:08:23 -0800916 def add_connected_peer(self, instance=0):
917 """Configure a station connected to a running AP instance.
918
919 Extract relevant configuration objects from the hostap
920 configuration for |instance| and generate a wpa_supplicant
921 instance that connects to it. This allows the DUT to interact
922 with a client entity that is also connected to the same AP. A
923 full wpa_supplicant instance is necessary here (instead of just
924 using the "iw" command to connect) since we want to enable
925 advanced features such as TDLS.
926
927 @param instance int indicating which hostapd instance to connect to.
928
929 """
930 if not self.hostapd_instances:
931 raise error.TestFail('Hostapd is not configured.')
932
933 if self.station['configured']:
934 raise error.TestFail('Station is already configured.')
935
936 client_conf = self.station['conf']
937 client_conf['ssid'] = self.get_ssid(instance)
938
939 hostap_conf = self.hostapd_instances[instance]['config_dict']
940 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
941 hostap_conf['channel'])
942 interface = self._get_wlanif(
943 frequency, 'managed', hostap_conf['hw_mode'])
944 client_conf['interface'] = interface
945
946 # TODO(pstew): Configure other bits like PSK, 802.11n if tests
947 # require them...
948 supplicant_config = (
949 'network={\n'
950 ' ssid="%(ssid)s"\n'
951 ' key_mgmt=NONE\n'
952 '}\n' % client_conf
953 )
954
955 conf_file = self.station['config_file'] % interface
956 log_file = self.station['log_file'] % interface
957 pid_file = self.station['pid_file'] % interface
958
959 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
960 (conf_file, supplicant_config))
961
962 # Connect the station.
963 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
964 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s &> %s &' %
965 (self.cmd_wpa_supplicant,
966 interface, pid_file, conf_file,
967 self.hostapd['driver'], log_file))
968 self.router.run(start_command)
969 self.iw_runner.wait_for_link(interface)
970
971 # Assign an IP address to this interface.
972 self.router.run('%s addr add %s/24 dev %s' %
973 (self.cmd_ip, self.local_peer_ip_address(instance),
974 interface))
975
976 # Since we now have two network interfaces connected to the same
977 # network, we need to disable the kernel's protection against
978 # incoming packets to an "unexpected" interface.
979 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' %
980 interface)
981
982 self.station['configured'] = True
983 self.station['type'] = 'supplicant'
984 self.station['interface'] = interface
985
986
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700987 def _pre_config_hook(self, config):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700988 """Hook for subclasses.
989
990 Run after gathering configuration parameters,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700991 but before writing parameters to config file.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700992
993 @param config dict containing hostapd config parameters.
994
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700995 """
996 pass
997
998
999 def _pre_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -07001000 """Hook for subclasses.
1001
1002 Run after generating hostapd config file, but before starting hostapd.
1003
1004 @param params dict parameters from site_wifitest.
1005
mukesh agrawalfe0e85b2011-08-09 14:24:15 -07001006 """
1007 pass
1008
1009
1010 def _post_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -07001011 """Hook for subclasses run after starting hostapd.
1012
1013 @param params dict parameters from site_wifitest.
1014
1015 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -07001016 pass