blob: 4065644a18c6b370d8db1d826b8d70771702080c [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
Sam Leffler19bb0a72010-04-12 08:51:08 -070011
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070012def isLinuxRouter(host):
13 """Check if host is a linux router.
14
15 @param host Host object representing the remote machine.
16 @return True iff remote system is a Linux system.
17
18 """
19 router_uname = host.run('uname').stdout
Sam Leffler19bb0a72010-04-12 08:51:08 -070020 return re.search('Linux', router_uname)
21
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070022class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070023 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070024
25 This class implements test methods/steps that communicate with a
26 router implemented with Linux/mac80211. The router must
27 be pre-configured to enable ssh access and have a mac80211-based
28 wireless device. We also assume hostapd 0.7.x and iw are present
29 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070030
Sam Leffler6969d1d2010-03-15 16:07:11 -070031 """
32
33
34 def __init__(self, host, params, defssid):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070035 """Build a LinuxRouter.
36
37 @param host Host object representing the remote machine.
38 @param params dict of settings from site_wifitest based tests.
39 @param defssid string default SSID for networks created on this router.
40
41 """
42 site_linux_system.LinuxSystem.__init__(self, host, params, 'router')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070043 self._remove_interfaces()
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070044
Wade Guthrie24d1e312012-04-24 16:53:40 -070045 # Router host.
46 self.router = host
47
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070048 self.cmd_hostapd = wifi_test_utils.must_be_installed(
49 host, params.get('cmd_hostapd', '/usr/sbin/hostapd'))
50 self.cmd_hostapd_cli = params.get('cmd_hostapd_cli',
51 '/usr/sbin/hostapd_cli')
52 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
53 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070054
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070055 # hostapd configuration persists throughout the test, subsequent
56 # 'config' commands only modify it.
Paul Stewart7cb1f062010-06-10 15:46:20 -070057 self.defssid = defssid
Paul Stewartd5aafa92013-03-24 19:06:14 -070058 self.default_config = {
59 'ssid': defssid,
60 'hw_mode': 'g',
61 'ctrl_interface': '/tmp/hostapd-test.control',
62 'logger_syslog': '-1',
63 'logger_syslog_level': '0'
64 }
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070065 self.hostapd = {
66 'configured': False,
Paul Stewart326badb2012-12-18 14:18:54 -080067 'config_file': "/tmp/hostapd-test-%s.conf",
68 'log_file': "/tmp/hostapd-test-%s.log",
Paul Stewartf854d2e2011-05-04 13:19:18 -070069 'log_count': 0,
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070070 'driver': "nl80211",
Paul Stewartd5aafa92013-03-24 19:06:14 -070071 'conf': self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070072 }
Paul Stewartc2b3de82011-03-03 14:45:31 -080073 self.station = {
74 'configured': False,
75 'conf': {
76 'ssid': defssid,
Paul Stewartf05d7fd2011-04-06 16:19:37 -070077 },
Paul Stewartc2b3de82011-03-03 14:45:31 -080078 }
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070079 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -080080 self.hostapd_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -070081 self.force_local_server = "force_local_server" in params
82 self.dhcp_low = 1
83 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -070084
Paul Stewart548cf452012-11-27 17:46:23 -080085 # Kill hostapd and dhcp server if already running.
Thieu Le7b23a542012-01-27 15:54:48 -080086 self.kill_hostapd()
Paul Stewart548cf452012-11-27 17:46:23 -080087 self.stop_dhcp_servers()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070088
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -070089 # Place us in the US by default
90 self.router.run("%s reg set US" % self.cmd_iw)
Sam Leffler6969d1d2010-03-15 16:07:11 -070091
Paul Stewartf05d7fd2011-04-06 16:19:37 -070092
Sam Leffler6969d1d2010-03-15 16:07:11 -070093 def create(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070094 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -070095
96 @param params dict containing the device type under key 'type'.
97
98 """
99 self.create_wifi_device(params['type'])
100
101
102 def create_wifi_device(self, device_type='hostap'):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700103 """Create a wifi device of the specified type.
Christopher Wiley14796b32013-04-03 14:53:33 -0700104
105 Defaults to creating a hostap managed device.
106
107 @param device_type string device type.
108
109 """
Sam Leffler6969d1d2010-03-15 16:07:11 -0700110 #
111 # AP mode is handled entirely by hostapd so we only
112 # have to setup others (mapping the bsd type to what
113 # iw wants)
114 #
115 # map from bsd types to iw types
Christopher Wiley14796b32013-04-03 14:53:33 -0700116 self.apmode = device_type in ('ap', 'hostap')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800117 if not self.apmode:
Christopher Wiley14796b32013-04-03 14:53:33 -0700118 self.station['type'] = device_type
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700119 self.phytype = {
Christopher Wiley14796b32013-04-03 14:53:33 -0700120 'sta' : 'managed',
121 'monitor' : 'monitor',
122 'adhoc' : 'adhoc',
123 'ibss' : 'ibss',
124 'ap' : 'managed', # NB: handled by hostapd
125 'hostap' : 'managed', # NB: handled by hostapd
126 'mesh' : 'mesh',
127 'wds' : 'wds',
128 }[device_type]
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700129
Sam Leffler6969d1d2010-03-15 16:07:11 -0700130
Christopher Wileyd89b5282013-04-10 15:21:26 -0700131 def destroy(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700132 """Destroy a previously created device.
133
134 @param params dict of site_wifitest parameters.
135
136 """
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700137 self.deconfig(params)
Paul Stewartd5aafa92013-03-24 19:06:14 -0700138 self.hostapd['conf'] = self.default_config.copy()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700139
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700140
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700141 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700142 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700143 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700144
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700145
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700146 def cleanup(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700147 """Clean up any resources in use.
148
149 @param params dict of site_wifitest parameters.
150
151 """
Paul Stewart9e3ff0b2011-08-17 20:35:19 -0700152 # For linux, this is a no-op
153 pass
154
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700155
Paul Stewart548cf452012-11-27 17:46:23 -0800156 def start_hostapd(self, conf, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700157 """Start a hostapd instance described by conf.
158
159 @param conf dict of hostapd configuration parameters.
160 @param params dict of site_wifitest parameters.
161
162 """
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700163 logging.info('Starting hostapd with parameters: %r', conf)
Paul Stewart548cf452012-11-27 17:46:23 -0800164 # Figure out the correct interface.
Paul Stewart326badb2012-12-18 14:18:54 -0800165 interface = self._get_wlanif(self.hostapd['frequency'],
166 self.phytype,
167 mode=conf.get('hw_mode', 'b'))
168
169 conf_file = self.hostapd['config_file'] % interface
170 log_file = self.hostapd['log_file'] % interface
171 conf['interface'] = interface
Paul Stewart548cf452012-11-27 17:46:23 -0800172
173 # Generate hostapd.conf.
174 self._pre_config_hook(conf)
175 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
176 (conf_file, '\n'.join(
177 "%s=%s" % kv for kv in conf.iteritems())))
178
179 # Run hostapd.
180 logging.info("Starting hostapd...")
181 self._pre_start_hook(params)
182 self.router.run("%s -dd %s &> %s &" %
183 (self.cmd_hostapd, conf_file, log_file))
184
185 self.hostapd_instances.append({
186 'conf_file': conf_file,
187 'log_file': log_file,
Paul Stewart326badb2012-12-18 14:18:54 -0800188 'interface': interface
Paul Stewart548cf452012-11-27 17:46:23 -0800189 })
190
Paul Stewart326badb2012-12-18 14:18:54 -0800191 def _kill_process_instance(self, process, instance=None, wait=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700192 """Kill a process on the router.
193
Paul Stewart326badb2012-12-18 14:18:54 -0800194 Kills program named |process|, optionally only a specific
195 |instance|. If |wait| is specified, we makes sure |process| exits
196 before returning.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700197
198 @param process string name of process to kill.
199 @param instance string instance of process to kill.
200 @param wait int timeout in seconds to wait for.
201
Thieu Le7b23a542012-01-27 15:54:48 -0800202 """
Paul Stewart21737812012-12-06 11:03:32 -0800203 if instance:
Paul Stewart326badb2012-12-18 14:18:54 -0800204 search_arg = '-f "%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800205 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800206 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800207
Paul Stewart326badb2012-12-18 14:18:54 -0800208 cmd = "pkill %s >/dev/null 2>&1" % search_arg
209
210 if wait:
211 cmd += (" && while pgrep %s &> /dev/null; do sleep 1; done" %
212 search_arg)
213 self.router.run(cmd, timeout=wait, ignore_status=True)
214 else:
215 self.router.run(cmd, ignore_status=True)
216
217 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700218 """Kills a hostapd instance.
219
220 @param instance string instance to kill.
221
222 """
Paul Stewart326badb2012-12-18 14:18:54 -0800223 self._kill_process_instance('hostapd', instance, 30)
Thieu Le7b23a542012-01-27 15:54:48 -0800224
Paul Stewart21737812012-12-06 11:03:32 -0800225 def kill_hostapd(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700226 """Kill all hostapd instances."""
Paul Stewart21737812012-12-06 11:03:32 -0800227 self.kill_hostapd_instance(None)
228
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700229
230 def __get_default_hostap_config(self):
231 """@return dict of default options for hostapd."""
232 conf = self.hostapd['conf']
233 # default RTS and frag threshold to ``off''
234 conf['rts_threshold'] = '2347'
235 conf['fragm_threshold'] = '2346'
236 conf['driver'] = self.hostapd['driver']
237 return conf
238
239
240 def hostap_configure(self, configuration, multi_interface=None):
241 """Build up a hostapd configuration file and start hostapd.
242
243 Also setup a local server if this router supports them.
244
245 @param configuration HosetapConfig object.
246 @param multi_interface bool True iff multiple interfaces allowed.
247
248 """
249 if multi_interface is None and (self.hostapd['configured'] or
250 self.station['configured']):
251 self.deconfig()
252 # Start with the default hostapd config parameters.
253 conf = self.__get_default_hostap_config()
254 conf['ssid'] = (self.defssid + configuration.ssid_suffix)[-32:]
255 conf['channel'] = configuration.channel
256 self.hostapd['frequency'] = configuration.frequency
257 conf['hw_mode'] = configuration.hw_mode
258 if configuration.hide_ssid:
259 conf['ignore_broadcast_ssid'] = 1
260 if configuration.is_11n:
261 conf['ieee80211n'] = 1
262 conf['ht_capab'] = ''.join(configuration.n_capabilities)
263 if configuration.wmm_enabled:
264 conf['wmm_enabled'] = 1
265 if configuration.require_ht:
266 conf['require_ht'] = 1
267 # TODO(wiley) beacon interval support
268 self.start_hostapd(conf, {})
269 # Configure transmit power
270 tx_power_params = {'interface': conf['interface']}
271 # TODO(wiley) support for setting transmit power
272 self.set_txpower(tx_power_params)
273 if self.force_local_server:
274 self.start_local_server(conf['interface'])
275 self._post_start_hook({})
276 logging.info('AP configured.')
277 self.hostapd['configured'] = True
278
279
Paul Stewartc2b3de82011-03-03 14:45:31 -0800280 def hostap_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700281 """Configure the AP per test requirements.
Sam Leffler6969d1d2010-03-15 16:07:11 -0700282
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700283 @param params dict of site_wifitest parameters.
284
285 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700286 # keep parameter modifications local-only
287 orig_params = params
288 params = params.copy()
289
Paul Stewart45338d22010-10-21 10:57:02 -0700290 multi_interface = 'multi_interface' in params
291 if multi_interface:
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700292 # remove non-hostapd config item from params
Paul Stewart45338d22010-10-21 10:57:02 -0700293 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800294 elif self.hostapd['configured'] or self.station['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700295 self.deconfig()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700296
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700297 local_server = params.pop('local_server', False)
298
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700299 conf = self.__get_default_hostap_config()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800300 tx_power_params = {}
301 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700302
Paul Stewartc2b3de82011-03-03 14:45:31 -0800303 for k, v in params.iteritems():
304 if k == 'ssid':
305 conf['ssid'] = v
306 elif k == 'ssid_suffix':
Paul Stewart05cebab2012-05-29 11:58:17 -0700307 conf['ssid'] = self.defssid[:(32-len(v))] + v
Paul Stewartc2b3de82011-03-03 14:45:31 -0800308 elif k == 'channel':
309 freq = int(v)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700310 self.hostapd['frequency'] = freq
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700311
Paul Stewartc2b3de82011-03-03 14:45:31 -0800312 # 2.4GHz
313 if freq <= 2484:
314 # Make sure hw_mode is set
315 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700316 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800317
318 # Freq = 5 * chan + 2407, except channel 14
319 if freq == 2484:
320 conf['channel'] = 14
321 else:
322 conf['channel'] = (freq - 2407) / 5
323 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700324 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800325 # Make sure hw_mode is set
326 conf['hw_mode'] = 'a'
327 # Freq = 5 * chan + 4000
328 if freq < 5000:
329 conf['channel'] = (freq - 4000) / 5
330 # Freq = 5 * chan + 5000
331 else:
332 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700333
Paul Stewartc2b3de82011-03-03 14:45:31 -0800334 elif k == 'country':
335 conf['country_code'] = v
336 elif k == 'dotd':
337 conf['ieee80211d'] = 1
338 elif k == '-dotd':
339 conf['ieee80211d'] = 0
340 elif k == 'mode':
341 if v == '11a':
342 conf['hw_mode'] = 'a'
343 elif v == '11g':
344 conf['hw_mode'] = 'g'
345 elif v == '11b':
346 conf['hw_mode'] = 'b'
347 elif v == '11n':
348 conf['ieee80211n'] = 1
349 elif k == 'bintval':
350 conf['beacon_int'] = v
351 elif k == 'dtimperiod':
352 conf['dtim_period'] = v
353 elif k == 'rtsthreshold':
354 conf['rts_threshold'] = v
355 elif k == 'fragthreshold':
356 conf['fragm_threshold'] = v
357 elif k == 'shortpreamble':
358 conf['preamble'] = 1
359 elif k == 'authmode':
360 if v == "open":
361 conf['auth_algs'] = 1
362 elif v == "shared":
363 conf['auth_algs'] = 2
364 elif k == 'hidessid':
365 conf['ignore_broadcast_ssid'] = 1
366 elif k == 'wme':
367 conf['wmm_enabled'] = 1
368 elif k == '-wme':
369 conf['wmm_enabled'] = 0
370 elif k == 'deftxkey':
371 conf['wep_default_key'] = v
372 elif k == 'ht20':
373 htcaps.add('') # NB: ensure 802.11n setup below
374 conf['wmm_enabled'] = 1
375 elif k == 'ht40':
376 htcaps.add('[HT40-]')
377 htcaps.add('[HT40+]')
378 conf['wmm_enabled'] = 1
Paul Stewartc1df8d62011-04-07 14:28:15 -0700379 elif k in ('ht40+', 'ht40-'):
380 htcaps.add('[%s]' % k.upper())
381 conf['wmm_enabled'] = 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800382 elif k == 'shortgi':
383 htcaps.add('[SHORT-GI-20]')
384 htcaps.add('[SHORT-GI-40]')
385 elif k == 'pureg':
386 pass # TODO(sleffler) need hostapd support
387 elif k == 'puren':
388 pass # TODO(sleffler) need hostapd support
389 elif k == 'protmode':
390 pass # TODO(sleffler) need hostapd support
391 elif k == 'ht':
392 htcaps.add('') # NB: ensure 802.11n setup below
393 elif k == 'htprotmode':
394 pass # TODO(sleffler) need hostapd support
395 elif k == 'rifs':
396 pass # TODO(sleffler) need hostapd support
397 elif k == 'wepmode':
398 pass # NB: meaningless for hostapd; ignore
399 elif k == '-ampdu':
400 pass # TODO(sleffler) need hostapd support
401 elif k == 'txpower':
402 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700403 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800404 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700405
Paul Stewartc2b3de82011-03-03 14:45:31 -0800406 # Aggregate ht_capab.
407 if htcaps:
408 conf['ieee80211n'] = 1
409 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700410
Paul Stewart548cf452012-11-27 17:46:23 -0800411 self.start_hostapd(conf, orig_params)
Paul Stewart1ae854b2011-02-08 15:10:14 -0800412
Paul Stewartc2b3de82011-03-03 14:45:31 -0800413 # Configure transmit power
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700414 tx_power_params['interface'] = conf['interface']
Paul Stewartc2b3de82011-03-03 14:45:31 -0800415 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700416
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700417 if self.force_local_server or local_server is not False:
418 self.start_local_server(conf['interface'])
Sam Leffler6969d1d2010-03-15 16:07:11 -0700419
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700420 self._post_start_hook(orig_params)
421
422 logging.info("AP configured.")
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700423 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700424
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700425 @staticmethod
426 def ip_addr(netblock, idx):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700427 """Simple IPv4 calculator.
428
429 Takes host address in "IP/bits" notation and returns netmask, broadcast
430 address as well as integer offsets into the address range.
431
432 @param netblock string host address in "IP/bits" notation.
433 @param idx string describing what to return.
434 @return string containing something you hopefully requested.
435
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700436 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700437 addr_str,bits = netblock.split('/')
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700438 addr = map(int, addr_str.split('.'))
439 mask_bits = (-1 << (32-int(bits))) & 0xffffffff
440 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)]
Paul Stewart5977da92011-06-01 19:14:08 -0700441 if idx == 'local':
442 return addr_str
443 elif idx == 'netmask':
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700444 return '.'.join(map(str, mask))
445 elif idx == 'broadcast':
446 offset = [m ^ 0xff for m in mask]
447 else:
448 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)]
449 return '.'.join(map(str, [(a & m) + o
450 for a, m, o in zip(addr, mask, offset)]))
451
452
Paul Stewartc2b3de82011-03-03 14:45:31 -0800453 def station_config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700454 """Configure a station based AP.
455
456 @param params dict of site_wifitest parameters.
457
458 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700459 # keep parameter modifications local-only
460 orig_params = params
461 params = params.copy()
462
463 if 'multi_interface' in params:
464 raise NotImplementedError("station with multi_interface")
465
466 if self.station['type'] != 'ibss':
467 raise NotImplementedError("non-ibss station")
468
469 if self.station['configured'] or self.hostapd['configured']:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700470 self.deconfig()
Paul Stewartc2b3de82011-03-03 14:45:31 -0800471
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700472 local_server = params.pop('local_server', False)
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700473 mode = None
Paul Stewartc2b3de82011-03-03 14:45:31 -0800474 conf = self.station['conf']
475 for k, v in params.iteritems():
476 if k == 'ssid_suffix':
477 conf['ssid'] = self.defssid + v
478 elif k == 'channel':
479 freq = int(v)
480 if freq > 2484:
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700481 mode = 'a'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800482 elif k == 'mode':
483 if v == '11a':
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700484 mode = 'a'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800485 else:
486 conf[k] = v
487
Paul Stewart2ee7fdf2011-05-19 16:29:23 -0700488 interface = self._get_wlanif(freq, self.phytype, mode)
489
Paul Stewartc2b3de82011-03-03 14:45:31 -0800490 # Run interface configuration commands
491 for k, v in conf.iteritems():
492 if k != 'ssid':
493 self.router.run("%s dev %s set %s %s" %
494 (self.cmd_iw, interface, k, v))
495
496 # Connect the station
497 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700498 self.router.run("%s dev %s ibss join %s %d" %
499 (self.cmd_iw, interface, conf['ssid'], freq))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800500
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700501 if self.force_local_server or local_server is not False:
502 self.start_local_server(interface)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800503
504 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
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700519 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700520 """Start a local server on an interface.
521
522 @param interface string (e.g. wlan0)
523
524 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700525 logging.info("Starting up local server...")
526
527 if len(self.local_servers) >= 256:
528 raise error.TestFail('Exhausted available local servers')
529
Paul Stewart2bd823b2012-11-21 15:03:37 -0800530 netblock = '%s/24' % self.local_server_address(len(self.local_servers))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700531
532 params = {}
533 params['netblock'] = netblock
534 params['subnet'] = self.ip_addr(netblock, 0)
535 params['netmask'] = self.ip_addr(netblock, 'netmask')
536 params['dhcp_range'] = ' '.join(
537 (self.ip_addr(netblock, self.dhcp_low),
538 self.ip_addr(netblock, self.dhcp_high)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700539 params['interface'] = interface
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700540
541 params['ip_params'] = ("%s broadcast %s dev %s" %
542 (netblock,
543 self.ip_addr(netblock, 'broadcast'),
544 interface))
545 self.local_servers.append(params)
546
547 self.router.run("%s addr flush %s" %
548 (self.cmd_ip, interface))
549 self.router.run("%s addr add %s" %
550 (self.cmd_ip, params['ip_params']))
551 self.router.run("%s link set %s up" %
552 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800553 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -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
Paul Stewart548cf452012-11-27 17:46:23 -0800586 def stop_dhcp_servers(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700587 """Stop all dhcp servers on the router."""
Paul Stewart326badb2012-12-18 14:18:54 -0800588 self.stop_dhcp_server(None)
Paul Stewart548cf452012-11-27 17:46:23 -0800589
590
Paul Stewartc2b3de82011-03-03 14:45:31 -0800591 def config(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700592 """Configure an AP based on site_wifitest parameters.
593
594 @param params dict of site_wifitest parameters.
595
596 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800597 if self.apmode:
598 self.hostap_config(params)
599 else:
600 self.station_config(params)
601
602
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700603 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700604 """Return IP address on the WiFi subnet of a local server on the router.
605
606 If no local servers are configured (e.g. for an RSPro), a TestFail will
607 be raised.
608
609 @param ap_num int which local server to get an address from.
610
611 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700612 if self.local_servers:
613 return self.ip_addr(self.local_servers[ap_num]['netblock'],
614 'local')
615 else:
616 raise error.TestFail("No IP address assigned")
Paul Stewart5977da92011-06-01 19:14:08 -0700617
618
Paul Stewart17350be2012-12-14 13:34:54 -0800619 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700620 """Return the MAC address of an AP in the test.
621
622 @param ap_num int index of local server to read the MAC address from.
623 @return string MAC address like 00:11:22:33:44:55.
624
625 """
Paul Stewart17350be2012-12-14 13:34:54 -0800626 instance = self.hostapd_instances[ap_num]
627 interface = instance['interface']
628 result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
629 # Example response:
630 # 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
631 # link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
632 # inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
633 # inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
634 # we want the MAC address after the "link/ether" above.
635 parts = result.stdout.split(' ')
636 return parts[parts.index('link/ether') + 1]
637
638
Christopher Wileyd89b5282013-04-10 15:21:26 -0700639 def deconfig(self, params={}):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700640 """De-configure the AP (will also bring wlan down).
Sam Leffler6969d1d2010-03-15 16:07:11 -0700641
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700642 @param params dict of parameters from site_wifitest.
643
644 """
Paul Stewartc2b3de82011-03-03 14:45:31 -0800645 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700646 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700647
Paul Stewartc2b3de82011-03-03 14:45:31 -0800648 if self.hostapd['configured']:
Paul Stewart326badb2012-12-18 14:18:54 -0800649 local_servers = []
Paul Stewart21737812012-12-06 11:03:32 -0800650 if 'instance' in params:
651 instances = [ self.hostapd_instances.pop(params['instance']) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800652 for server in self.local_servers:
653 if server['interface'] == instances[0]['interface']:
654 local_servers = [server]
655 self.local_servers.remove(server)
656 break
Paul Stewart21737812012-12-06 11:03:32 -0800657 else:
658 instances = self.hostapd_instances
659 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800660 local_servers = self.local_servers
661 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700662
Paul Stewart21737812012-12-06 11:03:32 -0800663 for instance in instances:
664 if 'silent' in params:
665 # Deconfigure without notifying DUT. Remove the interface
666 # hostapd uses to send beacon and DEAUTH packets.
667 self._remove_interface(instance['interface'], True)
668
Paul Stewart326badb2012-12-18 14:18:54 -0800669 self.kill_hostapd_instance(instance['conf_file'])
Paul Stewart548cf452012-11-27 17:46:23 -0800670 self.router.get_file(instance['log_file'],
671 'debug/hostapd_router_%d_%s.log' %
672 (self.hostapd['log_count'],
673 instance['interface']))
674 self._release_wlanif(instance['interface'])
675# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance)
Paul Stewartf854d2e2011-05-04 13:19:18 -0700676 self.hostapd['log_count'] += 1
Paul Stewartc2b3de82011-03-03 14:45:31 -0800677 if self.station['configured']:
Christopher Wiley05262d62013-04-17 17:53:59 -0700678 local_servers = self.local_servers
679 self.local_servers = []
Paul Stewartc2b3de82011-03-03 14:45:31 -0800680 if self.station['type'] == 'ibss':
681 self.router.run("%s dev %s ibss leave" %
682 (self.cmd_iw, self.station['interface']))
683 else:
684 self.router.run("%s dev %s disconnect" %
685 (self.cmd_iw, self.station['interface']))
686 self.router.run("%s link set %s down" % (self.cmd_ip,
687 self.station['interface']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700688
Paul Stewart326badb2012-12-18 14:18:54 -0800689 for server in local_servers:
690 self.stop_dhcp_server(server['interface'])
691 self.router.run("%s addr del %s" %
692 (self.cmd_ip, server['ip_params']),
693 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700694
695 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800696 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700697
698
Paul Stewart17350be2012-12-14 13:34:54 -0800699 def verify_pmksa_auth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700700 """Verify that the PMKSA auth was cached on a hostapd instance.
701
702 @param params dict with optional key 'instance' (defaults to 0).
703
704 """
Paul Stewart17350be2012-12-14 13:34:54 -0800705 instance_num = params.get('instance', 0)
706 instance = self.hostapd_instances[instance_num]
707 pmksa_match = 'PMK from PMKSA cache - skip IEEE 802.1X.EAP'
708 self.router.run('grep -q "%s" %s' % (pmksa_match, instance['log_file']))
709
710
Paul Stewart7cb1f062010-06-10 15:46:20 -0700711 def get_ssid(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700712 """@return string ssid for the network stemming from this router."""
Paul Stewart7cb1f062010-06-10 15:46:20 -0700713 return self.hostapd['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700714
715
716 def set_txpower(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700717 """Set the transmission power for an interface.
718
719 Assumes that we want to refer to the first hostapd instance unless
720 'interface' is defined in params. Sets the transmission power to
721 'auto' if 'power' is not defined in params.
722
723 @param params dict of parameters as described above.
724
725 """
Paul Stewart548cf452012-11-27 17:46:23 -0800726 interface = params.get('interface',
727 self.hostapd_instances[0]['interface'])
728 power = params.get('power', 'auto')
Paul Stewart98022e22010-10-22 10:33:14 -0700729 self.router.run("%s dev %s set txpower %s" %
Paul Stewart548cf452012-11-27 17:46:23 -0800730 (self.cmd_iw, interface, power))
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700731
732
733 def deauth(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700734 """Deauthenticates a client described in params.
735
736 @param params dict containing a key 'client'.
737
738 """
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700739 self.router.run('%s -p%s deauthenticate %s' %
740 (self.cmd_hostapd_cli,
741 self.hostapd['conf']['ctrl_interface'],
742 params['client']))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700743
744
745 def _pre_config_hook(self, config):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700746 """Hook for subclasses.
747
748 Run after gathering configuration parameters,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700749 but before writing parameters to config file.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700750
751 @param config dict containing hostapd config parameters.
752
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700753 """
754 pass
755
756
757 def _pre_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700758 """Hook for subclasses.
759
760 Run after generating hostapd config file, but before starting hostapd.
761
762 @param params dict parameters from site_wifitest.
763
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700764 """
765 pass
766
767
768 def _post_start_hook(self, params):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700769 """Hook for subclasses run after starting hostapd.
770
771 @param params dict parameters from site_wifitest.
772
773 """
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700774 pass