Home | History | Annotate | Download | only in ap_configurators
      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 
      5 from autotest_lib.client.common_lib.cros.network import iw_runner
      6 
      7 
      8 # Supported bands
      9 BAND_2GHZ = '2.4GHz'
     10 BAND_5GHZ = '5GHz'
     11 
     12 # List of valid bands.
     13 VALID_BANDS = [BAND_2GHZ, BAND_5GHZ]
     14 
     15 # List of valid 802.11 protocols (modes).
     16 MODE_A = 0x01
     17 MODE_B = 0x02
     18 MODE_G = 0x04
     19 MODE_N = 0x08
     20 MODE_AC = 0x10
     21 MODE_AUTO = 0x20
     22 MODE_M = MODE_A | MODE_B | MODE_G # Used for standard maintenance
     23 MODE_D = MODE_A | MODE_B | MODE_N # International roaming extensions
     24 MODE_B_G = MODE_B | MODE_G
     25 MODE_B_G_N = MODE_B | MODE_G | MODE_N
     26 MODE_AC_N = MODE_AC | MODE_N
     27 MODE_A_N = MODE_A | MODE_N
     28 
     29 # List of valid modes.
     30 VALID_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]
     32 VALID_2GHZ_MODES = [MODE_B, MODE_G, MODE_N, MODE_B_G, MODE_B_G_N]
     33 VALID_5GHZ_MODES = [MODE_A, MODE_AC, MODE_N, MODE_A_N, MODE_AC_N]
     34 
     35 # Supported security types
     36 SECURITY_TYPE_DISABLED = iw_runner.SECURITY_OPEN
     37 SECURITY_TYPE_WEP = iw_runner.SECURITY_WEP
     38 SECURITY_TYPE_WPAPSK = iw_runner.SECURITY_WPA
     39 SECURITY_TYPE_WPA2PSK = iw_runner.SECURITY_WPA2
     40 # Mixed mode security is wpa/wpa2
     41 SECURITY_TYPE_MIXED = iw_runner.SECURITY_MIXED
     42 
     43 WEP_AUTHENTICATION_OPEN = object()
     44 WEP_AUTHENTICATION_SHARED = object()
     45 
     46 # List of valid securities.
     47 # TODO (krisr) the configurators do not support WEP at this time.
     48 VALID_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.
     55 VALID_2GHZ_CHANNELS = range(1,15)
     56 VALID_5GHZ_CHANNELS = [36, 40, 44, 48, 149, 153, 157, 161, 165]
     57 
     58 # Frequency to channel conversion table
     59 CHANNEL_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.
     70 FREQUENCY_TABLE = dict((v,k) for k,v in CHANNEL_TABLE.iteritems())
     71 
     72 # Configurator type
     73 CONFIGURATOR_STATIC = 1
     74 CONFIGURATOR_DYNAMIC = 2
     75 CONFIGURATOR_ANY = 3
     76 
     77 # Default values
     78 DEFAULT_BAND = BAND_2GHZ
     79 
     80 DEFAULT_2GHZ_MODE = MODE_G
     81 DEFAULT_5GHZ_MODE = MODE_A
     82 
     83 DEFAULT_SECURITY_TYPE = SECURITY_TYPE_DISABLED
     84 
     85 DEFAULT_2GHZ_CHANNEL = 5
     86 DEFAULT_5GHZ_CHANNEL = 149
     87 
     88 # Convenience method to convert modes and bands to human readable strings.
     89 def 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 
    101 def 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 
    128 class 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