• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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