blob: c1e8466199943e0a6384735879f9cc551e708462 [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 Wiley1febd6a2013-06-03 13:59:48 -070011from autotest_lib.server.cros.wlan 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 Wileyb8921c72013-06-13 09:51:47 -0700292 conf.update(configuration.get_security_hostapd_conf())
Christopher Wileya89706d2013-06-12 13:20:58 -0700293
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700294 self.start_hostapd(conf, {})
295 # Configure transmit power
296 tx_power_params = {'interface': conf['interface']}
297 # TODO(wiley) support for setting transmit power
298 self.set_txpower(tx_power_params)
299 if self.force_local_server:
300 self.start_local_server(conf['interface'])
301 self._post_start_hook({})
302 logging.info('AP configured.')
303 self.hostapd['configured'] = True
304
305
Paul Stewartc2b3de82011-03-03 14:45:31 -0800306 def hostap_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700307 """Configure the AP per test requirements.
Sam Leffler6969d1d2010-03-15 16:07:11 -0700308
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700309 @param params dict of site_wifitest parameters.
310
311 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700312 # keep parameter modifications local-only
313 orig_params = params
314 params = params.copy()
315
Paul Stewart45338d22010-10-21 10:57:02 -0700316 multi_interface = 'multi_interface' in params
317 if multi_interface:
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700318 # remove non-hostapd config item from params
Paul Stewart45338d22010-10-21 10:57:02 -0700319 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800320 elif self.hostapd['configured'] or self.station['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700321 self.deconfig()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700322
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700323 local_server = params.pop('local_server', False)
324
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700325 conf = self.__get_default_hostap_config()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800326 tx_power_params = {}
327 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700328
Paul Stewartc2b3de82011-03-03 14:45:31 -0800329 for k, v in params.iteritems():
330 if k == 'ssid':
331 conf['ssid'] = v
332 elif k == 'ssid_suffix':
Paul Stewart05cebab2012-05-29 11:58:17 -0700333 conf['ssid'] = self.defssid[:(32-len(v))] + v
Paul Stewartc2b3de82011-03-03 14:45:31 -0800334 elif k == 'channel':
335 freq = int(v)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700336 self.hostapd['frequency'] = freq
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700337
Paul Stewartc2b3de82011-03-03 14:45:31 -0800338 # 2.4GHz
339 if freq <= 2484:
340 # Make sure hw_mode is set
341 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700342 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800343
344 # Freq = 5 * chan + 2407, except channel 14
345 if freq == 2484:
346 conf['channel'] = 14
347 else:
348 conf['channel'] = (freq - 2407) / 5
349 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700350 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800351 # Make sure hw_mode is set
352 conf['hw_mode'] = 'a'
353 # Freq = 5 * chan + 4000
354 if freq < 5000:
355 conf['channel'] = (freq - 4000) / 5
356 # Freq = 5 * chan + 5000
357 else:
358 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700359
Paul Stewartc2b3de82011-03-03 14:45:31 -0800360 elif k == 'country':
361 conf['country_code'] = v
362 elif k == 'dotd':
363 conf['ieee80211d'] = 1
364 elif k == '-dotd':
365 conf['ieee80211d'] = 0
366 elif k == 'mode':
367 if v == '11a':
368 conf['hw_mode'] = 'a'
369 elif v == '11g':
370 conf['hw_mode'] = 'g'
371 elif v == '11b':
372 conf['hw_mode'] = 'b'
373 elif v == '11n':
374 conf['ieee80211n'] = 1
375 elif k == 'bintval':
376 conf['beacon_int'] = v
377 elif k == 'dtimperiod':
378 conf['dtim_period'] = v
379 elif k == 'rtsthreshold':
380 conf['rts_threshold'] = v
381 elif k == 'fragthreshold':
382 conf['fragm_threshold'] = v
383 elif k == 'shortpreamble':
384 conf['preamble'] = 1
385 elif k == 'authmode':
386 if v == "open":
387 conf['auth_algs'] = 1
388 elif v == "shared":
389 conf['auth_algs'] = 2
390 elif k == 'hidessid':
391 conf['ignore_broadcast_ssid'] = 1
392 elif k == 'wme':
393 conf['wmm_enabled'] = 1
394 elif k == '-wme':
395 conf['wmm_enabled'] = 0
396 elif k == 'deftxkey':
397 conf['wep_default_key'] = v
398 elif k == 'ht20':
399 htcaps.add('') # NB: ensure 802.11n setup below
400 conf['wmm_enabled'] = 1
401 elif k == 'ht40':
402 htcaps.add('[HT40-]')
403 htcaps.add('[HT40+]')
404 conf['wmm_enabled'] = 1
Paul Stewartc1df8d62011-04-07 14:28:15 -0700405 elif k in ('ht40+', 'ht40-'):
406 htcaps.add('[%s]' % k.upper())
407 conf['wmm_enabled'] = 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800408 elif k == 'shortgi':
409 htcaps.add('[SHORT-GI-20]')
410 htcaps.add('[SHORT-GI-40]')
411 elif k == 'pureg':
412 pass # TODO(sleffler) need hostapd support
413 elif k == 'puren':
414 pass # TODO(sleffler) need hostapd support
415 elif k == 'protmode':
416 pass # TODO(sleffler) need hostapd support
417 elif k == 'ht':
418 htcaps.add('') # NB: ensure 802.11n setup below
419 elif k == 'htprotmode':
420 pass # TODO(sleffler) need hostapd support
421 elif k == 'rifs':
422 pass # TODO(sleffler) need hostapd support
423 elif k == 'wepmode':
424 pass # NB: meaningless for hostapd; ignore
425 elif k == '-ampdu':
426 pass # TODO(sleffler) need hostapd support
427 elif k == 'txpower':
428 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700429 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800430 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700431
Paul Stewartc2b3de82011-03-03 14:45:31 -0800432 # Aggregate ht_capab.
433 if htcaps:
434 conf['ieee80211n'] = 1
435 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700436
Paul Stewart548cf452012-11-27 17:46:23 -0800437 self.start_hostapd(conf, orig_params)
Paul Stewart1ae854b2011-02-08 15:10:14 -0800438
Paul Stewartc2b3de82011-03-03 14:45:31 -0800439 # Configure transmit power
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700440 tx_power_params['interface'] = conf['interface']
Paul Stewartc2b3de82011-03-03 14:45:31 -0800441 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700442
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700443 if self.force_local_server or local_server is not False:
444 self.start_local_server(conf['interface'])
Sam Leffler6969d1d2010-03-15 16:07:11 -0700445
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700446 self._post_start_hook(orig_params)
447
448 logging.info("AP configured.")
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700449 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700450
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700451
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700452 @staticmethod
453 def ip_addr(netblock, idx):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700454 """Simple IPv4 calculator.
455
456 Takes host address in "IP/bits" notation and returns netmask, broadcast
457 address as well as integer offsets into the address range.
458
459 @param netblock string host address in "IP/bits" notation.
460 @param idx string describing what to return.
461 @return string containing something you hopefully requested.
462
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700463 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700464 addr_str,bits = netblock.split('/')
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700465 addr = map(int, addr_str.split('.'))
466 mask_bits = (-1 << (32-int(bits))) & 0xffffffff
467 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
Paul Stewart5977da92011-06-01 19:14:08 -0700468 if idx == 'local':
469 return addr_str
470 elif idx == 'netmask':
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700471 return '.'.join(map(str, mask))
472 elif idx == 'broadcast':
473 offset = [m ^ 0xff for m in mask]
474 else:
475 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
476 return '.'.join(map(str, [(a & m) + o
477 for a, m, o in zip(addr, mask, offset)]))
478
479
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700480 def ibss_configure(self, config):
481 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700482
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700483 Extract relevant configuration objects from |config| despite not
484 actually being a hostap managed endpoint.
485
486 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700487
488 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700489 if self.station['configured'] or self.hostapd['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700490 self.deconfig()
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700491 interface = self._get_wlanif(config.frequency, self.phytype,
492 config.hw_mode)
493 ssid = config.get_ssid(self.defssid)
494 self.station['conf']['ssid'] = ssid
Paul Stewartc2b3de82011-03-03 14:45:31 -0800495 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700496 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
497 self.router.run('%s dev %s ibss join %s %d' % (self.cmd_iw, interface,
498 ssid, config.frequency))
499 # Always start a local server.
500 self.start_local_server(interface)
501 # Remember that this interface is up.
Paul Stewartc2b3de82011-03-03 14:45:31 -0800502 self.station['configured'] = True
503 self.station['interface'] = interface
504
505
Paul Stewart2bd823b2012-11-21 15:03:37 -0800506 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700507 """Get the local server address for an interface.
508
509 When we multiple local servers, we give them static IP addresses
510 like 192.158.*.254.
511
512 @param index int describing which local server this is for.
513
514 """
Paul Stewart2bd823b2012-11-21 15:03:37 -0800515 return '%d.%d.%d.%d' % (192, 168, index, 254)
516
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700517
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700518 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700519 """Start a local server on an interface.
520
521 @param interface string (e.g. wlan0)
522
523 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700524 logging.info("Starting up local server...")
525
526 if len(self.local_servers) >= 256:
527 raise error.TestFail('Exhausted available local servers')
528
Paul Stewart2bd823b2012-11-21 15:03:37 -0800529 netblock = '%s/24' % self.local_server_address(len(self.local_servers))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700530
531 params = {}
532 params['netblock'] = netblock
533 params['subnet'] = self.ip_addr(netblock, 0)
534 params['netmask'] = self.ip_addr(netblock, 'netmask')
535 params['dhcp_range'] = ' '.join(
536 (self.ip_addr(netblock, self.dhcp_low),
537 self.ip_addr(netblock, self.dhcp_high)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700538 params['interface'] = interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700539
540 params['ip_params'] = ("%s broadcast %s dev %s" %
541 (netblock,
542 self.ip_addr(netblock, 'broadcast'),
543 interface))
544 self.local_servers.append(params)
545
546 self.router.run("%s addr flush %s" %
547 (self.cmd_ip, interface))
548 self.router.run("%s addr add %s" %
549 (self.cmd_ip, params['ip_params']))
550 self.router.run("%s link set %s up" %
551 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800552 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700553
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700554
Paul Stewart548cf452012-11-27 17:46:23 -0800555 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700556 """Start a dhcp server on an interface.
557
558 @param interface string (e.g. wlan0)
559
560 """
Paul Stewart326badb2012-12-18 14:18:54 -0800561 conf_file = self.dhcpd_conf % interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700562 dhcp_conf = '\n'.join(map(
563 lambda server_conf: \
564 "subnet %(subnet)s netmask %(netmask)s {\n" \
565 " range %(dhcp_range)s;\n" \
566 "}" % server_conf,
567 self.local_servers))
568 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
Paul Stewart326badb2012-12-18 14:18:54 -0800569 (conf_file,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700570 '\n'.join(('ddns-update-style none;', dhcp_conf))))
571 self.router.run("touch %s" % self.dhcpd_leases)
572
573 self.router.run("pkill dhcpd >/dev/null 2>&1", ignore_status=True)
574 self.router.run("%s -q -cf %s -lf %s" %
Paul Stewart326badb2012-12-18 14:18:54 -0800575 (self.cmd_dhcpd, conf_file, self.dhcpd_leases))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700576
577
Paul Stewart326badb2012-12-18 14:18:54 -0800578 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700579 """Stop a dhcp server on the router.
580
581 @param instance string instance to kill.
582
583 """
Paul Stewart326badb2012-12-18 14:18:54 -0800584 self._kill_process_instance('dhcpd', instance, 0)
585
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700586
Paul Stewart548cf452012-11-27 17:46:23 -0800587 def stop_dhcp_servers(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700588 """Stop all dhcp servers on the router."""
Paul Stewart326badb2012-12-18 14:18:54 -0800589 self.stop_dhcp_server(None)
Paul Stewart548cf452012-11-27 17:46:23 -0800590
591
Paul Stewartc2b3de82011-03-03 14:45:31 -0800592 def config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700593 """Configure an AP based on site_wifitest parameters.
594
595 @param params dict of site_wifitest parameters.
596
597 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800598 if self.apmode:
599 self.hostap_config(params)
600 else:
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700601 config = hostap_config.HostapConfig(
602 ssid=self.get_ssid(),
603 frequency=int(params.get('channel', None)))
604 self.ibss_configure(config)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800605
606
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700607 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700608 """Return IP address on the WiFi subnet of a local server on the router.
609
610 If no local servers are configured (e.g. for an RSPro), a TestFail will
611 be raised.
612
613 @param ap_num int which local server to get an address from.
614
615 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700616 if self.local_servers:
617 return self.ip_addr(self.local_servers[ap_num]['netblock'],
618 'local')
619 else:
620 raise error.TestFail("No IP address assigned")
Paul Stewart5977da92011-06-01 19:14:08 -0700621
622
Paul Stewart17350be2012-12-14 13:34:54 -0800623 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700624 """Return the MAC address of an AP in the test.
625
626 @param ap_num int index of local server to read the MAC address from.
627 @return string MAC address like 00:11:22:33:44:55.
628
629 """
Paul Stewart17350be2012-12-14 13:34:54 -0800630 instance = self.hostapd_instances[ap_num]
631 interface = instance['interface']
632 result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
633 # Example response:
634 # 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
635 # link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
636 # inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
637 # inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
638 # we want the MAC address after the "link/ether" above.
639 parts = result.stdout.split(' ')
640 return parts[parts.index('link/ether') + 1]
641
642
Christopher Wileyd89b5282013-04-10 15:21:26 -0700643 def deconfig(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700644 """De-configure the AP (will also bring wlan down).
Sam Leffler6969d1d2010-03-15 16:07:11 -0700645
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700646 @param params dict of parameters from site_wifitest.
647
648 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800649 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700650 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700651
Paul Stewartc2b3de82011-03-03 14:45:31 -0800652 if self.hostapd['configured']:
Paul Stewart326badb2012-12-18 14:18:54 -0800653 local_servers = []
Paul Stewart21737812012-12-06 11:03:32 -0800654 if 'instance' in params:
655 instances = [ self.hostapd_instances.pop(params['instance']) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800656 for server in self.local_servers:
657 if server['interface'] == instances[0]['interface']:
658 local_servers = [server]
659 self.local_servers.remove(server)
660 break
Paul Stewart21737812012-12-06 11:03:32 -0800661 else:
662 instances = self.hostapd_instances
663 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800664 local_servers = self.local_servers
665 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700666
Paul Stewart21737812012-12-06 11:03:32 -0800667 for instance in instances:
668 if 'silent' in params:
669 # Deconfigure without notifying DUT. Remove the interface
670 # hostapd uses to send beacon and DEAUTH packets.
671 self._remove_interface(instance['interface'], True)
672
Paul Stewart326badb2012-12-18 14:18:54 -0800673 self.kill_hostapd_instance(instance['conf_file'])
Paul Stewart548cf452012-11-27 17:46:23 -0800674 self.router.get_file(instance['log_file'],
675 'debug/hostapd_router_%d_%s.log' %
676 (self.hostapd['log_count'],
677 instance['interface']))
678 self._release_wlanif(instance['interface'])
679# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance)
Paul Stewartf854d2e2011-05-04 13:19:18 -0700680 self.hostapd['log_count'] += 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800681 if self.station['configured']:
Christopher Wiley05262d62013-04-17 17:53:59 -0700682 local_servers = self.local_servers
683 self.local_servers = []
Paul Stewartc2b3de82011-03-03 14:45:31 -0800684 if self.station['type'] == 'ibss':
685 self.router.run("%s dev %s ibss leave" %
686 (self.cmd_iw, self.station['interface']))
687 else:
688 self.router.run("%s dev %s disconnect" %
689 (self.cmd_iw, self.station['interface']))
690 self.router.run("%s link set %s down" % (self.cmd_ip,
691 self.station['interface']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700692
Paul Stewart326badb2012-12-18 14:18:54 -0800693 for server in local_servers:
694 self.stop_dhcp_server(server['interface'])
695 self.router.run("%s addr del %s" %
696 (self.cmd_ip, server['ip_params']),
697 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700698
699 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800700 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700701
702
Paul Stewart17350be2012-12-14 13:34:54 -0800703 def verify_pmksa_auth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700704 """Verify that the PMKSA auth was cached on a hostapd instance.
705
706 @param params dict with optional key 'instance' (defaults to 0).
707
708 """
Paul Stewart17350be2012-12-14 13:34:54 -0800709 instance_num = params.get('instance', 0)
710 instance = self.hostapd_instances[instance_num]
711 pmksa_match = 'PMK from PMKSA cache - skip IEEE 802.1X.EAP'
712 self.router.run('grep -q "%s" %s' % (pmksa_match, instance['log_file']))
713
714
Paul Stewart7cb1f062010-06-10 15:46:20 -0700715 def get_ssid(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700716 """@return string ssid for the network stemming from this router."""
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700717 if self.hostapd['configured']:
718 return self.hostapd['conf']['ssid']
719
720 return self.station['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700721
722
723 def set_txpower(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700724 """Set the transmission power for an interface.
725
726 Assumes that we want to refer to the first hostapd instance unless
727 'interface' is defined in params. Sets the transmission power to
728 'auto' if 'power' is not defined in params.
729
730 @param params dict of parameters as described above.
731
732 """
Paul Stewart548cf452012-11-27 17:46:23 -0800733 interface = params.get('interface',
734 self.hostapd_instances[0]['interface'])
735 power = params.get('power', 'auto')
Paul Stewart98022e22010-10-22 10:33:14 -0700736 self.router.run("%s dev %s set txpower %s" %
Paul Stewart548cf452012-11-27 17:46:23 -0800737 (self.cmd_iw, interface, power))
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700738
739
740 def deauth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700741 """Deauthenticates a client described in params.
742
743 @param params dict containing a key 'client'.
744
745 """
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700746 self.router.run('%s -p%s deauthenticate %s' %
747 (self.cmd_hostapd_cli,
748 self.hostapd['conf']['ctrl_interface'],
749 params['client']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700750
751
Paul Stewart51b0f382013-06-12 09:03:02 -0700752 def send_management_frame(self, frame_type, instance=0):
753 """Injects a management frame into an active hostapd session.
754
755 @param frame_type string the type of frame to send.
756 @param instance int indicating which hostapd instance to inject into.
757
758 """
759 hostap_interface = self.hostapd_instances[instance]['interface']
760 interface = self._get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
761 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
762 self.router.run('%s %s %s' %
763 (self.cmd_send_management_frame, interface, frame_type))
764 self._release_wlanif(interface)
765
766
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700767 def _pre_config_hook(self, config):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700768 """Hook for subclasses.
769
770 Run after gathering configuration parameters,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700771 but before writing parameters to config file.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700772
773 @param config dict containing hostapd config parameters.
774
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700775 """
776 pass
777
778
779 def _pre_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700780 """Hook for subclasses.
781
782 Run after generating hostapd config file, but before starting hostapd.
783
784 @param params dict parameters from site_wifitest.
785
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700786 """
787 pass
788
789
790 def _post_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700791 """Hook for subclasses run after starting hostapd.
792
793 @param params dict parameters from site_wifitest.
794
795 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700796 pass