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, 128, 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 5640: 128, 5745: 149, 5765: 153, 66 5785: 157, 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_N 81DEFAULT_5GHZ_MODE = MODE_AC_N 82 83DEFAULT_SECURITY_TYPE = SECURITY_TYPE_WPA2PSK 84 85DEFAULT_2GHZ_CHANNEL = 6 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_WPA2PSK, 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) or 170 (band == BAND_2GHZ and self._channel not in VALID_2GHZ_CHANNELS) or 171 (band == BAND_5GHZ and self._channel not in VALID_5GHZ_CHANNELS)): 172 raise ValueError('Conflicting band and modes/channels.') 173 174 self._validate_security() 175 176 177 def __str__(self): 178 return ('AP Specification:\n' 179 'visible=%r\n' 180 'security=%s\n' 181 'band=%s\n' 182 'mode=%s\n' 183 'channel=%d\n' 184 'password=%s' % (self._visible, self._security, 185 band_string_for_band(self.band), 186 mode_string_for_mode(self._mode), 187 self._channel, self._password)) 188 189 190 @property 191 def password(self): 192 """Returns the password for password supported secured networks.""" 193 return self._password 194 195 196 197 @property 198 def visible(self): 199 """Returns if the SSID is visible or not.""" 200 return self._visible 201 202 203 @property 204 def security(self): 205 """Returns the type of security.""" 206 return self._security 207 208 209 @property 210 def band(self): 211 """Return the band.""" 212 if self._channel in VALID_2GHZ_CHANNELS: 213 return BAND_2GHZ 214 return BAND_5GHZ 215 216 217 @property 218 def mode(self): 219 """Return the mode.""" 220 return self._mode 221 222 223 @property 224 def channel(self): 225 """Return the channel.""" 226 return self._channel 227 228 229 @property 230 def frequency(self): 231 """Return the frequency equivalent of the channel.""" 232 return FREQUENCY_TABLE[self._channel] 233 234 235 @property 236 def hostnames(self): 237 """Return the hostnames; this may be None.""" 238 return self._hostnames 239 240 241 @property 242 def configurator_type(self): 243 """Returns the configurator type.""" 244 return self._configurator_type 245 246 247 @property 248 def lab_ap(self): 249 """Returns if the AP should be in the lab or not.""" 250 return self._lab_ap 251 252 253 @property 254 def webdriver_hostname(self): 255 """Returns locked webdriver hostname.""" 256 return self._webdriver_hostname 257 258 259 @webdriver_hostname.setter 260 def webdriver_hostname(self, value): 261 """Sets webdriver_hostname to locked instance. 262 263 @param value: locked webdriver hostname 264 265 """ 266 self._webdriver_hostname = value 267 268 269 def _validate_channel_and_mode(self): 270 """Validates the channel and mode selected are correct. 271 272 raises ValueError: if the channel or mode selected is invalid 273 """ 274 if self._channel and self._mode: 275 if ((self._channel in VALID_2GHZ_CHANNELS and 276 self._mode not in VALID_2GHZ_MODES) or 277 (self._channel in VALID_5GHZ_CHANNELS and 278 self._mode not in VALID_5GHZ_MODES)): 279 raise ValueError('Conflicting mode/channel has been selected.') 280 elif self._channel: 281 if self._channel in VALID_2GHZ_CHANNELS: 282 self._mode = DEFAULT_2GHZ_MODE 283 elif self._channel in VALID_5GHZ_CHANNELS: 284 self._mode = DEFAULT_5GHZ_MODE 285 else: 286 raise ValueError('Invalid channel passed.') 287 else: 288 if self._mode in VALID_2GHZ_MODES: 289 self._channel = DEFAULT_2GHZ_CHANNEL 290 elif self._mode in VALID_5GHZ_MODES: 291 self._channel = DEFAULT_5GHZ_CHANNEL 292 else: 293 raise ValueError('Invalid mode passed.') 294 295 296 def _validate_security(self): 297 """Sets a password for security settings that need it. 298 299 raises ValueError: if the security setting passed is invalid. 300 """ 301 if self._security == SECURITY_TYPE_DISABLED: 302 self._password = None 303 elif (self._security == SECURITY_TYPE_WPAPSK or 304 self._security == SECURITY_TYPE_WPA2PSK or 305 self._security == SECURITY_TYPE_MIXED): 306 self._password = 'chromeos' 307 elif (self._security==SECURITY_TYPE_WEP): 308 self._password = 'chros' 309 else: 310 raise ValueError('Invalid security passed.') 311