conman: Get provisioning AP SSIDs from vendor IEs.
Provisioning network APs hide their SSIDs so as not to be visible to
users, instead putting their SSIDs in a vendor IE. This commit
updates conman to read these SSIDs.
Change-Id: I1e9b4165d935c1ff22af7f06f8b4b03bb9c59d6d
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 170b1fa..646d114 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -92,16 +92,19 @@
RegDomain: 00
"""
-IW_SCAN_OUTPUT = """BSS 00:11:22:33:44:55(on wcli0)
+IW_SCAN_DEFAULT_OUTPUT = """BSS 00:11:22:33:44:55(on wcli0)
SSID: s1
- Vendor specific: OUI f4:f5:e8, data: 01
BSS 66:77:88:99:aa:bb(on wcli0)
SSID: s1
- Vendor specific: OUI f4:f5:e8, data: 01
BSS 01:23:45:67:89:ab(on wcli0)
SSID: s2
"""
+IW_SCAN_HIDDEN_OUTPUT = """BSS ff:ee:dd:cc:bb:aa(on wcli0)
+ Vendor specific: OUI f4:f5:e8, data: 01
+ Vendor specific: OUI f4:f5:e8, data: 03 73 33
+"""
+
@wvtest.wvtest
def get_client_interfaces_test():
@@ -221,6 +224,8 @@
self.interface_by_name(wifi)._initially_connected = True
self.scan_has_results = False
+ self.scan_results_include_hidden = False
+ self.can_connect_to_s2 = True
@property
def IP_LINK(self):
@@ -245,37 +250,37 @@
socket = os.path.join(self._wpa_control_interface, wifi.name)
- if bss_info and bss_info.ssid == 's1':
+ def connect(connection_check_result):
if wifi.attached():
wifi.add_connected_event()
else:
open(socket, 'w')
- wifi.set_connection_check_result('fail')
- self.write_interface_status_file(wifi.name, '1')
+ wifi.set_connection_check_result(connection_check_result)
+ self.ifplugd_action(wifi.name, True)
+
+ if bss_info and bss_info.ssid == 's1':
+ connect('fail')
return True
- if bss_info and bss_info.ssid == 's2':
- if wifi.attached():
- wifi.add_connected_event()
- else:
- open(socket, 'w')
- wifi.set_connection_check_result('restricted')
- self.ifplugd_action(wifi.name, True)
+ if bss_info and bss_info.ssid == 's2' and self.can_connect_to_s2:
+ connect('succeed')
+ return True
+
+ if bss_info and bss_info.ssid == 's3':
+ connect('restricted')
return True
return False
- def _wifi_stopclient(self, band):
- super(ConnectionManager, self)._wifi_stopclient(band)
- self.wifi_for_band(band).add_terminating_event()
-
# pylint: disable=unused-argument,protected-access
def _find_bssids(self, band):
# Only wcli0 finds anything.
+ scan_output = ''
if band in self.interface_by_name('wcli0').bands and self.scan_has_results:
- iw._scan = lambda interface: IW_SCAN_OUTPUT
- else:
- iw._scan = lambda interface: ''
+ 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):
@@ -394,7 +399,7 @@
"""The actual test function."""
run_duration_s = .01
interface_update_period = 5
- wifi_scan_period = 5
+ wifi_scan_period = 15
wifi_scan_period_s = run_duration_s * wifi_scan_period
# pylint: disable=protected-access
@@ -537,12 +542,39 @@
# Wait for the connection to be processed.
c.run_once()
wvtest.WVPASS(c.acs())
- wvtest.WVFAIL(c.internet())
+ wvtest.WVPASS(c.internet())
wvtest.WVFAIL(c.client_up('2.4'))
wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
- # Now, create a WLAN configuration which should be connected to. Also, test
- # that atomic writes/renames work.
+ # Now, create a WLAN configuration which should be connected to.
+ ssid = 'wlan'
+ psk = 'password'
+ c.write_wlan_config('2.4', ssid, psk)
+ c.disable_access_point('2.4')
+ c.run_once()
+ wvtest.WVPASS(c.client_up('2.4'))
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
+ wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_WLAN]))
+
+ # Now, remove the WLAN configuration and make sure we are disconnected. Then
+ # disable the previously used ACS connection via s2, add the user's WLAN to
+ # the scan results, and scan again. This time, the first SSID tried should be
+ # 's3', which is not present in the scan results but *is* advertised by the
+ # secure AP running the user's WLAN.
+ c.can_connect_to_s2 = False
+ c.scan_results_include_hidden = True
+ c.delete_wlan_config('2.4')
+ c.run_once()
+ wvtest.WVFAIL(c.has_status_files([status.P.CONNECTED_TO_WLAN]))
+ c.run_until_interface_update()
+ c.run_until_scan('2.4')
+ 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')
+
+ # Now, recreate the same WLAN configuration, which should be connected to.
+ # Also, test that atomic writes/renames work.
ssid = 'wlan'
psk = 'password'
c.write_wlan_config('2.4', ssid, psk, atomic=True)
diff --git a/conman/iw.py b/conman/iw.py
index 7b40a80..b014220 100755
--- a/conman/iw.py
+++ b/conman/iw.py
@@ -6,6 +6,9 @@
import subprocess
+FIBER_OUI = 'f4:f5:e8'
+
+
def _scan(band, **kwargs):
try:
return subprocess.check_output(('wifi', 'scan', '-b', band), **kwargs)
@@ -36,6 +39,9 @@
# pylint: disable=protected-access
return self.__attrs() == other.__attrs()
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
def __hash__(self):
return hash(self.__attrs())
@@ -101,6 +107,14 @@
for bss_info in parsed:
if bss_info.security and not include_secure:
continue
+
+ for oui, data in bss_info.vendor_ies:
+ if oui == FIBER_OUI:
+ octets = data.split()
+ if octets[0] == '03' and not bss_info.ssid:
+ bss_info.ssid = ''.join(octets[1:]).decode('hex')
+ continue
+
for oui, data in bss_info.vendor_ies:
if vendor_ie_function(oui, data):
result_with_ie.add(bss_info)
diff --git a/conman/iw_test.py b/conman/iw_test.py
index c069c91..9c259e8 100755
--- a/conman/iw_test.py
+++ b/conman/iw_test.py
@@ -486,6 +486,72 @@
* 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: 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: 03 47 46 69 62 65 72 53 65 74 75 70 41 75 74 6f 6d 61 74 69 6f 6e
"""
@@ -498,15 +564,23 @@
@wvtest.wvtest
def find_bssids_test():
"""Test iw.find_bssids."""
+ test_ie = ('00:11:22', '01 23 45 67')
+ ssid_ie = (
+ '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',
+ )
short_scan_result = iw.BssInfo(ssid='short scan result',
bssid='00:23:97:57:f4:d8',
security=['WEP'],
- vendor_ies=[('00:11:22', '01 23 45 67')])
+ vendor_ies=[test_ie])
+ provisioning_bss_info = iw.BssInfo(ssid='GFiberSetupAutomation',
+ bssid='94:b4:0f:f1:36:41',
+ vendor_ies=[test_ie, ssid_ie])
with_ie, without_ie = iw.find_bssids('wcli0', lambda o, d: o == '00:11:22',
True)
- wvtest.WVPASSEQ(with_ie, set([short_scan_result]))
+ wvtest.WVPASSEQ(with_ie, set([short_scan_result, provisioning_bss_info]))
wvtest.WVPASSEQ(
without_ie,
@@ -524,7 +598,7 @@
with_ie, without_ie = iw.find_bssids('wcli0', lambda o, d: o == '00:11:22',
False)
- wvtest.WVPASSEQ(with_ie, set())
+ wvtest.WVPASSEQ(with_ie, set([provisioning_bss_info]))
wvtest.WVPASSEQ(
without_ie,
set([iw.BssInfo(ssid='GoogleGuest', bssid='94:b4:0f:f1:36:41'),