blob: cfab7cc7a3b538fe0e08b1040fb03012851bc99f [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
Peter Qiu79784512015-04-14 22:59:55 -07006import copy
Christopher Wiley14796b32013-04-03 14:53:33 -07007import logging
Christopher Wiley3166e432013-08-06 09:53:12 -07008import random
Christopher Wiley3166e432013-08-06 09:53:12 -07009import string
Rebecca Silberstein194b4582015-06-17 13:29:38 -070010import tempfile
Christopher Wileyeea12362013-12-12 17:24:29 -080011import time
Christopher Wiley14796b32013-04-03 14:53:33 -070012
Paul Stewartc9628b32010-08-11 13:03:51 -070013from autotest_lib.client.common_lib import error
Alex Khouderchahb0c624c2018-06-12 11:00:14 -070014from autotest_lib.client.common_lib import utils
Christopher Wiley36039222014-12-13 18:27:52 -080015from autotest_lib.client.common_lib.cros import path_utils
Paul Stewart6ddeba72013-11-18 10:08:23 -080016from autotest_lib.client.common_lib.cros.network import interface
Christopher Wiley594570d2014-03-14 16:50:15 -070017from autotest_lib.client.common_lib.cros.network import netblock
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070018from autotest_lib.client.common_lib.cros.network import ping_runner
19from autotest_lib.server import hosts
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070020from autotest_lib.server import site_linux_system
Christopher Wiley6b1d9e72014-12-13 18:07:41 -080021from autotest_lib.server.cros import dnsname_mangler
Christopher Wiley99d42c92013-07-09 16:40:16 -070022from autotest_lib.server.cros.network import hostap_config
Sam Leffler19bb0a72010-04-12 08:51:08 -070023
Christopher Wiley408d1812014-01-13 15:27:43 -080024
25StationInstance = collections.namedtuple('StationInstance',
26 ['ssid', 'interface', 'dev_type'])
Christopher Wileyd6e503c2014-06-23 15:53:47 -070027HostapdInstance = collections.namedtuple('HostapdInstance',
28 ['ssid', 'conf_file', 'log_file',
Tien Chang097f7462014-09-03 17:30:29 -070029 'interface', 'config_dict',
mukesh agrawal768e7b32015-05-06 14:53:59 -070030 'stderr_log_file',
31 'scenario_name'])
Tien Chang097f7462014-09-03 17:30:29 -070032
Eric Carusoeddedd32014-10-13 14:41:49 -070033# Send magic packets here, so they can wake up the system but are otherwise
34# dropped.
35UDP_DISCARD_PORT = 9
Christopher Wiley408d1812014-01-13 15:27:43 -080036
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070037def build_router_hostname(client_hostname=None, router_hostname=None):
38 """Build a router hostname from a client hostname.
39
40 @param client_hostname: string hostname of DUT connected to a router.
41 @param router_hostname: string hostname of router.
42 @return string hostname of connected router or None if the hostname
43 cannot be inferred from the client hostname.
44
45 """
46 if not router_hostname and not client_hostname:
47 raise error.TestError('Either client_hostname or router_hostname must '
48 'be specified to build_router_hostname.')
49
Christopher Wiley6b1d9e72014-12-13 18:07:41 -080050 return dnsname_mangler.get_router_addr(client_hostname,
51 cmdline_override=router_hostname)
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070052
53
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -070054def build_router_proxy(test_name='', client_hostname=None, router_addr=None,
55 enable_avahi=False):
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070056 """Build up a LinuxRouter object.
57
58 Verifies that the remote host responds to ping.
59 Either client_hostname or router_addr must be specified.
60
61 @param test_name: string name of this test (e.g. 'network_WiFi_TestName').
62 @param client_hostname: string hostname of DUT if we're in the lab.
63 @param router_addr: string DNS/IPv4 address to use for router host object.
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -070064 @param enable_avahi: boolean True iff avahi should be started on the router.
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070065
66 @return LinuxRouter or raise error.TestError on failure.
67
68 """
Christopher Wiley297910b2014-07-01 13:53:19 -070069 router_hostname = build_router_hostname(client_hostname=client_hostname,
70 router_hostname=router_addr)
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070071 logging.info('Connecting to router at %s', router_hostname)
72 ping_helper = ping_runner.PingRunner()
73 if not ping_helper.simple_ping(router_hostname):
74 raise error.TestError('Router at %s is not pingable.' %
75 router_hostname)
76
Edward Hill1f7ae3a2017-07-25 10:31:27 -060077 # Use CrosHost for all router hosts and avoid host detection.
78 # Host detection would use JetstreamHost for Whirlwind routers.
79 # JetstreamHost assumes ap-daemons are running.
80 # Testbed routers run the testbed-ap profile with no ap-daemons.
81 # TODO(ecgh): crbug.com/757075 Fix testbed-ap JetstreamHost detection.
82 return LinuxRouter(hosts.create_host(router_hostname,
83 host_class=hosts.CrosHost),
84 test_name,
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -070085 enable_avahi=enable_avahi)
Christopher Wiley9adeb2a2014-06-19 14:36:13 -070086
Christopher Wiley408d1812014-01-13 15:27:43 -080087
Paul Stewart2ee7fdf2011-05-19 16:29:23 -070088class LinuxRouter(site_linux_system.LinuxSystem):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070089 """Linux/mac80211-style WiFi Router support for WiFiTest class.
Sam Leffler6969d1d2010-03-15 16:07:11 -070090
91 This class implements test methods/steps that communicate with a
92 router implemented with Linux/mac80211. The router must
93 be pre-configured to enable ssh access and have a mac80211-based
94 wireless device. We also assume hostapd 0.7.x and iw are present
95 and any necessary modules are pre-loaded.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -070096
Sam Leffler6969d1d2010-03-15 16:07:11 -070097 """
98
Christopher Wiley08aafb02014-04-22 17:38:21 -070099 KNOWN_TEST_PREFIX = 'network_WiFi_'
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700100 POLLING_INTERVAL_SECONDS = 0.5
Brian Norris73456902018-12-06 10:05:33 -0800101 STARTUP_TIMEOUT_SECONDS = 30
Christopher Wiley3166e432013-08-06 09:53:12 -0700102 SUFFIX_LETTERS = string.ascii_lowercase + string.digits
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700103 SUBNET_PREFIX_OCTETS = (192, 168)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700104
Christopher Wileyeea12362013-12-12 17:24:29 -0800105 HOSTAPD_CONF_FILE_PATTERN = '/tmp/hostapd-test-%s.conf'
106 HOSTAPD_LOG_FILE_PATTERN = '/tmp/hostapd-test-%s.log'
Tien Chang097f7462014-09-03 17:30:29 -0700107 HOSTAPD_STDERR_LOG_FILE_PATTERN = '/tmp/hostapd-stderr-test-%s.log'
Paul Stewart80bb3372014-01-22 15:06:08 -0800108 HOSTAPD_CONTROL_INTERFACE_PATTERN = '/tmp/hostapd-test-%s.ctrl'
Christopher Wileyeea12362013-12-12 17:24:29 -0800109 HOSTAPD_DRIVER_NAME = 'nl80211'
110
111 STATION_CONF_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.conf'
112 STATION_LOG_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.log'
113 STATION_PID_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.pid'
114
Peter Qiuc4beba02014-03-24 14:46:24 -0700115 MGMT_FRAME_SENDER_LOG_FILE = '/tmp/send_management_frame-test.log'
116
Rebecca Silberstein194b4582015-06-17 13:29:38 -0700117 PROBE_RESPONSE_FOOTER_FILE = '/tmp/autotest-probe_response_footer'
118
Brian Norrisbd5f83d2018-08-03 14:45:02 -0700119 _RNG_AVAILABLE = '/sys/class/misc/hw_random/rng_available'
120 _RNG_CURRENT = '/sys/class/misc/hw_random/rng_current'
121
Brian Norrisfbf71da2019-05-07 15:11:03 -0700122 _UMA_EVENTS = '/var/lib/metrics/uma-events'
123
Paul Stewart51b0f382013-06-12 09:03:02 -0700124 def get_capabilities(self):
125 """@return iterable object of AP capabilities for this system."""
mukesh agrawal064ea972015-06-26 10:00:53 -0700126 caps = set()
Paul Stewart51b0f382013-06-12 09:03:02 -0700127 try:
Christopher Wiley36039222014-12-13 18:27:52 -0800128 self.cmd_send_management_frame = path_utils.must_be_installed(
129 '/usr/bin/send_management_frame', host=self.host)
Paul Stewart51b0f382013-06-12 09:03:02 -0700130 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME)
131 except error.TestFail:
132 pass
133 return super(LinuxRouter, self).get_capabilities().union(caps)
134
135
Christopher Wiley408d1812014-01-13 15:27:43 -0800136 @property
137 def router(self):
138 """Deprecated. Use self.host instead.
139
140 @return Host object representing the remote router.
141
142 """
143 return self.host
144
145
Christopher Wileyaeef9b52014-03-11 12:24:11 -0700146 @property
147 def wifi_ip(self):
148 """Simple accessor for the WiFi IP when there is only one AP.
149
150 @return string IP of WiFi interface.
151
152 """
153 if len(self.local_servers) != 1:
154 raise error.TestError('Could not pick a WiFi IP to return.')
155
156 return self.get_wifi_ip(0)
157
158
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -0700159 def __init__(self, host, test_name, enable_avahi=False):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700160 """Build a LinuxRouter.
161
162 @param host Host object representing the remote machine.
Christopher Wiley3166e432013-08-06 09:53:12 -0700163 @param test_name string name of this test. Used in SSID creation.
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -0700164 @param enable_avahi: boolean True iff avahi should be started on the
165 router.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700166
167 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800168 super(LinuxRouter, self).__init__(host, 'router')
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700169 self._ssid_prefix = test_name
170 self._enable_avahi = enable_avahi
171 self.__setup()
Wade Guthrie24d1e312012-04-24 16:53:40 -0700172
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700173
174 def __setup(self):
175 """Set up this system.
176
177 Can be used either to complete initialization of a LinuxRouter
178 object, or to re-establish a good state after a reboot.
179
180 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800181 self.cmd_dhcpd = '/usr/sbin/dhcpd'
Christopher Wiley36039222014-12-13 18:27:52 -0800182 self.cmd_hostapd = path_utils.must_be_installed(
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700183 '/usr/sbin/hostapd', host=self.host)
Christopher Wiley36039222014-12-13 18:27:52 -0800184 self.cmd_hostapd_cli = path_utils.must_be_installed(
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700185 '/usr/sbin/hostapd_cli', host=self.host)
Christopher Wiley36039222014-12-13 18:27:52 -0800186 self.cmd_wpa_supplicant = path_utils.must_be_installed(
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700187 '/usr/sbin/wpa_supplicant', host=self.host)
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700188 self.dhcpd_conf = '/tmp/dhcpd.%s.conf'
189 self.dhcpd_leases = '/tmp/dhcpd.leases'
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700190
Brian Norrisfacbe712019-02-11 15:40:27 -0800191 # TODO(crbug.com/839164): some routers fill their stateful partition
192 # with uncollected metrics.
Brian Norrisfbf71da2019-05-07 15:11:03 -0700193 if self.host.path_exists(self._UMA_EVENTS):
194 self.host.run('truncate -s 0 %s' % self._UMA_EVENTS,
195 ignore_status=True)
Brian Norrisfacbe712019-02-11 15:40:27 -0800196
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700197 # Log the most recent message on the router so that we can rebuild the
198 # suffix relevant to us when debugging failures.
Brian Norrise53f1692018-09-28 10:46:56 -0700199 last_log_line = self.host.run('tail -1 /var/log/messages',
200 ignore_status=True).stdout
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700201 # We're trying to get the timestamp from:
202 # 2014-07-23T17:29:34.961056+00:00 localhost kernel: blah blah blah
Brian Norris2d8f2972018-09-26 14:55:38 -0700203 self._log_start_timestamp = last_log_line.strip().partition(' ')[0]
204 if self._log_start_timestamp:
205 logging.debug('Will only retrieve logs after %s.',
206 self._log_start_timestamp)
207 else:
208 # If syslog is empty, we just use a wildcard pattern, to grab
209 # everything.
210 logging.debug('Empty or corrupt log; will retrieve whole log')
211 self._log_start_timestamp = '.'
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700212
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700213 # hostapd configuration persists throughout the test, subsequent
214 # 'config' commands only modify it.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700215 if self._ssid_prefix.startswith(self.KNOWN_TEST_PREFIX):
Christopher Wiley3166e432013-08-06 09:53:12 -0700216 # Many of our tests start with an uninteresting prefix.
217 # Remove it so we can have more unique bytes.
Christopher Wiley08aafb02014-04-22 17:38:21 -0700218 self._ssid_prefix = self._ssid_prefix[len(self.KNOWN_TEST_PREFIX):]
219 self._number_unique_ssids = 0
Christopher Wiley3166e432013-08-06 09:53:12 -0700220
Christopher Wileyeea12362013-12-12 17:24:29 -0800221 self._total_hostapd_instances = 0
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700222 self.local_servers = []
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700223 self.server_address_index = []
Paul Stewart548cf452012-11-27 17:46:23 -0800224 self.hostapd_instances = []
Christopher Wiley408d1812014-01-13 15:27:43 -0800225 self.station_instances = []
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700226 self.dhcp_low = 1
227 self.dhcp_high = 128
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700228
Brian Norris7b09bde2019-05-08 13:02:56 -0700229 # Tear down hostapbr bridge and intermediate functional block
230 # interfaces.
231 result = self.host.run('ls -d /sys/class/net/%s* /sys/class/net/%s*'
232 ' 2>/dev/null' %
233 (self.HOSTAP_BRIDGE_INTERFACE_PREFIX,
234 self.IFB_INTERFACE_PREFIX),
Matthew Wang08e868d2018-04-19 12:04:54 -0700235 ignore_status=True)
Brian Norris7b09bde2019-05-08 13:02:56 -0700236 for path in result.stdout.splitlines():
237 self.delete_link(path.split('/')[-1])
Matthew Wang08e868d2018-04-19 12:04:54 -0700238
Paul Stewart548cf452012-11-27 17:46:23 -0800239 # Kill hostapd and dhcp server if already running.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700240 self._kill_process_instance('hostapd', timeout_seconds=30)
241 self.stop_dhcp_server(instance=None)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700242
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700243 # Place us in the US by default
Christopher Wiley7bd2e082013-10-16 17:40:40 -0700244 self.iw_runner.set_regulatory_domain('US')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700245
mukesh agrawal4cfb7512015-12-11 15:54:11 -0800246 self.enable_all_antennas()
Peter Qiu2f973252014-02-20 15:30:37 -0800247
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -0700248 # Some tests want this functionality, but otherwise, it's a distraction.
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700249 if self._enable_avahi:
Christopher Wiley4d7bd3e2015-03-18 09:05:04 -0700250 self.host.run('start avahi', ignore_status=True)
251 else:
252 self.host.run('stop avahi', ignore_status=True)
253
Brian Norrisbd5f83d2018-08-03 14:45:02 -0700254 # Some routers have bad (slow?) random number generators.
255 self.rng_configure()
256
Paul Stewartf05d7fd2011-04-06 16:19:37 -0700257
Christopher Wileyf4bc88b2013-08-29 16:45:15 -0700258 def close(self):
259 """Close global resources held by this system."""
Christopher Wileyeea12362013-12-12 17:24:29 -0800260 self.deconfig()
Christopher Wileye5fdbdc2014-07-22 16:50:51 -0700261 # dnsmasq and hostapd cause interesting events to go to system logs.
262 # Retrieve only the suffix of the logs after the timestamp we stored on
263 # router creation.
264 self.host.run("sed -n -e '/%s/,$p' /var/log/messages >/tmp/router_log" %
265 self._log_start_timestamp, ignore_status=True)
266 self.host.get_file('/tmp/router_log', 'debug/router_host_messages')
Christopher Wiley408d1812014-01-13 15:27:43 -0800267 super(LinuxRouter, self).close()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700268
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700269
mukesh agrawal7fd4d092015-06-29 13:50:54 -0700270 def reboot(self, timeout):
271 """Reboot this router, and restore it to a known-good state.
272
273 @param timeout Maximum seconds to wait for router to return.
274
275 """
276 super(LinuxRouter, self).reboot(timeout)
277 self.__setup()
278
279
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700280 def has_local_server(self):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700281 """@return True iff this router has local servers configured."""
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700282 return bool(self.local_servers)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700283
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700284
Peter Qiuc89c9a22014-02-27 10:03:55 -0800285 def start_hostapd(self, configuration):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700286 """Start a hostapd instance described by conf.
287
Christopher Wileyeea12362013-12-12 17:24:29 -0800288 @param configuration HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700289
290 """
Paul Stewart548cf452012-11-27 17:46:23 -0800291 # Figure out the correct interface.
Brian Norris062390f2018-08-03 18:09:43 -0700292 interface = self.get_wlanif(configuration.frequency, 'managed',
293 configuration.min_streams)
Edward Hill7d411222017-10-02 17:26:56 -0600294 phy_name = self.iw_runner.get_interface(interface).phy
Paul Stewart326badb2012-12-18 14:18:54 -0800295
Christopher Wileyeea12362013-12-12 17:24:29 -0800296 conf_file = self.HOSTAPD_CONF_FILE_PATTERN % interface
297 log_file = self.HOSTAPD_LOG_FILE_PATTERN % interface
Tien Chang097f7462014-09-03 17:30:29 -0700298 stderr_log_file = self.HOSTAPD_STDERR_LOG_FILE_PATTERN % interface
Paul Stewart80bb3372014-01-22 15:06:08 -0800299 control_interface = self.HOSTAPD_CONTROL_INTERFACE_PATTERN % interface
Peter Qiuc89c9a22014-02-27 10:03:55 -0800300 hostapd_conf_dict = configuration.generate_dict(
301 interface, control_interface,
Peter Qiubde37ac2014-12-18 23:40:57 -0800302 self.build_unique_ssid(suffix=configuration.ssid_suffix))
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700303 logging.debug('hostapd parameters: %r', hostapd_conf_dict)
Paul Stewart548cf452012-11-27 17:46:23 -0800304
305 # Generate hostapd.conf.
Paul Stewart548cf452012-11-27 17:46:23 -0800306 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
307 (conf_file, '\n'.join(
Christopher Wileyeea12362013-12-12 17:24:29 -0800308 "%s=%s" % kv for kv in hostapd_conf_dict.iteritems())))
Paul Stewart548cf452012-11-27 17:46:23 -0800309
310 # Run hostapd.
Christopher Wileyfaaecd82014-05-29 10:53:40 -0700311 logging.info('Starting hostapd on %s(%s) channel=%s...',
Edward Hill7d411222017-10-02 17:26:56 -0600312 interface, phy_name, configuration.channel)
Christopher Wiley1defc242013-09-18 10:28:37 -0700313 self.router.run('rm %s' % log_file, ignore_status=True)
Christopher Wileyeea12362013-12-12 17:24:29 -0800314 self.router.run('stop wpasupplicant', ignore_status=True)
Tien Chang097f7462014-09-03 17:30:29 -0700315 start_command = '%s -dd -t %s > %s 2> %s & echo $!' % (
316 self.cmd_hostapd, conf_file, log_file, stderr_log_file)
Christopher Wiley0f64b842014-04-30 15:43:50 -0700317 pid = int(self.router.run(start_command).stdout.strip())
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700318 self.hostapd_instances.append(HostapdInstance(
319 hostapd_conf_dict['ssid'],
320 conf_file,
321 log_file,
322 interface,
Tien Chang097f7462014-09-03 17:30:29 -0700323 hostapd_conf_dict.copy(),
mukesh agrawal768e7b32015-05-06 14:53:59 -0700324 stderr_log_file,
325 configuration.scenario_name))
Paul Stewart548cf452012-11-27 17:46:23 -0800326
Christopher Wileyeea12362013-12-12 17:24:29 -0800327 # Wait for confirmation that the router came up.
Christopher Wileyeea12362013-12-12 17:24:29 -0800328 logging.info('Waiting for hostapd to startup.')
Alex Khouderchahb0c624c2018-06-12 11:00:14 -0700329 utils.poll_for_condition(
330 condition=lambda: self._has_hostapd_started(log_file, pid),
331 exception=error.TestFail('Timed out while waiting for hostapd '
332 'to start.'),
333 timeout=self.STARTUP_TIMEOUT_SECONDS,
334 sleep_interval=self.POLLING_INTERVAL_SECONDS)
Christopher Wileyeea12362013-12-12 17:24:29 -0800335
Edward Hill7d411222017-10-02 17:26:56 -0600336 if configuration.frag_threshold:
337 threshold = self.iw_runner.get_fragmentation_threshold(phy_name)
338 if threshold != configuration.frag_threshold:
339 raise error.TestNAError('Router does not support setting '
340 'fragmentation threshold')
341
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700342
Alex Khouderchahb0c624c2018-06-12 11:00:14 -0700343 def _has_hostapd_started(self, log_file, pid):
344 """Determines if hostapd has started.
345
346 @return Whether or not hostapd has started.
347 @raise error.TestFail if there was a bad config or hostapd terminated.
348 """
349 success = self.router.run(
350 'grep "Setup of interface done" %s' % log_file,
351 ignore_status=True).exit_status == 0
352 if success:
353 return True
354
355 # A common failure is an invalid router configuration.
356 # Detect this and exit early if we see it.
357 bad_config = self.router.run(
358 'grep "Interface initialization failed" %s' % log_file,
359 ignore_status=True).exit_status == 0
360 if bad_config:
361 raise error.TestFail('hostapd failed to initialize AP '
362 'interface.')
363
364 if pid:
365 early_exit = self.router.run('kill -0 %d' % pid,
366 ignore_status=True).exit_status
367 if early_exit:
368 raise error.TestFail('hostapd process terminated.')
369
370 return False
371
372
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700373 def _kill_process_instance(self,
374 process,
375 instance=None,
376 timeout_seconds=10,
377 ignore_timeouts=False):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700378 """Kill a process on the router.
379
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700380 Kills remote program named |process| (optionally only a specific
381 |instance|). Wait |timeout_seconds| for |process| to die
382 before returning. If |ignore_timeouts| is False, raise
383 a TestError on timeouts.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700384
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700385 @param process: string name of process to kill.
386 @param instance: string fragment of the command line unique to
387 this instance of the remote process.
388 @param timeout_seconds: float timeout in seconds to wait.
389 @param ignore_timeouts: True iff we should ignore failures to
390 kill processes.
391 @return True iff the specified process has exited.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700392
Thieu Le7b23a542012-01-27 15:54:48 -0800393 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700394 if instance is not None:
Christopher Wileya4754c02014-06-30 16:37:52 -0700395 search_arg = '-f "^%s.*%s"' % (process, instance)
Paul Stewart21737812012-12-06 11:03:32 -0800396 else:
Paul Stewart326badb2012-12-18 14:18:54 -0800397 search_arg = process
Paul Stewart21737812012-12-06 11:03:32 -0800398
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700399 self.host.run('pkill %s' % search_arg, ignore_status=True)
Paul Stewart326badb2012-12-18 14:18:54 -0800400
Alex Khouderchahb0c624c2018-06-12 11:00:14 -0700401 # Wait for process to die
402 time.sleep(self.POLLING_INTERVAL_SECONDS)
403 try:
404 utils.poll_for_condition(
405 condition=lambda: self.host.run(
406 'pgrep -l %s' % search_arg,
407 ignore_status=True).exit_status != 0,
408 timeout=timeout_seconds,
409 sleep_interval=self.POLLING_INTERVAL_SECONDS)
410 except utils.TimeoutError:
411 if ignore_timeouts:
412 return False
413
414 raise error.TestError(
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700415 'Timed out waiting for %s%s to die' %
416 (process,
417 '' if instance is None else ' (instance=%s)' % instance))
Alex Khouderchahb0c624c2018-06-12 11:00:14 -0700418 return True
Paul Stewart326badb2012-12-18 14:18:54 -0800419
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700420
Paul Stewart326badb2012-12-18 14:18:54 -0800421 def kill_hostapd_instance(self, instance):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700422 """Kills a hostapd instance.
423
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700424 @param instance HostapdInstance object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700425
426 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700427 is_dead = self._kill_process_instance(
Christopher Wileya4754c02014-06-30 16:37:52 -0700428 self.cmd_hostapd,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700429 instance=instance.conf_file,
430 timeout_seconds=30,
431 ignore_timeouts=True)
mukesh agrawal768e7b32015-05-06 14:53:59 -0700432 if instance.scenario_name:
433 log_identifier = instance.scenario_name
434 else:
435 log_identifier = '%d_%s' % (
436 self._total_hostapd_instances, instance.interface)
437 files_to_copy = [(instance.log_file,
438 'debug/hostapd_router_%s.log' % log_identifier),
Tien Chang097f7462014-09-03 17:30:29 -0700439 (instance.stderr_log_file,
mukesh agrawal768e7b32015-05-06 14:53:59 -0700440 'debug/hostapd_router_%s.stderr.log' %
441 log_identifier)]
Tien Chang097f7462014-09-03 17:30:29 -0700442 for remote_file, local_file in files_to_copy:
443 if self.host.run('ls %s >/dev/null 2>&1' % remote_file,
444 ignore_status=True).exit_status:
445 logging.error('Did not collect hostapd log file because '
446 'it was missing.')
447 else:
448 self.router.get_file(remote_file, local_file)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700449 self._total_hostapd_instances += 1
450 if not is_dead:
451 raise error.TestError('Timed out killing hostapd.')
Paul Stewart21737812012-12-06 11:03:32 -0800452
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700453
Peter Qiubde37ac2014-12-18 23:40:57 -0800454 def build_unique_ssid(self, suffix=''):
Tien Chang097f7462014-09-03 17:30:29 -0700455 """ Build our unique token by base-<len(self.SUFFIX_LETTERS)> encoding
456 the number of APs we've constructed already.
457
458 @param suffix string to append to SSID
459
460 """
Christopher Wiley08aafb02014-04-22 17:38:21 -0700461 base = len(self.SUFFIX_LETTERS)
462 number = self._number_unique_ssids
463 self._number_unique_ssids += 1
464 unique = ''
465 while number or not unique:
466 unique = self.SUFFIX_LETTERS[number % base] + unique
467 number = number / base
468 # And salt the SSID so that tests running in adjacent cells are unlikely
469 # to pick the same SSID and we're resistent to beacons leaking out of
470 # cells.
471 salt = ''.join([random.choice(self.SUFFIX_LETTERS) for x in range(5)])
472 return '_'.join([self._ssid_prefix, unique, salt, suffix])[-32:]
Christopher Wiley3166e432013-08-06 09:53:12 -0700473
474
Brian Norrisbd5f83d2018-08-03 14:45:02 -0700475 def rng_configure(self):
476 """Configure the random generator to our liking.
477
478 Some routers (particularly, Gale) seem to have bad Random Number
479 Generators, such that hostapd can't always generate keys fast enough.
480 The on-board TPM seems to serve as a better generator, so we try to
481 switch to that if available.
482
483 Symptoms of a slow RNG: hostapd complains with:
484
485 WPA: Not enough entropy in random pool to proceed - reject first
486 4-way handshake
487
488 Ref:
489 https://chromium.googlesource.com/chromiumos/third_party/hostap/+/7ea51f728bb7/src/ap/wpa_auth.c#1854
490
491 Linux devices may have RNG parameters at
492 /sys/class/misc/hw_random/rng_{available,current}. See:
493
494 https://www.kernel.org/doc/Documentation/hw_random.txt
495
496 """
497
498 available = self.host.run('cat %s' % self._RNG_AVAILABLE, \
499 ignore_status=True).stdout.strip().split(' ')
500 # System may not have HWRNG support. Just skip this.
501 if available == "":
502 return
503 current = self.host.run('cat %s' % self._RNG_CURRENT).stdout. \
504 strip()
505 want_rng = "tpm-rng"
506
Brian Norris4bb65822019-03-04 16:03:55 -0800507 logging.debug("Available / current RNGs on router: %r / %s",
508 available, current)
Brian Norrisbd5f83d2018-08-03 14:45:02 -0700509 if want_rng in available and want_rng != current:
Brian Norris4bb65822019-03-04 16:03:55 -0800510 logging.debug("Switching RNGs: %s -> %s", current, want_rng)
Brian Norrisbd5f83d2018-08-03 14:45:02 -0700511 self.host.run('echo -n "%s" > %s' % (want_rng, self._RNG_CURRENT))
512
513
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700514 def hostap_configure(self, configuration, multi_interface=None):
515 """Build up a hostapd configuration file and start hostapd.
516
517 Also setup a local server if this router supports them.
518
519 @param configuration HosetapConfig object.
520 @param multi_interface bool True iff multiple interfaces allowed.
521
522 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800523 if multi_interface is None and (self.hostapd_instances or
Christopher Wiley408d1812014-01-13 15:27:43 -0800524 self.station_instances):
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700525 self.deconfig()
Kris Rambishb9b46852014-12-19 15:29:58 -0800526 if configuration.is_11ac:
527 router_caps = self.get_capabilities()
528 if site_linux_system.LinuxSystem.CAPABILITY_VHT not in router_caps:
529 raise error.TestNAError('Router does not have AC support')
530
Matthew Wang08e868d2018-04-19 12:04:54 -0700531 if configuration.use_bridge:
532 configuration._bridge = self.get_brif()
533
Peter Qiuc89c9a22014-02-27 10:03:55 -0800534 self.start_hostapd(configuration)
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700535 interface = self.hostapd_instances[-1].interface
Christopher Wileyeea12362013-12-12 17:24:29 -0800536 self.iw_runner.set_tx_power(interface, 'auto')
Paul Stewart65fb9212014-12-01 19:54:20 -0800537 self.set_beacon_footer(interface, configuration.beacon_footer)
Matthew Wang1667e5c2018-02-27 17:32:59 -0800538 self.start_local_server(interface, bridge=configuration.bridge)
Christopher Wiley7d414cc2013-04-17 17:26:32 -0700539 logging.info('AP configured.')
Sam Leffler6969d1d2010-03-15 16:07:11 -0700540
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700541
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700542 def ibss_configure(self, config):
543 """Configure a station based AP in IBSS mode.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700544
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700545 Extract relevant configuration objects from |config| despite not
546 actually being a hostap managed endpoint.
547
548 @param config HostapConfig object.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700549
550 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800551 if self.station_instances or self.hostapd_instances:
Christopher Wileyd89b5282013-04-10 15:21:26 -0700552 self.deconfig()
Christopher Wiley408d1812014-01-13 15:27:43 -0800553 interface = self.get_wlanif(config.frequency, 'ibss')
Peter Qiubde37ac2014-12-18 23:40:57 -0800554 ssid = (config.ssid or
555 self.build_unique_ssid(suffix=config.ssid_suffix))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800556 # Connect the station
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700557 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Christopher Wiley408d1812014-01-13 15:27:43 -0800558 self.iw_runner.ibss_join(interface, ssid, config.frequency)
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700559 # Always start a local server.
560 self.start_local_server(interface)
561 # Remember that this interface is up.
Christopher Wiley408d1812014-01-13 15:27:43 -0800562 self.station_instances.append(
563 StationInstance(ssid=ssid, interface=interface,
564 dev_type='ibss'))
Paul Stewartc2b3de82011-03-03 14:45:31 -0800565
566
Paul Stewart2bd823b2012-11-21 15:03:37 -0800567 def local_server_address(self, index):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700568 """Get the local server address for an interface.
569
570 When we multiple local servers, we give them static IP addresses
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700571 like 192.168.*.254.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700572
573 @param index int describing which local server this is for.
574
575 """
Christopher Wileyb1ade0a2013-09-16 13:09:55 -0700576 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254))
Paul Stewart2bd823b2012-11-21 15:03:37 -0800577
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700578
Paul Stewart6ddeba72013-11-18 10:08:23 -0800579 def local_peer_ip_address(self, index):
580 """Get the IP address allocated for the peer associated to the AP.
581
582 This address is assigned to a locally associated peer device that
583 is created for the DUT to perform connectivity tests with.
584 When we have multiple local servers, we give them static IP addresses
585 like 192.168.*.253.
586
587 @param index int describing which local server this is for.
588
589 """
590 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253))
591
Matthew Wang1667e5c2018-02-27 17:32:59 -0800592 def local_bridge_address(self, index):
593 """Get the bridge address for an interface.
594
595 This address is assigned to a local bridge device.
596
597 @param index int describing which local server this is for.
598
599 """
600 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 252))
Paul Stewart6ddeba72013-11-18 10:08:23 -0800601
602 def local_peer_mac_address(self):
603 """Get the MAC address of the peer interface.
604
605 @return string MAC address of the peer interface.
Christopher Wiley408d1812014-01-13 15:27:43 -0800606
Paul Stewart6ddeba72013-11-18 10:08:23 -0800607 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800608 iface = interface.Interface(self.station_instances[0].interface,
609 self.router)
Paul Stewart6ddeba72013-11-18 10:08:23 -0800610 return iface.mac_address
611
612
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700613 def _get_unused_server_address_index(self):
614 """@return an unused server address index."""
615 for address_index in range(0, 256):
616 if address_index not in self.server_address_index:
617 return address_index
618 raise error.TestFail('No available server address index')
619
620
621 def change_server_address_index(self, ap_num=0, server_address_index=None):
622 """Restart the local server with a different server address index.
623
624 This will restart the local server with different gateway IP address
625 and DHCP address ranges.
626
627 @param ap_num: int hostapd instance number.
628 @param server_address_index: int server address index.
629
630 """
631 interface = self.local_servers[ap_num]['interface'];
632 # Get an unused server address index if one is not specified, which
633 # will be different from the one that's currently in used.
634 if server_address_index is None:
635 server_address_index = self._get_unused_server_address_index()
636
637 # Restart local server with the new server address index.
638 self.stop_local_server(self.local_servers[ap_num])
639 self.start_local_server(interface,
640 ap_num=ap_num,
641 server_address_index=server_address_index)
642
643
644 def start_local_server(self,
645 interface,
646 ap_num=None,
Matthew Wang1667e5c2018-02-27 17:32:59 -0800647 server_address_index=None,
648 bridge=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700649 """Start a local server on an interface.
650
651 @param interface string (e.g. wlan0)
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700652 @param ap_num int the ap instance to start the server for
653 @param server_address_index int server address index
Matthew Wang1667e5c2018-02-27 17:32:59 -0800654 @param bridge string (e.g. br0)
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700655
656 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700657 logging.info('Starting up local server...')
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700658
659 if len(self.local_servers) >= 256:
660 raise error.TestFail('Exhausted available local servers')
661
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700662 # Get an unused server address index if one is not specified.
663 # Validate server address index if one is specified.
664 if server_address_index is None:
665 server_address_index = self._get_unused_server_address_index()
666 elif server_address_index in self.server_address_index:
667 raise error.TestFail('Server address index %d already in used' %
668 server_address_index)
669
Christopher Wiley684878e2014-12-18 14:32:48 -0800670 server_addr = netblock.from_addr(
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700671 self.local_server_address(server_address_index),
Christopher Wiley594570d2014-03-14 16:50:15 -0700672 prefix_len=24)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700673
674 params = {}
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700675 params['address_index'] = server_address_index
Christopher Wiley594570d2014-03-14 16:50:15 -0700676 params['netblock'] = server_addr
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700677 params['dhcp_range'] = ' '.join(
Christopher Wiley594570d2014-03-14 16:50:15 -0700678 (server_addr.get_addr_in_block(1),
679 server_addr.get_addr_in_block(128)))
mukesh agrawal05c455a2011-10-12 13:40:27 -0700680 params['interface'] = interface
Matthew Wang1667e5c2018-02-27 17:32:59 -0800681 params['bridge'] = bridge
Christopher Wiley594570d2014-03-14 16:50:15 -0700682 params['ip_params'] = ('%s broadcast %s dev %s' %
683 (server_addr.netblock,
684 server_addr.broadcast,
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700685 interface))
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700686 if ap_num is None:
687 self.local_servers.append(params)
688 else:
689 self.local_servers.insert(ap_num, params)
690 self.server_address_index.append(server_address_index)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700691
Christopher Wiley594570d2014-03-14 16:50:15 -0700692 self.router.run('%s addr flush %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700693 (self.cmd_ip, interface))
Christopher Wiley594570d2014-03-14 16:50:15 -0700694 self.router.run('%s addr add %s' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700695 (self.cmd_ip, params['ip_params']))
Christopher Wiley594570d2014-03-14 16:50:15 -0700696 self.router.run('%s link set %s up' %
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700697 (self.cmd_ip, interface))
Matthew Wang1667e5c2018-02-27 17:32:59 -0800698 if params['bridge']:
699 bridge_addr = netblock.from_addr(
700 self.local_bridge_address(server_address_index),
701 prefix_len=24)
702 self.router.run("ifconfig %s %s" %
703 (params['bridge'], bridge_addr.netblock))
Paul Stewart548cf452012-11-27 17:46:23 -0800704 self.start_dhcp_server(interface)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700705
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700706
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700707 def stop_local_server(self, server):
708 """Stop a local server on the router
709
710 @param server object server configuration parameters.
711
712 """
713 self.stop_dhcp_server(server['interface'])
714 self.router.run("%s addr del %s" %
715 (self.cmd_ip, server['ip_params']),
716 ignore_status=True)
717 self.server_address_index.remove(server['address_index'])
718 self.local_servers.remove(server)
719
720
Paul Stewart548cf452012-11-27 17:46:23 -0800721 def start_dhcp_server(self, interface):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700722 """Start a dhcp server on an interface.
723
724 @param interface string (e.g. wlan0)
725
726 """
Christopher Wileyeea12362013-12-12 17:24:29 -0800727 for server in self.local_servers:
728 if server['interface'] == interface:
729 params = server
730 break
731 else:
732 raise error.TestFail('Could not find local server '
733 'to match interface: %r' % interface)
Christopher Wiley594570d2014-03-14 16:50:15 -0700734 server_addr = params['netblock']
Christopher Wileyeea12362013-12-12 17:24:29 -0800735 dhcpd_conf_file = self.dhcpd_conf % interface
736 dhcp_conf = '\n'.join([
737 'port=0', # disables DNS server
738 'bind-interfaces',
739 'log-dhcp',
Christopher Wiley594570d2014-03-14 16:50:15 -0700740 'dhcp-range=%s' % ','.join((server_addr.get_addr_in_block(1),
741 server_addr.get_addr_in_block(128))),
Matthew Wang1667e5c2018-02-27 17:32:59 -0800742 'interface=%s' % (params['bridge'] or params['interface']),
Christopher Wileyeea12362013-12-12 17:24:29 -0800743 'dhcp-leasefile=%s' % self.dhcpd_leases])
744 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
745 (dhcpd_conf_file, dhcp_conf))
746 self.router.run('dnsmasq --conf-file=%s' % dhcpd_conf_file)
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700747
748
Paul Stewart326badb2012-12-18 14:18:54 -0800749 def stop_dhcp_server(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700750 """Stop a dhcp server on the router.
751
752 @param instance string instance to kill.
753
754 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700755 self._kill_process_instance('dnsmasq', instance=instance)
Paul Stewart548cf452012-11-27 17:46:23 -0800756
757
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800758 def get_wifi_channel(self, ap_num):
759 """Return channel of BSS corresponding to |ap_num|.
760
761 @param ap_num int which BSS to get the channel of.
762 @return int primary channel of BSS.
763
764 """
765 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700766 return instance.config_dict['channel']
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800767
768
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700769 def get_wifi_ip(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700770 """Return IP address on the WiFi subnet of a local server on the router.
771
772 If no local servers are configured (e.g. for an RSPro), a TestFail will
773 be raised.
774
775 @param ap_num int which local server to get an address from.
776
777 """
Christopher Wiley594570d2014-03-14 16:50:15 -0700778 if not self.local_servers:
779 raise error.TestError('No IP address assigned')
780
781 return self.local_servers[ap_num]['netblock'].addr
782
783
784 def get_wifi_ip_subnet(self, ap_num):
785 """Return subnet of WiFi AP instance.
786
787 If no APs are configured a TestError will be raised.
788
789 @param ap_num int which local server to get an address from.
790
791 """
792 if not self.local_servers:
793 raise error.TestError('No APs configured.')
794
795 return self.local_servers[ap_num]['netblock'].subnet
Paul Stewart5977da92011-06-01 19:14:08 -0700796
797
Christopher Wileya3effac2014-02-05 11:16:11 -0800798 def get_hostapd_interface(self, ap_num):
799 """Get the name of the interface associated with a hostapd instance.
800
801 @param ap_num: int hostapd instance number.
802 @return string interface name (e.g. 'managed0').
803
804 """
805 if ap_num not in range(len(self.hostapd_instances)):
806 raise error.TestFail('Invalid instance number (%d) with %d '
807 'instances configured.' %
808 (ap_num, len(self.hostapd_instances)))
809
810 instance = self.hostapd_instances[ap_num]
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700811 return instance.interface
Christopher Wileya3effac2014-02-05 11:16:11 -0800812
813
Christopher Wileycc770d92015-02-02 17:37:16 -0800814 def get_station_interface(self, instance):
815 """Get the name of the interface associated with a station.
816
817 @param instance: int station instance number.
818 @return string interface name (e.g. 'managed0').
819
820 """
821 if instance not in range(len(self.station_instances)):
822 raise error.TestFail('Invalid instance number (%d) with %d '
823 'instances configured.' %
824 (instance, len(self.station_instances)))
825
826 instance = self.station_instances[instance]
827 return instance.interface
828
829
Paul Stewart17350be2012-12-14 13:34:54 -0800830 def get_hostapd_mac(self, ap_num):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700831 """Return the MAC address of an AP in the test.
832
833 @param ap_num int index of local server to read the MAC address from.
834 @return string MAC address like 00:11:22:33:44:55.
835
836 """
Paul Stewart2e5313a2014-02-14 09:12:02 -0800837 interface_name = self.get_hostapd_interface(ap_num)
838 ap_interface = interface.Interface(interface_name, self.host)
Christopher Wiley5689d362014-01-07 15:21:25 -0800839 return ap_interface.mac_address
Paul Stewart17350be2012-12-14 13:34:54 -0800840
841
Christopher Wileya3effac2014-02-05 11:16:11 -0800842 def get_hostapd_phy(self, ap_num):
843 """Get name of phy for hostapd instance.
844
845 @param ap_num int index of hostapd instance.
846 @return string phy name of phy corresponding to hostapd's
847 managed interface.
848
849 """
850 interface = self.iw_runner.get_interface(
851 self.get_hostapd_interface(ap_num))
852 return interface.phy
853
854
Christopher Wileyeea12362013-12-12 17:24:29 -0800855 def deconfig(self):
856 """A legacy, deprecated alias for deconfig_aps."""
857 self.deconfig_aps()
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800858
859
860 def deconfig_aps(self, instance=None, silent=False):
861 """De-configure an AP (will also bring wlan down).
862
863 @param instance: int or None. If instance is None, will bring down all
864 instances of hostapd.
865 @param silent: True if instances should be brought without de-authing
866 the DUT.
867
868 """
Christopher Wiley408d1812014-01-13 15:27:43 -0800869 if not self.hostapd_instances and not self.station_instances:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700870 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700871
Christopher Wileyeea12362013-12-12 17:24:29 -0800872 if self.hostapd_instances:
Paul Stewart326badb2012-12-18 14:18:54 -0800873 local_servers = []
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800874 if instance is not None:
875 instances = [ self.hostapd_instances.pop(instance) ]
Paul Stewart326badb2012-12-18 14:18:54 -0800876 for server in self.local_servers:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700877 if server['interface'] == instances[0].interface:
Paul Stewart326badb2012-12-18 14:18:54 -0800878 local_servers = [server]
Paul Stewart326badb2012-12-18 14:18:54 -0800879 break
Paul Stewart21737812012-12-06 11:03:32 -0800880 else:
881 instances = self.hostapd_instances
882 self.hostapd_instances = []
Peter Qiu79784512015-04-14 22:59:55 -0700883 local_servers = copy.copy(self.local_servers)
Paul Stewart64cc4292011-06-01 10:59:36 -0700884
Paul Stewart21737812012-12-06 11:03:32 -0800885 for instance in instances:
Christopher Wiley4cd4b3f2013-11-11 17:27:28 -0800886 if silent:
Paul Stewart21737812012-12-06 11:03:32 -0800887 # Deconfigure without notifying DUT. Remove the interface
888 # hostapd uses to send beacon and DEAUTH packets.
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700889 self.remove_interface(instance.interface)
Paul Stewart21737812012-12-06 11:03:32 -0800890
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700891 self.kill_hostapd_instance(instance)
892 self.release_interface(instance.interface)
Christopher Wiley408d1812014-01-13 15:27:43 -0800893 if self.station_instances:
Peter Qiu79784512015-04-14 22:59:55 -0700894 local_servers = copy.copy(self.local_servers)
Christopher Wiley408d1812014-01-13 15:27:43 -0800895 instance = self.station_instances.pop()
896 if instance.dev_type == 'ibss':
897 self.iw_runner.ibss_leave(instance.interface)
898 elif instance.dev_type == 'managed':
Christopher Wileya4754c02014-06-30 16:37:52 -0700899 self._kill_process_instance(self.cmd_wpa_supplicant,
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700900 instance=instance.interface)
Paul Stewartc2b3de82011-03-03 14:45:31 -0800901 else:
Christopher Wiley408d1812014-01-13 15:27:43 -0800902 self.iw_runner.disconnect_station(instance.interface)
903 self.router.run('%s link set %s down' %
904 (self.cmd_ip, instance.interface))
mukesh agrawalfe0e85b2011-08-09 14:24:15 -0700905
Paul Stewart326badb2012-12-18 14:18:54 -0800906 for server in local_servers:
Peter Qiucc4bd9e2015-04-07 14:22:40 -0700907 self.stop_local_server(server)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700908
Matthew Wang08e868d2018-04-19 12:04:54 -0700909 for brif in range(self._brif_index):
910 self.delete_link('%s%d' %
911 (self.HOSTAP_BRIDGE_INTERFACE_PREFIX, brif))
912
913
914 def delete_link(self, name):
915 """Delete link using the `ip` command.
916
917 @param name string link name.
918
919 """
920 self.host.run('%s link del %s' % (self.cmd_ip, name),
921 ignore_status=True)
922
Paul Stewart7cb1f062010-06-10 15:46:20 -0700923
Peter Qiu4bfb1e92015-03-12 16:43:28 -0700924 def set_ap_interface_down(self, instance=0):
925 """Bring down the hostapd interface.
926
927 @param instance int router instance number.
928
929 """
930 self.host.run('%s link set %s down' %
931 (self.cmd_ip, self.get_hostapd_interface(instance)))
932
933
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800934 def confirm_pmksa_cache_use(self, instance=0):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700935 """Verify that the PMKSA auth was cached on a hostapd instance.
936
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800937 @param instance int router instance number.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700938
939 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700940 log_file = self.hostapd_instances[instance].log_file
Christopher Wiley8c5ae9a2013-12-04 14:57:56 -0800941 pmksa_match = 'PMK from PMKSA cache'
942 result = self.router.run('grep -q "%s" %s' % (pmksa_match, log_file),
943 ignore_status=True)
944 if result.exit_status:
945 raise error.TestFail('PMKSA cache was not used in roaming.')
Paul Stewart17350be2012-12-14 13:34:54 -0800946
947
Christopher Wileye0afecb2013-11-11 10:54:23 -0800948 def get_ssid(self, instance=None):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700949 """@return string ssid for the network stemming from this router."""
Christopher Wileye0afecb2013-11-11 10:54:23 -0800950 if instance is None:
951 instance = 0
952 if len(self.hostapd_instances) > 1:
953 raise error.TestFail('No instance of hostapd specified with '
954 'multiple instances present.')
955
Christopher Wiley3099be72013-11-06 16:49:02 -0800956 if self.hostapd_instances:
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700957 return self.hostapd_instances[instance].ssid
Christopher Wiley1febd6a2013-06-03 13:59:48 -0700958
Christopher Wiley408d1812014-01-13 15:27:43 -0800959 if self.station_instances:
960 return self.station_instances[0].ssid
Christopher Wiley3166e432013-08-06 09:53:12 -0700961
Christopher Wiley408d1812014-01-13 15:27:43 -0800962 raise error.TestFail('Requested ssid of an unconfigured AP.')
Paul Stewart98022e22010-10-22 10:33:14 -0700963
964
Wade Guthriee4074dd2013-10-30 11:00:48 -0700965 def deauth_client(self, client_mac):
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700966 """Deauthenticates a client described in params.
967
Wade Guthriee4074dd2013-10-30 11:00:48 -0700968 @param client_mac string containing the mac address of the client to be
969 deauthenticated.
Christopher Wileyf99e6cc2013-04-19 10:12:43 -0700970
971 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -0700972 control_if = self.hostapd_instances[-1].config_dict['ctrl_interface']
Paul Stewartaa52e8c2011-05-24 08:46:23 -0700973 self.router.run('%s -p%s deauthenticate %s' %
Paul Stewart80bb3372014-01-22 15:06:08 -0800974 (self.cmd_hostapd_cli, control_if, client_mac))
Wade Guthriee4074dd2013-10-30 11:00:48 -0700975
Matthew Wangab74f8d2018-01-18 16:29:35 -0800976 def send_bss_tm_req(self, client_mac, neighbor_list):
977 """Send a BSS Transition Management Request to a client.
978
979 @param client_mac string containing the mac address of the client.
980 @param neighbor_list list of strings containing mac addresses of
981 candidate APs.
Matthew Wang91660ed2019-04-03 15:38:01 -0700982 @return string reply received from command
Matthew Wangab74f8d2018-01-18 16:29:35 -0800983
984 """
985 control_if = self.hostapd_instances[0].config_dict['ctrl_interface']
Matthew Wang6886f282018-06-05 14:42:20 -0700986 command = ('%s -p%s BSS_TM_REQ %s neighbor=%s,0,0,0,0 pref=1' %
987 (self.cmd_hostapd_cli, control_if, client_mac,
988 ',0,0,0,0 neighbor='.join(neighbor_list)))
989 ret = self.router.run(command).stdout
Matthew Wang91660ed2019-04-03 15:38:01 -0700990 return ret.splitlines()[-1]
Wade Guthriee4074dd2013-10-30 11:00:48 -0700991
Rebecca Silberstein194b4582015-06-17 13:29:38 -0700992 def _prep_probe_response_footer(self, footer):
993 """Write probe response footer temporarily to a local file and copy
994 over to test router.
995
996 @param footer string containing bytes for the probe response footer.
997 @raises AutoservRunError: If footer file copy fails.
998
999 """
mukesh agrawale2102b82015-07-17 11:16:30 -07001000 with tempfile.NamedTemporaryFile() as fp:
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001001 fp.write(footer)
mukesh agrawal41a817c2015-07-22 10:00:43 -07001002 fp.flush()
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001003 try:
mukesh agrawale2102b82015-07-17 11:16:30 -07001004 self.host.send_file(fp.name, self.PROBE_RESPONSE_FOOTER_FILE)
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001005 except error.AutoservRunError:
1006 logging.error('failed to copy footer file to AP')
1007 raise
1008
mukesh agrawale2102b82015-07-17 11:16:30 -07001009
Peter Qiuc4beba02014-03-24 14:46:24 -07001010 def send_management_frame_on_ap(self, frame_type, channel, instance=0):
Paul Stewart51b0f382013-06-12 09:03:02 -07001011 """Injects a management frame into an active hostapd session.
1012
1013 @param frame_type string the type of frame to send.
Peter Qiuc4beba02014-03-24 14:46:24 -07001014 @param channel int targeted channel
Paul Stewart51b0f382013-06-12 09:03:02 -07001015 @param instance int indicating which hostapd instance to inject into.
1016
1017 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -07001018 hostap_interface = self.hostapd_instances[instance].interface
Christopher Wileyf671a5a2013-12-13 15:44:41 -08001019 interface = self.get_wlanif(0, 'monitor', same_phy_as=hostap_interface)
Paul Stewart51b0f382013-06-12 09:03:02 -07001020 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
Peter Qiuc4beba02014-03-24 14:46:24 -07001021 self.router.run('%s -i %s -t %s -c %d' %
1022 (self.cmd_send_management_frame, interface, frame_type,
1023 channel))
Christopher Wileyf671a5a2013-12-13 15:44:41 -08001024 self.release_interface(interface)
Paul Stewart51b0f382013-06-12 09:03:02 -07001025
1026
Peter Qiuc4beba02014-03-24 14:46:24 -07001027 def send_management_frame(self, interface, frame_type, channel,
1028 ssid_prefix=None, num_bss=None,
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001029 frame_count=None, delay=None,
1030 dest_addr=None, probe_resp_footer=None):
Peter Qiuc4beba02014-03-24 14:46:24 -07001031 """
1032 Injects management frames on specify channel |frequency|.
1033
1034 This function will spawn off a new process to inject specified
1035 management frames |frame_type| at the specified interface |interface|.
1036
1037 @param interface string interface to inject frames.
1038 @param frame_type string message type.
1039 @param channel int targeted channel.
1040 @param ssid_prefix string SSID prefix.
1041 @param num_bss int number of BSS.
1042 @param frame_count int number of frames to send.
1043 @param delay int milliseconds delay between frames.
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001044 @param dest_addr string destination address (DA) MAC address.
1045 @param probe_resp_footer string footer for probe response.
Peter Qiuc4beba02014-03-24 14:46:24 -07001046
1047 @return int PID of the newly created process.
1048
1049 """
1050 command = '%s -i %s -t %s -c %d' % (self.cmd_send_management_frame,
1051 interface, frame_type, channel)
1052 if ssid_prefix is not None:
1053 command += ' -s %s' % (ssid_prefix)
1054 if num_bss is not None:
1055 command += ' -b %d' % (num_bss)
1056 if frame_count is not None:
1057 command += ' -n %d' % (frame_count)
1058 if delay is not None:
1059 command += ' -d %d' % (delay)
Rebecca Silberstein194b4582015-06-17 13:29:38 -07001060 if dest_addr is not None:
1061 command += ' -a %s' % (dest_addr)
1062 if probe_resp_footer is not None:
1063 self._prep_probe_response_footer(footer=probe_resp_footer)
1064 command += ' -f %s' % (self.PROBE_RESPONSE_FOOTER_FILE)
Peter Qiuc4beba02014-03-24 14:46:24 -07001065 command += ' > %s 2>&1 & echo $!' % (self.MGMT_FRAME_SENDER_LOG_FILE)
1066 pid = int(self.router.run(command).stdout)
1067 return pid
1068
1069
Paul Stewart25536942013-08-15 17:33:42 -07001070 def detect_client_deauth(self, client_mac, instance=0):
1071 """Detects whether hostapd has logged a deauthentication from
1072 |client_mac|.
1073
1074 @param client_mac string the MAC address of the client to detect.
1075 @param instance int indicating which hostapd instance to query.
1076
1077 """
Christopher Wileyd6e503c2014-06-23 15:53:47 -07001078 interface = self.hostapd_instances[instance].interface
Paul Stewart25536942013-08-15 17:33:42 -07001079 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
Christopher Wileyd6e503c2014-06-23 15:53:47 -07001080 log_file = self.hostapd_instances[instance].log_file
Paul Stewart25536942013-08-15 17:33:42 -07001081 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
1082 ignore_status=True)
1083 return result.exit_status == 0
1084
1085
Paul Stewart4ae471e2013-09-04 15:42:35 -07001086 def detect_client_coexistence_report(self, client_mac, instance=0):
1087 """Detects whether hostapd has logged an action frame from
1088 |client_mac| indicating information about 20/40MHz BSS coexistence.
1089
1090 @param client_mac string the MAC address of the client to detect.
1091 @param instance int indicating which hostapd instance to query.
1092
1093 """
1094 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): '
1095 '.. .. .. .. .. .. .. .. .. .. %s '
1096 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' %
1097 ' '.join(client_mac.split(':')))
Christopher Wileyd6e503c2014-06-23 15:53:47 -07001098 log_file = self.hostapd_instances[instance].log_file
Paul Stewart4ae471e2013-09-04 15:42:35 -07001099 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file),
1100 ignore_status=True)
1101 return result.exit_status == 0
1102
1103
Paul Stewart6ddeba72013-11-18 10:08:23 -08001104 def add_connected_peer(self, instance=0):
1105 """Configure a station connected to a running AP instance.
1106
1107 Extract relevant configuration objects from the hostap
1108 configuration for |instance| and generate a wpa_supplicant
1109 instance that connects to it. This allows the DUT to interact
1110 with a client entity that is also connected to the same AP. A
1111 full wpa_supplicant instance is necessary here (instead of just
1112 using the "iw" command to connect) since we want to enable
1113 advanced features such as TDLS.
1114
1115 @param instance int indicating which hostapd instance to connect to.
1116
1117 """
1118 if not self.hostapd_instances:
1119 raise error.TestFail('Hostapd is not configured.')
1120
Christopher Wiley408d1812014-01-13 15:27:43 -08001121 if self.station_instances:
Paul Stewart6ddeba72013-11-18 10:08:23 -08001122 raise error.TestFail('Station is already configured.')
1123
Christopher Wiley408d1812014-01-13 15:27:43 -08001124 ssid = self.get_ssid(instance)
Christopher Wileyd6e503c2014-06-23 15:53:47 -07001125 hostap_conf = self.hostapd_instances[instance].config_dict
Paul Stewart6ddeba72013-11-18 10:08:23 -08001126 frequency = hostap_config.HostapConfig.get_frequency_for_channel(
1127 hostap_conf['channel'])
Christopher Wileyab3964f2015-01-26 12:59:12 -08001128 self.configure_managed_station(
1129 ssid, frequency, self.local_peer_ip_address(instance))
1130 interface = self.station_instances[0].interface
1131 # Since we now have two network interfaces connected to the same
1132 # network, we need to disable the kernel's protection against
1133 # incoming packets to an "unexpected" interface.
1134 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' %
1135 interface)
1136
1137 # Similarly, we'd like to prevent the hostap interface from
1138 # replying to ARP requests for the peer IP address and vice
1139 # versa.
1140 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
1141 interface)
1142 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' %
1143 hostap_conf['interface'])
1144
1145
1146 def configure_managed_station(self, ssid, frequency, ip_addr):
1147 """Configure a router interface to connect as a client to a network.
1148
1149 @param ssid: string SSID of network to join.
1150 @param frequency: int frequency required to join the network.
1151 @param ip_addr: IP address to assign to this interface
1152 (e.g. '192.168.1.200').
1153
1154 """
Christopher Wiley408d1812014-01-13 15:27:43 -08001155 interface = self.get_wlanif(frequency, 'managed')
Paul Stewart6ddeba72013-11-18 10:08:23 -08001156
1157 # TODO(pstew): Configure other bits like PSK, 802.11n if tests
1158 # require them...
1159 supplicant_config = (
1160 'network={\n'
1161 ' ssid="%(ssid)s"\n'
1162 ' key_mgmt=NONE\n'
Christopher Wiley408d1812014-01-13 15:27:43 -08001163 '}\n' % {'ssid': ssid}
Paul Stewart6ddeba72013-11-18 10:08:23 -08001164 )
1165
Christopher Wileyeea12362013-12-12 17:24:29 -08001166 conf_file = self.STATION_CONF_FILE_PATTERN % interface
1167 log_file = self.STATION_LOG_FILE_PATTERN % interface
1168 pid_file = self.STATION_PID_FILE_PATTERN % interface
Paul Stewart6ddeba72013-11-18 10:08:23 -08001169
1170 self.router.run('cat <<EOF >%s\n%s\nEOF\n' %
1171 (conf_file, supplicant_config))
1172
1173 # Connect the station.
1174 self.router.run('%s link set %s up' % (self.cmd_ip, interface))
Ben Chan630d6b42015-02-13 18:14:45 -08001175 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s >%s 2>&1 &' %
Paul Stewart6ddeba72013-11-18 10:08:23 -08001176 (self.cmd_wpa_supplicant,
1177 interface, pid_file, conf_file,
Christopher Wileyeea12362013-12-12 17:24:29 -08001178 self.HOSTAPD_DRIVER_NAME, log_file))
Paul Stewart6ddeba72013-11-18 10:08:23 -08001179 self.router.run(start_command)
1180 self.iw_runner.wait_for_link(interface)
1181
1182 # Assign an IP address to this interface.
1183 self.router.run('%s addr add %s/24 dev %s' %
Christopher Wileyab3964f2015-01-26 12:59:12 -08001184 (self.cmd_ip, ip_addr, interface))
Christopher Wiley408d1812014-01-13 15:27:43 -08001185 self.station_instances.append(
1186 StationInstance(ssid=ssid, interface=interface,
1187 dev_type='managed'))
Eric Carusoeddedd32014-10-13 14:41:49 -07001188
1189
1190 def send_magic_packet(self, dest_ip, dest_mac):
1191 """Sends a magic packet to the NIC with the given IP and MAC addresses.
1192
1193 @param dest_ip the IP address of the device to send the packet to
1194 @param dest_mac the hardware MAC address of the device
1195
1196 """
1197 # magic packet is 6 0xff bytes followed by the hardware address
1198 # 16 times
1199 mac_bytes = ''.join([chr(int(b, 16)) for b in dest_mac.split(':')])
1200 magic_packet = '\xff' * 6 + mac_bytes * 16
1201
1202 logging.info('Sending magic packet to %s...', dest_ip)
1203 self.host.run('python -uc "import socket, sys;'
1204 's = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);'
1205 's.sendto(sys.stdin.read(), (\'%s\', %d))"' %
1206 (dest_ip, UDP_DISCARD_PORT),
1207 stdin=magic_packet)
Paul Stewart65fb9212014-12-01 19:54:20 -08001208
1209
1210 def set_beacon_footer(self, interface, footer=''):
1211 """Sets the beacon footer (appended IE information) for this interface.
1212
1213 @param interface string interface to set the footer on.
1214 @param footer string footer to be set on the interface.
1215
1216 """
1217 footer_file = ('/sys/kernel/debug/ieee80211/%s/beacon_footer' %
1218 self.iw_runner.get_interface(interface).phy)
1219 if self.router.run('test -e %s' % footer_file,
1220 ignore_status=True).exit_status != 0:
1221 logging.info('Beacon footer file does not exist. Ignoring.')
1222 return
1223 self.host.run('echo -ne %s > %s' % ('%r' % footer, footer_file))
Peter Qiu9a63a8b2015-02-03 09:08:16 -08001224
1225
1226 def setup_bridge_mode_dhcp_server(self):
1227 """Setup an DHCP server for bridge mode.
1228
1229 Setup an DHCP server on the master interface of the virtual ethernet
1230 pair, with peer interface connected to the bridge interface. This is
1231 used for testing APs in bridge mode.
1232
1233 """
1234 # Start a local server on master interface of virtual ethernet pair.
1235 self.start_local_server(
1236 self.get_virtual_ethernet_master_interface())
1237 # Add peer interface to the bridge.
1238 self.add_interface_to_bridge(
Ben Chan630d6b42015-02-13 18:14:45 -08001239 self.get_virtual_ethernet_peer_interface())