1# Copyright (c) 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from autotest_lib.client.common_lib.cros.network import iw_runner 6 7 8# Supported bands 9BAND_2GHZ = '2.4GHz' 10BAND_5GHZ = '5GHz' 11 12# List of valid bands. 13VALID_BANDS = [BAND_2GHZ, BAND_5GHZ] 14 15# List of valid 802.11 protocols (modes). 16MODE_A = 0x01 17MODE_B = 0x02 18MODE_G = 0x04 19MODE_N = 0x08 20MODE_AC = 0x10 21MODE_AUTO = 0x20 22MODE_M = MODE_A | MODE_B | MODE_G # Used for standard maintenance 23MODE_D = MODE_A | MODE_B | MODE_N # International roaming extensions 24 25# List of valid modes. 26VALID_MODES = [MODE_A, MODE_AC, MODE_AUTO, MODE_B, MODE_D, MODE_G, MODE_M, 27 MODE_N] 28VALID_2GHZ_MODES = [MODE_B, MODE_G, MODE_N] 29VALID_5GHZ_MODES = [MODE_A, MODE_AC, MODE_N] 30 31# Supported security types 32SECURITY_TYPE_DISABLED = iw_runner.SECURITY_OPEN 33SECURITY_TYPE_WEP = iw_runner.SECURITY_WEP 34SECURITY_TYPE_WPAPSK = iw_runner.SECURITY_WPA 35SECURITY_TYPE_WPA2PSK = iw_runner.SECURITY_WPA2 36# Mixed mode security is wpa/wpa2 37SECURITY_TYPE_MIXED = iw_runner.SECURITY_MIXED 38 39WEP_AUTHENTICATION_OPEN = object() 40WEP_AUTHENTICATION_SHARED = object() 41 42# List of valid securities. 43# TODO (krisr) the configurators do not support WEP at this time. 44VALID_SECURITIES = [SECURITY_TYPE_DISABLED, 45 SECURITY_TYPE_WPAPSK, 46 SECURITY_TYPE_WPA2PSK] 47 48# List of valid channels. 49VALID_2GHZ_CHANNELS = range(1,15) 50VALID_5GHZ_CHANNELS = [36, 40, 44, 48, 149, 153, 157, 161, 165] 51 52# Frequency to channel conversion table 53CHANNEL_TABLE = {2412: 1, 2417: 2, 2422: 3, 54 2427: 4, 2432: 5, 2437: 6, 55 2442: 7, 2447: 8, 2452: 9, 56 2457: 10, 2462: 11, 2467: 12, 57 2472: 13, 2484: 14, 5180: 36, 58 5200: 40, 5220: 44, 5240: 48, 59 5745: 149, 5765: 153, 5785: 157, 60 5805: 161, 5825: 165} 61 62# This only works because the frequency table is one to one 63# for channels/frequencies. 64FREQUENCY_TABLE = dict((v,k) for k,v in CHANNEL_TABLE.iteritems()) 65 66# Configurator type 67CONFIGURATOR_STATIC = 1 68CONFIGURATOR_DYNAMIC = 2 69CONFIGURATOR_ANY = 3 70 71# Default values 72DEFAULT_BAND = BAND_2GHZ 73 74DEFAULT_2GHZ_MODE = MODE_G 75DEFAULT_5GHZ_MODE = MODE_A 76 77DEFAULT_SECURITY_TYPE = SECURITY_TYPE_DISABLED 78 79DEFAULT_2GHZ_CHANNEL = 5 80DEFAULT_5GHZ_CHANNEL = 149 81 82# Convenience method to convert modes and bands to human readable strings. 83def band_string_for_band(band): 84 """Returns a human readable string of the band 85 86 @param band: band object 87 @returns: string representation of the band 88 """ 89 if band == BAND_2GHZ: 90 return '2.4 GHz' 91 elif band == BAND_5GHZ: 92 return '5 GHz' 93 94 95def mode_string_for_mode(mode): 96 """Returns a human readable string of the mode. 97 98 @param mode: integer, the mode to convert. 99 @returns: string representation of the mode 100 """ 101 string_table = {MODE_A:'a', MODE_AC:'ac', MODE_B:'b', MODE_G:'g', 102 MODE_N:'n'} 103 104 if mode == MODE_AUTO: 105 return 'Auto' 106 total = 0 107 string = '' 108 for current_mode in sorted(string_table.keys()): 109 i = current_mode & mode 110 total = total | i 111 if i in string_table: 112 string = string + string_table[i] + '/' 113 if total == MODE_M: 114 string = 'm' 115 elif total == MODE_D: 116 string = 'd' 117 if string[-1] == '/': 118 return string[:-1] 119 return string 120 121 122class APSpec(object): 123 """Object to specify an APs desired capabilities. 124 125 The APSpec object is immutable. All of the parameters are optional. 126 For those not given the defaults listed above will be used. Validation 127 is done on the values to make sure the spec created is valid. If 128 validation fails a ValueError is raised. 129 """ 130 131 132 def __init__(self, visible=True, security=SECURITY_TYPE_DISABLED, 133 band=None, mode=None, channel=None, hostnames=None, 134 configurator_type=CONFIGURATOR_ANY, 135 # lab_ap set to true means the AP must be in the lab; 136 # if it set to false the AP is outside of the lab. 137 lab_ap=True): 138 super(APSpec, self).__init__() 139 self._visible = visible 140 self._security = security 141 self._mode = mode 142 self._channel = channel 143 self._hostnames = hostnames 144 self._configurator_type = configurator_type 145 self._lab_ap = lab_ap 146 self._webdriver_hostname = None 147 148 if not self._channel and (self._mode == MODE_N or not self._mode): 149 if band == BAND_2GHZ or not band: 150 self._channel = DEFAULT_2GHZ_CHANNEL 151 if not self._mode: 152 self._mode = DEFAULT_2GHZ_MODE 153 elif band == BAND_5GHZ: 154 self._channel = DEFAULT_5GHZ_CHANNEL 155 if not self._mode: 156 self._mode = DEFAULT_5GHZ_MODE 157 else: 158 raise ValueError('Invalid Band.') 159 160 self._validate_channel_and_mode() 161 162 if ((band == BAND_2GHZ and self._mode not in VALID_2GHZ_MODES) or 163 (band == BAND_5GHZ and self._mode not in VALID_5GHZ_MODES)): 164 raise ValueError('Conflicting band and modes/channels.') 165 166 self._validate_security() 167 168 169 def __str__(self): 170 return ('AP Specification:\n' 171 'visible=%r\n' 172 'security=%s\n' 173 'band=%s\n' 174 'mode=%s\n' 175 'channel=%d\n' 176 'password=%s' % (self._visible, self._security, 177 band_string_for_band(self.band), 178 mode_string_for_mode(self._mode), 179 self._channel, self._password)) 180 181 182 @property 183 def password(self): 184 """Returns the password for password supported secured networks.""" 185 return self._password 186 187 188 189 @property 190 def visible(self): 191 """Returns if the SSID is visible or not.""" 192 return self._visible 193 194 195 @property 196 def security(self): 197 """Returns the type of security.""" 198 return self._security 199 200 201 @property 202 def band(self): 203 """Return the band.""" 204 if self._channel in VALID_2GHZ_CHANNELS: 205 return BAND_2GHZ 206 return BAND_5GHZ 207 208 209 @property 210 def mode(self): 211 """Return the mode.""" 212 return self._mode 213 214 215 @property 216 def channel(self): 217 """Return the channel.""" 218 return self._channel 219 220 221 @property 222 def frequency(self): 223 """Return the frequency equivalent of the channel.""" 224 return FREQUENCY_TABLE[self._channel] 225 226 227 @property 228 def hostnames(self): 229 """Return the hostnames; this may be None.""" 230 return self._hostnames 231 232 233 @property 234 def configurator_type(self): 235 """Returns the configurator type.""" 236 return self._configurator_type 237 238 239 @property 240 def lab_ap(self): 241 """Returns if the AP should be in the lab or not.""" 242 return self._lab_ap 243 244 245 @property 246 def webdriver_hostname(self): 247 """Returns locked webdriver hostname.""" 248 return self._webdriver_hostname 249 250 251 @webdriver_hostname.setter 252 def webdriver_hostname(self, value): 253 """Sets webdriver_hostname to locked instance. 254 255 @param value: locked webdriver hostname 256 257 """ 258 self._webdriver_hostname = value 259 260 261 def _validate_channel_and_mode(self): 262 """Validates the channel and mode selected are correct. 263 264 raises ValueError: if the channel or mode selected is invalid 265 """ 266 if self._channel and self._mode: 267 if ((self._channel in VALID_2GHZ_CHANNELS and 268 self._mode not in VALID_2GHZ_MODES) or 269 (self._channel in VALID_5GHZ_CHANNELS and 270 self._mode not in VALID_5GHZ_MODES)): 271 raise ValueError('Conflicting mode/channel has been selected.') 272 elif self._channel: 273 if self._channel in VALID_2GHZ_CHANNELS: 274 self._mode = DEFAULT_2GHZ_MODE 275 elif self._channel in VALID_5GHZ_CHANNELS: 276 self._mode = DEFAULT_5GHZ_MODE 277 else: 278 raise ValueError('Invalid channel passed.') 279 else: 280 if self._mode in VALID_2GHZ_MODES: 281 self._channel = DEFAULT_2GHZ_CHANNEL 282 elif self._mode in VALID_5GHZ_MODES: 283 self._channel = DEFAULT_5GHZ_CHANNEL 284 else: 285 raise ValueError('Invalid mode passed.') 286 287 288 def _validate_security(self): 289 """Sets a password for security settings that need it. 290 291 raises ValueError: if the security setting passed is invalid. 292 """ 293 if self._security == SECURITY_TYPE_DISABLED: 294 self._password = None 295 elif (self._security == SECURITY_TYPE_WPAPSK or 296 self._security == SECURITY_TYPE_WPA2PSK): 297 self._password = 'chromeos' 298 else: 299 raise ValueError('Invalid security passed.') 300