# Copyright (c) 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from autotest_lib.client.common_lib.cros.network import iw_runner # Supported bands BAND_2GHZ = '2.4GHz' BAND_5GHZ = '5GHz' # List of valid bands. VALID_BANDS = [BAND_2GHZ, BAND_5GHZ] # List of valid 802.11 protocols (modes). MODE_A = 0x01 MODE_B = 0x02 MODE_G = 0x04 MODE_N = 0x08 MODE_AC = 0x10 MODE_AUTO = 0x20 MODE_M = MODE_A | MODE_B | MODE_G # Used for standard maintenance MODE_D = MODE_A | MODE_B | MODE_N # International roaming extensions # List of valid modes. VALID_MODES = [MODE_A, MODE_AC, MODE_AUTO, MODE_B, MODE_D, MODE_G, MODE_M, MODE_N] VALID_2GHZ_MODES = [MODE_B, MODE_G, MODE_N] VALID_5GHZ_MODES = [MODE_A, MODE_AC, MODE_N] # Supported security types SECURITY_TYPE_DISABLED = iw_runner.SECURITY_OPEN SECURITY_TYPE_WEP = iw_runner.SECURITY_WEP SECURITY_TYPE_WPAPSK = iw_runner.SECURITY_WPA SECURITY_TYPE_WPA2PSK = iw_runner.SECURITY_WPA2 # Mixed mode security is wpa/wpa2 SECURITY_TYPE_MIXED = iw_runner.SECURITY_MIXED WEP_AUTHENTICATION_OPEN = object() WEP_AUTHENTICATION_SHARED = object() # List of valid securities. # TODO (krisr) the configurators do not support WEP at this time. VALID_SECURITIES = [SECURITY_TYPE_DISABLED, SECURITY_TYPE_WPAPSK, SECURITY_TYPE_WPA2PSK] # List of valid channels. VALID_2GHZ_CHANNELS = range(1,15) VALID_5GHZ_CHANNELS = [36, 40, 44, 48, 149, 153, 157, 161, 165] # Frequency to channel conversion table CHANNEL_TABLE = {2412: 1, 2417: 2, 2422: 3, 2427: 4, 2432: 5, 2437: 6, 2442: 7, 2447: 8, 2452: 9, 2457: 10, 2462: 11, 2467: 12, 2472: 13, 2484: 14, 5180: 36, 5200: 40, 5220: 44, 5240: 48, 5745: 149, 5765: 153, 5785: 157, 5805: 161, 5825: 165} # This only works because the frequency table is one to one # for channels/frequencies. FREQUENCY_TABLE = dict((v,k) for k,v in CHANNEL_TABLE.iteritems()) # Configurator type CONFIGURATOR_STATIC = 1 CONFIGURATOR_DYNAMIC = 2 CONFIGURATOR_ANY = 3 # Default values DEFAULT_BAND = BAND_2GHZ DEFAULT_2GHZ_MODE = MODE_G DEFAULT_5GHZ_MODE = MODE_A DEFAULT_SECURITY_TYPE = SECURITY_TYPE_DISABLED DEFAULT_2GHZ_CHANNEL = 5 DEFAULT_5GHZ_CHANNEL = 149 # Convenience method to convert modes and bands to human readable strings. def band_string_for_band(band): """Returns a human readable string of the band @param band: band object @returns: string representation of the band """ if band == BAND_2GHZ: return '2.4 GHz' elif band == BAND_5GHZ: return '5 GHz' def mode_string_for_mode(mode): """Returns a human readable string of the mode. @param mode: integer, the mode to convert. @returns: string representation of the mode """ string_table = {MODE_A:'a', MODE_AC:'ac', MODE_B:'b', MODE_G:'g', MODE_N:'n'} if mode == MODE_AUTO: return 'Auto' total = 0 string = '' for current_mode in sorted(string_table.keys()): i = current_mode & mode total = total | i if i in string_table: string = string + string_table[i] + '/' if total == MODE_M: string = 'm' elif total == MODE_D: string = 'd' if string[-1] == '/': return string[:-1] return string class APSpec(object): """Object to specify an APs desired capabilities. The APSpec object is immutable. All of the parameters are optional. For those not given the defaults listed above will be used. Validation is done on the values to make sure the spec created is valid. If validation fails a ValueError is raised. """ def __init__(self, visible=True, security=SECURITY_TYPE_DISABLED, band=None, mode=None, channel=None, hostnames=None, configurator_type=CONFIGURATOR_ANY, # lab_ap set to true means the AP must be in the lab; # if it set to false the AP is outside of the lab. lab_ap=True): super(APSpec, self).__init__() self._visible = visible self._security = security self._mode = mode self._channel = channel self._hostnames = hostnames self._configurator_type = configurator_type self._lab_ap = lab_ap self._webdriver_hostname = None if not self._channel and (self._mode == MODE_N or not self._mode): if band == BAND_2GHZ or not band: self._channel = DEFAULT_2GHZ_CHANNEL if not self._mode: self._mode = DEFAULT_2GHZ_MODE elif band == BAND_5GHZ: self._channel = DEFAULT_5GHZ_CHANNEL if not self._mode: self._mode = DEFAULT_5GHZ_MODE else: raise ValueError('Invalid Band.') self._validate_channel_and_mode() if ((band == BAND_2GHZ and self._mode not in VALID_2GHZ_MODES) or (band == BAND_5GHZ and self._mode not in VALID_5GHZ_MODES)): raise ValueError('Conflicting band and modes/channels.') self._validate_security() def __str__(self): return ('AP Specification:\n' 'visible=%r\n' 'security=%s\n' 'band=%s\n' 'mode=%s\n' 'channel=%d\n' 'password=%s' % (self._visible, self._security, band_string_for_band(self.band), mode_string_for_mode(self._mode), self._channel, self._password)) @property def password(self): """Returns the password for password supported secured networks.""" return self._password @property def visible(self): """Returns if the SSID is visible or not.""" return self._visible @property def security(self): """Returns the type of security.""" return self._security @property def band(self): """Return the band.""" if self._channel in VALID_2GHZ_CHANNELS: return BAND_2GHZ return BAND_5GHZ @property def mode(self): """Return the mode.""" return self._mode @property def channel(self): """Return the channel.""" return self._channel @property def frequency(self): """Return the frequency equivalent of the channel.""" return FREQUENCY_TABLE[self._channel] @property def hostnames(self): """Return the hostnames; this may be None.""" return self._hostnames @property def configurator_type(self): """Returns the configurator type.""" return self._configurator_type @property def lab_ap(self): """Returns if the AP should be in the lab or not.""" return self._lab_ap @property def webdriver_hostname(self): """Returns locked webdriver hostname.""" return self._webdriver_hostname @webdriver_hostname.setter def webdriver_hostname(self, value): """Sets webdriver_hostname to locked instance. @param value: locked webdriver hostname """ self._webdriver_hostname = value def _validate_channel_and_mode(self): """Validates the channel and mode selected are correct. raises ValueError: if the channel or mode selected is invalid """ if self._channel and self._mode: if ((self._channel in VALID_2GHZ_CHANNELS and self._mode not in VALID_2GHZ_MODES) or (self._channel in VALID_5GHZ_CHANNELS and self._mode not in VALID_5GHZ_MODES)): raise ValueError('Conflicting mode/channel has been selected.') elif self._channel: if self._channel in VALID_2GHZ_CHANNELS: self._mode = DEFAULT_2GHZ_MODE elif self._channel in VALID_5GHZ_CHANNELS: self._mode = DEFAULT_5GHZ_MODE else: raise ValueError('Invalid channel passed.') else: if self._mode in VALID_2GHZ_MODES: self._channel = DEFAULT_2GHZ_CHANNEL elif self._mode in VALID_5GHZ_MODES: self._channel = DEFAULT_5GHZ_CHANNEL else: raise ValueError('Invalid mode passed.') def _validate_security(self): """Sets a password for security settings that need it. raises ValueError: if the security setting passed is invalid. """ if self._security == SECURITY_TYPE_DISABLED: self._password = None elif (self._security == SECURITY_TYPE_WPAPSK or self._security == SECURITY_TYPE_WPA2PSK): self._password = 'chromeos' else: raise ValueError('Invalid security passed.')