blob: e2ea7a5214e744f79e12c1b3341fad012421f493 [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 logging
from autotest_lib.client.common_lib import error
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,
# 34 valid in Japan.
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,
5745: 149,
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'
N_CAPABILITY_WMM = object()
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()
@property
def ht_packet_capture_mode(self):
"""Get an appropriate packet capture HT 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 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 string HT parameter for frequency configuration.
"""
if not self.n_capabilities:
return None
is_plus = '[HT40+]' in self.n_capabilities
is_minus = '[HT40-]' in self.n_capabilities
if is_plus and is_minus:
# TODO(wiley) Apparently, for some channels, there are regulatory
# rules for which side of the channel you may use with
# HT40 mode. For some channels, HT40 is disabled
# altogether.
logging.warning('Packet capture may fail because both HT40+ and '
'HT40- enabled. hostap will choose one or the '
'other, but we do not know that decision.')
return 'HT40-'
if is_plus:
return 'HT40+'
if is_minus:
return 'HT40-'
return 'HT20'
def __init__(self, mode=None, 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, wep_keys=None, wep_default_key=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 wep_keys list of string wep keys
@param wep_default_key int index into wep_keys to use as default key.
"""
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 channel is None and frequency is None:
raise error.TestError('Specify either frequency or channel.')
for real_frequency, real_channel in self.CHANNEL_MAP.iteritems():
if frequency == real_frequency or channel == real_channel:
self.frequency = real_frequency
self.channel = real_channel
break
else:
raise error.TestError('Invalid channel %r or frequency %r '
'specified.' % (channel, frequency))
self.is_11n = False
self.require_ht = False
if mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE) or n_capabilities:
if mode == self.MODE_11N_PURE:
# TODO(wiley) Why? (crbug.com/237370)
logging.warning('Not enforcing pure N mode because Snow does '
'not seem to support it...')
#self.require_ht = True
# For their own historical reasons, hostapd wants it this way.
if self.frequency > 5000:
mode = self.MODE_11A
else:
mode = self.MODE_11G
self.is_11n = True
if self.frequency > 5000 and mode != self.MODE_11A:
raise error.TestError('Must use 11a or 11n mode for '
'frequency > 5Ghz')
if self.frequency < 5000 and mode == self.MODE_11A:
raise error.TestError('Cannot use 11a with frequency %d.' %
self.frequency)
if not mode in (self.MODE_11A, self.MODE_11B, self.MODE_11G, None):
raise error.TestError('Invalid router mode %r' % mode)
self.wmm_enabled = False
self.hw_mode = mode or self.MODE_11B
self.ssid_suffix = '_ch%d' % self.channel
if n_capabilities is None:
n_capabilities = []
self.n_capabilities = set()
for cap in n_capabilities:
self.wmm_enabled = True
if cap == self.N_CAPABILITY_HT40:
self.n_capabilities.add('[HT40-]')
self.n_capabilities.add('[HT40+]')
elif cap == self.N_CAPABILITY_HT40_PLUS:
self.n_capabilities.add('[HT40+]')
elif cap == self.N_CAPABILITY_HT40_MINUS:
self.n_capabilities.add('[HT40-]')
elif cap == self.N_CAPABILITY_GREENFIELD:
logging.warning('Greenfield flag is ignored for hostap...')
#TODO(wiley) Why does this not work?
#self.n_capabilities.add('[GF]')
elif cap == self.N_CAPABILITY_SGI20:
self.n_capabilities.add('[SHORT-GI-20]')
elif cap == self.N_CAPABILITY_SGI40:
self.n_capabilities.add('[SHORT-GI-40]')
elif cap == self.N_CAPABILITY_HT20:
# This isn't a real thing. HT mode implies 20 supported.
pass
elif cap == self.N_CAPABILITY_WMM:
pass
else:
raise error.TestError('Unknown capability: %r' % cap)
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 wep_keys and len(wep_keys) > 4:
raise error.TestFail('More than 4 WEP keys specified (%d).' %
len(self.wep_keys))
self.wep_keys = wep_keys
self.wep_default_key = wep_default_key
if self.wep_default_key is None and self.wep_keys:
self.wep_default_key = 0
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, wep_keys=%r, wep_default_key=%r)' % (
self.__class__.__name__,
self.hw_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.wep_keys,
self.wep_default_key))
def get_ssid(self, default_ssid):
"""Build up the SSID for this network given a router's default SSID.
@param default_ssid string default ssid for a router.
@return string configured ssid or a slight mutation of the default ssid.
"""
return self.ssid or (default_ssid + self.ssid_suffix)[-32:]
def get_shill_compatible_psk(self):
"""@return string shill psk for the encryption in this configuration."""
if self.wep_keys:
return '%d:%s' % (self.wep_default_key,
self.wep_keys[self.wep_default_key])
raise error.TestFail('Failed to build shill compatible psk.')