blob: a6ee75303709730a2471a9ff92c247cf889b497d [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
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700158 # Log the most recent message on the router so that we can rebuild the
159 # suffix relevant to us when debugging failures.
160 last_log_line = self.host.run('tail -1 /var/log/messages').stdout
161 # We're trying to get the timestamp from:
162 # 2014-07-23T17:29:34.961056+00:00 localhost kernel: blah blah blah
163 self._log_start_timestamp = last_log_line.strip().split(None, 2)[0]
164 logging.debug('Will only retrieve logs after %s.',
165 self._log_start_timestamp)
166
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700167 # hostapd configuration persists throughout the test, subsequent
168 # 'config' commands only modify it.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700169 self._ssid_prefix = test_name
170 if self._ssid_prefix.startswith(self.KNOWN_TEST_PREFIX):
Christopher Wiley3166e432013-08-06 09:53:12 -0700171 # Many of our tests start with an uninteresting prefix.
172 # Remove it so we can have more unique bytes.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700173 self._ssid_prefix = self._ssid_prefix[len(self.KNOWN_TEST_PREFIX):]
174 self._number_unique_ssids = 0
Christopher Wiley3166e432013-08-06 09:53:12 -0700175
Christopher Wileyeea12362013-12-12 17:24:29 -0800176 self._total_hostapd_instances = 0
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700177 self.local_servers = []
Paul Stewart548cf452012-11-27 17:46:23 -0800178 self.hostapd_instances = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800179 self.station_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700180 self.dhcp_low = 1
181 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700182
Paul Stewart548cf452012-11-27 17:46:23 -0800183 # Kill hostapd and dhcp server if already running.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700184 self._kill_process_instance('hostapd', timeout_seconds=30)
185 self.stop_dhcp_server(instance=None)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700186
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700187 # Place us in the US by default
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700188 self.iw_runner.set_regulatory_domain('US')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700189
Peter Qiu2f973252014-02-20 15:30:37 -0800190 # Reset all antennas to be active
191 self.set_default_antenna_bitmap()
192
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700193
Christopher Wileyf4bc88b2013-08-29 16:45:15 -0700194 def close(self):
195 """Close global resources held by this system."""
Christopher Wileyeea12362013-12-12 17:24:29 -0800196 self.deconfig()
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700197 # dnsmasq and hostapd cause interesting events to go to system logs.
198 # Retrieve only the suffix of the logs after the timestamp we stored on
199 # router creation.
200 self.host.run("sed -n -e '/%s/,$p' /var/log/messages >/tmp/router_log" %
201 self._log_start_timestamp, ignore_status=True)
202 self.host.get_file('/tmp/router_log', 'debug/router_host_messages')
Christopher Wiley408d1812014-01-13 15:27:43 -0800203 super(LinuxRouter, self).close()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700204
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700205
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700206 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700207 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700208 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700209
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700210
Peter Qiuc89c9a22014-02-27 10:03:55 -0800211 def start_hostapd(self, configuration):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700212 """Start a hostapd instance described by conf.
213
Christopher Wileyeea12362013-12-12 17:24:29 -0800214 @param configuration HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700215
216 """
Paul Stewart548cf452012-11-27 17:46:23 -0800217 # Figure out the correct interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800218 interface = self.get_wlanif(configuration.frequency, 'managed')
Paul Stewart326badb2012-12-18 14:18:54 -0800219
Christopher Wileyeea12362013-12-12 17:24:29 -0800220 conf_file = self.HOSTAPD_CONF_FILE_PATTERN % interface
221 log_file = self.HOSTAPD_LOG_FILE_PATTERN % interface
Paul Stewart80bb3372014-01-22 15:06:08 -0800222 control_interface = self.HOSTAPD_CONTROL_INTERFACE_PATTERN % interface
Peter Qiuc89c9a22014-02-27 10:03:55 -0800223 hostapd_conf_dict = configuration.generate_dict(
224 interface, control_interface,
Christopher Wiley08aafb02014-04-22 17:38:21 -0700225 self._build_unique_ssid(configuration.ssid_suffix))
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700226 logging.debug('hostapd parameters: %r', hostapd_conf_dict)
Paul Stewart548cf452012-11-27 17:46:23 -0800227
228 # Generate hostapd.conf.
Paul Stewart548cf452012-11-27 17:46:23 -0800229 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
230 (conf_file, '\n'.join(
Christopher Wileyeea12362013-12-12 17:24:29 -0800231 "%s=%s" % kv for kv in hostapd_conf_dict.iteritems())))
Paul Stewart548cf452012-11-27 17:46:23 -0800232
233 # Run hostapd.
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700234 logging.info('Starting hostapd on %s(%s) channel=%s...',
235 interface, self.iw_runner.get_interface(interface).phy,
236 configuration.channel)
Christopher Wiley1defc242013-09-18 10:28:37 -0700237 self.router.run('rm %s' % log_file, ignore_status=True)
Christopher Wileyeea12362013-12-12 17:24:29 -0800238 self.router.run('stop wpasupplicant', ignore_status=True)
Christopher Wiley3c427cc2014-07-23 15:38:00 -0700239 start_command = 'stdbuf -oL %s -dd -t %s &> %s & echo $!' % (
Christopher Wiley0f64b842014-04-30 15:43:50 -0700240 self.cmd_hostapd, conf_file, log_file)
241 pid = int(self.router.run(start_command).stdout.strip())
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700242 self.hostapd_instances.append(HostapdInstance(
243 hostapd_conf_dict['ssid'],
244 conf_file,
245 log_file,
246 interface,
247 hostapd_conf_dict.copy()))
Paul Stewart548cf452012-11-27 17:46:23 -0800248
Christopher Wileyeea12362013-12-12 17:24:29 -0800249 # Wait for confirmation that the router came up.
Christopher Wileyeea12362013-12-12 17:24:29 -0800250 logging.info('Waiting for hostapd to startup.')
251 start_time = time.time()
252 while time.time() - start_time < self.STARTUP_TIMEOUT_SECONDS:
253 success = self.router.run(
254 'grep "Completing interface initialization" %s' % log_file,
255 ignore_status=True).exit_status == 0
256 if success:
257 break
258
Christopher Wileyebafa132014-07-15 10:18:28 -0700259 # TODO(wiley) Remove this once we resolve crbug.com/393667
260 self.host.run('tail -1 %s' % log_file, ignore_status=True)
Christopher Wileyeea12362013-12-12 17:24:29 -0800261 # A common failure is an invalid router configuration.
262 # Detect this and exit early if we see it.
263 bad_config = self.router.run(
264 'grep "Interface initialization failed" %s' % log_file,
265 ignore_status=True).exit_status == 0
266 if bad_config:
267 raise error.TestFail('hostapd failed to initialize AP '
268 'interface.')
269
270 if pid:
271 early_exit = self.router.run('kill -0 %d' % pid,
272 ignore_status=True).exit_status
273 if early_exit:
274 raise error.TestFail('hostapd process terminated.')
275
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700276 time.sleep(self.POLLING_INTERVAL_SECONDS)
Christopher Wileyeea12362013-12-12 17:24:29 -0800277 else:
278 raise error.TestFail('Timed out while waiting for hostapd '
279 'to start.')
280
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700281
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700282 def _kill_process_instance(self,
283 process,
284 instance=None,
285 timeout_seconds=10,
286 ignore_timeouts=False):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700287 """Kill a process on the router.
288
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700289 Kills remote program named |process| (optionally only a specific
290 |instance|). Wait |timeout_seconds| for |process| to die
291 before returning. If |ignore_timeouts| is False, raise
292 a TestError on timeouts.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700293
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700294 @param process: string name of process to kill.
295 @param instance: string fragment of the command line unique to
296 this instance of the remote process.
297 @param timeout_seconds: float timeout in seconds to wait.
298 @param ignore_timeouts: True iff we should ignore failures to
299 kill processes.
300 @return True iff the specified process has exited.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700301
Thieu Le7b23a542012-01-27 15:54:48 -0800302 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700303 if instance is not None:
Christopher Wileya4754c02014-06-30 16:37:52 -0700304 search_arg = '-f "^%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800305 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800306 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800307
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700308 self.host.run('pkill %s' % search_arg, ignore_status=True)
309 is_dead = False
310 start_time = time.time()
311 while not is_dead and time.time() - start_time < timeout_seconds:
312 time.sleep(self.POLLING_INTERVAL_SECONDS)
313 is_dead = self.host.run(
Christopher Wileya4754c02014-06-30 16:37:52 -0700314 'pgrep -l %s' % search_arg,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700315 ignore_status=True).exit_status != 0
316 if is_dead or ignore_timeouts:
317 return is_dead
Paul Stewart326badb2012-12-18 14:18:54 -0800318
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700319 raise error.TestError(
320 'Timed out waiting for %s%s to die' %
321 (process,
322 '' if instance is None else ' (instance=%s)' % instance))
Paul Stewart326badb2012-12-18 14:18:54 -0800323
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700324
Paul Stewart326badb2012-12-18 14:18:54 -0800325 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700326 """Kills a hostapd instance.
327
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700328 @param instance HostapdInstance object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700329
330 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700331 is_dead = self._kill_process_instance(
Christopher Wileya4754c02014-06-30 16:37:52 -0700332 self.cmd_hostapd,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700333 instance=instance.conf_file,
334 timeout_seconds=30,
335 ignore_timeouts=True)
336 if self.host.run('ls %s >/dev/null 2>&1' % instance.log_file,
337 ignore_status=True).exit_status:
338 logging.error('Did not collect hostapd log file because '
339 'it was missing.')
340 else:
341 self.router.get_file(instance.log_file,
342 'debug/hostapd_router_%d_%s.log' %
343 (self._total_hostapd_instances,
344 instance.interface))
345 self._total_hostapd_instances += 1
346 if not is_dead:
347 raise error.TestError('Timed out killing hostapd.')
Paul Stewart21737812012-12-06 11:03:32 -0800348
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700349
Christopher Wiley08aafb02014-04-22 17:38:21 -0700350 def _build_unique_ssid(self, suffix):
351 # Build our unique token by base-<len(self.SUFFIX_LETTERS)> encoding
352 # the number of APs we've constructed already.
353 base = len(self.SUFFIX_LETTERS)
354 number = self._number_unique_ssids
355 self._number_unique_ssids += 1
356 unique = ''
357 while number or not unique:
358 unique = self.SUFFIX_LETTERS[number % base] + unique
359 number = number / base
360 # And salt the SSID so that tests running in adjacent cells are unlikely
361 # to pick the same SSID and we're resistent to beacons leaking out of
362 # cells.
363 salt = ''.join([random.choice(self.SUFFIX_LETTERS) for x in range(5)])
364 return '_'.join([self._ssid_prefix, unique, salt, suffix])[-32:]
Christopher Wiley3166e432013-08-06 09:53:12 -0700365
366
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700367 def hostap_configure(self, configuration, multi_interface=None):
368 """Build up a hostapd configuration file and start hostapd.
369
370 Also setup a local server if this router supports them.
371
372 @param configuration HosetapConfig object.
373 @param multi_interface bool True iff multiple interfaces allowed.
374
375 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800376 if multi_interface is None and (self.hostapd_instances or
Christopher Wiley408d1812014-01-13 15:27:43 -0800377 self.station_instances):
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700378 self.deconfig()
Peter Qiuc89c9a22014-02-27 10:03:55 -0800379 self.start_hostapd(configuration)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700380 interface = self.hostapd_instances[-1].interface
Christopher Wileyeea12362013-12-12 17:24:29 -0800381 self.iw_runner.set_tx_power(interface, 'auto')
382 self.start_local_server(interface)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700383 logging.info('AP configured.')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700384
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700385
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700386 def ibss_configure(self, config):
387 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700388
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700389 Extract relevant configuration objects from |config| despite not
390 actually being a hostap managed endpoint.
391
392 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700393
394 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800395 if self.station_instances or self.hostapd_instances:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700396 self.deconfig()
Christopher Wiley408d1812014-01-13 15:27:43 -0800397 interface = self.get_wlanif(config.frequency, 'ibss')
Christopher Wiley08aafb02014-04-22 17:38:21 -0700398 ssid = (config.ssid or self._build_unique_ssid(config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800399 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700400 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley408d1812014-01-13 15:27:43 -0800401 self.iw_runner.ibss_join(interface, ssid, config.frequency)
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700402 # Always start a local server.
403 self.start_local_server(interface)
404 # Remember that this interface is up.
Christopher Wiley408d1812014-01-13 15:27:43 -0800405 self.station_instances.append(
406 StationInstance(ssid=ssid, interface=interface,
407 dev_type='ibss'))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800408
409
Paul Stewart2bd823b2012-11-21 15:03:37 -0800410 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700411 """Get the local server address for an interface.
412
413 When we multiple local servers, we give them static IP addresses
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700414 like 192.168.*.254.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700415
416 @param index int describing which local server this is for.
417
418 """
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700419 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254))
Paul Stewart2bd823b2012-11-21 15:03:37 -0800420
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700421
Paul Stewart6ddeba72013-11-18 10:08:23 -0800422 def local_peer_ip_address(self, index):
423 """Get the IP address allocated for the peer associated to the AP.
424
425 This address is assigned to a locally associated peer device that
426 is created for the DUT to perform connectivity tests with.
427 When we have multiple local servers, we give them static IP addresses
428 like 192.168.*.253.
429
430 @param index int describing which local server this is for.
431
432 """
433 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253))
434
435
436 def local_peer_mac_address(self):
437 """Get the MAC address of the peer interface.
438
439 @return string MAC address of the peer interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800440
Paul Stewart6ddeba72013-11-18 10:08:23 -0800441 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800442 iface = interface.Interface(self.station_instances[0].interface,
443 self.router)
Paul Stewart6ddeba72013-11-18 10:08:23 -0800444 return iface.mac_address
445
446
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700447 def start_local_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700448 """Start a local server on an interface.
449
450 @param interface string (e.g. wlan0)
451
452 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700453 logging.info('Starting up local server...')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700454
455 if len(self.local_servers) >= 256:
456 raise error.TestFail('Exhausted available local servers')
457
Christopher Wiley594570d2014-03-14 16:50:15 -0700458 server_addr = netblock.Netblock.from_addr(
459 self.local_server_address(len(self.local_servers)),
460 prefix_len=24)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700461
462 params = {}
Christopher Wiley594570d2014-03-14 16:50:15 -0700463 params['netblock'] = server_addr
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700464 params['dhcp_range'] = ' '.join(
Christopher Wiley594570d2014-03-14 16:50:15 -0700465 (server_addr.get_addr_in_block(1),
466 server_addr.get_addr_in_block(128)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700467 params['interface'] = interface
Christopher Wiley594570d2014-03-14 16:50:15 -0700468 params['ip_params'] = ('%s broadcast %s dev %s' %
469 (server_addr.netblock,
470 server_addr.broadcast,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700471 interface))
472 self.local_servers.append(params)
473
Christopher Wiley594570d2014-03-14 16:50:15 -0700474 self.router.run('%s addr flush %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700475 (self.cmd_ip, interface))
Christopher Wiley594570d2014-03-14 16:50:15 -0700476 self.router.run('%s addr add %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700477 (self.cmd_ip, params['ip_params']))
Christopher Wiley594570d2014-03-14 16:50:15 -0700478 self.router.run('%s link set %s up' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700479 (self.cmd_ip, interface))
Paul Stewart548cf452012-11-27 17:46:23 -0800480 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700481
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700482
Paul Stewart548cf452012-11-27 17:46:23 -0800483 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700484 """Start a dhcp server on an interface.
485
486 @param interface string (e.g. wlan0)
487
488 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800489 for server in self.local_servers:
490 if server['interface'] == interface:
491 params = server
492 break
493 else:
494 raise error.TestFail('Could not find local server '
495 'to match interface: %r' % interface)
Christopher Wiley594570d2014-03-14 16:50:15 -0700496 server_addr = params['netblock']
Christopher Wileyeea12362013-12-12 17:24:29 -0800497 dhcpd_conf_file = self.dhcpd_conf % interface
498 dhcp_conf = '\n'.join([
499 'port=0', # disables DNS server
500 'bind-interfaces',
501 'log-dhcp',
Christopher Wiley594570d2014-03-14 16:50:15 -0700502 'dhcp-range=%s' % ','.join((server_addr.get_addr_in_block(1),
503 server_addr.get_addr_in_block(128))),
Christopher Wileyeea12362013-12-12 17:24:29 -0800504 'interface=%s' % params['interface'],
505 'dhcp-leasefile=%s' % self.dhcpd_leases])
506 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
507 (dhcpd_conf_file, dhcp_conf))
508 self.router.run('dnsmasq --conf-file=%s' % dhcpd_conf_file)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700509
510
Paul Stewart326badb2012-12-18 14:18:54 -0800511 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700512 """Stop a dhcp server on the router.
513
514 @param instance string instance to kill.
515
516 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700517 self._kill_process_instance('dnsmasq', instance=instance)
Paul Stewart548cf452012-11-27 17:46:23 -0800518
519
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800520 def get_wifi_channel(self, ap_num):
521 """Return channel of BSS corresponding to |ap_num|.
522
523 @param ap_num int which BSS to get the channel of.
524 @return int primary channel of BSS.
525
526 """
527 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700528 return instance.config_dict['channel']
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800529
530
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700531 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700532 """Return IP address on the WiFi subnet of a local server on the router.
533
534 If no local servers are configured (e.g. for an RSPro), a TestFail will
535 be raised.
536
537 @param ap_num int which local server to get an address from.
538
539 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700540 if not self.local_servers:
541 raise error.TestError('No IP address assigned')
542
543 return self.local_servers[ap_num]['netblock'].addr
544
545
546 def get_wifi_ip_subnet(self, ap_num):
547 """Return subnet of WiFi AP instance.
548
549 If no APs are configured a TestError will be raised.
550
551 @param ap_num int which local server to get an address from.
552
553 """
554 if not self.local_servers:
555 raise error.TestError('No APs configured.')
556
557 return self.local_servers[ap_num]['netblock'].subnet
Paul Stewart5977da92011-06-01 19:14:08 -0700558
559
Christopher Wileya3effac2014-02-05 11:16:11 -0800560 def get_hostapd_interface(self, ap_num):
561 """Get the name of the interface associated with a hostapd instance.
562
563 @param ap_num: int hostapd instance number.
564 @return string interface name (e.g. 'managed0').
565
566 """
567 if ap_num not in range(len(self.hostapd_instances)):
568 raise error.TestFail('Invalid instance number (%d) with %d '
569 'instances configured.' %
570 (ap_num, len(self.hostapd_instances)))
571
572 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700573 return instance.interface
Christopher Wileya3effac2014-02-05 11:16:11 -0800574
575
Paul Stewart17350be2012-12-14 13:34:54 -0800576 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700577 """Return the MAC address of an AP in the test.
578
579 @param ap_num int index of local server to read the MAC address from.
580 @return string MAC address like 00:11:22:33:44:55.
581
582 """
Paul Stewart2e5313a2014-02-14 09:12:02 -0800583 interface_name = self.get_hostapd_interface(ap_num)
584 ap_interface = interface.Interface(interface_name, self.host)
Christopher Wiley5689d362014-01-07 15:21:25 -0800585 return ap_interface.mac_address
Paul Stewart17350be2012-12-14 13:34:54 -0800586
587
Christopher Wileya3effac2014-02-05 11:16:11 -0800588 def get_hostapd_phy(self, ap_num):
589 """Get name of phy for hostapd instance.
590
591 @param ap_num int index of hostapd instance.
592 @return string phy name of phy corresponding to hostapd's
593 managed interface.
594
595 """
596 interface = self.iw_runner.get_interface(
597 self.get_hostapd_interface(ap_num))
598 return interface.phy
599
600
Christopher Wileyeea12362013-12-12 17:24:29 -0800601 def deconfig(self):
602 """A legacy, deprecated alias for deconfig_aps."""
603 self.deconfig_aps()
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800604
605
606 def deconfig_aps(self, instance=None, silent=False):
607 """De-configure an AP (will also bring wlan down).
608
609 @param instance: int or None. If instance is None, will bring down all
610 instances of hostapd.
611 @param silent: True if instances should be brought without de-authing
612 the DUT.
613
614 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800615 if not self.hostapd_instances and not self.station_instances:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700616 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700617
Christopher Wileyeea12362013-12-12 17:24:29 -0800618 if self.hostapd_instances:
Paul Stewart326badb2012-12-18 14:18:54 -0800619 local_servers = []
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800620 if instance is not None:
621 instances = [ self.hostapd_instances.pop(instance) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800622 for server in self.local_servers:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700623 if server['interface'] == instances[0].interface:
Paul Stewart326badb2012-12-18 14:18:54 -0800624 local_servers = [server]
625 self.local_servers.remove(server)
626 break
Paul Stewart21737812012-12-06 11:03:32 -0800627 else:
628 instances = self.hostapd_instances
629 self.hostapd_instances = []
Paul Stewart326badb2012-12-18 14:18:54 -0800630 local_servers = self.local_servers
631 self.local_servers = []
Paul Stewart64cc4292011-06-01 10:59:36 -0700632
Paul Stewart21737812012-12-06 11:03:32 -0800633 for instance in instances:
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800634 if silent:
Paul Stewart21737812012-12-06 11:03:32 -0800635 # Deconfigure without notifying DUT. Remove the interface
636 # hostapd uses to send beacon and DEAUTH packets.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700637 self.remove_interface(instance.interface)
Paul Stewart21737812012-12-06 11:03:32 -0800638
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700639 self.kill_hostapd_instance(instance)
640 self.release_interface(instance.interface)
Christopher Wiley408d1812014-01-13 15:27:43 -0800641 if self.station_instances:
Christopher Wiley05262d62013-04-17 17:53:59 -0700642 local_servers = self.local_servers
643 self.local_servers = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800644 instance = self.station_instances.pop()
645 if instance.dev_type == 'ibss':
646 self.iw_runner.ibss_leave(instance.interface)
647 elif instance.dev_type == 'managed':
Christopher Wileya4754c02014-06-30 16:37:52 -0700648 self._kill_process_instance(self.cmd_wpa_supplicant,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700649 instance=instance.interface)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800650 else:
Christopher Wiley408d1812014-01-13 15:27:43 -0800651 self.iw_runner.disconnect_station(instance.interface)
652 self.router.run('%s link set %s down' %
653 (self.cmd_ip, instance.interface))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700654
Paul Stewart326badb2012-12-18 14:18:54 -0800655 for server in local_servers:
656 self.stop_dhcp_server(server['interface'])
657 self.router.run("%s addr del %s" %
658 (self.cmd_ip, server['ip_params']),
659 ignore_status=True)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700660
Paul Stewart7cb1f062010-06-10 15:46:20 -0700661
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800662 def confirm_pmksa_cache_use(self, instance=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700663 """Verify that the PMKSA auth was cached on a hostapd instance.
664
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800665 @param instance int router instance number.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700666
667 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700668 log_file = self.hostapd_instances[instance].log_file
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800669 pmksa_match = 'PMK from PMKSA cache'
670 result = self.router.run('grep -q "%s" %s' % (pmksa_match, log_file),
671 ignore_status=True)
672 if result.exit_status:
673 raise error.TestFail('PMKSA cache was not used in roaming.')
Paul Stewart17350be2012-12-14 13:34:54 -0800674
675
Christopher Wileye0afecb2013-11-11 10:54:23 -0800676 def get_ssid(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700677 """@return string ssid for the network stemming from this router."""
Christopher Wileye0afecb2013-11-11 10:54:23 -0800678 if instance is None:
679 instance = 0
680 if len(self.hostapd_instances) > 1:
681 raise error.TestFail('No instance of hostapd specified with '
682 'multiple instances present.')
683
Christopher Wiley3099be72013-11-06 16:49:02 -0800684 if self.hostapd_instances:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700685 return self.hostapd_instances[instance].ssid
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700686
Christopher Wiley408d1812014-01-13 15:27:43 -0800687 if self.station_instances:
688 return self.station_instances[0].ssid
Christopher Wiley3166e432013-08-06 09:53:12 -0700689
Christopher Wiley408d1812014-01-13 15:27:43 -0800690 raise error.TestFail('Requested ssid of an unconfigured AP.')
Paul Stewart98022e22010-10-22 10:33:14 -0700691
692
Wade Guthriee4074dd2013-10-30 11:00:48 -0700693 def deauth_client(self, client_mac):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700694 """Deauthenticates a client described in params.
695
Wade Guthriee4074dd2013-10-30 11:00:48 -0700696 @param client_mac string containing the mac address of the client to be
697 deauthenticated.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700698
699 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700700 control_if = self.hostapd_instances[-1].config_dict['ctrl_interface']
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700701 self.router.run('%s -p%s deauthenticate %s' %
Paul Stewart80bb3372014-01-22 15:06:08 -0800702 (self.cmd_hostapd_cli, control_if, client_mac))
Wade Guthriee4074dd2013-10-30 11:00:48 -0700703
704
Peter Qiuc4beba02014-03-24 14:46:24 -0700705 def send_management_frame_on_ap(self, frame_type, channel, instance=0):
Paul Stewart51b0f382013-06-12 09:03:02 -0700706 """Injects a management frame into an active hostapd session.
707
708 @param frame_type string the type of frame to send.
Peter Qiuc4beba02014-03-24 14:46:24 -0700709 @param channel int targeted channel
Paul Stewart51b0f382013-06-12 09:03:02 -0700710 @param instance int indicating which hostapd instance to inject into.
711
712 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700713 hostap_interface = self.hostapd_instances[instance].interface
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800714 interface = self.get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700715 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
Peter Qiuc4beba02014-03-24 14:46:24 -0700716 self.router.run('%s -i %s -t %s -c %d' %
717 (self.cmd_send_management_frame, interface, frame_type,
718 channel))
Christopher Wileyf671a5a2013-12-13 15:44:41 -0800719 self.release_interface(interface)
Paul Stewart51b0f382013-06-12 09:03:02 -0700720
721
Peter Qiuc4beba02014-03-24 14:46:24 -0700722 def setup_management_frame_interface(self, channel):
723 """
724 Setup interface for injecting management frames.
725
726 @param channel int channel to inject the frames.
727
728 @return string name of the interface.
729
730 """
731 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
732 channel)
733 interface = self.get_wlanif(frequency, 'monitor')
734 self.iw_runner.set_freq(interface, frequency)
735 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
736 return interface
737
738
739 def send_management_frame(self, interface, frame_type, channel,
740 ssid_prefix=None, num_bss=None,
741 frame_count=None, delay=None):
742 """
743 Injects management frames on specify channel |frequency|.
744
745 This function will spawn off a new process to inject specified
746 management frames |frame_type| at the specified interface |interface|.
747
748 @param interface string interface to inject frames.
749 @param frame_type string message type.
750 @param channel int targeted channel.
751 @param ssid_prefix string SSID prefix.
752 @param num_bss int number of BSS.
753 @param frame_count int number of frames to send.
754 @param delay int milliseconds delay between frames.
755
756 @return int PID of the newly created process.
757
758 """
759 command = '%s -i %s -t %s -c %d' % (self.cmd_send_management_frame,
760 interface, frame_type, channel)
761 if ssid_prefix is not None:
762 command += ' -s %s' % (ssid_prefix)
763 if num_bss is not None:
764 command += ' -b %d' % (num_bss)
765 if frame_count is not None:
766 command += ' -n %d' % (frame_count)
767 if delay is not None:
768 command += ' -d %d' % (delay)
769 command += ' > %s 2>&1 & echo $!' % (self.MGMT_FRAME_SENDER_LOG_FILE)
770 pid = int(self.router.run(command).stdout)
771 return pid
772
773
Paul Stewart25536942013-08-15 17:33:42 -0700774 def detect_client_deauth(self, client_mac, instance=0):
775 """Detects whether hostapd has logged a deauthentication from
776 |client_mac|.
777
778 @param client_mac string the MAC address of the client to detect.
779 @param instance int indicating which hostapd instance to query.
780
781 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700782 interface = self.hostapd_instances[instance].interface
Paul Stewart25536942013-08-15 17:33:42 -0700783 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700784 log_file = self.hostapd_instances[instance].log_file
Paul Stewart25536942013-08-15 17:33:42 -0700785 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
786 ignore_status=True)
787 return result.exit_status == 0
788
789
Paul Stewart4ae471e2013-09-04 15:42:35 -0700790 def detect_client_coexistence_report(self, client_mac, instance=0):
791 """Detects whether hostapd has logged an action frame from
792 |client_mac| indicating information about 20/40MHz BSS coexistence.
793
794 @param client_mac string the MAC address of the client to detect.
795 @param instance int indicating which hostapd instance to query.
796
797 """
798 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): '
799 '.. .. .. .. .. .. .. .. .. .. %s '
800 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' %
801 ' '.join(client_mac.split(':')))
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700802 log_file = self.hostapd_instances[instance].log_file
Paul Stewart4ae471e2013-09-04 15:42:35 -0700803 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file),
804 ignore_status=True)
805 return result.exit_status == 0
806
807
Paul Stewart6ddeba72013-11-18 10:08:23 -0800808 def add_connected_peer(self, instance=0):
809 """Configure a station connected to a running AP instance.
810
811 Extract relevant configuration objects from the hostap
812 configuration for |instance| and generate a wpa_supplicant
813 instance that connects to it. This allows the DUT to interact
814 with a client entity that is also connected to the same AP. A
815 full wpa_supplicant instance is necessary here (instead of just
816 using the "iw" command to connect) since we want to enable
817 advanced features such as TDLS.
818
819 @param instance int indicating which hostapd instance to connect to.
820
821 """
822 if not self.hostapd_instances:
823 raise error.TestFail('Hostapd is not configured.')
824
Christopher Wiley408d1812014-01-13 15:27:43 -0800825 if self.station_instances:
Paul Stewart6ddeba72013-11-18 10:08:23 -0800826 raise error.TestFail('Station is already configured.')
827
Christopher Wiley408d1812014-01-13 15:27:43 -0800828 ssid = self.get_ssid(instance)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700829 hostap_conf = self.hostapd_instances[instance].config_dict
Paul Stewart6ddeba72013-11-18 10:08:23 -0800830 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
831 hostap_conf['channel'])
Christopher Wiley408d1812014-01-13 15:27:43 -0800832 interface = self.get_wlanif(frequency, 'managed')
Paul Stewart6ddeba72013-11-18 10:08:23 -0800833
834 # TODO(pstew): Configure other bits like PSK, 802.11n if tests
835 # require them...
836 supplicant_config = (
837 'network={\n'
838 ' ssid="%(ssid)s"\n'
839 ' key_mgmt=NONE\n'
Christopher Wiley408d1812014-01-13 15:27:43 -0800840 '}\n' % {'ssid': ssid}
Paul Stewart6ddeba72013-11-18 10:08:23 -0800841 )
842
Christopher Wileyeea12362013-12-12 17:24:29 -0800843 conf_file = self.STATION_CONF_FILE_PATTERN % interface
844 log_file = self.STATION_LOG_FILE_PATTERN % interface
845 pid_file = self.STATION_PID_FILE_PATTERN % interface
Paul Stewart6ddeba72013-11-18 10:08:23 -0800846
847 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
848 (conf_file, supplicant_config))
849
850 # Connect the station.
851 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
852 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s &> %s &' %
853 (self.cmd_wpa_supplicant,
854 interface, pid_file, conf_file,
Christopher Wileyeea12362013-12-12 17:24:29 -0800855 self.HOSTAPD_DRIVER_NAME, log_file))
Paul Stewart6ddeba72013-11-18 10:08:23 -0800856 self.router.run(start_command)
857 self.iw_runner.wait_for_link(interface)
858
859 # Assign an IP address to this interface.
860 self.router.run('%s addr add %s/24 dev %s' %
861 (self.cmd_ip, self.local_peer_ip_address(instance),
862 interface))
863
864 # Since we now have two network interfaces connected to the same
865 # network, we need to disable the kernel's protection against
866 # incoming packets to an "unexpected" interface.
867 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' %
868 interface)
869
Paul Stewartb01839b2013-12-06 15:49:56 -0800870 # Similarly, we'd like to prevent the hostap interface from
871 # replying to ARP requests for the peer IP address and vice
872 # versa.
873 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
874 interface)
875 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
876 hostap_conf['interface'])
877
Christopher Wiley408d1812014-01-13 15:27:43 -0800878 self.station_instances.append(
879 StationInstance(ssid=ssid, interface=interface,
880 dev_type='managed'))