1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import collections
6import copy
7import logging
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
11from autotest_lib.server.cros.network import packet_capturer
12
13
14class HostapConfig(object):
15    """Parameters for router configuration."""
16
17    # A mapping of frequency to channel number.  This includes some
18    # frequencies used outside the US.
19    CHANNEL_MAP = {2412: 1,
20                   2417: 2,
21                   2422: 3,
22                   2427: 4,
23                   2432: 5,
24                   2437: 6,
25                   2442: 7,
26                   2447: 8,
27                   2452: 9,
28                   2457: 10,
29                   2462: 11,
30                   # 12, 13 are only legitimate outside the US.
31                   2467: 12,
32                   2472: 13,
33                   # 14 is for Japan, DSSS and CCK only.
34                   2484: 14,
35                   # 32 valid in Europe.
36                   5160: 32,
37                   # 34 valid in Europe.
38                   5170: 34,
39                   # 36-116 valid in the US, except 38, 42, and 46, which have
40                   # mixed international support.
41                   5180: 36,
42                   5190: 38,
43                   5200: 40,
44                   5210: 42,
45                   5220: 44,
46                   5230: 46,
47                   5240: 48,
48                   5260: 52,
49                   5280: 56,
50                   5300: 60,
51                   5320: 64,
52                   5500: 100,
53                   5520: 104,
54                   5540: 108,
55                   5560: 112,
56                   5580: 116,
57                   # 120, 124, 128 valid in Europe/Japan.
58                   5600: 120,
59                   5620: 124,
60                   5640: 128,
61                   # 132+ valid in US.
62                   5660: 132,
63                   5680: 136,
64                   5700: 140,
65                   5710: 142,
66                   # 144 is supported by a subset of WiFi chips
67                   # (e.g. bcm4354, but not ath9k).
68                   5720: 144,
69                   5745: 149,
70                   5755: 151,
71                   5765: 153,
72                   5785: 157,
73                   5805: 161,
74                   5825: 165}
75
76    MODE_11A = 'a'
77    MODE_11B = 'b'
78    MODE_11G = 'g'
79    MODE_11N_MIXED = 'n-mixed'
80    MODE_11N_PURE = 'n-only'
81    MODE_11AC_MIXED = 'ac-mixed'
82    MODE_11AC_PURE = 'ac-only'
83
84    N_CAPABILITY_HT20 = object()
85    N_CAPABILITY_HT40 = object()
86    N_CAPABILITY_HT40_PLUS = object()
87    N_CAPABILITY_HT40_MINUS = object()
88    N_CAPABILITY_GREENFIELD = object()
89    N_CAPABILITY_SGI20 = object()
90    N_CAPABILITY_SGI40 = object()
91    ALL_N_CAPABILITIES = [N_CAPABILITY_HT20,
92                          N_CAPABILITY_HT40,
93                          N_CAPABILITY_HT40_PLUS,
94                          N_CAPABILITY_HT40_MINUS,
95                          N_CAPABILITY_GREENFIELD,
96                          N_CAPABILITY_SGI20,
97                          N_CAPABILITY_SGI40]
98
99    AC_CAPABILITY_VHT160 = object()
100    AC_CAPABILITY_VHT160_80PLUS80 = object()
101    AC_CAPABILITY_RXLDPC = object()
102    AC_CAPABILITY_SHORT_GI_80 = object()
103    AC_CAPABILITY_SHORT_GI_160 = object()
104    AC_CAPABILITY_TX_STBC_2BY1 = object()
105    AC_CAPABILITY_RX_STBC_1 = object()
106    AC_CAPABILITY_RX_STBC_12 = object()
107    AC_CAPABILITY_RX_STBC_123 = object()
108    AC_CAPABILITY_RX_STBC_1234 = object()
109    AC_CAPABILITY_SU_BEAMFORMER = object()
110    AC_CAPABILITY_SU_BEAMFORMEE = object()
111    AC_CAPABILITY_BF_ANTENNA_2 = object()
112    AC_CAPABILITY_SOUNDING_DIMENSION_2 = object()
113    AC_CAPABILITY_MU_BEAMFORMER = object()
114    AC_CAPABILITY_MU_BEAMFORMEE = object()
115    AC_CAPABILITY_VHT_TXOP_PS = object()
116    AC_CAPABILITY_HTC_VHT = object()
117    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object()
118    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object()
119    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object()
120    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object()
121    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object()
122    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object()
123    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object()
124    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object()
125    AC_CAPABILITY_VHT_LINK_ADAPT2 = object()
126    AC_CAPABILITY_VHT_LINK_ADAPT3 = object()
127    AC_CAPABILITY_RX_ANTENNA_PATTERN = object()
128    AC_CAPABILITY_TX_ANTENNA_PATTERN = object()
129    AC_CAPABILITIES_MAPPING = {
130            AC_CAPABILITY_VHT160: '[VHT160]',
131            AC_CAPABILITY_VHT160_80PLUS80: '[VHT160-80PLUS80]',
132            AC_CAPABILITY_RXLDPC: '[RXLDPC]',
133            AC_CAPABILITY_SHORT_GI_80: '[SHORT-GI-80]',
134            AC_CAPABILITY_SHORT_GI_160: '[SHORT-GI-160]',
135            AC_CAPABILITY_TX_STBC_2BY1: '[TX-STBC-2BY1]',
136            AC_CAPABILITY_RX_STBC_1: '[RX-STBC-1]',
137            AC_CAPABILITY_RX_STBC_12: '[RX-STBC-12]',
138            AC_CAPABILITY_RX_STBC_123: '[RX-STBC-123]',
139            AC_CAPABILITY_RX_STBC_1234: '[RX-STBC-1234]',
140            AC_CAPABILITY_SU_BEAMFORMER: '[SU-BEAMFORMER]',
141            AC_CAPABILITY_SU_BEAMFORMEE: '[SU-BEAMFORMEE]',
142            AC_CAPABILITY_BF_ANTENNA_2: '[BF-ANTENNA-2]',
143            AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING-DIMENSION-2]',
144            AC_CAPABILITY_MU_BEAMFORMER: '[MU-BEAMFORMER]',
145            AC_CAPABILITY_MU_BEAMFORMEE: '[MU-BEAMFORMEE]',
146            AC_CAPABILITY_VHT_TXOP_PS: '[VHT-TXOP-PS]',
147            AC_CAPABILITY_HTC_VHT: '[HTC-VHT]',
148            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX-A-MPDU-LEN-EXP0]',
149            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX-A-MPDU-LEN-EXP1]',
150            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX-A-MPDU-LEN-EXP2]',
151            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX-A-MPDU-LEN-EXP3]',
152            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX-A-MPDU-LEN-EXP4]',
153            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX-A-MPDU-LEN-EXP5]',
154            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX-A-MPDU-LEN-EXP6]',
155            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX-A-MPDU-LEN-EXP7]',
156            AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT-LINK-ADAPT2]',
157            AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT-LINK-ADAPT3]',
158            AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX-ANTENNA-PATTERN]',
159            AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX-ANTENNA-PATTERN]'}
160
161    HT_CHANNEL_WIDTH_20 = object()
162    HT_CHANNEL_WIDTH_40_PLUS = object()
163    HT_CHANNEL_WIDTH_40_MINUS = object()
164
165    HT_NAMES = {
166        HT_CHANNEL_WIDTH_20: 'HT20',
167        HT_CHANNEL_WIDTH_40_PLUS: 'HT40+',
168        HT_CHANNEL_WIDTH_40_MINUS: 'HT40-',
169    }
170
171    VHT_CHANNEL_WIDTH_40 = object()
172    VHT_CHANNEL_WIDTH_80 = object()
173    VHT_CHANNEL_WIDTH_160 = object()
174    VHT_CHANNEL_WIDTH_80_80 = object()
175
176    # Human readable names for these channel widths.
177    VHT_NAMES = {
178        VHT_CHANNEL_WIDTH_40: 'VHT40',
179        VHT_CHANNEL_WIDTH_80: 'VHT80',
180        VHT_CHANNEL_WIDTH_160: 'VHT160',
181        VHT_CHANNEL_WIDTH_80_80: 'VHT80+80',
182    }
183
184    # This is a loose merging of the rules for US and EU regulatory
185    # domains as taken from IEEE Std 802.11-2016 Appendix E.  For instance,
186    # we tolerate HT40 in channels 149-161 (not allowed in EU), but also
187    # tolerate HT40+ on channel 7 (not allowed in the US).  We take the loose
188    # definition so that we don't prohibit testing in either domain.
189    HT40_ALLOW_MAP = {N_CAPABILITY_HT40_MINUS: range(5, 14) +
190                                           range(40, 65, 8) +
191                                           range(104, 145, 8) +
192                                           [153, 161],
193                  N_CAPABILITY_HT40_PLUS: range(1, 10) +
194                                           range(36, 61, 8) +
195                                           range(100, 141, 8) +
196                                           [149, 157]}
197
198    PMF_SUPPORT_DISABLED = 0
199    PMF_SUPPORT_ENABLED = 1
200    PMF_SUPPORT_REQUIRED = 2
201    PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED,
202                          PMF_SUPPORT_ENABLED,
203                          PMF_SUPPORT_REQUIRED)
204
205    DRIVER_NAME = 'nl80211'
206
207
208    @staticmethod
209    def get_channel_for_frequency(frequency):
210        """Returns the channel number associated with a given frequency.
211
212        @param value: int frequency in MHz.
213
214        @return int frequency associated with the channel.
215
216        """
217        return HostapConfig.CHANNEL_MAP[frequency]
218
219
220    @staticmethod
221    def get_frequency_for_channel(channel):
222        """Returns the frequency associated with a given channel number.
223
224        @param value: int channel number.
225
226        @return int frequency in MHz associated with the channel.
227
228        """
229        for frequency, channel_iter in HostapConfig.CHANNEL_MAP.iteritems():
230            if channel == channel_iter:
231                return frequency
232        else:
233            raise error.TestFail('Unknown channel value: %r.' % channel)
234
235
236    @property
237    def _get_default_config(self):
238        """@return dict of default options for hostapd."""
239        return collections.OrderedDict([
240                ('hw_mode', 'g'),
241                ('logger_syslog', '-1'),
242                ('logger_syslog_level', '0'),
243                # default RTS and frag threshold to ``off''
244                ('rts_threshold', '-1'),
245                ('fragm_threshold', '2346'),
246                ('driver', self.DRIVER_NAME)])
247
248
249    @property
250    def _ht40_plus_allowed(self):
251        """@return True iff HT40+ is enabled for this configuration."""
252        channel_supported = (self.channel in
253                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
254        return ((self.N_CAPABILITY_HT40_PLUS in self._n_capabilities or
255                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
256                channel_supported)
257
258
259    @property
260    def _ht40_minus_allowed(self):
261        """@return True iff HT40- is enabled for this configuration."""
262        channel_supported = (self.channel in
263                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
264        return ((self.N_CAPABILITY_HT40_MINUS in self._n_capabilities or
265                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
266                channel_supported)
267
268
269    @property
270    def _hostapd_ht_capabilities(self):
271        """@return string suitable for the ht_capab= line in a hostapd config"""
272        ret = []
273        if self._ht40_plus_allowed:
274            ret.append('[HT40+]')
275        elif self._ht40_minus_allowed:
276            ret.append('[HT40-]')
277        if self.N_CAPABILITY_GREENFIELD in self._n_capabilities:
278            logging.warning('Greenfield flag is ignored for hostap...')
279        if self.N_CAPABILITY_SGI20 in self._n_capabilities:
280            ret.append('[SHORT-GI-20]')
281        if self.N_CAPABILITY_SGI40 in self._n_capabilities:
282            ret.append('[SHORT-GI-40]')
283        return ''.join(ret)
284
285
286    @property
287    def _hostapd_vht_capabilities(self):
288        """@return string suitable for the vht_capab= line in a hostapd config.
289        """
290        ret = []
291        for cap in self.AC_CAPABILITIES_MAPPING.keys():
292            if cap in self._ac_capabilities:
293                ret.append(self.AC_CAPABILITIES_MAPPING[cap])
294        return ''.join(ret)
295
296
297    @property
298    def _require_ht(self):
299        """@return True iff clients should be required to support HT."""
300        return self._mode == self.MODE_11N_PURE
301
302
303    @property
304    def require_vht(self):
305        """@return True iff clients should be required to support VHT."""
306        return self._mode == self.MODE_11AC_PURE
307
308
309    @property
310    def _hw_mode(self):
311        """@return string hardware mode understood by hostapd."""
312        if self._mode == self.MODE_11A:
313            return self.MODE_11A
314        if self._mode == self.MODE_11B:
315            return self.MODE_11B
316        if self._mode == self.MODE_11G:
317            return self.MODE_11G
318        if self._is_11n or self.is_11ac:
319            # For their own historical reasons, hostapd wants it this way.
320            if self._frequency > 5000:
321                return self.MODE_11A
322
323            return self.MODE_11G
324
325        raise error.TestFail('Invalid mode.')
326
327
328    @property
329    def _is_11n(self):
330        """@return True iff we're trying to host an 802.11n network."""
331        return self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE)
332
333
334    @property
335    def is_11ac(self):
336        """@return True iff we're trying to host an 802.11ac network."""
337        return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE)
338
339
340    @property
341    def channel(self):
342        """@return int channel number for self.frequency."""
343        return self.get_channel_for_frequency(self.frequency)
344
345
346    @channel.setter
347    def channel(self, value):
348        """Sets the channel number to configure hostapd to listen on.
349
350        @param value: int channel number.
351
352        """
353        self.frequency = self.get_frequency_for_channel(value)
354
355
356    @property
357    def frequency(self):
358        """@return int frequency for hostapd to listen on."""
359        return self._frequency
360
361
362    @frequency.setter
363    def frequency(self, value):
364        """Sets the frequency for hostapd to listen on.
365
366        @param value: int frequency in MHz.
367
368        """
369        if value not in self.CHANNEL_MAP or not self.supports_frequency(value):
370            raise error.TestFail('Tried to set an invalid frequency: %r.' %
371                                 value)
372
373        self._frequency = value
374
375
376    @property
377    def ssid(self):
378        """@return string SSID."""
379        return self._ssid
380
381
382    @ssid.setter
383    def ssid(self, value):
384        """Sets the ssid for the hostapd.
385
386        @param value: string ssid name.
387
388        """
389        self._ssid = value
390
391
392    @property
393    def _ht_mode(self):
394        """
395        @return object one of ( None,
396                                HT_CHANNEL_WIDTH_40_PLUS,
397                                HT_CHANNEL_WIDTH_40_MINUS,
398                                HT_CHANNEL_WIDTH_20)
399        """
400        if self._is_11n or self.is_11ac:
401            if self._ht40_plus_allowed:
402                return self.HT_CHANNEL_WIDTH_40_PLUS
403            if self._ht40_minus_allowed:
404                return self.HT_CHANNEL_WIDTH_40_MINUS
405            return self.HT_CHANNEL_WIDTH_20
406        return None
407
408
409    @property
410    def packet_capture_mode(self):
411        """Get an appropriate packet capture HT/VHT parameter.
412
413        When we go to configure a raw monitor we need to configure
414        the phy to listen on the correct channel.  Part of doing
415        so is to specify the channel width for HT/VHT channels.  In the
416        case that the AP is configured to be either HT40+ or HT40-,
417        we could return the wrong parameter because we don't know which
418        configuration will be chosen by hostap.
419
420        @return object width_type parameter from packet_capturer.
421
422        """
423
424        if (not self.vht_channel_width or
425                self.vht_channel_width == self.VHT_CHANNEL_WIDTH_40):
426            # if it is VHT40, capture packets on the correct 40MHz band since
427            # for packet capturing purposes, only the channel width matters
428            ht_mode = self._ht_mode
429            if ht_mode == self.HT_CHANNEL_WIDTH_40_PLUS:
430                return packet_capturer.WIDTH_HT40_PLUS
431            if ht_mode == self.HT_CHANNEL_WIDTH_40_MINUS:
432                return packet_capturer.WIDTH_HT40_MINUS
433            if ht_mode == self.HT_CHANNEL_WIDTH_20:
434                return packet_capturer.WIDTH_HT20
435
436        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
437            return packet_capturer.WIDTH_VHT80
438        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
439            return packet_capturer.WIDTH_VHT160
440        if self.vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
441            return packet_capturer.WIDTH_VHT80_80
442        return None
443
444
445    @property
446    def perf_loggable_description(self):
447        """@return string test description suitable for performance logging."""
448        mode = 'mode%s' % (
449                self.printable_mode.replace('+', 'p').replace('-', 'm'))
450        channel = 'ch%03d' % self.channel
451        return '_'.join([channel, mode, self._security_config.security])
452
453
454    @property
455    def printable_mode(self):
456        """@return human readable mode string."""
457
458        if self.vht_channel_width is not None:
459            return self.VHT_NAMES[self.vht_channel_width]
460
461        ht_mode = self._ht_mode
462        if ht_mode:
463            return self.HT_NAMES[ht_mode]
464
465        return '11' + self._hw_mode.upper()
466
467
468    @property
469    def ssid_suffix(self):
470        """@return meaningful suffix for SSID."""
471        return 'ch%d' % self.channel
472
473
474    @property
475    def security_config(self):
476        """@return SecurityConfig security config object"""
477        return self._security_config
478
479
480    @property
481    def hide_ssid(self):
482        """@return bool _hide_ssid flag."""
483        return self._hide_ssid
484
485
486    @property
487    def scenario_name(self):
488        """@return string _scenario_name value, or None."""
489        return self._scenario_name
490
491
492    @property
493    def min_streams(self):
494        """@return int _min_streams value, or None."""
495        return self._min_streams
496
497
498    @property
499    def frag_threshold(self):
500        """@return int frag threshold value, or None."""
501        return self._frag_threshold
502
503    @property
504    def bridge(self):
505        """@return string _bridge value, or None."""
506        return self._bridge
507
508    @property
509    def max_stas(self):
510        """@return int _max_stas value, or None."""
511        return self._max_stas
512
513    @property
514    def supported_rates(self):
515        """@return list of supported bitrates (in Mbps, or None if not
516           specified.
517        """
518        return self._supported_rates
519
520    def __init__(self, mode=MODE_11B, channel=None, frequency=None,
521                 n_capabilities=None, hide_ssid=None, beacon_interval=None,
522                 dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
523                 force_wmm=None, security_config=None,
524                 pmf_support=PMF_SUPPORT_DISABLED,
525                 obss_interval=None,
526                 vht_channel_width=None,
527                 vht_center_channel=None,
528                 ac_capabilities=None,
529                 spectrum_mgmt_required=None,
530                 scenario_name=None,
531                 supported_rates=None,
532                 basic_rates=None,
533                 min_streams=None,
534                 nas_id=None,
535                 mdid=None,
536                 r1kh_id=None,
537                 r0kh=None,
538                 r1kh=None,
539                 bridge=None,
540                 max_stas=None):
541        """Construct a HostapConfig.
542
543        You may specify channel or frequency, but not both.  Both options
544        are checked for validity (i.e. you can't specify an invalid channel
545        or a frequency that will not be accepted).
546
547        @param mode string MODE_11x defined above.
548        @param channel int channel number.
549        @param frequency int frequency of channel.
550        @param n_capabilities list of N_CAPABILITY_x defined above.
551        @param hide_ssid True if we should set up a hidden SSID.
552        @param beacon_interval int beacon interval of AP.
553        @param dtim_period int include a DTIM every |dtim_period| beacons.
554        @param frag_threshold int maximum outgoing data frame size.
555        @param ssid string up to 32 byte SSID overriding the router default.
556        @param bssid string like 00:11:22:33:44:55.
557        @param force_wmm True if we should force WMM on, False if we should
558            force it off, None if we shouldn't force anything.
559        @param security_config SecurityConfig object.
560        @param pmf_support one of PMF_SUPPORT_* above.  Controls whether the
561            client supports/must support 802.11w.
562        @param obss_interval int interval in seconds that client should be
563            required to do background scans for overlapping BSSes.
564        @param vht_channel_width object channel width
565        @param vht_center_channel int center channel of segment 0.
566        @param ac_capabilities list of AC_CAPABILITY_x defined above.
567        @param spectrum_mgmt_required True if we require the DUT to support
568            spectrum management.
569        @param scenario_name string to be included in file names, instead
570            of the interface name.
571        @param supported_rates list of rates (in Mbps) that the AP should
572             advertise.
573        @param basic_rates list of basic rates (in Mbps) that the AP should
574            advertise.
575        @param min_streams int number of spatial streams required.
576        @param nas_id string for RADIUS messages (needed for 802.11r)
577        @param mdid string used to indicate a group of APs for FT
578        @param r1kh_id string PMK-R1 key holder id for FT
579        @param r0kh string R0KHs in the same mobility domain
580        @param r1kh string R1KHs in the same mobility domain
581        @param bridge string bridge interface to use, if any
582        @param max_stas int maximum number of STAs allowed to connect to AP.
583
584        """
585        super(HostapConfig, self).__init__()
586        if channel is not None and frequency is not None:
587            raise error.TestError('Specify either frequency or channel '
588                                  'but not both.')
589
590        if n_capabilities is None:
591            n_capabilities = []
592        if ac_capabilities is None:
593            ac_capabilities = []
594        self._wmm_enabled = False
595        unknown_caps = [cap for cap in n_capabilities
596                        if cap not in self.ALL_N_CAPABILITIES]
597        if unknown_caps:
598            raise error.TestError('Unknown capabilities: %r' % unknown_caps)
599
600        self._n_capabilities = set(n_capabilities)
601        if self._n_capabilities:
602            self._wmm_enabled = True
603        if self._n_capabilities and mode is None:
604            mode = self.MODE_11N_PURE
605        self._mode = mode
606
607        self._frequency = None
608        if channel:
609            self.channel = channel
610        elif frequency:
611            self.frequency = frequency
612        else:
613            raise error.TestError('Specify either frequency or channel.')
614
615        if not self.supports_frequency(self.frequency):
616            raise error.TestFail('Configured a mode %s that does not support '
617                                 'frequency %d' % (self._mode, self.frequency))
618
619        self._hide_ssid = hide_ssid
620        self._beacon_interval = beacon_interval
621        self._dtim_period = dtim_period
622        self._frag_threshold = frag_threshold
623        if ssid and len(ssid) > 32:
624            raise error.TestFail('Tried to specify SSID that was too long.')
625
626        self._ssid = ssid
627        self._bssid = bssid
628        if force_wmm is not None:
629            self._wmm_enabled = force_wmm
630        if pmf_support not in self.PMF_SUPPORT_VALUES:
631            raise error.TestFail('Invalid value for pmf_support: %r' %
632                                 pmf_support)
633
634        self._pmf_support = pmf_support
635        self._security_config = (copy.copy(security_config) or
636                                xmlrpc_security_types.SecurityConfig())
637        self._obss_interval = obss_interval
638        if vht_channel_width == self.VHT_CHANNEL_WIDTH_40:
639            self._vht_oper_chwidth = 0
640        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
641            self._vht_oper_chwidth = 1
642        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
643            self._vht_oper_chwidth = 2
644        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
645            self._vht_oper_chwidth = 3
646        elif vht_channel_width is not None:
647            raise error.TestFail('Invalid channel width')
648        self.vht_channel_width = vht_channel_width
649        # TODO(zqiu) Add checking for center channel based on the channel width
650        # and operating channel.
651        self._vht_oper_centr_freq_seg0_idx = vht_center_channel
652        self._ac_capabilities = set(ac_capabilities)
653        self._spectrum_mgmt_required = spectrum_mgmt_required
654        self._scenario_name = scenario_name
655        self._supported_rates = supported_rates
656        self._basic_rates = basic_rates
657        self._min_streams = min_streams
658        self._nas_id = nas_id
659        self._mdid = mdid
660        self._r1kh_id = r1kh_id
661        self._r0kh = r0kh
662        self._r1kh = r1kh
663        self._bridge = bridge
664        # keep _max_stas in [0, 2007], as valid AIDs must be in [1, 2007]
665        if max_stas is None:
666            self._max_stas = None
667        else:
668            self._max_stas = max(0, min(max_stas, 2007))
669
670
671    def __repr__(self):
672        return ('%s(mode=%r, channel=%r, frequency=%r, '
673                'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
674                'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
675                'wmm_enabled=%r, security_config=%r, '
676                'spectrum_mgmt_required=%r)' % (
677                        self.__class__.__name__,
678                        self._mode,
679                        self.channel,
680                        self.frequency,
681                        self._n_capabilities,
682                        self._hide_ssid,
683                        self._beacon_interval,
684                        self._dtim_period,
685                        self._frag_threshold,
686                        self._ssid,
687                        self._bssid,
688                        self._wmm_enabled,
689                        self._security_config,
690                        self._spectrum_mgmt_required))
691
692
693    def supports_channel(self, value):
694        """Check whether channel is supported by the current hardware mode.
695
696        @param value: int channel to check.
697        @return True iff the current mode supports the band of the channel.
698
699        """
700        for freq, channel in self.CHANNEL_MAP.iteritems():
701            if channel == value:
702                return self.supports_frequency(freq)
703
704        return False
705
706
707    def supports_frequency(self, frequency):
708        """Check whether frequency is supported by the current hardware mode.
709
710        @param frequency: int frequency to check.
711        @return True iff the current mode supports the band of the frequency.
712
713        """
714        if self._mode == self.MODE_11A and frequency < 5000:
715            return False
716
717        if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000:
718            return False
719
720        if frequency not in self.CHANNEL_MAP:
721            return False
722
723        channel = self.CHANNEL_MAP[frequency]
724        supports_plus = (channel in
725                         self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
726        supports_minus = (channel in
727                          self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
728        if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and
729                not supports_plus):
730            return False
731
732        if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and
733                not supports_minus):
734            return False
735
736        if (self.N_CAPABILITY_HT40 in self._n_capabilities and
737                not supports_plus and not supports_minus):
738            return False
739
740        return True
741
742
743    def generate_dict(self, interface, control_interface, ssid):
744        """Generate config dictionary.
745
746        Generate config dictionary for the given |interface|.
747
748        @param interface: string interface to generate config dict for.
749        @param control_interface: string control interface
750        @param ssid: string SSID of the AP.
751        @return dict of hostap configurations.
752
753        """
754        # Start with the default config parameters.
755        conf = self._get_default_config
756        conf['ssid'] = (self._ssid or ssid)
757        if self._bssid:
758            conf['bssid'] = self._bssid
759        conf['channel'] = self.channel
760        conf['hw_mode'] = self._hw_mode
761
762        # hostapd specifies rates in units of 100Kbps.
763        rate_to_100kbps = lambda rate: str(int(rate * 10))
764        if self._supported_rates:
765            conf['supported_rates'] = ' '.join(map(rate_to_100kbps,
766                                                   self._supported_rates))
767        if self._basic_rates:
768            conf['basic_rates'] = ' '.join(map(rate_to_100kbps,
769                                               self._basic_rates))
770
771        if self._hide_ssid:
772            conf['ignore_broadcast_ssid'] = 1
773        if self._is_11n or self.is_11ac:
774            conf['ieee80211n'] = 1
775            conf['ht_capab'] = self._hostapd_ht_capabilities
776        if self.is_11ac:
777            conf['ieee80211ac'] = 1
778            conf['vht_oper_chwidth'] = self._vht_oper_chwidth
779            if self._vht_oper_centr_freq_seg0_idx is not None:
780                conf['vht_oper_centr_freq_seg0_idx'] = \
781                        self._vht_oper_centr_freq_seg0_idx
782            conf['vht_capab'] = self._hostapd_vht_capabilities
783        if self._wmm_enabled:
784            conf['wmm_enabled'] = 1
785        if self._require_ht:
786            conf['require_ht'] = 1
787        if self.require_vht:
788            conf['require_vht'] = 1
789        if self._beacon_interval:
790            conf['beacon_int'] = self._beacon_interval
791        if self._dtim_period:
792            conf['dtim_period'] = self._dtim_period
793        if self._frag_threshold:
794            conf['fragm_threshold'] = self._frag_threshold
795        if self._pmf_support:
796            conf['ieee80211w'] = self._pmf_support
797        if self._obss_interval:
798            conf['obss_interval'] = self._obss_interval
799        if self._nas_id:
800            conf['nas_identifier'] = self._nas_id
801        if self._mdid:
802            conf['mobility_domain'] = self._mdid
803        if self._r1kh_id:
804            conf['r1_key_holder'] = self._r1kh_id
805        if self._r0kh:
806            conf['r0kh'] = self._r0kh
807        if self._r1kh:
808            conf['r1kh'] = self._r1kh
809        if self._bridge:
810            conf['bridge'] = self._bridge
811        if self._max_stas is not None:
812            conf['max_num_sta'] = self._max_stas
813        conf['interface'] = interface
814        conf['ctrl_interface'] = control_interface
815        if self._spectrum_mgmt_required:
816            # To set spectrum_mgmt_required, we must first set
817            # local_pwr_constraint. And to set local_pwr_constraint,
818            # we must first set ieee80211d. And to set ieee80211d, ...
819            # Point being: order matters here.
820            conf['country_code'] = 'US'  # Required for local_pwr_constraint
821            conf['ieee80211d'] = 1  # Required for local_pwr_constraint
822            conf['local_pwr_constraint'] = 0  # No local constraint
823            conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
824        conf.update(self._security_config.get_hostapd_config())
825        return conf
826