autotest: Refactor WiFi security credentials into per type classes
This lets us be a little more abstract about the types of security
credentials we support in testing, and concentrates the relevant logic
into one place.
TEST=Passes both:
./run_remote_tests.sh --remote=chromeos1-shelf1-host3 check11b
./run_remote_tests.sh --remote=chromeos1-shelf1-host3 checkStaticWEP40
which indicates this works for both open and WEP security types.
BUG=chromium:249096
CQ-DEPEND=CL:58588
Change-Id: I085665db9bead45fcd8d0b94a136b9dac2f646da
Reviewed-on: https://gerrit.chromium.org/gerrit/58589
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
diff --git a/client/common_lib/cros/network/shill_xmlrpc_server.py b/client/common_lib/cros/network/shill_xmlrpc_server.py
index 9bcfc2b..514b690 100755
--- a/client/common_lib/cros/network/shill_xmlrpc_server.py
+++ b/client/common_lib/cros/network/shill_xmlrpc_server.py
@@ -112,7 +112,7 @@
raw = self._shill_proxy.connect_to_wifi_network(
params.ssid,
params.security,
- params.psk,
+ params.security_parameters,
params.save_credentials,
# FIXME(wiley): comment out b/c this breaks RvR tests
#station_type=params.station_type,
diff --git a/client/common_lib/cros/network/xmlrpc_datatypes.py b/client/common_lib/cros/network/xmlrpc_datatypes.py
index 2fbd581..2483f8d 100644
--- a/client/common_lib/cros/network/xmlrpc_datatypes.py
+++ b/client/common_lib/cros/network/xmlrpc_datatypes.py
@@ -3,12 +3,12 @@
# found in the LICENSE file.
from autotest_lib.client.common_lib.cros import xmlrpc_datatypes
+from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
+
class AssociationParameters(xmlrpc_datatypes.XmlRpcStruct):
"""Describes parameters used in WiFi connection attempts."""
- DEFAULT_SECURITY = 'none'
- DEFAULT_PSK = ''
DEFAULT_DISCOVERY_TIMEOUT = 15
DEFAULT_ASSOCIATION_TIMEOUT = 15
DEFAULT_CONFIGURATION_TIMEOUT = 15
@@ -17,6 +17,18 @@
# Mode for certain kinds of p2p networks like old Android phone hotspots.
STATION_TYPE_IBSS = 'ibss'
+ @property
+ def security(self):
+ """@return string security type for this network."""
+ return self.security_config.security
+
+
+ @property
+ def security_parameters(self):
+ """@return dict of service property/value pairs related to security."""
+ return self.security_config.get_shill_service_properties()
+
+
def __init__(self, serialized=None):
"""Construct an AssociationParameters.
@@ -28,10 +40,11 @@
serialized = {}
# The network to connect to (e.g. 'GoogleGuest').
self.ssid = serialized.get('ssid', None)
- # Which encryption to use (e.g. 'wpa').
- self.security = serialized.get('security', self.DEFAULT_SECURITY)
- # Passphrase for this network (e.g. 'password123').
- self.psk = serialized.get('psk', self.DEFAULT_PSK)
+ # Marshall our bundle of security configuration.
+ serialized_security_config = serialized.get('security_config', {})
+ self.security_config = (
+ xmlrpc_security_types.deserialize(serialized_security_config) or
+ xmlrpc_security_types.SecurityConfig())
# Max delta in seconds between XMLRPC call to connect in the proxy
# and when shill finds the service. Presumably callers call connect
# quickly after configuring the AP so that this is an approximation
diff --git a/client/common_lib/cros/network/xmlrpc_security_types.py b/client/common_lib/cros/network/xmlrpc_security_types.py
new file mode 100644
index 0000000..d5ccecb
--- /dev/null
+++ b/client/common_lib/cros/network/xmlrpc_security_types.py
@@ -0,0 +1,97 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+from autotest_lib.client.common_lib.cros import xmlrpc_datatypes
+
+TYPE_KEY = 'security_class_type_key'
+
+def deserialize(serialized):
+ """Deserialize a SecurityConfig.
+
+ Because Python XMLRPC doesn't understand anything more than basic
+ types, we're forced to reinvent type marshalling. This is one way
+ to do it.
+
+ @param serialized dict representing a serialized SecurityConfig.
+ @return a SecurityConfig object built from |serialized|.
+
+ """
+ if TYPE_KEY not in serialized:
+ return None
+
+ return getattr(sys.modules[__name__],
+ serialized[TYPE_KEY])(serialized=serialized)
+
+
+class SecurityConfig(xmlrpc_datatypes.XmlRpcStruct):
+ """Abstracts the security configuration for a WiFi network.
+
+ This bundle of credentials can be passed to both HostapConfig and
+ AssociationParameters so that both shill and hostapd can set up and connect
+ to an encrypted WiFi network. By default, we'll assume we're connecting
+ to an open network.
+
+ """
+ SERVICE_PROPERTY_PASSPHRASE = 'Passphrase'
+
+ def __init__(self, serialized=None, security=None):
+ super(SecurityConfig, self).__init__()
+ setattr(self, TYPE_KEY, self.__class__.__name__)
+ if serialized is None:
+ serialized = {}
+ # Default to no security.
+ self.security = serialized.get('security', security or 'none')
+
+
+ def get_hostapd_config(self):
+ """@return dict fragment of hostapd configuration for security."""
+ return {}
+
+
+ def get_shill_service_properties(self):
+ """@return dict of shill service properties."""
+ return {}
+
+
+ def __repr__(self):
+ return '%s(security=%r)' % (self.__class__.__name__, self.security)
+
+
+class WEPConfig(SecurityConfig):
+ """Abstracts security configuration for a WiFi network using static WEP."""
+
+ def __init__(self, serialized=None, wep_keys=None, wep_default_key=None):
+ super(WEPConfig, self).__init__(serialized=serialized, security='wep')
+ if serialized is None:
+ serialized = {}
+ self.wep_keys = serialized.get('wep_keys', wep_keys or [])
+ self.wep_default_key = serialized.get('wep_default_key',
+ wep_default_key or 0)
+ if self.wep_keys and len(self.wep_keys) > 4:
+ raise error.TestFail('More than 4 WEP keys specified (%d).' %
+ len(self.wep_keys))
+
+
+ def get_hostapd_config(self):
+ """@return dict fragment of hostapd configuration for security."""
+ ret = {}
+ for idx,key in enumerate(self.wep_keys):
+ ret['wep_key%d' % idx] = key
+ ret['wep_default_key'] = self.wep_default_key
+ return ret
+
+
+ def get_shill_service_properties(self):
+ """@return dict of shill service properties."""
+ return {self.SERVICE_PROPERTY_PASSPHRASE: '%d:%s' % (
+ self.wep_default_key,
+ self.wep_keys[self.wep_default_key])}
+
+
+ def __repr__(self):
+ return '%s(wep_keys=%r, wep_default_key=%r)' % (self.__class__.__name__,
+ self.wep_keys,
+ self.wep_default_key)
diff --git a/server/cros/wlan/hostap_config.py b/server/cros/wlan/hostap_config.py
index e2ea7a5..0428a15 100644
--- a/server/cros/wlan/hostap_config.py
+++ b/server/cros/wlan/hostap_config.py
@@ -5,6 +5,7 @@
import logging
from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
class HostapConfig(object):
@@ -116,7 +117,7 @@
def __init__(self, mode=None, channel=None, frequency=None,
n_capabilities=None, hide_ssid=None, beacon_interval=None,
dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
- force_wmm=None, wep_keys=None, wep_default_key=None):
+ force_wmm=None, security_config=None):
"""Construct a HostapConfig.
You may specify channel or frequency, but not both. Both options
@@ -135,8 +136,7 @@
@param bssid string like 00:11:22:33:44:55.
@param force_wmm True if we should force WMM on, False if we should
force it off, None if we shouldn't force anything.
- @param wep_keys list of string wep keys
- @param wep_default_key int index into wep_keys to use as default key.
+ @param security_config SecurityConfig object.
"""
super(HostapConfig, self).__init__()
@@ -223,21 +223,15 @@
self.bssid = bssid
if force_wmm is not None:
self.wmm_enabled = force_wmm
- if wep_keys and len(wep_keys) > 4:
- raise error.TestFail('More than 4 WEP keys specified (%d).' %
- len(self.wep_keys))
-
- self.wep_keys = wep_keys
- self.wep_default_key = wep_default_key
- if self.wep_default_key is None and self.wep_keys:
- self.wep_default_key = 0
+ self.security_config = (security_config or
+ xmlrpc_security_types.SecurityConfig())
def __repr__(self):
return ('%s(mode=%r, channel=%r, frequency=%r, '
'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
- 'wmm_enabled=%r, wep_keys=%r, wep_default_key=%r)' % (
+ 'wmm_enabled=%r, security_config=%r)' % (
self.__class__.__name__,
self.hw_mode,
self.channel,
@@ -250,8 +244,7 @@
self.ssid,
self.bssid,
self.wmm_enabled,
- self.wep_keys,
- self.wep_default_key))
+ self.security_config))
def get_ssid(self, default_ssid):
@@ -264,10 +257,5 @@
return self.ssid or (default_ssid + self.ssid_suffix)[-32:]
- def get_shill_compatible_psk(self):
- """@return string shill psk for the encryption in this configuration."""
- if self.wep_keys:
- return '%d:%s' % (self.wep_default_key,
- self.wep_keys[self.wep_default_key])
-
- raise error.TestFail('Failed to build shill compatible psk.')
+ def get_security_hostapd_conf(self):
+ return self.security_config.get_hostapd_config()
diff --git a/server/site_linux_router.py b/server/site_linux_router.py
index a089f26..3af1d05 100644
--- a/server/site_linux_router.py
+++ b/server/site_linux_router.py
@@ -277,10 +277,7 @@
conf['dtim_period'] = configuration.dtim_period
if configuration.frag_threshold:
conf['fragm_threshold'] = configuration.frag_threshold
- if configuration.wep_keys:
- for idx,key in enumerate(configuration.wep_keys):
- conf['wep_key%d' % idx] = key
- conf['wep_default_key'] = configuration.wep_default_key
+ conf.update(configuration.get_security_hostapd_conf())
self.start_hostapd(conf, {})
# Configure transmit power
diff --git a/server/site_tests/network_WiFi_SimpleConnect/control.checkStaticWEP40 b/server/site_tests/network_WiFi_SimpleConnect/control.checkStaticWEP40
index 15b6510..a8abf8e 100644
--- a/server/site_tests/network_WiFi_SimpleConnect/control.checkStaticWEP40
+++ b/server/site_tests/network_WiFi_SimpleConnect/control.checkStaticWEP40
@@ -14,24 +14,26 @@
WEP open system authentication with 40-bit pre-shared keys.
"""
+import copy
from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
+from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
from autotest_lib.server.cros.wlan import hostap_config
def run(machine):
wep_keys = ['0123456789', '89abcdef01', '9876543210', 'fedcba9876']
- print wep_keys
+ master_wep_config = xmlrpc_security_types.WEPConfig(wep_keys=wep_keys)
configurations = []
- for idx,key in enumerate(wep_keys):
+ for i in range(len(wep_keys)):
+ wep_config = copy.copy(master_wep_config)
+ wep_config.wep_default_key = i
ap_config = hostap_config.HostapConfig(
frequency=2412,
mode=hostap_config.HostapConfig.MODE_11G,
- wep_keys=wep_keys,
- wep_default_key=idx)
+ security_config=wep_config)
assoc_params = xmlrpc_datatypes.AssociationParameters()
- assoc_params.security = 'wep'
- assoc_params.psk = ap_config.get_shill_compatible_psk()
+ assoc_params.security_config = wep_config
configurations.append((ap_config, assoc_params))
host = hosts.create_host(machine)
job.run_test('network_WiFi_SimpleConnect',