You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
826 lines
30 KiB
826 lines
30 KiB
# 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
|