Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright 2019 - The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the 'License'); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an 'AS IS' BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 17 | import csv |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 18 | import os |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 19 | import posixpath |
| 20 | import time |
Xianyuan Jia | 63751fb | 2020-11-17 00:07:40 +0000 | [diff] [blame^] | 21 | import acts_contrib.test_utils.wifi.wifi_test_utils as wutils |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 22 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 23 | from acts import context |
| 24 | from acts import logger |
| 25 | from acts import utils |
| 26 | from acts.controllers.utils_lib import ssh |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 27 | |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 28 | WifiEnums = wutils.WifiEnums |
Omar El Ayach | 4ab5c8b | 2020-06-15 17:01:35 -0700 | [diff] [blame] | 29 | SNIFFER_TIMEOUT = 6 |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 30 | |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 31 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 32 | def create(configs): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 33 | """Factory method for sniffer. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 34 | Args: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 35 | configs: list of dicts with sniffer settings. |
| 36 | Settings must contain the following : ssh_settings, type, OS, interface. |
| 37 | |
| 38 | Returns: |
| 39 | objs: list of sniffer class objects. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 40 | """ |
| 41 | objs = [] |
| 42 | for config in configs: |
| 43 | try: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 44 | if config['type'] == 'tshark': |
| 45 | if config['os'] == 'unix': |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 46 | objs.append(TsharkSnifferOnUnix(config)) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 47 | elif config['os'] == 'linux': |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 48 | objs.append(TsharkSnifferOnLinux(config)) |
| 49 | else: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 50 | raise RuntimeError('Wrong sniffer config') |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 51 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 52 | elif config['type'] == 'mock': |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 53 | objs.append(MockSniffer(config)) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 54 | except KeyError: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 55 | raise KeyError('Invalid sniffer configurations') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 56 | return objs |
| 57 | |
| 58 | |
| 59 | def destroy(objs): |
| 60 | return |
| 61 | |
| 62 | |
| 63 | class OtaSnifferBase(object): |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 64 | """Base class defining common sniffers functions.""" |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 65 | |
| 66 | _log_file_counter = 0 |
| 67 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 68 | @property |
| 69 | def started(self): |
| 70 | raise NotImplementedError('started must be specified.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 71 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 72 | def start_capture(self, network, duration=30): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 73 | """Starts the sniffer Capture. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 74 | |
| 75 | Args: |
| 76 | network: dict containing network information such as SSID, etc. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 77 | duration: duration of sniffer capture in seconds. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 78 | """ |
| 79 | raise NotImplementedError('start_capture must be specified.') |
| 80 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 81 | def stop_capture(self, tag=''): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 82 | """Stops the sniffer Capture. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 83 | |
| 84 | Args: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 85 | tag: string to tag sniffer capture file name with. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 86 | """ |
| 87 | raise NotImplementedError('stop_capture must be specified.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 88 | |
| 89 | def _get_remote_dump_path(self): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 90 | """Returns name of the sniffer dump file.""" |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 91 | remote_file_name = 'sniffer_dump.{}'.format( |
| 92 | self.sniffer_output_file_type) |
| 93 | remote_dump_path = posixpath.join(posixpath.sep, 'tmp', remote_file_name) |
| 94 | return remote_dump_path |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 95 | |
| 96 | def _get_full_file_path(self, tag=None): |
| 97 | """Returns the full file path for the sniffer capture dump file. |
| 98 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 99 | Returns the full file path (on test machine) for the sniffer capture |
| 100 | dump file. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 101 | |
| 102 | Args: |
| 103 | tag: The tag appended to the sniffer capture dump file . |
| 104 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 105 | tags = [tag, 'count', OtaSnifferBase._log_file_counter] |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 106 | out_file_name = 'Sniffer_Capture_%s.%s' % ('_'.join([ |
| 107 | str(x) for x in tags if x != '' and x is not None |
| 108 | ]), self.sniffer_output_file_type) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 109 | OtaSnifferBase._log_file_counter += 1 |
| 110 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 111 | file_path = os.path.join(self.log_path, out_file_name) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 112 | return file_path |
| 113 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 114 | @property |
| 115 | def log_path(self): |
| 116 | current_context = context.get_current_context() |
| 117 | full_out_dir = os.path.join(current_context.get_full_output_path(), |
| 118 | 'sniffer_captures') |
| 119 | |
| 120 | # Ensure the directory exists. |
Mark De Ruyter | 72f8df9 | 2020-02-12 13:44:49 -0800 | [diff] [blame] | 121 | os.makedirs(full_out_dir, exist_ok=True) |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 122 | |
| 123 | return full_out_dir |
| 124 | |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 125 | |
| 126 | class MockSniffer(OtaSnifferBase): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 127 | """Class that implements mock sniffer for test development and debug.""" |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 128 | def __init__(self, config): |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 129 | self.log = logger.create_tagged_trace_logger('Mock Sniffer') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 130 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 131 | def start_capture(self, network, duration=30): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 132 | """Starts sniffer capture on the specified machine. |
| 133 | |
| 134 | Args: |
| 135 | network: dict of network credentials. |
| 136 | duration: duration of the sniff. |
| 137 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 138 | self.log.info('Starting sniffer.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 139 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 140 | def stop_capture(self): |
| 141 | """Stops the sniffer. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 142 | |
| 143 | Returns: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 144 | log_file: name of processed sniffer. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 145 | """ |
| 146 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 147 | self.log.info('Stopping sniffer.') |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 148 | log_file = self._get_full_file_path() |
| 149 | with open(log_file, 'w') as file: |
| 150 | file.write('this is a sniffer dump.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 151 | return log_file |
| 152 | |
| 153 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 154 | class TsharkSnifferBase(OtaSnifferBase): |
| 155 | """Class that implements Tshark based sniffer controller. """ |
| 156 | |
| 157 | TYPE_SUBTYPE_DICT = { |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 158 | '0': 'Association Requests', |
| 159 | '1': 'Association Responses', |
| 160 | '2': 'Reassociation Requests', |
| 161 | '3': 'Resssociation Responses', |
| 162 | '4': 'Probe Requests', |
| 163 | '5': 'Probe Responses', |
| 164 | '8': 'Beacon', |
| 165 | '9': 'ATIM', |
| 166 | '10': 'Disassociations', |
| 167 | '11': 'Authentications', |
| 168 | '12': 'Deauthentications', |
| 169 | '13': 'Actions', |
| 170 | '24': 'Block ACK Requests', |
| 171 | '25': 'Block ACKs', |
| 172 | '26': 'PS-Polls', |
| 173 | '27': 'RTS', |
| 174 | '28': 'CTS', |
| 175 | '29': 'ACK', |
| 176 | '30': 'CF-Ends', |
| 177 | '31': 'CF-Ends/CF-Acks', |
| 178 | '32': 'Data', |
| 179 | '33': 'Data+CF-Ack', |
| 180 | '34': 'Data+CF-Poll', |
| 181 | '35': 'Data+CF-Ack+CF-Poll', |
| 182 | '36': 'Null', |
| 183 | '37': 'CF-Ack', |
| 184 | '38': 'CF-Poll', |
| 185 | '39': 'CF-Ack+CF-Poll', |
| 186 | '40': 'QoS Data', |
| 187 | '41': 'QoS Data+CF-Ack', |
| 188 | '42': 'QoS Data+CF-Poll', |
| 189 | '43': 'QoS Data+CF-Ack+CF-Poll', |
| 190 | '44': 'QoS Null', |
| 191 | '46': 'QoS CF-Poll (Null)', |
| 192 | '47': 'QoS CF-Ack+CF-Poll (Null)' |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | TSHARK_COLUMNS = [ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 196 | 'frame_number', 'frame_time_relative', 'mactime', 'frame_len', 'rssi', |
| 197 | 'channel', 'ta', 'ra', 'bssid', 'type', 'subtype', 'duration', 'seq', |
| 198 | 'retry', 'pwrmgmt', 'moredata', 'ds', 'phy', 'radio_datarate', |
| 199 | 'vht_datarate', 'radiotap_mcs_index', 'vht_mcs', 'wlan_data_rate', |
| 200 | '11n_mcs_index', '11ac_mcs', '11n_bw', '11ac_bw', 'vht_nss', 'mcs_gi', |
| 201 | 'vht_gi', 'vht_coding', 'ba_bm', 'fc_status', 'bf_report' |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 202 | ] |
| 203 | |
| 204 | TSHARK_OUTPUT_COLUMNS = [ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 205 | 'frame_number', 'frame_time_relative', 'mactime', 'ta', 'ra', 'bssid', |
| 206 | 'rssi', 'channel', 'frame_len', 'Info', 'radio_datarate', |
| 207 | 'radiotap_mcs_index', 'pwrmgmt', 'phy', 'vht_nss', 'vht_mcs', |
| 208 | 'vht_datarate', '11ac_mcs', '11ac_bw', 'vht_gi', 'vht_coding', |
| 209 | 'wlan_data_rate', '11n_mcs_index', '11n_bw', 'mcs_gi', 'type', |
| 210 | 'subtype', 'duration', 'seq', 'retry', 'moredata', 'ds', 'ba_bm', |
| 211 | 'fc_status', 'bf_report' |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 212 | ] |
| 213 | |
| 214 | TSHARK_FIELDS_LIST = [ |
| 215 | 'frame.number', 'frame.time_relative', 'radiotap.mactime', 'frame.len', |
| 216 | 'radiotap.dbm_antsignal', 'wlan_radio.channel', 'wlan.ta', 'wlan.ra', |
| 217 | 'wlan.bssid', 'wlan.fc.type', 'wlan.fc.type_subtype', 'wlan.duration', |
| 218 | 'wlan.seq', 'wlan.fc.retry', 'wlan.fc.pwrmgt', 'wlan.fc.moredata', |
| 219 | 'wlan.fc.ds', 'wlan_radio.phy', 'radiotap.datarate', |
| 220 | 'radiotap.vht.datarate.0', 'radiotap.mcs.index', 'radiotap.vht.mcs.0', |
| 221 | 'wlan_radio.data_rate', 'wlan_radio.11n.mcs_index', |
| 222 | 'wlan_radio.11ac.mcs', 'wlan_radio.11n.bandwidth', |
| 223 | 'wlan_radio.11ac.bandwidth', 'radiotap.vht.nss.0', 'radiotap.mcs.gi', |
| 224 | 'radiotap.vht.gi', 'radiotap.vht.coding.0', 'wlan.ba.bm', |
| 225 | 'wlan.fcs.status', 'wlan.vht.compressed_beamforming_report.snr' |
| 226 | ] |
| 227 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 228 | def __init__(self, config): |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 229 | self.sniffer_proc_pid = None |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 230 | self.log = logger.create_tagged_trace_logger('Tshark Sniffer') |
| 231 | self.ssh_config = config['ssh_config'] |
| 232 | self.sniffer_os = config['os'] |
Omar El Ayach | 8a60454 | 2020-05-13 16:19:27 -0700 | [diff] [blame] | 233 | self.run_as_sudo = config.get('run_as_sudo', False) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 234 | self.sniffer_output_file_type = config['output_file_type'] |
| 235 | self.sniffer_snap_length = config['snap_length'] |
| 236 | self.sniffer_interface = config['interface'] |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 237 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 238 | #Logging into sniffer |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 239 | self.log.info('Logging into sniffer.') |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 240 | self._sniffer_server = ssh.connection.SshConnection( |
| 241 | ssh.settings.from_config(self.ssh_config)) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 242 | # Get tshark params |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 243 | self.tshark_fields = self._generate_tshark_fields( |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 244 | self.TSHARK_FIELDS_LIST) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 245 | self.tshark_path = self._sniffer_server.run('which tshark').stdout |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 246 | |
| 247 | @property |
| 248 | def _started(self): |
| 249 | return self.sniffer_proc_pid is not None |
| 250 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 251 | def _scan_for_networks(self): |
| 252 | """Scans for wireless networks on the sniffer.""" |
| 253 | raise NotImplementedError |
| 254 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 255 | def _get_tshark_command(self, duration): |
| 256 | """Frames the appropriate tshark command. |
| 257 | |
| 258 | Args: |
| 259 | duration: duration to sniff for. |
| 260 | |
| 261 | Returns: |
| 262 | tshark_command : appropriate tshark command. |
| 263 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 264 | tshark_command = '{} -l -i {} -I -t u -a duration:{}'.format( |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 265 | self.tshark_path, self.sniffer_interface, int(duration)) |
Omar El Ayach | feb40b2 | 2020-05-13 12:18:20 -0700 | [diff] [blame] | 266 | if self.run_as_sudo: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 267 | tshark_command = 'sudo {}'.format(tshark_command) |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 268 | |
| 269 | return tshark_command |
| 270 | |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 271 | def _get_sniffer_command(self, tshark_command): |
| 272 | """ |
| 273 | Frames the appropriate sniffer command. |
| 274 | |
| 275 | Args: |
| 276 | tshark_command: framed tshark command |
| 277 | |
| 278 | Returns: |
| 279 | sniffer_command: appropriate sniffer command |
| 280 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 281 | if self.sniffer_output_file_type in ['pcap', 'pcapng']: |
| 282 | sniffer_command = ' {tshark} -s {snaplength} -w {log_file} '.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 283 | tshark=tshark_command, |
| 284 | snaplength=self.sniffer_snap_length, |
| 285 | log_file=self._get_remote_dump_path()) |
| 286 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 287 | elif self.sniffer_output_file_type == 'csv': |
| 288 | sniffer_command = '{tshark} {fields} > {log_file}'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 289 | tshark=tshark_command, |
| 290 | fields=self.tshark_fields, |
| 291 | log_file=self._get_remote_dump_path()) |
| 292 | |
| 293 | else: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 294 | raise KeyError('Sniffer output file type not configured correctly') |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 295 | |
| 296 | return sniffer_command |
| 297 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 298 | def _generate_tshark_fields(self, fields): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 299 | """Generates tshark fields to be appended to the tshark command. |
| 300 | |
| 301 | Args: |
| 302 | fields: list of tshark fields to be appended to the tshark command. |
| 303 | |
| 304 | Returns: |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 305 | tshark_fields: string of tshark fields to be appended |
| 306 | to the tshark command. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 307 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 308 | tshark_fields = "-T fields -y IEEE802_11_RADIO -E separator='^'" |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 309 | for field in fields: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 310 | tshark_fields = tshark_fields + ' -e {}'.format(field) |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 311 | return tshark_fields |
| 312 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 313 | def _configure_sniffer(self, network, chan, bw): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 314 | """ Connects to a wireless network using networksetup utility. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 315 | |
| 316 | Args: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 317 | network: dictionary of network credentials; SSID and password. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 318 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 319 | raise NotImplementedError |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 320 | |
| 321 | def _run_tshark(self, sniffer_command): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 322 | """Starts the sniffer. |
| 323 | |
| 324 | Args: |
| 325 | sniffer_command: sniffer command to execute. |
| 326 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 327 | self.log.info('Starting sniffer.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 328 | sniffer_job = self._sniffer_server.run_async(sniffer_command) |
| 329 | self.sniffer_proc_pid = sniffer_job.stdout |
| 330 | |
| 331 | def _stop_tshark(self): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 332 | """ Stops the sniffer.""" |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 333 | self.log.info('Stopping sniffer') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 334 | |
| 335 | # while loop to kill the sniffer process |
Omar El Ayach | 4ab5c8b | 2020-06-15 17:01:35 -0700 | [diff] [blame] | 336 | stop_time = time.time() + SNIFFER_TIMEOUT |
| 337 | while time.time() < stop_time: |
| 338 | # Wait before sending more kill signals |
| 339 | time.sleep(0.1) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 340 | try: |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 341 | # Returns 1 if process was killed |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 342 | self._sniffer_server.run( |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 343 | 'ps aux| grep {} | grep -v grep'.format( |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 344 | self.sniffer_proc_pid)) |
| 345 | except: |
Omar El Ayach | 4ab5c8b | 2020-06-15 17:01:35 -0700 | [diff] [blame] | 346 | return |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 347 | try: |
| 348 | # Returns error if process was killed already |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 349 | self._sniffer_server.run('sudo kill -15 {}'.format( |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 350 | str(self.sniffer_proc_pid))) |
| 351 | except: |
| 352 | # Except is hit when tshark is already dead but we will break |
| 353 | # out of the loop when confirming process is dead using ps aux |
| 354 | pass |
Omar El Ayach | 4ab5c8b | 2020-06-15 17:01:35 -0700 | [diff] [blame] | 355 | self.log.warning('Could not stop sniffer. Trying with SIGKILL.') |
| 356 | try: |
| 357 | self.log.debug('Killing sniffer with SIGKILL.') |
| 358 | self._sniffer_server.run('sudo kill -9 {}'.format( |
| 359 | str(self.sniffer_proc_pid))) |
| 360 | except: |
| 361 | self.log.debug('Sniffer process may have stopped succesfully.') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 362 | |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 363 | def _process_tshark_dump(self, log_file): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 364 | """ Process tshark dump for better readability. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 365 | |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 366 | Processes tshark dump for better readability and saves it to a file. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 367 | Adds an info column at the end of each row. Format of the info columns: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 368 | subtype of the frame, sequence no and retry status. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 369 | |
| 370 | Args: |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 371 | log_file : unprocessed sniffer output |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 372 | Returns: |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 373 | log_file : processed sniffer output |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 374 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 375 | temp_dump_file = os.path.join(self.log_path, 'sniffer_temp_dump.csv') |
| 376 | utils.exe_cmd('cp {} {}'.format(log_file, temp_dump_file)) |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 377 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 378 | with open(temp_dump_file, 'r') as input_csv, open(log_file, |
| 379 | 'w') as output_csv: |
Omar El Ayach | b2ca606 | 2019-11-05 22:25:21 -0800 | [diff] [blame] | 380 | reader = csv.DictReader(input_csv, |
| 381 | fieldnames=self.TSHARK_COLUMNS, |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 382 | delimiter='^') |
Omar El Ayach | b2ca606 | 2019-11-05 22:25:21 -0800 | [diff] [blame] | 383 | writer = csv.DictWriter(output_csv, |
| 384 | fieldnames=self.TSHARK_OUTPUT_COLUMNS, |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 385 | delimiter='\t') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 386 | writer.writeheader() |
| 387 | for row in reader: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 388 | if row['subtype'] in self.TYPE_SUBTYPE_DICT: |
| 389 | row['Info'] = '{sub} S={seq} retry={retry_status}'.format( |
| 390 | sub=self.TYPE_SUBTYPE_DICT[row['subtype']], |
| 391 | seq=row['seq'], |
| 392 | retry_status=row['retry']) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 393 | else: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 394 | row['Info'] = '{} S={} retry={}\n'.format( |
| 395 | row['subtype'], row['seq'], row['retry']) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 396 | writer.writerow(row) |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 397 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 398 | utils.exe_cmd('rm -f {}'.format(temp_dump_file)) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 399 | return log_file |
| 400 | |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 401 | def start_capture(self, network, chan, bw, duration=60): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 402 | """Starts sniffer capture on the specified machine. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 403 | |
| 404 | Args: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 405 | network: dict describing network to sniff on. |
| 406 | duration: duration of sniff. |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 407 | """ |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 408 | # Checking for existing sniffer processes |
| 409 | if self._started: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 410 | self.log.info('Sniffer already running') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 411 | return |
| 412 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 413 | # Configure sniffer |
| 414 | self._configure_sniffer(network, chan, bw) |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 415 | tshark_command = self._get_tshark_command(duration) |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 416 | sniffer_command = self._get_sniffer_command(tshark_command) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 417 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 418 | # Starting sniffer capture by executing tshark command |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 419 | self._run_tshark(sniffer_command) |
| 420 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 421 | def stop_capture(self, tag=''): |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 422 | """Stops the sniffer. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 423 | |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 424 | Args: |
| 425 | tag: tag to be appended to the sniffer output file. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 426 | Returns: |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 427 | log_file: path to sniffer dump. |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 428 | """ |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 429 | # Checking if there is an ongoing sniffer capture |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 430 | if not self._started: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 431 | self.log.error('No sniffer process running') |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 432 | return |
| 433 | # Killing sniffer process |
| 434 | self._stop_tshark() |
| 435 | |
Omar El Ayach | 4a02f7e | 2019-10-14 14:37:30 -0700 | [diff] [blame] | 436 | # Processing writing capture output to file |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 437 | log_file = self._get_full_file_path(tag) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 438 | self._sniffer_server.run('sudo chmod 777 {}'.format( |
| 439 | self._get_remote_dump_path())) |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 440 | self._sniffer_server.pull_file(log_file, self._get_remote_dump_path()) |
| 441 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 442 | if self.sniffer_output_file_type == 'csv': |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 443 | log_file = self._process_tshark_dump(log_file) |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 444 | |
| 445 | self.sniffer_proc_pid = None |
Abhinav | b27e6d3 | 2019-10-05 17:15:24 -0700 | [diff] [blame] | 446 | return log_file |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 447 | |
| 448 | |
| 449 | class TsharkSnifferOnUnix(TsharkSnifferBase): |
| 450 | """Class that implements Tshark based sniffer controller on Unix systems.""" |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 451 | def _scan_for_networks(self): |
| 452 | """Scans the wireless networks on the sniffer. |
| 453 | |
| 454 | Returns: |
| 455 | scan_results : output of the scan command. |
| 456 | """ |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 457 | scan_command = '/usr/local/bin/airport -s' |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 458 | scan_result = self._sniffer_server.run(scan_command).stdout |
| 459 | |
| 460 | return scan_result |
| 461 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 462 | def _configure_sniffer(self, network, chan, bw): |
| 463 | """Connects to a wireless network using networksetup utility. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 464 | |
| 465 | Args: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 466 | network: dictionary of network credentials; SSID and password. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 467 | """ |
| 468 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 469 | self.log.debug('Connecting to network {}'.format(network['SSID'])) |
| 470 | |
| 471 | if 'password' not in network: |
| 472 | network['password'] = '' |
| 473 | |
| 474 | connect_command = 'networksetup -setairportnetwork en0 {} {}'.format( |
| 475 | network['SSID'], network['password']) |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 476 | self._sniffer_server.run(connect_command) |
| 477 | |
| 478 | |
| 479 | class TsharkSnifferOnLinux(TsharkSnifferBase): |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 480 | """Class that implements Tshark based sniffer controller on Linux.""" |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 481 | def __init__(self, config): |
| 482 | super().__init__(config) |
Omar El Ayach | feb40b2 | 2020-05-13 12:18:20 -0700 | [diff] [blame] | 483 | self._init_sniffer() |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 484 | self.channel = None |
| 485 | self.bandwidth = None |
| 486 | |
Omar El Ayach | feb40b2 | 2020-05-13 12:18:20 -0700 | [diff] [blame] | 487 | def _init_sniffer(self): |
| 488 | """Function to configure interface for the first time""" |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 489 | self._sniffer_server.run('sudo modprobe -r iwlwifi') |
| 490 | self._sniffer_server.run('sudo dmesg -C') |
| 491 | self._sniffer_server.run('cat /dev/null | sudo tee /var/log/syslog') |
| 492 | self._sniffer_server.run('sudo modprobe iwlwifi debug=0x1') |
| 493 | # Wait for wifi config changes before trying to further configuration |
| 494 | # e.g. setting monitor mode (which will fail if above is not complete) |
| 495 | time.sleep(1) |
Omar El Ayach | feb40b2 | 2020-05-13 12:18:20 -0700 | [diff] [blame] | 496 | |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 497 | def set_monitor_mode(self, chan, bw): |
| 498 | """Function to configure interface to monitor mode |
| 499 | |
| 500 | Brings up the sniffer wireless interface in monitor mode and |
| 501 | tunes it to the appropriate channel and bandwidth |
| 502 | |
| 503 | Args: |
| 504 | chan: primary channel (int) to tune the sniffer to |
| 505 | bw: bandwidth (int) to tune the sniffer to |
| 506 | """ |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 507 | if chan == self.channel and bw == self.bandwidth: |
| 508 | return |
| 509 | |
| 510 | self.channel = chan |
| 511 | self.bandwidth = bw |
| 512 | |
| 513 | channel_map = { |
| 514 | 80: { |
| 515 | tuple(range(36, 50, 2)): 42, |
| 516 | tuple(range(52, 66, 2)): 58, |
| 517 | tuple(range(100, 114, 2)): 106, |
| 518 | tuple(range(116, 130, 2)): 122, |
| 519 | tuple(range(132, 146, 2)): 138, |
| 520 | tuple(range(149, 163, 2)): 155 |
| 521 | }, |
| 522 | 40: { |
| 523 | (36, 38, 40): 38, |
| 524 | (44, 46, 48): 46, |
| 525 | (52, 54, 56): 54, |
| 526 | (60, 62, 64): 62, |
| 527 | (100, 102, 104): 102, |
| 528 | (108, 110, 112): 108, |
| 529 | (116, 118, 120): 118, |
| 530 | (124, 126, 128): 126, |
| 531 | (132, 134, 136): 134, |
| 532 | (140, 142, 144): 142, |
| 533 | (149, 151, 153): 151, |
| 534 | (157, 159, 161): 159 |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | if chan <= 13: |
| 539 | primary_freq = WifiEnums.channel_2G_to_freq[chan] |
| 540 | else: |
| 541 | primary_freq = WifiEnums.channel_5G_to_freq[chan] |
| 542 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 543 | self._sniffer_server.run('sudo ifconfig {} down'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 544 | self.sniffer_interface)) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 545 | self._sniffer_server.run('sudo iwconfig {} mode monitor'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 546 | self.sniffer_interface)) |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 547 | self._sniffer_server.run('sudo ifconfig {} up'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 548 | self.sniffer_interface)) |
| 549 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 550 | if bw in channel_map: |
| 551 | for tuple_chan in channel_map[bw]: |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 552 | if chan in tuple_chan: |
| 553 | center_freq = WifiEnums.channel_5G_to_freq[channel_map[bw] |
| 554 | [tuple_chan]] |
| 555 | self._sniffer_server.run( |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 556 | 'sudo iw dev {} set freq {} {} {}'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 557 | self.sniffer_interface, primary_freq, bw, |
| 558 | center_freq)) |
| 559 | |
| 560 | else: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 561 | self._sniffer_server.run('sudo iw dev {} set freq {}'.format( |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 562 | self.sniffer_interface, primary_freq)) |
| 563 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 564 | def _configure_sniffer(self, network, chan, bw): |
| 565 | """ Connects to a wireless network using networksetup utility. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 566 | |
| 567 | Args: |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 568 | network: dictionary of network credentials; SSID and password. |
Abhinav | f57a70e | 2019-10-31 17:21:49 -0700 | [diff] [blame] | 569 | """ |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 570 | |
Omar El Ayach | db8463b | 2020-05-15 18:08:23 -0700 | [diff] [blame] | 571 | self.log.debug('Connecting to network {}'.format(network['SSID'])) |
Abhinav | 0b8138e | 2019-12-06 09:27:10 -0800 | [diff] [blame] | 572 | self.set_monitor_mode(chan, bw) |