site_linux_router.py revision aeef9b52c54a27098925b44d93f270a6d8f29e3b
1# 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 5import collections 6import logging 7import random 8import string 9import time 10 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros.network import interface 13from autotest_lib.server import site_linux_system 14from autotest_lib.server.cros import wifi_test_utils 15from autotest_lib.server.cros.network import hostap_config 16 17 18StationInstance = collections.namedtuple('StationInstance', 19 ['ssid', 'interface', 'dev_type']) 20 21 22class LinuxRouter(site_linux_system.LinuxSystem): 23 """Linux/mac80211-style WiFi Router support for WiFiTest class. 24 25 This class implements test methods/steps that communicate with a 26 router implemented with Linux/mac80211. The router must 27 be pre-configured to enable ssh access and have a mac80211-based 28 wireless device. We also assume hostapd 0.7.x and iw are present 29 and any necessary modules are pre-loaded. 30 31 """ 32 33 KNOWN_TEST_PREFIX = 'network_WiFi' 34 STARTUP_POLLING_INTERVAL_SECONDS = 0.5 35 STARTUP_TIMEOUT_SECONDS = 10 36 SUFFIX_LETTERS = string.ascii_lowercase + string.digits 37 SUBNET_PREFIX_OCTETS = (192, 168) 38 39 HOSTAPD_CONF_FILE_PATTERN = '/tmp/hostapd-test-%s.conf' 40 HOSTAPD_LOG_FILE_PATTERN = '/tmp/hostapd-test-%s.log' 41 HOSTAPD_PID_FILE_PATTERN = '/tmp/hostapd-test-%s.pid' 42 HOSTAPD_CONTROL_INTERFACE_PATTERN = '/tmp/hostapd-test-%s.ctrl' 43 HOSTAPD_DRIVER_NAME = 'nl80211' 44 45 STATION_CONF_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.conf' 46 STATION_LOG_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.log' 47 STATION_PID_FILE_PATTERN = '/tmp/wpa-supplicant-test-%s.pid' 48 49 def get_capabilities(self): 50 """@return iterable object of AP capabilities for this system.""" 51 caps = set([self.CAPABILITY_IBSS]) 52 try: 53 self.cmd_send_management_frame = wifi_test_utils.must_be_installed( 54 self.host, '/usr/bin/send_management_frame') 55 caps.add(self.CAPABILITY_SEND_MANAGEMENT_FRAME) 56 except error.TestFail: 57 pass 58 return super(LinuxRouter, self).get_capabilities().union(caps) 59 60 61 @property 62 def router(self): 63 """Deprecated. Use self.host instead. 64 65 @return Host object representing the remote router. 66 67 """ 68 return self.host 69 70 71 @property 72 def wifi_ip(self): 73 """Simple accessor for the WiFi IP when there is only one AP. 74 75 @return string IP of WiFi interface. 76 77 """ 78 if len(self.local_servers) != 1: 79 raise error.TestError('Could not pick a WiFi IP to return.') 80 81 return self.get_wifi_ip(0) 82 83 84 def __init__(self, host, test_name): 85 """Build a LinuxRouter. 86 87 @param host Host object representing the remote machine. 88 @param test_name string name of this test. Used in SSID creation. 89 90 """ 91 super(LinuxRouter, self).__init__(host, 'router') 92 93 self.cmd_dhcpd = '/usr/sbin/dhcpd' 94 self.cmd_hostapd = wifi_test_utils.must_be_installed( 95 host, '/usr/sbin/hostapd') 96 self.cmd_hostapd_cli = wifi_test_utils.must_be_installed( 97 host, '/usr/sbin/hostapd_cli') 98 self.cmd_wpa_supplicant = wifi_test_utils.must_be_installed( 99 host, '/usr/sbin/wpa_supplicant') 100 self.dhcpd_conf = '/tmp/dhcpd.%s.conf' 101 self.dhcpd_leases = '/tmp/dhcpd.leases' 102 103 # hostapd configuration persists throughout the test, subsequent 104 # 'config' commands only modify it. 105 self.ssid_prefix = test_name 106 if self.ssid_prefix.startswith(self.KNOWN_TEST_PREFIX): 107 # Many of our tests start with an uninteresting prefix. 108 # Remove it so we can have more unique bytes. 109 self.ssid_prefix = self.ssid_prefix[len(self.KNOWN_TEST_PREFIX):] 110 self.ssid_prefix = self.ssid_prefix.lstrip('_') 111 self.ssid_prefix += '_' 112 113 self._total_hostapd_instances = 0 114 self.local_servers = [] 115 self.hostapd_instances = [] 116 self.station_instances = [] 117 self.dhcp_low = 1 118 self.dhcp_high = 128 119 120 # Kill hostapd and dhcp server if already running. 121 self.kill_hostapd() 122 self.stop_dhcp_servers() 123 124 # Place us in the US by default 125 self.iw_runner.set_regulatory_domain('US') 126 127 # Reset all antennas to be active 128 self.set_default_antenna_bitmap() 129 130 131 def close(self): 132 """Close global resources held by this system.""" 133 self.deconfig() 134 super(LinuxRouter, self).close() 135 136 137 def has_local_server(self): 138 """@return True iff this router has local servers configured.""" 139 return bool(self.local_servers) 140 141 142 def start_hostapd(self, configuration): 143 """Start a hostapd instance described by conf. 144 145 @param configuration HostapConfig object. 146 147 """ 148 # Figure out the correct interface. 149 interface = self.get_wlanif(configuration.frequency, 'managed') 150 151 conf_file = self.HOSTAPD_CONF_FILE_PATTERN % interface 152 log_file = self.HOSTAPD_LOG_FILE_PATTERN % interface 153 pid_file = self.HOSTAPD_PID_FILE_PATTERN % interface 154 control_interface = self.HOSTAPD_CONTROL_INTERFACE_PATTERN % interface 155 hostapd_conf_dict = configuration.generate_dict( 156 interface, control_interface, 157 self._build_ssid(configuration.ssid_suffix)) 158 logging.info('Starting hostapd with parameters: %r', hostapd_conf_dict) 159 160 # Generate hostapd.conf. 161 self.router.run("cat <<EOF >%s\n%s\nEOF\n" % 162 (conf_file, '\n'.join( 163 "%s=%s" % kv for kv in hostapd_conf_dict.iteritems()))) 164 165 # Run hostapd. 166 logging.info("Starting hostapd...") 167 self.router.run('rm %s' % log_file, ignore_status=True) 168 self.router.run('rm %s' % pid_file, ignore_status=True) 169 self.router.run('stop wpasupplicant', ignore_status=True) 170 start_command = '%s -dd -B -t -f %s -P %s %s' % ( 171 self.cmd_hostapd, log_file, pid_file, conf_file) 172 self.router.run(start_command) 173 self.hostapd_instances.append({ 174 'ssid': hostapd_conf_dict['ssid'], 175 'conf_file': conf_file, 176 'log_file': log_file, 177 'interface': interface, 178 'pid_file': pid_file, 179 'config_dict': hostapd_conf_dict.copy() 180 }) 181 182 # Wait for confirmation that the router came up. 183 pid = int(self.router.run('cat %s' % pid_file).stdout) 184 logging.info('Waiting for hostapd to startup.') 185 start_time = time.time() 186 while time.time() - start_time < self.STARTUP_TIMEOUT_SECONDS: 187 success = self.router.run( 188 'grep "Completing interface initialization" %s' % log_file, 189 ignore_status=True).exit_status == 0 190 if success: 191 break 192 193 # A common failure is an invalid router configuration. 194 # Detect this and exit early if we see it. 195 bad_config = self.router.run( 196 'grep "Interface initialization failed" %s' % log_file, 197 ignore_status=True).exit_status == 0 198 if bad_config: 199 raise error.TestFail('hostapd failed to initialize AP ' 200 'interface.') 201 202 if pid: 203 early_exit = self.router.run('kill -0 %d' % pid, 204 ignore_status=True).exit_status 205 if early_exit: 206 raise error.TestFail('hostapd process terminated.') 207 208 time.sleep(self.STARTUP_POLLING_INTERVAL_SECONDS) 209 else: 210 raise error.TestFail('Timed out while waiting for hostapd ' 211 'to start.') 212 213 214 def _kill_process_instance(self, process, instance=None, wait=0): 215 """Kill a process on the router. 216 217 Kills program named |process|, optionally only a specific 218 |instance|. If |wait| is specified, we makes sure |process| exits 219 before returning. 220 221 @param process string name of process to kill. 222 @param instance string instance of process to kill. 223 @param wait int timeout in seconds to wait for. 224 225 """ 226 if instance: 227 search_arg = '-f "%s.*%s"' % (process, instance) 228 else: 229 search_arg = process 230 231 cmd = "pkill %s >/dev/null 2>&1" % search_arg 232 233 if wait: 234 cmd += (" && while pgrep %s &> /dev/null; do sleep 1; done" % 235 search_arg) 236 self.router.run(cmd, timeout=wait, ignore_status=True) 237 else: 238 self.router.run(cmd, ignore_status=True) 239 240 241 def kill_hostapd_instance(self, instance): 242 """Kills a hostapd instance. 243 244 @param instance string instance to kill. 245 246 """ 247 self._kill_process_instance('hostapd', instance, 30) 248 249 250 def kill_hostapd(self): 251 """Kill all hostapd instances.""" 252 self.kill_hostapd_instance(None) 253 254 255 def _build_ssid(self, suffix): 256 unique_salt = ''.join([random.choice(self.SUFFIX_LETTERS) 257 for x in range(5)]) 258 return (self.ssid_prefix + unique_salt + suffix)[-32:] 259 260 261 def hostap_configure(self, configuration, multi_interface=None): 262 """Build up a hostapd configuration file and start hostapd. 263 264 Also setup a local server if this router supports them. 265 266 @param configuration HosetapConfig object. 267 @param multi_interface bool True iff multiple interfaces allowed. 268 269 """ 270 if multi_interface is None and (self.hostapd_instances or 271 self.station_instances): 272 self.deconfig() 273 self.start_hostapd(configuration) 274 interface = self.hostapd_instances[-1]['interface'] 275 self.iw_runner.set_tx_power(interface, 'auto') 276 self.start_local_server(interface) 277 logging.info('AP configured.') 278 279 280 @staticmethod 281 def ip_addr(netblock, idx): 282 """Simple IPv4 calculator. 283 284 Takes host address in "IP/bits" notation and returns netmask, broadcast 285 address as well as integer offsets into the address range. 286 287 @param netblock string host address in "IP/bits" notation. 288 @param idx string describing what to return. 289 @return string containing something you hopefully requested. 290 291 """ 292 addr_str,bits = netblock.split('/') 293 addr = map(int, addr_str.split('.')) 294 mask_bits = (-1 << (32-int(bits))) & 0xffffffff 295 mask = [(mask_bits >> s) & 0xff for s in range(24, -1, -8)] 296 if idx == 'local': 297 return addr_str 298 elif idx == 'netmask': 299 return '.'.join(map(str, mask)) 300 elif idx == 'broadcast': 301 offset = [m ^ 0xff for m in mask] 302 else: 303 offset = [(idx >> s) & 0xff for s in range(24, -1, -8)] 304 return '.'.join(map(str, [(a & m) + o 305 for a, m, o in zip(addr, mask, offset)])) 306 307 308 def ibss_configure(self, config): 309 """Configure a station based AP in IBSS mode. 310 311 Extract relevant configuration objects from |config| despite not 312 actually being a hostap managed endpoint. 313 314 @param config HostapConfig object. 315 316 """ 317 if self.station_instances or self.hostapd_instances: 318 self.deconfig() 319 interface = self.get_wlanif(config.frequency, 'ibss') 320 ssid = (config.ssid or self._build_ssid(config.ssid_suffix)) 321 # Connect the station 322 self.router.run('%s link set %s up' % (self.cmd_ip, interface)) 323 self.iw_runner.ibss_join(interface, ssid, config.frequency) 324 # Always start a local server. 325 self.start_local_server(interface) 326 # Remember that this interface is up. 327 self.station_instances.append( 328 StationInstance(ssid=ssid, interface=interface, 329 dev_type='ibss')) 330 331 332 def local_server_address(self, index): 333 """Get the local server address for an interface. 334 335 When we multiple local servers, we give them static IP addresses 336 like 192.168.*.254. 337 338 @param index int describing which local server this is for. 339 340 """ 341 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 254)) 342 343 344 def local_peer_ip_address(self, index): 345 """Get the IP address allocated for the peer associated to the AP. 346 347 This address is assigned to a locally associated peer device that 348 is created for the DUT to perform connectivity tests with. 349 When we have multiple local servers, we give them static IP addresses 350 like 192.168.*.253. 351 352 @param index int describing which local server this is for. 353 354 """ 355 return '%d.%d.%d.%d' % (self.SUBNET_PREFIX_OCTETS + (index, 253)) 356 357 358 def local_peer_mac_address(self): 359 """Get the MAC address of the peer interface. 360 361 @return string MAC address of the peer interface. 362 363 """ 364 iface = interface.Interface(self.station_instances[0].interface, 365 self.router) 366 return iface.mac_address 367 368 369 def start_local_server(self, interface): 370 """Start a local server on an interface. 371 372 @param interface string (e.g. wlan0) 373 374 """ 375 logging.info("Starting up local server...") 376 377 if len(self.local_servers) >= 256: 378 raise error.TestFail('Exhausted available local servers') 379 380 netblock = '%s/24' % self.local_server_address(len(self.local_servers)) 381 382 params = {} 383 params['netblock'] = netblock 384 params['subnet'] = self.ip_addr(netblock, 0) 385 params['netmask'] = self.ip_addr(netblock, 'netmask') 386 params['dhcp_range'] = ' '.join( 387 (self.ip_addr(netblock, self.dhcp_low), 388 self.ip_addr(netblock, self.dhcp_high))) 389 params['interface'] = interface 390 391 params['ip_params'] = ("%s broadcast %s dev %s" % 392 (netblock, 393 self.ip_addr(netblock, 'broadcast'), 394 interface)) 395 self.local_servers.append(params) 396 397 self.router.run("%s addr flush %s" % 398 (self.cmd_ip, interface)) 399 self.router.run("%s addr add %s" % 400 (self.cmd_ip, params['ip_params'])) 401 self.router.run("%s link set %s up" % 402 (self.cmd_ip, interface)) 403 self.start_dhcp_server(interface) 404 405 406 def start_dhcp_server(self, interface): 407 """Start a dhcp server on an interface. 408 409 @param interface string (e.g. wlan0) 410 411 """ 412 for server in self.local_servers: 413 if server['interface'] == interface: 414 params = server 415 break 416 else: 417 raise error.TestFail('Could not find local server ' 418 'to match interface: %r' % interface) 419 420 dhcpd_conf_file = self.dhcpd_conf % interface 421 dhcp_conf = '\n'.join([ 422 'port=0', # disables DNS server 423 'bind-interfaces', 424 'log-dhcp', 425 'dhcp-range=%s' % params['dhcp_range'].replace(' ', ','), 426 'interface=%s' % params['interface'], 427 'dhcp-leasefile=%s' % self.dhcpd_leases]) 428 self.router.run('cat <<EOF >%s\n%s\nEOF\n' % 429 (dhcpd_conf_file, dhcp_conf)) 430 self.router.run('dnsmasq --conf-file=%s' % dhcpd_conf_file) 431 432 433 def stop_dhcp_server(self, instance=None): 434 """Stop a dhcp server on the router. 435 436 @param instance string instance to kill. 437 438 """ 439 self._kill_process_instance('dnsmasq', instance, 0) 440 441 442 def stop_dhcp_servers(self): 443 """Stop all dhcp servers on the router.""" 444 self.stop_dhcp_server(None) 445 446 447 def get_wifi_channel(self, ap_num): 448 """Return channel of BSS corresponding to |ap_num|. 449 450 @param ap_num int which BSS to get the channel of. 451 @return int primary channel of BSS. 452 453 """ 454 instance = self.hostapd_instances[ap_num] 455 return instance['config_dict']['channel'] 456 457 458 def get_wifi_ip(self, ap_num): 459 """Return IP address on the WiFi subnet of a local server on the router. 460 461 If no local servers are configured (e.g. for an RSPro), a TestFail will 462 be raised. 463 464 @param ap_num int which local server to get an address from. 465 466 """ 467 if self.local_servers: 468 return self.ip_addr(self.local_servers[ap_num]['netblock'], 469 'local') 470 else: 471 raise error.TestFail("No IP address assigned") 472 473 474 def get_hostapd_interface(self, ap_num): 475 """Get the name of the interface associated with a hostapd instance. 476 477 @param ap_num: int hostapd instance number. 478 @return string interface name (e.g. 'managed0'). 479 480 """ 481 if ap_num not in range(len(self.hostapd_instances)): 482 raise error.TestFail('Invalid instance number (%d) with %d ' 483 'instances configured.' % 484 (ap_num, len(self.hostapd_instances))) 485 486 instance = self.hostapd_instances[ap_num] 487 return instance['interface'] 488 489 490 def get_hostapd_mac(self, ap_num): 491 """Return the MAC address of an AP in the test. 492 493 @param ap_num int index of local server to read the MAC address from. 494 @return string MAC address like 00:11:22:33:44:55. 495 496 """ 497 interface_name = self.get_hostapd_interface(ap_num) 498 ap_interface = interface.Interface(interface_name, self.host) 499 return ap_interface.mac_address 500 501 502 def get_hostapd_phy(self, ap_num): 503 """Get name of phy for hostapd instance. 504 505 @param ap_num int index of hostapd instance. 506 @return string phy name of phy corresponding to hostapd's 507 managed interface. 508 509 """ 510 interface = self.iw_runner.get_interface( 511 self.get_hostapd_interface(ap_num)) 512 return interface.phy 513 514 515 def deconfig(self): 516 """A legacy, deprecated alias for deconfig_aps.""" 517 self.deconfig_aps() 518 519 520 def deconfig_aps(self, instance=None, silent=False): 521 """De-configure an AP (will also bring wlan down). 522 523 @param instance: int or None. If instance is None, will bring down all 524 instances of hostapd. 525 @param silent: True if instances should be brought without de-authing 526 the DUT. 527 528 """ 529 if not self.hostapd_instances and not self.station_instances: 530 return 531 532 if self.hostapd_instances: 533 local_servers = [] 534 if instance is not None: 535 instances = [ self.hostapd_instances.pop(instance) ] 536 for server in self.local_servers: 537 if server['interface'] == instances[0]['interface']: 538 local_servers = [server] 539 self.local_servers.remove(server) 540 break 541 else: 542 instances = self.hostapd_instances 543 self.hostapd_instances = [] 544 local_servers = self.local_servers 545 self.local_servers = [] 546 547 for instance in instances: 548 if silent: 549 # Deconfigure without notifying DUT. Remove the interface 550 # hostapd uses to send beacon and DEAUTH packets. 551 self.remove_interface(instance['interface']) 552 553 self.kill_hostapd_instance(instance['conf_file']) 554 if wifi_test_utils.is_installed(self.host, 555 instance['log_file']): 556 self.router.get_file(instance['log_file'], 557 'debug/hostapd_router_%d_%s.log' % 558 (self._total_hostapd_instances, 559 instance['interface'])) 560 else: 561 logging.error('Did not collect hostapd log file because ' 562 'it was missing.') 563 self.release_interface(instance['interface']) 564# self.router.run("rm -f %(log_file)s %(conf_file)s" % instance) 565 self._total_hostapd_instances += 1 566 if self.station_instances: 567 local_servers = self.local_servers 568 self.local_servers = [] 569 instance = self.station_instances.pop() 570 if instance.dev_type == 'ibss': 571 self.iw_runner.ibss_leave(instance.interface) 572 elif instance.dev_type == 'managed': 573 self._kill_process_instance('wpa_supplicant', 574 instance.interface) 575 else: 576 self.iw_runner.disconnect_station(instance.interface) 577 self.router.run('%s link set %s down' % 578 (self.cmd_ip, instance.interface)) 579 580 for server in local_servers: 581 self.stop_dhcp_server(server['interface']) 582 self.router.run("%s addr del %s" % 583 (self.cmd_ip, server['ip_params']), 584 ignore_status=True) 585 586 587 def confirm_pmksa_cache_use(self, instance=0): 588 """Verify that the PMKSA auth was cached on a hostapd instance. 589 590 @param instance int router instance number. 591 592 """ 593 log_file = self.hostapd_instances[instance]['log_file'] 594 pmksa_match = 'PMK from PMKSA cache' 595 result = self.router.run('grep -q "%s" %s' % (pmksa_match, log_file), 596 ignore_status=True) 597 if result.exit_status: 598 raise error.TestFail('PMKSA cache was not used in roaming.') 599 600 601 def get_ssid(self, instance=None): 602 """@return string ssid for the network stemming from this router.""" 603 if instance is None: 604 instance = 0 605 if len(self.hostapd_instances) > 1: 606 raise error.TestFail('No instance of hostapd specified with ' 607 'multiple instances present.') 608 609 if self.hostapd_instances: 610 return self.hostapd_instances[instance]['ssid'] 611 612 if self.station_instances: 613 return self.station_instances[0].ssid 614 615 raise error.TestFail('Requested ssid of an unconfigured AP.') 616 617 618 def deauth_client(self, client_mac): 619 """Deauthenticates a client described in params. 620 621 @param client_mac string containing the mac address of the client to be 622 deauthenticated. 623 624 """ 625 control_if = self.hostapd_instances[-1]['config_dict']['ctrl_interface'] 626 self.router.run('%s -p%s deauthenticate %s' % 627 (self.cmd_hostapd_cli, control_if, client_mac)) 628 629 630 def send_management_frame(self, frame_type, instance=0): 631 """Injects a management frame into an active hostapd session. 632 633 @param frame_type string the type of frame to send. 634 @param instance int indicating which hostapd instance to inject into. 635 636 """ 637 hostap_interface = self.hostapd_instances[instance]['interface'] 638 interface = self.get_wlanif(0, 'monitor', same_phy_as=hostap_interface) 639 self.router.run("%s link set %s up" % (self.cmd_ip, interface)) 640 self.router.run('%s %s %s' % 641 (self.cmd_send_management_frame, interface, frame_type)) 642 self.release_interface(interface) 643 644 645 def detect_client_deauth(self, client_mac, instance=0): 646 """Detects whether hostapd has logged a deauthentication from 647 |client_mac|. 648 649 @param client_mac string the MAC address of the client to detect. 650 @param instance int indicating which hostapd instance to query. 651 652 """ 653 interface = self.hostapd_instances[instance]['interface'] 654 deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac) 655 log_file = self.hostapd_instances[instance]['log_file'] 656 result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file), 657 ignore_status=True) 658 return result.exit_status == 0 659 660 661 def detect_client_coexistence_report(self, client_mac, instance=0): 662 """Detects whether hostapd has logged an action frame from 663 |client_mac| indicating information about 20/40MHz BSS coexistence. 664 665 @param client_mac string the MAC address of the client to detect. 666 @param instance int indicating which hostapd instance to query. 667 668 """ 669 coex_msg = ('nl80211: MLME event frame - hexdump(len=.*): ' 670 '.. .. .. .. .. .. .. .. .. .. %s ' 671 '.. .. .. .. .. .. .. .. 04 00.*48 01 ..' % 672 ' '.join(client_mac.split(':'))) 673 log_file = self.hostapd_instances[instance]['log_file'] 674 result = self.router.run("grep -qi '%s' %s" % (coex_msg, log_file), 675 ignore_status=True) 676 return result.exit_status == 0 677 678 679 def add_connected_peer(self, instance=0): 680 """Configure a station connected to a running AP instance. 681 682 Extract relevant configuration objects from the hostap 683 configuration for |instance| and generate a wpa_supplicant 684 instance that connects to it. This allows the DUT to interact 685 with a client entity that is also connected to the same AP. A 686 full wpa_supplicant instance is necessary here (instead of just 687 using the "iw" command to connect) since we want to enable 688 advanced features such as TDLS. 689 690 @param instance int indicating which hostapd instance to connect to. 691 692 """ 693 if not self.hostapd_instances: 694 raise error.TestFail('Hostapd is not configured.') 695 696 if self.station_instances: 697 raise error.TestFail('Station is already configured.') 698 699 ssid = self.get_ssid(instance) 700 hostap_conf = self.hostapd_instances[instance]['config_dict'] 701 frequency = hostap_config.HostapConfig.get_frequency_for_channel( 702 hostap_conf['channel']) 703 interface = self.get_wlanif(frequency, 'managed') 704 705 # TODO(pstew): Configure other bits like PSK, 802.11n if tests 706 # require them... 707 supplicant_config = ( 708 'network={\n' 709 ' ssid="%(ssid)s"\n' 710 ' key_mgmt=NONE\n' 711 '}\n' % {'ssid': ssid} 712 ) 713 714 conf_file = self.STATION_CONF_FILE_PATTERN % interface 715 log_file = self.STATION_LOG_FILE_PATTERN % interface 716 pid_file = self.STATION_PID_FILE_PATTERN % interface 717 718 self.router.run('cat <<EOF >%s\n%s\nEOF\n' % 719 (conf_file, supplicant_config)) 720 721 # Connect the station. 722 self.router.run('%s link set %s up' % (self.cmd_ip, interface)) 723 start_command = ('%s -dd -t -i%s -P%s -c%s -D%s &> %s &' % 724 (self.cmd_wpa_supplicant, 725 interface, pid_file, conf_file, 726 self.HOSTAPD_DRIVER_NAME, log_file)) 727 self.router.run(start_command) 728 self.iw_runner.wait_for_link(interface) 729 730 # Assign an IP address to this interface. 731 self.router.run('%s addr add %s/24 dev %s' % 732 (self.cmd_ip, self.local_peer_ip_address(instance), 733 interface)) 734 735 # Since we now have two network interfaces connected to the same 736 # network, we need to disable the kernel's protection against 737 # incoming packets to an "unexpected" interface. 738 self.router.run('echo 2 > /proc/sys/net/ipv4/conf/%s/rp_filter' % 739 interface) 740 741 # Similarly, we'd like to prevent the hostap interface from 742 # replying to ARP requests for the peer IP address and vice 743 # versa. 744 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' % 745 interface) 746 self.router.run('echo 1 > /proc/sys/net/ipv4/conf/%s/arp_ignore' % 747 hostap_conf['interface']) 748 749 self.station_instances.append( 750 StationInstance(ssid=ssid, interface=interface, 751 dev_type='managed')) 752