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