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    # This is a loose merging of the rules for US and EU regulatory
162    # domains as taken from IEEE Std 802.11-2012 Appendix E.  For instance,
163    # we tolerate HT40 in channels 149-161 (not allowed in EU), but also
164    # tolerate HT40+ on channel 7 (not allowed in the US).  We take the loose
165    # definition so that we don't prohibit testing in either domain.
166    HT40_ALLOW_MAP = {N_CAPABILITY_HT40_MINUS: range(6, 14) +
167                                               range(40, 65, 8) +
168                                               range(104, 137, 8) +
169                                               [153, 161],
170                      N_CAPABILITY_HT40_PLUS: range(1, 8) +
171                                              range(36, 61, 8) +
172                                              range(100, 133, 8) +
173                                              [149, 157]}
174
175    PMF_SUPPORT_DISABLED = 0
176    PMF_SUPPORT_ENABLED = 1
177    PMF_SUPPORT_REQUIRED = 2
178    PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED,
179                          PMF_SUPPORT_ENABLED,
180                          PMF_SUPPORT_REQUIRED)
181
182    DRIVER_NAME = 'nl80211'
183
184
185    @staticmethod
186    def get_channel_for_frequency(frequency):
187        """Returns the channel number associated with a given frequency.
188
189        @param value: int frequency in MHz.
190
191        @return int frequency associated with the channel.
192
193        """
194        return HostapConfig.CHANNEL_MAP[frequency]
195
196
197    @staticmethod
198    def get_frequency_for_channel(channel):
199        """Returns the frequency associated with a given channel number.
200
201        @param value: int channel number.
202
203        @return int frequency in MHz associated with the channel.
204
205        """
206        for frequency, channel_iter in HostapConfig.CHANNEL_MAP.iteritems():
207            if channel == channel_iter:
208                return frequency
209        else:
210            raise error.TestFail('Unknown channel value: %r.' % channel)
211
212
213    @property
214    def _get_default_config(self):
215        """@return dict of default options for hostapd."""
216        return collections.OrderedDict([
217                ('hw_mode', 'g'),
218                ('logger_syslog', '-1'),
219                ('logger_syslog_level', '0'),
220                # default RTS and frag threshold to ``off''
221                ('rts_threshold', '2347'),
222                ('fragm_threshold', '2346'),
223                ('driver', self.DRIVER_NAME)])
224
225
226    @property
227    def _ht40_plus_allowed(self):
228        """@return True iff HT40+ is enabled for this configuration."""
229        channel_supported = (self.channel in
230                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
231        return ((self.N_CAPABILITY_HT40_PLUS in self._n_capabilities or
232                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
233                channel_supported)
234
235
236    @property
237    def _ht40_minus_allowed(self):
238        """@return True iff HT40- is enabled for this configuration."""
239        channel_supported = (self.channel in
240                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
241        return ((self.N_CAPABILITY_HT40_MINUS in self._n_capabilities or
242                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
243                channel_supported)
244
245
246    @property
247    def _hostapd_ht_capabilities(self):
248        """@return string suitable for the ht_capab= line in a hostapd config"""
249        ret = []
250        if self._ht40_plus_allowed:
251            ret.append('[HT40+]')
252        elif self._ht40_minus_allowed:
253            ret.append('[HT40-]')
254        if self.N_CAPABILITY_GREENFIELD in self._n_capabilities:
255            logging.warning('Greenfield flag is ignored for hostap...')
256        if self.N_CAPABILITY_SGI20 in self._n_capabilities:
257            ret.append('[SHORT-GI-20]')
258        if self.N_CAPABILITY_SGI40 in self._n_capabilities:
259            ret.append('[SHORT-GI-40]')
260        return ''.join(ret)
261
262
263    @property
264    def _hostapd_vht_capabilities(self):
265        """@return string suitable for the vht_capab= line in a hostapd config.
266        """
267        ret = []
268        for cap in self.AC_CAPABILITIES_MAPPING.keys():
269            if cap in self._ac_capabilities:
270                ret.append(self.AC_CAPABILITIES_MAPPING[cap])
271        return ''.join(ret)
272
273
274    @property
275    def _require_ht(self):
276        """@return True iff clients should be required to support HT."""
277        # TODO(wiley) Why? (crbug.com/237370)
278        logging.warning('Not enforcing pure N mode because Snow does '
279                        'not seem to support it...')
280        return False
281
282
283    @property
284    def _require_vht(self):
285        """@return True iff clients should be required to support VHT."""
286        return self._mode == self.MODE_11AC_PURE
287
288
289    @property
290    def _hw_mode(self):
291        """@return string hardware mode understood by hostapd."""
292        if self._mode == self.MODE_11A:
293            return self.MODE_11A
294        if self._mode == self.MODE_11B:
295            return self.MODE_11B
296        if self._mode == self.MODE_11G:
297            return self.MODE_11G
298        if self._is_11n or self.is_11ac:
299            # For their own historical reasons, hostapd wants it this way.
300            if self._frequency > 5000:
301                return self.MODE_11A
302
303            return self.MODE_11G
304
305        raise error.TestFail('Invalid mode.')
306
307
308    @property
309    def _is_11n(self):
310        """@return True iff we're trying to host an 802.11n network."""
311        return self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE)
312
313
314    @property
315    def is_11ac(self):
316        """@return True iff we're trying to host an 802.11ac network."""
317        return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE)
318
319
320    @property
321    def channel(self):
322        """@return int channel number for self.frequency."""
323        return self.get_channel_for_frequency(self.frequency)
324
325
326    @channel.setter
327    def channel(self, value):
328        """Sets the channel number to configure hostapd to listen on.
329
330        @param value: int channel number.
331
332        """
333        self.frequency = self.get_frequency_for_channel(value)
334
335
336    @property
337    def frequency(self):
338        """@return int frequency for hostapd to listen on."""
339        return self._frequency
340
341
342    @frequency.setter
343    def frequency(self, value):
344        """Sets the frequency for hostapd to listen on.
345
346        @param value: int frequency in MHz.
347
348        """
349        if value not in self.CHANNEL_MAP or not self.supports_frequency(value):
350            raise error.TestFail('Tried to set an invalid frequency: %r.' %
351                                 value)
352
353        self._frequency = value
354
355
356    @property
357    def ssid(self):
358        """@return string SSID."""
359        return self._ssid
360
361
362    @ssid.setter
363    def ssid(self, value):
364        """Sets the ssid for the hostapd.
365
366        @param value: string ssid name.
367
368        """
369        self._ssid = value
370
371
372    @property
373    def ht_packet_capture_mode(self):
374        """Get an appropriate packet capture HT parameter.
375
376        When we go to configure a raw monitor we need to configure
377        the phy to listen on the correct channel.  Part of doing
378        so is to specify the channel width for HT channels.  In the
379        case that the AP is configured to be either HT40+ or HT40-,
380        we could return the wrong parameter because we don't know which
381        configuration will be chosen by hostap.
382
383        @return string HT parameter for frequency configuration.
384
385        """
386        if not self._is_11n:
387            return None
388
389        if self._ht40_plus_allowed:
390            return 'HT40+'
391
392        if self._ht40_minus_allowed:
393            return 'HT40-'
394
395        return 'HT20'
396
397
398    @property
399    def perf_loggable_description(self):
400        """@return string test description suitable for performance logging."""
401        mode = 'mode%s' % (
402                self.printable_mode.replace('+', 'p').replace('-', 'm'))
403        channel = 'ch%03d' % self.channel
404        return '_'.join([channel, mode, self._security_config.security])
405
406
407    @property
408    def printable_mode(self):
409        """@return human readable mode string."""
410        if self._is_11n:
411            return self.ht_packet_capture_mode
412
413        return '11' + self._hw_mode.upper()
414
415
416    @property
417    def ssid_suffix(self):
418        """@return meaningful suffix for SSID."""
419        return 'ch%d' % self.channel
420
421
422    @property
423    def security_config(self):
424        """@return SecurityConfig security config object"""
425        return self._security_config
426
427
428    @property
429    def hide_ssid(self):
430        """@return bool _hide_ssid flag."""
431        return self._hide_ssid
432
433
434    @property
435    def beacon_footer(self):
436        """@return bool _beacon_footer value."""
437        return self._beacon_footer
438
439
440    @property
441    def scenario_name(self):
442        """@return string _scenario_name value, or None."""
443        return self._scenario_name
444
445
446    @property
447    def min_streams(self):
448        """@return int _min_streams value, or None."""
449        return self._min_streams
450
451
452    def __init__(self, mode=MODE_11B, channel=None, frequency=None,
453                 n_capabilities=[], hide_ssid=None, beacon_interval=None,
454                 dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
455                 force_wmm=None, security_config=None,
456                 pmf_support=PMF_SUPPORT_DISABLED,
457                 obss_interval=None,
458                 vht_channel_width=None,
459                 vht_center_channel=None,
460                 ac_capabilities=[],
461                 beacon_footer='',
462                 spectrum_mgmt_required=None,
463                 scenario_name=None,
464                 min_streams=None):
465        """Construct a HostapConfig.
466
467        You may specify channel or frequency, but not both.  Both options
468        are checked for validity (i.e. you can't specify an invalid channel
469        or a frequency that will not be accepted).
470
471        @param mode string MODE_11x defined above.
472        @param channel int channel number.
473        @param frequency int frequency of channel.
474        @param n_capabilities list of N_CAPABILITY_x defined above.
475        @param hide_ssid True if we should set up a hidden SSID.
476        @param beacon_interval int beacon interval of AP.
477        @param dtim_period int include a DTIM every |dtim_period| beacons.
478        @param frag_threshold int maximum outgoing data frame size.
479        @param ssid string up to 32 byte SSID overriding the router default.
480        @param bssid string like 00:11:22:33:44:55.
481        @param force_wmm True if we should force WMM on, False if we should
482            force it off, None if we shouldn't force anything.
483        @param security_config SecurityConfig object.
484        @param pmf_support one of PMF_SUPPORT_* above.  Controls whether the
485            client supports/must support 802.11w.
486        @param obss_interval int interval in seconds that client should be
487            required to do background scans for overlapping BSSes.
488        @param vht_channel_width object channel width
489        @param vht_center_channel int center channel of segment 0.
490        @param ac_capabilities list of AC_CAPABILITY_x defined above.
491        @param beacon_footer string containing (unvalidated) IE data to be
492            placed at the end of the beacon.
493        @param spectrum_mgmt_required True if we require the DUT to support
494            spectrum management.
495        @param scenario_name string to be included in file names, instead
496            of the interface name.
497        @param min_streams int number of spatial streams required.
498
499        """
500        super(HostapConfig, self).__init__()
501        if channel is not None and frequency is not None:
502            raise error.TestError('Specify either frequency or channel '
503                                  'but not both.')
504
505        self._wmm_enabled = False
506        unknown_caps = [cap for cap in n_capabilities
507                        if cap not in self.ALL_N_CAPABILITIES]
508        if unknown_caps:
509            raise error.TestError('Unknown capabilities: %r' % unknown_caps)
510
511        self._n_capabilities = set(n_capabilities)
512        if self._n_capabilities:
513            self._wmm_enabled = True
514        if self._n_capabilities and mode is None:
515            mode = self.MODE_11N_PURE
516        self._mode = mode
517
518        self._frequency = None
519        if channel:
520            self.channel = channel
521        elif frequency:
522            self.frequency = frequency
523        else:
524            raise error.TestError('Specify either frequency or channel.')
525
526        if not self.supports_frequency(self.frequency):
527            raise error.TestFail('Configured a mode %s that does not support '
528                                 'frequency %d' % (self._mode, self.frequency))
529
530        self._hide_ssid = hide_ssid
531        self._beacon_interval = beacon_interval
532        self._dtim_period = dtim_period
533        self._frag_threshold = frag_threshold
534        if ssid and len(ssid) > 32:
535            raise error.TestFail('Tried to specify SSID that was too long.')
536
537        self._ssid = ssid
538        self._bssid = bssid
539        if force_wmm is not None:
540            self._wmm_enabled = force_wmm
541        if pmf_support not in self.PMF_SUPPORT_VALUES:
542            raise error.TestFail('Invalid value for pmf_support: %r' %
543                                 pmf_support)
544
545        self._pmf_support = pmf_support
546        self._security_config = (copy.copy(security_config) or
547                                xmlrpc_security_types.SecurityConfig())
548        self._obss_interval = obss_interval
549        if vht_channel_width == self.VHT_CHANNEL_WIDTH_40:
550            self._vht_oper_chwidth = 0
551        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
552            self._vht_oper_chwidth = 1
553        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
554            self._vht_oper_chwidth = 2
555        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
556            self._vht_oper_chwidth = 3
557        elif vht_channel_width is not None:
558            raise error.TestFail('Invalid channel width')
559        # TODO(zqiu) Add checking for center channel based on the channel width
560        # and operating channel.
561        self._vht_oper_centr_freq_seg0_idx = vht_center_channel
562        self._ac_capabilities = set(ac_capabilities)
563        self._beacon_footer = beacon_footer
564        self._spectrum_mgmt_required = spectrum_mgmt_required
565        self._scenario_name = scenario_name
566        self._min_streams = min_streams
567
568
569    def __repr__(self):
570        return ('%s(mode=%r, channel=%r, frequency=%r, '
571                'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
572                'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
573                'wmm_enabled=%r, security_config=%r, '
574                'spectrum_mgmt_required=%r)' % (
575                        self.__class__.__name__,
576                        self._mode,
577                        self.channel,
578                        self.frequency,
579                        self._n_capabilities,
580                        self._hide_ssid,
581                        self._beacon_interval,
582                        self._dtim_period,
583                        self._frag_threshold,
584                        self._ssid,
585                        self._bssid,
586                        self._wmm_enabled,
587                        self._security_config,
588                        self._spectrum_mgmt_required))
589
590
591    def supports_channel(self, value):
592        """Check whether channel is supported by the current hardware mode.
593
594        @param value: int channel to check.
595        @return True iff the current mode supports the band of the channel.
596
597        """
598        for freq, channel in self.CHANNEL_MAP.iteritems():
599            if channel == value:
600                return self.supports_frequency(freq)
601
602        return False
603
604
605    def supports_frequency(self, frequency):
606        """Check whether frequency is supported by the current hardware mode.
607
608        @param frequency: int frequency to check.
609        @return True iff the current mode supports the band of the frequency.
610
611        """
612        if self._mode == self.MODE_11A and frequency < 5000:
613            return False
614
615        if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000:
616            return False
617
618        if frequency not in self.CHANNEL_MAP:
619            return False
620
621        channel = self.CHANNEL_MAP[frequency]
622        supports_plus = (channel in
623                         self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
624        supports_minus = (channel in
625                          self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
626        if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and
627                not supports_plus):
628            return False
629
630        if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and
631                not supports_minus):
632            return False
633
634        if (self.N_CAPABILITY_HT40 in self._n_capabilities and
635                not supports_plus and not supports_minus):
636            return False
637
638        return True
639
640
641    def generate_dict(self, interface, control_interface, ssid):
642        """Generate config dictionary.
643
644        Generate config dictionary for the given |interface|.
645
646        @param interface: string interface to generate config dict for.
647        @param control_interface: string control interface
648        @param ssid: string SSID of the AP.
649        @return dict of hostap configurations.
650
651        """
652        # Start with the default config parameters.
653        conf = self._get_default_config
654        conf['ssid'] = (self._ssid or ssid)
655        if self._bssid:
656            conf['bssid'] = self._bssid
657        conf['channel'] = self.channel
658        conf['hw_mode'] = self._hw_mode
659        if self._hide_ssid:
660            conf['ignore_broadcast_ssid'] = 1
661        if self._is_11n or self.is_11ac:
662            conf['ieee80211n'] = 1
663            conf['ht_capab'] = self._hostapd_ht_capabilities
664        if self.is_11ac:
665            conf['ieee80211ac'] = 1
666            conf['vht_oper_chwidth'] = self._vht_oper_chwidth
667            conf['vht_oper_centr_freq_seg0_idx'] = \
668                    self._vht_oper_centr_freq_seg0_idx
669            conf['vht_capab'] = self._hostapd_vht_capabilities
670        if self._wmm_enabled:
671            conf['wmm_enabled'] = 1
672        if self._require_ht:
673            conf['require_ht'] = 1
674        if self._require_vht:
675            conf['require_vht'] = 1
676        if self._beacon_interval:
677            conf['beacon_int'] = self._beacon_interval
678        if self._dtim_period:
679            conf['dtim_period'] = self._dtim_period
680        if self._frag_threshold:
681            conf['fragm_threshold'] = self._frag_threshold
682        if self._pmf_support:
683            conf['ieee80211w'] = self._pmf_support
684        if self._obss_interval:
685            conf['obss_interval'] = self._obss_interval
686        conf['interface'] = interface
687        conf['ctrl_interface'] = control_interface
688        if self._spectrum_mgmt_required:
689            # To set spectrum_mgmt_required, we must first set
690            # local_pwr_constraint. And to set local_pwr_constraint,
691            # we must first set ieee80211d. And to set ieee80211d, ...
692            # Point being: order matters here.
693            conf['country_code'] = 'US'  # Required for local_pwr_constraint
694            conf['ieee80211d'] = 1  # Required for local_pwr_constraint
695            conf['local_pwr_constraint'] = 0  # No local constraint
696            conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
697        conf.update(self._security_config.get_hostapd_config())
698        return conf
699