/bin/wifi:  Add flags for provisioning vendor IEs.

Creates flags for provisioning APs to advertise that they provide ACS
access, and for WLAN APs to provide the SSIDs of provisioning networks
for each band.

Change-Id: I39e5e365df897ec9c5f5e093c548698513ad2e0e
diff --git a/wifi/configs.py b/wifi/configs.py
index 11867b2..0d4c642 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -23,6 +23,15 @@
   experiment.register(_i)
 
 
+# From http://go/alphabet-ie-registry, OUI f4f5e8.
+# The properties of this class are hex string representations of varints.
+# pylint: disable=invalid-name
+class VENDOR_IE_FEATURE_ID(object):
+  SUPPORTS_PROVISIONING = '01'
+  PROVISION_VIA_2G = '02'
+  PROVISION_VIA_5G = '03'
+
+
 # Recommended HT40/VHT80 settings for given primary channels.
 # HT40 channels can fall back to 20 MHz, and VHT80 can fall back to 40 or 20.
 # So we configure using a "primary" 20 MHz channel, then allow wider
@@ -78,6 +87,7 @@
 {require_vht}
 {hidden}
 {ap_isolate}
+{vendor_elements}
 
 ht_capab={ht20}{ht40}{guard_interval}{ht_rxstbc}
 {vht_settings}
@@ -285,7 +295,8 @@
       ht_rxstbc=ht_rxstbc, vht_settings=vht_settings,
       guard_interval=guard_interval, enable_wmm=enable_wmm, hidden=hidden,
       ap_isolate=ap_isolate, auth_algs=auth_algs, bridge=bridge,
-      ssid=utils.sanitize_ssid(opt.ssid))]
+      ssid=utils.sanitize_ssid(opt.ssid),
+      vendor_elements=get_vendor_elements(opt))]
 
   if opt.encryption != 'NONE':
     hostapd_conf_parts.append(_HOSTCONF_WPA_TPL.format(
@@ -342,3 +353,53 @@
   ]
   return '\n'.join(lines)
 
+
+def create_vendor_ie(feature_id, payload=''):
+  """Create a vendor IE in hostapd config format.
+
+  Args:
+    feature_id:  The go/alphabet-ie-registry feature ID for OUI f4f5e8.
+    payload:  A string payload (must be ASCII), or none.
+
+  Returns:
+    The vendor IE, as a string.
+  """
+  length = '%02x' % (3 + (len(feature_id)/2) + len(payload))
+  oui = 'f4f5e8'
+  return 'dd%s%s%s%s' % (length, oui, feature_id, payload.encode('hex'))
+
+
+def get_vendor_elements(opt):
+  """Get vendor_elements value hostapd config.
+
+  The way to specify multiple vendor IEs in hostapd is to concatenate them, e.g.
+
+    vendor_elements=dd0411223301dd051122330203
+
+  Args:
+    opt:  The optdict containing user-specified options.
+
+  Returns:
+    The vendor_elements string (including that prefix, or empty if there are no
+    vendor IEs.)
+  """
+  vendor_ies = []
+
+  if opt.supports_provisioning:
+    vendor_ies.append(
+        create_vendor_ie(VENDOR_IE_FEATURE_ID.SUPPORTS_PROVISIONING))
+
+  if opt.provision_via_2g:
+    vendor_ies.append(
+        create_vendor_ie(VENDOR_IE_FEATURE_ID.PROVISION_VIA_2G,
+                         opt.provision_via_2g))
+
+  if opt.provision_via_5g:
+    vendor_ies.append(
+        create_vendor_ie(VENDOR_IE_FEATURE_ID.PROVISION_VIA_5G,
+                         opt.provision_via_5g))
+
+  if vendor_ies:
+    return 'vendor_elements=%s' % ''.join(vendor_ies)
+
+  return ''
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index 77773ce..21a824e 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -310,6 +310,7 @@
 
 
 
+
 ht_capab=[HT20][RX-STBC1]
 
 """
@@ -333,6 +334,55 @@
 
 
 
+
+ht_capab=[HT20][RX-STBC1]
+
+"""
+
+_HOSTAPD_CONFIG_SUPPORTS_PROVISIONING = """ctrl_interface=/var/run/hostapd
+interface=wlan0
+
+ssid=TEST_SSID
+utf8_ssid=1
+auth_algs=1
+hw_mode=g
+channel=1
+country_code=US
+ieee80211d=1
+ieee80211h=1
+ieee80211n=1
+
+
+
+
+
+
+vendor_elements=dd04f4f5e801
+
+ht_capab=[HT20][RX-STBC1]
+
+"""
+
+_HOSTAPD_CONFIG_PROVISION_VIA = """ctrl_interface=/var/run/hostapd
+interface=wlan0
+
+ssid=TEST_SSID
+utf8_ssid=1
+auth_algs=1
+hw_mode=g
+channel=1
+country_code=US
+ieee80211d=1
+ieee80211h=1
+ieee80211n=1
+
+
+
+
+
+
+vendor_elements=dd19f4f5e80247466962657253657475704175746f6d6174696f6edd1af4f5e80347466962657253657475704175746f6d6174696f6e35
+
 ht_capab=[HT20][RX-STBC1]
 
 """
@@ -366,6 +416,9 @@
     self.persist = False
     self.interface_suffix = ''
     self.client_isolation = False
+    self.supports_provisioning = False
+    self.provision_via_2g = ''
+    self.provision_via_5g = ''
 
 
 # pylint: disable=protected-access
@@ -393,6 +446,30 @@
                   config)
   opt.bridge = default_bridge
 
