# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import collections import copy import logging from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types from autotest_lib.server.cros.network import packet_capturer class HostapConfig(object): """Parameters for router configuration.""" # A mapping of frequency to channel number. This includes some # frequencies used outside the US. CHANNEL_MAP = {2412: 1, 2417: 2, 2422: 3, 2427: 4, 2432: 5, 2437: 6, 2442: 7, 2447: 8, 2452: 9, 2457: 10, 2462: 11, # 12, 13 are only legitimate outside the US. 2467: 12, 2472: 13, # 14 is for Japan, DSSS and CCK only. 2484: 14, # 32 valid in Europe. 5160: 32, # 34 valid in Europe. 5170: 34, # 36-116 valid in the US, except 38, 42, and 46, which have # mixed international support. 5180: 36, 5190: 38, 5200: 40, 5210: 42, 5220: 44, 5230: 46, 5240: 48, 5260: 52, 5280: 56, 5300: 60, 5320: 64, 5500: 100, 5520: 104, 5540: 108, 5560: 112, 5580: 116, # 120, 124, 128 valid in Europe/Japan. 5600: 120, 5620: 124, 5640: 128, # 132+ valid in US. 5660: 132, 5680: 136, 5700: 140, 5710: 142, # 144 is supported by a subset of WiFi chips # (e.g. bcm4354, but not ath9k). 5720: 144, 5745: 149, 5755: 151, 5765: 153, 5785: 157, 5805: 161, 5825: 165} MODE_11A = 'a' MODE_11B = 'b' MODE_11G = 'g' MODE_11N_MIXED = 'n-mixed' MODE_11N_PURE = 'n-only' MODE_11AC_MIXED = 'ac-mixed' MODE_11AC_PURE = 'ac-only' N_CAPABILITY_HT20 = object() N_CAPABILITY_HT40 = object() N_CAPABILITY_HT40_PLUS = object() N_CAPABILITY_HT40_MINUS = object() N_CAPABILITY_GREENFIELD = object() N_CAPABILITY_SGI20 = object() N_CAPABILITY_SGI40 = object() ALL_N_CAPABILITIES = [N_CAPABILITY_HT20, N_CAPABILITY_HT40, N_CAPABILITY_HT40_PLUS, N_CAPABILITY_HT40_MINUS, N_CAPABILITY_GREENFIELD, N_CAPABILITY_SGI20, N_CAPABILITY_SGI40] AC_CAPABILITY_VHT160 = object() AC_CAPABILITY_VHT160_80PLUS80 = object() AC_CAPABILITY_RXLDPC = object() AC_CAPABILITY_SHORT_GI_80 = object() AC_CAPABILITY_SHORT_GI_160 = object() AC_CAPABILITY_TX_STBC_2BY1 = object() AC_CAPABILITY_RX_STBC_1 = object() AC_CAPABILITY_RX_STBC_12 = object() AC_CAPABILITY_RX_STBC_123 = object() AC_CAPABILITY_RX_STBC_1234 = object() AC_CAPABILITY_SU_BEAMFORMER = object() AC_CAPABILITY_SU_BEAMFORMEE = object() AC_CAPABILITY_BF_ANTENNA_2 = object() AC_CAPABILITY_SOUNDING_DIMENSION_2 = object() AC_CAPABILITY_MU_BEAMFORMER = object() AC_CAPABILITY_MU_BEAMFORMEE = object() AC_CAPABILITY_VHT_TXOP_PS = object() AC_CAPABILITY_HTC_VHT = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object() AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object() AC_CAPABILITY_VHT_LINK_ADAPT2 = object() AC_CAPABILITY_VHT_LINK_ADAPT3 = object() AC_CAPABILITY_RX_ANTENNA_PATTERN = object() AC_CAPABILITY_TX_ANTENNA_PATTERN = object() AC_CAPABILITIES_MAPPING = { AC_CAPABILITY_VHT160: '[VHT160]', AC_CAPABILITY_VHT160_80PLUS80: '[VHT160-80PLUS80]', AC_CAPABILITY_RXLDPC: '[RXLDPC]', AC_CAPABILITY_SHORT_GI_80: '[SHORT-GI-80]', AC_CAPABILITY_SHORT_GI_160: '[SHORT-GI-160]', AC_CAPABILITY_TX_STBC_2BY1: '[TX-STBC-2BY1]', AC_CAPABILITY_RX_STBC_1: '[RX-STBC-1]', AC_CAPABILITY_RX_STBC_12: '[RX-STBC-12]', AC_CAPABILITY_RX_STBC_123: '[RX-STBC-123]', AC_CAPABILITY_RX_STBC_1234: '[RX-STBC-1234]', AC_CAPABILITY_SU_BEAMFORMER: '[SU-BEAMFORMER]', AC_CAPABILITY_SU_BEAMFORMEE: '[SU-BEAMFORMEE]', AC_CAPABILITY_BF_ANTENNA_2: '[BF-ANTENNA-2]', AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING-DIMENSION-2]', AC_CAPABILITY_MU_BEAMFORMER: '[MU-BEAMFORMER]', AC_CAPABILITY_MU_BEAMFORMEE: '[MU-BEAMFORMEE]', AC_CAPABILITY_VHT_TXOP_PS: '[VHT-TXOP-PS]', AC_CAPABILITY_HTC_VHT: '[HTC-VHT]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX-A-MPDU-LEN-EXP0]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX-A-MPDU-LEN-EXP1]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX-A-MPDU-LEN-EXP2]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX-A-MPDU-LEN-EXP3]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX-A-MPDU-LEN-EXP4]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX-A-MPDU-LEN-EXP5]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX-A-MPDU-LEN-EXP6]', AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX-A-MPDU-LEN-EXP7]', AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT-LINK-ADAPT2]', AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT-LINK-ADAPT3]', AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX-ANTENNA-PATTERN]', AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX-ANTENNA-PATTERN]'} HT_CHANNEL_WIDTH_20 = object() HT_CHANNEL_WIDTH_40_PLUS = object() HT_CHANNEL_WIDTH_40_MINUS = object() HT_NAMES = { HT_CHANNEL_WIDTH_20: 'HT20', HT_CHANNEL_WIDTH_40_PLUS: 'HT40+', HT_CHANNEL_WIDTH_40_MINUS: 'HT40-', } VHT_CHANNEL_WIDTH_40 = object() VHT_CHANNEL_WIDTH_80 = object() VHT_CHANNEL_WIDTH_160 = object() VHT_CHANNEL_WIDTH_80_80 = object() # Human readable names for these channel widths. VHT_NAMES = { VHT_CHANNEL_WIDTH_40: 'VHT40', VHT_CHANNEL_WIDTH_80: 'VHT80', VHT_CHANNEL_WIDTH_160: 'VHT160', VHT_CHANNEL_WIDTH_80_80: 'VHT80+80', } # This is a loose merging of the rules for US and EU regulatory # domains as taken from IEEE Std 802.11-2016 Appendix E. For instance, # we tolerate HT40 in channels 149-161 (not allowed in EU), but also # tolerate HT40+ on channel 7 (not allowed in the US). We take the loose # definition so that we don't prohibit testing in either domain. HT40_ALLOW_MAP = {N_CAPABILITY_HT40_MINUS: range(5, 14) + range(40, 65, 8) + range(104, 145, 8) + [153, 161], N_CAPABILITY_HT40_PLUS: range(1, 10) + range(36, 61, 8) + range(100, 141, 8) + [149, 157]} PMF_SUPPORT_DISABLED = 0 PMF_SUPPORT_ENABLED = 1 PMF_SUPPORT_REQUIRED = 2 PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED, PMF_SUPPORT_ENABLED, PMF_SUPPORT_REQUIRED) DRIVER_NAME = 'nl80211' @staticmethod def get_channel_for_frequency(frequency): """Returns the channel number associated with a given frequency. @param value: int frequency in MHz. @return int frequency associated with the channel. """ return HostapConfig.CHANNEL_MAP[frequency] @staticmethod def get_frequency_for_channel(channel): """Returns the frequency associated with a given channel number. @param value: int channel number. @return int frequency in MHz associated with the channel. """ for frequency, channel_iter in HostapConfig.CHANNEL_MAP.iteritems(): if channel == channel_iter: return frequency else: raise error.TestFail('Unknown channel value: %r.' % channel) @property def _get_default_config(self): """@return dict of default options for hostapd.""" return collections.OrderedDict([ ('hw_mode', 'g'), ('logger_syslog', '-1'), ('logger_syslog_level', '0'), # default RTS and frag threshold to ``off'' ('rts_threshold', '-1'), ('fragm_threshold', '2346'), ('driver', self.DRIVER_NAME)]) @property def _ht40_plus_allowed(self): """@return True iff HT40+ is enabled for this configuration.""" channel_supported = (self.channel in self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS]) return ((self.N_CAPABILITY_HT40_PLUS in self._n_capabilities or self.N_CAPABILITY_HT40 in self._n_capabilities) and channel_supported) @property def _ht40_minus_allowed(self): """@return True iff HT40- is enabled for this configuration.""" channel_supported = (self.channel in self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS]) return ((self.N_CAPABILITY_HT40_MINUS in self._n_capabilities or self.N_CAPABILITY_HT40 in self._n_capabilities) and channel_supported) @property def _hostapd_ht_capabilities(self): """@return string suitable for the ht_capab= line in a hostapd config""" ret = [] if self._ht40_plus_allowed: ret.append('[HT40+]') elif self._ht40_minus_allowed: ret.append('[HT40-]') if self.N_CAPABILITY_GREENFIELD in self._n_capabilities: logging.warning('Greenfield flag is ignored for hostap...') if self.N_CAPABILITY_SGI20 in self._n_capabilities: ret.append('[SHORT-GI-20]') if self.N_CAPABILITY_SGI40 in self._n_capabilities: ret.append('[SHORT-GI-40]') return ''.join(ret) @property def _hostapd_vht_capabilities(self): """@return string suitable for the vht_capab= line in a hostapd config. """ ret = [] for cap in self.AC_CAPABILITIES_MAPPING.keys(): if cap in self._ac_capabilities: ret.append(self.AC_CAPABILITIES_MAPPING[cap]) return ''.join(ret) @property def _require_ht(self): """@return True iff clients should be required to support HT.""" return self._mode == self.MODE_11N_PURE @property def require_vht(self): """@return True iff clients should be required to support VHT.""" return self._mode == self.MODE_11AC_PURE @property def _hw_mode(self): """@return string hardware mode understood by hostapd.""" if self._mode == self.MODE_11A: return self.MODE_11A if self._mode == self.MODE_11B: return self.MODE_11B if self._mode == self.MODE_11G: return self.MODE_11G if self._is_11n or self.is_11ac: # For their own historical reasons, hostapd wants it this way. if self._frequency > 5000: return self.MODE_11A return self.MODE_11G raise error.TestFail('Invalid mode.') @property def _is_11n(self): """@return True iff we're trying to host an 802.11n network.""" return self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE) @property def is_11ac(self): """@return True iff we're trying to host an 802.11ac network.""" return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE) @property def channel(self): """@return int channel number for self.frequency.""" return self.get_channel_for_frequency(self.frequency) @channel.setter def channel(self, value): """Sets the channel number to configure hostapd to listen on. @param value: int channel number. """ self.frequency = self.get_frequency_for_channel(value) @property def frequency(self): """@return int frequency for hostapd to listen on.""" return self._frequency @frequency.setter def frequency(self, value): """Sets the frequency for hostapd to listen on. @param value: int frequency in MHz. """ if value not in self.CHANNEL_MAP or not self.supports_frequency(value): raise error.TestFail('Tried to set an invalid frequency: %r.' % value) self._frequency = value @property def ssid(self): """@return string SSID.""" return self._ssid @ssid.setter def ssid(self, value): """Sets the ssid for the hostapd. @param value: string ssid name. """ self._ssid = value @property def _ht_mode(self): """ @return object one of ( None, HT_CHANNEL_WIDTH_40_PLUS, HT_CHANNEL_WIDTH_40_MINUS, HT_CHANNEL_WIDTH_20) """ if self._is_11n or self.is_11ac: if self._ht40_plus_allowed: return self.HT_CHANNEL_WIDTH_40_PLUS if self._ht40_minus_allowed: return self.HT_CHANNEL_WIDTH_40_MINUS return self.HT_CHANNEL_WIDTH_20 return None @property def packet_capture_mode(self): """Get an appropriate packet capture HT/VHT parameter. When we go to configure a raw monitor we need to configure the phy to listen on the correct channel. Part of doing so is to specify the channel width for HT/VHT channels. In the case that the AP is configured to be either HT40+ or HT40-, we could return the wrong parameter because we don't know which configuration will be chosen by hostap. @return object width_type parameter from packet_capturer. """ if (not self.vht_channel_width or self.vht_channel_width == self.VHT_CHANNEL_WIDTH_40): # if it is VHT40, capture packets on the correct 40MHz band since # for packet capturing purposes, only the channel width matters ht_mode = self._ht_mode if ht_mode == self.HT_CHANNEL_WIDTH_40_PLUS: return packet_capturer.WIDTH_HT40_PLUS if ht_mode == self.HT_CHANNEL_WIDTH_40_MINUS: return packet_capturer.WIDTH_HT40_MINUS if ht_mode == self.HT_CHANNEL_WIDTH_20: return packet_capturer.WIDTH_HT20 if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80: return packet_capturer.WIDTH_VHT80 if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_160: return packet_capturer.WIDTH_VHT160 if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80: return packet_capturer.WIDTH_VHT80_80 return None @property def perf_loggable_description(self): """@return string test description suitable for performance logging.""" mode = 'mode%s' % ( self.printable_mode.replace('+', 'p').replace('-', 'm')) channel = 'ch%03d' % self.channel return '_'.join([channel, mode, self._security_config.security]) @property def printable_mode(self): """@return human readable mode string.""" if self.vht_channel_width is not None: return self.VHT_NAMES[self.vht_channel_width] ht_mode = self._ht_mode if ht_mode: return self.HT_NAMES[ht_mode] return '11' + self._hw_mode.upper() @property def ssid_suffix(self): """@return meaningful suffix for SSID.""" return 'ch%d' % self.channel @property def security_config(self): """@return SecurityConfig security config object""" return self._security_config @property def hide_ssid(self): """@return bool _hide_ssid flag.""" return self._hide_ssid @property def scenario_name(self): """@return string _scenario_name value, or None.""" return self._scenario_name @property def min_streams(self): """@return int _min_streams value, or None.""" return self._min_streams @property def frag_threshold(self): """@return int frag threshold value, or None.""" return self._frag_threshold @property def bridge(self): """@return string _bridge value, or None.""" return self._bridge @property def max_stas(self): """@return int _max_stas value, or None.""" return self._max_stas @property def supported_rates(self): """@return list of supported bitrates (in Mbps, or None if not specified. """ return self._supported_rates def __init__(self, mode=MODE_11B, channel=None, frequency=None, n_capabilities=None, hide_ssid=None, beacon_interval=None, dtim_period=None, frag_threshold=None, ssid=None, bssid=None, force_wmm=None, security_config=None, pmf_support=PMF_SUPPORT_DISABLED, obss_interval=None, vht_channel_width=None, vht_center_channel=None, ac_capabilities=None, spectrum_mgmt_required=None, scenario_name=None, supported_rates=None, basic_rates=None, min_streams=None, nas_id=None, mdid=None, r1kh_id=None, r0kh=None, r1kh=None, bridge=None, max_stas=None): """Construct a HostapConfig. You may specify channel or frequency, but not both. Both options are checked for validity (i.e. you can't specify an invalid channel or a frequency that will not be accepted). @param mode string MODE_11x defined above. @param channel int channel number. @param frequency int frequency of channel. @param n_capabilities list of N_CAPABILITY_x defined above. @param hide_ssid True if we should set up a hidden SSID. @param beacon_interval int beacon interval of AP. @param dtim_period int include a DTIM every |dtim_period| beacons. @param frag_threshold int maximum outgoing data frame size. @param ssid string up to 32 byte SSID overriding the router default. @param bssid string like 00:11:22:33:44:55. @param force_wmm True if we should force WMM on, False if we should force it off, None if we shouldn't force anything. @param security_config SecurityConfig object. @param pmf_support one of PMF_SUPPORT_* above. Controls whether the client supports/must support 802.11w. @param obss_interval int interval in seconds that client should be required to do background scans for overlapping BSSes. @param vht_channel_width object channel width @param vht_center_channel int center channel of segment 0. @param ac_capabilities list of AC_CAPABILITY_x defined above. @param spectrum_mgmt_required True if we require the DUT to support spectrum management. @param scenario_name string to be included in file names, instead of the interface name. @param supported_rates list of rates (in Mbps) that the AP should advertise. @param basic_rates list of basic rates (in Mbps) that the AP should advertise. @param min_streams int number of spatial streams required. @param nas_id string for RADIUS messages (needed for 802.11r) @param mdid string used to indicate a group of APs for FT @param r1kh_id string PMK-R1 key holder id for FT @param r0kh string R0KHs in the same mobility domain @param r1kh string R1KHs in the same mobility domain @param bridge string bridge interface to use, if any @param max_stas int maximum number of STAs allowed to connect to AP. """ super(HostapConfig, self).__init__() if channel is not None and frequency is not None: raise error.TestError('Specify either frequency or channel ' 'but not both.') if n_capabilities is None: n_capabilities = [] if ac_capabilities is None: ac_capabilities = [] self._wmm_enabled = False unknown_caps = [cap for cap in n_capabilities if cap not in self.ALL_N_CAPABILITIES] if unknown_caps: raise error.TestError('Unknown capabilities: %r' % unknown_caps) self._n_capabilities = set(n_capabilities) if self._n_capabilities: self._wmm_enabled = True if self._n_capabilities and mode is None: mode = self.MODE_11N_PURE self._mode = mode self._frequency = None if channel: self.channel = channel elif frequency: self.frequency = frequency else: raise error.TestError('Specify either frequency or channel.') if not self.supports_frequency(self.frequency): raise error.TestFail('Configured a mode %s that does not support ' 'frequency %d' % (self._mode, self.frequency)) self._hide_ssid = hide_ssid self._beacon_interval = beacon_interval self._dtim_period = dtim_period self._frag_threshold = frag_threshold if ssid and len(ssid) > 32: raise error.TestFail('Tried to specify SSID that was too long.') self._ssid = ssid self._bssid = bssid if force_wmm is not None: self._wmm_enabled = force_wmm if pmf_support not in self.PMF_SUPPORT_VALUES: raise error.TestFail('Invalid value for pmf_support: %r' % pmf_support) self._pmf_support = pmf_support self._security_config = (copy.copy(security_config) or xmlrpc_security_types.SecurityConfig()) self._obss_interval = obss_interval if vht_channel_width == self.VHT_CHANNEL_WIDTH_40: self._vht_oper_chwidth = 0 elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80: self._vht_oper_chwidth = 1 elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160: self._vht_oper_chwidth = 2 elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80: self._vht_oper_chwidth = 3 elif vht_channel_width is not None: raise error.TestFail('Invalid channel width') self.vht_channel_width = vht_channel_width # TODO(zqiu) Add checking for center channel based on the channel width # and operating channel. self._vht_oper_centr_freq_seg0_idx = vht_center_channel self._ac_capabilities = set(ac_capabilities) self._spectrum_mgmt_required = spectrum_mgmt_required self._scenario_name = scenario_name self._supported_rates = supported_rates self._basic_rates = basic_rates self._min_streams = min_streams self._nas_id = nas_id self._mdid = mdid self._r1kh_id = r1kh_id self._r0kh = r0kh self._r1kh = r1kh self._bridge = bridge # keep _max_stas in [0, 2007], as valid AIDs must be in [1, 2007] if max_stas is None: self._max_stas = None else: self._max_stas = max(0, min(max_stas, 2007)) def __repr__(self): return ('%s(mode=%r, channel=%r, frequency=%r, ' 'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, ' 'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, ' 'wmm_enabled=%r, security_config=%r, ' 'spectrum_mgmt_required=%r)' % ( self.__class__.__name__, self._mode, self.channel, self.frequency, self._n_capabilities, self._hide_ssid, self._beacon_interval, self._dtim_period, self._frag_threshold, self._ssid, self._bssid, self._wmm_enabled, self._security_config, self._spectrum_mgmt_required)) def supports_channel(self, value): """Check whether channel is supported by the current hardware mode. @param value: int channel to check. @return True iff the current mode supports the band of the channel. """ for freq, channel in self.CHANNEL_MAP.iteritems(): if channel == value: return self.supports_frequency(freq) return False def supports_frequency(self, frequency): """Check whether frequency is supported by the current hardware mode. @param frequency: int frequency to check. @return True iff the current mode supports the band of the frequency. """ if self._mode == self.MODE_11A and frequency < 5000: return False if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000: return False if frequency not in self.CHANNEL_MAP: return False channel = self.CHANNEL_MAP[frequency] supports_plus = (channel in self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS]) supports_minus = (channel in self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS]) if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and not supports_plus): return False if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and not supports_minus): return False if (self.N_CAPABILITY_HT40 in self._n_capabilities and not supports_plus and not supports_minus): return False return True def generate_dict(self, interface, control_interface, ssid): """Generate config dictionary. Generate config dictionary for the given |interface|. @param interface: string interface to generate config dict for. @param control_interface: string control interface @param ssid: string SSID of the AP. @return dict of hostap configurations. """ # Start with the default config parameters. conf = self._get_default_config conf['ssid'] = (self._ssid or ssid) if self._bssid: conf['bssid'] = self._bssid conf['channel'] = self.channel conf['hw_mode'] = self._hw_mode # hostapd specifies rates in units of 100Kbps. rate_to_100kbps = lambda rate: str(int(rate * 10)) if self._supported_rates: conf['supported_rates'] = ' '.join(map(rate_to_100kbps, self._supported_rates)) if self._basic_rates: conf['basic_rates'] = ' '.join(map(rate_to_100kbps, self._basic_rates)) if self._hide_ssid: conf['ignore_broadcast_ssid'] = 1 if self._is_11n or self.is_11ac: conf['ieee80211n'] = 1 conf['ht_capab'] = self._hostapd_ht_capabilities if self.is_11ac: conf['ieee80211ac'] = 1 conf['vht_oper_chwidth'] = self._vht_oper_chwidth if self._vht_oper_centr_freq_seg0_idx is not None: conf['vht_oper_centr_freq_seg0_idx'] = \ self._vht_oper_centr_freq_seg0_idx conf['vht_capab'] = self._hostapd_vht_capabilities if self._wmm_enabled: conf['wmm_enabled'] = 1 if self._require_ht: conf['require_ht'] = 1 if self.require_vht: conf['require_vht'] = 1 if self._beacon_interval: conf['beacon_int'] = self._beacon_interval if self._dtim_period: conf['dtim_period'] = self._dtim_period if self._frag_threshold: conf['fragm_threshold'] = self._frag_threshold if self._pmf_support: conf['ieee80211w'] = self._pmf_support if self._obss_interval: conf['obss_interval'] = self._obss_interval if self._nas_id: conf['nas_identifier'] = self._nas_id if self._mdid: conf['mobility_domain'] = self._mdid if self._r1kh_id: conf['r1_key_holder'] = self._r1kh_id if self._r0kh: conf['r0kh'] = self._r0kh if self._r1kh: conf['r1kh'] = self._r1kh if self._bridge: conf['bridge'] = self._bridge if self._max_stas is not None: conf['max_num_sta'] = self._max_stas conf['interface'] = interface conf['ctrl_interface'] = control_interface if self._spectrum_mgmt_required: # To set spectrum_mgmt_required, we must first set # local_pwr_constraint. And to set local_pwr_constraint, # we must first set ieee80211d. And to set ieee80211d, ... # Point being: order matters here. conf['country_code'] = 'US' # Required for local_pwr_constraint conf['ieee80211d'] = 1 # Required for local_pwr_constraint conf['local_pwr_constraint'] = 0 # No local constraint conf['spectrum_mgmt_required'] = 1 # Requires local_pwr_constraint conf.update(self._security_config.get_hostapd_config()) return conf