Home | History | Annotate | Download | only in network
      1 # Copyright 2017 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 
      5 import logging
      6 import time
      7 
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.client.common_lib.cros.network import tcpdump_analyzer
     10 from autotest_lib.server import site_linux_system
     11 from autotest_lib.server.cros.network import hostap_config
     12 from autotest_lib.server.cros.network import wifi_cell_test_base
     13 
     14 class RandomMACAddressTestBase(wifi_cell_test_base.WiFiCellTestBase):
     15     """An abstract base class for MAC address randomization tests in WiFi cells.
     16 
     17        MAC address randomization tests will test the ability of the DUT to
     18        successfully randomize the MAC address it sends in probe requests when
     19        such a feature is supported and configured, and that it does not do so
     20        otherwise.
     21     """
     22 
     23     # Approximate number of seconds to perform a full scan.
     24     REQUEST_SCAN_DELAY = 5
     25 
     26     # Default number of shill scans to run.
     27     DEFAULT_NUM_SCANS = 5
     28 
     29     def initialize(self, host):
     30         super(RandomMACAddressTestBase, self).initialize(host)
     31         self._ap_config = hostap_config.HostapConfig(channel=1)
     32 
     33 
     34     def warmup(self, host, raw_cmdline_args, additional_params=None):
     35         super(RandomMACAddressTestBase, self).warmup(
     36                 host, raw_cmdline_args, additional_params)
     37 
     38         self.context.router.require_capabilities(
     39                 [site_linux_system.LinuxSystem.CAPABILITY_MULTI_AP_SAME_BAND])
     40 
     41         self.context.configure(self._ap_config)
     42 
     43 
     44     def start_capture(self):
     45         """
     46         Start packet capture.
     47         """
     48         logging.debug('Starting packet capture')
     49         self.context.capture_host.start_capture(
     50                 self._ap_config.frequency,
     51                 ht_type=self._ap_config.ht_packet_capture_mode)
     52 
     53 
     54     def request_scans(self, num_scans=DEFAULT_NUM_SCANS):
     55         """
     56         Helper to request a few scans with the configured randomization.
     57 
     58         @param num_scans: The number of scans to perform.
     59         """
     60         for i in range(num_scans):
     61             # Request scan through shill rather than iw because iw won't
     62             # set the random MAC flag in the scan request netlink packet.
     63             self.context.client.shill.request_scan()
     64             time.sleep(self.REQUEST_SCAN_DELAY)
     65 
     66 
     67     def _frame_matches_ssid(self, frame):
     68         # Empty string is a wildcard SSID.
     69         return frame.ssid == '' or frame.ssid == self.context.router.get_ssid()
     70 
     71 
     72     def stop_capture_and_get_probe_requests(self):
     73         """
     74         Stop packet capture and return probe requests from the DUT.
     75         have the hardware MAC address.
     76         """
     77         logging.debug('Stopping packet capture')
     78         results = self.context.capture_host.stop_capture()
     79         if len(results) != 1:
     80             raise error.TestError('Expected to generate one packet '
     81                                   'capture but got %d captures instead.',
     82                                   len(results))
     83 
     84         logging.debug('Analyzing packet capture...')
     85         # Get all the frames in chronological order.
     86         frames = tcpdump_analyzer.get_frames(
     87                 results[0].local_pcap_path,
     88                 tcpdump_analyzer.WLAN_PROBE_REQ_ACCEPTOR,
     89                 bad_fcs='discard')
     90 
     91         return [frame for frame in frames if self._frame_matches_ssid(frame)]
     92