blob: 73fb033beda7f12ad4bd3ca94259b34f708dcc63 [file] [log] [blame]
Omar El Ayach4a02f7e2019-10-14 14:37:30 -07001#!/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
Abhinavb27e6d32019-10-05 17:15:24 -070017import csv
Abhinavb27e6d32019-10-05 17:15:24 -070018import os
Omar El Ayachdb8463b2020-05-15 18:08:23 -070019import posixpath
20import time
Xianyuan Jia63751fb2020-11-17 00:07:40 +000021import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
Abhinav0b8138e2019-12-06 09:27:10 -080022
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070023from acts import context
24from acts import logger
25from acts import utils
26from acts.controllers.utils_lib import ssh
Abhinavb27e6d32019-10-05 17:15:24 -070027
Abhinav0b8138e2019-12-06 09:27:10 -080028WifiEnums = wutils.WifiEnums
Omar El Ayach4ab5c8b2020-06-15 17:01:35 -070029SNIFFER_TIMEOUT = 6
Abhinav0b8138e2019-12-06 09:27:10 -080030
Abhinavb27e6d32019-10-05 17:15:24 -070031
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070032def create(configs):
Abhinavf57a70e2019-10-31 17:21:49 -070033 """Factory method for sniffer.
Abhinavb27e6d32019-10-05 17:15:24 -070034 Args:
Abhinavf57a70e2019-10-31 17:21:49 -070035 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.
Abhinavb27e6d32019-10-05 17:15:24 -070040 """
41 objs = []
42 for config in configs:
43 try:
Omar El Ayachdb8463b2020-05-15 18:08:23 -070044 if config['type'] == 'tshark':
45 if config['os'] == 'unix':
Abhinavf57a70e2019-10-31 17:21:49 -070046 objs.append(TsharkSnifferOnUnix(config))
Omar El Ayachdb8463b2020-05-15 18:08:23 -070047 elif config['os'] == 'linux':
Abhinavf57a70e2019-10-31 17:21:49 -070048 objs.append(TsharkSnifferOnLinux(config))
49 else:
Omar El Ayachdb8463b2020-05-15 18:08:23 -070050 raise RuntimeError('Wrong sniffer config')
Abhinavf57a70e2019-10-31 17:21:49 -070051
Omar El Ayachdb8463b2020-05-15 18:08:23 -070052 elif config['type'] == 'mock':
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070053 objs.append(MockSniffer(config))
Abhinavb27e6d32019-10-05 17:15:24 -070054 except KeyError:
Omar El Ayachdb8463b2020-05-15 18:08:23 -070055 raise KeyError('Invalid sniffer configurations')
Abhinavb27e6d32019-10-05 17:15:24 -070056 return objs
57
58
59def destroy(objs):
60 return
61
62
63class OtaSnifferBase(object):
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070064 """Base class defining common sniffers functions."""
Abhinavb27e6d32019-10-05 17:15:24 -070065
66 _log_file_counter = 0
67
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070068 @property
69 def started(self):
70 raise NotImplementedError('started must be specified.')
Abhinavb27e6d32019-10-05 17:15:24 -070071
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070072 def start_capture(self, network, duration=30):
Abhinavf57a70e2019-10-31 17:21:49 -070073 """Starts the sniffer Capture.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070074
75 Args:
76 network: dict containing network information such as SSID, etc.
Abhinavf57a70e2019-10-31 17:21:49 -070077 duration: duration of sniffer capture in seconds.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070078 """
79 raise NotImplementedError('start_capture must be specified.')
80
Omar El Ayachdb8463b2020-05-15 18:08:23 -070081 def stop_capture(self, tag=''):
Abhinavf57a70e2019-10-31 17:21:49 -070082 """Stops the sniffer Capture.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070083
84 Args:
Abhinavf57a70e2019-10-31 17:21:49 -070085 tag: string to tag sniffer capture file name with.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070086 """
87 raise NotImplementedError('stop_capture must be specified.')
Abhinavb27e6d32019-10-05 17:15:24 -070088
89 def _get_remote_dump_path(self):
Abhinavf57a70e2019-10-31 17:21:49 -070090 """Returns name of the sniffer dump file."""
Omar El Ayachdb8463b2020-05-15 18:08:23 -070091 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
Abhinavb27e6d32019-10-05 17:15:24 -070095
96 def _get_full_file_path(self, tag=None):
97 """Returns the full file path for the sniffer capture dump file.
98
Omar El Ayach4a02f7e2019-10-14 14:37:30 -070099 Returns the full file path (on test machine) for the sniffer capture
100 dump file.
Abhinavb27e6d32019-10-05 17:15:24 -0700101
102 Args:
103 tag: The tag appended to the sniffer capture dump file .
104 """
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700105 tags = [tag, 'count', OtaSnifferBase._log_file_counter]
Abhinav0b8138e2019-12-06 09:27:10 -0800106 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)
Abhinavb27e6d32019-10-05 17:15:24 -0700109 OtaSnifferBase._log_file_counter += 1
110
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700111 file_path = os.path.join(self.log_path, out_file_name)
Abhinavb27e6d32019-10-05 17:15:24 -0700112 return file_path
113
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700114 @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 Ruyter72f8df92020-02-12 13:44:49 -0800121 os.makedirs(full_out_dir, exist_ok=True)
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700122
123 return full_out_dir
124
Abhinavb27e6d32019-10-05 17:15:24 -0700125
126class MockSniffer(OtaSnifferBase):
Abhinavf57a70e2019-10-31 17:21:49 -0700127 """Class that implements mock sniffer for test development and debug."""
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700128 def __init__(self, config):
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700129 self.log = logger.create_tagged_trace_logger('Mock Sniffer')
Abhinavb27e6d32019-10-05 17:15:24 -0700130
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700131 def start_capture(self, network, duration=30):
Abhinavf57a70e2019-10-31 17:21:49 -0700132 """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 Ayachdb8463b2020-05-15 18:08:23 -0700138 self.log.info('Starting sniffer.')
Abhinavb27e6d32019-10-05 17:15:24 -0700139
Abhinavf57a70e2019-10-31 17:21:49 -0700140 def stop_capture(self):
141 """Stops the sniffer.
Abhinavb27e6d32019-10-05 17:15:24 -0700142
143 Returns:
Abhinavf57a70e2019-10-31 17:21:49 -0700144 log_file: name of processed sniffer.
Abhinavb27e6d32019-10-05 17:15:24 -0700145 """
146
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700147 self.log.info('Stopping sniffer.')
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700148 log_file = self._get_full_file_path()
149 with open(log_file, 'w') as file:
150 file.write('this is a sniffer dump.')
Abhinavb27e6d32019-10-05 17:15:24 -0700151 return log_file
152
153
Abhinavf57a70e2019-10-31 17:21:49 -0700154class TsharkSnifferBase(OtaSnifferBase):
155 """Class that implements Tshark based sniffer controller. """
156
157 TYPE_SUBTYPE_DICT = {
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700158 '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)'
Abhinavf57a70e2019-10-31 17:21:49 -0700193 }
194
195 TSHARK_COLUMNS = [
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700196 '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'
Abhinavf57a70e2019-10-31 17:21:49 -0700202 ]
203
204 TSHARK_OUTPUT_COLUMNS = [
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700205 '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'
Abhinavf57a70e2019-10-31 17:21:49 -0700212 ]
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 Ayach4a02f7e2019-10-14 14:37:30 -0700228 def __init__(self, config):
Abhinavb27e6d32019-10-05 17:15:24 -0700229 self.sniffer_proc_pid = None
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700230 self.log = logger.create_tagged_trace_logger('Tshark Sniffer')
231 self.ssh_config = config['ssh_config']
232 self.sniffer_os = config['os']
Omar El Ayach8a604542020-05-13 16:19:27 -0700233 self.run_as_sudo = config.get('run_as_sudo', False)
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700234 self.sniffer_output_file_type = config['output_file_type']
235 self.sniffer_snap_length = config['snap_length']
236 self.sniffer_interface = config['interface']
Abhinavb27e6d32019-10-05 17:15:24 -0700237
Abhinavf57a70e2019-10-31 17:21:49 -0700238 #Logging into sniffer
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700239 self.log.info('Logging into sniffer.')
Abhinavf57a70e2019-10-31 17:21:49 -0700240 self._sniffer_server = ssh.connection.SshConnection(
241 ssh.settings.from_config(self.ssh_config))
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700242 # Get tshark params
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700243 self.tshark_fields = self._generate_tshark_fields(
Abhinavf57a70e2019-10-31 17:21:49 -0700244 self.TSHARK_FIELDS_LIST)
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700245 self.tshark_path = self._sniffer_server.run('which tshark').stdout
Abhinavb27e6d32019-10-05 17:15:24 -0700246
247 @property
248 def _started(self):
249 return self.sniffer_proc_pid is not None
250
Abhinavf57a70e2019-10-31 17:21:49 -0700251 def _scan_for_networks(self):
252 """Scans for wireless networks on the sniffer."""
253 raise NotImplementedError
254
Abhinavf57a70e2019-10-31 17:21:49 -0700255 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 Ayachdb8463b2020-05-15 18:08:23 -0700264 tshark_command = '{} -l -i {} -I -t u -a duration:{}'.format(
Abhinavf57a70e2019-10-31 17:21:49 -0700265 self.tshark_path, self.sniffer_interface, int(duration))
Omar El Ayachfeb40b22020-05-13 12:18:20 -0700266 if self.run_as_sudo:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700267 tshark_command = 'sudo {}'.format(tshark_command)
Abhinavf57a70e2019-10-31 17:21:49 -0700268
269 return tshark_command
270
Abhinav0b8138e2019-12-06 09:27:10 -0800271 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 Ayachdb8463b2020-05-15 18:08:23 -0700281 if self.sniffer_output_file_type in ['pcap', 'pcapng']:
282 sniffer_command = ' {tshark} -s {snaplength} -w {log_file} '.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800283 tshark=tshark_command,
284 snaplength=self.sniffer_snap_length,
285 log_file=self._get_remote_dump_path())
286
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700287 elif self.sniffer_output_file_type == 'csv':
288 sniffer_command = '{tshark} {fields} > {log_file}'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800289 tshark=tshark_command,
290 fields=self.tshark_fields,
291 log_file=self._get_remote_dump_path())
292
293 else:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700294 raise KeyError('Sniffer output file type not configured correctly')
Abhinav0b8138e2019-12-06 09:27:10 -0800295
296 return sniffer_command
297
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700298 def _generate_tshark_fields(self, fields):
Abhinavf57a70e2019-10-31 17:21:49 -0700299 """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:
Abhinav0b8138e2019-12-06 09:27:10 -0800305 tshark_fields: string of tshark fields to be appended
306 to the tshark command.
Abhinavf57a70e2019-10-31 17:21:49 -0700307 """
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700308 tshark_fields = "-T fields -y IEEE802_11_RADIO -E separator='^'"
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700309 for field in fields:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700310 tshark_fields = tshark_fields + ' -e {}'.format(field)
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700311 return tshark_fields
312
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700313 def _configure_sniffer(self, network, chan, bw):
Abhinavf57a70e2019-10-31 17:21:49 -0700314 """ Connects to a wireless network using networksetup utility.
Abhinavb27e6d32019-10-05 17:15:24 -0700315
316 Args:
Abhinavf57a70e2019-10-31 17:21:49 -0700317 network: dictionary of network credentials; SSID and password.
Abhinavb27e6d32019-10-05 17:15:24 -0700318 """
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700319 raise NotImplementedError
Abhinavb27e6d32019-10-05 17:15:24 -0700320
321 def _run_tshark(self, sniffer_command):
Abhinavf57a70e2019-10-31 17:21:49 -0700322 """Starts the sniffer.
323
324 Args:
325 sniffer_command: sniffer command to execute.
326 """
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700327 self.log.info('Starting sniffer.')
Abhinavb27e6d32019-10-05 17:15:24 -0700328 sniffer_job = self._sniffer_server.run_async(sniffer_command)
329 self.sniffer_proc_pid = sniffer_job.stdout
330
331 def _stop_tshark(self):
Abhinavf57a70e2019-10-31 17:21:49 -0700332 """ Stops the sniffer."""
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700333 self.log.info('Stopping sniffer')
Abhinavb27e6d32019-10-05 17:15:24 -0700334
335 # while loop to kill the sniffer process
Omar El Ayach4ab5c8b2020-06-15 17:01:35 -0700336 stop_time = time.time() + SNIFFER_TIMEOUT
337 while time.time() < stop_time:
338 # Wait before sending more kill signals
339 time.sleep(0.1)
Abhinavb27e6d32019-10-05 17:15:24 -0700340 try:
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700341 # Returns 1 if process was killed
Abhinavb27e6d32019-10-05 17:15:24 -0700342 self._sniffer_server.run(
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700343 'ps aux| grep {} | grep -v grep'.format(
Abhinavb27e6d32019-10-05 17:15:24 -0700344 self.sniffer_proc_pid))
345 except:
Omar El Ayach4ab5c8b2020-06-15 17:01:35 -0700346 return
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700347 try:
348 # Returns error if process was killed already
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700349 self._sniffer_server.run('sudo kill -15 {}'.format(
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700350 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 Ayach4ab5c8b2020-06-15 17:01:35 -0700355 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.')
Abhinavb27e6d32019-10-05 17:15:24 -0700362
Abhinav0b8138e2019-12-06 09:27:10 -0800363 def _process_tshark_dump(self, log_file):
Abhinavf57a70e2019-10-31 17:21:49 -0700364 """ Process tshark dump for better readability.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700365
Abhinavb27e6d32019-10-05 17:15:24 -0700366 Processes tshark dump for better readability and saves it to a file.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700367 Adds an info column at the end of each row. Format of the info columns:
Abhinavf57a70e2019-10-31 17:21:49 -0700368 subtype of the frame, sequence no and retry status.
Abhinavb27e6d32019-10-05 17:15:24 -0700369
370 Args:
Abhinav0b8138e2019-12-06 09:27:10 -0800371 log_file : unprocessed sniffer output
Abhinavb27e6d32019-10-05 17:15:24 -0700372 Returns:
Abhinav0b8138e2019-12-06 09:27:10 -0800373 log_file : processed sniffer output
Abhinavb27e6d32019-10-05 17:15:24 -0700374 """
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700375 temp_dump_file = os.path.join(self.log_path, 'sniffer_temp_dump.csv')
376 utils.exe_cmd('cp {} {}'.format(log_file, temp_dump_file))
Abhinav0b8138e2019-12-06 09:27:10 -0800377
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700378 with open(temp_dump_file, 'r') as input_csv, open(log_file,
379 'w') as output_csv:
Omar El Ayachb2ca6062019-11-05 22:25:21 -0800380 reader = csv.DictReader(input_csv,
381 fieldnames=self.TSHARK_COLUMNS,
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700382 delimiter='^')
Omar El Ayachb2ca6062019-11-05 22:25:21 -0800383 writer = csv.DictWriter(output_csv,
384 fieldnames=self.TSHARK_OUTPUT_COLUMNS,
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700385 delimiter='\t')
Abhinavb27e6d32019-10-05 17:15:24 -0700386 writer.writeheader()
387 for row in reader:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700388 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'])
Abhinavb27e6d32019-10-05 17:15:24 -0700393 else:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700394 row['Info'] = '{} S={} retry={}\n'.format(
395 row['subtype'], row['seq'], row['retry'])
Abhinavb27e6d32019-10-05 17:15:24 -0700396 writer.writerow(row)
Abhinav0b8138e2019-12-06 09:27:10 -0800397
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700398 utils.exe_cmd('rm -f {}'.format(temp_dump_file))
Abhinavb27e6d32019-10-05 17:15:24 -0700399 return log_file
400
Abhinav0b8138e2019-12-06 09:27:10 -0800401 def start_capture(self, network, chan, bw, duration=60):
Abhinavf57a70e2019-10-31 17:21:49 -0700402 """Starts sniffer capture on the specified machine.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700403
404 Args:
Abhinavf57a70e2019-10-31 17:21:49 -0700405 network: dict describing network to sniff on.
406 duration: duration of sniff.
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700407 """
Abhinavb27e6d32019-10-05 17:15:24 -0700408 # Checking for existing sniffer processes
409 if self._started:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700410 self.log.info('Sniffer already running')
Abhinavb27e6d32019-10-05 17:15:24 -0700411 return
412
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700413 # Configure sniffer
414 self._configure_sniffer(network, chan, bw)
Abhinavf57a70e2019-10-31 17:21:49 -0700415 tshark_command = self._get_tshark_command(duration)
Abhinav0b8138e2019-12-06 09:27:10 -0800416 sniffer_command = self._get_sniffer_command(tshark_command)
Abhinavb27e6d32019-10-05 17:15:24 -0700417
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700418 # Starting sniffer capture by executing tshark command
Abhinavb27e6d32019-10-05 17:15:24 -0700419 self._run_tshark(sniffer_command)
420
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700421 def stop_capture(self, tag=''):
Abhinavf57a70e2019-10-31 17:21:49 -0700422 """Stops the sniffer.
Abhinavb27e6d32019-10-05 17:15:24 -0700423
Abhinavf57a70e2019-10-31 17:21:49 -0700424 Args:
425 tag: tag to be appended to the sniffer output file.
Abhinavb27e6d32019-10-05 17:15:24 -0700426 Returns:
Abhinavf57a70e2019-10-31 17:21:49 -0700427 log_file: path to sniffer dump.
Abhinavb27e6d32019-10-05 17:15:24 -0700428 """
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700429 # Checking if there is an ongoing sniffer capture
Abhinavb27e6d32019-10-05 17:15:24 -0700430 if not self._started:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700431 self.log.error('No sniffer process running')
Abhinavb27e6d32019-10-05 17:15:24 -0700432 return
433 # Killing sniffer process
434 self._stop_tshark()
435
Omar El Ayach4a02f7e2019-10-14 14:37:30 -0700436 # Processing writing capture output to file
Abhinav0b8138e2019-12-06 09:27:10 -0800437 log_file = self._get_full_file_path(tag)
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700438 self._sniffer_server.run('sudo chmod 777 {}'.format(
439 self._get_remote_dump_path()))
Abhinav0b8138e2019-12-06 09:27:10 -0800440 self._sniffer_server.pull_file(log_file, self._get_remote_dump_path())
441
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700442 if self.sniffer_output_file_type == 'csv':
Abhinav0b8138e2019-12-06 09:27:10 -0800443 log_file = self._process_tshark_dump(log_file)
Abhinavb27e6d32019-10-05 17:15:24 -0700444
445 self.sniffer_proc_pid = None
Abhinavb27e6d32019-10-05 17:15:24 -0700446 return log_file
Abhinavf57a70e2019-10-31 17:21:49 -0700447
448
449class TsharkSnifferOnUnix(TsharkSnifferBase):
450 """Class that implements Tshark based sniffer controller on Unix systems."""
Abhinavf57a70e2019-10-31 17:21:49 -0700451 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 Ayachdb8463b2020-05-15 18:08:23 -0700457 scan_command = '/usr/local/bin/airport -s'
Abhinavf57a70e2019-10-31 17:21:49 -0700458 scan_result = self._sniffer_server.run(scan_command).stdout
459
460 return scan_result
461
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700462 def _configure_sniffer(self, network, chan, bw):
463 """Connects to a wireless network using networksetup utility.
Abhinavf57a70e2019-10-31 17:21:49 -0700464
465 Args:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700466 network: dictionary of network credentials; SSID and password.
Abhinavf57a70e2019-10-31 17:21:49 -0700467 """
468
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700469 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'])
Abhinavf57a70e2019-10-31 17:21:49 -0700476 self._sniffer_server.run(connect_command)
477
478
479class TsharkSnifferOnLinux(TsharkSnifferBase):
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700480 """Class that implements Tshark based sniffer controller on Linux."""
Abhinav0b8138e2019-12-06 09:27:10 -0800481 def __init__(self, config):
482 super().__init__(config)
Omar El Ayachfeb40b22020-05-13 12:18:20 -0700483 self._init_sniffer()
Abhinav0b8138e2019-12-06 09:27:10 -0800484 self.channel = None
485 self.bandwidth = None
486
Omar El Ayachfeb40b22020-05-13 12:18:20 -0700487 def _init_sniffer(self):
488 """Function to configure interface for the first time"""
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700489 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 Ayachfeb40b22020-05-13 12:18:20 -0700496
Abhinav0b8138e2019-12-06 09:27:10 -0800497 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 """
Abhinav0b8138e2019-12-06 09:27:10 -0800507 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 Ayachdb8463b2020-05-15 18:08:23 -0700543 self._sniffer_server.run('sudo ifconfig {} down'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800544 self.sniffer_interface))
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700545 self._sniffer_server.run('sudo iwconfig {} mode monitor'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800546 self.sniffer_interface))
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700547 self._sniffer_server.run('sudo ifconfig {} up'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800548 self.sniffer_interface))
549
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700550 if bw in channel_map:
551 for tuple_chan in channel_map[bw]:
Abhinav0b8138e2019-12-06 09:27:10 -0800552 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 Ayachdb8463b2020-05-15 18:08:23 -0700556 'sudo iw dev {} set freq {} {} {}'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800557 self.sniffer_interface, primary_freq, bw,
558 center_freq))
559
560 else:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700561 self._sniffer_server.run('sudo iw dev {} set freq {}'.format(
Abhinav0b8138e2019-12-06 09:27:10 -0800562 self.sniffer_interface, primary_freq))
563
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700564 def _configure_sniffer(self, network, chan, bw):
565 """ Connects to a wireless network using networksetup utility.
Abhinavf57a70e2019-10-31 17:21:49 -0700566
567 Args:
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700568 network: dictionary of network credentials; SSID and password.
Abhinavf57a70e2019-10-31 17:21:49 -0700569 """
Abhinav0b8138e2019-12-06 09:27:10 -0800570
Omar El Ayachdb8463b2020-05-15 18:08:23 -0700571 self.log.debug('Connecting to network {}'.format(network['SSID']))
Abhinav0b8138e2019-12-06 09:27:10 -0800572 self.set_monitor_mode(chan, bw)