Home | History | Annotate | Download | only in network
      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 
      5 import collections
      6 import copy
      7 import logging
      8 
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
     11 
     12 
     13 class 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     @property
    453     def frag_threshold(self):
    454         """@return int frag threshold value, or None."""
    455         return self._frag_threshold
    456 
    457 
    458     def __init__(self, mode=MODE_11B, channel=None, frequency=None,
    459                  n_capabilities=[], hide_ssid=None, beacon_interval=None,
    460                  dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
    461                  force_wmm=None, security_config=None,
    462                  pmf_support=PMF_SUPPORT_DISABLED,
    463                  obss_interval=None,
    464                  vht_channel_width=None,
    465                  vht_center_channel=None,
    466                  ac_capabilities=[],
    467                  beacon_footer='',
    468                  spectrum_mgmt_required=None,
    469                  scenario_name=None,
    470                  min_streams=None):
    471         """Construct a HostapConfig.
    472 
    473         You may specify channel or frequency, but not both.  Both options
    474         are checked for validity (i.e. you can't specify an invalid channel
    475         or a frequency that will not be accepted).
    476 
    477         @param mode string MODE_11x defined above.
    478         @param channel int channel number.
    479         @param frequency int frequency of channel.
    480         @param n_capabilities list of N_CAPABILITY_x defined above.
    481         @param hide_ssid True if we should set up a hidden SSID.
    482         @param beacon_interval int beacon interval of AP.
    483         @param dtim_period int include a DTIM every |dtim_period| beacons.
    484         @param frag_threshold int maximum outgoing data frame size.
    485         @param ssid string up to 32 byte SSID overriding the router default.
    486         @param bssid string like 00:11:22:33:44:55.
    487         @param force_wmm True if we should force WMM on, False if we should
    488             force it off, None if we shouldn't force anything.
    489         @param security_config SecurityConfig object.
    490         @param pmf_support one of PMF_SUPPORT_* above.  Controls whether the
    491             client supports/must support 802.11w.
    492         @param obss_interval int interval in seconds that client should be
    493             required to do background scans for overlapping BSSes.
    494         @param vht_channel_width object channel width
    495         @param vht_center_channel int center channel of segment 0.
    496         @param ac_capabilities list of AC_CAPABILITY_x defined above.
    497         @param beacon_footer string containing (unvalidated) IE data to be
    498             placed at the end of the beacon.
    499         @param spectrum_mgmt_required True if we require the DUT to support
    500             spectrum management.
    501         @param scenario_name string to be included in file names, instead
    502             of the interface name.
    503         @param min_streams int number of spatial streams required.
    504 
    505         """
    506         super(HostapConfig, self).__init__()
    507         if channel is not None and frequency is not None:
    508             raise error.TestError('Specify either frequency or channel '
    509                                   'but not both.')
    510 
    511         self._wmm_enabled = False
    512         unknown_caps = [cap for cap in n_capabilities
    513                         if cap not in self.ALL_N_CAPABILITIES]
    514         if unknown_caps:
    515             raise error.TestError('Unknown capabilities: %r' % unknown_caps)
    516 
    517         self._n_capabilities = set(n_capabilities)
    518         if self._n_capabilities:
    519             self._wmm_enabled = True
    520         if self._n_capabilities and mode is None:
    521             mode = self.MODE_11N_PURE
    522         self._mode = mode
    523 
    524         self._frequency = None
    525         if channel:
    526             self.channel = channel
    527         elif frequency:
    528             self.frequency = frequency
    529         else:
    530             raise error.TestError('Specify either frequency or channel.')
    531 
    532         if not self.supports_frequency(self.frequency):
    533             raise error.TestFail('Configured a mode %s that does not support '
    534                                  'frequency %d' % (self._mode, self.frequency))
    535 
    536         self._hide_ssid = hide_ssid
    537         self._beacon_interval = beacon_interval
    538         self._dtim_period = dtim_period
    539         self._frag_threshold = frag_threshold
    540         if ssid and len(ssid) > 32:
    541             raise error.TestFail('Tried to specify SSID that was too long.')
    542 
    543         self._ssid = ssid
    544         self._bssid = bssid
    545         if force_wmm is not None:
    546             self._wmm_enabled = force_wmm
    547         if pmf_support not in self.PMF_SUPPORT_VALUES:
    548             raise error.TestFail('Invalid value for pmf_support: %r' %
    549                                  pmf_support)
    550 
    551         self._pmf_support = pmf_support
    552         self._security_config = (copy.copy(security_config) or
    553                                 xmlrpc_security_types.SecurityConfig())
    554         self._obss_interval = obss_interval
    555         if vht_channel_width == self.VHT_CHANNEL_WIDTH_40:
    556             self._vht_oper_chwidth = 0
    557         elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
    558             self._vht_oper_chwidth = 1
    559         elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
    560             self._vht_oper_chwidth = 2
    561         elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
    562             self._vht_oper_chwidth = 3
    563         elif vht_channel_width is not None:
    564             raise error.TestFail('Invalid channel width')
    565         # TODO(zqiu) Add checking for center channel based on the channel width
    566         # and operating channel.
    567         self._vht_oper_centr_freq_seg0_idx = vht_center_channel
    568         self._ac_capabilities = set(ac_capabilities)
    569         self._beacon_footer = beacon_footer
    570         self._spectrum_mgmt_required = spectrum_mgmt_required
    571         self._scenario_name = scenario_name
    572         self._min_streams = min_streams
    573 
    574 
    575     def __repr__(self):
    576         return ('%s(mode=%r, channel=%r, frequency=%r, '
    577                 'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
    578                 'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
    579                 'wmm_enabled=%r, security_config=%r, '
    580                 'spectrum_mgmt_required=%r)' % (
    581                         self.__class__.__name__,
    582                         self._mode,
    583                         self.channel,
    584                         self.frequency,
    585                         self._n_capabilities,
    586                         self._hide_ssid,
    587                         self._beacon_interval,
    588                         self._dtim_period,
    589                         self._frag_threshold,
    590                         self._ssid,
    591                         self._bssid,
    592                         self._wmm_enabled,
    593                         self._security_config,
    594                         self._spectrum_mgmt_required))
    595 
    596 
    597     def supports_channel(self, value):
    598         """Check whether channel is supported by the current hardware mode.
    599 
    600         @param value: int channel to check.
    601         @return True iff the current mode supports the band of the channel.
    602 
    603         """
    604         for freq, channel in self.CHANNEL_MAP.iteritems():
    605             if channel == value:
    606                 return self.supports_frequency(freq)
    607 
    608         return False
    609 
    610 
    611     def supports_frequency(self, frequency):
    612         """Check whether frequency is supported by the current hardware mode.
    613 
    614         @param frequency: int frequency to check.
    615         @return True iff the current mode supports the band of the frequency.
    616 
    617         """
    618         if self._mode == self.MODE_11A and frequency < 5000:
    619             return False
    620 
    621         if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000:
    622             return False
    623 
    624         if frequency not in self.CHANNEL_MAP:
    625             return False
    626 
    627         channel = self.CHANNEL_MAP[frequency]
    628         supports_plus = (channel in
    629                          self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
    630         supports_minus = (channel in
    631                           self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
    632         if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and
    633                 not supports_plus):
    634             return False
    635 
    636         if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and
    637                 not supports_minus):
    638             return False
    639 
    640         if (self.N_CAPABILITY_HT40 in self._n_capabilities and
    641                 not supports_plus and not supports_minus):
    642             return False
    643 
    644         return True
    645 
    646 
    647     def generate_dict(self, interface, control_interface, ssid):
    648         """Generate config dictionary.
    649 
    650         Generate config dictionary for the given |interface|.
    651 
    652         @param interface: string interface to generate config dict for.
    653         @param control_interface: string control interface
    654         @param ssid: string SSID of the AP.
    655         @return dict of hostap configurations.
    656 
    657         """
    658         # Start with the default config parameters.
    659         conf = self._get_default_config
    660         conf['ssid'] = (self._ssid or ssid)
    661         if self._bssid:
    662             conf['bssid'] = self._bssid
    663         conf['channel'] = self.channel
    664         conf['hw_mode'] = self._hw_mode
    665         if self._hide_ssid:
    666             conf['ignore_broadcast_ssid'] = 1
    667         if self._is_11n or self.is_11ac:
    668             conf['ieee80211n'] = 1
    669             conf['ht_capab'] = self._hostapd_ht_capabilities
    670         if self.is_11ac:
    671             conf['ieee80211ac'] = 1
    672             conf['vht_oper_chwidth'] = self._vht_oper_chwidth
    673             conf['vht_oper_centr_freq_seg0_idx'] = \
    674                     self._vht_oper_centr_freq_seg0_idx
    675             conf['vht_capab'] = self._hostapd_vht_capabilities
    676         if self._wmm_enabled:
    677             conf['wmm_enabled'] = 1
    678         if self._require_ht:
    679             conf['require_ht'] = 1
    680         if self._require_vht:
    681             conf['require_vht'] = 1
    682         if self._beacon_interval:
    683             conf['beacon_int'] = self._beacon_interval
    684         if self._dtim_period:
    685             conf['dtim_period'] = self._dtim_period
    686         if self._frag_threshold:
    687             conf['fragm_threshold'] = self._frag_threshold
    688         if self._pmf_support:
    689             conf['ieee80211w'] = self._pmf_support
    690         if self._obss_interval:
    691             conf['obss_interval'] = self._obss_interval
    692         conf['interface'] = interface
    693         conf['ctrl_interface'] = control_interface
    694         if self._spectrum_mgmt_required:
    695             # To set spectrum_mgmt_required, we must first set
    696             # local_pwr_constraint. And to set local_pwr_constraint,
    697             # we must first set ieee80211d. And to set ieee80211d, ...
    698             # Point being: order matters here.
    699             conf['country_code'] = 'US'  # Required for local_pwr_constraint
    700             conf['ieee80211d'] = 1  # Required for local_pwr_constraint
    701             conf['local_pwr_constraint'] = 0  # No local constraint
    702             conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
    703         conf.update(self._security_config.get_hostapd_config())
    704         return conf
    705