+  # Test provisioning IE.
+  opt.supports_provisioning = True
+  config = configs.generate_hostapd_config(
+      _PHY_INFO, 'wlan0', '2.4', '1', '20', set(('a', 'b', 'g', 'n', 'ac')),
+      'asdfqwer', opt)
+  wvtest.WVPASSEQ('\n'.join((_HOSTAPD_CONFIG_SUPPORTS_PROVISIONING,
+                             _HOSTAPD_CONFIG_WPA,
+                             '# Experiments: ()\n')),
+                  config)
+  opt.supports_provisioning = False
+
+  # Test provision via IEs.
+  opt.provision_via_2g = 'GFiberSetupAutomation'
+  opt.provision_via_5g = 'GFiberSetupAutomation5'
+  config = configs.generate_hostapd_config(
+      _PHY_INFO, 'wlan0', '2.4', '1', '20', set(('a', 'b', 'g', 'n', 'ac')),
+      'asdfqwer', opt)
+  wvtest.WVPASSEQ('\n'.join((_HOSTAPD_CONFIG_PROVISION_VIA,
+                             _HOSTAPD_CONFIG_WPA,
+                             '# Experiments: ()\n')),
+                  config)
+  opt.provision_via_2g = ''
+  opt.provision_via_5g = ''
+
   # Test with no encryption.
   default_encryption, opt.encryption = opt.encryption, 'NONE'
   config = configs.generate_hostapd_config(
@@ -444,5 +521,12 @@
   wvtest.WVPASSEQ(new_config, config)
 
 
+@wvtest.wvtest
+def create_vendor_ie_test():
+  wvtest.WVPASSEQ(configs.create_vendor_ie('01'), 'dd04f4f5e801')
+  wvtest.WVPASSEQ(configs.create_vendor_ie('02', 'GFiberSetupAutomation'),
+                  'dd19f4f5e80247466962657253657475704175746f6d6174696f6e')
+
+
 if __name__ == '__main__':
   wvtest.wvtest_main()
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 9ba8b31..1ba7cf9 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -55,6 +55,9 @@
 scan-ap-force                     (Scan only) scan when in AP mode
 scan-passive                      (Scan only) do not probe, scan passively
 scan-freq=                        (Scan only) limit scan to specific frequencies.
+supports-provisioning             Indicate via vendor IE that this AP supports provisioning.  Corresponds to feature ID 01 of OUI f4f5e8 at go/alphabet-ie-registry.
+provision-via-2g=                 The SSID of the 2.4 GHz open provisioning network supporting the user's wireless devices, to be exposed as a vendor IE.  Corresponds to feature ID 02 of OUI f4f5e8 at go/alphabet-ie-registry. []
+provision-via-5g=                 The SSID of the 5 GHz open provisioning network supporting the user's wireless devices, to be exposed as a vendor IE.  Corresponds to feature ID 03 of OUI f4f5e8 at go/alphabet-ie-registry. []
 """
 
 _FINGERPRINTS_DIRECTORY = '/tmp/wifi/fingerprints'