blob: 561f02efbcb5903be6f76f0707dad1207a4fe8f7 [file] [log] [blame]
# 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