blob: 1a3c4e1ba69ce9e05e6537a0ea24a4b6689a67af [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
6import re
7
Paul Stewartc9628b32010-08-11 13:03:51 -07008from autotest_lib.client.common_lib import error
Paul Stewart2ee7fdf2011-05-19 16:29:23 -07009from autotest_lib.server import site_linux_system
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070010from autotest_lib.server.cros import wifi_test_utils
Christopher Wiley99d42c92013-07-09 16:40:16 -070011from autotest_lib.server.cros.network import hostap_config
Sam Leffler19bb0a72010-04-12 08:51:08 -070012
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070013def isLinuxRouter(host):
14 """Check if host is a linux router.
15
16 @param host Host object representing the remote machine.
17 @return True iff remote system is a Linux system.
18
19 """
20 router_uname = host.run('uname').stdout
Sam Leffler19bb0a72010-04-12 08:51:08 -070021 return re.search('Linux', router_uname)
22
Christopher Wiley1febd6a2013-06-03 13:59:48 -070023
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070024class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070025 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070026
27 This class implements test methods/steps that communicate with a
28 router implemented with Linux/mac80211. The router must
29 be pre-configured to enable ssh access and have a mac80211-based
30 wireless device. We also assume hostapd 0.7.x and iw are present
31 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070032
Sam Leffler6969d1d2010-03-15 16:07:11 -070033 """
34
35
Paul Stewart51b0f382013-06-12 09:03:02 -070036 def get_capabilities(self):
37 """@return iterable object of AP capabilities for this system."""
38 caps = set()
39 try:
40 self.cmd_send_management_frame = wifi_test_utils.must_be_installed(
41 self.router, '/usr/bin/send_management_frame')
42 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME)
43 except error.TestFail:
44 pass
45 return super(LinuxRouter, self).get_capabilities().union(caps)
46
47
Sam Leffler6969d1d2010-03-15 16:07:11 -070048 def __init__(self, host, params, defssid):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070049 """Build a LinuxRouter.
50
51 @param host Host object representing the remote machine.
52 @param params dict of settings from site_wifitest based tests.
53 @param defssid string default SSID for networks created on this router.
54
55 """
56 site_linux_system.LinuxSystem.__init__(self, host, params, 'router')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070057 self._remove_interfaces()
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070058
Wade Guthrie24d1e312012-04-24 16:53:40 -070059 # Router host.
60 self.router = host
61
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070062 self.cmd_hostapd = wifi_test_utils.must_be_installed(
63 host, params.get('cmd_hostapd', '/usr/sbin/hostapd'))
64 self.cmd_hostapd_cli = params.get('cmd_hostapd_cli',
65 '/usr/sbin/hostapd_cli')
66 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
67 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070068
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070069 # hostapd configuration persists throughout the test, subsequent
70 # 'config' commands only modify it.
Paul Stewart7cb1f062010-06-10 15:46:20 -070071 self.defssid = defssid
Paul Stewartd5aafa92013-03-24 19:06:14 -070072 self.default_config = {
73 'ssid': defssid,
74 'hw_mode': 'g',
75 'ctrl_interface': '/tmp/hostapd-test.control',
76 'logger_syslog': '-1',
77 'logger_syslog_level': '0'
78 }
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070079 self.hostapd = {
80 'configured': False,
Paul Stewart326badb2012-12-18 14:18:54 -080081 'config_file': "/tmp/hostapd-test-%s.conf",
82 'log_file': "/tmp/hostapd-test-%s.log",
Paul Stewartf854d2e2011-05-04 13:19:18 -070083 'log_count': 0,
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070084 'driver': "nl80211",
Paul Stewartd5aafa92013-03-24 19:06:14 -070085 'conf': self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070086 }
Paul Stewartc2b3de82011-03-03 14:45:31 -080087 self.station = {
88 'configured': False,
89 'conf': {
90 'ssid': defssid,
Paul Stewartf05d7fd2011-04-06 16:19:37 -070091 },
Paul Stewartc2b3de82011-03-03 14:45:31 -080092 }
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070093 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -080094 self.hostapd_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070095 self.force_local_server = "force_local_server" in params
96 self.dhcp_low = 1
97 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -070098
Paul Stewart548cf452012-11-27 17:46:23 -080099 # Kill hostapd and dhcp server if already running.
Thieu Le7b23a542012-01-27 15:54:48 -0800100 self.kill_hostapd()
Paul Stewart548cf452012-11-27 17:46:23 -0800101 self.stop_dhcp_servers()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700102
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700103 # Place us in the US by default
104 self.router.run("%s reg set US" % self.cmd_iw)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700105
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700106
Sam Leffler6969d1d2010-03-15 16:07:11 -0700107 def create(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700108 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700109
110 @param params dict containing the device type under key 'type'.
111
112 """
113 self.create_wifi_device(params['type'])
114
115
116 def create_wifi_device(self, device_type='hostap'):
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 Defaults to creating a hostap managed device.
120
121 @param device_type string device type.
122
123 """
Sam Leffler6969d1d2010-03-15 16:07:11 -0700124 #
125 # AP mode is handled entirely by hostapd so we only
126 # have to setup others (mapping the bsd type to what
127 # iw wants)
128 #
129 # map from bsd types to iw types
Christopher Wiley14796b32013-04-03 14:53:33 -0700130 self.apmode = device_type in ('ap', 'hostap')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800131 if not self.apmode:
Christopher Wiley14796b32013-04-03 14:53:33 -0700132 self.station['type'] = device_type
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700133 self.phytype = {
Christopher Wiley14796b32013-04-03 14:53:33 -0700134 'sta' : 'managed',
135 'monitor' : 'monitor',
136 'adhoc' : 'adhoc',
137 'ibss' : 'ibss',
138 'ap' : 'managed', # NB: handled by hostapd
139 'hostap' : 'managed', # NB: handled by hostapd
140 'mesh' : 'mesh',
141 'wds' : 'wds',
142 }[device_type]
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700143
Sam Leffler6969d1d2010-03-15 16:07:11 -0700144
Christopher Wileyd89b5282013-04-10 15:21:26 -0700145 def destroy(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700146 """Destroy a previously created device.
147
148 @param params dict of site_wifitest parameters.
149
150 """
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700151 self.deconfig(params)
Paul Stewartd5aafa92013-03-24 19:06:14 -0700152 self.hostapd['conf'] = self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700153
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700154
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700155 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700156 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700157 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700158
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700159
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700160 def cleanup(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700161 """Clean up any resources in use.
162
163 @param params dict of site_wifitest parameters.
164
165 """
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700166 # For linux, this is a no-op
167 pass
168
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700169
Paul Stewart548cf452012-11-27 17:46:23 -0800170 def start_hostapd(self, conf, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700171 """Start a hostapd instance described by conf.
172
173 @param conf dict of hostapd configuration parameters.
174 @param params dict of site_wifitest parameters.
175
176 """
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700177 logging.info('Starting hostapd with parameters: %r', conf)
Paul Stewart548cf452012-11-27 17:46:23 -0800178 # Figure out the correct interface.
Paul Stewart326badb2012-12-18 14:18:54 -0800179 interface = self._get_wlanif(self.hostapd['frequency'],
180 self.phytype,
181 mode=conf.get('hw_mode', 'b'))
182
183 conf_file = self.hostapd['config_file'] % interface
184 log_file = self.hostapd['log_file'] % interface
185 conf['interface'] = interface
Paul Stewart548cf452012-11-27 17:46:23 -0800186
187 # Generate hostapd.conf.
188 self._pre_config_hook(conf)
189 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
190 (conf_file, '\n'.join(
191 "%s=%s" % kv for kv in conf.iteritems())))
192
193 # Run hostapd.
194 logging.info("Starting hostapd...")
195 self._pre_start_hook(params)
196 self.router.run("%s -dd %s &> %s &" %
197 (self.cmd_hostapd, conf_file, log_file))
198
199 self.hostapd_instances.append({
200 'conf_file': conf_file,
201 'log_file': log_file,
Paul Stewart326badb2012-12-18 14:18:54 -0800202 'interface': interface
Paul Stewart548cf452012-11-27 17:46:23 -0800203 })
204
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700205
Paul Stewart326badb2012-12-18 14:18:54 -0800206 def _kill_process_instance(self, process, instance=None, wait=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700207 """Kill a process on the router.
208
Paul Stewart326badb2012-12-18 14:18:54 -0800209 Kills program named |process|, optionally only a specific
210 |instance|. If |wait| is specified, we makes sure |process| exits
211 before returning.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700212
213 @param process string name of process to kill.
214 @param instance string instance of process to kill.
215 @param wait int timeout in seconds to wait for.
216
Thieu Le7b23a542012-01-27 15:54:48 -0800217 """
Paul Stewart21737812012-12-06 11:03:32 -0800218 if instance:
Paul Stewart326badb2012-12-18 14:18:54 -0800219 search_arg = '-f "%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800220 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800221 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800222
Paul Stewart326badb2012-12-18 14:18:54 -0800223 cmd = "pkill %s >/dev/null 2>&1" % search_arg
224
225 if wait:
226 cmd += (" && while pgrep %s &> /dev/null; do sleep 1; done" %
227 search_arg)
228 self.router.run(cmd, timeout=wait, ignore_status=True)
229 else:
230 self.router.run(cmd, ignore_status=True)
231
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700232
Paul Stewart326badb2012-12-18 14:18:54 -0800233 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700234 """Kills a hostapd instance.
235
236 @param instance string instance to kill.
237
238 """
Paul Stewart326badb2012-12-18 14:18:54 -0800239 self._kill_process_instance('hostapd', instance, 30)
Thieu Le7b23a542012-01-27 15:54:48 -0800240
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700241
Paul Stewart21737812012-12-06 11:03:32 -0800242 def kill_hostapd(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700243 """Kill all hostapd instances."""
Paul Stewart21737812012-12-06 11:03:32 -0800244 self.kill_hostapd_instance(None)
245
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700246
247 def __get_default_hostap_config(self):
248 """@return dict of default options for hostapd."""
249 conf = self.hostapd['conf']
250 # default RTS and frag threshold to ``off''
251 conf['rts_threshold'] = '2347'
252 conf['fragm_threshold'] = '2346'
253 conf['driver'] = self.hostapd['driver']
254 return conf
255
256
257 def hostap_configure(self, configuration, multi_interface=None):
258 """Build up a hostapd configuration file and start hostapd.
259
260 Also setup a local server if this router supports them.
261
262 @param configuration HosetapConfig object.
263 @param multi_interface bool True iff multiple interfaces allowed.
264
265 """
266 if multi_interface is None and (self.hostapd['configured'] or
267 self.station['configured']):
268 self.deconfig()
269 # Start with the default hostapd config parameters.
270 conf = self.__get_default_hostap_config()
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700271 conf['ssid'] = configuration.get_ssid(self.defssid)
Christopher Wiley9b406202013-05-06 14:07:49 -0700272 if configuration.bssid:
273 conf['bssid'] = configuration.bssid
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700274 conf['channel'] = configuration.channel
275 self.hostapd['frequency'] = configuration.frequency
276 conf['hw_mode'] = configuration.hw_mode
277 if configuration.hide_ssid:
278 conf['ignore_broadcast_ssid'] = 1
279 if configuration.is_11n:
280 conf['ieee80211n'] = 1
281 conf['ht_capab'] = ''.join(configuration.n_capabilities)
282 if configuration.wmm_enabled:
283 conf['wmm_enabled'] = 1
284 if configuration.require_ht:
285 conf['require_ht'] = 1
Christopher Wiley9fa7c632013-05-01 11:58:06 -0700286 if configuration.beacon_interval:
287 conf['beacon_int'] = configuration.beacon_interval
Christopher Wileya51258e2013-05-03 13:05:06 -0700288 if configuration.dtim_period:
289 conf['dtim_period'] = configuration.dtim_period
Christopher Wileye1235b62013-05-03 15:09:34 -0700290 if configuration.frag_threshold:
291 conf['fragm_threshold'] = configuration.frag_threshold
Christopher Wileyebdc27d2013-06-28 14:35:41 -0700292 if configuration.pmf_support:
293 conf['ieee80211w'] = configuration.pmf_support
Christopher Wileyb8921c72013-06-13 09:51:47 -0700294 conf.update(configuration.get_security_hostapd_conf())
Christopher Wileya89706d2013-06-12 13:20:58 -0700295
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700296 self.start_hostapd(conf, {})
297 # Configure transmit power
298 tx_power_params = {'interface': conf['interface']}
299 # TODO(wiley) support for setting transmit power
300 self.set_txpower(tx_power_params)
301 if self.force_local_server:
302 self.start_local_server(conf['interface'])
303 self._post_start_hook({})
304 logging.info('AP configured.')
305 self.hostapd['configured'] = True
306
307
Paul Stewartc2b3de82011-03-03 14:45:31 -0800308 def hostap_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700309 """Configure the AP per test requirements.
Sam Leffler6969d1d2010-03-15 16:07:11 -0700310
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700311 @param params dict of site_wifitest parameters.
312
313 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700314 # keep parameter modifications local-only
315 orig_params = params
316 params = params.copy()
317
Paul Stewart45338d22010-10-21 10:57:02 -0700318 multi_interface = 'multi_interface' in params
319 if multi_interface:
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700320 # remove non-hostapd config item from params
Paul Stewart45338d22010-10-21 10:57:02 -0700321 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800322 elif self.hostapd['configured'] or self.station['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700323 self.deconfig()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700324
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700325 local_server = params.pop('local_server', False)
326
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700327 conf = self.__get_default_hostap_config()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800328 tx_power_params = {}
329 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700330
Paul Stewartc2b3de82011-03-03 14:45:31 -0800331 for k, v in params.iteritems():
332 if k == 'ssid':
333 conf['ssid'] = v
334 elif k == 'ssid_suffix':
Paul Stewart05cebab2012-05-29 11:58:17 -0700335 conf['ssid'] = self.defssid[:(32-len(v))] + v
Paul Stewartc2b3de82011-03-03 14:45:31 -0800336 elif k == 'channel':
337 freq = int(v)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700338 self.hostapd['frequency'] = freq
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700339
Paul Stewartc2b3de82011-03-03 14:45:31 -0800340 # 2.4GHz
341 if freq <= 2484:
342 # Make sure hw_mode is set
343 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700344 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800345
346 # Freq = 5 * chan + 2407, except channel 14
347 if freq == 2484:
348 conf['channel'] = 14
349 else:
350 conf['channel'] = (freq - 2407) / 5
351 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700352 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800353 # Make sure hw_mode is set
354 conf['hw_mode'] = 'a'
355 # Freq = 5 * chan + 4000
356 if freq < 5000:
357 conf['channel'] = (freq - 4000) / 5
358 # Freq = 5 * chan + 5000
359 else:
360 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700361
Paul Stewartc2b3de82011-03-03 14:45:31 -0800362 elif k == 'country':
363 conf['country_code'] = v
364 elif k == 'dotd':
365 conf['ieee80211d'] = 1
366 elif k == '-dotd':
367 conf['ieee80211d'] = 0
368 elif k == 'mode':
369 if v == '11a':
370 conf['hw_mode'] = 'a'
371 elif v == '11g':
372 conf['hw_mode'] = 'g'
373 elif v == '11b':
374 conf['hw_mode'] = 'b'
375 elif v == '11n':
376 conf['ieee80211n'] = 1
377 elif k == 'bintval':
378 conf['beacon_int'] = v
379 elif k == 'dtimperiod':
380 conf['dtim_period'] = v
381 elif k == 'rtsthreshold':
382 conf['rts_threshold'] = v
383 elif k == 'fragthreshold':
384 conf['fragm_threshold'] = v
385 elif k == 'shortpreamble':
386 conf['preamble'] = 1
387 elif k == 'authmode':
388 if v == "open":
389 conf['auth_algs'] = 1
390 elif v == "shared":
391 conf['auth_algs'] = 2
392 elif k == 'hidessid':
393 conf['ignore_broadcast_ssid'] = 1
394 elif k == 'wme':
395 conf['wmm_enabled'] = 1
396 elif k == '-wme':
397 conf['wmm_enabled'] = 0
398 elif k == 'deftxkey':
399 conf['wep_default_key'] = v
400 elif k == 'ht20':
401 htcaps.add('') # NB: ensure 802.11n setup below
402 conf['wmm_enabled'] = 1
403 elif k == 'ht40':
404 htcaps.add('[HT40-]')
405 htcaps.add('[HT40+]')
406 conf['wmm_enabled'] = 1
Paul Stewartc1df8d62011-04-07 14:28:15 -0700407 elif k in ('ht40+', 'ht40-'):
408 htcaps.add('[%s]' % k.upper())
409 conf['wmm_enabled'] = 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800410 elif k == 'shortgi':
411 htcaps.add('[SHORT-GI-20]')
412 htcaps.add('[SHORT-GI-40]')
413 elif k == 'pureg':
414 pass # TODO(sleffler) need hostapd support
415 elif k == 'puren':
416 pass # TODO(sleffler) need hostapd support
417 elif k == 'protmode':
418 pass # TODO(sleffler) need hostapd support
419 elif k == 'ht':
420 htcaps.add('') # NB: ensure 802.11n setup below
421 elif k == 'htprotmode':
422 pass # TODO(sleffler) need hostapd support
423 elif k == 'rifs':
424 pass # TODO(sleffler) need hostapd support
425 elif k == 'wepmode':
426 pass # NB: meaningless for hostapd; ignore
427 elif k == '-ampdu':
428 pass # TODO(sleffler) need hostapd support
429 elif k == 'txpower':
430 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700431 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800432 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700433
Paul Stewartc2b3de82011-03-03 14:45:31 -0800434 # Aggregate ht_capab.
435 if htcaps:
436 conf['ieee80211n'] = 1
437 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700438
Paul Stewart548cf452012-11-27 17:46:23 -0800439 self.start_hostapd(conf, orig_params)
Paul Stewart1ae854b2011-02-08 15:10:14 -0800440
Paul Stewartc2b3de82011-03-03 14:45:31 -0800441 # Configure transmit power
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700442 tx_power_params['interface'] = conf['interface']
Paul Stewartc2b3de82011-03-03 14:45:31 -0800443 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700444
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700445 if self.force_local_server or local_server is not False:
446 self.start_local_server(conf['interface'])
Sam Leffler6969d1d2010-03-15 16:07:11 -0700447
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700448 self._post_start_hook(orig_params)
449
450 logging.info("AP configured.")
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700451 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700452
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700453
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700454 @staticmethod
455 def ip_addr(netblock, idx):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700456 """Simple IPv4 calculator.
457
458 Takes host address in "IP/bits" notation and returns netmask, broadcast
459 address as well as integer offsets into the address range.
460
461 @param netblock string host address in "IP/bits" notation.
462 @param idx string describing what to return.
463 @return string containing something you hopefully requested.
464
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700465 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700466 addr_str,bits = netblock.split('/')
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700467 addr = map(int, addr_str.split('.'))
468 mask_bits = (-1 << (32-int(bits))) & 0xffffffff
469 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
Paul Stewart5977da92011-06-01 19:14:08 -0700470 if idx == 'local':
471 return addr_str
472 elif idx == 'netmask':
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700473 return '.'.join(map(str, mask))
474 elif idx == 'broadcast':
475 offset = [m ^ 0xff for m in mask]
476 else:
477 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
478 return '.'.join(map(str, [(a & m) + o
479 for a, m, o in zip(addr, mask, offset)]))
480
481
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700482 def ibss_configure(self, config):
483 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700484
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700485 Extract relevant configuration objects from |config| despite not
486 actually being a hostap managed endpoint.
487
488 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700489
490 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700491 if self.station['configured'] or self.hostapd['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700492 self.deconfig()
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700493 interface = self._get_wlanif(config.frequency, self.phytype,
494 config.hw_mode)
495 ssid = config.get_ssid(self.defssid)
496 self.station['conf']['ssid'] = ssid
Paul Stewartc2b3de82011-03-03 14:45:31 -0800497 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700498 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
499 self.router.run('%s dev %s ibss join %s %d' % (self.cmd_iw, interface,
500 ssid, config.frequency))
501 # Always start a local server.
502 self.start_local_server(interface)
503 # Remember that this interface is up.
Paul Stewartc2b3de82011-03-03 14:45:31 -0800504 self.station['configured'] = True
505 self.station['interface'] = interface
506
507
Paul Stewart2bd823b2012-11-21 15:03:37 -0800508 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700509 """Get the local server address for an interface.
510
511 When we multiple local servers, we give them static IP addresses
512 like 192.158.*.254.
513
514 @param index int describing which local server this is for.
515
516 """
Paul Stewart2bd823b2012-11-21 15:03:37 -0800517 return '%d.%d.%d.%d' % (192, 168, index, 254)
518
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700519
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700520 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700521 """Start a local server on an interface.
522
523 @param interface string (e.g. wlan0)
524
525 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700526 logging.info("Starting up local server...")
527
528 if len(self.local_servers) >= 256:
529 raise error.TestFail('Exhausted available local servers')
530
Paul Stewart2bd823b2012-11-21 15:03:37 -0800531 netblock = '%s/24' % self.local_server_address(len(self.local_servers))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700532
533 params = {}
534 params['netblock'] = netblock
535 params['subnet'] = self.ip_addr(netblock, 0)
536 params['netmask'] = self.ip_addr(netblock, 'netmask')
537 params['dhcp_range'] = ' '.join(
538 (self.ip_addr(netblock, self.dhcp_low),
539 self.ip_addr(netblock, self.dhcp_high)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700540 params['interface'] = interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700541
542 params['ip_params'] = ("%s broadcast %s dev %s" %
543 (netblock,
544 self.ip_addr(netblock, 'broadcast'),
545 interface))
546 self.local_servers.append(params)
547
548 self.router.run("%s addr flush %s" %
549 (self.cmd_ip, interface))
550 self.router.run("%s addr add %s" %
551 (self.cmd_ip, params['ip_params']))
552 self.router.run("%s link set %s up" %
553 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800554 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700555
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700556
Paul Stewart548cf452012-11-27 17:46:23 -0800557 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700558 """Start a dhcp server on an interface.
559
560 @param interface string (e.g. wlan0)
561
562 """
Paul Stewart326badb2012-12-18 14:18:54 -0800563 conf_file = self.dhcpd_conf % interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700564 dhcp_conf = '\n'.join(map(
565 lambda server_conf: \
566 "subnet %(subnet)s netmask %(netmask)s {\n" \
567 " range %(dhcp_range)s;\n" \
568 "}" % server_conf,
569 self.local_servers))
570 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
Paul Stewart326badb2012-12-18 14:18:54 -0800571 (conf_file,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700572 '\n'.join(('ddns-update-style none;', dhcp_conf))))
573 self.router.run("touch %s" % self.dhcpd_leases)
574
575 self.router.run("pkill dhcpd >/dev/null 2>&1", ignore_status=True)
576 self.router.run("%s -q -cf %s -lf %s" %
Paul Stewart326badb2012-12-18 14:18:54 -0800577 (self.cmd_dhcpd, conf_file, self.dhcpd_leases))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700578
579
Paul Stewart326badb2012-12-18 14:18:54 -0800580 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700581 """Stop a dhcp server on the router.
582
583 @param instance string instance to kill.
584
585 """
Paul Stewart326badb2012-12-18 14:18:54 -0800586 self._kill_process_instance('dhcpd', instance, 0)
587
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700588
Paul Stewart548cf452012-11-27 17:46:23 -0800589 def stop_dhcp_servers(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700590 """Stop all dhcp servers on the router."""
Paul Stewart326badb2012-12-18 14:18:54 -0800591 self.stop_dhcp_server(None)
Paul Stewart548cf452012-11-27 17:46:23 -0800592
593
Paul Stewartc2b3de82011-03-03 14:45:31 -0800594 def config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700595 """Configure an AP based on site_wifitest parameters.
596
597 @param params dict of site_wifitest parameters.
598
599 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800600 if self.apmode:
601 self.hostap_config(params)
602 else:
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700603 config = hostap_config.HostapConfig(
604 ssid=self.get_ssid(),
605 frequency=int(params.get('channel', None)))
606 self.ibss_configure(config)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800607
608
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700609 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700610 """Return IP address on the WiFi subnet of a local server on the router.
611
612 If no local servers are configured (e.g. for an RSPro), a TestFail will
613 be raised.
614
615 @param ap_num int which local server to get an address from.
616
617 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700618 if self.local_servers:
619 return self.ip_addr(self.local_servers[ap_num]['netblock'],
620 'local')
621 else:
622 raise error.TestFail("No IP address assigned")
Paul Stewart5977da92011-06-01 19:14:08 -0700623
624
Paul Stewart17350be2012-12-14 13:34:54 -0800625 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700626 """Return the MAC address of an AP in the test.
627
628 @param ap_num int index of local server to read the MAC address from.
629 @return string MAC address like 00:11:22:33:44:55.
630
631 """
Paul Stewart17350be2012-12-14 13:34:54 -0800632 instance = self.hostapd_instances[ap_num]
633 interface = instance['interface']
634 result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
635 # Example response:
636 # 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
637 # link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
638 # inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
639 # inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
640 # we want the MAC address after the "link/ether" above.
641 parts = result.stdout.split(' ')
642 return parts[parts.index('link/ether') + 1]
643
644
Christopher Wileyd89b5282013-04-10 15:21:26 -0700645 def deconfig(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700646 """De-configure the AP (will also bring wlan down).
Sam Leffler6969d1d2010-03-15 16:07:11 -0700647
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700648 @param params dict of parameters from site_wifitest.
649
650 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800651 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700652 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700653
Paul Stewartc2b3de82011-03-03 14:45:31 -0800654 if self.hostapd['configured']:
Paul Stewart326badb2012-12-18 14:18:54 -0800655 local_servers = []
Paul Stewart21737812012-12-06 11:03:32 -0800656 if 'instance' in params:
657 instances = [ self.hostapd_instances.pop(params['instance']) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800658 for server in self.local_servers:
659 if server['interface'] == instances[0]['interface']:
660 local_servers = [server]
661 self.local_servers.remove(server)
662 break
Paul Stewart21737812012-12-06 11:03:32 -0800663 else:
664 instances = self.hostapd_instances
665 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800666 local_servers = self.local_servers
667 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700668
Paul Stewart21737812012-12-06 11:03:32 -0800669 for instance in instances:
670 if 'silent' in params:
671 # Deconfigure without notifying DUT. Remove the interface
672 # hostapd uses to send beacon and DEAUTH packets.
673 self._remove_interface(instance['interface'], True)
674
Paul Stewart326badb2012-12-18 14:18:54 -0800675 self.kill_hostapd_instance(instance['conf_file'])
Paul Stewart548cf452012-11-27 17:46:23 -0800676 self.router.get_file(instance['log_file'],
677 'debug/hostapd_router_%d_%s.log' %
678 (self.hostapd['log_count'],
679 instance['interface']))
680 self._release_wlanif(instance['interface'])
681# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance)
Paul Stewartf854d2e2011-05-04 13:19:18 -0700682 self.hostapd['log_count'] += 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800683 if self.station['configured']:
Christopher Wiley05262d62013-04-17 17:53:59 -0700684 local_servers = self.local_servers
685 self.local_servers = []
Paul Stewartc2b3de82011-03-03 14:45:31 -0800686 if self.station['type'] == 'ibss':
687 self.router.run("%s dev %s ibss leave" %
688 (self.cmd_iw, self.station['interface']))
689 else:
690 self.router.run("%s dev %s disconnect" %
691 (self.cmd_iw, self.station['interface']))
692 self.router.run("%s link set %s down" % (self.cmd_ip,
693 self.station['interface']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700694
Paul Stewart326badb2012-12-18 14:18:54 -0800695 for server in local_servers:
696 self.stop_dhcp_server(server['interface'])
697 self.router.run("%s addr del %s" %
698 (self.cmd_ip, server['ip_params']),
699 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700700
701 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800702 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700703
704
Paul Stewart17350be2012-12-14 13:34:54 -0800705 def verify_pmksa_auth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700706 """Verify that the PMKSA auth was cached on a hostapd instance.
707
708 @param params dict with optional key 'instance' (defaults to 0).
709
710 """
Paul Stewart17350be2012-12-14 13:34:54 -0800711 instance_num = params.get('instance', 0)
712 instance = self.hostapd_instances[instance_num]
713 pmksa_match = 'PMK from PMKSA cache - skip IEEE 802.1X.EAP'
714 self.router.run('grep -q "%s" %s' % (pmksa_match, instance['log_file']))
715
716
Paul Stewart7cb1f062010-06-10 15:46:20 -0700717 def get_ssid(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700718 """@return string ssid for the network stemming from this router."""
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700719 if self.hostapd['configured']:
720 return self.hostapd['conf']['ssid']
721
722 return self.station['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700723
724
725 def set_txpower(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700726 """Set the transmission power for an interface.
727
728 Assumes that we want to refer to the first hostapd instance unless
729 'interface' is defined in params. Sets the transmission power to
730 'auto' if 'power' is not defined in params.
731
732 @param params dict of parameters as described above.
733
734 """
Paul Stewart548cf452012-11-27 17:46:23 -0800735 interface = params.get('interface',
736 self.hostapd_instances[0]['interface'])
737 power = params.get('power', 'auto')
Paul Stewart98022e22010-10-22 10:33:14 -0700738 self.router.run("%s dev %s set txpower %s" %
Paul Stewart548cf452012-11-27 17:46:23 -0800739 (self.cmd_iw, interface, power))
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700740
741
742 def deauth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700743 """Deauthenticates a client described in params.
744
745 @param params dict containing a key 'client'.
746
747 """
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700748 self.router.run('%s -p%s deauthenticate %s' %
749 (self.cmd_hostapd_cli,
750 self.hostapd['conf']['ctrl_interface'],
751 params['client']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700752
753
Paul Stewart51b0f382013-06-12 09:03:02 -0700754 def send_management_frame(self, frame_type, instance=0):
755 """Injects a management frame into an active hostapd session.
756
757 @param frame_type string the type of frame to send.
758 @param instance int indicating which hostapd instance to inject into.
759
760 """
761 hostap_interface = self.hostapd_instances[instance]['interface']
762 interface = self._get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
763 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
764 self.router.run('%s %s %s' %
765 (self.cmd_send_management_frame, interface, frame_type))
766 self._release_wlanif(interface)
767
768
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700769 def _pre_config_hook(self, config):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700770 """Hook for subclasses.
771
772 Run after gathering configuration parameters,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700773 but before writing parameters to config file.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700774
775 @param config dict containing hostapd config parameters.
776
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700777 """
778 pass
779
780
781 def _pre_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700782 """Hook for subclasses.
783
784 Run after generating hostapd config file, but before starting hostapd.
785
786 @param params dict parameters from site_wifitest.
787
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700788 """
789 pass
790
791
792 def _post_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700793 """Hook for subclasses run after starting hostapd.
794
795 @param params dict parameters from site_wifitest.
796
797 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700798 pass