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