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