blob: bbf6bcf2446e6372e247e24f8805310ec16cff79 [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 Wiley3166e432013-08-06 09:53:12 -07005import base64
Christopher Wiley14796b32013-04-03 14:53:33 -07006import logging
Christopher Wiley3166e432013-08-06 09:53:12 -07007import random
Christopher Wiley14796b32013-04-03 14:53:33 -07008import re
Christopher Wiley3166e432013-08-06 09:53:12 -07009import string
Christopher Wiley14796b32013-04-03 14:53:33 -070010
Paul Stewartc9628b32010-08-11 13:03:51 -070011from autotest_lib.client.common_lib import error
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070012from autotest_lib.server import site_linux_system
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070013from autotest_lib.server.cros import wifi_test_utils
Christopher Wiley99d42c92013-07-09 16:40:16 -070014from autotest_lib.server.cros.network import hostap_config
Sam Leffler19bb0a72010-04-12 08:51:08 -070015
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070016def isLinuxRouter(host):
17 """Check if host is a linux router.
18
19 @param host Host object representing the remote machine.
20 @return True iff remote system is a Linux system.
21
22 """
23 router_uname = host.run('uname').stdout
Sam Leffler19bb0a72010-04-12 08:51:08 -070024 return re.search('Linux', router_uname)
25
Christopher Wiley1febd6a2013-06-03 13:59:48 -070026
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070027class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070028 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070029
30 This class implements test methods/steps that communicate with a
31 router implemented with Linux/mac80211. The router must
32 be pre-configured to enable ssh access and have a mac80211-based
33 wireless device. We also assume hostapd 0.7.x and iw are present
34 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070035
Sam Leffler6969d1d2010-03-15 16:07:11 -070036 """
37
Christopher Wiley3166e432013-08-06 09:53:12 -070038 KNOWN_TEST_PREFIX = 'network_WiFi'
39 SUFFIX_LETTERS = string.ascii_lowercase + string.digits
Sam Leffler6969d1d2010-03-15 16:07:11 -070040
Paul Stewart51b0f382013-06-12 09:03:02 -070041 def get_capabilities(self):
42 """@return iterable object of AP capabilities for this system."""
43 caps = set()
44 try:
45 self.cmd_send_management_frame = wifi_test_utils.must_be_installed(
46 self.router, '/usr/bin/send_management_frame')
47 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME)
48 except error.TestFail:
49 pass
50 return super(LinuxRouter, self).get_capabilities().union(caps)
51
52
Christopher Wiley3166e432013-08-06 09:53:12 -070053 def __init__(self, host, params, test_name):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070054 """Build a LinuxRouter.
55
56 @param host Host object representing the remote machine.
57 @param params dict of settings from site_wifitest based tests.
Christopher Wiley3166e432013-08-06 09:53:12 -070058 @param test_name string name of this test. Used in SSID creation.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070059
60 """
61 site_linux_system.LinuxSystem.__init__(self, host, params, 'router')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070062 self._remove_interfaces()
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070063
Wade Guthrie24d1e312012-04-24 16:53:40 -070064 # Router host.
65 self.router = host
66
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070067 self.cmd_hostapd = wifi_test_utils.must_be_installed(
68 host, params.get('cmd_hostapd', '/usr/sbin/hostapd'))
69 self.cmd_hostapd_cli = params.get('cmd_hostapd_cli',
70 '/usr/sbin/hostapd_cli')
71 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
72 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070073
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070074 # hostapd configuration persists throughout the test, subsequent
75 # 'config' commands only modify it.
Christopher Wiley3166e432013-08-06 09:53:12 -070076 self.ssid_prefix = test_name
77 if self.ssid_prefix.startswith(self.KNOWN_TEST_PREFIX):
78 # Many of our tests start with an uninteresting prefix.
79 # Remove it so we can have more unique bytes.
80 self.ssid_prefix = self.ssid_prefix[len(self.KNOWN_TEST_PREFIX):]
81 self.ssid_prefix = self.ssid_prefix.lstrip('_')
82 self.ssid_prefix += '_'
83
Paul Stewartd5aafa92013-03-24 19:06:14 -070084 self.default_config = {
Paul Stewartd5aafa92013-03-24 19:06:14 -070085 'hw_mode': 'g',
86 'ctrl_interface': '/tmp/hostapd-test.control',
87 'logger_syslog': '-1',
88 'logger_syslog_level': '0'
89 }
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070090 self.hostapd = {
91 'configured': False,
Paul Stewart326badb2012-12-18 14:18:54 -080092 'config_file': "/tmp/hostapd-test-%s.conf",
93 'log_file': "/tmp/hostapd-test-%s.log",
Paul Stewartf854d2e2011-05-04 13:19:18 -070094 'log_count': 0,
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070095 'driver': "nl80211",
Paul Stewartd5aafa92013-03-24 19:06:14 -070096 'conf': self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070097 }
Paul Stewartc2b3de82011-03-03 14:45:31 -080098 self.station = {
99 'configured': False,
Christopher Wiley3166e432013-08-06 09:53:12 -0700100 'conf': {},
Paul Stewartc2b3de82011-03-03 14:45:31 -0800101 }
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700102 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -0800103 self.hostapd_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700104 self.force_local_server = "force_local_server" in params
105 self.dhcp_low = 1
106 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700107
Paul Stewart548cf452012-11-27 17:46:23 -0800108 # Kill hostapd and dhcp server if already running.
Thieu Le7b23a542012-01-27 15:54:48 -0800109 self.kill_hostapd()
Paul Stewart548cf452012-11-27 17:46:23 -0800110 self.stop_dhcp_servers()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700111
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700112 # Place us in the US by default
113 self.router.run("%s reg set US" % self.cmd_iw)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700114
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700115
Sam Leffler6969d1d2010-03-15 16:07:11 -0700116 def create(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700117 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700118
119 @param params dict containing the device type under key 'type'.
120
121 """
122 self.create_wifi_device(params['type'])
123
124
125 def create_wifi_device(self, device_type='hostap'):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700126 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700127
128 Defaults to creating a hostap managed device.
129
130 @param device_type string device type.
131
132 """
Sam Leffler6969d1d2010-03-15 16:07:11 -0700133 #
134 # AP mode is handled entirely by hostapd so we only
135 # have to setup others (mapping the bsd type to what
136 # iw wants)
137 #
138 # map from bsd types to iw types
Christopher Wiley14796b32013-04-03 14:53:33 -0700139 self.apmode = device_type in ('ap', 'hostap')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800140 if not self.apmode:
Christopher Wiley14796b32013-04-03 14:53:33 -0700141 self.station['type'] = device_type
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700142 self.phytype = {
Christopher Wiley14796b32013-04-03 14:53:33 -0700143 'sta' : 'managed',
144 'monitor' : 'monitor',
145 'adhoc' : 'adhoc',
146 'ibss' : 'ibss',
147 'ap' : 'managed', # NB: handled by hostapd
148 'hostap' : 'managed', # NB: handled by hostapd
149 'mesh' : 'mesh',
150 'wds' : 'wds',
151 }[device_type]
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700152
Sam Leffler6969d1d2010-03-15 16:07:11 -0700153
Christopher Wileyd89b5282013-04-10 15:21:26 -0700154 def destroy(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700155 """Destroy a previously created device.
156
157 @param params dict of site_wifitest parameters.
158
159 """
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700160 self.deconfig(params)
Paul Stewartd5aafa92013-03-24 19:06:14 -0700161 self.hostapd['conf'] = self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700162
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700163
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700164 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700165 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700166 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700167
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700168
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700169 def cleanup(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700170 """Clean up any resources in use.
171
172 @param params dict of site_wifitest parameters.
173
174 """
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700175 # For linux, this is a no-op
176 pass
177
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700178
Paul Stewart548cf452012-11-27 17:46:23 -0800179 def start_hostapd(self, conf, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700180 """Start a hostapd instance described by conf.
181
182 @param conf dict of hostapd configuration parameters.
183 @param params dict of site_wifitest parameters.
184
185 """
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700186 logging.info('Starting hostapd with parameters: %r', conf)
Paul Stewart548cf452012-11-27 17:46:23 -0800187 # Figure out the correct interface.
Paul Stewart326badb2012-12-18 14:18:54 -0800188 interface = self._get_wlanif(self.hostapd['frequency'],
189 self.phytype,
190 mode=conf.get('hw_mode', 'b'))
191
192 conf_file = self.hostapd['config_file'] % interface
193 log_file = self.hostapd['log_file'] % interface
194 conf['interface'] = interface
Paul Stewart548cf452012-11-27 17:46:23 -0800195
196 # Generate hostapd.conf.
197 self._pre_config_hook(conf)
198 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
199 (conf_file, '\n'.join(
200 "%s=%s" % kv for kv in conf.iteritems())))
201
202 # Run hostapd.
203 logging.info("Starting hostapd...")
204 self._pre_start_hook(params)
205 self.router.run("%s -dd %s &> %s &" %
206 (self.cmd_hostapd, conf_file, log_file))
207
208 self.hostapd_instances.append({
209 'conf_file': conf_file,
210 'log_file': log_file,
Paul Stewart326badb2012-12-18 14:18:54 -0800211 'interface': interface
Paul Stewart548cf452012-11-27 17:46:23 -0800212 })
213
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700214
Paul Stewart326badb2012-12-18 14:18:54 -0800215 def _kill_process_instance(self, process, instance=None, wait=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700216 """Kill a process on the router.
217
Paul Stewart326badb2012-12-18 14:18:54 -0800218 Kills program named |process|, optionally only a specific
219 |instance|. If |wait| is specified, we makes sure |process| exits
220 before returning.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700221
222 @param process string name of process to kill.
223 @param instance string instance of process to kill.
224 @param wait int timeout in seconds to wait for.
225
Thieu Le7b23a542012-01-27 15:54:48 -0800226 """
Paul Stewart21737812012-12-06 11:03:32 -0800227 if instance:
Paul Stewart326badb2012-12-18 14:18:54 -0800228 search_arg = '-f "%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800229 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800230 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800231
Paul Stewart326badb2012-12-18 14:18:54 -0800232 cmd = "pkill %s >/dev/null 2>&1" % search_arg
233
234 if wait:
235 cmd += (" && while pgrep %s &> /dev/null; do sleep 1; done" %
236 search_arg)
237 self.router.run(cmd, timeout=wait, ignore_status=True)
238 else:
239 self.router.run(cmd, ignore_status=True)
240
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700241
Paul Stewart326badb2012-12-18 14:18:54 -0800242 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700243 """Kills a hostapd instance.
244
245 @param instance string instance to kill.
246
247 """
Paul Stewart326badb2012-12-18 14:18:54 -0800248 self._kill_process_instance('hostapd', instance, 30)
Thieu Le7b23a542012-01-27 15:54:48 -0800249
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700250
Paul Stewart21737812012-12-06 11:03:32 -0800251 def kill_hostapd(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700252 """Kill all hostapd instances."""
Paul Stewart21737812012-12-06 11:03:32 -0800253 self.kill_hostapd_instance(None)
254
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700255
256 def __get_default_hostap_config(self):
257 """@return dict of default options for hostapd."""
258 conf = self.hostapd['conf']
259 # default RTS and frag threshold to ``off''
260 conf['rts_threshold'] = '2347'
261 conf['fragm_threshold'] = '2346'
262 conf['driver'] = self.hostapd['driver']
Christopher Wiley3166e432013-08-06 09:53:12 -0700263 conf['ssid'] = self._get_ssid('')
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700264 return conf
265
266
Christopher Wiley3166e432013-08-06 09:53:12 -0700267 def _get_ssid(self, suffix):
268 unique_salt = ''.join([random.choice(self.SUFFIX_LETTERS)
269 for x in range(5)])
270 return (self.ssid_prefix + unique_salt + suffix)[-32:]
271
272
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700273 def hostap_configure(self, configuration, multi_interface=None):
274 """Build up a hostapd configuration file and start hostapd.
275
276 Also setup a local server if this router supports them.
277
278 @param configuration HosetapConfig object.
279 @param multi_interface bool True iff multiple interfaces allowed.
280
281 """
282 if multi_interface is None and (self.hostapd['configured'] or
283 self.station['configured']):
284 self.deconfig()
285 # Start with the default hostapd config parameters.
286 conf = self.__get_default_hostap_config()
Christopher Wiley3166e432013-08-06 09:53:12 -0700287 conf['ssid'] = (configuration.ssid or
288 self._get_ssid(configuration.ssid_suffix))
Christopher Wiley9b406202013-05-06 14:07:49 -0700289 if configuration.bssid:
290 conf['bssid'] = configuration.bssid
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700291 conf['channel'] = configuration.channel
292 self.hostapd['frequency'] = configuration.frequency
293 conf['hw_mode'] = configuration.hw_mode
294 if configuration.hide_ssid:
295 conf['ignore_broadcast_ssid'] = 1
296 if configuration.is_11n:
297 conf['ieee80211n'] = 1
298 conf['ht_capab'] = ''.join(configuration.n_capabilities)
299 if configuration.wmm_enabled:
300 conf['wmm_enabled'] = 1
301 if configuration.require_ht:
302 conf['require_ht'] = 1
Christopher Wiley9fa7c632013-05-01 11:58:06 -0700303 if configuration.beacon_interval:
304 conf['beacon_int'] = configuration.beacon_interval
Christopher Wileya51258e2013-05-03 13:05:06 -0700305 if configuration.dtim_period:
306 conf['dtim_period'] = configuration.dtim_period
Christopher Wileye1235b62013-05-03 15:09:34 -0700307 if configuration.frag_threshold:
308 conf['fragm_threshold'] = configuration.frag_threshold
Christopher Wileyebdc27d2013-06-28 14:35:41 -0700309 if configuration.pmf_support:
310 conf['ieee80211w'] = configuration.pmf_support
Christopher Wileyb8921c72013-06-13 09:51:47 -0700311 conf.update(configuration.get_security_hostapd_conf())
Christopher Wileya89706d2013-06-12 13:20:58 -0700312
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700313 self.start_hostapd(conf, {})
314 # Configure transmit power
315 tx_power_params = {'interface': conf['interface']}
316 # TODO(wiley) support for setting transmit power
317 self.set_txpower(tx_power_params)
318 if self.force_local_server:
319 self.start_local_server(conf['interface'])
320 self._post_start_hook({})
321 logging.info('AP configured.')
322 self.hostapd['configured'] = True
323
324
Paul Stewartc2b3de82011-03-03 14:45:31 -0800325 def hostap_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700326 """Configure the AP per test requirements.
Sam Leffler6969d1d2010-03-15 16:07:11 -0700327
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700328 @param params dict of site_wifitest parameters.
329
330 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700331 # keep parameter modifications local-only
332 orig_params = params
333 params = params.copy()
334
Paul Stewart45338d22010-10-21 10:57:02 -0700335 multi_interface = 'multi_interface' in params
336 if multi_interface:
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700337 # remove non-hostapd config item from params
Paul Stewart45338d22010-10-21 10:57:02 -0700338 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800339 elif self.hostapd['configured'] or self.station['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700340 self.deconfig()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700341
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700342 local_server = params.pop('local_server', False)
343
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700344 conf = self.__get_default_hostap_config()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800345 tx_power_params = {}
346 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700347
Paul Stewartc2b3de82011-03-03 14:45:31 -0800348 for k, v in params.iteritems():
349 if k == 'ssid':
350 conf['ssid'] = v
351 elif k == 'ssid_suffix':
Christopher Wiley3166e432013-08-06 09:53:12 -0700352 conf['ssid'] = self._get_ssid(v)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800353 elif k == 'channel':
354 freq = int(v)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700355 self.hostapd['frequency'] = freq
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700356
Paul Stewartc2b3de82011-03-03 14:45:31 -0800357 # 2.4GHz
358 if freq <= 2484:
359 # Make sure hw_mode is set
360 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700361 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800362
363 # Freq = 5 * chan + 2407, except channel 14
364 if freq == 2484:
365 conf['channel'] = 14
366 else:
367 conf['channel'] = (freq - 2407) / 5
368 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700369 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800370 # Make sure hw_mode is set
371 conf['hw_mode'] = 'a'
372 # Freq = 5 * chan + 4000
373 if freq < 5000:
374 conf['channel'] = (freq - 4000) / 5
375 # Freq = 5 * chan + 5000
376 else:
377 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700378
Paul Stewartc2b3de82011-03-03 14:45:31 -0800379 elif k == 'country':
380 conf['country_code'] = v
381 elif k == 'dotd':
382 conf['ieee80211d'] = 1
383 elif k == '-dotd':
384 conf['ieee80211d'] = 0
385 elif k == 'mode':
386 if v == '11a':
387 conf['hw_mode'] = 'a'
388 elif v == '11g':
389 conf['hw_mode'] = 'g'
390 elif v == '11b':
391 conf['hw_mode'] = 'b'
392 elif v == '11n':
393 conf['ieee80211n'] = 1
394 elif k == 'bintval':
395 conf['beacon_int'] = v
396 elif k == 'dtimperiod':
397 conf['dtim_period'] = v
398 elif k == 'rtsthreshold':
399 conf['rts_threshold'] = v
400 elif k == 'fragthreshold':
401 conf['fragm_threshold'] = v
402 elif k == 'shortpreamble':
403 conf['preamble'] = 1
404 elif k == 'authmode':
405 if v == "open":
406 conf['auth_algs'] = 1
407 elif v == "shared":
408 conf['auth_algs'] = 2
409 elif k == 'hidessid':
410 conf['ignore_broadcast_ssid'] = 1
411 elif k == 'wme':
412 conf['wmm_enabled'] = 1
413 elif k == '-wme':
414 conf['wmm_enabled'] = 0
415 elif k == 'deftxkey':
416 conf['wep_default_key'] = v
417 elif k == 'ht20':
418 htcaps.add('') # NB: ensure 802.11n setup below
419 conf['wmm_enabled'] = 1
420 elif k == 'ht40':
421 htcaps.add('[HT40-]')
422 htcaps.add('[HT40+]')
423 conf['wmm_enabled'] = 1
Paul Stewartc1df8d62011-04-07 14:28:15 -0700424 elif k in ('ht40+', 'ht40-'):
425 htcaps.add('[%s]' % k.upper())
426 conf['wmm_enabled'] = 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800427 elif k == 'shortgi':
428 htcaps.add('[SHORT-GI-20]')
429 htcaps.add('[SHORT-GI-40]')
430 elif k == 'pureg':
431 pass # TODO(sleffler) need hostapd support
432 elif k == 'puren':
433 pass # TODO(sleffler) need hostapd support
434 elif k == 'protmode':
435 pass # TODO(sleffler) need hostapd support
436 elif k == 'ht':
437 htcaps.add('') # NB: ensure 802.11n setup below
438 elif k == 'htprotmode':
439 pass # TODO(sleffler) need hostapd support
440 elif k == 'rifs':
441 pass # TODO(sleffler) need hostapd support
442 elif k == 'wepmode':
443 pass # NB: meaningless for hostapd; ignore
444 elif k == '-ampdu':
445 pass # TODO(sleffler) need hostapd support
446 elif k == 'txpower':
447 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700448 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800449 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700450
Paul Stewartc2b3de82011-03-03 14:45:31 -0800451 # Aggregate ht_capab.
452 if htcaps:
453 conf['ieee80211n'] = 1
454 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700455
Paul Stewart548cf452012-11-27 17:46:23 -0800456 self.start_hostapd(conf, orig_params)
Paul Stewart1ae854b2011-02-08 15:10:14 -0800457
Paul Stewartc2b3de82011-03-03 14:45:31 -0800458 # Configure transmit power
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700459 tx_power_params['interface'] = conf['interface']
Paul Stewartc2b3de82011-03-03 14:45:31 -0800460 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700461
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700462 if self.force_local_server or local_server is not False:
463 self.start_local_server(conf['interface'])
Sam Leffler6969d1d2010-03-15 16:07:11 -0700464
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700465 self._post_start_hook(orig_params)
466
467 logging.info("AP configured.")
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700468 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700469
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700470
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700471 @staticmethod
472 def ip_addr(netblock, idx):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700473 """Simple IPv4 calculator.
474
475 Takes host address in "IP/bits" notation and returns netmask, broadcast
476 address as well as integer offsets into the address range.
477
478 @param netblock string host address in "IP/bits" notation.
479 @param idx string describing what to return.
480 @return string containing something you hopefully requested.
481
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700482 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700483 addr_str,bits = netblock.split('/')
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700484 addr = map(int, addr_str.split('.'))
485 mask_bits = (-1 << (32-int(bits))) & 0xffffffff
486 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
Paul Stewart5977da92011-06-01 19:14:08 -0700487 if idx == 'local':
488 return addr_str
489 elif idx == 'netmask':
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700490 return '.'.join(map(str, mask))
491 elif idx == 'broadcast':
492 offset = [m ^ 0xff for m in mask]
493 else:
494 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
495 return '.'.join(map(str, [(a & m) + o
496 for a, m, o in zip(addr, mask, offset)]))
497
498
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700499 def ibss_configure(self, config):
500 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700501
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700502 Extract relevant configuration objects from |config| despite not
503 actually being a hostap managed endpoint.
504
505 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700506
507 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700508 if self.station['configured'] or self.hostapd['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700509 self.deconfig()
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700510 interface = self._get_wlanif(config.frequency, self.phytype,
511 config.hw_mode)
Christopher Wiley3166e432013-08-06 09:53:12 -0700512 self.station['conf']['ssid'] = (config.ssid or
513 self._get_ssid(config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800514 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700515 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley3166e432013-08-06 09:53:12 -0700516 self.router.run('%s dev %s ibss join %s %d' % (
517 self.cmd_iw, interface, self.station['conf']['ssid'],
518 config.frequency))
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700519 # Always start a local server.
520 self.start_local_server(interface)
521 # Remember that this interface is up.
Paul Stewartc2b3de82011-03-03 14:45:31 -0800522 self.station['configured'] = True
523 self.station['interface'] = interface
524
525
Paul Stewart2bd823b2012-11-21 15:03:37 -0800526 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700527 """Get the local server address for an interface.
528
529 When we multiple local servers, we give them static IP addresses
530 like 192.158.*.254.
531
532 @param index int describing which local server this is for.
533
534 """
Paul Stewart2bd823b2012-11-21 15:03:37 -0800535 return '%d.%d.%d.%d' % (192, 168, index, 254)
536
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700537
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700538 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700539 """Start a local server on an interface.
540
541 @param interface string (e.g. wlan0)
542
543 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700544 logging.info("Starting up local server...")
545
546 if len(self.local_servers) >= 256:
547 raise error.TestFail('Exhausted available local servers')
548
Paul Stewart2bd823b2012-11-21 15:03:37 -0800549 netblock = '%s/24' % self.local_server_address(len(self.local_servers))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700550
551 params = {}
552 params['netblock'] = netblock
553 params['subnet'] = self.ip_addr(netblock, 0)
554 params['netmask'] = self.ip_addr(netblock, 'netmask')
555 params['dhcp_range'] = ' '.join(
556 (self.ip_addr(netblock, self.dhcp_low),
557 self.ip_addr(netblock, self.dhcp_high)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700558 params['interface'] = interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700559
560 params['ip_params'] = ("%s broadcast %s dev %s" %
561 (netblock,
562 self.ip_addr(netblock, 'broadcast'),
563 interface))
564 self.local_servers.append(params)
565
566 self.router.run("%s addr flush %s" %
567 (self.cmd_ip, interface))
568 self.router.run("%s addr add %s" %
569 (self.cmd_ip, params['ip_params']))
570 self.router.run("%s link set %s up" %
571 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800572 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700573
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700574
Paul Stewart548cf452012-11-27 17:46:23 -0800575 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700576 """Start a dhcp server on an interface.
577
578 @param interface string (e.g. wlan0)
579
580 """
Paul Stewart326badb2012-12-18 14:18:54 -0800581 conf_file = self.dhcpd_conf % interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700582 dhcp_conf = '\n'.join(map(
583 lambda server_conf: \
584 "subnet %(subnet)s netmask %(netmask)s {\n" \
585 " range %(dhcp_range)s;\n" \
586 "}" % server_conf,
587 self.local_servers))
588 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
Paul Stewart326badb2012-12-18 14:18:54 -0800589 (conf_file,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700590 '\n'.join(('ddns-update-style none;', dhcp_conf))))
591 self.router.run("touch %s" % self.dhcpd_leases)
592
593 self.router.run("pkill dhcpd >/dev/null 2>&1", ignore_status=True)
594 self.router.run("%s -q -cf %s -lf %s" %
Paul Stewart326badb2012-12-18 14:18:54 -0800595 (self.cmd_dhcpd, conf_file, self.dhcpd_leases))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700596
597
Paul Stewart326badb2012-12-18 14:18:54 -0800598 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700599 """Stop a dhcp server on the router.
600
601 @param instance string instance to kill.
602
603 """
Paul Stewart326badb2012-12-18 14:18:54 -0800604 self._kill_process_instance('dhcpd', instance, 0)
605
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700606
Paul Stewart548cf452012-11-27 17:46:23 -0800607 def stop_dhcp_servers(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700608 """Stop all dhcp servers on the router."""
Paul Stewart326badb2012-12-18 14:18:54 -0800609 self.stop_dhcp_server(None)
Paul Stewart548cf452012-11-27 17:46:23 -0800610
611
Paul Stewartc2b3de82011-03-03 14:45:31 -0800612 def config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700613 """Configure an AP based on site_wifitest parameters.
614
615 @param params dict of site_wifitest parameters.
616
617 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800618 if self.apmode:
619 self.hostap_config(params)
620 else:
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700621 config = hostap_config.HostapConfig(
622 ssid=self.get_ssid(),
623 frequency=int(params.get('channel', None)))
624 self.ibss_configure(config)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800625
626
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700627 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700628 """Return IP address on the WiFi subnet of a local server on the router.
629
630 If no local servers are configured (e.g. for an RSPro), a TestFail will
631 be raised.
632
633 @param ap_num int which local server to get an address from.
634
635 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700636 if self.local_servers:
637 return self.ip_addr(self.local_servers[ap_num]['netblock'],
638 'local')
639 else:
640 raise error.TestFail("No IP address assigned")
Paul Stewart5977da92011-06-01 19:14:08 -0700641
642
Paul Stewart17350be2012-12-14 13:34:54 -0800643 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700644 """Return the MAC address of an AP in the test.
645
646 @param ap_num int index of local server to read the MAC address from.
647 @return string MAC address like 00:11:22:33:44:55.
648
649 """
Paul Stewart17350be2012-12-14 13:34:54 -0800650 instance = self.hostapd_instances[ap_num]
651 interface = instance['interface']
652 result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
653 # Example response:
654 # 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
655 # link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
656 # inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
657 # inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
658 # we want the MAC address after the "link/ether" above.
659 parts = result.stdout.split(' ')
660 return parts[parts.index('link/ether') + 1]
661
662
Christopher Wileyd89b5282013-04-10 15:21:26 -0700663 def deconfig(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700664 """De-configure the AP (will also bring wlan down).
Sam Leffler6969d1d2010-03-15 16:07:11 -0700665
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700666 @param params dict of parameters from site_wifitest.
667
668 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800669 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700670 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700671
Paul Stewartc2b3de82011-03-03 14:45:31 -0800672 if self.hostapd['configured']:
Paul Stewart326badb2012-12-18 14:18:54 -0800673 local_servers = []
Paul Stewart21737812012-12-06 11:03:32 -0800674 if 'instance' in params:
675 instances = [ self.hostapd_instances.pop(params['instance']) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800676 for server in self.local_servers:
677 if server['interface'] == instances[0]['interface']:
678 local_servers = [server]
679 self.local_servers.remove(server)
680 break
Paul Stewart21737812012-12-06 11:03:32 -0800681 else:
682 instances = self.hostapd_instances
683 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800684 local_servers = self.local_servers
685 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700686
Paul Stewart21737812012-12-06 11:03:32 -0800687 for instance in instances:
688 if 'silent' in params:
689 # Deconfigure without notifying DUT. Remove the interface
690 # hostapd uses to send beacon and DEAUTH packets.
691 self._remove_interface(instance['interface'], True)
692
Paul Stewart326badb2012-12-18 14:18:54 -0800693 self.kill_hostapd_instance(instance['conf_file'])
Paul Stewart548cf452012-11-27 17:46:23 -0800694 self.router.get_file(instance['log_file'],
695 'debug/hostapd_router_%d_%s.log' %
696 (self.hostapd['log_count'],
697 instance['interface']))
698 self._release_wlanif(instance['interface'])
699# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance)
Paul Stewartf854d2e2011-05-04 13:19:18 -0700700 self.hostapd['log_count'] += 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800701 if self.station['configured']:
Christopher Wiley05262d62013-04-17 17:53:59 -0700702 local_servers = self.local_servers
703 self.local_servers = []
Paul Stewartc2b3de82011-03-03 14:45:31 -0800704 if self.station['type'] == 'ibss':
705 self.router.run("%s dev %s ibss leave" %
706 (self.cmd_iw, self.station['interface']))
707 else:
708 self.router.run("%s dev %s disconnect" %
709 (self.cmd_iw, self.station['interface']))
710 self.router.run("%s link set %s down" % (self.cmd_ip,
711 self.station['interface']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700712
Paul Stewart326badb2012-12-18 14:18:54 -0800713 for server in local_servers:
714 self.stop_dhcp_server(server['interface'])
715 self.router.run("%s addr del %s" %
716 (self.cmd_ip, server['ip_params']),
717 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700718
719 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800720 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700721
722
Paul Stewart17350be2012-12-14 13:34:54 -0800723 def verify_pmksa_auth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700724 """Verify that the PMKSA auth was cached on a hostapd instance.
725
726 @param params dict with optional key 'instance' (defaults to 0).
727
728 """
Paul Stewart17350be2012-12-14 13:34:54 -0800729 instance_num = params.get('instance', 0)
730 instance = self.hostapd_instances[instance_num]
731 pmksa_match = 'PMK from PMKSA cache - skip IEEE 802.1X.EAP'
732 self.router.run('grep -q "%s" %s' % (pmksa_match, instance['log_file']))
733
734
Paul Stewart7cb1f062010-06-10 15:46:20 -0700735 def get_ssid(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700736 """@return string ssid for the network stemming from this router."""
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700737 if self.hostapd['configured']:
738 return self.hostapd['conf']['ssid']
739
Christopher Wiley3166e432013-08-06 09:53:12 -0700740 if not 'ssid' in self.station['conf']:
741 raise error.TestFail('Requested ssid of an unconfigured AP.')
742
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700743 return self.station['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700744
745
746 def set_txpower(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700747 """Set the transmission power for an interface.
748
749 Assumes that we want to refer to the first hostapd instance unless
750 'interface' is defined in params. Sets the transmission power to
751 'auto' if 'power' is not defined in params.
752
753 @param params dict of parameters as described above.
754
755 """
Paul Stewart548cf452012-11-27 17:46:23 -0800756 interface = params.get('interface',
757 self.hostapd_instances[0]['interface'])
758 power = params.get('power', 'auto')
Paul Stewart98022e22010-10-22 10:33:14 -0700759 self.router.run("%s dev %s set txpower %s" %
Paul Stewart548cf452012-11-27 17:46:23 -0800760 (self.cmd_iw, interface, power))
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700761
762
763 def deauth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700764 """Deauthenticates a client described in params.
765
766 @param params dict containing a key 'client'.
767
768 """
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700769 self.router.run('%s -p%s deauthenticate %s' %
770 (self.cmd_hostapd_cli,
771 self.hostapd['conf']['ctrl_interface'],
772 params['client']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700773
774
Paul Stewart51b0f382013-06-12 09:03:02 -0700775 def send_management_frame(self, frame_type, instance=0):
776 """Injects a management frame into an active hostapd session.
777
778 @param frame_type string the type of frame to send.
779 @param instance int indicating which hostapd instance to inject into.
780
781 """
782 hostap_interface = self.hostapd_instances[instance]['interface']
783 interface = self._get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
784 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
785 self.router.run('%s %s %s' %
786 (self.cmd_send_management_frame, interface, frame_type))
787 self._release_wlanif(interface)
788
789
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700790 def _pre_config_hook(self, config):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700791 """Hook for subclasses.
792
793 Run after gathering configuration parameters,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700794 but before writing parameters to config file.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700795
796 @param config dict containing hostapd config parameters.
797
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700798 """
799 pass
800
801
802 def _pre_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700803 """Hook for subclasses.
804
805 Run after generating hostapd config file, but before starting hostapd.
806
807 @param params dict parameters from site_wifitest.
808
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700809 """
810 pass
811
812
813 def _post_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700814 """Hook for subclasses run after starting hostapd.
815
816 @param params dict parameters from site_wifitest.
817
818 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700819 pass