blob: e710e3a21fe26cb71d3c99c1a8aff756ba17f2cb [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 Wiley408d1812014-01-13 15:27:43 -08005import collections
Christopher Wiley14796b32013-04-03 14:53:33 -07006import logging
Christopher Wiley3166e432013-08-06 09:53:12 -07007import random
Christopher Wiley3166e432013-08-06 09:53:12 -07008import string
Christopher Wileyeea12362013-12-12 17:24:29 -08009import time
Christopher Wiley14796b32013-04-03 14:53:33 -070010
Paul Stewartc9628b32010-08-11 13:03:51 -070011from autotest_lib.client.common_lib import error
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070012from autotest_lib.client.common_lib import utils
Paul Stewart6ddeba72013-11-18 10:08:23 -080013from autotest_lib.client.common_lib.cros.network import interface
Christopher Wiley594570d2014-03-14 16:50:15 -070014from autotest_lib.client.common_lib.cros.network import netblock
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070015from autotest_lib.client.common_lib.cros.network import ping_runner
16from autotest_lib.server import hosts
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070017from autotest_lib.server import site_linux_system
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070018from autotest_lib.server.cros import wifi_test_utils
Christopher Wiley99d42c92013-07-09 16:40:16 -070019from autotest_lib.server.cros.network import hostap_config
Sam Leffler19bb0a72010-04-12 08:51:08 -070020
Christopher Wiley408d1812014-01-13 15:27:43 -080021
22StationInstance = collections.namedtuple('StationInstance',
23 ['ssid', 'interface', 'dev_type'])
Christopher Wileyd6e503c2014-06-23 15:53:47 -070024HostapdInstance = collections.namedtuple('HostapdInstance',
25 ['ssid', 'conf_file', 'log_file',
26 'interface', 'config_dict'])
Christopher Wiley408d1812014-01-13 15:27:43 -080027
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070028def build_router_hostname(client_hostname=None, router_hostname=None):
29 """Build a router hostname from a client hostname.
30
31 @param client_hostname: string hostname of DUT connected to a router.
32 @param router_hostname: string hostname of router.
33 @return string hostname of connected router or None if the hostname
34 cannot be inferred from the client hostname.
35
36 """
37 if not router_hostname and not client_hostname:
38 raise error.TestError('Either client_hostname or router_hostname must '
39 'be specified to build_router_hostname.')
40
41 if router_hostname:
42 return router_hostname
43
44 if utils.host_is_in_lab_zone(client_hostname):
45 # Lab naming convention in: go/chromeos-lab-hostname-convention
46 return wifi_test_utils.get_router_addr_in_lab(client_hostname)
47
Christopher Wileya4754c02014-06-30 16:37:52 -070048 raise error.TestError('Could not infer router hostname from client '
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070049 'hostname: %s.' % client_hostname)
50
51
52def build_router_proxy(test_name='', client_hostname=None, router_addr=None):
53 """Build up a LinuxRouter object.
54
55 Verifies that the remote host responds to ping.
56 Either client_hostname or router_addr must be specified.
57
58 @param test_name: string name of this test (e.g. 'network_WiFi_TestName').
59 @param client_hostname: string hostname of DUT if we're in the lab.
60 @param router_addr: string DNS/IPv4 address to use for router host object.
61
62 @return LinuxRouter or raise error.TestError on failure.
63
64 """
Christopher Wiley297910b2014-07-01 13:53:19 -070065 router_hostname = build_router_hostname(client_hostname=client_hostname,
66 router_hostname=router_addr)
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070067 logging.info('Connecting to router at %s', router_hostname)
68 ping_helper = ping_runner.PingRunner()
69 if not ping_helper.simple_ping(router_hostname):
70 raise error.TestError('Router at %s is not pingable.' %
71 router_hostname)
72
73 return LinuxRouter(hosts.create_host(router_hostname), test_name)
74
Christopher Wiley408d1812014-01-13 15:27:43 -080075
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070076class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070077 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070078
79 This class implements test methods/steps that communicate with a
80 router implemented with Linux/mac80211. The router must
81 be pre-configured to enable ssh access and have a mac80211-based
82 wireless device. We also assume hostapd 0.7.x and iw are present
83 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070084
Sam Leffler6969d1d2010-03-15 16:07:11 -070085 """
86
Christopher Wiley08aafb02014-04-22 17:38:21 -070087 KNOWN_TEST_PREFIX = 'network_WiFi_'
Christopher Wileyd6e503c2014-06-23 15:53:47 -070088 POLLING_INTERVAL_SECONDS = 0.5
Christopher Wiley1defc242013-09-18 10:28:37 -070089 STARTUP_TIMEOUT_SECONDS = 10
Christopher Wiley3166e432013-08-06 09:53:12 -070090 SUFFIX_LETTERS = string.ascii_lowercase + string.digits
Christopher Wileyb1ade0a2013-09-16 13:09:55 -070091 SUBNET_PREFIX_OCTETS = (192, 168)
Sam Leffler6969d1d2010-03-15 16:07:11 -070092
Christopher Wileyeea12362013-12-12 17:24:29 -080093 HOSTAPD_CONF_FILE_PATTERN = '/tmp/hostapd-test-%s.conf'
94 HOSTAPD_LOG_FILE_PATTERN = '/tmp/hostapd-test-%s.log'
Paul Stewart80bb3372014-01-22 15:06:08 -080095 HOSTAPD_CONTROL_INTERFACE_PATTERN = '/tmp/hostapd-test-%s.ctrl'
Christopher Wileyeea12362013-12-12 17:24:29 -080096 HOSTAPD_DRIVER_NAME = 'nl80211'
97
98 STATION_CONF_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.conf'
99 STATION_LOG_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.log'
100 STATION_PID_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.pid'
101
Peter Qiuc4beba02014-03-24 14:46:24 -0700102 MGMT_FRAME_SENDER_LOG_FILE = '/tmp/send_management_frame-test.log'
103
Paul Stewart51b0f382013-06-12 09:03:02 -0700104 def get_capabilities(self):
105 """@return iterable object of AP capabilities for this system."""
Christopher Wileyeea12362013-12-12 17:24:29 -0800106 caps = set([self.CAPABILITY_IBSS])
Paul Stewart51b0f382013-06-12 09:03:02 -0700107 try:
108 self.cmd_send_management_frame = wifi_test_utils.must_be_installed(
Christopher Wiley408d1812014-01-13 15:27:43 -0800109 self.host, '/usr/bin/send_management_frame')
Paul Stewart51b0f382013-06-12 09:03:02 -0700110 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME)
111 except error.TestFail:
112 pass
113 return super(LinuxRouter, self).get_capabilities().union(caps)
114
115
Christopher Wiley408d1812014-01-13 15:27:43 -0800116 @property
117 def router(self):
118 """Deprecated. Use self.host instead.
119
120 @return Host object representing the remote router.
121
122 """
123 return self.host
124
125
Christopher Wileyaeef9b52014-03-11 12:24:11 -0700126 @property
127 def wifi_ip(self):
128 """Simple accessor for the WiFi IP when there is only one AP.
129
130 @return string IP of WiFi interface.
131
132 """
133 if len(self.local_servers) != 1:
134 raise error.TestError('Could not pick a WiFi IP to return.')
135
136 return self.get_wifi_ip(0)
137
138
Christopher Wiley408d1812014-01-13 15:27:43 -0800139 def __init__(self, host, test_name):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700140 """Build a LinuxRouter.
141
142 @param host Host object representing the remote machine.
Christopher Wiley3166e432013-08-06 09:53:12 -0700143 @param test_name string name of this test. Used in SSID creation.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700144
145 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800146 super(LinuxRouter, self).__init__(host, 'router')
Wade Guthrie24d1e312012-04-24 16:53:40 -0700147
Christopher Wileyeea12362013-12-12 17:24:29 -0800148 self.cmd_dhcpd = '/usr/sbin/dhcpd'
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700149 self.cmd_hostapd = wifi_test_utils.must_be_installed(
Christopher Wileyeea12362013-12-12 17:24:29 -0800150 host, '/usr/sbin/hostapd')
Christopher Wiley7337ff62013-10-03 17:21:46 -0700151 self.cmd_hostapd_cli = wifi_test_utils.must_be_installed(
Christopher Wileyeea12362013-12-12 17:24:29 -0800152 host, '/usr/sbin/hostapd_cli')
Paul Stewart6ddeba72013-11-18 10:08:23 -0800153 self.cmd_wpa_supplicant = wifi_test_utils.must_be_installed(
Christopher Wileyeea12362013-12-12 17:24:29 -0800154 host, '/usr/sbin/wpa_supplicant')
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700155 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
156 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700157
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700158 # hostapd configuration persists throughout the test, subsequent
159 # 'config' commands only modify it.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700160 self._ssid_prefix = test_name
161 if self._ssid_prefix.startswith(self.KNOWN_TEST_PREFIX):
Christopher Wiley3166e432013-08-06 09:53:12 -0700162 # Many of our tests start with an uninteresting prefix.
163 # Remove it so we can have more unique bytes.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700164 self._ssid_prefix = self._ssid_prefix[len(self.KNOWN_TEST_PREFIX):]
165 self._number_unique_ssids = 0
Christopher Wiley3166e432013-08-06 09:53:12 -0700166
Christopher Wileyeea12362013-12-12 17:24:29 -0800167 self._total_hostapd_instances = 0
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700168 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -0800169 self.hostapd_instances = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800170 self.station_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700171 self.dhcp_low = 1
172 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700173
Paul Stewart548cf452012-11-27 17:46:23 -0800174 # Kill hostapd and dhcp server if already running.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700175 self._kill_process_instance('hostapd', timeout_seconds=30)
176 self.stop_dhcp_server(instance=None)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700177
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700178 # Place us in the US by default
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700179 self.iw_runner.set_regulatory_domain('US')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700180
Peter Qiu2f973252014-02-20 15:30:37 -0800181 # Reset all antennas to be active
182 self.set_default_antenna_bitmap()
183
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700184
Christopher Wileyf4bc88b2013-08-29 16:45:15 -0700185 def close(self):
186 """Close global resources held by this system."""
Christopher Wileyeea12362013-12-12 17:24:29 -0800187 self.deconfig()
Christopher Wiley408d1812014-01-13 15:27:43 -0800188 super(LinuxRouter, self).close()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700189
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700190
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700191 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700192 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700193 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700194
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700195
Peter Qiuc89c9a22014-02-27 10:03:55 -0800196 def start_hostapd(self, configuration):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700197 """Start a hostapd instance described by conf.
198
Christopher Wileyeea12362013-12-12 17:24:29 -0800199 @param configuration HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700200
201 """
Paul Stewart548cf452012-11-27 17:46:23 -0800202 # Figure out the correct interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800203 interface = self.get_wlanif(configuration.frequency, 'managed')
Paul Stewart326badb2012-12-18 14:18:54 -0800204
Christopher Wileyeea12362013-12-12 17:24:29 -0800205 conf_file = self.HOSTAPD_CONF_FILE_PATTERN % interface
206 log_file = self.HOSTAPD_LOG_FILE_PATTERN % interface
Paul Stewart80bb3372014-01-22 15:06:08 -0800207 control_interface = self.HOSTAPD_CONTROL_INTERFACE_PATTERN % interface
Peter Qiuc89c9a22014-02-27 10:03:55 -0800208 hostapd_conf_dict = configuration.generate_dict(
209 interface, control_interface,
Christopher Wiley08aafb02014-04-22 17:38:21 -0700210 self._build_unique_ssid(configuration.ssid_suffix))
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700211 logging.debug('hostapd parameters: %r', hostapd_conf_dict)
Paul Stewart548cf452012-11-27 17:46:23 -0800212
213 # Generate hostapd.conf.
Paul Stewart548cf452012-11-27 17:46:23 -0800214 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
215 (conf_file, '\n'.join(
Christopher Wileyeea12362013-12-12 17:24:29 -0800216 "%s=%s" % kv for kv in hostapd_conf_dict.iteritems())))
Paul Stewart548cf452012-11-27 17:46:23 -0800217
218 # Run hostapd.
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700219 logging.info('Starting hostapd on %s(%s) channel=%s...',
220 interface, self.iw_runner.get_interface(interface).phy,
221 configuration.channel)
Christopher Wiley1defc242013-09-18 10:28:37 -0700222 self.router.run('rm %s' % log_file, ignore_status=True)
Christopher Wileyeea12362013-12-12 17:24:29 -0800223 self.router.run('stop wpasupplicant', ignore_status=True)
Christopher Wiley0f64b842014-04-30 15:43:50 -0700224 start_command = '%s -dd -t %s &> %s & echo $!' % (
225 self.cmd_hostapd, conf_file, log_file)
226 pid = int(self.router.run(start_command).stdout.strip())
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700227 self.hostapd_instances.append(HostapdInstance(
228 hostapd_conf_dict['ssid'],
229 conf_file,
230 log_file,
231 interface,
232 hostapd_conf_dict.copy()))
Paul Stewart548cf452012-11-27 17:46:23 -0800233
Christopher Wileyeea12362013-12-12 17:24:29 -0800234 # Wait for confirmation that the router came up.
Christopher Wileyeea12362013-12-12 17:24:29 -0800235 logging.info('Waiting for hostapd to startup.')
236 start_time = time.time()
237 while time.time() - start_time < self.STARTUP_TIMEOUT_SECONDS:
238 success = self.router.run(
239 'grep "Completing interface initialization" %s' % log_file,
240 ignore_status=True).exit_status == 0
241 if success:
242 break
243
Christopher Wileyebafa132014-07-15 10:18:28 -0700244 # TODO(wiley) Remove this once we resolve crbug.com/393667
245 self.host.run('tail -1 %s' % log_file, ignore_status=True)
Christopher Wileyeea12362013-12-12 17:24:29 -0800246 # A common failure is an invalid router configuration.
247 # Detect this and exit early if we see it.
248 bad_config = self.router.run(
249 'grep "Interface initialization failed" %s' % log_file,
250 ignore_status=True).exit_status == 0
251 if bad_config:
252 raise error.TestFail('hostapd failed to initialize AP '
253 'interface.')
254
255 if pid:
256 early_exit = self.router.run('kill -0 %d' % pid,
257 ignore_status=True).exit_status
258 if early_exit:
259 raise error.TestFail('hostapd process terminated.')
260
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700261 time.sleep(self.POLLING_INTERVAL_SECONDS)
Christopher Wileyeea12362013-12-12 17:24:29 -0800262 else:
263 raise error.TestFail('Timed out while waiting for hostapd '
264 'to start.')
265
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700266
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700267 def _kill_process_instance(self,
268 process,
269 instance=None,
270 timeout_seconds=10,
271 ignore_timeouts=False):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700272 """Kill a process on the router.
273
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700274 Kills remote program named |process| (optionally only a specific
275 |instance|). Wait |timeout_seconds| for |process| to die
276 before returning. If |ignore_timeouts| is False, raise
277 a TestError on timeouts.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700278
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700279 @param process: string name of process to kill.
280 @param instance: string fragment of the command line unique to
281 this instance of the remote process.
282 @param timeout_seconds: float timeout in seconds to wait.
283 @param ignore_timeouts: True iff we should ignore failures to
284 kill processes.
285 @return True iff the specified process has exited.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700286
Thieu Le7b23a542012-01-27 15:54:48 -0800287 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700288 if instance is not None:
Christopher Wileya4754c02014-06-30 16:37:52 -0700289 search_arg = '-f "^%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800290 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800291 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800292
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700293 self.host.run('pkill %s' % search_arg, ignore_status=True)
294 is_dead = False
295 start_time = time.time()
296 while not is_dead and time.time() - start_time < timeout_seconds:
297 time.sleep(self.POLLING_INTERVAL_SECONDS)
298 is_dead = self.host.run(
Christopher Wileya4754c02014-06-30 16:37:52 -0700299 'pgrep -l %s' % search_arg,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700300 ignore_status=True).exit_status != 0
301 if is_dead or ignore_timeouts:
302 return is_dead
Paul Stewart326badb2012-12-18 14:18:54 -0800303
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700304 raise error.TestError(
305 'Timed out waiting for %s%s to die' %
306 (process,
307 '' if instance is None else ' (instance=%s)' % instance))
Paul Stewart326badb2012-12-18 14:18:54 -0800308
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700309
Paul Stewart326badb2012-12-18 14:18:54 -0800310 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700311 """Kills a hostapd instance.
312
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700313 @param instance HostapdInstance object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700314
315 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700316 is_dead = self._kill_process_instance(
Christopher Wileya4754c02014-06-30 16:37:52 -0700317 self.cmd_hostapd,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700318 instance=instance.conf_file,
319 timeout_seconds=30,
320 ignore_timeouts=True)
321 if self.host.run('ls %s >/dev/null 2>&1' % instance.log_file,
322 ignore_status=True).exit_status:
323 logging.error('Did not collect hostapd log file because '
324 'it was missing.')
325 else:
326 self.router.get_file(instance.log_file,
327 'debug/hostapd_router_%d_%s.log' %
328 (self._total_hostapd_instances,
329 instance.interface))
330 self._total_hostapd_instances += 1
331 if not is_dead:
332 raise error.TestError('Timed out killing hostapd.')
Paul Stewart21737812012-12-06 11:03:32 -0800333
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700334
Christopher Wiley08aafb02014-04-22 17:38:21 -0700335 def _build_unique_ssid(self, suffix):
336 # Build our unique token by base-<len(self.SUFFIX_LETTERS)> encoding
337 # the number of APs we've constructed already.
338 base = len(self.SUFFIX_LETTERS)
339 number = self._number_unique_ssids
340 self._number_unique_ssids += 1
341 unique = ''
342 while number or not unique:
343 unique = self.SUFFIX_LETTERS[number % base] + unique
344 number = number / base
345 # And salt the SSID so that tests running in adjacent cells are unlikely
346 # to pick the same SSID and we're resistent to beacons leaking out of
347 # cells.
348 salt = ''.join([random.choice(self.SUFFIX_LETTERS) for x in range(5)])
349 return '_'.join([self._ssid_prefix, unique, salt, suffix])[-32:]
Christopher Wiley3166e432013-08-06 09:53:12 -0700350
351
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700352 def hostap_configure(self, configuration, multi_interface=None):
353 """Build up a hostapd configuration file and start hostapd.
354
355 Also setup a local server if this router supports them.
356
357 @param configuration HosetapConfig object.
358 @param multi_interface bool True iff multiple interfaces allowed.
359
360 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800361 if multi_interface is None and (self.hostapd_instances or
Christopher Wiley408d1812014-01-13 15:27:43 -0800362 self.station_instances):
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700363 self.deconfig()
Peter Qiuc89c9a22014-02-27 10:03:55 -0800364 self.start_hostapd(configuration)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700365 interface = self.hostapd_instances[-1].interface
Christopher Wileyeea12362013-12-12 17:24:29 -0800366 self.iw_runner.set_tx_power(interface, 'auto')
367 self.start_local_server(interface)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700368 logging.info('AP configured.')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700369
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700370
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700371 def ibss_configure(self, config):
372 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700373
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700374 Extract relevant configuration objects from |config| despite not
375 actually being a hostap managed endpoint.
376
377 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700378
379 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800380 if self.station_instances or self.hostapd_instances:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700381 self.deconfig()
Christopher Wiley408d1812014-01-13 15:27:43 -0800382 interface = self.get_wlanif(config.frequency, 'ibss')
Christopher Wiley08aafb02014-04-22 17:38:21 -0700383 ssid = (config.ssid or self._build_unique_ssid(config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800384 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700385 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley408d1812014-01-13 15:27:43 -0800386 self.iw_runner.ibss_join(interface, ssid, config.frequency)
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700387 # Always start a local server.
388 self.start_local_server(interface)
389 # Remember that this interface is up.
Christopher Wiley408d1812014-01-13 15:27:43 -0800390 self.station_instances.append(
391 StationInstance(ssid=ssid, interface=interface,
392 dev_type='ibss'))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800393
394
Paul Stewart2bd823b2012-11-21 15:03:37 -0800395 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700396 """Get the local server address for an interface.
397
398 When we multiple local servers, we give them static IP addresses
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700399 like 192.168.*.254.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700400
401 @param index int describing which local server this is for.
402
403 """
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700404 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254))
Paul Stewart2bd823b2012-11-21 15:03:37 -0800405
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700406
Paul Stewart6ddeba72013-11-18 10:08:23 -0800407 def local_peer_ip_address(self, index):
408 """Get the IP address allocated for the peer associated to the AP.
409
410 This address is assigned to a locally associated peer device that
411 is created for the DUT to perform connectivity tests with.
412 When we have multiple local servers, we give them static IP addresses
413 like 192.168.*.253.
414
415 @param index int describing which local server this is for.
416
417 """
418 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253))
419
420
421 def local_peer_mac_address(self):
422 """Get the MAC address of the peer interface.
423
424 @return string MAC address of the peer interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800425
Paul Stewart6ddeba72013-11-18 10:08:23 -0800426 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800427 iface = interface.Interface(self.station_instances[0].interface,
428 self.router)
Paul Stewart6ddeba72013-11-18 10:08:23 -0800429 return iface.mac_address
430
431
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700432 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700433 """Start a local server on an interface.
434
435 @param interface string (e.g. wlan0)
436
437 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700438 logging.info('Starting up local server...')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700439
440 if len(self.local_servers) >= 256:
441 raise error.TestFail('Exhausted available local servers')
442
Christopher Wiley594570d2014-03-14 16:50:15 -0700443 server_addr = netblock.Netblock.from_addr(
444 self.local_server_address(len(self.local_servers)),
445 prefix_len=24)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700446
447 params = {}
Christopher Wiley594570d2014-03-14 16:50:15 -0700448 params['netblock'] = server_addr
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700449 params['dhcp_range'] = ' '.join(
Christopher Wiley594570d2014-03-14 16:50:15 -0700450 (server_addr.get_addr_in_block(1),
451 server_addr.get_addr_in_block(128)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700452 params['interface'] = interface
Christopher Wiley594570d2014-03-14 16:50:15 -0700453 params['ip_params'] = ('%s broadcast %s dev %s' %
454 (server_addr.netblock,
455 server_addr.broadcast,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700456 interface))
457 self.local_servers.append(params)
458
Christopher Wiley594570d2014-03-14 16:50:15 -0700459 self.router.run('%s addr flush %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700460 (self.cmd_ip, interface))
Christopher Wiley594570d2014-03-14 16:50:15 -0700461 self.router.run('%s addr add %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700462 (self.cmd_ip, params['ip_params']))
Christopher Wiley594570d2014-03-14 16:50:15 -0700463 self.router.run('%s link set %s up' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700464 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800465 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700466
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700467
Paul Stewart548cf452012-11-27 17:46:23 -0800468 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700469 """Start a dhcp server on an interface.
470
471 @param interface string (e.g. wlan0)
472
473 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800474 for server in self.local_servers:
475 if server['interface'] == interface:
476 params = server
477 break
478 else:
479 raise error.TestFail('Could not find local server '
480 'to match interface: %r' % interface)
Christopher Wiley594570d2014-03-14 16:50:15 -0700481 server_addr = params['netblock']
Christopher Wileyeea12362013-12-12 17:24:29 -0800482 dhcpd_conf_file = self.dhcpd_conf % interface
483 dhcp_conf = '\n'.join([
484 'port=0', # disables DNS server
485 'bind-interfaces',
486 'log-dhcp',
Christopher Wiley594570d2014-03-14 16:50:15 -0700487 'dhcp-range=%s' % ','.join((server_addr.get_addr_in_block(1),
488 server_addr.get_addr_in_block(128))),
Christopher Wileyeea12362013-12-12 17:24:29 -0800489 'interface=%s' % params['interface'],
490 'dhcp-leasefile=%s' % self.dhcpd_leases])
491 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
492 (dhcpd_conf_file, dhcp_conf))
493 self.router.run('dnsmasq --conf-file=%s' % dhcpd_conf_file)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700494
495
Paul Stewart326badb2012-12-18 14:18:54 -0800496 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700497 """Stop a dhcp server on the router.
498
499 @param instance string instance to kill.
500
501 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700502 self._kill_process_instance('dnsmasq', instance=instance)
Paul Stewart548cf452012-11-27 17:46:23 -0800503
504
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800505 def get_wifi_channel(self, ap_num):
506 """Return channel of BSS corresponding to |ap_num|.
507
508 @param ap_num int which BSS to get the channel of.
509 @return int primary channel of BSS.
510
511 """
512 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700513 return instance.config_dict['channel']
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800514
515
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700516 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700517 """Return IP address on the WiFi subnet of a local server on the router.
518
519 If no local servers are configured (e.g. for an RSPro), a TestFail will
520 be raised.
521
522 @param ap_num int which local server to get an address from.
523
524 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700525 if not self.local_servers:
526 raise error.TestError('No IP address assigned')
527
528 return self.local_servers[ap_num]['netblock'].addr
529
530
531 def get_wifi_ip_subnet(self, ap_num):
532 """Return subnet of WiFi AP instance.
533
534 If no APs are configured a TestError will be raised.
535
536 @param ap_num int which local server to get an address from.
537
538 """
539 if not self.local_servers:
540 raise error.TestError('No APs configured.')
541
542 return self.local_servers[ap_num]['netblock'].subnet
Paul Stewart5977da92011-06-01 19:14:08 -0700543
544
Christopher Wileya3effac2014-02-05 11:16:11 -0800545 def get_hostapd_interface(self, ap_num):
546 """Get the name of the interface associated with a hostapd instance.
547
548 @param ap_num: int hostapd instance number.
549 @return string interface name (e.g. 'managed0').
550
551 """
552 if ap_num not in range(len(self.hostapd_instances)):
553 raise error.TestFail('Invalid instance number (%d) with %d '
554 'instances configured.' %
555 (ap_num, len(self.hostapd_instances)))
556
557 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700558 return instance.interface
Christopher Wileya3effac2014-02-05 11:16:11 -0800559
560
Paul Stewart17350be2012-12-14 13:34:54 -0800561 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700562 """Return the MAC address of an AP in the test.
563
564 @param ap_num int index of local server to read the MAC address from.
565 @return string MAC address like 00:11:22:33:44:55.
566
567 """
Paul Stewart2e5313a2014-02-14 09:12:02 -0800568 interface_name = self.get_hostapd_interface(ap_num)
569 ap_interface = interface.Interface(interface_name, self.host)
Christopher Wiley5689d362014-01-07 15:21:25 -0800570 return ap_interface.mac_address
Paul Stewart17350be2012-12-14 13:34:54 -0800571
572
Christopher Wileya3effac2014-02-05 11:16:11 -0800573 def get_hostapd_phy(self, ap_num):
574 """Get name of phy for hostapd instance.
575
576 @param ap_num int index of hostapd instance.
577 @return string phy name of phy corresponding to hostapd's
578 managed interface.
579
580 """
581 interface = self.iw_runner.get_interface(
582 self.get_hostapd_interface(ap_num))
583 return interface.phy
584
585
Christopher Wileyeea12362013-12-12 17:24:29 -0800586 def deconfig(self):
587 """A legacy, deprecated alias for deconfig_aps."""
588 self.deconfig_aps()
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800589
590
591 def deconfig_aps(self, instance=None, silent=False):
592 """De-configure an AP (will also bring wlan down).
593
594 @param instance: int or None. If instance is None, will bring down all
595 instances of hostapd.
596 @param silent: True if instances should be brought without de-authing
597 the DUT.
598
599 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800600 if not self.hostapd_instances and not self.station_instances:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700601 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700602
Christopher Wileyeea12362013-12-12 17:24:29 -0800603 if self.hostapd_instances:
Paul Stewart326badb2012-12-18 14:18:54 -0800604 local_servers = []
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800605 if instance is not None:
606 instances = [ self.hostapd_instances.pop(instance) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800607 for server in self.local_servers:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700608 if server['interface'] == instances[0].interface:
Paul Stewart326badb2012-12-18 14:18:54 -0800609 local_servers = [server]
610 self.local_servers.remove(server)
611 break
Paul Stewart21737812012-12-06 11:03:32 -0800612 else:
613 instances = self.hostapd_instances
614 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800615 local_servers = self.local_servers
616 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700617
Paul Stewart21737812012-12-06 11:03:32 -0800618 for instance in instances:
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800619 if silent:
Paul Stewart21737812012-12-06 11:03:32 -0800620 # Deconfigure without notifying DUT. Remove the interface
621 # hostapd uses to send beacon and DEAUTH packets.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700622 self.remove_interface(instance.interface)
Paul Stewart21737812012-12-06 11:03:32 -0800623
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700624 self.kill_hostapd_instance(instance)
625 self.release_interface(instance.interface)
Christopher Wiley408d1812014-01-13 15:27:43 -0800626 if self.station_instances:
Christopher Wiley05262d62013-04-17 17:53:59 -0700627 local_servers = self.local_servers
628 self.local_servers = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800629 instance = self.station_instances.pop()
630 if instance.dev_type == 'ibss':
631 self.iw_runner.ibss_leave(instance.interface)
632 elif instance.dev_type == 'managed':
Christopher Wileya4754c02014-06-30 16:37:52 -0700633 self._kill_process_instance(self.cmd_wpa_supplicant,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700634 instance=instance.interface)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800635 else:
Christopher Wiley408d1812014-01-13 15:27:43 -0800636 self.iw_runner.disconnect_station(instance.interface)
637 self.router.run('%s link set %s down' %
638 (self.cmd_ip, instance.interface))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700639
Paul Stewart326badb2012-12-18 14:18:54 -0800640 for server in local_servers:
641 self.stop_dhcp_server(server['interface'])
642 self.router.run("%s addr del %s" %
643 (self.cmd_ip, server['ip_params']),
644 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700645
Paul Stewart7cb1f062010-06-10 15:46:20 -0700646
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800647 def confirm_pmksa_cache_use(self, instance=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700648 """Verify that the PMKSA auth was cached on a hostapd instance.
649
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800650 @param instance int router instance number.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700651
652 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700653 log_file = self.hostapd_instances[instance].log_file
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800654 pmksa_match = 'PMK from PMKSA cache'
655 result = self.router.run('grep -q "%s" %s' % (pmksa_match, log_file),
656 ignore_status=True)
657 if result.exit_status:
658 raise error.TestFail('PMKSA cache was not used in roaming.')
Paul Stewart17350be2012-12-14 13:34:54 -0800659
660
Christopher Wileye0afecb2013-11-11 10:54:23 -0800661 def get_ssid(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700662 """@return string ssid for the network stemming from this router."""
Christopher Wileye0afecb2013-11-11 10:54:23 -0800663 if instance is None:
664 instance = 0
665 if len(self.hostapd_instances) > 1:
666 raise error.TestFail('No instance of hostapd specified with '
667 'multiple instances present.')
668
Christopher Wiley3099be72013-11-06 16:49:02 -0800669 if self.hostapd_instances:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700670 return self.hostapd_instances[instance].ssid
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700671
Christopher Wiley408d1812014-01-13 15:27:43 -0800672 if self.station_instances:
673 return self.station_instances[0].ssid
Christopher Wiley3166e432013-08-06 09:53:12 -0700674
Christopher Wiley408d1812014-01-13 15:27:43 -0800675 raise error.TestFail('Requested ssid of an unconfigured AP.')
Paul Stewart98022e22010-10-22 10:33:14 -0700676
677
Wade Guthriee4074dd2013-10-30 11:00:48 -0700678 def deauth_client(self, client_mac):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700679 """Deauthenticates a client described in params.
680
Wade Guthriee4074dd2013-10-30 11:00:48 -0700681 @param client_mac string containing the mac address of the client to be
682 deauthenticated.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700683
684 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700685 control_if = self.hostapd_instances[-1].config_dict['ctrl_interface']
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700686 self.router.run('%s -p%s deauthenticate %s' %
Paul Stewart80bb3372014-01-22 15:06:08 -0800687 (self.cmd_hostapd_cli, control_if, client_mac))
Wade Guthriee4074dd2013-10-30 11:00:48 -0700688
689
Peter Qiuc4beba02014-03-24 14:46:24 -0700690 def send_management_frame_on_ap(self, frame_type, channel, instance=0):
Paul Stewart51b0f382013-06-12 09:03:02 -0700691 """Injects a management frame into an active hostapd session.
692
693 @param frame_type string the type of frame to send.
Peter Qiuc4beba02014-03-24 14:46:24 -0700694 @param channel int targeted channel
Paul Stewart51b0f382013-06-12 09:03:02 -0700695 @param instance int indicating which hostapd instance to inject into.
696
697 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700698 hostap_interface = self.hostapd_instances[instance].interface
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800699 interface = self.get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700700 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
Peter Qiuc4beba02014-03-24 14:46:24 -0700701 self.router.run('%s -i %s -t %s -c %d' %
702 (self.cmd_send_management_frame, interface, frame_type,
703 channel))
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800704 self.release_interface(interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700705
706
Peter Qiuc4beba02014-03-24 14:46:24 -0700707 def setup_management_frame_interface(self, channel):
708 """
709 Setup interface for injecting management frames.
710
711 @param channel int channel to inject the frames.
712
713 @return string name of the interface.
714
715 """
716 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
717 channel)
718 interface = self.get_wlanif(frequency, 'monitor')
719 self.iw_runner.set_freq(interface, frequency)
720 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
721 return interface
722
723
724 def send_management_frame(self, interface, frame_type, channel,
725 ssid_prefix=None, num_bss=None,
726 frame_count=None, delay=None):
727 """
728 Injects management frames on specify channel |frequency|.
729
730 This function will spawn off a new process to inject specified
731 management frames |frame_type| at the specified interface |interface|.
732
733 @param interface string interface to inject frames.
734 @param frame_type string message type.
735 @param channel int targeted channel.
736 @param ssid_prefix string SSID prefix.
737 @param num_bss int number of BSS.
738 @param frame_count int number of frames to send.
739 @param delay int milliseconds delay between frames.
740
741 @return int PID of the newly created process.
742
743 """
744 command = '%s -i %s -t %s -c %d' % (self.cmd_send_management_frame,
745 interface, frame_type, channel)
746 if ssid_prefix is not None:
747 command += ' -s %s' % (ssid_prefix)
748 if num_bss is not None:
749 command += ' -b %d' % (num_bss)
750 if frame_count is not None:
751 command += ' -n %d' % (frame_count)
752 if delay is not None:
753 command += ' -d %d' % (delay)
754 command += ' > %s 2>&1 & echo $!' % (self.MGMT_FRAME_SENDER_LOG_FILE)
755 pid = int(self.router.run(command).stdout)
756 return pid
757
758
Paul Stewart25536942013-08-15 17:33:42 -0700759 def detect_client_deauth(self, client_mac, instance=0):
760 """Detects whether hostapd has logged a deauthentication from
761 |client_mac|.
762
763 @param client_mac string the MAC address of the client to detect.
764 @param instance int indicating which hostapd instance to query.
765
766 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700767 interface = self.hostapd_instances[instance].interface
Paul Stewart25536942013-08-15 17:33:42 -0700768 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700769 log_file = self.hostapd_instances[instance].log_file
Paul Stewart25536942013-08-15 17:33:42 -0700770 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
771 ignore_status=True)
772 return result.exit_status == 0
773
774
Paul Stewart4ae471e2013-09-04 15:42:35 -0700775 def detect_client_coexistence_report(self, client_mac, instance=0):
776 """Detects whether hostapd has logged an action frame from
777 |client_mac| indicating information about 20/40MHz BSS coexistence.
778
779 @param client_mac string the MAC address of the client to detect.
780 @param instance int indicating which hostapd instance to query.
781
782 """
783 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): '
784 '.. .. .. .. .. .. .. .. .. .. %s '
785 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' %
786 ' '.join(client_mac.split(':')))
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700787 log_file = self.hostapd_instances[instance].log_file
Paul Stewart4ae471e2013-09-04 15:42:35 -0700788 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file),
789 ignore_status=True)
790 return result.exit_status == 0
791
792
Paul Stewart6ddeba72013-11-18 10:08:23 -0800793 def add_connected_peer(self, instance=0):
794 """Configure a station connected to a running AP instance.
795
796 Extract relevant configuration objects from the hostap
797 configuration for |instance| and generate a wpa_supplicant
798 instance that connects to it. This allows the DUT to interact
799 with a client entity that is also connected to the same AP. A
800 full wpa_supplicant instance is necessary here (instead of just
801 using the "iw" command to connect) since we want to enable
802 advanced features such as TDLS.
803
804 @param instance int indicating which hostapd instance to connect to.
805
806 """
807 if not self.hostapd_instances:
808 raise error.TestFail('Hostapd is not configured.')
809
Christopher Wiley408d1812014-01-13 15:27:43 -0800810 if self.station_instances:
Paul Stewart6ddeba72013-11-18 10:08:23 -0800811 raise error.TestFail('Station is already configured.')
812
Christopher Wiley408d1812014-01-13 15:27:43 -0800813 ssid = self.get_ssid(instance)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700814 hostap_conf = self.hostapd_instances[instance].config_dict
Paul Stewart6ddeba72013-11-18 10:08:23 -0800815 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
816 hostap_conf['channel'])
Christopher Wiley408d1812014-01-13 15:27:43 -0800817 interface = self.get_wlanif(frequency, 'managed')
Paul Stewart6ddeba72013-11-18 10:08:23 -0800818
819 # TODO(pstew): Configure other bits like PSK, 802.11n if tests
820 # require them...
821 supplicant_config = (
822 'network={\n'
823 ' ssid="%(ssid)s"\n'
824 ' key_mgmt=NONE\n'
Christopher Wiley408d1812014-01-13 15:27:43 -0800825 '}\n' % {'ssid': ssid}
Paul Stewart6ddeba72013-11-18 10:08:23 -0800826 )
827
Christopher Wileyeea12362013-12-12 17:24:29 -0800828 conf_file = self.STATION_CONF_FILE_PATTERN % interface
829 log_file = self.STATION_LOG_FILE_PATTERN % interface
830 pid_file = self.STATION_PID_FILE_PATTERN % interface
Paul Stewart6ddeba72013-11-18 10:08:23 -0800831
832 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
833 (conf_file, supplicant_config))
834
835 # Connect the station.
836 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
837 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s &> %s &' %
838 (self.cmd_wpa_supplicant,
839 interface, pid_file, conf_file,
Christopher Wileyeea12362013-12-12 17:24:29 -0800840 self.HOSTAPD_DRIVER_NAME, log_file))
Paul Stewart6ddeba72013-11-18 10:08:23 -0800841 self.router.run(start_command)
842 self.iw_runner.wait_for_link(interface)
843
844 # Assign an IP address to this interface.
845 self.router.run('%s addr add %s/24 dev %s' %
846 (self.cmd_ip, self.local_peer_ip_address(instance),
847 interface))
848
849 # Since we now have two network interfaces connected to the same
850 # network, we need to disable the kernel's protection against
851 # incoming packets to an "unexpected" interface.
852 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' %
853 interface)
854
Paul Stewartb01839b2013-12-06 15:49:56 -0800855 # Similarly, we'd like to prevent the hostap interface from
856 # replying to ARP requests for the peer IP address and vice
857 # versa.
858 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
859 interface)
860 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
861 hostap_conf['interface'])
862
Christopher Wiley408d1812014-01-13 15:27:43 -0800863 self.station_instances.append(
864 StationInstance(ssid=ssid, interface=interface,
865 dev_type='managed'))