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