def set_client_wifi(opt): """Set up a wifi client in response to the 'setclient' command. Args: opt: The OptDict parsed from command line options. Returns: Whether wpa_supplicant successfully started and associated. Raises: BinWifiException: On various errors. """ if not opt.ssid: raise utils.BinWifiException('You must specify an ssid with --ssid') band = opt.band if band not in ('2.4', '5'): raise utils.BinWifiException('You must specify band with -b2.4 or -b5') psk = os.environ.get('WIFI_CLIENT_PSK', None) if band == '5' and quantenna.set_client_wifi(opt): return True mwifiex.set_recovery(experiment.enabled('MwifiexFirmwareRecovery')) phy = iw.find_phy(band, 'auto') if phy is None: utils.log("Couldn't find phy for band %s", band) return False interface = iw.find_interface_from_phy(phy, iw.INTERFACE_TYPE.client, opt.interface_suffix) if interface is None: # Create the client interface if it does not exist, using the same number as # an existing AP interface, which is stable across driver reloads. interface = client_interface_name(phy, opt.interface_suffix) if interface is None: raise utils.BinWifiException( 'AP interface not initialized for %s' % phy) if not iw.does_interface_exist(interface): utils.log('Creating client interface %s', interface) utils.subprocess_quiet(('iw', 'phy', phy, 'interface', 'add', interface, 'type', 'station'), no_stdout=True) ap_mac_address = utils.get_mac_address_for_interface( iw.find_interface_from_phy(phy, iw.INTERFACE_TYPE.ap, opt.interface_suffix)) mac_address = utils.increment_mac_address(ap_mac_address) subprocess.check_call( ('ip', 'link', 'set', interface, 'address', mac_address)) wpa_config = configs.generate_wpa_supplicant_config(opt.ssid, psk, opt) if not _set_wpa_supplicant_config(interface, wpa_config, opt): return False return True
def _reload_driver(): """Reload the ath10k driver so it picks up modified calibration file.""" ret = utils.subprocess_quiet(('rmmod', 'ath10k_pci')) if ret != 0: _log('rmmod ath10k_pci failed: {}.'.format(ret)) return ret = utils.subprocess_quiet(('modprobe', 'ath10k_pci')) if ret != 0: _log('modprobe ath10k_pci failed: {}.'.format(ret)) return _log('reload ath10k driver complete')
def _stop_hostapd(interface): """Stops hostapd from running on the given interface. Also removes the pid file, sets them interface down and deletes the monitor interface, if it exists. Args: interface: The interface on which to stop hostapd. Returns: Whether hostapd was successfully stopped and cleaned up. """ if not _is_hostapd_running(interface): utils.log('hostapd already not running.') return True config_filename = utils.get_filename('hostapd', utils.FILENAME_KIND.config, interface, tmp=True) pid_filename = utils.get_filename('hostapd', utils.FILENAME_KIND.pid, interface, tmp=True) if not utils.kill_pid('hostapd .* %s$' % config_filename, pid_filename): return False # TODO(apenwarr): hostapd doesn't always delete interface mon.$ifc. Then it # gets confused by the fact that it already exists. Let's help out. We # should really fix this by eliminating the need for hostapd to have a # monitor interface at all (which is deprecated anyway) Remove this line when # our hostapd no longer needs a monitor interface. utils.subprocess_quiet(('iw', 'dev', 'mon.%s' % interface, 'del')) subprocess.check_call(('ip', 'link', 'set', interface, 'down')) return True
def create_client_interface(interface, phy, suffix): """Creates a client interface. Args: interface: The name of the interface to create. phy: The phy on which to create the interface. suffix: The suffix of the AP interface on the same phy. Returns: Whether interface creation succeeded. """ utils.log('Creating client interface %s.', interface) try: utils.subprocess_quiet(('iw', 'phy', phy, 'interface', 'add', interface, 'type', 'station'), no_stdout=True) ap_mac_address = utils.get_mac_address_for_interface( find_interface_from_phy(phy, INTERFACE_TYPE.ap, suffix)) mac_address = utils.increment_mac_address(ap_mac_address) subprocess.check_call( ('ip', 'link', 'set', interface, 'address', mac_address)) except subprocess.CalledProcessError as e: utils.log('Creating client interface failed: %s', e)
def _wpa_cli(program, interface, command): return utils.subprocess_quiet((program, '-i', interface, command), no_stdout=True) == 0
def _is_hostapd_running(interface): return utils.subprocess_quiet(('hostapd_cli', '-i', interface, 'quit'), no_stdout=True) == 0
def does_interface_exist(interface): return utils.subprocess_quiet(('iw', interface, 'info'), no_stdout=True) == 0
def scan(interface, band, autotype, width): """Do an autochannel scan and return the recommended channel. Args: interface: The interface on which to scan. band: The band on which to scan. autotype: Determines permitted frequencies. See get_permitted_frequencies for valid values. width: Determines permitted frequencies. See get_permitted_frequencies for valid values. Returns: The channel to use, or None if no recommendation can be made. """ utils.log('Doing autochannel scan.') permitted_frequencies = get_permitted_frequencies(band, autotype, width) subprocess.call(('ip', 'link', 'set', interface, 'up')) # TODO(apenwarr): We really want to clear any old survey results first. But # there seems to be no iw command for that yet... # # TODO(apenwarr): This only scans each channel for 100ms. Ideally it should # scan for longer, to get a better activity sample. It would also be nice to # continue scanning in the background while hostapd is running, using 'iw # offchannel'. Retry this a few times if it fails, just in case there was a # scan already in progress started somewhere else (e.g. from waveguide). for _ in xrange(9): if utils.subprocess_quiet(('iw', 'dev', interface, 'scan', 'passive'), no_stdout=True) == 0: break time.sleep(0.5) # TODO(apenwarr): This algorithm doesn't deal with overlapping channels. Just # because channel 1 looks good doesn't mean we should use it; activity in # overlapping channels could destroy performance. In fact, overlapping # channel activity is much worse than activity on the main channel. Also, if # using 40 MHz or 80 MHz channel width, we should count activity in all the 20 # MHz sub-channels separately, and choose the least-active sub-channel as the # primary. best_frequency = best_noise = best_ratio = frequency = None for tokens in utils.subprocess_line_tokens( ('iw', 'dev', interface, 'survey', 'dump')): # TODO(apenwarr): Randomize the order of channels. Otherwise when channels # are all about equally good, we would always choose exactly the same # channel, which might be bad in the case of hidden nodes. if len(tokens) >= 2 and tokens[0] == 'frequency:': frequency = tokens[1] noise = active = busy = None elif len(tokens) >= 2 and tokens[0] == 'noise:': noise = int(tokens[1]) elif len(tokens) >= 4 and tokens[:3] == ('channel', 'active', 'time:'): active = int(tokens[3]) elif len(tokens) >= 4 and tokens[:3] == ('channel', 'receive', 'time:'): busy = int(tokens[3]) # TODO(rofrankel): busy or 1 might make more sense than busy + 1 here; # need to discuss with apenwarr@. ratio = (active + 1) * 1000 / (busy + 1) if frequency not in permitted_frequencies.split(): continue # Some radios support both bands, but we only want to match channels on # the band we have chosen. if band[0] != frequency[0]: continue utils.log('freq=%s ratio=%s noise=%s', frequency, ratio, noise) if best_noise is None or best_noise - 15 > noise or best_ratio < ratio: best_frequency, best_ratio, best_noise = frequency, ratio, noise if not best_frequency: utils.log('Autoscan did not find any channel, picking random channel.') utils.log('Permitted frequencies: %s', permitted_frequencies) if not permitted_frequencies: utils.log('No default channel: type=%s band=%s width=%s', autotype, band, width) return None best_frequency = random.choice(permitted_frequencies.split()) utils.log('autofreq=%s', best_frequency) for tokens in utils.subprocess_line_tokens(('iw', 'phy')): if len(tokens) >= 4 and tokens[2] == 'MHz': frequency = tokens[1] if frequency == best_frequency: channel = tokens[3].strip('[]') break if not channel: utils.log('No channel number matched freq=%s.', best_frequency) return None utils.log('autochannel=%s', channel) return channel