blob: bbd296b7ad965d88149567d862eceb30103704be [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
244 # A common failure is an invalid router configuration.
245 # Detect this and exit early if we see it.
246 bad_config = self.router.run(
247 'grep "Interface initialization failed" %s' % log_file,
248 ignore_status=True).exit_status == 0
249 if bad_config:
250 raise error.TestFail('hostapd failed to initialize AP '
251 'interface.')
252
253 if pid:
254 early_exit = self.router.run('kill -0 %d' % pid,
255 ignore_status=True).exit_status
256 if early_exit:
257 raise error.TestFail('hostapd process terminated.')
258
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700259 time.sleep(self.POLLING_INTERVAL_SECONDS)
Christopher Wileyeea12362013-12-12 17:24:29 -0800260 else:
261 raise error.TestFail('Timed out while waiting for hostapd '
262 'to start.')
263
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700264
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700265 def _kill_process_instance(self,
266 process,
267 instance=None,
268 timeout_seconds=10,
269 ignore_timeouts=False):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700270 """Kill a process on the router.
271
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700272 Kills remote program named |process| (optionally only a specific
273 |instance|). Wait |timeout_seconds| for |process| to die
274 before returning. If |ignore_timeouts| is False, raise
275 a TestError on timeouts.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700276
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700277 @param process: string name of process to kill.
278 @param instance: string fragment of the command line unique to
279 this instance of the remote process.
280 @param timeout_seconds: float timeout in seconds to wait.
281 @param ignore_timeouts: True iff we should ignore failures to
282 kill processes.
283 @return True iff the specified process has exited.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700284
Thieu Le7b23a542012-01-27 15:54:48 -0800285 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700286 if instance is not None:
Christopher Wileya4754c02014-06-30 16:37:52 -0700287 search_arg = '-f "^%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800288 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800289 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800290
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700291 self.host.run('pkill %s' % search_arg, ignore_status=True)
292 is_dead = False
293 start_time = time.time()
294 while not is_dead and time.time() - start_time < timeout_seconds:
295 time.sleep(self.POLLING_INTERVAL_SECONDS)
296 is_dead = self.host.run(
Christopher Wileya4754c02014-06-30 16:37:52 -0700297 'pgrep -l %s' % search_arg,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700298 ignore_status=True).exit_status != 0
299 if is_dead or ignore_timeouts:
300 return is_dead
Paul Stewart326badb2012-12-18 14:18:54 -0800301
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700302 raise error.TestError(
303 'Timed out waiting for %s%s to die' %
304 (process,
305 '' if instance is None else ' (instance=%s)' % instance))
Paul Stewart326badb2012-12-18 14:18:54 -0800306
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700307
Paul Stewart326badb2012-12-18 14:18:54 -0800308 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700309 """Kills a hostapd instance.
310
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700311 @param instance HostapdInstance object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700312
313 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700314 is_dead = self._kill_process_instance(
Christopher Wileya4754c02014-06-30 16:37:52 -0700315 self.cmd_hostapd,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700316 instance=instance.conf_file,
317 timeout_seconds=30,
318 ignore_timeouts=True)
319 if self.host.run('ls %s >/dev/null 2>&1' % instance.log_file,
320 ignore_status=True).exit_status:
321 logging.error('Did not collect hostapd log file because '
322 'it was missing.')
323 else:
324 self.router.get_file(instance.log_file,
325 'debug/hostapd_router_%d_%s.log' %
326 (self._total_hostapd_instances,
327 instance.interface))
328 self._total_hostapd_instances += 1
329 if not is_dead:
330 raise error.TestError('Timed out killing hostapd.')
Paul Stewart21737812012-12-06 11:03:32 -0800331
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700332
Christopher Wiley08aafb02014-04-22 17:38:21 -0700333 def _build_unique_ssid(self, suffix):
334 # Build our unique token by base-<len(self.SUFFIX_LETTERS)> encoding
335 # the number of APs we've constructed already.
336 base = len(self.SUFFIX_LETTERS)
337 number = self._number_unique_ssids
338 self._number_unique_ssids += 1
339 unique = ''
340 while number or not unique:
341 unique = self.SUFFIX_LETTERS[number % base] + unique
342 number = number / base
343 # And salt the SSID so that tests running in adjacent cells are unlikely
344 # to pick the same SSID and we're resistent to beacons leaking out of
345 # cells.
346 salt = ''.join([random.choice(self.SUFFIX_LETTERS) for x in range(5)])
347 return '_'.join([self._ssid_prefix, unique, salt, suffix])[-32:]
Christopher Wiley3166e432013-08-06 09:53:12 -0700348
349
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700350 def hostap_configure(self, configuration, multi_interface=None):
351 """Build up a hostapd configuration file and start hostapd.
352
353 Also setup a local server if this router supports them.
354
355 @param configuration HosetapConfig object.
356 @param multi_interface bool True iff multiple interfaces allowed.
357
358 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800359 if multi_interface is None and (self.hostapd_instances or
Christopher Wiley408d1812014-01-13 15:27:43 -0800360 self.station_instances):
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700361 self.deconfig()
Peter Qiuc89c9a22014-02-27 10:03:55 -0800362 self.start_hostapd(configuration)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700363 interface = self.hostapd_instances[-1].interface
Christopher Wileyeea12362013-12-12 17:24:29 -0800364 self.iw_runner.set_tx_power(interface, 'auto')
365 self.start_local_server(interface)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700366 logging.info('AP configured.')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700367
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700368
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700369 def ibss_configure(self, config):
370 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700371
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700372 Extract relevant configuration objects from |config| despite not
373 actually being a hostap managed endpoint.
374
375 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700376
377 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800378 if self.station_instances or self.hostapd_instances:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700379 self.deconfig()
Christopher Wiley408d1812014-01-13 15:27:43 -0800380 interface = self.get_wlanif(config.frequency, 'ibss')
Christopher Wiley08aafb02014-04-22 17:38:21 -0700381 ssid = (config.ssid or self._build_unique_ssid(config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800382 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700383 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley408d1812014-01-13 15:27:43 -0800384 self.iw_runner.ibss_join(interface, ssid, config.frequency)
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700385 # Always start a local server.
386 self.start_local_server(interface)
387 # Remember that this interface is up.
Christopher Wiley408d1812014-01-13 15:27:43 -0800388 self.station_instances.append(
389 StationInstance(ssid=ssid, interface=interface,
390 dev_type='ibss'))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800391
392
Paul Stewart2bd823b2012-11-21 15:03:37 -0800393 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700394 """Get the local server address for an interface.
395
396 When we multiple local servers, we give them static IP addresses
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700397 like 192.168.*.254.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700398
399 @param index int describing which local server this is for.
400
401 """
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700402 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254))
Paul Stewart2bd823b2012-11-21 15:03:37 -0800403
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700404
Paul Stewart6ddeba72013-11-18 10:08:23 -0800405 def local_peer_ip_address(self, index):
406 """Get the IP address allocated for the peer associated to the AP.
407
408 This address is assigned to a locally associated peer device that
409 is created for the DUT to perform connectivity tests with.
410 When we have multiple local servers, we give them static IP addresses
411 like 192.168.*.253.
412
413 @param index int describing which local server this is for.
414
415 """
416 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253))
417
418
419 def local_peer_mac_address(self):
420 """Get the MAC address of the peer interface.
421
422 @return string MAC address of the peer interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800423
Paul Stewart6ddeba72013-11-18 10:08:23 -0800424 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800425 iface = interface.Interface(self.station_instances[0].interface,
426 self.router)
Paul Stewart6ddeba72013-11-18 10:08:23 -0800427 return iface.mac_address
428
429
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700430 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700431 """Start a local server on an interface.
432
433 @param interface string (e.g. wlan0)
434
435 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700436 logging.info('Starting up local server...')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700437
438 if len(self.local_servers) >= 256:
439 raise error.TestFail('Exhausted available local servers')
440
Christopher Wiley594570d2014-03-14 16:50:15 -0700441 server_addr = netblock.Netblock.from_addr(
442 self.local_server_address(len(self.local_servers)),
443 prefix_len=24)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700444
445 params = {}
Christopher Wiley594570d2014-03-14 16:50:15 -0700446 params['netblock'] = server_addr
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700447 params['dhcp_range'] = ' '.join(
Christopher Wiley594570d2014-03-14 16:50:15 -0700448 (server_addr.get_addr_in_block(1),
449 server_addr.get_addr_in_block(128)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700450 params['interface'] = interface
Christopher Wiley594570d2014-03-14 16:50:15 -0700451 params['ip_params'] = ('%s broadcast %s dev %s' %
452 (server_addr.netblock,
453 server_addr.broadcast,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700454 interface))
455 self.local_servers.append(params)
456
Christopher Wiley594570d2014-03-14 16:50:15 -0700457 self.router.run('%s addr flush %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700458 (self.cmd_ip, interface))
Christopher Wiley594570d2014-03-14 16:50:15 -0700459 self.router.run('%s addr add %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700460 (self.cmd_ip, params['ip_params']))
Christopher Wiley594570d2014-03-14 16:50:15 -0700461 self.router.run('%s link set %s up' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700462 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800463 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700464
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700465
Paul Stewart548cf452012-11-27 17:46:23 -0800466 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700467 """Start a dhcp server on an interface.
468
469 @param interface string (e.g. wlan0)
470
471 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800472 for server in self.local_servers:
473 if server['interface'] == interface:
474 params = server
475 break
476 else:
477 raise error.TestFail('Could not find local server '
478 'to match interface: %r' % interface)
Christopher Wiley594570d2014-03-14 16:50:15 -0700479 server_addr = params['netblock']
Christopher Wileyeea12362013-12-12 17:24:29 -0800480 dhcpd_conf_file = self.dhcpd_conf % interface
481 dhcp_conf = '\n'.join([
482 'port=0', # disables DNS server
483 'bind-interfaces',
484 'log-dhcp',
Christopher Wiley594570d2014-03-14 16:50:15 -0700485 'dhcp-range=%s' % ','.join((server_addr.get_addr_in_block(1),
486 server_addr.get_addr_in_block(128))),
Christopher Wileyeea12362013-12-12 17:24:29 -0800487 'interface=%s' % params['interface'],
488 'dhcp-leasefile=%s' % self.dhcpd_leases])
489 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
490 (dhcpd_conf_file, dhcp_conf))
491 self.router.run('dnsmasq --conf-file=%s' % dhcpd_conf_file)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700492
493
Paul Stewart326badb2012-12-18 14:18:54 -0800494 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700495 """Stop a dhcp server on the router.
496
497 @param instance string instance to kill.
498
499 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700500 self._kill_process_instance('dnsmasq', instance=instance)
Paul Stewart548cf452012-11-27 17:46:23 -0800501
502
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800503 def get_wifi_channel(self, ap_num):
504 """Return channel of BSS corresponding to |ap_num|.
505
506 @param ap_num int which BSS to get the channel of.
507 @return int primary channel of BSS.
508
509 """
510 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700511 return instance.config_dict['channel']
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800512
513
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700514 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700515 """Return IP address on the WiFi subnet of a local server on the router.
516
517 If no local servers are configured (e.g. for an RSPro), a TestFail will
518 be raised.
519
520 @param ap_num int which local server to get an address from.
521
522 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700523 if not self.local_servers:
524 raise error.TestError('No IP address assigned')
525
526 return self.local_servers[ap_num]['netblock'].addr
527
528
529 def get_wifi_ip_subnet(self, ap_num):
530 """Return subnet of WiFi AP instance.
531
532 If no APs are configured a TestError will be raised.
533
534 @param ap_num int which local server to get an address from.
535
536 """
537 if not self.local_servers:
538 raise error.TestError('No APs configured.')
539
540 return self.local_servers[ap_num]['netblock'].subnet
Paul Stewart5977da92011-06-01 19:14:08 -0700541
542
Christopher Wileya3effac2014-02-05 11:16:11 -0800543 def get_hostapd_interface(self, ap_num):
544 """Get the name of the interface associated with a hostapd instance.
545
546 @param ap_num: int hostapd instance number.
547 @return string interface name (e.g. 'managed0').
548
549 """
550 if ap_num not in range(len(self.hostapd_instances)):
551 raise error.TestFail('Invalid instance number (%d) with %d '
552 'instances configured.' %
553 (ap_num, len(self.hostapd_instances)))
554
555 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700556 return instance.interface
Christopher Wileya3effac2014-02-05 11:16:11 -0800557
558
Paul Stewart17350be2012-12-14 13:34:54 -0800559 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700560 """Return the MAC address of an AP in the test.
561
562 @param ap_num int index of local server to read the MAC address from.
563 @return string MAC address like 00:11:22:33:44:55.
564
565 """
Paul Stewart2e5313a2014-02-14 09:12:02 -0800566 interface_name = self.get_hostapd_interface(ap_num)
567 ap_interface = interface.Interface(interface_name, self.host)
Christopher Wiley5689d362014-01-07 15:21:25 -0800568 return ap_interface.mac_address
Paul Stewart17350be2012-12-14 13:34:54 -0800569
570
Christopher Wileya3effac2014-02-05 11:16:11 -0800571 def get_hostapd_phy(self, ap_num):
572 """Get name of phy for hostapd instance.
573
574 @param ap_num int index of hostapd instance.
575 @return string phy name of phy corresponding to hostapd's
576 managed interface.
577
578 """
579 interface = self.iw_runner.get_interface(
580 self.get_hostapd_interface(ap_num))
581 return interface.phy
582
583
Christopher Wileyeea12362013-12-12 17:24:29 -0800584 def deconfig(self):
585 """A legacy, deprecated alias for deconfig_aps."""
586 self.deconfig_aps()
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800587
588
589 def deconfig_aps(self, instance=None, silent=False):
590 """De-configure an AP (will also bring wlan down).
591
592 @param instance: int or None. If instance is None, will bring down all
593 instances of hostapd.
594 @param silent: True if instances should be brought without de-authing
595 the DUT.
596
597 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800598 if not self.hostapd_instances and not self.station_instances:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700599 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700600
Christopher Wileyeea12362013-12-12 17:24:29 -0800601 if self.hostapd_instances:
Paul Stewart326badb2012-12-18 14:18:54 -0800602 local_servers = []
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800603 if instance is not None:
604 instances = [ self.hostapd_instances.pop(instance) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800605 for server in self.local_servers:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700606 if server['interface'] == instances[0].interface:
Paul Stewart326badb2012-12-18 14:18:54 -0800607 local_servers = [server]
608 self.local_servers.remove(server)
609 break
Paul Stewart21737812012-12-06 11:03:32 -0800610 else:
611 instances = self.hostapd_instances
612 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800613 local_servers = self.local_servers
614 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700615
Paul Stewart21737812012-12-06 11:03:32 -0800616 for instance in instances:
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800617 if silent:
Paul Stewart21737812012-12-06 11:03:32 -0800618 # Deconfigure without notifying DUT. Remove the interface
619 # hostapd uses to send beacon and DEAUTH packets.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700620 self.remove_interface(instance.interface)
Paul Stewart21737812012-12-06 11:03:32 -0800621
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700622 self.kill_hostapd_instance(instance)
623 self.release_interface(instance.interface)
Christopher Wiley408d1812014-01-13 15:27:43 -0800624 if self.station_instances:
Christopher Wiley05262d62013-04-17 17:53:59 -0700625 local_servers = self.local_servers
626 self.local_servers = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800627 instance = self.station_instances.pop()
628 if instance.dev_type == 'ibss':
629 self.iw_runner.ibss_leave(instance.interface)
630 elif instance.dev_type == 'managed':
Christopher Wileya4754c02014-06-30 16:37:52 -0700631 self._kill_process_instance(self.cmd_wpa_supplicant,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700632 instance=instance.interface)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800633 else:
Christopher Wiley408d1812014-01-13 15:27:43 -0800634 self.iw_runner.disconnect_station(instance.interface)
635 self.router.run('%s link set %s down' %
636 (self.cmd_ip, instance.interface))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700637
Paul Stewart326badb2012-12-18 14:18:54 -0800638 for server in local_servers:
639 self.stop_dhcp_server(server['interface'])
640 self.router.run("%s addr del %s" %
641 (self.cmd_ip, server['ip_params']),
642 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700643
Paul Stewart7cb1f062010-06-10 15:46:20 -0700644
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800645 def confirm_pmksa_cache_use(self, instance=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700646 """Verify that the PMKSA auth was cached on a hostapd instance.
647
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800648 @param instance int router instance number.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700649
650 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700651 log_file = self.hostapd_instances[instance].log_file
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800652 pmksa_match = 'PMK from PMKSA cache'
653 result = self.router.run('grep -q "%s" %s' % (pmksa_match, log_file),
654 ignore_status=True)
655 if result.exit_status:
656 raise error.TestFail('PMKSA cache was not used in roaming.')
Paul Stewart17350be2012-12-14 13:34:54 -0800657
658
Christopher Wileye0afecb2013-11-11 10:54:23 -0800659 def get_ssid(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700660 """@return string ssid for the network stemming from this router."""
Christopher Wileye0afecb2013-11-11 10:54:23 -0800661 if instance is None:
662 instance = 0
663 if len(self.hostapd_instances) > 1:
664 raise error.TestFail('No instance of hostapd specified with '
665 'multiple instances present.')
666
Christopher Wiley3099be72013-11-06 16:49:02 -0800667 if self.hostapd_instances:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700668 return self.hostapd_instances[instance].ssid
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700669
Christopher Wiley408d1812014-01-13 15:27:43 -0800670 if self.station_instances:
671 return self.station_instances[0].ssid
Christopher Wiley3166e432013-08-06 09:53:12 -0700672
Christopher Wiley408d1812014-01-13 15:27:43 -0800673 raise error.TestFail('Requested ssid of an unconfigured AP.')
Paul Stewart98022e22010-10-22 10:33:14 -0700674
675
Wade Guthriee4074dd2013-10-30 11:00:48 -0700676 def deauth_client(self, client_mac):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700677 """Deauthenticates a client described in params.
678
Wade Guthriee4074dd2013-10-30 11:00:48 -0700679 @param client_mac string containing the mac address of the client to be
680 deauthenticated.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700681
682 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700683 control_if = self.hostapd_instances[-1].config_dict['ctrl_interface']
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700684 self.router.run('%s -p%s deauthenticate %s' %
Paul Stewart80bb3372014-01-22 15:06:08 -0800685 (self.cmd_hostapd_cli, control_if, client_mac))
Wade Guthriee4074dd2013-10-30 11:00:48 -0700686
687
Peter Qiuc4beba02014-03-24 14:46:24 -0700688 def send_management_frame_on_ap(self, frame_type, channel, instance=0):
Paul Stewart51b0f382013-06-12 09:03:02 -0700689 """Injects a management frame into an active hostapd session.
690
691 @param frame_type string the type of frame to send.
Peter Qiuc4beba02014-03-24 14:46:24 -0700692 @param channel int targeted channel
Paul Stewart51b0f382013-06-12 09:03:02 -0700693 @param instance int indicating which hostapd instance to inject into.
694
695 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700696 hostap_interface = self.hostapd_instances[instance].interface
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800697 interface = self.get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700698 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
Peter Qiuc4beba02014-03-24 14:46:24 -0700699 self.router.run('%s -i %s -t %s -c %d' %
700 (self.cmd_send_management_frame, interface, frame_type,
701 channel))
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800702 self.release_interface(interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700703
704
Peter Qiuc4beba02014-03-24 14:46:24 -0700705 def setup_management_frame_interface(self, channel):
706 """
707 Setup interface for injecting management frames.
708
709 @param channel int channel to inject the frames.
710
711 @return string name of the interface.
712
713 """
714 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
715 channel)
716 interface = self.get_wlanif(frequency, 'monitor')
717 self.iw_runner.set_freq(interface, frequency)
718 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
719 return interface
720
721
722 def send_management_frame(self, interface, frame_type, channel,
723 ssid_prefix=None, num_bss=None,
724 frame_count=None, delay=None):
725 """
726 Injects management frames on specify channel |frequency|.
727
728 This function will spawn off a new process to inject specified
729 management frames |frame_type| at the specified interface |interface|.
730
731 @param interface string interface to inject frames.
732 @param frame_type string message type.
733 @param channel int targeted channel.
734 @param ssid_prefix string SSID prefix.
735 @param num_bss int number of BSS.
736 @param frame_count int number of frames to send.
737 @param delay int milliseconds delay between frames.
738
739 @return int PID of the newly created process.
740
741 """
742 command = '%s -i %s -t %s -c %d' % (self.cmd_send_management_frame,
743 interface, frame_type, channel)
744 if ssid_prefix is not None:
745 command += ' -s %s' % (ssid_prefix)
746 if num_bss is not None:
747 command += ' -b %d' % (num_bss)
748 if frame_count is not None:
749 command += ' -n %d' % (frame_count)
750 if delay is not None:
751 command += ' -d %d' % (delay)
752 command += ' > %s 2>&1 & echo $!' % (self.MGMT_FRAME_SENDER_LOG_FILE)
753 pid = int(self.router.run(command).stdout)
754 return pid
755
756
Paul Stewart25536942013-08-15 17:33:42 -0700757 def detect_client_deauth(self, client_mac, instance=0):
758 """Detects whether hostapd has logged a deauthentication from
759 |client_mac|.
760
761 @param client_mac string the MAC address of the client to detect.
762 @param instance int indicating which hostapd instance to query.
763
764 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700765 interface = self.hostapd_instances[instance].interface
Paul Stewart25536942013-08-15 17:33:42 -0700766 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700767 log_file = self.hostapd_instances[instance].log_file
Paul Stewart25536942013-08-15 17:33:42 -0700768 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
769 ignore_status=True)
770 return result.exit_status == 0
771
772
Paul Stewart4ae471e2013-09-04 15:42:35 -0700773 def detect_client_coexistence_report(self, client_mac, instance=0):
774 """Detects whether hostapd has logged an action frame from
775 |client_mac| indicating information about 20/40MHz BSS coexistence.
776
777 @param client_mac string the MAC address of the client to detect.
778 @param instance int indicating which hostapd instance to query.
779
780 """
781 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): '
782 '.. .. .. .. .. .. .. .. .. .. %s '
783 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' %
784 ' '.join(client_mac.split(':')))
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700785 log_file = self.hostapd_instances[instance].log_file
Paul Stewart4ae471e2013-09-04 15:42:35 -0700786 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file),
787 ignore_status=True)
788 return result.exit_status == 0
789
790
Paul Stewart6ddeba72013-11-18 10:08:23 -0800791 def add_connected_peer(self, instance=0):
792 """Configure a station connected to a running AP instance.
793
794 Extract relevant configuration objects from the hostap
795 configuration for |instance| and generate a wpa_supplicant
796 instance that connects to it. This allows the DUT to interact
797 with a client entity that is also connected to the same AP. A
798 full wpa_supplicant instance is necessary here (instead of just
799 using the "iw" command to connect) since we want to enable
800 advanced features such as TDLS.
801
802 @param instance int indicating which hostapd instance to connect to.
803
804 """
805 if not self.hostapd_instances:
806 raise error.TestFail('Hostapd is not configured.')
807
Christopher Wiley408d1812014-01-13 15:27:43 -0800808 if self.station_instances:
Paul Stewart6ddeba72013-11-18 10:08:23 -0800809 raise error.TestFail('Station is already configured.')
810
Christopher Wiley408d1812014-01-13 15:27:43 -0800811 ssid = self.get_ssid(instance)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700812 hostap_conf = self.hostapd_instances[instance].config_dict
Paul Stewart6ddeba72013-11-18 10:08:23 -0800813 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
814 hostap_conf['channel'])
Christopher Wiley408d1812014-01-13 15:27:43 -0800815 interface = self.get_wlanif(frequency, 'managed')
Paul Stewart6ddeba72013-11-18 10:08:23 -0800816
817 # TODO(pstew): Configure other bits like PSK, 802.11n if tests
818 # require them...
819 supplicant_config = (
820 'network={\n'
821 ' ssid="%(ssid)s"\n'
822 ' key_mgmt=NONE\n'
Christopher Wiley408d1812014-01-13 15:27:43 -0800823 '}\n' % {'ssid': ssid}
Paul Stewart6ddeba72013-11-18 10:08:23 -0800824 )
825
Christopher Wileyeea12362013-12-12 17:24:29 -0800826 conf_file = self.STATION_CONF_FILE_PATTERN % interface
827 log_file = self.STATION_LOG_FILE_PATTERN % interface
828 pid_file = self.STATION_PID_FILE_PATTERN % interface
Paul Stewart6ddeba72013-11-18 10:08:23 -0800829
830 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
831 (conf_file, supplicant_config))
832
833 # Connect the station.
834 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
835 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s &> %s &' %
836 (self.cmd_wpa_supplicant,
837 interface, pid_file, conf_file,
Christopher Wileyeea12362013-12-12 17:24:29 -0800838 self.HOSTAPD_DRIVER_NAME, log_file))
Paul Stewart6ddeba72013-11-18 10:08:23 -0800839 self.router.run(start_command)
840 self.iw_runner.wait_for_link(interface)
841
842 # Assign an IP address to this interface.
843 self.router.run('%s addr add %s/24 dev %s' %
844 (self.cmd_ip, self.local_peer_ip_address(instance),
845 interface))
846
847 # Since we now have two network interfaces connected to the same
848 # network, we need to disable the kernel's protection against
849 # incoming packets to an "unexpected" interface.
850 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' %
851 interface)
852
Paul Stewartb01839b2013-12-06 15:49:56 -0800853 # Similarly, we'd like to prevent the hostap interface from
854 # replying to ARP requests for the peer IP address and vice
855 # versa.
856 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
857 interface)
858 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
859 hostap_conf['interface'])
860
Christopher Wiley408d1812014-01-13 15:27:43 -0800861 self.station_instances.append(
862 StationInstance(ssid=ssid, interface=interface,
863 dev_type='managed'))