• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright (c) 2013 The Chromium OS 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 collections
7import copy
8import logging
9
10import six
11
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
14from autotest_lib.server.cros.network import packet_capturer
15
16
17class HostapConfig(object):
18    """Parameters for router configuration."""
19
20    # A mapping of frequency to channel number.  This includes some
21    # frequencies used outside the US.
22    CHANNEL_MAP = {2412: 1,
23                   2417: 2,
24                   2422: 3,
25                   2427: 4,
26                   2432: 5,
27                   2437: 6,
28                   2442: 7,
29                   2447: 8,
30                   2452: 9,
31                   2457: 10,
32                   2462: 11,
33                   # 12, 13 are only legitimate outside the US.
34                   2467: 12,
35                   2472: 13,
36                   # 14 is for Japan, DSSS and CCK only.
37                   2484: 14,
38                   # 32 valid in Europe.
39                   5160: 32,
40                   # 34 valid in Europe.
41                   5170: 34,
42                   # 36-116 valid in the US, except 38, 42, and 46, which have
43                   # mixed international support.
44                   5180: 36,
45                   5190: 38,
46                   5200: 40,
47                   5210: 42,
48                   5220: 44,
49                   5230: 46,
50                   5240: 48,
51                   5260: 52,
52                   5280: 56,
53                   5300: 60,
54                   5320: 64,
55                   5500: 100,
56                   5520: 104,
57                   5540: 108,
58                   5560: 112,
59                   5580: 116,
60                   # 120, 124, 128 valid in Europe/Japan.
61                   5600: 120,
62                   5620: 124,
63                   5640: 128,
64                   # 132+ valid in US.
65                   5660: 132,
66                   5680: 136,
67                   5700: 140,
68                   5710: 142,
69                   # 144 is supported by a subset of WiFi chips
70                   # (e.g. bcm4354, but not ath9k).
71                   5720: 144,
72                   5745: 149,
73                   5755: 151,
74                   5765: 153,
75                   5785: 157,
76                   5805: 161,
77                   5825: 165}
78
79    MODE_11A = 'a'
80    MODE_11B = 'b'
81    MODE_11G = 'g'
82    MODE_11N_MIXED = 'n-mixed'
83    MODE_11N_PURE = 'n-only'
84    MODE_11AC_MIXED = 'ac-mixed'
85    MODE_11AC_PURE = 'ac-only'
86
87    N_CAPABILITY_HT20 = object()
88    N_CAPABILITY_HT40 = object()
89    N_CAPABILITY_HT40_PLUS = object()
90    N_CAPABILITY_HT40_MINUS = object()
91    N_CAPABILITY_GREENFIELD = object()
92    N_CAPABILITY_SGI20 = object()
93    N_CAPABILITY_SGI40 = object()
94    ALL_N_CAPABILITIES = [N_CAPABILITY_HT20,
95                          N_CAPABILITY_HT40,
96                          N_CAPABILITY_HT40_PLUS,
97                          N_CAPABILITY_HT40_MINUS,
98                          N_CAPABILITY_GREENFIELD,
99                          N_CAPABILITY_SGI20,
100                          N_CAPABILITY_SGI40]
101
102    AC_CAPABILITY_VHT160 = object()
103    AC_CAPABILITY_VHT160_80PLUS80 = object()
104    AC_CAPABILITY_RXLDPC = object()
105    AC_CAPABILITY_SHORT_GI_80 = object()
106    AC_CAPABILITY_SHORT_GI_160 = object()
107    AC_CAPABILITY_TX_STBC_2BY1 = object()
108    AC_CAPABILITY_RX_STBC_1 = object()
109    AC_CAPABILITY_RX_STBC_12 = object()
110    AC_CAPABILITY_RX_STBC_123 = object()
111    AC_CAPABILITY_RX_STBC_1234 = object()
112    AC_CAPABILITY_SU_BEAMFORMER = object()
113    AC_CAPABILITY_SU_BEAMFORMEE = object()
114    AC_CAPABILITY_BF_ANTENNA_2 = object()
115    AC_CAPABILITY_SOUNDING_DIMENSION_2 = object()
116    AC_CAPABILITY_MU_BEAMFORMER = object()
117    AC_CAPABILITY_MU_BEAMFORMEE = object()
118    AC_CAPABILITY_VHT_TXOP_PS = object()
119    AC_CAPABILITY_HTC_VHT = object()
120    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object()
121    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object()
122    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object()
123    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object()
124    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object()
125    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object()
126    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object()
127    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object()
128    AC_CAPABILITY_VHT_LINK_ADAPT2 = object()
129    AC_CAPABILITY_VHT_LINK_ADAPT3 = object()
130    AC_CAPABILITY_RX_ANTENNA_PATTERN = object()
131    AC_CAPABILITY_TX_ANTENNA_PATTERN = object()
132    AC_CAPABILITIES_MAPPING = {
133            AC_CAPABILITY_VHT160: '[VHT160]',
134            AC_CAPABILITY_VHT160_80PLUS80: '[VHT160-80PLUS80]',
135            AC_CAPABILITY_RXLDPC: '[RXLDPC]',
136            AC_CAPABILITY_SHORT_GI_80: '[SHORT-GI-80]',
137            AC_CAPABILITY_SHORT_GI_160: '[SHORT-GI-160]',
138            AC_CAPABILITY_TX_STBC_2BY1: '[TX-STBC-2BY1]',
139            AC_CAPABILITY_RX_STBC_1: '[RX-STBC-1]',
140            AC_CAPABILITY_RX_STBC_12: '[RX-STBC-12]',
141            AC_CAPABILITY_RX_STBC_123: '[RX-STBC-123]',
142            AC_CAPABILITY_RX_STBC_1234: '[RX-STBC-1234]',
143            AC_CAPABILITY_SU_BEAMFORMER: '[SU-BEAMFORMER]',
144            AC_CAPABILITY_SU_BEAMFORMEE: '[SU-BEAMFORMEE]',
145            AC_CAPABILITY_BF_ANTENNA_2: '[BF-ANTENNA-2]',
146            AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING-DIMENSION-2]',
147            AC_CAPABILITY_MU_BEAMFORMER: '[MU-BEAMFORMER]',
148            AC_CAPABILITY_MU_BEAMFORMEE: '[MU-BEAMFORMEE]',
149            AC_CAPABILITY_VHT_TXOP_PS: '[VHT-TXOP-PS]',
150            AC_CAPABILITY_HTC_VHT: '[HTC-VHT]',
151            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX-A-MPDU-LEN-EXP0]',
152            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX-A-MPDU-LEN-EXP1]',
153            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX-A-MPDU-LEN-EXP2]',
154            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX-A-MPDU-LEN-EXP3]',
155            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX-A-MPDU-LEN-EXP4]',
156            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX-A-MPDU-LEN-EXP5]',
157            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX-A-MPDU-LEN-EXP6]',
158            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX-A-MPDU-LEN-EXP7]',
159            AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT-LINK-ADAPT2]',
160            AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT-LINK-ADAPT3]',
161            AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX-ANTENNA-PATTERN]',
162            AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX-ANTENNA-PATTERN]'}
163
164    HT_CHANNEL_WIDTH_20 = object()
165    HT_CHANNEL_WIDTH_40_PLUS = object()
166    HT_CHANNEL_WIDTH_40_MINUS = object()
167
168    HT_NAMES = {
169        HT_CHANNEL_WIDTH_20: 'HT20',
170        HT_CHANNEL_WIDTH_40_PLUS: 'HT40+',
171        HT_CHANNEL_WIDTH_40_MINUS: 'HT40-',
172    }
173
174    VHT_CHANNEL_WIDTH_20 = object()
175    VHT_CHANNEL_WIDTH_40 = object()
176    VHT_CHANNEL_WIDTH_80 = object()
177    VHT_CHANNEL_WIDTH_160 = object()
178    VHT_CHANNEL_WIDTH_80_80 = object()
179
180    # Human readable names for these channel widths.
181    VHT_NAMES = {
182            VHT_CHANNEL_WIDTH_20: 'VHT20',
183            VHT_CHANNEL_WIDTH_40: 'VHT40',
184            VHT_CHANNEL_WIDTH_80: 'VHT80',
185            VHT_CHANNEL_WIDTH_160: 'VHT160',
186            VHT_CHANNEL_WIDTH_80_80: 'VHT80+80',
187    }
188
189    # This is a loose merging of the rules for US and EU regulatory
190    # domains as taken from IEEE Std 802.11-2016 Appendix E.  For instance,
191    # we tolerate HT40 in channels 149-161 (not allowed in EU), but also
192    # tolerate HT40+ on channel 7 (not allowed in the US).  We take the loose
193    # definition so that we don't prohibit testing in either domain.
194    HT40_ALLOW_MAP = {N_CAPABILITY_HT40_MINUS: list(range(5, 14)) +
195                                           list(range(40, 65, 8)) +
196                                           list(range(104, 145, 8)) +
197                                           [153, 161],
198                  N_CAPABILITY_HT40_PLUS: list(range(1, 10)) +
199                                           list(range(36, 61, 8)) +
200                                           list(range(100, 141, 8)) +
201                                           [149, 157]}
202
203    PMF_SUPPORT_DISABLED = 0
204    PMF_SUPPORT_ENABLED = 1
205    PMF_SUPPORT_REQUIRED = 2
206    PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED,
207                          PMF_SUPPORT_ENABLED,
208                          PMF_SUPPORT_REQUIRED)
209
210    DRIVER_NAME = 'nl80211'
211
212
213    @staticmethod
214    def get_channel_for_frequency(frequency):
215        """Returns the channel number associated with a given frequency.
216
217        @param value: int frequency in MHz.
218
219        @return int frequency associated with the channel.
220
221        """
222        return HostapConfig.CHANNEL_MAP[frequency]
223
224
225    @staticmethod
226    def get_frequency_for_channel(channel):
227        """Returns the frequency associated with a given channel number.
228
229        @param value: int channel number.
230
231        @return int frequency in MHz associated with the channel.
232
233        """
234        for frequency, channel_iter in six.iteritems(HostapConfig.CHANNEL_MAP):
235            if channel == channel_iter:
236                return frequency
237        else:
238            raise error.TestFail('Unknown channel value: %r.' % channel)
239
240
241    @property
242    def _get_default_config(self):
243        """@return dict of default options for hostapd."""
244        return collections.OrderedDict([
245                ('hw_mode', 'g'),
246                ('logger_syslog', '-1'),
247                ('logger_syslog_level', '0'),
248                # default RTS and frag threshold to ``off''
249                ('rts_threshold', '-1'),
250                ('fragm_threshold', '2346'),
251                ('driver', self.DRIVER_NAME)])
252
253
254    @property
255    def _ht40_plus_allowed(self):
256        """@return True iff HT40+ is enabled for this configuration."""
257        channel_supported = (self.channel in
258                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
259        return ((self.N_CAPABILITY_HT40_PLUS in self._n_capabilities or
260                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
261                channel_supported)
262
263
264    @property
265    def _ht40_minus_allowed(self):
266        """@return True iff HT40- is enabled for this configuration."""
267        channel_supported = (self.channel in
268                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
269        return ((self.N_CAPABILITY_HT40_MINUS in self._n_capabilities or
270                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
271                channel_supported)
272
273
274    @property
275    def _hostapd_ht_capabilities(self):
276        """@return string suitable for the ht_capab= line in a hostapd config"""
277        ret = []
278        if self._ht40_plus_allowed:
279            ret.append('[HT40+]')
280        elif self._ht40_minus_allowed:
281            ret.append('[HT40-]')
282        if self.N_CAPABILITY_GREENFIELD in self._n_capabilities:
283            logging.warning('Greenfield flag is ignored for hostap...')
284        if self.N_CAPABILITY_SGI20 in self._n_capabilities:
285            ret.append('[SHORT-GI-20]')
286        if self.N_CAPABILITY_SGI40 in self._n_capabilities:
287            ret.append('[SHORT-GI-40]')
288        return ''.join(ret)
289
290
291    @property
292    def _hostapd_vht_capabilities(self):
293        """@return string suitable for the vht_capab= line in a hostapd config.
294        """
295        ret = []
296        for cap in list(self.AC_CAPABILITIES_MAPPING.keys()):
297            if cap in self._ac_capabilities:
298                ret.append(self.AC_CAPABILITIES_MAPPING[cap])
299        return ''.join(ret)
300
301
302    @property
303    def _require_ht(self):
304        """@return True iff clients should be required to support HT."""
305        return self._mode == self.MODE_11N_PURE
306
307
308    @property
309    def require_vht(self):
310        """@return True iff clients should be required to support VHT."""
311        return self._mode == self.MODE_11AC_PURE
312
313
314    @property
315    def _hw_mode(self):
316        """@return string hardware mode understood by hostapd."""
317        if self._mode == self.MODE_11A:
318            return self.MODE_11A
319        if self._mode == self.MODE_11B:
320            return self.MODE_11B
321        if self._mode == self.MODE_11G:
322            return self.MODE_11G
323        if self._is_11n or self.is_11ac:
324            # For their own historical reasons, hostapd wants it this way.
325            if self._frequency > 5000:
326                return self.MODE_11A
327
328            return self.MODE_11G
329
330        raise error.TestFail('Invalid mode.')
331
332    @property
333    def mode(self):
334        """@return string hardware mode."""
335        return self._mode
336
337    @property
338    def channel_width(self):
339        """@return object channel width.
340        Note: This property ignores legacy rate (e.g., 11g), It will return
341              None for these rate.
342        """
343        ht_channel_width = self._ht_mode
344        if self.vht_channel_width is not None:
345            if (
346                    self.vht_channel_width == self.VHT_CHANNEL_WIDTH_40
347                    or self.vht_channel_width == self.VHT_CHANNEL_WIDTH_20):
348                if ht_channel_width == self.HT_CHANNEL_WIDTH_20:
349                    return self.VHT_CHANNEL_WIDTH_20
350            return self.vht_channel_width
351        if ht_channel_width:
352            return ht_channel_width
353        return None
354
355    @property
356    def _is_11n(self):
357        """@return True iff we're trying to host an 802.11n network."""
358        return self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE)
359
360
361    @property
362    def is_11ac(self):
363        """@return True iff we're trying to host an 802.11ac network."""
364        return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE)
365
366
367    @property
368    def channel(self):
369        """@return int channel number for self.frequency."""
370        return self.get_channel_for_frequency(self.frequency)
371
372
373    @channel.setter
374    def channel(self, value):
375        """Sets the channel number to configure hostapd to listen on.
376
377        @param value: int channel number.
378
379        """
380        self.frequency = self.get_frequency_for_channel(value)
381
382
383    @property
384    def frequency(self):
385        """@return int frequency for hostapd to listen on."""
386        return self._frequency
387
388
389    @frequency.setter
390    def frequency(self, value):
391        """Sets the frequency for hostapd to listen on.
392
393        @param value: int frequency in MHz.
394
395        """
396        if value not in self.CHANNEL_MAP or not self.supports_frequency(value):
397            raise error.TestFail('Tried to set an invalid frequency: %r.' %
398                                 value)
399
400        self._frequency = value
401
402
403    @property
404    def ssid(self):
405        """@return string SSID."""
406        return self._ssid
407
408
409    @ssid.setter
410    def ssid(self, value):
411        """Sets the ssid for the hostapd.
412
413        @param value: string ssid name.
414
415        """
416        self._ssid = value
417
418
419    @property
420    def _ht_mode(self):
421        """
422        @return object one of ( None,
423                                HT_CHANNEL_WIDTH_40_PLUS,
424                                HT_CHANNEL_WIDTH_40_MINUS,
425                                HT_CHANNEL_WIDTH_20)
426        """
427        if self._is_11n or self.is_11ac:
428            if self._ht40_plus_allowed:
429                return self.HT_CHANNEL_WIDTH_40_PLUS
430            if self._ht40_minus_allowed:
431                return self.HT_CHANNEL_WIDTH_40_MINUS
432            return self.HT_CHANNEL_WIDTH_20
433        return None
434
435
436    @property
437    def packet_capture_mode(self):
438        """Get an appropriate packet capture HT/VHT parameter.
439
440        When we go to configure a raw monitor we need to configure
441        the phy to listen on the correct channel.  Part of doing
442        so is to specify the channel width for HT/VHT channels.  In the
443        case that the AP is configured to be either HT40+ or HT40-,
444        we could return the wrong parameter because we don't know which
445        configuration will be chosen by hostap.
446
447        @return object width_type parameter from packet_capturer.
448
449        """
450
451        if (not self.vht_channel_width
452                    or self.vht_channel_width == self.VHT_CHANNEL_WIDTH_40
453                    or self.vht_channel_width == self.VHT_CHANNEL_WIDTH_20):
454            # if it is VHT40, capture packets on the correct 40MHz band since
455            # for packet capturing purposes, only the channel width matters
456            ht_mode = self._ht_mode
457            if ht_mode == self.HT_CHANNEL_WIDTH_40_PLUS:
458                return packet_capturer.WIDTH_HT40_PLUS
459            if ht_mode == self.HT_CHANNEL_WIDTH_40_MINUS:
460                return packet_capturer.WIDTH_HT40_MINUS
461            if ht_mode == self.HT_CHANNEL_WIDTH_20:
462                return packet_capturer.WIDTH_HT20
463
464        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
465            return packet_capturer.WIDTH_VHT80
466        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
467            return packet_capturer.WIDTH_VHT160
468        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
469            return packet_capturer.WIDTH_VHT80_80
470        return None
471
472
473    @property
474    def perf_loggable_description(self):
475        """@return string test description suitable for performance logging."""
476        mode = 'mode%s' % (
477                self.printable_mode.replace('+', 'p').replace('-', 'm'))
478        channel = 'ch%03d' % self.channel
479        return '_'.join([channel, mode, self._security_config.security])
480
481
482    @property
483    def printable_mode(self):
484        """@return human readable mode string."""
485
486        if self.vht_channel_width is not None:
487            return self.VHT_NAMES[self.vht_channel_width]
488
489        ht_mode = self._ht_mode
490        if ht_mode:
491            return self.HT_NAMES[ht_mode]
492
493        return '11' + self._hw_mode.upper()
494
495
496    @property
497    def ssid_suffix(self):
498        """@return meaningful suffix for SSID."""
499        return 'ch%d' % self.channel
500
501
502    @property
503    def security_config(self):
504        """@return SecurityConfig security config object"""
505        return self._security_config
506
507
508    @property
509    def hide_ssid(self):
510        """@return bool _hide_ssid flag."""
511        return self._hide_ssid
512
513
514    @property
515    def scenario_name(self):
516        """@return string _scenario_name value, or None."""
517        return self._scenario_name
518
519
520    @property
521    def min_streams(self):
522        """@return int _min_streams value, or None."""
523        return self._min_streams
524
525
526    @property
527    def frag_threshold(self):
528        """@return int frag threshold value, or None."""
529        return self._frag_threshold
530
531    @property
532    def bridge(self):
533        """@return string _bridge value, or None."""
534        return self._bridge
535
536    @property
537    def max_stas(self):
538        """@return int _max_stas value, or None."""
539        return self._max_stas
540
541    @property
542    def supported_rates(self):
543        """@return list of supported bitrates (in Mbps, or None if not
544           specified.
545        """
546        return self._supported_rates
547
548    def __init__(self, mode=MODE_11B, channel=None, frequency=None,
549                 n_capabilities=None, hide_ssid=None, beacon_interval=None,
550                 dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
551                 force_wmm=None, security_config=None,
552                 pmf_support=PMF_SUPPORT_DISABLED,
553                 obss_interval=None,
554                 vht_channel_width=None,
555                 vht_center_channel=None,
556                 ac_capabilities=None,
557                 spectrum_mgmt_required=None,
558                 scenario_name=None,
559                 supported_rates=None,
560                 basic_rates=None,
561                 min_streams=None,
562                 nas_id=None,
563                 mdid=None,
564                 r1kh_id=None,
565                 r0kh=None,
566                 r1kh=None,
567                 bridge=None,
568                 max_stas=None):
569        """Construct a HostapConfig.
570
571        You may specify channel or frequency, but not both.  Both options
572        are checked for validity (i.e. you can't specify an invalid channel
573        or a frequency that will not be accepted).
574
575        According to IEEE80211ac, both HT and VHT channel width fields must
576        be used to select the desired VHT channel width. Refer to IEEE 80211ac
577        Tables (VHT Operation Information subfields) and VHT BSS operating
578        channel width.
579
580        @param mode string MODE_11x defined above.
581        @param channel int channel number.
582        @param frequency int frequency of channel.
583        @param n_capabilities list of N_CAPABILITY_x defined above.
584        @param hide_ssid True if we should set up a hidden SSID.
585        @param beacon_interval int beacon interval of AP.
586        @param dtim_period int include a DTIM every |dtim_period| beacons.
587        @param frag_threshold int maximum outgoing data frame size.
588        @param ssid string up to 32 byte SSID overriding the router default.
589        @param bssid string like 00:11:22:33:44:55.
590        @param force_wmm True if we should force WMM on, False if we should
591            force it off, None if we shouldn't force anything.
592        @param security_config SecurityConfig object.
593        @param pmf_support one of PMF_SUPPORT_* above.  Controls whether the
594            client supports/must support 802.11w.
595        @param obss_interval int interval in seconds that client should be
596            required to do background scans for overlapping BSSes.
597        @param vht_channel_width object channel width
598        @param vht_center_channel int center channel of segment 0.
599        @param ac_capabilities list of AC_CAPABILITY_x defined above.
600        @param spectrum_mgmt_required True if we require the DUT to support
601            spectrum management.
602        @param scenario_name string to be included in file names, instead
603            of the interface name.
604        @param supported_rates list of rates (in Mbps) that the AP should
605             advertise.
606        @param basic_rates list of basic rates (in Mbps) that the AP should
607            advertise.
608        @param min_streams int number of spatial streams required.
609        @param nas_id string for RADIUS messages (needed for 802.11r)
610        @param mdid string used to indicate a group of APs for FT
611        @param r1kh_id string PMK-R1 key holder id for FT
612        @param r0kh string R0KHs in the same mobility domain
613        @param r1kh string R1KHs in the same mobility domain
614        @param bridge string bridge interface to use, if any
615        @param max_stas int maximum number of STAs allowed to connect to AP.
616
617        """
618        super(HostapConfig, self).__init__()
619        if channel is not None and frequency is not None:
620            raise error.TestError('Specify either frequency or channel '
621                                  'but not both.')
622
623        if n_capabilities is None:
624            n_capabilities = []
625        if ac_capabilities is None:
626            ac_capabilities = []
627        self._wmm_enabled = False
628        unknown_caps = [cap for cap in n_capabilities
629                        if cap not in self.ALL_N_CAPABILITIES]
630        if unknown_caps:
631            raise error.TestError('Unknown capabilities: %r' % unknown_caps)
632
633        self._n_capabilities = set(n_capabilities)
634        if self._n_capabilities:
635            self._wmm_enabled = True
636        if self._n_capabilities and mode is None:
637            mode = self.MODE_11N_PURE
638        self._mode = mode
639
640        self._frequency = None
641        if channel:
642            self.channel = channel
643        elif frequency:
644            self.frequency = frequency
645        else:
646            raise error.TestError('Specify either frequency or channel.')
647
648        if not self.supports_frequency(self.frequency):
649            raise error.TestFail('Configured a mode %s that does not support '
650                                 'frequency %d' % (self._mode, self.frequency))
651
652        self._hide_ssid = hide_ssid
653        self._beacon_interval = beacon_interval
654        self._dtim_period = dtim_period
655        self._frag_threshold = frag_threshold
656        if ssid and len(ssid) > 32:
657            raise error.TestFail('Tried to specify SSID that was too long.')
658
659        self._ssid = ssid
660        self._bssid = bssid
661        if force_wmm is not None:
662            self._wmm_enabled = force_wmm
663        if pmf_support not in self.PMF_SUPPORT_VALUES:
664            raise error.TestFail('Invalid value for pmf_support: %r' %
665                                 pmf_support)
666
667        self._pmf_support = pmf_support
668        self._security_config = (copy.copy(security_config) or
669                                xmlrpc_security_types.SecurityConfig())
670        self._obss_interval = obss_interval
671        if (vht_channel_width == self.VHT_CHANNEL_WIDTH_40
672                    or vht_channel_width == self.VHT_CHANNEL_WIDTH_20):
673            self._vht_oper_chwidth = 0
674        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
675            self._vht_oper_chwidth = 1
676        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
677            self._vht_oper_chwidth = 2
678        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
679            self._vht_oper_chwidth = 3
680        elif vht_channel_width is not None:
681            raise error.TestFail('Invalid channel width')
682        self.vht_channel_width = vht_channel_width
683        # TODO(zqiu) Add checking for center channel based on the channel width
684        # and operating channel.
685        self._vht_oper_centr_freq_seg0_idx = vht_center_channel
686        self._ac_capabilities = set(ac_capabilities)
687        self._spectrum_mgmt_required = spectrum_mgmt_required
688        self._scenario_name = scenario_name
689        self._supported_rates = supported_rates
690        self._basic_rates = basic_rates
691        self._min_streams = min_streams
692        self._nas_id = nas_id
693        self._mdid = mdid
694        self._r1kh_id = r1kh_id
695        self._r0kh = r0kh
696        self._r1kh = r1kh
697        self._bridge = bridge
698        # keep _max_stas in [0, 2007], as valid AIDs must be in [1, 2007]
699        if max_stas is None:
700            self._max_stas = None
701        else:
702            self._max_stas = max(0, min(max_stas, 2007))
703
704
705    def __repr__(self):
706        return ('%s(mode=%r, channel=%r, frequency=%r, '
707                'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
708                'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
709                'wmm_enabled=%r, security_config=%r, '
710                'spectrum_mgmt_required=%r)' % (
711                        self.__class__.__name__,
712                        self._mode,
713                        self.channel,
714                        self.frequency,
715                        self._n_capabilities,
716                        self._hide_ssid,
717                        self._beacon_interval,
718                        self._dtim_period,
719                        self._frag_threshold,
720                        self._ssid,
721                        self._bssid,
722                        self._wmm_enabled,
723                        self._security_config,
724                        self._spectrum_mgmt_required))
725
726
727    def supports_channel(self, value):
728        """Check whether channel is supported by the current hardware mode.
729
730        @param value: int channel to check.
731        @return True iff the current mode supports the band of the channel.
732
733        """
734        for freq, channel in six.iteritems(self.CHANNEL_MAP):
735            if channel == value:
736                return self.supports_frequency(freq)
737
738        return False
739
740
741    def supports_frequency(self, frequency):
742        """Check whether frequency is supported by the current hardware mode.
743
744        @param frequency: int frequency to check.
745        @return True iff the current mode supports the band of the frequency.
746
747        """
748        if self._mode == self.MODE_11A and frequency < 5000:
749            return False
750
751        if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000:
752            return False
753
754        if frequency not in self.CHANNEL_MAP:
755            return False
756
757        channel = self.CHANNEL_MAP[frequency]
758        supports_plus = (channel in
759                         self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
760        supports_minus = (channel in
761                          self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
762        if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and
763                not supports_plus):
764            return False
765
766        if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and
767                not supports_minus):
768            return False
769
770        if (self.N_CAPABILITY_HT40 in self._n_capabilities and
771                not supports_plus and not supports_minus):
772            return False
773
774        return True
775
776
777    def generate_dict(self, interface, control_interface, ssid):
778        """Generate config dictionary.
779
780        Generate config dictionary for the given |interface|.
781
782        @param interface: string interface to generate config dict for.
783        @param control_interface: string control interface
784        @param ssid: string SSID of the AP.
785        @return dict of hostap configurations.
786
787        """
788        # Start with the default config parameters.
789        conf = self._get_default_config
790        conf['ssid'] = (self._ssid or ssid)
791        if self._bssid:
792            conf['bssid'] = self._bssid
793        conf['channel'] = self.channel
794        conf['hw_mode'] = self._hw_mode
795
796        # hostapd specifies rates in units of 100Kbps.
797        rate_to_100kbps = lambda rate: str(int(rate * 10))
798        if self._supported_rates:
799            conf['supported_rates'] = ' '.join(map(rate_to_100kbps,
800                                                   self._supported_rates))
801        if self._basic_rates:
802            conf['basic_rates'] = ' '.join(map(rate_to_100kbps,
803                                               self._basic_rates))
804
805        if self._hide_ssid:
806            conf['ignore_broadcast_ssid'] = 1
807        if self._is_11n or self.is_11ac:
808            conf['ieee80211n'] = 1
809            conf['ht_capab'] = self._hostapd_ht_capabilities
810        if self.is_11ac:
811            conf['ieee80211ac'] = 1
812            conf['vht_oper_chwidth'] = self._vht_oper_chwidth
813            if self._vht_oper_centr_freq_seg0_idx is not None:
814                conf['vht_oper_centr_freq_seg0_idx'] = \
815                        self._vht_oper_centr_freq_seg0_idx
816            conf['vht_capab'] = self._hostapd_vht_capabilities
817        if self._wmm_enabled:
818            conf['wmm_enabled'] = 1
819        if self._require_ht:
820            conf['require_ht'] = 1
821        if self.require_vht:
822            conf['require_vht'] = 1
823        if self._beacon_interval:
824            conf['beacon_int'] = self._beacon_interval
825        if self._dtim_period:
826            conf['dtim_period'] = self._dtim_period
827        if self._frag_threshold:
828            conf['fragm_threshold'] = self._frag_threshold
829        if self._pmf_support:
830            conf['ieee80211w'] = self._pmf_support
831        if self._obss_interval:
832            conf['obss_interval'] = self._obss_interval
833        if self._nas_id:
834            conf['nas_identifier'] = self._nas_id
835        if self._mdid:
836            conf['mobility_domain'] = self._mdid
837        if self._r1kh_id:
838            conf['r1_key_holder'] = self._r1kh_id
839        if self._r0kh:
840            conf['r0kh'] = self._r0kh
841        if self._r1kh:
842            conf['r1kh'] = self._r1kh
843        if self._bridge:
844            conf['bridge'] = self._bridge
845        if self._max_stas is not None:
846            conf['max_num_sta'] = self._max_stas
847        conf['interface'] = interface
848        conf['ctrl_interface'] = control_interface
849        if self._spectrum_mgmt_required:
850            # To set spectrum_mgmt_required, we must first set
851            # local_pwr_constraint. And to set local_pwr_constraint,
852            # we must first set ieee80211d. And to set ieee80211d, ...
853            # Point being: order matters here.
854            conf['country_code'] = 'US'  # Required for local_pwr_constraint
855            conf['ieee80211d'] = 1  # Required for local_pwr_constraint
856            conf['local_pwr_constraint'] = 0  # No local constraint
857            conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
858        conf.update(self._security_config.get_hostapd_config())
859        return conf
860