| # 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 |