Merge "ginstall: Add support for Android images"
diff --git a/cmds/anonid.c b/cmds/anonid.c
index e7854ad..106f5fc 100644
--- a/cmds/anonid.c
+++ b/cmds/anonid.c
@@ -177,6 +177,7 @@
usage(argv[0]);
}
+ memset(anonid, 0, sizeof(anonid));
get_anonid_for_mac(addr, anonid);
printf("%s\n", anonid);
diff --git a/cmds/castcheck b/cmds/castcheck
index 70080ec..40c90cc 100755
--- a/cmds/castcheck
+++ b/cmds/castcheck
@@ -13,7 +13,7 @@
while IFS=";" read ip; do
cast_devices="$cast_devices $ip"
done<<EOT
-$($AVAHI -tpvlr _googlecast._tcp | grep "^=" | cut -d";" -f8 | sort)
+$(timeout 10 $AVAHI -tpvlr _googlecast._tcp | grep "^=" | cut -d";" -f8 | sort)
EOT
echo "Cast responses from:$cast_devices"
diff --git a/cmds/dialcheck.cc b/cmds/dialcheck.cc
index 17f8fbd..d5ea202 100644
--- a/cmds/dialcheck.cc
+++ b/cmds/dialcheck.cc
@@ -290,7 +290,7 @@
int s4, s6;
setlinebuf(stdout);
- alarm(30);
+ alarm(10);
while ((c = getopt(argc, argv, "t:")) != -1) {
switch(c) {
diff --git a/cmds/soft_rc.py b/cmds/soft_rc.py
index 78bf5bc..685a069 100755
--- a/cmds/soft_rc.py
+++ b/cmds/soft_rc.py
@@ -142,7 +142,8 @@
LOG_VERB = 3
LOG_ALL = 99
-SLEEP_BEFORE_RELEASE_TIME = 0.1 # secs
+SLEEP_BEFORE_RELEASE_TIME = 0.1 # secs
+SLEEP_BETWEEN_DIGITS_TIME = 0.25 # secs
optspec = """
soft_rc.py [options]
@@ -523,6 +524,7 @@
for d in token:
tok = "DIGIT_" + d
self.SendKeyCode(tok, keymap.get(tok))
+ time.sleep(SLEEP_BETWEEN_DIGITS_TIME)
self.SendKeyCode("OK", keymap.get("OK"))
# regular key
diff --git a/conman/Makefile b/conman/Makefile
index 0faf301..6126f84 100644
--- a/conman/Makefile
+++ b/conman/Makefile
@@ -15,7 +15,7 @@
%.test: %_test.py
echo ./$<
- PYTHONPATH=..:./test/fake_wpactrl:$(PYTHONPATH) ./$<
+ PYTHONPATH=..:./test/fake_python:$(PYTHONPATH) ./$<
runtests: \
$(patsubst %_test.py,%.test,$(wildcard *_test.py))
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index fb01acf..b4c3887 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -245,7 +245,8 @@
wpa_control_interface='/var/run/wpa_supplicant',
run_duration_s=1, interface_update_period=5,
wifi_scan_period_s=120, wlan_retry_s=120, associate_wait_s=15,
- dhcp_wait_s=10, acs_start_wait_s=20, acs_finish_wait_s=120,
+ dhcp_wait_s=10, acs_connection_check_wait_s=1,
+ acs_start_wait_s=20, acs_finish_wait_s=120,
bssid_cycle_length_s=30):
self._tmp_dir = tmp_dir
@@ -260,6 +261,7 @@
self._wlan_retry_s = wlan_retry_s
self._associate_wait_s = associate_wait_s
self._dhcp_wait_s = dhcp_wait_s
+ self._acs_connection_check_wait_s = acs_connection_check_wait_s
self._acs_start_wait_s = acs_start_wait_s
self._acs_finish_wait_s = acs_finish_wait_s
self._bssid_cycle_length_s = bssid_cycle_length_s
@@ -366,7 +368,10 @@
ratchet.Condition('trying_open', wifi.connected_to_open,
self._associate_wait_s,
callback=wifi.expire_connection_status_cache),
- ratchet.Condition('waiting_for_dhcp', wifi.gateway, self._dhcp_wait_s,
+ ratchet.Condition('waiting_for_dhcp', wifi.gateway,
+ self._dhcp_wait_s),
+ ratchet.Condition('acs_connection_check', wifi.acs,
+ self._acs_connection_check_wait_s,
callback=self.cwmp_wakeup),
ratchet.FileTouchedCondition('waiting_for_cwmp_wakeup',
os.path.join(CWMP_PATH, 'acscontact'),
@@ -445,16 +450,18 @@
1. Process any changes in watched files.
2. Check interfaces for changed connectivity, if
update_interfaces_and_routes is true.
- 3. Start, stop, or restart access points as appropriate. If running an
+ 3. Try to upload logs, if we just joined a new open network.
+ 4. Start, stop, or restart access points as appropriate. If running an
access point, skip all remaining wifi steps for that band.
- 3. Handle any wpa_supplicant events.
- 4. Periodically, perform a wifi scan.
- 5. If not connected to the WLAN or to the ACS, try to connect to something.
- 6. If connected to the ACS but not the WLAN, and enough time has passed
+ 5. Handle any wpa_supplicant events.
+ 6. Periodically, perform a wifi scan.
+ 7. If not connected to the WLAN or to the ACS, try to connect to something.
+ 8. If connected to the ACS but not the WLAN, and enough time has passed
since connecting that we should expect a current WLAN configuration, try
to join the WLAN again.
- 7. Sleep for the rest of the duration of _run_duration_s.
+ 9. Sleep for the rest of the duration of _run_duration_s.
"""
+
start_time = _gettime()
self.notifier.process_events()
while self.notifier.check_events():
@@ -466,13 +473,22 @@
self._interface_update_counter = 0
self._update_interfaces_and_routes()
+ if self.acs() and self._try_to_upload_logs:
+ self._try_upload_logs()
+ self._try_to_upload_logs = False
+
for wifi in self.wifi:
- continue_wifi = False
- provisioning_failed = self.provisioning_failed(wifi)
if self.currently_provisioning(wifi):
+ logging.debug('Currently provisioning, nothing else to do.')
continue
provisioning_failed = self.provisioning_failed(wifi)
+ if provisioning_failed and (
+ getattr(wifi, 'last_attempted_bss_info', None) ==
+ getattr(wifi, 'last_successful_bss_info', None)):
+ wifi.last_successful_bss_info = None
+
+ continue_wifi = False
# Only one wlan_configuration per interface will have access_point ==
# True. Try 5 GHz first, then 2.4 GHz. If both bands are supported by
@@ -550,9 +566,6 @@
wifi.status.connected_to_wlan = False
if self.acs():
logging.debug('Connected to ACS')
- if self._try_to_upload_logs:
- self._try_upload_logs()
- self._try_to_upload_logs = False
if wifi.acs():
wifi.last_successful_bss_info = getattr(wifi,
@@ -859,7 +872,7 @@
wifi.last_attempted_bss_info = bss_info
return subprocess.call(self.WIFI_SETCLIENT +
['--ssid', bss_info.ssid,
- '--band', wifi.bands[0],
+ '--band', bss_info.band,
'--bssid', bss_info.bssid]) == 0
def _connected_to_wlan(self, wifi):
@@ -877,6 +890,7 @@
band = wlan_configuration.band
current = self._wlan_configuration.get(band, None)
if current is None or wlan_configuration.command != current.command:
+ logging.debug('Received new WLAN configuration for band %s', band)
if current is not None:
wlan_configuration.access_point = current.access_point
else:
@@ -960,6 +974,7 @@
wifi.provisioning_ratchet.check()
if wifi.provisioning_ratchet.done_after:
wifi.status.provisioning_completed = True
+ wifi.provisioning_ratchet.stop()
logging.info('%s successfully provisioned', wifi.name)
return False
except ratchet.TimeoutException:
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index d7d0a40..22cf89f 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -5,8 +5,10 @@
import logging
import os
import shutil
+import subprocess # Fake subprocess module in test/fake_python.
import tempfile
import time
+import traceback
import connection_manager
import experiment_testutils
@@ -29,92 +31,26 @@
}
"""
-WIFI_SHOW_OUTPUT_MARVELL8897 = """Band: 2.4
-RegDomain: US
-Interface: wlan0 # 2.4 GHz ap
-Channel: 149
-BSSID: f4:f5:e8:81:1b:a0
-AutoChannel: True
-AutoType: NONDFS
-Station List for band: 2.4
-Client Interface: wcli0 # 2.4 GHz client
-Client BSSID: f4:f5:e8:81:1b:a1
-
-Band: 5
-RegDomain: US
-Interface: wlan0 # 5 GHz ap
-Channel: 149
-BSSID: f4:f5:e8:81:1b:a0
-AutoChannel: True
-AutoType: NONDFS
-Station List for band: 5
-
-Client Interface: wcli0 # 5 GHz client
-Client BSSID: f4:f5:e8:81:1b:a1
-"""
-
-WIFI_SHOW_OUTPUT_ATH9K_ATH10K = """Band: 2.4
-RegDomain: US
-Interface: wlan0 # 2.4 GHz ap
-Channel: 149
-BSSID: f4:f5:e8:81:1b:a0
-AutoChannel: True
-AutoType: NONDFS
-Station List for band: 2.4
-
-Client Interface: wcli0 # 2.4 GHz client
-Client BSSID: f4:f5:e8:81:1b:a1
-
-Band: 5
-RegDomain: US
-Interface: wlan1 # 5 GHz ap
-Channel: 149
-BSSID: f4:f5:e8:81:1b:a0
-AutoChannel: True
-AutoType: NONDFS
-Station List for band: 5
-
-Client Interface: wcli1 # 5 GHz client
-Client BSSID: f4:f5:e8:81:1b:a1
-"""
-
+WIFI_SHOW_OUTPUT_MARVELL8897 = (
+ subprocess.wifi.MockInterface(phynum='0', bands=['2.4', '5'],
+ driver='cfg80211'),
+)
+WIFI_SHOW_OUTPUT_ATH9K_ATH10K = (
+ subprocess.wifi.MockInterface(phynum='0', bands=['2.4'], driver='cfg80211'),
+ subprocess.wifi.MockInterface(phynum='1', bands=['5'], driver='cfg80211'),
+)
# See b/27328894.
-WIFI_SHOW_OUTPUT_MARVELL8897_NO_5GHZ = """Band: 2.4
-RegDomain: 00
-Interface: wlan0 # 2.4 GHz ap
-BSSID: 00:50:43:02:fe:01
-AutoChannel: False
-Station List for band: 2.4
-
-Client Interface: wcli0 # 2.4 GHz client
-Client BSSID: 00:50:43:02:fe:02
-
-Band: 5
-RegDomain: 00
-"""
-
-WIFI_SHOW_OUTPUT_ATH9K_FRENZY = """Band: 2.4
-RegDomain: US
-Interface: wlan0 # 2.4 GHz ap
-Channel: 149
-BSSID: f4:f5:e8:81:1b:a0
-AutoChannel: True
-AutoType: NONDFS
-Station List for band: 2.4
-
-Client Interface: wcli0 # 2.4 GHz client
-Client BSSID: f4:f5:e8:81:1b:a1
-
-Band: 5
-RegDomain: 00
-"""
-
-WIFI_SHOW_OUTPUT_FRENZY = """Band: 2.4
-RegDomain: 00
-Band: 5
-RegDomain: 00
-"""
+WIFI_SHOW_OUTPUT_MARVELL8897_NO_5GHZ = (
+ subprocess.wifi.MockInterface(phynum='0', bands=['2.4'], driver='cfg80211'),
+)
+WIFI_SHOW_OUTPUT_ATH9K_FRENZY = (
+ subprocess.wifi.MockInterface(phynum='0', bands=['2.4'], driver='cfg80211'),
+ subprocess.wifi.MockInterface(phynum='1', bands=['5'], driver='frenzy'),
+)
+WIFI_SHOW_OUTPUT_FRENZY = (
+ subprocess.wifi.MockInterface(phynum='0', bands=['5'], driver='frenzy'),
+)
IW_SCAN_DEFAULT_OUTPUT = """BSS 00:11:22:33:44:55(on wcli0)
SSID: s1
@@ -133,22 +69,13 @@
@wvtest.wvtest
def get_client_interfaces_test():
"""Test get_client_interfaces."""
- wifi_show = None
- quantenna_interfaces = None
+ subprocess.reset()
- # pylint: disable=protected-access
- old_wifi_show = connection_manager._wifi_show
- old_get_quantenna_interfaces = connection_manager._get_quantenna_interfaces
- connection_manager._wifi_show = lambda: wifi_show
- connection_manager._get_quantenna_interfaces = lambda: quantenna_interfaces
-
- wifi_show = WIFI_SHOW_OUTPUT_MARVELL8897
- quantenna_interfaces = []
+ subprocess.mock('wifi', 'interfaces', *WIFI_SHOW_OUTPUT_MARVELL8897)
wvtest.WVPASSEQ(connection_manager.get_client_interfaces(),
{'wcli0': {'bands': set(['2.4', '5'])}})
- wifi_show = WIFI_SHOW_OUTPUT_ATH9K_ATH10K
- quantenna_interfaces = []
+ subprocess.mock('wifi', 'interfaces', *WIFI_SHOW_OUTPUT_ATH9K_ATH10K)
wvtest.WVPASSEQ(connection_manager.get_client_interfaces(), {
'wcli0': {'bands': set(['2.4'])},
'wcli1': {'bands': set(['5'])}
@@ -156,113 +83,30 @@
# Test Quantenna devices.
- # 2.4 GHz cfg80211 radio + 5 GHz Frenzy (Optimus Prime).
- wifi_show = WIFI_SHOW_OUTPUT_ATH9K_FRENZY
- quantenna_interfaces = ['wlan1', 'wlan1_portal', 'wcli1']
+ # 2.4 GHz cfg80211 radio + 5 GHz Frenzy (e.g. Optimus Prime).
+ subprocess.mock('wifi', 'interfaces', *WIFI_SHOW_OUTPUT_ATH9K_FRENZY)
wvtest.WVPASSEQ(connection_manager.get_client_interfaces(), {
'wcli0': {'bands': set(['2.4'])},
'wcli1': {'frenzy': True, 'bands': set(['5'])}
})
# Only Frenzy (e.g. Lockdown).
- wifi_show = WIFI_SHOW_OUTPUT_FRENZY
- quantenna_interfaces = ['wlan0', 'wlan0_portal', 'wcli0']
+ subprocess.mock('wifi', 'interfaces', *WIFI_SHOW_OUTPUT_FRENZY)
wvtest.WVPASSEQ(connection_manager.get_client_interfaces(),
{'wcli0': {'frenzy': True, 'bands': set(['5'])}})
- connection_manager._wifi_show = old_wifi_show
- connection_manager._get_quantenna_interfaces = old_get_quantenna_interfaces
-
-
-class WLANConfiguration(connection_manager.WLANConfiguration):
- """WLANConfiguration subclass for testing."""
-
- WIFI_STOPAP = ['echo', 'stopap']
- WIFI_SETCLIENT = ['echo', 'setclient']
- WIFI_STOPCLIENT = ['echo', 'stopclient']
-
- def __init__(self, *args, **kwargs):
- super(WLANConfiguration, self).__init__(*args, **kwargs)
- self.stale = False
-
- def _actually_start_client(self):
- self.client_was_up = self.client_up
- self.was_attached = self.wifi.attached()
- self.wifi._secure_testonly = True
-
- super(WLANConfiguration, self)._actually_start_client()
-
- if not self.client_was_up and not self.was_attached:
- self.wifi._initial_ssid_testonly = self.ssid
- self.wifi.start_wpa_supplicant_testonly(self._wpa_control_interface)
-
- if self.wifi._wpa_control:
- self.wifi._wpa_control.connected = not self.stale
- return not self.stale
-
- def _post_start_client(self):
- if not self.client_was_up:
- self.wifi.set_connection_check_result('succeed')
-
- if self.was_attached:
- self.wifi._wpa_control.ssid_testonly = self.ssid
- self.wifi._wpa_control.secure_testonly = True
- self.wifi.add_connected_event()
-
- # Normally, wpa_supplicant would bring up the client interface, which
- # would trigger ifplugd, which would run ifplugd.action, which would do
- # two things:
- #
- # 1) Write an interface status file.
- # 2) Call run-dhclient, which would call dhclient-script, which would
- # call ipapply, which would write gateway and subnet files.
- #
- # Fake both of these things instead.
- self.write_interface_status_file('1')
- self.write_gateway_file()
- self.write_subnet_file()
-
- def stop_client(self):
- client_was_up = self.client_up
-
- super(WLANConfiguration, self).stop_client()
-
- if client_was_up:
- self.wifi.add_terminating_event()
- self.wifi.set_connection_check_result('fail')
-
- # See comments in start_client.
- self.write_interface_status_file('0')
-
- def write_gateway_file(self):
- gateway_file = os.path.join(self.tmp_dir,
- self.gateway_file_prefix + self.wifi.name)
- with open(gateway_file, 'w') as f:
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write('192.168.1.1')
-
- def write_subnet_file(self):
- subnet_file = os.path.join(self.tmp_dir,
- self.subnet_file_prefix + self.wifi.name)
- with open(subnet_file, 'w') as f:
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write('192.168.1.0/24')
-
- def write_interface_status_file(self, value):
- status_file = os.path.join(self.interface_status_dir, self.wifi.name)
- with open(status_file, 'w') as f:
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write(value)
-
@wvtest.wvtest
def WLANConfigurationParseTest(): # pylint: disable=invalid-name
"""Test WLANConfiguration parsing."""
+ subprocess.reset()
+
cmd = '\n'.join([
'WIFI_PSK=abcdWIFI_PSK=qwer', 'wifi', 'set', '-P', '-b', '5',
'--bridge=br0', '-s', 'my ssid=1', '--interface-suffix', '_suffix',
])
- config = WLANConfiguration('5', interface_test.Wifi('wcli0', 20), cmd, None)
+ config = connection_manager.WLANConfiguration(
+ '5', interface_test.Wifi('wcli0', 20), cmd, None)
wvtest.WVPASSEQ('my ssid=1', config.ssid)
wvtest.WVPASSEQ('abcdWIFI_PSK=qwer', config.passphrase)
@@ -290,150 +134,41 @@
Bridge = interface_test.Bridge
Wifi = Wifi
FrenzyWifi = FrenzyWifi
- WLANConfiguration = WLANConfiguration
-
- WIFI_SETCLIENT = ['echo', 'setclient']
- IFUP = ['echo', 'ifup']
- IFPLUGD_ACTION = ['echo', 'ifplugd.action']
- BINWIFI = ['echo', 'wifi']
- UPLOAD_LOGS_AND_WAIT = ['echo', 'upload-logs-and-wait']
- CWMP_WAKEUP = ['echo', 'cwmp', 'wakeup']
def __init__(self, *args, **kwargs):
self._binwifi_commands = []
- self.interfaces_already_up = kwargs.pop('__test_interfaces_already_up',
- ['eth0'])
+ for interface_name in kwargs.pop('__test_interfaces_already_up', ['eth0']):
+ subprocess.call(['ifup', interface_name])
+ if interface_name.startswith('w'):
+ phynum = interface_name[-1]
+ for band, interface in subprocess.wifi.INTERFACE_FOR_BAND.iteritems():
+ if interface.phynum == phynum:
+ break
+ else:
+ raise ValueError('Could not find matching interface for '
+ '__test_interfaces_already_up')
+ ssid = 'my ssid'
+ psk = 'passphrase'
+ # If band is undefined then a ValueError will be raised above. pylint
+ # isn't smart enough to figure that out.
+ # pylint: disable=undefined-loop-variable
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk, write_now=True)
+ subprocess.mock('wifi', 'remote_ap', band=band, ssid=ssid, psk=psk,
+ bssid='00:00:00:00:00:00')
- self.wifi_interfaces_already_up = [ifc for ifc in self.interfaces_already_up
- if ifc.startswith('w')]
- for wifi in self.wifi_interfaces_already_up:
- # wcli1 is always 5 GHz. wcli0 always *includes* 2.4. wlan* client
- # interfaces are Frenzy interfaces and therefore 5 GHz-only.
- band = '5' if wifi in ('wlan0', 'wlan1', 'wcli1') else '2.4'
- # This will happen in the super function, but in order for
- # write_wlan_config to work we have to do it now. This has to happen
- # before the super function so that the files exist before the inotify
- # registration.
- self._config_dir = kwargs['config_dir']
- self.write_wlan_config(band, 'my ssid', 'passphrase')
-
- # Also create the wpa_supplicant socket to which to attach.
- open(os.path.join(kwargs['wpa_control_interface'], wifi), 'w')
+ # Also create the wpa_supplicant socket to which to attach.
+ open(os.path.join(kwargs['wpa_control_interface'], interface_name),
+ 'w')
super(ConnectionManager, self).__init__(*args, **kwargs)
- self.interface_with_scan_results = None
- self.scan_results_include_hidden = False
- # Should we be able to connect to open network s2?
- self.can_connect_to_s2 = True
- self.can_connect_to_s3 = True
- # Will s2 fail rather than providing ACS access?
- self.s2_fail = False
- # Will s3 fail to acquire a DHCP lease?
- self.dhcp_failure_on_s3 = False
- self.log_upload_count = 0
- self.acs_session_fails = False
- for wifi in self.wifi:
- wifi.bssids_tried_testonly = 0
-
- def create_wifi_interfaces(self):
- super(ConnectionManager, self).create_wifi_interfaces()
- for wifi in self.wifi_interfaces_already_up:
- # pylint: disable=protected-access
- self.interface_by_name(wifi)._initial_ssid_testonly = 'my ssid'
- self.interface_by_name(wifi)._secure_testonly = True
-
- @property
- def IP_LINK(self):
- return ['echo'] + ['%s LOWER_UP' % ifc
- for ifc in self.interfaces_already_up]
-
- def _update_access_point(self, wlan_configuration):
- client_was_up = wlan_configuration.client_up
- super(ConnectionManager, self)._update_access_point(wlan_configuration)
- if wlan_configuration.access_point_up:
- if client_was_up:
- wifi = self.wifi_for_band(wlan_configuration.band)
- wifi.add_terminating_event()
-
- def _try_bssid(self, wifi, bss_info):
- if wifi.wpa_status().get('wpa_state', None) == 'COMPLETED':
- wifi.add_disconnected_event()
- self.last_provisioning_attempt = bss_info
-
- super(ConnectionManager, self)._try_bssid(wifi, bss_info)
-
- wifi.bssids_tried_testonly += 1
-
- def connect(connection_check_result, dhcp_failure=False):
- # pylint: disable=protected-access
- if wifi.attached():
- wifi._wpa_control.ssid_testonly = bss_info.ssid
- wifi._wpa_control.secure_testonly = False
- wifi.add_connected_event()
- else:
- wifi._initial_ssid_testonly = bss_info.ssid
- wifi._secure_testonly = False
- wifi.start_wpa_supplicant_testonly(self._wpa_control_interface)
- wifi.set_connection_check_result(connection_check_result)
- self.ifplugd_action(wifi.name, True, dhcp_failure)
-
- if bss_info and bss_info.ssid == 's1':
- connect('fail')
- return True
-
- if bss_info and bss_info.ssid == 's2' and self.can_connect_to_s2:
- connect('fail' if self.s2_fail else 'succeed')
- return True
-
- if bss_info and bss_info.ssid == 's3' and self.can_connect_to_s3:
- connect('restricted', self.dhcp_failure_on_s3)
- return True
-
- return False
-
- # pylint: disable=unused-argument,protected-access
- def _find_bssids(self, band):
- scan_output = ''
- if (self.interface_with_scan_results and
- band in self.interface_by_name(self.interface_with_scan_results).bands):
- scan_output = IW_SCAN_DEFAULT_OUTPUT
- if self.scan_results_include_hidden:
- scan_output += IW_SCAN_HIDDEN_OUTPUT
- iw._scan = lambda interface: scan_output
- return super(ConnectionManager, self)._find_bssids(band)
-
- def _update_wlan_configuration(self, wlan_configuration):
- wlan_configuration.command.insert(0, 'echo')
- wlan_configuration._wpa_control_interface = self._wpa_control_interface
- wlan_configuration.tmp_dir = self._tmp_dir
- wlan_configuration.interface_status_dir = self._interface_status_dir
- wlan_configuration.gateway_file_prefix = self.GATEWAY_FILE_PREFIX
- wlan_configuration.subnet_file_prefix = self.SUBNET_FILE_PREFIX
-
- super(ConnectionManager, self)._update_wlan_configuration(
- wlan_configuration)
-
# Just looking for last_wifi_scan_time to change doesn't work because the
# tests run too fast.
def _wifi_scan(self, wifi):
super(ConnectionManager, self)._wifi_scan(wifi)
wifi.wifi_scan_counter += 1
- def ifplugd_action(self, interface_name, up, dhcp_failure=False):
- # Typically, when moca comes up, conman calls ifplugd.action, which writes
- # this file. Also, when conman starts, it calls ifplugd.action for eth0.
- self.write_interface_status_file(interface_name, '1' if up else '0')
-
- # ifplugd calls run-dhclient, which results in a gateway file if the link is
- # up (and working).
- if up and not dhcp_failure:
- self.write_gateway_file('br0' if interface_name in ('eth0', 'moca0')
- else interface_name)
- self.write_subnet_file('br0' if interface_name in ('eth0', 'moca0')
- else interface_name)
-
def _binwifi(self, *command):
super(ConnectionManager, self)._binwifi(*command)
self._binwifi_commands.append(command)
@@ -452,50 +187,7 @@
return self._wlan_configuration[band].client_up
- def _try_upload_logs(self):
- self.log_upload_count += 1
- return super(ConnectionManager, self)._try_upload_logs()
-
- # Test methods
-
- def tried_to_upload_logs(self):
- result = getattr(self, 'last_log_upload_count', 0) < self.log_upload_count
- self.last_log_upload_count = self.log_upload_count
- return result
-
- def delete_wlan_config(self, band):
- delete_wlan_config(self._config_dir, band)
-
- def write_wlan_config(self, *args, **kwargs):
- write_wlan_config(self._config_dir, *args, **kwargs)
-
- def enable_access_point(self, band):
- enable_access_point(self._config_dir, band)
-
- def disable_access_point(self, band):
- disable_access_point(self._config_dir, band)
-
- def write_gateway_file(self, interface_name):
- gateway_file = os.path.join(self._tmp_dir,
- self.GATEWAY_FILE_PREFIX + interface_name)
- with open(gateway_file, 'w') as f:
- logging.debug('Writing gateway file %s', gateway_file)
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write('192.168.1.1')
-
- def write_subnet_file(self, interface_name):
- subnet_file = os.path.join(self._tmp_dir,
- self.SUBNET_FILE_PREFIX + interface_name)
- with open(subnet_file, 'w') as f:
- logging.debug('Writing subnet file %s', subnet_file)
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write('192.168.1.0/24')
-
- def write_interface_status_file(self, interface_name, value):
- status_file = os.path.join(self._interface_status_dir, interface_name)
- with open(status_file, 'w') as f:
- # This value doesn't matter to conman, so it's fine to hard code it here.
- f.write(value)
+ # # Test methods
def set_ethernet(self, up):
self.ifplugd_action('eth0', up)
@@ -514,6 +206,7 @@
self.run_once()
def run_until_scan(self, band):
+ logging.debug('running until scan on band %r', band)
wifi = self.wifi_for_band(band)
wifi_scan_counter = wifi.wifi_scan_counter
while wifi_scan_counter == wifi.wifi_scan_counter:
@@ -529,58 +222,12 @@
def has_status_files(self, files):
return not set(files) - set(os.listdir(self._status_dir))
- def cwmp_wakeup(self):
- super(ConnectionManager, self).cwmp_wakeup()
- self.write_acscontact()
- if self.acs():
- self.write_acsconnected()
-
- def write_acscontact(self):
- open(os.path.join(connection_manager.CWMP_PATH, 'acscontact'), 'w')
-
- def write_acsconnected(self):
- if not self.acs_session_fails:
- open(os.path.join(connection_manager.CWMP_PATH, 'acsconnected'), 'w')
-
-
-def wlan_config_filename(path, band):
- return os.path.join(path, 'command.%s' % band)
-
-
-def access_point_filename(path, band):
- return os.path.join(path, 'access_point.%s' % band)
-
-
-def write_wlan_config(path, band, ssid, psk, atomic=False):
- final_filename = wlan_config_filename(path, band)
- filename = final_filename + ('.tmp' if atomic else '')
- with open(filename, 'w') as f:
- f.write('\n'.join(['env', 'WIFI_PSK=%s' % psk,
- 'wifi', 'set', '-b', band, '--ssid', ssid]))
- if atomic:
- os.rename(filename, final_filename)
-
-
-def delete_wlan_config(path, band):
- os.unlink(wlan_config_filename(path, band))
-
-
-def enable_access_point(path, band):
- open(access_point_filename(path, band), 'w')
-
-
-def disable_access_point(path, band):
- ap_filename = access_point_filename(path, band)
- if os.path.isfile(ap_filename):
- os.unlink(ap_filename)
-
def check_tmp_hosts(expected_contents):
wvtest.WVPASSEQ(open(connection_manager.TMP_HOSTS).read(), expected_contents)
-def connection_manager_test(radio_config, wlan_configs=None,
- quantenna_interfaces=None, **cm_kwargs):
+def connection_manager_test(radio_config, wlan_configs=None, **cm_kwargs):
"""Returns a decorator that does ConnectionManager test boilerplate."""
if wlan_configs is None:
wlan_configs = {}
@@ -589,38 +236,39 @@
"""The actual decorator."""
def actual_test():
"""The actual test function."""
+ subprocess.reset()
+
run_duration_s = .01
interface_update_period = 5
wifi_scan_period = 15
wifi_scan_period_s = run_duration_s * wifi_scan_period
associate_wait_s = 0
dhcp_wait_s = .5
+ acs_cc_wait_s = 0
acs_start_wait_s = 0
- acs_finish_wait_s = 0
+ acs_finish_wait_s = 0.25
- # pylint: disable=protected-access
- old_wifi_show = connection_manager._wifi_show
- connection_manager._wifi_show = lambda: radio_config
-
- old_gqi = connection_manager._get_quantenna_interfaces
- connection_manager._get_quantenna_interfaces = (
- lambda: quantenna_interfaces or [])
+ subprocess.mock('wifi', 'interfaces', *radio_config)
try:
# No initial state.
connection_manager.TMP_HOSTS = tempfile.mktemp()
tmp_dir = tempfile.mkdtemp()
config_dir = tempfile.mkdtemp()
- os.mkdir(os.path.join(tmp_dir, 'interfaces'))
+ interfaces_dir = os.path.join(tmp_dir, 'interfaces')
+ if not os.path.exists(interfaces_dir):
+ os.mkdir(interfaces_dir)
moca_tmp_dir = tempfile.mkdtemp()
wpa_control_interface = tempfile.mkdtemp()
+ subprocess.mock('wifi', 'wpa_path', wpa_control_interface)
FrenzyWifi.WPACtrl.WIFIINFO_PATH = tempfile.mkdtemp()
connection_manager.CWMP_PATH = tempfile.mkdtemp()
+ subprocess.set_conman_paths(tmp_dir, config_dir,
+ connection_manager.CWMP_PATH)
for band, access_point in wlan_configs.iteritems():
- write_wlan_config(config_dir, band, 'initial ssid', 'initial psk')
- if access_point:
- open(os.path.join(config_dir, 'access_point.%s' % band), 'w')
+ subprocess.mock('cwmp', band, ssid='initial ssid', psk='initial psk',
+ access_point=access_point, write_now=True)
# Test that missing directories are created by ConnectionManager.
shutil.rmtree(tmp_dir)
@@ -635,12 +283,17 @@
wifi_scan_period_s=wifi_scan_period_s,
associate_wait_s=associate_wait_s,
dhcp_wait_s=dhcp_wait_s,
+ acs_connection_check_wait_s=acs_cc_wait_s,
acs_start_wait_s=acs_start_wait_s,
acs_finish_wait_s=acs_finish_wait_s,
bssid_cycle_length_s=1,
**cm_kwargs)
f(c)
+ except Exception:
+ logging.error('Uncaught exception!')
+ traceback.print_exc()
+ raise
finally:
if os.path.exists(connection_manager.TMP_HOSTS):
os.unlink(connection_manager.TMP_HOSTS)
@@ -650,9 +303,6 @@
shutil.rmtree(wpa_control_interface)
shutil.rmtree(FrenzyWifi.WPACtrl.WIFIINFO_PATH)
shutil.rmtree(connection_manager.CWMP_PATH)
- # pylint: disable=protected-access
- connection_manager._wifi_show = old_wifi_show
- connection_manager._get_quantenna_interfaces = old_gqi
actual_test.func_name = f.func_name
return actual_test
@@ -660,6 +310,20 @@
return inner
+def _enable_basic_scan_results(band):
+ for bssid, ssid, ccr in (('00:11:22:33:44:55', 's1', 'fail'),
+ ('66:77:88:99:aa:bb', 's1', 'fail'),
+ ('01:23:45:67:89:ab', 's2', 'succeed')):
+ subprocess.mock('wifi', 'remote_ap', bssid=bssid, ssid=ssid,
+ band=band, security=None, connection_check_result=ccr)
+
+
+def _disable_basic_scan_results(band):
+ for bssid in (('00:11:22:33:44:55'), ('66:77:88:99:aa:bb'),
+ ('01:23:45:67:89:ab')):
+ subprocess.mock('wifi', 'remote_ap_remove', bssid=bssid, band=band)
+
+
def connection_manager_test_generic(c, band):
"""Test ConnectionManager for things independent of radio configuration.
@@ -754,16 +418,28 @@
check_tmp_hosts('127.0.0.1 localhost')
# Now there are some scan results.
- c.interface_with_scan_results = c.wifi_for_band(band).name
- # Wait for a scan, plus 3 cycles, so that s2 will have been tried.
+ _enable_basic_scan_results(band)
+
+ # Create a WLAN configuration which should eventually be connected to.
+ ssid = 'wlan'
+ psk = 'password'
+ subprocess.mock('wifi', 'remote_ap',
+ bssid='11:22:33:44:55:66',
+ ssid=ssid, psk=psk, band=band, security='WPA2')
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk, access_point=False)
+
+ wvtest.WVFAIL(subprocess.upload_logs_and_wait.uploaded_logs())
+ # Wait for a scan, then until s2 is tried.
c.run_until_scan(band)
- wvtest.WVPASSEQ(c.log_upload_count, 0)
- c.wifi_for_band(band).ip_testonly = '192.168.1.100'
+ subprocess.call(['ip', 'addr', 'add', '192.168.1.100',
+ 'dev', c.wifi_for_band(band).name])
for _ in range(len(c.wifi_for_band(band).cycler)):
c.run_once()
wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ if last_bss_info.ssid == 's2':
+ break
- last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
wvtest.WVPASSEQ(last_bss_info.ssid, 's2')
wvtest.WVPASSEQ(last_bss_info.bssid, '01:23:45:67:89:ab')
@@ -773,17 +449,10 @@
wvtest.WVPASS(c.internet())
wvtest.WVFAIL(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_routes())
- wvtest.WVPASS(c.tried_to_upload_logs())
- # Disable scan results again.
- c.interface_with_scan_results = None
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
c.run_until_interface_update()
check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
- # Now, create a WLAN configuration which should be connected to.
- ssid = 'wlan'
- psk = 'password'
- c.write_wlan_config(band, ssid, psk)
- c.disable_access_point(band)
c.run_once()
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_routes())
@@ -792,7 +461,7 @@
# Kill wpa_supplicant. conman should restart it.
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c._connected_to_wlan(c.wifi_for_band(band)))
- c.wifi_for_band(band).kill_wpa_supplicant_testonly(c._wpa_control_interface)
+ subprocess.mock('wifi', 'kill_wpa_supplicant', band)
wvtest.WVFAIL(c.client_up(band))
wvtest.WVFAIL(c._connected_to_wlan(c.wifi_for_band(band)))
# Make sure we stay connected to s2, rather than disconnecting and
@@ -806,18 +475,25 @@
# The AP restarts with a new configuration, kicking us off. We should
# reprovision.
- c._wlan_configuration[band].stale = True
- c.wifi_for_band(band).add_disconnected_event()
+ ssid = 'wlan2'
+ psk = 'password2'
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk)
+ subprocess.mock('wifi', 'remote_ap',
+ bssid='11:22:33:44:55:66',
+ ssid=ssid, psk=psk, band=band, security='WPA2')
+ subprocess.mock('wifi', 'disconnected_event', band)
c.run_once()
wvtest.WVFAIL(c.client_up(band))
wvtest.WVPASS(c._connected_to_open(c.wifi_for_band(band)))
- wvtest.WVPASSEQ(c.last_provisioning_attempt.ssid, 's2')
+ wvtest.WVPASSEQ(c.wifi_for_band(band).last_attempted_bss_info.ssid, 's2')
- # Now that we're on the provisioning network, create the new WLAN
- # configuration, which should be connected to.
- ssid = 'wlan2'
- psk = 'password2'
- c.write_wlan_config(band, ssid, psk)
+ # Overwrites previous one due to same BSSID.
+ subprocess.mock('wifi', 'remote_ap',
+ bssid='11:22:33:44:55:66',
+ ssid=ssid, psk=psk, band=band, security='WPA2')
+ # Run once for cwmp wakeup to get called, then once more for the new config to
+ # be received.
+ c.run_once()
c.run_once()
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).wpa_status()['ssid'] == ssid)
@@ -829,23 +505,26 @@
# add the user's WLAN to the scan results, and scan again. This time, the
# first SSID tried should be 's3', which is now present in the scan results
# (with its SSID hidden, but included via vendor IE).
- c.delete_wlan_config(band)
- c.can_connect_to_s2 = False
- c.interface_with_scan_results = c.wifi_for_band(band).name
- c.scan_results_include_hidden = True
- c.run_until_interface_update_and_scan(band)
- wvtest.WVFAIL(c.has_status_files([status.P.CONNECTED_TO_WLAN]))
- c.run_until_interface_update()
- wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
- wvtest.WVPASSEQ(c.last_provisioning_attempt.ssid, 's3')
- wvtest.WVPASSEQ(c.last_provisioning_attempt.bssid, 'ff:ee:dd:cc:bb:aa')
- # The log upload happens on the next main loop after joining s3.
- c.run_once()
- wvtest.WVPASS(c.tried_to_upload_logs())
+ subprocess.mock('cwmp', band, delete_config=True, write_now=True)
+ del c.wifi_for_band(band).cycler
+ _enable_basic_scan_results(band)
+ # Remove s2.
+ subprocess.mock('wifi', 'remote_ap_remove',
+ bssid='01:23:45:67:89:ab', band=band)
+ # Create s3.
+ subprocess.mock('wifi', 'remote_ap', bssid='ff:ee:dd:cc:bb:aa', ssid='s3',
+ band=band, security=None, hidden=True,
+ vendor_ies=(('f4:f5:e8', '01'), ('f4:f5:e8', '03 73 33')))
+ #### Now, recreate the same WLAN configuration, which should be connected to.
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk)
- # Now, recreate the same WLAN configuration, which should be connected to.
- # Also, test that atomic writes/renames work.
- c.write_wlan_config(band, ssid, psk, atomic=True)
+ c.run_until_interface_update_and_scan(band)
+ c.run_until_interface_update()
+ wvtest.WVPASSEQ(c.wifi_for_band(band).last_attempted_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(c.wifi_for_band(band).last_attempted_bss_info.bssid,
+ 'ff:ee:dd:cc:bb:aa')
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
+
c.run_once()
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_routes())
@@ -853,7 +532,7 @@
# Now enable the AP. Since we have no wired connection, this should have no
# effect.
- c.enable_access_point(band)
+ subprocess.mock('cwmp', band, access_point=True, write_now=True)
c.run_once()
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_routes())
@@ -865,7 +544,7 @@
# an AP.
c.set_ethernet(True)
c.bridge.set_connection_check_result('succeed')
- c.bridge.ip_testonly = '192.168.1.101'
+ subprocess.call(['ip', 'addr', 'add', '192.168.1.101', 'dev', c.bridge.name])
c.run_until_interface_update()
wvtest.WVPASS(c.access_point_up(band))
wvtest.WVFAIL(c.client_up(band))
@@ -876,7 +555,7 @@
# Now move (rather than delete) the configuration file. The AP should go
# away, and we should not be able to join the WLAN. Routes should not be
# affected.
- filename = wlan_config_filename(c._config_dir, band)
+ filename = subprocess.cwmp.wlan_config_filename(band)
other_filename = filename + '.bak'
os.rename(filename, other_filename)
c.run_once()
@@ -896,7 +575,7 @@
# Now delete the config and bring down the bridge and make sure we reprovision
# via the last working BSS.
- c.delete_wlan_config(band)
+ subprocess.mock('cwmp', band, delete_config=True, write_now=True)
c.bridge.set_connection_check_result('fail')
scan_count_for_band = c.wifi_for_band(band).wifi_scan_counter
c.run_until_interface_update()
@@ -907,6 +586,7 @@
check_tmp_hosts('192.168.1.101 %s\n127.0.0.1 localhost' % hostname)
# s3 is not what the cycler would suggest trying next.
wvtest.WVPASSNE('s3', c.wifi_for_band(band).cycler.peek())
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk)
# Run only once, so that only one BSS can be tried. It should be the s3 one,
# since that worked previously.
c.run_once()
@@ -914,18 +594,23 @@
# Make sure we didn't scan on `band`.
wvtest.WVPASSEQ(scan_count_for_band, c.wifi_for_band(band).wifi_scan_counter)
c.run_once()
- wvtest.WVPASS(c.tried_to_upload_logs())
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
- # Now re-create the WLAN config, connect to the WLAN, and make sure that s3 is
- # unset as last_successful_bss_info, since it is no longer available.
- c.write_wlan_config(band, ssid, psk)
+ # Now wait for the WLAN config to be created, connect to the WLAN, and make
+ # sure that s3 is unset as last_successful_bss_info, since it is no longer
+ # available.
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- c.can_connect_to_s3 = False
- c.scan_results_include_hidden = False
- c.delete_wlan_config(band)
+ # Remove s3.
+ subprocess.mock('wifi', 'remote_ap_remove',
+ bssid='ff:ee:dd:cc:bb:aa', band=band)
+ # Bring s2 back.
+ subprocess.mock('wifi', 'remote_ap', bssid='01:23:45:67:89:ab', ssid='s2',
+ band=band, security=None)
+
+ subprocess.mock('cwmp', band, delete_config=True, write_now=True)
c.run_once()
wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, None)
@@ -939,7 +624,7 @@
# disconnecting.
# 4) Connect to s2 but get no ACS access; see that last_successful_bss_info is
# unset.
- c.write_wlan_config(band, ssid, psk)
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk, write_now=True)
# Connect
c.run_once()
# Process DHCP results
@@ -947,31 +632,37 @@
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- c.delete_wlan_config(band)
+ subprocess.mock('cwmp', band, delete_config=True, write_now=True)
c.run_once()
wvtest.WVFAIL(c.wifi_for_band(band).acs())
- c.can_connect_to_s2 = True
# Give it time to try all BSSIDs. This means sleeping long enough that
# everything in the cycler is active, then doing n+1 loops (the n+1st loop is
# when we decide that the SSID in the nth loop was successful).
time.sleep(c._bssid_cycle_length_s)
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk)
for _ in range(len(c.wifi_for_band(band).cycler) + 1):
c.run_once()
- s2_bss = iw.BssInfo('01:23:45:67:89:ab', 's2')
+ s2_bss = iw.BssInfo(bssid='01:23:45:67:89:ab', ssid='s2', band=band)
wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, s2_bss)
c.run_once()
- wvtest.WVPASS(c.tried_to_upload_logs())
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
- c.s2_fail = True
- c.write_wlan_config(band, ssid, psk)
+ # Make s2's connection check fail.
+ subprocess.mock('wifi', 'remote_ap', bssid='01:23:45:67:89:ab', ssid='s2',
+ band=band, security=None, connection_check_result='fail')
+
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
-
wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, s2_bss)
- c._wlan_configuration[band].stale = True
- c.wifi_for_band(band).add_disconnected_event()
+ # Disconnect.
+ ssid = 'wlan3'
+ psk = 'password3'
+ subprocess.mock('wifi', 'remote_ap',
+ bssid='11:22:33:44:55:66',
+ ssid=ssid, psk=psk, band=band, security='WPA2')
+ subprocess.mock('wifi', 'disconnected_event', band)
# Run once so that c will reconnect to s2.
c.run_once()
wvtest.WVPASS(c.wifi_for_band(band).connected_to_open())
@@ -986,16 +677,18 @@
# which lets us force a timeout and proceed to the next AP. Having a stale
# WLAN configuration shouldn't interrupt provisioning.
del c.wifi_for_band(band).cycler
- c.interface_with_scan_results = c.wifi_for_band(band).name
- c.scan_results_include_hidden = True
- c.can_connect_to_s3 = True
- c.dhcp_failure_on_s3 = True
+ subprocess.mock('wifi', 'remote_ap', bssid='ff:ee:dd:cc:bb:aa', ssid='s3',
+ band=band, security=None, hidden=True,
+ vendor_ies=(('f4:f5:e8', '01'), ('f4:f5:e8', '03 73 33')))
+ subprocess.mock('run-dhclient', c.wifi_for_band(band).name, failure=True)
+
# First iteration: check that we try s3.
c.run_until_scan(band)
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
- c.write_wlan_config(band, ssid, psk)
+ # Attempt to interrupt provisioning, make sure it doesn't work.
+ c._try_wlan_after[band] = 0
# Second iteration: check that we try s3 again since there's no gateway yet.
c.run_once()
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
@@ -1011,7 +704,8 @@
wvtest.WVPASSNE(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
# We can delete the stale WLAN config now, to simplify subsequent tests.
- c.delete_wlan_config(band)
+ # c.delete_wlan_config(band)
+ subprocess.mock('cwmp', band, delete_config=True, write_now=True)
# Now repeat the above, but for an ACS session that takes a while. We don't
# necessarily want to leave if it fails (so we don't want the third check),
@@ -1020,12 +714,13 @@
# Unlike DHCP, which we can always simulate working immediately above, it is
# wrong to simulate ACS sessions working for connections without ACS access.
# This means we can either always wait for the ACS session timeout in every
- # test above, making the tests much slower, or we can set that timeout to 0
- # and then be a little gross here and change it. The latter is unfortunately
- # the lesser evil, because slow tests are bad.
+ # test above, making the tests much slower, or we can set that timeout very
+ # low and then be a little gross here and change it. The latter is
+ # unfortunately the lesser evil, because slow tests are bad.
del c.wifi_for_band(band).cycler
- c.dhcp_failure_on_s3 = False
- c.acs_session_fails = True
+ subprocess.mock('run-dhclient', c.wifi_for_band(band).name, failure=False)
+ subprocess.mock('cwmp', band, acs_session_fails=True)
+
# First iteration: check that we try s3.
c.run_until_scan(band)
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
@@ -1050,8 +745,7 @@
# Finally, test successful provisioning.
del c.wifi_for_band(band).cycler
- c.dhcp_failure_on_s3 = False
- c.acs_session_fails = False
+ subprocess.mock('cwmp', band, acs_session_fails=False)
# First iteration: check that we try s3.
c.run_until_scan(band)
last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
@@ -1092,25 +786,19 @@
@wvtest.wvtest
-@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY,
- quantenna_interfaces=['wlan1', 'wlan1_portal', 'wcli1']
- )
+@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY)
def connection_manager_test_generic_ath9k_frenzy_2g(c):
connection_manager_test_generic(c, '2.4')
@wvtest.wvtest
-@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY,
- quantenna_interfaces=['wlan1', 'wlan1_portal', 'wcli1']
- )
+@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY)
def connection_manager_test_generic_ath9k_frenzy_5g(c):
connection_manager_test_generic(c, '5')
@wvtest.wvtest
-@connection_manager_test(WIFI_SHOW_OUTPUT_FRENZY,
- quantenna_interfaces=['wlan0', 'wlan0_portal', 'wcli0']
- )
+@connection_manager_test(WIFI_SHOW_OUTPUT_FRENZY)
def connection_manager_test_generic_frenzy_5g(c):
connection_manager_test_generic(c, '5')
@@ -1123,24 +811,28 @@
Args:
c: The ConnectionManager set up by @connection_manager_test.
"""
+ ssid = 'my ssid'
+ psk = 'passphrase'
+
wvtest.WVPASSEQ(len(c._binwifi_commands), 2)
+
for band in ['2.4', '5']:
wvtest.WVPASS(('stop', '--band', band, '--persist') in c._binwifi_commands)
+ subprocess.mock('wifi', 'remote_ap',
+ bssid='11:22:33:44:55:66',
+ ssid=ssid, psk=psk, band=band, security='WPA2')
+
# Bring up ethernet, access.
c.set_ethernet(True)
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- ssid = 'my ssid'
- psk = 'passphrase'
-
# Bring up both access points.
- c.write_wlan_config('2.4', ssid, psk)
- c.enable_access_point('2.4')
- c.write_wlan_config('5', ssid, psk)
- c.enable_access_point('5')
+ for band in ('2.4', '5'):
+ subprocess.mock('cwmp', band, ssid=ssid, psk=psk, access_point=True,
+ write_now=True)
c.run_once()
wvtest.WVPASS(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1152,7 +844,7 @@
# Disable the 2.4 GHz AP, make sure the 5 GHz AP stays up. 2.4 GHz should
# join the WLAN.
- c.disable_access_point('2.4')
+ subprocess.mock('cwmp', '2.4', access_point=False, write_now=True)
c.run_until_interface_update()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1163,7 +855,7 @@
# Delete the 2.4 GHz WLAN configuration; it should leave the WLAN but nothing
# else should change.
- c.delete_wlan_config('2.4')
+ subprocess.mock('cwmp', '2.4', delete_config=True, write_now=True)
c.run_until_interface_update()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1175,7 +867,7 @@
# Disable the wired connection and remove the WLAN configurations. Both
# radios should scan. Wait for 5 GHz to scan, then enable scan results for
# 2.4. This should lead to ACS access.
- c.delete_wlan_config('5')
+ subprocess.mock('cwmp', '5', delete_config=True, write_now=True)
c.set_ethernet(False)
c.run_once()
wvtest.WVFAIL(c.acs())
@@ -1193,7 +885,7 @@
wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# The next 2.4 GHz scan will have results.
- c.interface_with_scan_results = c.wifi_for_band('2.4').name
+ _enable_basic_scan_results('2.4')
c.run_until_scan('2.4')
# Now run for enough cycles that s2 will have been tried.
for _ in range(len(c.wifi_for_band('2.4').cycler)):
@@ -1204,7 +896,7 @@
wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
c.run_once()
- wvtest.WVPASS(c.tried_to_upload_logs())
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
@wvtest.wvtest
@@ -1214,9 +906,7 @@
@wvtest.wvtest
-@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY,
- quantenna_interfaces=['wlan1', 'wlan1_portal', 'wcli1']
- )
+@connection_manager_test(WIFI_SHOW_OUTPUT_ATH9K_FRENZY)
def connection_manager_test_dual_band_two_radios_ath9k_frenzy(c):
connection_manager_test_dual_band_two_radios(c)
@@ -1243,10 +933,10 @@
psk = 'passphrase'
# Enable both access points. Only 5 should be up.
- c.write_wlan_config('2.4', ssid, psk)
- c.enable_access_point('2.4')
- c.write_wlan_config('5', ssid, psk)
- c.enable_access_point('5')
+ subprocess.mock('cwmp', '2.4', ssid=ssid, psk=psk, access_point=True,
+ write_now=True)
+ subprocess.mock('cwmp', '5', ssid=ssid, psk=psk, access_point=True,
+ write_now=True)
c.run_once()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1256,7 +946,7 @@
# Disable the 2.4 GHz AP; nothing should change. The 2.4 GHz client should
# not be up because the same radio is being used to run a 5 GHz AP.
- c.disable_access_point('2.4')
+ subprocess.mock('cwmp', '2.4', access_point=False, write_now=True)
c.run_until_interface_update()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1266,7 +956,7 @@
wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Delete the 2.4 GHz WLAN configuration; nothing should change.
- c.delete_wlan_config('2.4')
+ subprocess.mock('cwmp', '2.4', delete_config=True, write_now=True)
c.run_once()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
@@ -1279,7 +969,7 @@
# should be a single scan that leads to ACS access. (It doesn't matter which
# band we specify in run_until_scan, since both bands point to the same
# interface.)
- c.delete_wlan_config('5')
+ subprocess.mock('cwmp', '5', delete_config=True, write_now=True)
c.set_ethernet(False)
c.run_once()
wvtest.WVFAIL(c.acs())
@@ -1288,7 +978,7 @@
wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# The scan will have results that will lead to ACS access.
- c.interface_with_scan_results = c.wifi_for_band('2.4').name
+ _enable_basic_scan_results('2.4')
c.run_until_scan('5')
for _ in range(len(c.wifi_for_band('2.4').cycler)):
c.run_once()
@@ -1298,7 +988,7 @@
wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
wvtest.WVPASS(c.wifi_for_band('5').current_routes())
c.run_once()
- wvtest.WVPASSEQ(c.log_upload_count, 1)
+ wvtest.WVPASS(subprocess.upload_logs_and_wait.uploaded_logs())
@wvtest.wvtest
@@ -1320,6 +1010,12 @@
"""
# Make sure we've correctly set up the test; that there is no 5 GHz wifi
# interface.
+ ssid = 'my ssid'
+ psk = 'my psk'
+ subprocess.mock('wifi', 'remote_ap', band='2.4', ssid=ssid, psk=psk,
+ bssid='00:00:00:00:00:00', security='WPA2',
+ connection_check_result='succeed')
+
wvtest.WVPASSEQ(c.wifi_for_band('5'), None)
c.set_ethernet(True)
@@ -1327,17 +1023,17 @@
wvtest.WVPASS(c.internet())
# Make sure this doesn't crash.
- c.write_wlan_config('5', 'my ssid', 'my psk')
+ subprocess.mock('cwmp', '5', ssid=ssid, psk=psk, write_now=True)
c.run_once()
- c.enable_access_point('5')
+ subprocess.mock('cwmp', '5', access_point=True, write_now=True)
c.run_once()
- c.disable_access_point('5')
+ subprocess.mock('cwmp', '5', access_point=False, write_now=True)
c.run_once()
- c.delete_wlan_config('5')
+ subprocess.mock('cwmp', '5', delete_config=True, write_now=True)
c.run_once()
# Make sure 2.4 still works.
- c.write_wlan_config('2.4', 'my ssid', 'my psk')
+ subprocess.mock('cwmp', '2.4', ssid=ssid, psk=psk, write_now=True)
# Connect
c.run_once()
# Process DHCP results
@@ -1394,19 +1090,23 @@
# First, establish that we connect on 2.4 without the experiment, to make sure
# this test doesn't spuriously pass.
- c.write_wlan_config('2.4', 'my ssid', 'my psk')
+ ssid = 'my ssid'
+ psk = 'my psk'
+ subprocess.mock('wifi', 'remote_ap', ssid=ssid, psk=psk, band='2.4',
+ bssid='00:00:00:00:00:00')
+ subprocess.mock('cwmp', '2.4', ssid=ssid, psk=psk, write_now=True)
c.run_once()
wvtest.WVPASS(c.client_up('2.4'))
# Now, force a disconnect by deleting the config.
- c.delete_wlan_config('2.4')
+ subprocess.mock('cwmp', '2.4', delete_config=True, write_now=True)
c.run_once()
wvtest.WVFAIL(c.client_up('2.4'))
# Now enable the experiment, recreate the config, and make sure we don't
# connect.
experiment_testutils.enable('WifiNo2GClient')
- c.write_wlan_config('2.4', 'my ssid', 'my psk')
+ subprocess.mock('cwmp', '2.4', ssid=ssid, psk=psk, write_now=True)
c.run_once()
wvtest.WVFAIL(c.client_up('2.4'))
diff --git a/conman/interface.py b/conman/interface.py
index e71a322..b741555 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -229,15 +229,12 @@
try:
logging.debug('%s calling ip route %s', self.name, ' '.join(args))
- return self._really_ip_route(*args)
+ return subprocess.check_output(self.IP_ROUTE + list(args))
except subprocess.CalledProcessError as e:
logging.error('Failed to call "ip route" with args %r: %s', args,
e.message)
return ''
- def _really_ip_route(self, *args):
- return subprocess.check_output(self.IP_ROUTE + list(args))
-
def _ip_addr_show(self):
try:
return subprocess.check_output(self.IP_ADDR_SHOW + [self.name])
@@ -491,9 +488,7 @@
"""
status = {}
- if self._wpa_control and self._wpa_control.attached:
- logging.debug('%s ctrl_iface_path %s',
- self, self._wpa_control.ctrl_iface_path)
+ if self.attached():
lines = []
try:
lines = self._wpa_control.request('STATUS').splitlines()
@@ -507,6 +502,7 @@
k, v = line.strip().split('=', 1)
status[k] = v
+ logging.debug('wpa_status is %r', status)
return status
def get_wpa_control(self, socket):
@@ -528,6 +524,9 @@
self.wpa_supplicant = False
return
+ # b/31261343: Make sure we didn't miss wpa_supplicant being up.
+ self.wpa_supplicant = self.wpa_status().get('wpa_state', '') == 'COMPLETED'
+
while self._wpa_control.pending():
match = self.WPA_EVENT_RE.match(self._wpa_control.recv())
if match:
@@ -546,13 +545,13 @@
def initialize(self):
"""Unset self.initial_ssid, which is only relevant during initialization."""
-
self.initial_ssid = None
super(Wifi, self).initialize()
def connected_to_open(self):
- return (self.wpa_status().get('wpa_state', None) == 'COMPLETED' and
- self.wpa_status().get('key_mgmt', None) == 'NONE')
+ status = self.wpa_status()
+ return (status.get('wpa_state', None) == 'COMPLETED' and
+ status.get('key_mgmt', None) == 'NONE')
# TODO(rofrankel): Remove this if and when the wpactrl failures are fixed.
def wpa_cli_status(self):
@@ -600,6 +599,7 @@
return self._client_mode
def detach(self):
+ self._events = []
raise wpactrl.error('Real WPACtrl always raises this when detaching.')
def pending(self):
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 74363b8..14fb795 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -5,8 +5,6 @@
import logging
import os
import shutil
-import socket
-import struct
import subprocess
import tempfile
import time
@@ -15,11 +13,6 @@
# pylint: disable=g-import-not-at-top
logging.basicConfig(level=logging.DEBUG)
-# This is in site-packages on the device, but not when running tests, and so
-# raises lint errors.
-# pylint: disable=g-bad-import-order
-import wpactrl
-
import experiment_testutils
import interface
from wvtest import wvtest
@@ -38,87 +31,13 @@
def __init__(self, *args, **kwargs):
super(FakeInterfaceMixin, self).__init__(*args, **kwargs)
self.set_connection_check_result('succeed')
- self.routing_table = {}
- self.ip_testonly = None
-
- def _connection_check(self, *args, **kwargs):
- result = super(FakeInterfaceMixin, self)._connection_check(*args, **kwargs)
- if not self.links:
- return False
- if (self.current_routes().get('default', {}).get('via', None) !=
- self._gateway_ip):
- return False
- return result
+ subprocess.ip.register_testonly(self.name)
def set_connection_check_result(self, result):
if result in ['succeed', 'fail', 'restricted']:
- # pylint: disable=invalid-name
- self.CONNECTION_CHECK = './test/' + result
+ subprocess.mock(self.CONNECTION_CHECK, self.name, result)
else:
- raise ValueError('Invalid fake connection_check script.')
-
- def _really_ip_route(self, *args):
- def can_add_route():
- def ip_to_int(ip):
- return struct.unpack('!I', socket.inet_pton(socket.AF_INET, ip))[0]
-
- if args[1] != 'default':
- return True
-
- via = ip_to_int(args[args.index('via') + 1])
- for (ifc, route, _), _ in self.routing_table.iteritems():
- if ifc != self.name:
- continue
-
- netmask = 0
- if '/' in route:
- route, netmask = route.split('/')
- netmask = 32 - int(netmask)
- route = ip_to_int(route)
-
- if (route >> netmask) == (via >> netmask):
- return True
-
- return False
-
- if not args:
- return '\n'.join(self.routing_table.values() +
- ['1.2.3.4/24 dev fake0 proto kernel scope link',
- # Non-subnet route, e.g. to NFS host.
- '1.2.3.1 dev %s proto kernel scope link' % self.name,
- 'default via 1.2.3.4 dev fake0',
- 'random junk'])
-
- metric = None
- if 'metric' in args:
- metric = args[args.index('metric') + 1]
- if args[0] in ('add', 'del'):
- route = args[1]
- key = (self.name, route, metric)
- if args[0] == 'add' and key not in self.routing_table:
- if not can_add_route():
- raise subprocess.CalledProcessError(
- 'Tried to add default route without subnet route: %r',
- self.routing_table)
- logging.debug('Adding route for %r', key)
- self.routing_table[key] = ' '.join(args[1:])
- elif args[0] == 'del':
- if key in self.routing_table:
- logging.debug('Deleting route for %r', key)
- del self.routing_table[key]
- elif key[2] is None:
- # pylint: disable=g-builtin-op
- for k in self.routing_table.keys():
- if k[:-1] == key[:-1]:
- logging.debug('Deleting route for %r (generalized from %s)', k, key)
- del self.routing_table[k]
- break
-
- def _ip_addr_show(self):
- if self.ip_testonly:
- return _IP_ADDR_SHOW_TPL.format(name=self.name, ip=self.ip_testonly)
-
- return ''
+ raise ValueError('Invalid fake connection_check value.')
def current_routes_normal_testonly(self):
result = self.current_routes()
@@ -129,244 +48,13 @@
pass
-class FakeWPACtrl(object):
- """Fake wpactrl.WPACtrl."""
-
- # pylint: disable=unused-argument
- def __init__(self, wpa_socket):
- self._socket = wpa_socket
- self.events = []
- self.attached = False
- self.connected = False
- self.ssid_testonly = None
- self.secure_testonly = False
- self.request_status_fails = False
-
- def pending(self):
- self.check_socket_exists('pending: socket does not exist')
- return bool(self.events)
-
- def recv(self):
- self.check_socket_exists('recv: socket does not exist')
- return self.events.pop(0)
-
- def attach(self):
- if not os.path.exists(self._socket):
- raise wpactrl.error('wpactrl_attach failed')
- self.attached = True
-
- def detach(self):
- self.attached = False
- self.ssid_testonly = None
- self.secure_testonly = False
- self.connected = False
- self.check_socket_exists('wpactrl_detach failed')
-
- def request(self, request_type):
- if request_type == 'STATUS':
- if self.request_status_fails:
- raise wpactrl.error('test error')
- return self.wpa_cli_status_testonly()
- else:
- raise ValueError('Invalid request_type %s' % request_type)
-
- @property
- def ctrl_iface_path(self):
- return os.path.split(self._socket)[0]
-
- # Below methods are not part of WPACtrl.
-
- def add_event(self, event):
- self.events.append(event)
-
- def add_connected_event(self):
- self.connected = True
- self.add_event(Wifi.CONNECTED_EVENT)
-
- def add_disconnected_event(self):
- self.connected = False
- self.add_event(Wifi.DISCONNECTED_EVENT)
-
- def add_terminating_event(self):
- self.connected = False
- self.add_event(Wifi.TERMINATING_EVENT)
-
- def check_socket_exists(self, msg='Fake socket does not exist'):
- if not os.path.exists(self._socket):
- raise wpactrl.error(msg)
-
- def wpa_cli_status_testonly(self):
- if self.connected:
- return ('foo\nwpa_state=COMPLETED\nssid=%s\nkey_mgmt=%s\nbar' %
- (self.ssid_testonly,
- 'WPA2-PSK' if self.secure_testonly else 'NONE'))
- else:
- return 'wpa_state=SCANNING\naddress=12:34:56:78:90:ab'
-
-
class Wifi(FakeInterfaceMixin, interface.Wifi):
"""Fake Wifi for testing."""
-
- CONNECTED_EVENT = '<2>CTRL-EVENT-CONNECTED'
- DISCONNECTED_EVENT = '<2>CTRL-EVENT-DISCONNECTED'
- TERMINATING_EVENT = '<2>CTRL-EVENT-TERMINATING'
-
- WPACtrl = FakeWPACtrl
-
- def __init__(self, *args, **kwargs):
- super(Wifi, self).__init__(*args, **kwargs)
- self._initial_ssid_testonly = None
- self._secure_testonly = False
-
- def attach_wpa_control(self, path):
- if self._initial_ssid_testonly and self._wpa_control:
- self._wpa_control.connected = True
- super(Wifi, self).attach_wpa_control(path)
-
- def get_wpa_control(self, *args, **kwargs):
- result = super(Wifi, self).get_wpa_control(*args, **kwargs)
- if self._initial_ssid_testonly:
- result.connected = True
- result.ssid_testonly = self._initial_ssid_testonly
- result.secure_testonly = self._secure_testonly
- return result
-
- def add_connected_event(self):
- if self.attached():
- self._wpa_control.add_connected_event()
-
- def add_disconnected_event(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- if self.attached():
- self._wpa_control.add_disconnected_event()
-
- def add_terminating_event(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- if self.attached():
- self._wpa_control.add_terminating_event()
-
- def detach_wpa_control(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- super(Wifi, self).detach_wpa_control()
-
- def wpa_cli_status(self):
- # This is just a convenient way of keeping things dry; the actual wpa_cli
- # status makes a subprocess call which returns the same string.
- return self._wpa_control.wpa_cli_status_testonly()
-
- def start_wpa_supplicant_testonly(self, path):
- wpa_socket = os.path.join(path, self.name)
- logging.debug('Starting fake wpa_supplicant for %s: %s',
- self.name, wpa_socket)
- open(wpa_socket, 'w')
-
- def kill_wpa_supplicant_testonly(self, path):
- logging.debug('Killing fake wpa_supplicant for %s', self.name)
- if self.attached():
- self.detach_wpa_control()
- os.unlink(os.path.join(path, self.name))
- else:
- raise RuntimeError('Trying to kill wpa_supplicant while not attached')
-
-
-class FrenzyWPACtrl(interface.FrenzyWPACtrl):
-
- def __init__(self, *args, **kwargs):
- super(FrenzyWPACtrl, self).__init__(*args, **kwargs)
- self.ssid_testonly = None
- self.secure_testonly = False
- self.request_status_fails = False
-
- def _qcsapi(self, *command):
- return self.fake_qcsapi.get(command[0], None)
-
- def add_connected_event(self):
- self.fake_qcsapi['get_mode'] = 'Station'
- self.fake_qcsapi['get_ssid'] = self.ssid_testonly
- security = 'PSKAuthentication' if self.secure_testonly else 'NONE'
- self.fake_qcsapi['ssid_get_authentication_mode'] = security
-
- def add_disconnected_event(self):
- self.ssid_testonly = None
- self.secure_testonly = False
- self.fake_qcsapi['get_ssid'] = None
- self.fake_qcsapi['ssid_get_authentication_mode'] = 'NONE'
-
- def add_terminating_event(self):
- self.ssid_testonly = None
- self.secure_testonly = False
- self.fake_qcsapi['get_ssid'] = None
- self.fake_qcsapi['get_mode'] = 'AP'
- self.fake_qcsapi['ssid_get_authentication_mode'] = 'NONE'
-
- def detach(self):
- self.add_terminating_event()
- super(FrenzyWPACtrl, self).detach()
+ pass
class FrenzyWifi(FakeInterfaceMixin, interface.FrenzyWifi):
- WPACtrl = FrenzyWPACtrl
-
- def __init__(self, *args, **kwargs):
- super(FrenzyWifi, self).__init__(*args, **kwargs)
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- self.fake_qcsapi = {}
-
- def attach_wpa_control(self, *args, **kwargs):
- super(FrenzyWifi, self).attach_wpa_control(*args, **kwargs)
- if self._wpa_control:
- self._wpa_control.ssid_testonly = self._initial_ssid_testonly
- self._wpa_control.secure_testonly = self._secure_testonly
- if self._initial_ssid_testonly:
- self._wpa_control.add_connected_event()
-
- def get_wpa_control(self, *args, **kwargs):
- result = super(FrenzyWifi, self).get_wpa_control(*args, **kwargs)
- result.fake_qcsapi = self.fake_qcsapi
- if self._initial_ssid_testonly:
- result.fake_qcsapi['get_mode'] = 'Station'
- result.ssid_testonly = self._initial_ssid_testonly
- result.secure_testonly = self._secure_testonly
- result.add_connected_event()
- return result
-
- def add_connected_event(self):
- if self.attached():
- self._wpa_control.add_connected_event()
-
- def add_disconnected_event(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- if self.attached():
- self._wpa_control.add_disconnected_event()
-
- def add_terminating_event(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- if self.attached():
- self._wpa_control.add_terminating_event()
-
- def detach_wpa_control(self):
- self._initial_ssid_testonly = None
- self._secure_testonly = False
- super(FrenzyWifi, self).detach_wpa_control()
-
- def start_wpa_supplicant_testonly(self, unused_path):
- logging.debug('Starting fake wpa_supplicant for %s', self.name)
- self.fake_qcsapi['get_mode'] = 'Station'
-
- def kill_wpa_supplicant_testonly(self, unused_path):
- logging.debug('Killing fake wpa_supplicant for %s', self.name)
- if self.attached():
- # This happens to do what we need.
- self.add_terminating_event()
- self.detach_wpa_control()
- else:
- raise RuntimeError('Trying to kill wpa_supplicant while not attached')
+ pass
@wvtest.wvtest
@@ -443,7 +131,7 @@
wvtest.WVPASS(os.path.exists(autoprov_filepath))
wvtest.WVFAIL(b.get_ip_address())
- b.ip_testonly = '192.168.1.100'
+ subprocess.call(['ip', 'addr', 'add', '192.168.1.100', 'dev', b.name])
wvtest.WVPASSEQ(b.get_ip_address(), '192.168.1.100')
# Get a new gateway/subnet (e.g. due to joining a new network).
@@ -487,38 +175,39 @@
def generic_wifi_test(w, wpa_path):
# Not currently connected.
- w.start_wpa_supplicant_testonly(wpa_path)
+ subprocess.wifi.WPA_PATH = wpa_path
w.attach_wpa_control(wpa_path)
wvtest.WVFAIL(w.wpa_supplicant)
- # pylint: disable=protected-access
- wpa_control = w._wpa_control
-
# wpa_supplicant connects.
- wpa_control.ssid_testonly = 'my=ssid'
- wpa_control.add_connected_event()
+ ssid = 'my=ssid'
+ psk = 'passphrase'
+ subprocess.mock('wifi', 'remote_ap', ssid=ssid, psk=psk, band='5',
+ bssid='00:00:00:00:00:00', connection_check_result='succeed')
+ subprocess.check_call(['wifi', 'setclient', '--ssid', ssid, '--band', '5'],
+ env={'WIFI_CLIENT_PSK': psk})
wvtest.WVFAIL(w.wpa_supplicant)
+ w.attach_wpa_control(wpa_path)
w.handle_wpa_events()
wvtest.WVPASS(w.wpa_supplicant)
w.set_gateway_ip('192.168.1.1')
# wpa_supplicant disconnects.
- wpa_control.add_disconnected_event()
+ subprocess.mock('wifi', 'disconnected_event', '5')
w.handle_wpa_events()
wvtest.WVFAIL(w.wpa_supplicant)
# Now, start over so we can test what happens when wpa_supplicant is already
# connected when we attach.
w.detach_wpa_control()
- # pylint: disable=protected-access
- w._initial_ssid_testonly = 'my=ssid'
w._initialized = False
+ subprocess.check_call(['wifi', 'setclient', '--ssid', ssid, '--band', '5'],
+ env={'WIFI_CLIENT_PSK': psk})
w.attach_wpa_control(wpa_path)
- wpa_control = w._wpa_control
# wpa_supplicant was already connected when we attached.
wvtest.WVPASS(w.wpa_supplicant)
- wvtest.WVPASSEQ(w.initial_ssid, 'my=ssid')
+ wvtest.WVPASSEQ(w.initial_ssid, ssid)
w.initialize()
wvtest.WVPASSEQ(w.initial_ssid, None)
@@ -527,8 +216,7 @@
wvtest.WVPASSNE(w.wpa_status(), {})
# The wpa_supplicant process disconnects and terminates.
- wpa_control.add_disconnected_event()
- wpa_control.add_terminating_event()
+ subprocess.check_call(['wifi', 'stopclient', '--band', '5'])
w.handle_wpa_events()
wvtest.WVFAIL(w.wpa_supplicant)
@@ -537,32 +225,42 @@
def wifi_test():
"""Test Wifi."""
w = Wifi('wcli0', '21')
- w.set_connection_check_result('succeed')
w.initialize()
try:
wpa_path = tempfile.mkdtemp()
+ conman_path = tempfile.mkdtemp()
+ subprocess.set_conman_paths(conman_path, None)
+ subprocess.mock('wifi', 'interfaces',
+ subprocess.wifi.MockInterface(phynum='0', bands=['5'],
+ driver='cfg80211'))
generic_wifi_test(w, wpa_path)
finally:
shutil.rmtree(wpa_path)
+ shutil.rmtree(conman_path)
@wvtest.wvtest
def frenzy_wifi_test():
"""Test FrenzyWifi."""
w = FrenzyWifi('wlan0', '20')
- w.set_connection_check_result('succeed')
w.initialize()
try:
wpa_path = tempfile.mkdtemp()
+ conman_path = tempfile.mkdtemp()
+ subprocess.set_conman_paths(conman_path, None)
+ subprocess.mock('wifi', 'interfaces',
+ subprocess.wifi.MockInterface(phynum='0', bands=['5'],
+ driver='frenzy'))
FrenzyWifi.WPACtrl.WIFIINFO_PATH = wifiinfo_path = tempfile.mkdtemp()
generic_wifi_test(w, wpa_path)
finally:
shutil.rmtree(wpa_path)
+ shutil.rmtree(conman_path)
shutil.rmtree(wifiinfo_path)
@@ -631,5 +329,62 @@
shutil.rmtree(interface.CWMP_PATH)
+@wvtest.wvtest
+def b31261343_test():
+ """Test Wifi."""
+ w = Wifi('wcli0', '21')
+ w.initialize()
+
+ try:
+ wpa_path = tempfile.mkdtemp()
+ conman_path = tempfile.mkdtemp()
+ subprocess.set_conman_paths(conman_path, None)
+ subprocess.mock('wifi', 'interfaces',
+ subprocess.wifi.MockInterface(phynum='0', bands=['5'],
+ driver='cfg80211'))
+ subprocess.wifi.WPA_PATH = wpa_path
+
+ w.attach_wpa_control(wpa_path)
+ wvtest.WVFAIL(w.wpa_supplicant)
+
+ # Set up.
+ ssid = 'my=ssid'
+ psk = 'passphrase'
+ subprocess.mock('wifi', 'remote_ap', ssid=ssid, psk=psk, band='5',
+ bssid='00:00:00:00:00:00', connection_check_result='succeed')
+ subprocess.check_call(['wifi', 'setclient', '--ssid', ssid, '--band', '5'],
+ env={'WIFI_CLIENT_PSK': psk})
+
+ w.set_gateway_ip('192.168.1.1')
+ w.set_subnet('192.168.1.0/24')
+ wvtest.WVFAIL(w.wpa_supplicant)
+ w.attach_wpa_control(wpa_path)
+ w.handle_wpa_events()
+
+ def check_working():
+ w.update_routes(True)
+ wvtest.WVPASS(w.wpa_supplicant)
+ wvtest.WVPASS('default' in w.current_routes())
+
+ def check_broken():
+ w.update_routes(True)
+ wvtest.WVFAIL(w.wpa_supplicant)
+ wvtest.WVFAIL('default' in w.current_routes())
+
+ check_working()
+
+ # This is the buggy state.
+ w.wpa_supplicant = False
+ check_broken()
+
+ # Should fix itself when we next run handle_wpa_events.
+ w.handle_wpa_events()
+ check_working()
+
+ finally:
+ shutil.rmtree(wpa_path)
+ shutil.rmtree(conman_path)
+
+
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/conman/iw.py b/conman/iw.py
index f2e15d8..8d80010 100755
--- a/conman/iw.py
+++ b/conman/iw.py
@@ -15,6 +15,7 @@
_BSSID_RE = r'BSS (?P<BSSID>([0-9a-f]{2}:?){6})\(on .*\)'
_SSID_RE = r'SSID: (?P<SSID>.*)'
_RSSI_RE = r'signal: (?P<RSSI>.*) dBm'
+_FREQ_RE = r'freq: (?P<freq>\d+)'
_VENDOR_IE_RE = (r'Vendor specific: OUI (?P<OUI>([0-9a-f]{2}:?){3}), '
'data:(?P<data>( [0-9a-f]{2})+)')
@@ -29,16 +30,17 @@
class BssInfo(object):
"""Contains info about a BSS, parsed from 'iw scan'."""
- def __init__(self, bssid='', ssid='', rssi=-100, security=None,
+ def __init__(self, bssid='', ssid='', rssi=0, band=None, security=None,
vendor_ies=None):
self.bssid = bssid
self.ssid = ssid
self.rssi = rssi
+ self.band = band
self.vendor_ies = vendor_ies or []
self.security = security or []
def __attrs(self):
- return (self.bssid, self.ssid, tuple(sorted(self.vendor_ies)),
+ return (self.bssid, self.ssid, self.band, tuple(sorted(self.vendor_ies)),
tuple(sorted(self.security)), self.rssi)
def __eq__(self, other):
@@ -52,9 +54,9 @@
return hash(self.__attrs())
def __repr__(self):
- return '<BssInfo: SSID=%s BSSID=%s Security=%s Vendor IEs=%s>' % (
- self.ssid, self.bssid, ','.join(self.security),
- ','.join('|'.join(ie) for ie in self.vendor_ies))
+ return ('<BssInfo: SSID=%s BSSID=%s Band=%s Security=%s Vendor IEs=%s>'
+ % (self.ssid, self.bssid, self.band, ','.join(self.security),
+ ','.join('|'.join(ie) for ie in self.vendor_ies)))
# TODO(rofrankel): waveguide also scans. Can we find a way to avoid two programs
@@ -79,6 +81,10 @@
if match:
bss_info.rssi = float(match.group('RSSI'))
continue
+ match = re.match(_FREQ_RE, line)
+ if match:
+ bss_info.band = '2.4' if match.group('freq').startswith('2') else '5'
+ continue
match = re.match(_VENDOR_IE_RE, line)
if match:
bss_info.vendor_ies.append((match.group('OUI'),
diff --git a/conman/iw_test.py b/conman/iw_test.py
index 55b2e7b..202d10c 100755
--- a/conman/iw_test.py
+++ b/conman/iw_test.py
@@ -2,633 +2,48 @@
"""Tests for iw.py."""
+import subprocess
+
import iw
from wvtest import wvtest
-SCAN_OUTPUT = """BSS 00:23:97:57:f4:d8(on wcli0)
- TSF: 1269828266773 usec (14d, 16:43:48)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS Privacy ShortSlotTime (0x0411)
- signal: -60.00 dBm
- last seen: 2190 ms ago
- Information elements from Probe Response frame:
- Vendor specific: OUI 00:11:22, data: 01 23 45 67
- SSID: short scan result
- Supported rates: 1.0* 2.0* 5.5* 11.0* 18.0 24.0 36.0 54.0
- DS Parameter set: channel 6
- ERP: <no flags>
- ERP D4.0: <no flags>
- Privacy: WEP
- Extended supported rates: 6.0 9.0 12.0 48.0
-BSS 94:b4:0f:f1:02:a0(on wcli0)
- TSF: 16233722683 usec (0d, 04:30:33)
- freq: 2412
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -54.00 dBm
- last seen: 2490 ms ago
- Information elements from Probe Response frame:
- SSID: Google
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 1
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- RSN: * Version: 1
- * Group cipher: CCMP
- * Pairwise ciphers: CCMP
- * Authentication suites: IEEE 802.1X
- * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028)
- BSS Load:
- * station count: 0
- * channel utilisation: 33/255
- * available admission capacity: 25625 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 1
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 0
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:35:60(on wcli0)
- TSF: 1739987968 usec (0d, 00:28:59)
- freq: 2462
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -39.00 dBm
- last seen: 1910 ms ago
- Information elements from Probe Response frame:
- SSID: Google
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 11
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- RSN: * Version: 1
- * Group cipher: CCMP
- * Pairwise ciphers: CCMP
- * Authentication suites: IEEE 802.1X
- * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028)
- BSS Load:
- * station count: 0
- * channel utilisation: 49/255
- * available admission capacity: 26875 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 11
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:35:61(on wcli0)
- TSF: 1739988134 usec (0d, 00:28:59)
- freq: 2462
- beacon interval: 100 TUs
- capability: ESS ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1521)
- signal: -38.00 dBm
- last seen: 1910 ms ago
- Information elements from Probe Response frame:
- SSID: GoogleGuest
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 11
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- BSS Load:
- * station count: 1
- * channel utilisation: 49/255
- * available admission capacity: 26875 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 11
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:3a:e0(on wcli0)
- TSF: 24578560051 usec (0d, 06:49:38)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -55.00 dBm
- last seen: 2310 ms ago
- Information elements from Probe Response frame:
- SSID: Google
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- TIM: DTIM Count 0 DTIM Period 1 Bitmap Control 0x0 Bitmap[0] 0x0
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- RSN: * Version: 1
- * Group cipher: CCMP
- * Pairwise ciphers: CCMP
- * Authentication suites: IEEE 802.1X
- * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028)
- BSS Load:
- * station count: 1
- * channel utilisation: 21/255
- * available admission capacity: 28125 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:3a:e1(on wcli0)
- TSF: 24578576547 usec (0d, 06:49:38)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1521)
- signal: -65.00 dBm
- last seen: 80 ms ago
- Information elements from Probe Response frame:
- SSID: GoogleGuest
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- BSS Load:
- * station count: 2
- * channel utilisation: 21/255
- * available admission capacity: 28125 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-
-BSS 94:b4:0f:f1:36:41(on wcli0)
- TSF: 12499149351 usec (0d, 03:28:19)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1521)
- signal: -67.00 dBm
- last seen: 80 ms ago
- Information elements from Probe Response frame:
- SSID: GoogleGuest
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- BSS Load:
- * station count: 1
- * channel utilisation: 28/255
- * available admission capacity: 27500 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:36:40(on wcli0)
- TSF: 12499150000 usec (0d, 03:28:19)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -66.00 dBm
- last seen: 2350 ms ago
- Information elements from Probe Response frame:
- SSID: Google
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- TIM: DTIM Count 0 DTIM Period 1 Bitmap Control 0x0 Bitmap[0] 0x0
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- RSN: * Version: 1
- * Group cipher: CCMP
- * Pairwise ciphers: CCMP
- * Authentication suites: IEEE 802.1X
- * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028)
- BSS Load:
- * station count: 0
- * channel utilisation: 28/255
- * available admission capacity: 27500 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-BSS 94:b4:0f:f1:36:42(on wcli0)
- TSF: 12499150000 usec (0d, 03:28:19)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -66.00 dBm
- last seen: 2350 ms ago
- Information elements from Probe Response frame:
- SSID:
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- TIM: DTIM Count 0 DTIM Period 1 Bitmap Control 0x0 Bitmap[0] 0x0
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- BSS Load:
- * station count: 0
- * channel utilisation: 28/255
- * available admission capacity: 27500 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
- Vendor specific: OUI 00:11:22, data: 01 23 45 67
- Vendor specific: OUI f4:f5:e8, data: 01
- Vendor specific: OUI f4:f5:e8, data: 03 47 46 69 62 65 72 53 65 74 75 70 41 75 74 6f 6d 61 74 69 6f 6e
-BSS 00:1a:11:f1:36:43(on wcli0)
- TSF: 12499150000 usec (0d, 03:28:19)
- freq: 2437
- beacon interval: 100 TUs
- capability: ESS Privacy ShortPreamble SpectrumMgmt ShortSlotTime RadioMeasure (0x1531)
- signal: -66.00 dBm
- last seen: 2350 ms ago
- Information elements from Probe Response frame:
- SSID:
- Supported rates: 36.0* 48.0 54.0
- DS Parameter set: channel 6
- TIM: DTIM Count 0 DTIM Period 1 Bitmap Control 0x0 Bitmap[0] 0x0
- Country: US Environment: Indoor/Outdoor
- Channels [1 - 11] @ 36 dBm
- Power constraint: 0 dB
- TPC report: TX power: 3 dBm
- ERP: <no flags>
- BSS Load:
- * station count: 0
- * channel utilisation: 28/255
- * available admission capacity: 27500 [*32us]
- HT capabilities:
- Capabilities: 0x19ad
- RX LDPC
- HT20
- SM Power Save disabled
- RX HT20 SGI
- TX STBC
- RX STBC 1-stream
- Max AMSDU length: 7935 bytes
- DSSS/CCK HT40
- Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
- Minimum RX AMPDU time spacing: 4 usec (0x05)
- HT RX MCS rate indexes supported: 0-23
- HT TX MCS rate indexes are undefined
- HT operation:
- * primary channel: 6
- * secondary channel offset: no secondary
- * STA channel width: 20 MHz
- * RIFS: 1
- * HT protection: nonmember
- * non-GF present: 1
- * OBSS non-GF present: 1
- * dual beacon: 0
- * dual CTS protection: 0
- * STBC beacon: 0
- * L-SIG TXOP Prot: 0
- * PCO active: 0
- * PCO phase: 0
- Overlapping BSS scan params:
- * passive dwell: 20 TUs
- * active dwell: 10 TUs
- * channel width trigger scan interval: 300 s
- * scan passive total per channel: 200 TUs
- * scan active total per channel: 20 TUs
- * BSS width channel transition delay factor: 5
- * OBSS Scan Activity Threshold: 0.25 %
- Extended capabilities: HT Information Exchange Supported, Extended Channel Switching, BSS Transition, 6
- WMM: * Parameter version 1
- * u-APSD
- * BE: CW 15-1023, AIFSN 3
- * BK: CW 15-1023, AIFSN 7
- * VI: CW 7-15, AIFSN 2, TXOP 3008 usec
- * VO: CW 3-7, AIFSN 2, TXOP 1504 usec
-"""
-
-
-# pylint: disable=unused-argument,protected-access
-def fake_scan(*args, **kwargs):
- return SCAN_OUTPUT
-iw._scan = fake_scan
+SCAN_RESULTS = (
+ {'rssi': -60, 'ssid': 'short scan result', 'bssid': '00:23:97:57:f4:d8',
+ 'vendor_ies': [('00:11:22', '01 23 45 67')], 'security': 'WEP'},
+ {'rssi': -54, 'ssid': 'Google', 'bssid': '94:b4:0f:f1:02:a0',
+ 'vendor_ies': [], 'security': 'WPA2'},
+ {'rssi': -39, 'ssid': 'Google', 'bssid': '94:b4:0f:f1:35:60',
+ 'vendor_ies': [], 'security': 'WPA2'},
+ {'rssi': -38, 'ssid': 'GoogleGuest', 'bssid': '94:b4:0f:f1:35:61',
+ 'vendor_ies': [], 'security': None},
+ {'rssi': -55, 'ssid': 'Google', 'bssid': '94:b4:0f:f1:3a:e0',
+ 'vendor_ies': [], 'security': 'WPA2'},
+ {'rssi': -65, 'ssid': 'GoogleGuest', 'bssid': '94:b4:0f:f1:3a:e1',
+ 'vendor_ies': [], 'security': None},
+ {'rssi': -67, 'ssid': 'GoogleGuest', 'bssid': '94:b4:0f:f1:36:41',
+ 'vendor_ies': [], 'security': None},
+ {'rssi': -66, 'ssid': 'Google', 'bssid': '94:b4:0f:f1:36:40',
+ 'vendor_ies': [], 'security': 'WPA2'},
+ {'rssi': -66, 'ssid': '', 'bssid': '94:b4:0f:f1:36:42',
+ 'vendor_ies': [('00:11:22', '01 23 45 67'), ('f4:f5:e8', '01'),
+ ('f4:f5:e8', '03 47 46 69 62 65 72 53 65 74 75 70 41 75 74 '
+ '6f 6d 61 74 69 6f 6e')], 'security': None},
+ {'rssi': -66, 'ssid': '', 'bssid': '00:1a:11:f1:36:43',
+ 'vendor_ies': [], 'security': None},
+)
@wvtest.wvtest
def find_bssids_test():
"""Test iw.find_bssids."""
+ subprocess.mock('wifi', 'interfaces',
+ subprocess.wifi.MockInterface(phynum='0', bands=['2.4', '5'],
+ driver='cfg80211'))
+ subprocess.call(['ifup', 'wcli0'])
+ for scan_result in SCAN_RESULTS:
+ subprocess.mock('wifi', 'remote_ap', band='5', **scan_result)
+
test_ie = ('00:11:22', '01 23 45 67')
provisioning_ie = ('f4:f5:e8', '01')
ssid_ie = (
@@ -637,48 +52,51 @@
)
short_scan_result = iw.BssInfo(ssid='short scan result',
bssid='00:23:97:57:f4:d8',
+ band='5',
rssi=-60,
security=['WEP'],
vendor_ies=[test_ie])
provisioning_bss_info = iw.BssInfo(ssid=iw.DEFAULT_GFIBERSETUP_SSID,
bssid='94:b4:0f:f1:36:42',
+ band='5',
rssi=-66,
vendor_ies=[test_ie, provisioning_ie,
ssid_ie])
provisioning_bss_info_frenzy = iw.BssInfo(ssid=iw.DEFAULT_GFIBERSETUP_SSID,
bssid='00:1a:11:f1:36:43',
+ band='5',
rssi=-66)
wvtest.WVPASSEQ(
- set(iw.find_bssids('wcli0', True)),
+ set(iw.find_bssids('2.4', True)),
set([(short_scan_result, 2.4),
(provisioning_bss_info, 5.34),
(provisioning_bss_info_frenzy, 4.34),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:36:41', rssi=-67),
- 2.33),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:3a:e1', rssi=-65),
- 2.35),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:35:61', rssi=-38),
- 2.62),
- (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:36:40', rssi=-66,
- security=['WPA2']), 2.34),
- (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:3a:e0', rssi=-55,
- security=['WPA2']), 2.45),
- (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:35:60', rssi=-39,
- security=['WPA2']), 2.61),
- (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:02:a0', rssi=-54,
- security=['WPA2']), 2.46)]))
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:36:41',
+ band='5', rssi=-67), 2.33),
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:3a:e1',
+ band='5', rssi=-65), 2.35),
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:35:61',
+ band='5', rssi=-38), 2.62),
+ (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:36:40', band='5',
+ rssi=-66, security=['WPA2']), 2.34),
+ (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:3a:e0', band='5',
+ rssi=-55, security=['WPA2']), 2.45),
+ (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:35:60', band='5',
+ rssi=-39, security=['WPA2']), 2.61),
+ (iw.BssInfo(ssid='Google', bssid='94:b4:0f:f1:02:a0', band='5',
+ rssi=-54, security=['WPA2']), 2.46)]))
wvtest.WVPASSEQ(
- set(iw.find_bssids('wcli0', False)),
+ set(iw.find_bssids('2.4', False)),
set([(provisioning_bss_info, 5.34),
(provisioning_bss_info_frenzy, 4.34),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:36:41', rssi=-67),
- 2.33),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:3a:e1', rssi=-65),
- 2.35),
- (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:35:61', rssi=-38),
- 2.62)]))
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:36:41', band='5',
+ rssi=-67), 2.33),
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:3a:e1', band='5',
+ rssi=-65), 2.35),
+ (iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:35:61', band='5',
+ rssi=-38), 2.62)]))
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/conman/ratchet.py b/conman/ratchet.py
index 07e61a8..61e8705 100644
--- a/conman/ratchet.py
+++ b/conman/ratchet.py
@@ -15,6 +15,7 @@
except AttributeError:
_gettime = time.time
+
# This has to be called before another module calls it with a higher log level.
# pylint: disable=g-import-not-at-top
logging.basicConfig(level=logging.DEBUG)
@@ -69,7 +70,7 @@
if now > self.start_at + self.timeout:
self.timed_out = True
self.logger.info('%s timed out after %.2f seconds',
- self.name, now - self.t0)
+ self.name, now - self.start_at)
raise TimeoutException()
self.not_done_before = _gettime()
@@ -81,7 +82,7 @@
self.done_after = self.not_done_before
self.done_by = _gettime()
self.logger.info('%s completed after %.2f seconds',
- self.name, self.done_by - self.t0)
+ self.name, self.done_by - self.start_at)
if self.callback:
self.callback()
@@ -90,47 +91,35 @@
class FileExistsCondition(Condition):
"""A condition that checks for the existence of a file."""
- def __init__(self, name, filename, timeout):
- self._filename = filename
+ def __init__(self, name, filepath, timeout):
+ self._filepath = filepath
super(FileExistsCondition, self).__init__(name, None, timeout)
def evaluate(self):
- return os.path.exists(self._filename)
-
- def mtime(self):
- if os.path.exists(self._filename):
- return os.stat(self._filename).st_mtime
-
- return None
-
- def mark_done(self):
- super(FileExistsCondition, self).mark_done()
- # We have to check this because the file could have been deleted while this
- # was being called. But this condition should almost always be true.
- mtime = self.mtime()
- if mtime:
- self.done_after = self.done_by = mtime
+ return os.path.exists(self._filepath)
class FileTouchedCondition(FileExistsCondition):
- """A condition that checks that a file was touched after a certain time."""
+ """A condition that checks that a file is touched.
- def reset(self, t0=None, start_at=None):
- mtime = self.mtime
- if t0 and mtime and mtime < t0:
- self.initial_mtime = self.mtime()
- else:
- self.initial_mtime = None
- super(FileTouchedCondition, self).reset(t0, start_at)
+ Because the clock may be adjusted, we can't compare the file's mtime to a
+ timestamp. So just look for mtime changes instead. This means that t0 and
+ start_at aren't respected; instead, look for touches after whenever the
+ FileTouchedCondition is reset.
+ """
+
+ def reset(self, *args, **kwargs):
+ super(FileTouchedCondition, self).reset(*args, **kwargs)
+ self.initial_mtime = self.mtime()
def evaluate(self):
if not super(FileTouchedCondition, self).evaluate():
return False
+ return self.mtime() != self.initial_mtime
- if self.initial_mtime:
- return self.mtime() > self.initial_mtime
-
- return self.mtime() >= self.t0
+ def mtime(self):
+ if os.path.exists(self._filepath):
+ return os.stat(self._filepath).st_mtime
class Ratchet(Condition):
@@ -146,6 +135,7 @@
def reset(self):
self._current_step = 0
+ self.active = False
for step in self.steps:
step.reset()
self._set_step_status(step, False)
@@ -153,11 +143,18 @@
def start(self):
self.reset()
+ self.active = True
self._set_current_step_status(True)
+ def stop(self):
+ self.active = False
+
# Override check rather than evaluate because we don't want the Ratchet to
# time out unless one of its steps does.
def check(self):
+ if not self.active:
+ return
+
if not self.done_after:
while self.current_step().check():
if not self.advance():
diff --git a/conman/ratchet_test.py b/conman/ratchet_test.py
index 97f7c94..48b693c 100755
--- a/conman/ratchet_test.py
+++ b/conman/ratchet_test.py
@@ -45,38 +45,29 @@
@wvtest.wvtest
def file_condition_test():
"""Test File*Condition functionality."""
- try:
- _, filename = tempfile.mkstemp()
- c_exists = ratchet.FileExistsCondition('c exists', filename, 0.1)
- c_mtime = ratchet.FileTouchedCondition('c mtime', filename, 0.1)
- wvtest.WVPASS(c_exists.check())
- wvtest.WVFAIL(c_mtime.check())
- # mtime precision is too low to notice that we're touching the file *after*
- # capturing its initial mtime rather than at the same time, so take a short
- # nap before touching it.
- time.sleep(0.01)
- open(filename, 'w')
- wvtest.WVPASS(c_mtime.check())
+ _, filename = tempfile.mkstemp()
+ c_exists = ratchet.FileExistsCondition('c exists', filename, 0.1)
+ c_touched = ratchet.FileTouchedCondition('c touched', filename, 0.1)
+ wvtest.WVPASS(c_exists.check())
+ wvtest.WVFAIL(c_touched.check())
+ # File mtime resolution isn't fine enough to see the difference between this
+ # write and the previous one, so sleep for a short time before writing to
+ # ensure a different mtime.
+ time.sleep(0.01)
+ open(filename, 'w')
+ wvtest.WVPASS(c_touched.check())
- # Test that old mtimes don't count.
- time.sleep(0.01)
- c_mtime.reset()
- wvtest.WVFAIL(c_mtime.check())
- time.sleep(0.1)
- wvtest.WVEXCEPT(ratchet.TimeoutException, c_mtime.check)
+ # Test that pre-existing files don't count.
+ c_touched.reset()
+ wvtest.WVFAIL(c_touched.check())
+ time.sleep(0.1)
+ wvtest.WVEXCEPT(ratchet.TimeoutException, c_touched.check)
- # Test t0 and start_at.
- os.unlink(filename)
- now = time.time()
- c_mtime.reset(t0=now, start_at=now + 0.2)
- wvtest.WVFAIL(c_mtime.check())
- time.sleep(0.15)
- wvtest.WVFAIL(c_mtime.check())
- open(filename, 'w')
- wvtest.WVPASS(c_mtime.check())
-
- finally:
- os.unlink(filename)
+ # Test that deleting files doesn't count.
+ c_touched.reset()
+ wvtest.WVFAIL(c_touched.check())
+ os.unlink(filename)
+ wvtest.WVFAIL(c_touched.check())
@wvtest.wvtest
@@ -122,7 +113,7 @@
wvtest.WVEXCEPT(ratchet.TimeoutException, r.check)
x = y = z = 1
- r.reset()
+ r.start()
wvtest.WVPASS(r.check())
finally:
shutil.rmtree(status_export_path)
diff --git a/conman/status.py b/conman/status.py
index 8f8d3a1..c5d4187 100644
--- a/conman/status.py
+++ b/conman/status.py
@@ -34,6 +34,7 @@
WAITING_FOR_PROVISIONING = 'WAITING_FOR_PROVISIONING'
WAITING_FOR_DHCP = 'WAITING_FOR_DHCP'
+ ACS_CONNECTION_CHECK = 'ACS_CONNECTION_CHECK'
WAITING_FOR_CWMP_WAKEUP = 'WAITING_FOR_CWMP_WAKEUP'
WAITING_FOR_ACS_SESSION = 'WAITING_FOR_ACS_SESSION'
PROVISIONING_COMPLETED = 'PROVISIONING_COMPLETED'
@@ -93,13 +94,18 @@
(P.WAITING_FOR_PROVISIONING,),
(P.WAITING_FOR_CWMP_WAKEUP, P.WAITING_FOR_ACS_SESSION),
),
+ P.ACS_CONNECTION_CHECK: (
+ (P.WAITING_FOR_PROVISIONING,),
+ (P.WAITING_FOR_DHCP, P.WAITING_FOR_CWMP_WAKEUP,
+ P.WAITING_FOR_ACS_SESSION),
+ ),
P.WAITING_FOR_CWMP_WAKEUP: (
(P.WAITING_FOR_PROVISIONING,),
- (P.WAITING_FOR_DHCP, P.WAITING_FOR_ACS_SESSION),
+ (P.WAITING_FOR_DHCP, P.ACS_CONNECTION_CHECK, P.WAITING_FOR_ACS_SESSION),
),
P.WAITING_FOR_ACS_SESSION: (
(P.WAITING_FOR_PROVISIONING,),
- (P.WAITING_FOR_DHCP, P.WAITING_FOR_CWMP_WAKEUP),
+ (P.WAITING_FOR_DHCP, P.ACS_CONNECTION_CHECK, P.WAITING_FOR_CWMP_WAKEUP),
),
P.PROVISIONING_COMPLETED: (
(),
diff --git a/conman/status_test.py b/conman/status_test.py
index befebbf..38dda83 100755
--- a/conman/status_test.py
+++ b/conman/status_test.py
@@ -137,8 +137,11 @@
check_exported(True, False, status.P.CONNECTED_TO_OPEN)
check_exported(True, False, status.P.WAITING_FOR_PROVISIONING)
check_exported(True, False, status.P.WAITING_FOR_DHCP)
- s.waiting_for_cwmp_wakeup = True
+ s.acs_connection_check = True
check_exported(False, False, status.P.WAITING_FOR_DHCP)
+ check_exported(True, False, status.P.ACS_CONNECTION_CHECK)
+ s.waiting_for_cwmp_wakeup = True
+ check_exported(False, False, status.P.ACS_CONNECTION_CHECK)
check_exported(True, False, status.P.WAITING_FOR_CWMP_WAKEUP)
s.waiting_for_acs_session = True
check_exported(False, False, status.P.WAITING_FOR_DHCP)
@@ -147,6 +150,7 @@
s.provisioning_completed = True
check_exported(False, False, status.P.WAITING_FOR_PROVISIONING)
check_exported(False, False, status.P.WAITING_FOR_DHCP)
+ check_exported(False, False, status.P.ACS_CONNECTION_CHECK)
check_exported(False, False, status.P.WAITING_FOR_CWMP_WAKEUP)
check_exported(False, False, status.P.WAITING_FOR_CWMP_WAKEUP)
diff --git a/conman/test/fail b/conman/test/fail
deleted file mode 100755
index 2bb8d86..0000000
--- a/conman/test/fail
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-exit 1
diff --git a/conman/test/fake_python/subprocess/__init__.py b/conman/test/fake_python/subprocess/__init__.py
new file mode 100644
index 0000000..3d73d4d
--- /dev/null
+++ b/conman/test/fake_python/subprocess/__init__.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+"""subprocess replacement that implements specific programs in Python."""
+
+import importlib
+import logging
+import os
+import types
+
+logger = logging.getLogger('subprocess')
+logger.setLevel(logging.DEBUG)
+
+
+# Values are only for when the module name does not match the command name.
+_COMMAND_NAMES = {
+ 'connection_check': None,
+ 'cwmp': None,
+ 'get_quantenna_interfaces': 'get-quantenna-interfaces',
+ 'ifdown': None,
+ 'ifplugd_action': '/etc/ifplugd/ifplugd.action',
+ 'ifup': None,
+ 'ip': None,
+ 'register_experiment': None,
+ 'run_dhclient': 'run-dhclient',
+ 'qcsapi': None,
+ 'upload_logs_and_wait': 'upload-logs-and-wait',
+ 'wifi': None,
+ 'wpa_cli': None,
+}
+_COMMANDS = {v or k: importlib.import_module('.' + k, __name__)
+ for k, v in _COMMAND_NAMES.iteritems()}
+
+STDOUT = 1
+STDERR = 2
+
+
+class CalledProcessError(Exception):
+
+ def __init__(self, returncode, cmd, output):
+ super(CalledProcessError, self).__init__()
+ self.returncode = returncode
+ self.cmd = cmd
+ self.output = output
+
+ def __repr__(self):
+ return ('CalledProcessError: '
+ 'Command "%r" returned non-zero exit status %d: %s'
+ % (self.cmd, self.returncode, self.output))
+
+
+def _call(command, **kwargs):
+ """Fake subprocess call."""
+ if type(command) not in (tuple, list):
+ raise Exception('Fake subprocess.call only supports list/tuple commands, '
+ 'got: %s', command)
+
+ ignored_kwargs = ('stdout', 'stderr')
+ for ignored_kwarg in ignored_kwargs:
+ kwargs.pop(ignored_kwarg, None)
+ extra_env = kwargs.pop('env', {})
+ if kwargs:
+ raise Exception('Fake subprocess.call does not support these kwargs: %s'
+ % kwargs.keys())
+
+ logger.debug('%r%s', command, (', env %r' % extra_env) if extra_env else '')
+
+ command, args = command[0], command[1:]
+
+ if command not in _COMMANDS:
+ raise Exception('Fake subprocess.call does not support %r, supports %r' %
+ (command, _COMMANDS.keys()))
+
+ impl = _COMMANDS[command]
+ if isinstance(impl, types.ModuleType):
+ impl = impl.call
+
+ forwarded_kwargs = {}
+ if extra_env:
+ forwarded_kwargs['env'] = extra_env
+ return impl(*args, **forwarded_kwargs)
+
+
+def call(command, **kwargs):
+ rc, _ = _call(command, **kwargs)
+ return rc
+
+
+def check_call(command, **kwargs):
+ rc, output = _call(command, **kwargs)
+ if rc:
+ raise CalledProcessError(rc, command, output)
+ return True
+
+
+def check_output(command, **kwargs):
+ rc, output = _call(command, **kwargs)
+ if rc != 0:
+ raise CalledProcessError(rc, command, output)
+ return output
+
+
+def mock(command, *args, **kwargs):
+ _COMMANDS[command].mock(*args, **kwargs)
+
+
+def reset():
+ """Reset any module-level state."""
+ for command in _COMMANDS.itervalues():
+ if isinstance(command, types.ModuleType):
+ reload(command)
+
+
+def set_conman_paths(tmp_path=None, config_path=None, cwmp_path=None):
+ for command in ('run-dhclient', '/etc/ifplugd/ifplugd.action'):
+ _COMMANDS[command].CONMAN_PATH = tmp_path
+
+ for command in ('cwmp',):
+ _COMMANDS[command].CONMAN_CONFIG_PATH = config_path
+
+ for command in ('cwmp',):
+ _COMMANDS[command].CWMP_PATH = cwmp_path
+
+ # Make sure <tmp_path>/interfaces exists.
+ tmp_interfaces_path = os.path.join(tmp_path, 'interfaces')
+ if not os.path.exists(tmp_interfaces_path):
+ os.mkdir(tmp_interfaces_path)
+
+
+# Some tiny fake implementations don't need their own file.
+
+
+def echo(*s):
+ return 0, ' '.join(s)
+
+
+def env(extra_env, *command, **kwargs):
+ final_env = kwargs.get('env', {})
+ k, v = extra_env.split('=')
+ final_env[k] = v
+ kwargs['env'] = final_env
+ return _call(command, **kwargs)
+
+
+def timeout(unused_t, *command, **kwargs):
+ """Just a transparent pass-through."""
+ return _call(command, **kwargs)
+
+
+_COMMANDS.update({'echo': echo, 'env': env, 'timeout': timeout,})
diff --git a/conman/test/fake_python/subprocess/connection_check.py b/conman/test/fake_python/subprocess/connection_check.py
new file mode 100644
index 0000000..8c23c50
--- /dev/null
+++ b/conman/test/fake_python/subprocess/connection_check.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+"""Fake connection_check implementation."""
+
+RESULTS = {}
+
+
+def mock(interface, result):
+ RESULTS[interface] = result
+
+
+def call(*args):
+ interface = args[args.index('-I') + 1]
+ result = RESULTS.get(interface, 'fail')
+
+ if result == 'restricted' and '-a' in args:
+ result = 'succeed'
+
+ return (0 if result == 'succeed' else 1), ''
diff --git a/conman/test/fake_python/subprocess/cwmp.py b/conman/test/fake_python/subprocess/cwmp.py
new file mode 100644
index 0000000..f34cd2f
--- /dev/null
+++ b/conman/test/fake_python/subprocess/cwmp.py
@@ -0,0 +1,148 @@
+#!/usr/bin/python
+
+"""Fake catawampus implementation."""
+
+import logging
+import os
+
+import connection_check
+
+
+logger = logging.getLogger('subprocess.cwmp')
+
+CONMAN_CONFIG_PATH = None
+CWMP_PATH = None
+CONFIG = {}
+ACCESS_POINT = {}
+ACS_SESSION_FAILS = False
+
+
+def call(command, env=None):
+ if command == 'wakeup':
+ if not CONMAN_CONFIG_PATH:
+ raise ValueError('Call subprocess.set_conman_paths before calling '
+ '"cwmp wakeup".')
+
+ write_acscontact()
+
+ if ACS_SESSION_FAILS:
+ return 0, ''
+
+ if ((env and 'write_now_testonly' in env) or
+ [result for result in connection_check.RESULTS.itervalues()
+ if result in ('restricted', 'succeed')]):
+ for band in ('2.4', '5'):
+ if CONFIG.get(band, None):
+ write_wlan_config(band)
+ else:
+ delete_wlan_config(band)
+ disable_access_point(band)
+
+ if ACCESS_POINT.get(band, False):
+ enable_access_point(band)
+ else:
+ disable_access_point(band)
+
+ logger.debug('Fake ACS session completing')
+ write_acsconnected()
+ else:
+ logger.debug('ACS session failed due to no working connections')
+
+ return 0, ''
+
+ raise ValueError('Fake cwmp only supports "wakeup" command.')
+
+
+def wlan_config_filename(band):
+ return os.path.join(CONMAN_CONFIG_PATH, 'command.%s' % band)
+
+
+def access_point_filename(band):
+ return os.path.join(CONMAN_CONFIG_PATH, 'access_point.%s' % band)
+
+
+def write_wlan_config(band):
+ final_filename = wlan_config_filename(band)
+ logger.debug('Writing config for band %s: %s', band, final_filename)
+ # We don't care which writes are atomic, as long as some but not all are.
+ # Making it depend on band achieves this.
+ atomic = band == '2.4'
+ filename = final_filename + ('.tmp' if atomic else '')
+ with open(filename, 'w') as f:
+ f.write('\n'.join(['env', 'WIFI_PSK=%s' % CONFIG[band]['psk'],
+ 'wifi', 'set', '--band', band,
+ '--ssid', CONFIG[band]['ssid']]))
+ logger.debug( 'wrote to filename %s', filename)
+ if atomic:
+ logger.debug( 'moving from %s to %s', filename, final_filename)
+ os.rename(filename, final_filename)
+
+
+def enable_access_point(band):
+ logger.debug('Enabling AP for band %s', band)
+ open(access_point_filename(band), 'w')
+
+
+def delete_wlan_config(band):
+ config_filename = wlan_config_filename(band)
+ if os.path.exists(config_filename):
+ logger.debug('Deleting config for band %s', band)
+ os.unlink(config_filename)
+
+
+def disable_access_point(band):
+ ap_filename = access_point_filename(band)
+ if os.path.isfile(ap_filename):
+ logger.debug('Disabling AP for band %s', band)
+ os.unlink(ap_filename)
+
+
+def write_acscontact():
+ logger.debug('ACS session started')
+ open(os.path.join(CWMP_PATH, 'acscontact'), 'w')
+
+
+def write_acsconnected():
+ logger.debug('ACS session completed')
+ open(os.path.join(CWMP_PATH, 'acsconnected'), 'w')
+
+
+def mock(band, access_point=None, delete_config=False, ssid=None, psk=None,
+ write_now=False, acs_session_fails=None):
+ """Mock the config written by catawampus.
+
+ Args:
+ band: The band for which things are being mocked.
+ access_point: Set to True or False to enable/disable the AP.
+ delete_config: Set to True to delete the config.
+ ssid: If updating config, the ssid to use. psk must also be set.
+ psk: If updating config, the psk to use. ssid must also be set.
+ write_now: If updating config, write it immediately.
+
+ Raises:
+ ValueError: If invalid values are specified.
+ """
+ if acs_session_fails is not None:
+ global ACS_SESSION_FAILS
+ ACS_SESSION_FAILS = acs_session_fails
+
+ if access_point is not None:
+ if access_point not in (True, False):
+ raise ValueError('access_point should only be mocked as True/False')
+ ACCESS_POINT[band] = access_point
+ logger.debug('AP mocked %s', access_point)
+
+ if delete_config:
+ logger.debug('Config mock removed for band %s', band)
+ CONFIG[band] = None
+ elif ssid and psk:
+ logger.debug('Config mock updated for band %s', band)
+ CONFIG[band] = {'ssid': ssid, 'psk': psk}
+ elif ssid or psk:
+ raise ValueError('Cannot set only one of ssid (%r) and psk (%r).',
+ ssid, psk)
+
+ if write_now:
+ call('wakeup', env={'write_now_testonly': True})
+
+
diff --git a/conman/test/fake_python/subprocess/get_quantenna_interfaces.py b/conman/test/fake_python/subprocess/get_quantenna_interfaces.py
new file mode 100644
index 0000000..7316139
--- /dev/null
+++ b/conman/test/fake_python/subprocess/get_quantenna_interfaces.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python -S
+
+"""Fake get-quantenna-interfaces implementation."""
+
+_INTERFACES = []
+
+
+def call(*unused_args, **unused_kwargs):
+ return 0, '\n'.join(_INTERFACES)
+
+
+def mock(interfaces):
+ global _INTERFACES
+ _INTERFACES = list(interfaces)
diff --git a/conman/test/fake_python/subprocess/ifdown.py b/conman/test/fake_python/subprocess/ifdown.py
new file mode 100644
index 0000000..0677e4f
--- /dev/null
+++ b/conman/test/fake_python/subprocess/ifdown.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+"""Fake ifdown implementation."""
+
+import ifup
+
+
+def call(interface):
+ ifup.INTERFACE_STATE[interface] = False
+ return 0, ''
diff --git a/conman/test/fake_python/subprocess/ifplugd_action.py b/conman/test/fake_python/subprocess/ifplugd_action.py
new file mode 100644
index 0000000..ab6a97a
--- /dev/null
+++ b/conman/test/fake_python/subprocess/ifplugd_action.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+"""Fake ifplugd.action implementation."""
+
+import os
+
+import run_dhclient
+
+CONMAN_PATH = None
+
+
+def call(interface, state):
+ if CONMAN_PATH is None:
+ raise ValueError('Need to set subprocess.ifplugd_action.CONMAN_PATH')
+
+ if state not in ('up', 'down'):
+ raise ValueError('state should be "up" or "down"')
+
+ status_file = os.path.join(CONMAN_PATH, 'interfaces', interface)
+ with open(status_file, 'w') as f:
+ # This value doesn't matter to conman, so it's fine to hard code it here.
+ f.write('1' if state == 'up' else '0')
+
+ # ifplugd.action calls run-dhclient.
+ run_dhclient.call('br0' if interface in ('eth0', 'moca0') else interface)
+
+ return 0, ''
diff --git a/conman/test/fake_python/subprocess/ifup.py b/conman/test/fake_python/subprocess/ifup.py
new file mode 100644
index 0000000..7669555
--- /dev/null
+++ b/conman/test/fake_python/subprocess/ifup.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+"""Fake ifup implementation."""
+
+INTERFACE_STATE = {}
+
+
+def call(interface):
+ INTERFACE_STATE[interface] = True
+ return 0, ''
diff --git a/conman/test/fake_python/subprocess/ip.py b/conman/test/fake_python/subprocess/ip.py
new file mode 100644
index 0000000..d8baaf3
--- /dev/null
+++ b/conman/test/fake_python/subprocess/ip.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+
+"""Fake ip route implementation."""
+
+import logging
+import socket
+import struct
+
+import ifup
+
+
+_ROUTING_TABLE = {}
+_IP_TABLE = {}
+
+
+def call(subcommand, *args):
+ """Fake ip command."""
+ subcommands = {
+ 'route': _ip_route,
+ 'addr': _ip_addr,
+ 'link': _link,
+ }
+
+ if subcommand not in subcommands:
+ return 1, 'ip subcommand %r not supported' % subcommand
+
+ return subcommands[subcommand](args)
+
+
+def register_testonly(interface):
+ if interface not in _IP_TABLE:
+ _IP_TABLE[interface] = set()
+
+
+def _ip_route(args):
+ def can_add_route(dev):
+ def ip_to_int(ip_addr):
+ return struct.unpack('!I', socket.inet_pton(socket.AF_INET, ip_addr))[0]
+
+ if args[1] != 'default':
+ return True
+
+ via = ip_to_int(args[args.index('via') + 1])
+ for (ifc, route, _), _ in _ROUTING_TABLE.iteritems():
+ if ifc != dev:
+ continue
+
+ netmask = 0
+ if '/' in route:
+ route, netmask = route.split('/')
+ netmask = 32 - int(netmask)
+ route = ip_to_int(route)
+
+ if (route >> netmask) == (via >> netmask):
+ return True
+
+ return False
+
+ if not args:
+ return 0, '\n'.join(_ROUTING_TABLE.values())
+
+ if 'dev' not in args:
+ raise Exception('fake ip route got no dev')
+
+ dev = args[args.index('dev') + 1]
+
+ metric = None
+ if 'metric' in args:
+ metric = args[args.index('metric') + 1]
+ if args[0] in ('add', 'del'):
+ route = args[1]
+ key = (dev, route, metric)
+ if args[0] == 'add' and key not in _ROUTING_TABLE:
+ if not can_add_route(dev):
+ return (1, 'Tried to add default route without subnet route: %r' %
+ _ROUTING_TABLE)
+ logging.debug('Adding route for %r', key)
+ _ROUTING_TABLE[key] = ' '.join(args[1:])
+ elif args[0] == 'del':
+ if key in _ROUTING_TABLE:
+ logging.debug('Deleting route for %r', key)
+ del _ROUTING_TABLE[key]
+ elif key[2] is None:
+ # pylint: disable=g-builtin-op
+ for k in _ROUTING_TABLE.keys():
+ if k[:-1] == key[:-1]:
+ logging.debug('Deleting route for %r (generalized from %s)', k, key)
+ del _ROUTING_TABLE[k]
+ break
+
+ return 0, ''
+
+
+# pylint: disable=line-too-long
+_IP_ADDR_SHOW_TPL = """4: {name}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
+ link/ether fe:fb:01:80:1b:74 brd ff:ff:ff:ff:ff:ff
+{ips}
+"""
+
+_IP_ADDR_SHOW_IP_TPL = """ inet {ip}/24 brd 100.100.255.255 scope global {name}
+ valid_lft forever preferred_lft forever
+"""
+
+
+def _ip_addr(args):
+ if 'dev' not in args:
+ raise Exception('fake ip addr show got no dev')
+
+ dev = args[args.index('dev') + 1]
+ if dev not in _IP_TABLE:
+ return 255, 'Device "%r" does not exist' % dev
+
+ if 'show' in args:
+ ips = '\n'.join(_IP_ADDR_SHOW_IP_TPL.format(name=dev, ip=addr)
+ for addr in _IP_TABLE[dev])
+ return 0, _IP_ADDR_SHOW_TPL.format(name=dev, ips=ips)
+
+ if 'add' in args:
+ add = args[args.index('add') + 1]
+ _IP_TABLE[dev].add(add)
+ return 0, ''
+
+ if 'del' in args:
+ remove = args[args.index('del') + 1]
+ if remove in _IP_TABLE[dev]:
+ _IP_TABLE[dev].remove(remove)
+ return 0, ''
+ return 254, 'RTNETLINK answers: Cannot assign requested address'
+
+ raise Exception('no recognized ip addr command in %r' % args)
+
+
+def _link(args):
+ return 0, '\n'.join('%s LOWER_UP' % interface
+ for interface, state in ifup.INTERFACE_STATE.iteritems()
+ if state)
diff --git a/conman/test/fake_python/subprocess/qcsapi.py b/conman/test/fake_python/subprocess/qcsapi.py
new file mode 100644
index 0000000..3625772
--- /dev/null
+++ b/conman/test/fake_python/subprocess/qcsapi.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python -S
+
+"""Fake QCSAPI implementation."""
+
+
+STATE = {}
+
+
+def call(*args):
+ if args not in STATE:
+ return 1, 'No mocked value for args %r' % (args,)
+
+ return 0, STATE[args]
+
+
+def mock(*args, **kwargs):
+ import logging
+ if 'value' not in kwargs:
+ raise ValueError('Must specify value for mock qcsapi call %r' % args)
+ value = kwargs['value']
+ logging.debug ('qcsapi %r mocked: %r', args, value)
+ if value is None and args in STATE:
+ del STATE[args]
+ else:
+ STATE[args] = value
diff --git a/conman/test/fake_python/subprocess/register_experiment.py b/conman/test/fake_python/subprocess/register_experiment.py
new file mode 100644
index 0000000..a2dab49
--- /dev/null
+++ b/conman/test/fake_python/subprocess/register_experiment.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+
+"""Fake register_experiment implementation."""
+
+
+REGISTERED_EXPERIMENTS = set()
+
+
+def call(experiment):
+ REGISTERED_EXPERIMENTS.add(experiment)
+ return 0, ''
diff --git a/conman/test/fake_python/subprocess/run_dhclient.py b/conman/test/fake_python/subprocess/run_dhclient.py
new file mode 100644
index 0000000..a1bffb3
--- /dev/null
+++ b/conman/test/fake_python/subprocess/run_dhclient.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+"""Fake run-dhclient implementation."""
+
+import os
+
+
+CONMAN_PATH = None
+_FAILURE = {}
+
+
+def mock(interface, failure=False):
+ _FAILURE[interface] = failure
+
+
+def call(interface):
+ if CONMAN_PATH is None:
+ raise ValueError('Need to set subprocess.ifplugd_action.CONMAN_PATH')
+
+ if not _FAILURE.get(interface, False):
+ _write_subnet_file(interface)
+ _write_gateway_file(interface)
+
+
+def _write_gateway_file(interface):
+ gateway_file = os.path.join(CONMAN_PATH, 'gateway.' + interface)
+ with open(gateway_file, 'w') as f:
+ # This value doesn't matter to conman, so it's fine to hard code it here.
+ f.write('192.168.1.1')
+
+
+def _write_subnet_file(interface):
+ subnet_file = os.path.join(CONMAN_PATH, 'subnet.' + interface)
+ with open(subnet_file, 'w') as f:
+ # This value doesn't matter to conman, so it's fine to hard code it here.
+ f.write('192.168.1.0/24')
diff --git a/conman/test/fake_python/subprocess/upload_logs_and_wait.py b/conman/test/fake_python/subprocess/upload_logs_and_wait.py
new file mode 100644
index 0000000..6c45f87
--- /dev/null
+++ b/conman/test/fake_python/subprocess/upload_logs_and_wait.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+"""Fake upload-logs-and-wait implementation."""
+
+UPLOADED = False
+
+
+def call():
+ global UPLOADED
+ UPLOADED = True
+ return 0, ''
+
+
+def uploaded_logs():
+ global UPLOADED
+ result = UPLOADED
+ UPLOADED = False
+ return result
diff --git a/conman/test/fake_python/subprocess/wifi.py b/conman/test/fake_python/subprocess/wifi.py
new file mode 100644
index 0000000..13d1be3
--- /dev/null
+++ b/conman/test/fake_python/subprocess/wifi.py
@@ -0,0 +1,323 @@
+#!/usr/bin/python
+
+"""Fake /bin/wifi implementation."""
+
+import collections
+import os
+import random
+
+import connection_check
+import get_quantenna_interfaces
+import ifplugd_action
+import ifup
+import qcsapi
+import wpa_cli
+
+
+MockInterface = collections.namedtuple('MockInterface',
+ ['phynum', 'bands', 'driver'])
+
+
+# A randomly selceted wifi scan result with the interesting stuff templated.
+WIFI_SCAN_TPL = '''BSS {bssid}(on wcli0)
+ TSF: 1269828266773 usec (14d, 16:43:48)
+ freq: {freq}
+ beacon interval: 100 TUs
+ capability: ESS Privacy ShortSlotTime (0x0411)
+ signal: {rssi}
+ last seen: 2190 ms ago
+ Information elements from Probe Response frame:
+ {vendor_ies}
+ SSID: {ssid}
+ Supported rates: 1.0* 2.0* 5.5* 11.0* 18.0 24.0 36.0 54.0
+ DS Parameter set: channel 6
+ ERP: <no flags>
+ ERP D4.0: <no flags>
+ {security}
+ Extended supported rates: 6.0 9.0 12.0 48.0
+'''
+
+VENDOR_IE_TPL = ' Vendor specific: OUI {oui}, data: {data}'
+
+
+WIFI_SHOW_TPL = '''Band: {band}
+RegDomain: US
+Interface: wlan{phynum} # {band} GHz ap
+BSSID: f4:f5:e8:81:1b:a0
+AutoChannel: True
+AutoType: NONDFS
+Station List for band: {band}
+
+Client Interface: wcli{phynum} # {band} GHz client
+Client BSSID: f4:f5:e8:81:1b:a1
+'''
+
+WIFI_SHOW_NO_RADIO_TPL = '''Band: {band}
+RegDomain: 00
+'''
+
+WPA_PATH = None
+REMOTE_ACCESS_POINTS = collections.defaultdict(dict)
+INTERFACE_FOR_BAND = collections.defaultdict(lambda: None)
+INTERFACE_EVENTS = collections.defaultdict(list)
+LOCAL_ACCESS_POINTS = {}
+CLIENT_ASSOCIATIONS = {}
+
+
+class AccessPoint(object):
+
+ def __init__(self, **kwargs):
+ for attr in ('ssid', 'psk', 'band', 'bssid', 'security', 'rssi',
+ 'vendor_ies', 'connection_check_result', 'hidden'):
+ setattr(self, attr, kwargs.get(attr, None))
+
+ def scan_str(self):
+ security_strs = {
+ 'WEP': ' Privacy: WEP',
+ 'WPA': ' WPA:',
+ 'WPA2': ' RSN: * Version: 1',
+ }
+ return WIFI_SCAN_TPL.format(
+ ssid=self.ssid if not self.hidden else '',
+ freq='2437' if self.band == '2.4' else '5160',
+ bssid=self.bssid,
+ vendor_ies='\n'.join(VENDOR_IE_TPL.format(oui=oui, data=data)
+ for oui, data in (self.vendor_ies or [])),
+ rssi='%.2f dBm' % (self.rssi or 0),
+ security=security_strs.get(self.security, ''))
+
+
+def call(*args, **kwargs):
+ wifi_commands = {
+ 'scan': _scan,
+ 'set': _set,
+ 'stopap': _stopap,
+ 'setclient': _setclient,
+ 'stopclient': _stopclient,
+ 'stop': _stop,
+ 'show': _show,
+ }
+
+ if WPA_PATH is None and args[0].endswith('client'):
+ raise ValueError('Set subprocess.wifi.WPA_PATH before calling a fake '
+ '"wifi *client" command')
+
+ if args[0] in wifi_commands:
+ return wifi_commands[args[0]](args[1:], env=kwargs.get('env', {}))
+
+ return 99, 'unrecognized command %s' % args[0]
+
+
+def _set(args, env=None):
+ band = _get_flag(args, ('-b', '--band'))
+ LOCAL_ACCESS_POINTS[band] = args, env
+ return 0, ''
+
+
+def _stopap(args, env=None):
+ bands = _get_flag(args, ('-b', '--band')) or '2.4 5'
+ for band in bands.split():
+ if band in LOCAL_ACCESS_POINTS:
+ del LOCAL_ACCESS_POINTS[band]
+
+ return 0, ''
+
+
+def _setclient(args, env=None):
+ env = env or {}
+
+ band = _get_flag(args, ('-b', '--band'))
+ bssid = _get_flag(args, ('--bssid',))
+ ssid = _get_flag(args, ('S', '--ssid',))
+
+ if band not in INTERFACE_FOR_BAND:
+ raise ValueError('No interface for band %r' % band)
+
+ interface = INTERFACE_FOR_BAND[band]
+ interface_name = 'wcli%s' % interface.phynum
+
+ if bssid:
+ ap = REMOTE_ACCESS_POINTS[band].get(bssid, None)
+ if not ap or ap.ssid != ssid:
+ _setclient_error_not_found(interface_name, ssid, interface.driver)
+ return 1, ('AP with band %r and BSSID %r and ssid %s not found'
+ % (band, bssid, ssid))
+ elif ssid:
+ candidates = [ap for ap in REMOTE_ACCESS_POINTS[band].itervalues()
+ if ap.ssid == ssid]
+ if not candidates:
+ _setclient_error_not_found(interface_name, ssid, interface.driver)
+ return 1, 'AP with SSID %r not found' % ssid
+ ap = random.choice(candidates)
+ else:
+ raise ValueError('Did not specify BSSID or SSID in %r' % args)
+
+ psk = env.get('WIFI_CLIENT_PSK', None)
+ if psk != ap.psk:
+ _setclient_error_auth(interface_name, ssid, interface.driver)
+ return 1, 'Wrong PSK, got %r, expected %r' % (psk, ap.psk)
+
+ _setclient_success(interface_name, ssid, bssid, psk, interface.driver, ap,
+ band)
+
+ return 0, ''
+
+
+def _setclient_error_not_found(interface_name, ssid, driver):
+ if driver == 'cfg80211':
+ wpa_cli.mock(interface_name, wpa_state='SCANNING')
+ elif driver == 'frenzy':
+ qcsapi.mock('get_mode', 'wifi0', value='Station')
+ qcsapi.mock('get_ssid', 'wifi0', value='')
+ qcsapi.mock('ssid_get_authentication_mode', 'wifi0', ssid, value='')
+ qcsapi.mock('get_status', 'wifi0', value='Error')
+
+ CLIENT_ASSOCIATIONS[interface_name] = None
+
+
+def _setclient_error_auth(interface_name, ssid, driver):
+ if driver == 'cfg80211':
+ # This is what our version of wpa_supplicant does for auth failures.
+ INTERFACE_EVENTS[interface_name].append('<2>CTRL-EVENT-SSID-TEMP-DISABLED')
+ wpa_cli.mock(interface_name, wpa_state='SCANNING')
+ elif driver == 'frenzy':
+ qcsapi.mock('get_mode', 'wifi0', value='Station')
+ qcsapi.mock('get_ssid', 'wifi0', value='')
+ qcsapi.mock('ssid_get_authentication_mode', 'wifi0', ssid, value='')
+ qcsapi.mock('get_status', 'wifi0', value='Error')
+
+ CLIENT_ASSOCIATIONS[interface_name] = None
+
+
+def _setclient_success(interface_name, ssid, bssid, psk, driver, ap, band):
+ if CLIENT_ASSOCIATIONS.get(interface_name, None):
+ _disconnected_event(band)
+ if driver == 'cfg80211':
+ # Make sure the wpa_supplicant socket exists.
+ open(os.path.join(WPA_PATH, interface_name), 'w')
+
+ # Tell wpa_cli what to return.
+ key_mgmt = 'WPA2-PSK' if psk else 'NONE'
+ wpa_cli.mock(interface_name, wpa_state='COMPLETED', ssid=ssid, bssid=bssid,
+ key_mgmt=key_mgmt)
+
+ # Send the CONNECTED event.
+ INTERFACE_EVENTS[interface_name].append('<2>CTRL-EVENT-CONNECTED')
+
+ elif driver == 'frenzy':
+ qcsapi.mock('get_mode', 'wifi0', value='Station')
+ qcsapi.mock('get_ssid', 'wifi0', value=ssid)
+ qcsapi.mock('ssid_get_authentication_mode', 'wifi0', ssid,
+ value='PSKAuthentication' if psk else 'NONE')
+ qcsapi.mock('get_status', 'wifi0', value='')
+
+ CLIENT_ASSOCIATIONS[interface_name] = ap
+ connection_check.mock(interface_name, ap.connection_check_result or 'succeed')
+
+ # Call ifplugd.action for the interface coming up (wifi/quantenna.py does this
+ # manually).
+ ifplugd_action.call(interface_name, 'up')
+
+
+def _disconnected_event(band):
+ interface = INTERFACE_FOR_BAND[band]
+ interface_name = 'wcli%s' % interface.phynum
+ if interface.driver == 'cfg80211':
+ INTERFACE_EVENTS[interface_name].append('<2>CTRL-EVENT-DISCONNECTED')
+ wpa_cli.mock(interface_name, wpa_state='SCANNING')
+ else:
+ qcsapi.mock('get_ssid', 'wifi0', value='')
+ qcsapi.mock('get_status', 'wifi0', value='Error')
+
+ CLIENT_ASSOCIATIONS[interface_name] = None
+
+
+def _stopclient(args, env=None):
+ bands = _get_flag(args, ('-b', '--band')) or '2.4 5'
+ for band in bands.split():
+ interface = INTERFACE_FOR_BAND[band]
+ interface_name = 'wcli%s' % interface.phynum
+
+ if interface.driver == 'cfg80211':
+ # Send the DISCONNECTED and TERMINATING events.
+ INTERFACE_EVENTS[interface_name].append('<2>CTRL-EVENT-DISCONNECTED')
+ INTERFACE_EVENTS[interface_name].append('<2>CTRL-EVENT-TERMINATING')
+
+ # Clear the wpa_cli status response.
+ wpa_cli.mock(interface_name)
+
+ # Make sure the wpa_supplicant socket does not.
+ if os.path.exists(os.path.join(WPA_PATH, interface_name)):
+ os.unlink(os.path.join(WPA_PATH, interface_name))
+
+ elif interface.driver == 'frenzy':
+ qcsapi.mock('get_ssid', 'wifi0', value='')
+ qcsapi.mock('get_status', 'wifi0', value='Error')
+
+ CLIENT_ASSOCIATIONS[interface_name] = None
+
+ # Call ifplugd.action for the interface going down (wifi/quantenna.py does this
+ # manually).
+ ifplugd_action.call(interface_name, 'down')
+
+ return 0, ''
+
+
+def _stop(*args, **kwargs):
+ _stopap(*args, **kwargs)
+ _stopclient(*args, **kwargs)
+ return 0, ''
+
+
+def _kill_wpa_supplicant(band):
+ # From conman's perspective, there's no difference between someone running
+ # 'wifi stopclient' and the process dying for some other reason.
+ _stopclient(['--band', band])
+
+
+def _scan(args, **unused_kwargs):
+ band_flag = _get_flag(args, ('-b', '--band'))
+ interface = INTERFACE_FOR_BAND[band_flag]
+ interface_name = 'wcli%s' % interface.phynum
+ if not ifup.INTERFACE_STATE.get(interface_name, False):
+ return 1, 'interface down'
+
+ return 0, '\n'.join(ap.scan_str()
+ for band in interface.bands
+ for ap in REMOTE_ACCESS_POINTS[band].itervalues())
+
+
+def _show(unused_args, **unused_kwargs):
+ return 0, '\n\n'.join(WIFI_SHOW_TPL.format(band=band, **interface._asdict()) if interface
+ else WIFI_SHOW_NO_RADIO_TPL.format(band)
+ for band, interface in INTERFACE_FOR_BAND.iteritems())
+
+
+def _get_flag(args, flags):
+ for flag in flags:
+ if flag in args:
+ return args[args.index(flag) + 1]
+
+
+def mock(command, *args, **kwargs):
+ if command == 'remote_ap':
+ remote_ap = AccessPoint(**kwargs)
+ REMOTE_ACCESS_POINTS[kwargs['band']][kwargs['bssid']] = remote_ap
+ elif command == 'remote_ap_remove':
+ del REMOTE_ACCESS_POINTS[kwargs['band']][kwargs['bssid']]
+ elif command == 'interfaces':
+ INTERFACE_FOR_BAND.clear()
+ for interface in args:
+ for band in interface.bands:
+ INTERFACE_FOR_BAND[band] = interface
+ if interface.driver == 'frenzy':
+ get_quantenna_interfaces.mock(
+ fmt % interface.phynum
+ for fmt in ('wlan%s', 'wlan%s_portal', 'wcli%s'))
+ elif command == 'wpa_path':
+ global WPA_PATH
+ WPA_PATH = args[0]
+ elif command == 'disconnected_event':
+ _disconnected_event(args[0])
+ elif command == 'kill_wpa_supplicant':
+ _kill_wpa_supplicant(args[0])
diff --git a/conman/test/fake_python/subprocess/wpa_cli.py b/conman/test/fake_python/subprocess/wpa_cli.py
new file mode 100644
index 0000000..c1849a9
--- /dev/null
+++ b/conman/test/fake_python/subprocess/wpa_cli.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+"""Fake wpa_cli implementation. Used by fake WPACtrl too."""
+
+import ifdown
+import ifup
+
+
+_INTERFACE_STATE = {}
+
+
+def call(*args, **unused_kwargs):
+ if 'status' not in args:
+ raise ValueError('Fake wpa_cli can only do status requests.')
+
+ if '-i' not in args:
+ raise ValueError('Must specify interface with -i.')
+
+ interface = args[args.index('-i') + 1]
+
+ # Fails for not present or empty dict.
+ if not _INTERFACE_STATE.get(interface, None):
+ return 1, ('Failed to connect to non-global ctrl_ifname: %r '
+ 'error: No such file or directory' % interface)
+
+ state = _INTERFACE_STATE[interface]
+
+ return 0, '\n'.join('%s=%s' % (k, v) for k, v in state.iteritems())
+
+
+# Pass no kwargs to "kill" wpa_supplicant.
+def mock(interface, **kwargs):
+ _INTERFACE_STATE[interface] = {k: v for k, v in kwargs.iteritems() if v}
+ if kwargs:
+ ifup.call(interface)
+ else:
+ ifdown.call(interface)
diff --git a/conman/test/fake_python/wpactrl.py b/conman/test/fake_python/wpactrl.py
new file mode 100644
index 0000000..3d8e300
--- /dev/null
+++ b/conman/test/fake_python/wpactrl.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+"""Fake WPACtrl implementation."""
+
+import os
+
+import subprocess
+import subprocess.wifi
+
+
+CONNECTED_EVENT = '<2>CTRL-EVENT-CONNECTED'
+DISCONNECTED_EVENT = '<2>CTRL-EVENT-DISCONNECTED'
+TERMINATING_EVENT = '<2>CTRL-EVENT-TERMINATING'
+
+
+# pylint: disable=invalid-name
+class error(Exception):
+ pass
+
+
+class WPACtrl(object):
+ """Fake wpactrl.WPACtrl."""
+
+ # pylint: disable=unused-argument
+ def __init__(self, wpa_socket):
+ self._socket = wpa_socket
+ self.interface_name = os.path.split(self._socket)[-1]
+ self.attached = False
+ self.connected = False
+ self.request_status_fails = False
+ self._clear_events()
+
+ def pending(self):
+ return bool(subprocess.wifi.INTERFACE_EVENTS[self.interface_name])
+
+ def recv(self):
+ return subprocess.wifi.INTERFACE_EVENTS[self.interface_name].pop(0)
+
+ def attach(self):
+ if not os.path.exists(self._socket):
+ raise error('wpactrl_attach failed')
+ self.attached = True
+
+ def detach(self):
+ self.attached = False
+ self.connected = False
+ self.check_socket_exists('wpactrl_detach failed')
+ self._clear_events()
+
+ def request(self, request_type):
+ if request_type == 'STATUS':
+ if self.request_status_fails:
+ raise error('test error')
+ try:
+ return subprocess.check_output(['wpa_cli', '-i', self.interface_name,
+ 'status'])
+ except subprocess.CalledProcessError as e:
+ raise error(e.output)
+ else:
+ raise ValueError('Invalid request_type %s' % request_type)
+
+ @property
+ def ctrl_iface_path(self):
+ return os.path.split(self._socket)[0]
+
+ # Below methods are not part of WPACtrl.
+
+ def check_socket_exists(self, msg='Socket does not exist'):
+ if not os.path.exists(self._socket):
+ raise error(msg)
+
+ def _clear_events(self):
+ subprocess.wifi.INTERFACE_EVENTS[self.interface_name] = []
diff --git a/conman/test/fake_wpactrl/wpactrl/__init__.py b/conman/test/fake_wpactrl/wpactrl/__init__.py
deleted file mode 100644
index b8ce1fd..0000000
--- a/conman/test/fake_wpactrl/wpactrl/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-class error(Exception):
- pass
-
-class WPACtrl(object):
- pass
diff --git a/conman/test/restricted b/conman/test/restricted
deleted file mode 100755
index 9bd8fc3..0000000
--- a/conman/test/restricted
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-echo "$@" | grep -q -- "-a"
-
diff --git a/conman/test/succeed b/conman/test/succeed
deleted file mode 100755
index c52d3c2..0000000
--- a/conman/test/succeed
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-exit 0
diff --git a/diags/chameleon/sfp.c b/diags/chameleon/sfp.c
index e17e02d..1974d27 100644
--- a/diags/chameleon/sfp.c
+++ b/diags/chameleon/sfp.c
@@ -175,7 +175,7 @@
temp = value[0] + ((float)value[1]) / 256.0;
}
vcc = ((float)((value[2] << 8) + value[3])) / 10000.0;
- tx_bias = ((float)((value[4] << 8) + value[5])) / 1000.0;
+ tx_bias = (((float)((value[4] << 8) + value[5])) * 2) / 1000.0;
tx_power = ((float)((value[6] << 8) + value[7])) / 10000.0;
rx_power = ((float)((value[8] << 8) + value[9])) / 10000.0;
mod_curr = ((float)((value[12] << 8) + value[13])) / 1000.0;
diff --git a/ginstall/ginstall.py b/ginstall/ginstall.py
index f0dfe88..67f196a 100755
--- a/ginstall/ginstall.py
+++ b/ginstall/ginstall.py
@@ -1092,7 +1092,19 @@
uloader = loader = android_bsu = None
uloadersig = FileWithSecureHash(StringIO.StringIO(''), 'badsig')
- loadersig = FileWithSecureHash(StringIO.StringIO(''), 'badsig')
+
+ # TODO(cgibson): Modern ginstall images contain a loadersig. However, some
+ # releases, such as 42.33 for the FiberJack, do not have a loadersig. In 42.33
+ # this was okay since cwmp calls ginstall with the '--skiploadersig' flag.
+ # However, in later versions this flag was removed. Now if a new ginstall
+ # were to be used to downgrade to an older ginstall image, the install would
+ # fail. This seems to only affect the FiberJack platform, which is still
+ # running 42.33. This can safely be removed once all FiberJacks have been
+ # upgraded to gfiber-47 and are not anticipated to need to be downgraded back
+ # to 42.33.
+ loadersig = None
+ if not GetPlatform().startswith('GFLT'):
+ loadersig = FileWithSecureHash(StringIO.StringIO(''), 'badsig')
for ti in tar:
secure_hash = manifest.get('%s-sha1' % ti.name)
diff --git a/taxonomy/dhcp.py b/taxonomy/dhcp.py
index 26ecb82..e415126 100644
--- a/taxonomy/dhcp.py
+++ b/taxonomy/dhcp.py
@@ -31,6 +31,7 @@
'1,33,3,6,15,28,51,58,59': ['android'],
'1,3,6,28,33,51,58,59,121': ['android'],
'1,121,33,3,6,15,28,51,58,59,119': ['android'],
+ '1,3,6,15,26,28,51,58,59,43': ['android'],
'1,3,6,15,112,113,78,79,95,252': ['appletv1'],
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index f20c209..295652d 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -53,12 +53,18 @@
'58:67:1a': ['barnes&noble'],
+ '2c:b0:5d': ['dish'],
+
'30:8c:fb': ['dropcam'],
'00:1a:11': ['google'],
+ '3c:5a:b4': ['google'],
'54:60:09': ['google'],
+ '94:95:a0': ['google'],
'94:eb:2c': ['google'],
'a4:77:33': ['google'],
+ 'f4:03:04': ['google'],
+ 'f4:f5:d8': ['google'],
'f4:f5:e8': ['google'],
'f8:8f:ca': ['google'],
diff --git a/taxonomy/pcaptest.py b/taxonomy/pcaptest.py
index 3e2ea1f..116b4d1 100644
--- a/taxonomy/pcaptest.py
+++ b/taxonomy/pcaptest.py
@@ -51,6 +51,8 @@
('', './testdata/pcaps/Samsung Infuse 5GHz.pcap'),
('', './testdata/pcaps/Samsung Vibrant 2.4GHz.pcap'),
('', './testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap'),
+ ('', './testdata/pcaps/Sony NSX-48GT1 2.4GHz Broadcast Probe.pcap'),
+ ('', './testdata/pcaps/Sony NSX-48GT1 2.4GHz Specific Probe.pcap'),
# Names where the identified species doesn't exactly match the filename,
# usually because multiple devices are too similar to distinguish. We name
@@ -65,16 +67,15 @@
('Amazon Kindle', './testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap'),
('iPad 1st or 2nd gen', './testdata/pcaps/iPad 1st gen 5GHz.pcap'),
('iPad 1st or 2nd gen', './testdata/pcaps/iPad 2nd gen 5GHz.pcap'),
- ('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad (4th gen) 5GHz.pcap'),
- ('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad (4th gen) 2.4GHz.pcap'),
- ('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad Air 5GHz.pcap'),
- ('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad Air 2.4GHz.pcap'),
- ('iPhone 6/6+', './testdata/pcaps/iPhone 6 5GHz.pcap'),
- ('iPhone 6/6+', './testdata/pcaps/iPhone 6+ 5GHz.pcap'),
+ ('iPhone 6/6+', './testdata/pcaps/iPhone 6 5GHz iOS 9.pcap'),
+ ('iPhone 6/6+', './testdata/pcaps/iPhone 6+ 5GHz iOS 9.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s 2.4GHz.pcap'),
+ ('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s 5GHz.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 2.4GHz.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 2.4GHz RRM.pcap'),
- ('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s 5GHz.pcap'),
+ ('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s 2.4GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap'),
+ ('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Broadcast Probe.pcap'),
+ ('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Specific Probe.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 5GHz.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 5GHz RRM.pcap'),
('iPod Touch 1st or 2nd gen', './testdata/pcaps/iPod Touch 1st gen 2.4GHz.pcap'),
diff --git a/taxonomy/testdata/dhcp.leases b/taxonomy/testdata/dhcp.leases
index 51d4538..f46e1f1 100644
--- a/taxonomy/testdata/dhcp.leases
+++ b/taxonomy/testdata/dhcp.leases
@@ -12,7 +12,7 @@
1432237016 c8:69:cd:5e:b5:43 192.168.42.5 Apple-TV *
1432237016 6c:29:95:7c:25:fe 192.168.42.6 * *
1432237016 b0:34:95:02:66:83 192.168.42.7 iPaad-4th-gen *
-1432237016 04:69:f8:6b:99:5e 192.168.42.8 iPad-Air-2nd-gen *
+1432237016 04:69:f8:00:00:00 192.168.42.8 iPad-Air-2nd-gen *
1432237016 1c:e6:2b:9b:41:91 192.168.42.9 iPaad-Mini-1st-gen *
1432237016 84:8e:0c:99:48:d5 192.168.42.10 iPaad-Mini-2nd-gen *
1432237016 24:ab:81:e4:74:bc 192.168.42.11 iPhoone-4 *
@@ -22,14 +22,14 @@
1432237016 f0:db:e2:61:db:fa 192.168.42.13 iPhoone-6 *
1432237016 c8:85:50:e9:74:58 192.168.42.14 iPhoone-6+ *
1432237016 00:cd:fe:a7:47:96 192.168.42.15 iPhoone-6s *
-1432237016 68:db:ca:37:10:d8 192.168.42.16 iPhoone-6s+ *
+1432237016 68:db:ca:00:00:00 192.168.42.16 iPhoone-6s+ *
1432237016 00:1d:4f:0f:ee:14 192.168.42.17 iPood-Touch-1 *
1432237016 f0:b4:79:9d:28:0d 192.168.42.18 iPood-Touch-4 *
1432237016 3c:15:c2:d0:1b:0e 192.168.42.19 MacBoookPro2013 *
1432237016 10:2f:6b:ec:78:ff 192.168.42.20 NokiaLumia635 *
1432237016 08:05:81:21:68:57 192.168.42.21 Roku4 *
1432237016 5c:f6:dc:16:6a:17 192.168.42.22 SamsungSmartTV *
-1432237016 6c:40:08:55:76:8a 192.168.42.23 iPhoone-5s *
+1432237016 6c:40:08:00:00:00 192.168.42.23 iPhoone-5s *
1432237016 00:23:12:28:de:6e 192.168.42.24 AppleTV1
1432237016 28:cf:da:24:f4:ab 192.168.42.25 AppleTV2
1432237016 68:64:4b:11:ce:2b 192.168.42.26 AppleTV3A
@@ -69,7 +69,12 @@
1432237016 a4:d1:d2:00:00:00 192.168.42.58 iPaadOldiOS
1432237016 70:48:0f:00:00:00 192.168.42.59 iPadPro12_9
1432237016 6c:c2:17:00:00:00 192.168.42.60 HPPrinter
-1432237016 dc:2b:2a:95:bc:77 192.168.42.61 iPhoone 6s+
+1432237016 dc:2b:2a:00:00:00 192.168.42.61 iPhoone 6s+
1432237016 2c:33:61:00:00:00 192.168.42.62 iPhoone 7
1432237016 58:bd:a3:00:00:00 192.168.42.63 Wii
1432237016 28:0d:fc:00:00:00 192.168.42.64 Playstation 3
+1432237016 2c:1f:23:00:00:00 192.168.42.65 iPaadAir2ndGen
+1432237016 e0:b5:2d:00:00:00 192.168.42.66 iPhoone-6+
+1432237016 6c:72:e7:00:00:00 192.168.42.67 iPhoone-6s
+1432237016 f0:db:e2:00:00:00 192.168.42.68 iPhoone-6
+1432237016 b8:53:ac:00:00:00 192.168.42.67 iPhoone-7
diff --git a/taxonomy/testdata/dhcp.signatures b/taxonomy/testdata/dhcp.signatures
index 614f33d..5afd0d6 100644
--- a/taxonomy/testdata/dhcp.signatures
+++ b/taxonomy/testdata/dhcp.signatures
@@ -4,7 +4,7 @@
c8:69:cd:5e:b5:43 1,121,3,6,15,119,252
6c:29:95:7c:25:fe 1,121,33,3,6,12,15,26,28,51,54,58,59,119,252
b0:34:95:02:66:83 1,3,6,15,119,252
-04:69:f8:6b:99:5e 1,3,6,15,119,252
+04:69:f8:00:00:00 1,3,6,15,119,252
1c:e6:2b:9b:41:91 1,3,6,15,119,252
84:8e:0c:99:48:d5 1,3,6,15,119,252
24:ab:81:e4:74:bc 1,3,6,15,119,252
@@ -14,14 +14,14 @@
f0:db:e2:61:db:fa 1,3,6,15,119,252
c8:85:50:e9:74:58 1,3,6,15,119,252
00:cd:fe:a7:47:96 1,3,6,15,119,252
-68:db:ca:37:10:d8 1,3,6,15,119,252
+68:db:ca:00:00:00 1,3,6,15,119,252
00:1d:4f:0f:ee:14 1,3,6,15,119,95,252,44,46,47
f0:b4:79:9d:28:0d 1,3,6,15,119,252
3c:15:c2:d0:1b:0e 1,3,6,15,119,95,252,44,46
10:2f:6b:ec:78:ff 1,15,3,6,44,46,47,31,33,121,249,252,43
08:05:81:21:68:57 1,3,6,15,12
5c:f6:dc:16:6a:17 1,3,6,12,15,28,42,125
-6c:40:08:55:76:8a 1,3,6,15,119,252
+6c:40:08:00:00:00 1,3,6,15,119,252
00:23:12:28:de:6e 1,3,6,15,112,113,78,79,95,252
28:cf:da:24:f4:ab 1,3,6,15,119,252
68:64:4b:11:ce:2b 1,3,6,15,119,252
@@ -61,7 +61,12 @@
a4:d1:d2:00:00:00 1,3,6,15,119,252
70:48:0f:00:00:00 1,3,6,15,119,252
6c:c2:17:00:00:00 6,3,1,15,66,67,13,44,12,81,252
-dc:2b:2a:95:bc:77 1,3,6,15,119,252
+dc:2b:2a:00:00:00 1,3,6,15,119,252
2c:33:61:00:00:00 1,3,6,15,119,252
58:bd:a3:00:00:00 1,3,6,15,28,33
28:0d:fc:00:00:00 1,3,15,6
+2c:1f:23:00:00:00 1,121,3,6,15,119,252
+e0:b5:2d:00:00:00 1,121,3,6,15,119,252
+6c:72:e7:00:00:00 1,121,3,6,15,119,252
+f0:db:e2:00:00:00 1,121,3,6,15,119,252
+b8:53:ac:00:00:00 1,121,3,6,15,119,252
diff --git a/taxonomy/testdata/pcaps/Chromecast Ultra 2.4GHz.pcap b/taxonomy/testdata/pcaps/Chromecast Ultra 2.4GHz.pcap
new file mode 100644
index 0000000..0cbd07f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Chromecast Ultra 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Chromecast Ultra 5GHz.pcap b/taxonomy/testdata/pcaps/Chromecast Ultra 5GHz.pcap
new file mode 100644
index 0000000..3166217
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Chromecast Ultra 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Chromecast v2 2.4GHz Audio.pcap b/taxonomy/testdata/pcaps/Chromecast v2 2.4GHz Audio.pcap
new file mode 100644
index 0000000..1913498
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Chromecast v2 2.4GHz Audio.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Chromecast v2 5GHz Audio.pcap b/taxonomy/testdata/pcaps/Chromecast v2 5GHz Audio.pcap
new file mode 100644
index 0000000..d0af810
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Chromecast v2 5GHz Audio.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Broadcast Probe.pcap
new file mode 100644
index 0000000..d3c7ea8
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Specific Probe.pcap b/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Specific Probe.pcap
new file mode 100644
index 0000000..3985f98
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Dish Network Receiver 2.4GHz ViP722k Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Google Home 2.4GHz.pcap b/taxonomy/testdata/pcaps/Google Home 2.4GHz.pcap
new file mode 100644
index 0000000..11cfde3
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Google Home 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Google Home 5GHz.pcap b/taxonomy/testdata/pcaps/Google Home 5GHz.pcap
new file mode 100644
index 0000000..bdbecc8
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Google Home 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz XL.pcap b/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz XL.pcap
new file mode 100644
index 0000000..386fa90
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz XL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz.pcap b/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz.pcap
new file mode 100644
index 0000000..3ff791b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Pixel Phone 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Pixel Phone 5GHz XL.pcap b/taxonomy/testdata/pcaps/Pixel Phone 5GHz XL.pcap
new file mode 100644
index 0000000..c07af7b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Pixel Phone 5GHz XL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Pixel Phone 5GHz.pcap b/taxonomy/testdata/pcaps/Pixel Phone 5GHz.pcap
new file mode 100644
index 0000000..4091128
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Pixel Phone 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..dbf2886
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Specific Probe.pcap
new file mode 100644
index 0000000..ca5728d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony NSX-48GT1 2.4GHz Specific Probe.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/iPad \0504th gen\051 5GHz.pcap" "b/taxonomy/testdata/pcaps/iPad \0504th gen\051 5GHz.pcap"
deleted file mode 100644
index f7158b7..0000000
--- "a/taxonomy/testdata/pcaps/iPad \0504th gen\051 5GHz.pcap"
+++ /dev/null
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..a671244
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..72bd72e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap" b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 9 Specific Probe.pcap
similarity index 83%
rename from "taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap"
rename to taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 9 Specific Probe.pcap
index 7eb0924..21a1acc 100644
--- "a/taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap"
+++ b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz ME906LL iOS 9 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPad Air 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/iPad Air 1st gen 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..4aea434
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..b73645e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz ME906LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 5GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPad Air 5GHz.pcap
rename to taxonomy/testdata/pcaps/iPad Air 1st gen 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL iOS 10.0.2.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL iOS 10.0.2.pcap
new file mode 100644
index 0000000..a5a99aa
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL iOS 10.0.2.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL.pcap
new file mode 100644
index 0000000..cf87d66
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MGTX2LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..6426214
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..7fe499a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz.pcap
deleted file mode 100644
index d900fed..0000000
--- a/taxonomy/testdata/pcaps/iPad Air 2nd gen 2.4GHz.pcap
+++ /dev/null
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL iOS 10.0.2.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL iOS 10.0.2.pcap
new file mode 100644
index 0000000..e6ffc18
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL iOS 10.0.2.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL.pcap
new file mode 100644
index 0000000..06513ca
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MGTX2LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..9a22b8a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..a398f73
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz MH1J2LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz.pcap
deleted file mode 100644
index 67ff550..0000000
--- a/taxonomy/testdata/pcaps/iPad Air 2nd gen 5GHz.pcap
+++ /dev/null
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 1st gen 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPad Mini 1st gen 2.4GHz MD528LL.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPad Mini 1st gen 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/iPad Mini 1st gen 2.4GHz MD528LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 1st gen 5GHz.pcap b/taxonomy/testdata/pcaps/iPad Mini 1st gen 5GHz MD528LL.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPad Mini 1st gen 5GHz.pcap
rename to taxonomy/testdata/pcaps/iPad Mini 1st gen 5GHz MD528LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..0a0a5ec
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..d715d41
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5 2.4GHz MD654LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..0e05542
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..4f624a7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5 5GHz MD654LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..097cbc1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..6ae7358
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz.pcap
index 4a966ac..d8f7ed4 100644
--- a/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 5s 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..1ded7bc
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..ac2757b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 5s 5GHz iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 5s 5GHz.pcap b/taxonomy/testdata/pcaps/iPhone 5s 5GHz.pcap
index 23cd7de..15bffe0 100644
--- a/taxonomy/testdata/pcaps/iPhone 5s 5GHz.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 5s 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..7ff9271
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..a592849
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6 2.4GHz MG552LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6 2.4GHz iOS 9.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPhone 6 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/iPhone 6 2.4GHz iOS 9.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6 5GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6 5GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..1d83763
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6 5GHz MG552LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6 5GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6 5GHz iOS 9.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPhone 6 5GHz.pcap
rename to taxonomy/testdata/pcaps/iPhone 6 5GHz iOS 9.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..1ca5f73
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..f5a8f47
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz MGC02LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz iOS 9.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/iPhone 6+ 2.4GHz iOS 9.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6+ 5GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6+ 5GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..a2cee5c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6+ 5GHz MGC02LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6+ 5GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6+ 5GHz iOS 9.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/iPhone 6+ 5GHz.pcap
rename to taxonomy/testdata/pcaps/iPhone 6+ 5GHz iOS 9.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s 2.4GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s 2.4GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..20776c4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s 2.4GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..36cac21
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..e8a9eda
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s 5GHz GHz MKRD2LL iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz RRM.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz RRM.pcap
index 28cbed8..21e2825 100644
--- a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz RRM.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz RRM.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..f2e77cf
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..7e4d2ed
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz.pcap
index 6b0e932..b67817c 100644
--- a/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz RRM.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz RRM.pcap
index 1ba43ad..4da3754 100644
--- a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz RRM.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz RRM.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Broadcast Probe.pcap
new file mode 100644
index 0000000..74364bf
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Specific Probe.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Specific Probe.pcap
new file mode 100644
index 0000000..6cb8b42
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz iOS 10.0.2 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz.pcap b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz.pcap
index fdb82f9..cd9f32f 100644
--- a/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz.pcap
+++ b/taxonomy/testdata/pcaps/iPhone 6s+ 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 7+ 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPhone 7+ 2.4GHz.pcap
new file mode 100644
index 0000000..7e8ed23
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 7+ 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPhone 7+ 5GHz.pcap b/taxonomy/testdata/pcaps/iPhone 7+ 5GHz.pcap
new file mode 100644
index 0000000..dc6971a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPhone 7+ 5GHz.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index 36c92fc..3b497e2 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -176,6 +176,8 @@
('Chromecast', 'v1', '2.4GHz'),
'wifi4|probe:0,1,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000000000040|assoc:0,1,48,127,221(0050f2,2),45,191,htcap:006e,htagg:03,htmcs:000000ff,vhtcap:33c07030,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:0400000000000140|oui:google':
('Chromecast', 'v2', '5GHz'),
+ 'wifi4|probe:0,1,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000000000040|assoc:0,1,48,127,221(0050f2,2),45,191,htcap:006e,htagg:03,htmcs:000000ff,vhtcap:33c07030,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:0100000000000040|oui:google':
+ ('Chromecast', 'v2', '5GHz'),
'wifi4|probe:0,1,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000000000040|assoc:0,1,33,36,48,127,221(0050f2,2),45,191,htcap:006e,htagg:03,htmcs:000000ff,vhtcap:33c07030,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:0400000000000140|oui:google':
('Chromecast', 'v2', '5GHz'),
'wifi4|probe:0,1,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000000000040|assoc:0,1,33,36,48,127,221(0050f2,2),45,191,htcap:006e,htagg:03,htmcs:000000ff,vhtcap:33c07030,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,txpow:1308,extcap:0400000000000140|oui:google':
@@ -183,11 +185,20 @@
'wifi4|probe:0,1,3,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000000000040|assoc:0,1,48,50,127,221(0050f2,2),45,htcap:002c,htagg:03,htmcs:000000ff,extcap:0000000000000140|oui:google':
('Chromecast', 'v2', '2.4GHz'),
+ 'wifi4|probe:0,1,45,50,59,127,191,htcap:0163,htagg:03,htmcs:00000000,vhtcap:33d071b0,vhtrxmcs:0168fffa,vhttxmcs:0168fffa,extcap:040000000100004000|assoc:0,1,48,59,127,221(0050f2,2),45,191,199,htcap:016f,htagg:03,htmcs:0000ffff,vhtcap:33d071b0,vhtrxmcs:009cfffa,vhttxmcs:009cfffa,extcap:050000000000004000|oui:google':
+ ('Chromecast', 'Ultra', '5GHz'),
+ 'wifi4|probe:0,1,3,45,50,59,127,191,htcap:0163,htagg:03,htmcs:00000000,vhtcap:33d071b0,vhtrxmcs:0168fffa,vhttxmcs:0168fffa,extcap:040000000100004000|assoc:0,1,33,48,50,59,70,127,221(0050f2,2),45,199,htcap:012d,htagg:03,htmcs:0000ffff,txpow:1400,extcap:040000000000014000|oui:google':
+ ('Chromecast', 'Ultra', '2.4GHz'),
+
'wifi4|probe:0,1,45,221(001018,2),221(00904c,51),htcap:007c,htagg:1a,htmcs:0000ffff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:007c,htagg:1a,htmcs:0000ffff,txpow:1408|os:directv':
('DirecTV', 'HR44 or HD54', '5GHz'),
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:107c,htagg:1a,htmcs:0000ffff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:107c,htagg:1a,htmcs:0000ffff,txpow:1608|os:directv':
('DirecTV', 'HR44 or HF54', '2.4GHz'),
+ # Noted from a ViP722k, likely matches other models
+ 'wifi4|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:186c,htagg:1a,htmcs:0000ffff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:186c,htagg:1a,htmcs:0000ffff,txpow:1408|oui:dish':
+ ('Dish Network Receiver', '', '2.4GHz'),
+
'wifi4|probe:0,1,45,htcap:106e,htagg:01,htmcs:000000ff|assoc:0,1,45,33,36,48,221(0050f2,2),htcap:106e,htagg:01,htmcs:000000ff,txpow:0e00|oui:dropcam':
('Dropcam', '', '5GHz'),
'wifi4|probe:0,1,50,45,htcap:002c,htagg:01,htmcs:000000ff|assoc:0,1,50,45,48,221(0050f2,2),htcap:002c,htagg:01,htmcs:000000ff|oui:dropcam':
@@ -205,6 +216,11 @@
'wifi4|probe:0,1,50,3,45,221(001018,2),221(00904c,51),htcap:0020,htagg:1a,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff|os:epsonprinter':
('Epson Printer', '', '2.4GHz'),
+ 'wifi4|probe:0,1,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000020000040|assoc:0,1,48,127,221(0050f2,2),45,191,htcap:006e,htagg:03,htmcs:000000ff,vhtcap:33c07030,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:0000000020000040|oui:google':
+ ('Google Home', '', '5GHz'),
+ 'wifi4|probe:0,1,3,45,50,127,191,htcap:0062,htagg:03,htmcs:00000000,vhtcap:33c07030,vhtrxmcs:0124fffc,vhttxmcs:0124fffc,extcap:0000000020000040|assoc:0,1,48,50,127,221(0050f2,2),45,htcap:002c,htagg:03,htmcs:000000ff,extcap:0000000020000040|oui:google':
+ ('Google Home', '', '2.4GHz'),
+
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:100c,htagg:19,htmcs:000000ff|os:hpprinter':
('HP Printer', '', '2.4GHz'),
'wifi4|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:102c,htagg:1b,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:102c,htagg:1b,htmcs:000000ff|os:hpprinter':
@@ -298,17 +314,29 @@
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:0100,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0100,htagg:19,htmcs:000000ff,txpow:150c|os:ios':
('iPad', '3rd gen', '2.4GHz'),
+ # iPad Air 1st gen with iOS 9
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('iPad', '4th gen or Air 1st gen', '5GHz'),
+ ('iPad', 'Air 1st gen', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('iPad', '4th gen or Air 1st gen', '5GHz'),
+ ('iPad', 'Air 1st gen', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000004|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('iPad', '4th gen or Air 1st gen', '5GHz'),
+ ('iPad', 'Air 1st gen', '5GHz'),
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('iPad', '4th gen or Air 1st gen', '2.4GHz'),
+ ('iPad', 'Air 1st gen', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('iPad', '4th gen or Air 1st gen', '2.4GHz'),
+ ('iPad', 'Air 1st gen', '2.4GHz'),
+ # iPad Air 1st gen with iOS 10
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
+ ('iPad', 'Air 1st gen', '5GHz'),
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
+ ('iPad', 'Air 1st gen', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
+ ('iPad', 'Air 1st gen', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
+ ('iPad', 'Air 1st gen', '2.4GHz'),
+
+ # iPad Air 2nd gen with iOS 9. Signatures identical to iPhone 6s, use name to distinguish them.
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|name:ipad':
('iPad', 'Air 2nd gen', '5GHz'),
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|name:ipad':
@@ -328,6 +356,16 @@
'wifi4|probe:0,1,50,3,45,127,107,221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1502,extcap:0000000000000040|os:ios':
('iPad', 'Air 2nd gen', '2.4GHz'),
+ # iPad Air 2nd gen with iOS 10 changed the 5GHz tx power, no longer identical to iPhone 6s.
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPad', 'Air 2nd gen', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPad', 'Air 2nd gen', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0400088400000040|assoc:0,1,50,33,36,48,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1502,extcap:0000000000000040|os:ios':
+ ('iPad', 'Air 2nd gen', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1502,extcap:0000000000000040|os:ios':
+ ('iPad', 'Air 2nd gen', '2.4GHz'),
+
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1807|os:ios':
('iPad Mini', '1st gen', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1807|os:ios':
@@ -377,6 +415,7 @@
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:0100,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0100,htagg:19,htmcs:000000ff|os:ios':
('iPhone 4s', '', '2.4GHz'),
+ # iPhone 5 with iOS 9 and prior.
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1504|os:ios':
('iPhone 5', '', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1504|os:ios':
@@ -386,6 +425,16 @@
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1403|os:ios':
('iPhone 5', '', '2.4GHz'),
+ # iPhone 5 with iOS 10.
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1504|os:ios':
+ ('iPhone 5', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1504|os:ios':
+ ('iPhone 5', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1403|os:ios':
+ ('iPhone 5', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1403|os:ios':
+ ('iPhone 5', '', '2.4GHz'),
+
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
('iPhone 5c', '', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
@@ -405,10 +454,18 @@
('iPhone 5s', '', '5GHz'),
'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000004|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1603|os:ios':
('iPhone 5s', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1603|os:ios':
+ ('iPhone 5s', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0062,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0062,htagg:1a,htmcs:000000ff,txpow:1603|os:ios':
+ ('iPhone 5s', '', '5GHz'),
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
('iPhone 5s', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
('iPhone 5s', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
+ ('iPhone 5s', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0017f2,10),107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),221(0017f2,10),htcap:0020,htagg:1a,htmcs:000000ff,txpow:1805|os:ios':
+ ('iPhone 5s', '', '2.4GHz'),
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:e002,extcap:0400000000000040|os:ios':
('iPhone 6/6+', '', '5GHz'),
@@ -433,6 +490,41 @@
'wifi4|probe:0,1,50,3,45,127,107,221(00904c,51),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:1402,extcap:0000000000000040|os:ios':
('iPhone 6+', '', '2.4GHz'),
+ # iPhone 6 with iOS 10 changed txpow, now distinguishable from iPhone 6+.
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:1202,extcap:0400000000000040|os:ios':
+ ('iPhone 6', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:1202,extcap:0400000000000040|os:ios':
+ ('iPhone 6', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:1302,extcap:0000000000000040|os:ios':
+ ('iPhone 6', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:1302,extcap:0000000000000040|os:ios':
+ ('iPhone 6', '', '2.4GHz'),
+
+ # iPhone 6+ with iOS 10 changed txpow, now distinguishable from iPhone 6.
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPhone 6+', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPhone 6+', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:1402,extcap:0000000000000040|os:ios':
+ ('iPhone 6+', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:1402,extcap:0000000000000040|os:ios':
+ ('iPhone 6+', '', '2.4GHz'),
+
+ # iPhone 6s/6s+ with iOS 10 changed txpow, now distinguishable on 5GHz. 2.4GHz signatures are identical.
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:000000ff,vhtcap:0f815832,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPhone 6s', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:000000ff,vhtcap:0f815832,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1302,extcap:0400000000000040|os:ios':
+ ('iPhone 6s', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:000000ff,vhtcap:0f815832,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:0400000000000040|os:ios':
+ ('iPhone 6s+', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:000000ff,vhtcap:0f815832,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:0400000000000040|os:ios':
+ ('iPhone 6s+', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1202,extcap:0000000000000040|os:ios':
+ ('iPhone 6s/6s+', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:000000ff,extcap:0400088400000040|assoc:0,1,50,33,36,48,70,45,127,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1202,extcap:0000000000000040|os:ios':
+ ('iPhone 6s/6s+', '', '2.4GHz'),
+
+ # iOS 9 and earlier signature is identical between iPhone 6s and 6s+
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
('iPhone 6s/6s+', '', '5GHz'),
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
@@ -472,6 +564,10 @@
'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:000000ff,vhtcap:0f807032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000884|assoc:0,1,33,36,48,70,54,45,127,191,199,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f811032,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:13f9,extcap:000008|os:ios':
('iPhone 7', '', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f817032,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00000884|assoc:0,1,33,36,48,45,191,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f817032,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:13f9|os:ios':
+ ('iPhone 7+', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(0017f2,10),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:000000ff,extcap:00000884|assoc:0,1,50,33,36,48,70,45,221(0017f2,10),221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:13f9|os:ios':
+ ('iPhone 7+', '', '2.4GHz'),
'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0400088400000040|assoc:0,1,33,36,45,127,221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,txpow:e002,extcap:000008|os:ios':
('iPhone SE', '', '5GHz'),
@@ -789,6 +885,15 @@
'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ad,htagg:02,htmcs:0000ffff,wps:WPS_SUPPLICANT_STATION|assoc:0,1,50,45,48,221(0050f2,2),htcap:01ad,htagg:02,htmcs:0000ffff|os:panasonictv':
('Panasonic TV', '', '2.4GHz'),
+ 'wifi4|probe:0,1,45,221(0050f2,8),191,127,htcap:01ef,htagg:1f,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:040000000000004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:01ef,htagg:1f,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,txpow:1e08,extcap:04000a020100004080|oui:htc':
+ ('Pixel Phone', '', '5GHz'),
+ 'wifi4|probe:0,1,45,191,221(0050f2,8),127,htcap:01ef,htagg:df,htmcs:0000ffff,vhtcap:338001b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:04000a020100004080|assoc:0,1,48,45,221(0050f2,2),191,127,htcap:01ef,htagg:1f,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:04000a020100004080|oui:htc':
+ ('Pixel Phone', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,htcap:01ad,htagg:1f,htmcs:0000ffff,extcap:040000000000000080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:01ad,htagg:1f,htmcs:0000ffff,txpow:1e08,extcap:04000a020100000080|oui:htc':
+ ('Pixel Phone', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,191,221(0050f2,8),3,127,htcap:01ef,htagg:df,htmcs:0000ffff,vhtcap:33800192,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:04000a020100004080|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:01ad,htagg:1f,htmcs:0000ffff,extcap:04000a020100000080|oui:htc':
+ ('Pixel Phone', '', '2.4GHz'),
+
'wifi4|probe:0,1|assoc:0,1,221(005043,1)|os:playstation':
('Playstation', '3', '2.4GHz'),
diff --git a/wifi/configs.py b/wifi/configs.py
index 97a27ce..a4f20b8 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -22,6 +22,7 @@
'WifiHostapdDebug',
'WifiShortAggTimeout',
'WifiNoAggTimeout',
+ 'WifiNoAliveMonitor',
]
for _i in EXPERIMENTS:
experiment.register(_i)
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index f3f96ef..bb541f5 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -54,11 +54,11 @@
def _set_link_state(hif, state):
- subprocess.check_output(['if' + state, hif])
+ subprocess.check_call(['if' + state, hif])
def _ifplugd_action(hif, state):
- subprocess.check_output(['/etc/ifplugd/ifplugd.action', hif, state])
+ subprocess.check_call(['/etc/ifplugd/ifplugd.action', hif, state])
def _parse_scan_result(line):
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 34bb5e8..3773f03 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -61,6 +61,7 @@
_FINGERPRINTS_DIRECTORY = '/tmp/wifi/fingerprints'
_LOCKFILE = '/tmp/wifi/wifi'
+_PLATFORM_FILE = '/etc/platform'
lockfile_taken = False
@@ -536,12 +537,12 @@
raise utils.BinWifiException('No client interface for band %s', band)
scan_args = []
+ if opt.scan_freq:
+ scan_args += ['freq', str(opt.scan_freq)]
if opt.scan_ap_force:
scan_args += ['ap-force']
if opt.scan_passive:
scan_args += ['passive']
- if opt.scan_freq:
- scan_args += ['freq', opt.scan_freq]
print(iw.scan(interface, scan_args))
@@ -550,7 +551,7 @@
def _is_hostapd_running(interface):
return utils.subprocess_quiet(
- ('hostapd_cli', '-i', interface, 'status'), no_stdout=True) == 0
+ ('hostapd_cli', '-i', interface, 'quit'), no_stdout=True) == 0
def _wpa_cli(program, interface, command):
@@ -587,6 +588,18 @@
return None
+def _is_wind_charger():
+ try:
+ etc_platform = open(_PLATFORM_FILE).read()
+ if etc_platform[:-1] == 'GFMN100':
+ return True
+ else:
+ return False
+ except IOError as e:
+ print('_is_wind_charger: cant open %s: %s' % (_PLATFORM_FILE, e.strerror))
+ return False
+
+
def _start_hostapd(interface, config_filename, band, ssid):
"""Starts a babysat hostapd.
@@ -622,9 +635,15 @@
alivemonitor_filename = utils.get_filename(
'hostapd', utils.FILENAME_KIND.alive, interface, tmp=True)
+ # Don't use alivemonitor on Windcharger since no waveguide. b/32376077
+ if _is_wind_charger() or experiment.enabled('WifiNoAliveMonitor'):
+ alive_monitor = []
+ else:
+ alive_monitor = ['alivemonitor', alivemonitor_filename, '30', '2', '65']
+
utils.log('Starting hostapd.')
- utils.babysit(['alivemonitor', alivemonitor_filename, '30', '2', '65',
- 'hostapd',
+ utils.babysit(alive_monitor +
+ ['hostapd',
'-A', alivemonitor_filename,
'-F', _FINGERPRINTS_DIRECTORY] +
bandsteering.hostapd_options(band, ssid) +
@@ -898,8 +917,12 @@
"Couldn't stop hostapd to start wpa_supplicant.")
if already_running:
+ subprocess.check_call(['ifdown', interface])
+ subprocess.check_call(['/etc/ifplugd/ifplugd.action', interface, 'down'])
if not _reconfigure_wpa_supplicant(interface):
raise utils.BinWifiException('Failed to reconfigure wpa_supplicant.')
+ subprocess.check_call(['ifup', interface])
+ subprocess.check_call(['/etc/ifplugd/ifplugd.action', interface, 'up'])
elif not _start_wpa_supplicant(interface, tmp_config_filename):
raise utils.BinWifiException(
'wpa_supplicant failed to start. Look at wpa_supplicant logs for '