1#!/usr/bin/env python3 2# 3# Copyright 2016 - 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 17import importlib 18import logging 19 20MOBLY_CONTROLLER_CONFIG_NAME = "Sniffer" 21ACTS_CONTROLLER_REFERENCE_NAME = "sniffers" 22 23 24def create(configs): 25 """Initializes the sniffer structures based on the JSON configuration. The 26 expected keys are: 27 28 Type: A first-level type of sniffer. Planned to be 'local' for sniffers 29 running on the local machine, or 'remote' for sniffers running 30 remotely. 31 SubType: The specific sniffer type to be used. 32 Interface: The WLAN interface used to configure the sniffer. 33 BaseConfigs: A dictionary specifying baseline configurations of the 34 sniffer. Configurations can be overridden when starting a capture. 35 The keys must be one of the Sniffer.CONFIG_KEY_* values. 36 """ 37 objs = [] 38 for c in configs: 39 sniffer_type = c["Type"] 40 sniffer_subtype = c["SubType"] 41 interface = c["Interface"] 42 base_configs = c["BaseConfigs"] 43 module_name = "acts.controllers.sniffer_lib.{}.{}".format( 44 sniffer_type, sniffer_subtype) 45 module = importlib.import_module(module_name) 46 objs.append( 47 module.Sniffer(interface, 48 logging.getLogger(), 49 base_configs=base_configs)) 50 return objs 51 52 53def destroy(objs): 54 """Destroys the sniffers and terminates any ongoing capture sessions. 55 """ 56 for sniffer in objs: 57 try: 58 sniffer.stop_capture() 59 except SnifferError: 60 pass 61 62 63class SnifferError(Exception): 64 """This is the Exception class defined for all errors generated by 65 Sniffer-related modules. 66 """ 67 68 69class InvalidDataError(Exception): 70 """This exception is thrown when invalid configuration data is passed 71 to a method. 72 """ 73 74 75class ExecutionError(SnifferError): 76 """This exception is thrown when trying to configure the capture device 77 or when trying to execute the capture operation. 78 79 When this exception is seen, it is possible that the sniffer module is run 80 without sudo (for local sniffers) or keys are out-of-date (for remote 81 sniffers). 82 """ 83 84 85class InvalidOperationError(SnifferError): 86 """Certain methods may only be accessed when the instance upon which they 87 are invoked is in a certain state. This indicates that the object is not 88 in the correct state for a method to be called. 89 """ 90 91 92class Sniffer(object): 93 """This class defines an object representing a sniffer. 94 95 The object defines the generic behavior of sniffers - irrespective of how 96 they are implemented, or where they are located: on the local machine or on 97 the remote machine. 98 """ 99 100 CONFIG_KEY_CHANNEL = "channel" 101 102 def __init__(self, interface, logger, base_configs=None): 103 """The constructor for the Sniffer. It constructs a sniffer and 104 configures it to be ready for capture. 105 106 Args: 107 interface: A string specifying the interface used to configure the 108 sniffer. 109 logger: ACTS logger object. 110 base_configs: A dictionary containing baseline configurations of the 111 sniffer. These can be overridden when staring a capture. The 112 keys are specified by Sniffer.CONFIG_KEY_*. 113 114 Returns: 115 self: A configured sniffer. 116 117 Raises: 118 InvalidDataError: if the config_path is invalid. 119 NoPermissionError: if an error occurs while configuring the 120 sniffer. 121 """ 122 raise NotImplementedError("Base class should not be called directly!") 123 124 def get_descriptor(self): 125 """This function returns a string describing the sniffer. The specific 126 string (and its format) is up to each derived sniffer type. 127 128 Returns: 129 A string describing the sniffer. 130 """ 131 raise NotImplementedError("Base class should not be called directly!") 132 133 def get_type(self): 134 """This function returns the type of the sniffer. 135 136 Returns: 137 The type (string) of the sniffer. Corresponds to the 'Type' key of 138 the sniffer configuration. 139 """ 140 raise NotImplementedError("Base class should not be called directly!") 141 142 def get_subtype(self): 143 """This function returns the sub-type of the sniffer. 144 145 Returns: 146 The sub-type (string) of the sniffer. Corresponds to the 'SubType' 147 key of the sniffer configuration. 148 """ 149 raise NotImplementedError("Base class should not be called directly!") 150 151 def get_interface(self): 152 """This function returns The interface used to configure the sniffer, 153 e.g. 'wlan0'. 154 155 Returns: 156 The interface (string) used to configure the sniffer. Corresponds to 157 the 'Interface' key of the sniffer configuration. 158 """ 159 raise NotImplementedError("Base class should not be called directly!") 160 161 def get_capture_file(self): 162 """The sniffer places a capture in the logger directory. This function 163 enables the caller to obtain the path of that capture. 164 165 Returns: 166 The full path of the current or last capture. 167 """ 168 raise NotImplementedError("Base class should not be called directly!") 169 170 def start_capture(self, 171 override_configs=None, 172 additional_args=None, 173 duration=None, 174 packet_count=None): 175 """This function starts a capture which is saved to the specified file 176 path. 177 178 Depending on the type/subtype and configuration of the sniffer the 179 capture may terminate on its own or may require an explicit call to the 180 stop_capture() function. 181 182 This is a non-blocking function so a terminating function must be 183 called - either explicitly or implicitly: 184 - Explicitly: call either stop_capture() or wait_for_capture() 185 - Implicitly: use with a with clause. The wait_for_capture() function 186 will be called if a duration is specified (i.e. is not 187 None), otherwise a stop_capture() will be called. 188 189 The capture is saved to a file in the log path of the logger. Use 190 the get_capture_file() to get the full path to the current or most 191 recent capture. 192 193 Args: 194 override_configs: A dictionary which is combined with the 195 base_configs ("BaseConfigs" in the sniffer configuration). The 196 keys (specified by Sniffer.CONFIG_KEY_*) determine the 197 configuration of the sniffer for this specific capture. 198 additional_args: A string specifying additional raw 199 command-line arguments to pass to the underlying sniffer. The 200 interpretation of these flags is sniffer-dependent. 201 duration: An integer specifying the number of seconds over which to 202 capture packets. The sniffer will be terminated after this 203 duration. Used in implicit mode when using a 'with' clause. In 204 explicit control cases may have to be performed using a 205 sleep+stop or as the timeout argument to the wait function. 206 packet_count: An integer specifying the number of packets to capture 207 before terminating. Should be used with duration to guarantee 208 that capture terminates at some point (even if did not capture 209 the specified number of packets). 210 211 Returns: 212 An ActiveCaptureContext process which can be used with a 'with' 213 clause. 214 215 Raises: 216 InvalidDataError: for invalid configurations 217 NoPermissionError: if an error occurs while configuring and running 218 the sniffer. 219 """ 220 raise NotImplementedError("Base class should not be called directly!") 221 222 def stop_capture(self): 223 """This function stops a capture and guarantees that the capture is 224 saved to the capture file configured during the start_capture() method. 225 Depending on the type of the sniffer the file may previously contain 226 partial results (e.g. for a local sniffer) or may not exist until the 227 stop_capture() method is executed (e.g. for a remote sniffer). 228 229 Depending on the type/subtype and configuration of the sniffer the 230 capture may terminate on its own without requiring a call to this 231 function. In such a case it is still necessary to call either this 232 function or the wait_for_capture() function to make sure that the 233 capture file is moved to the correct location. 234 235 Raises: 236 NoPermissionError: No permission when trying to stop a capture 237 and save the capture file. 238 """ 239 raise NotImplementedError("Base class should not be called directly!") 240 241 def wait_for_capture(self, timeout=None): 242 """This function waits for a capture to terminate and guarantees that 243 the capture is saved to the capture file configured during the 244 start_capture() method. Depending on the type of the sniffer the file 245 may previously contain partial results (e.g. for a local sniffer) or 246 may not exist until the stop_capture() method is executed (e.g. for a 247 remote sniffer). 248 249 Depending on the type/subtype and configuration of the sniffer the 250 capture may terminate on its own without requiring a call to this 251 function. In such a case it is still necessary to call either this 252 function or the stop_capture() function to make sure that the capture 253 file is moved to the correct location. 254 255 Args: 256 timeout: An integer specifying the number of seconds to wait for 257 the capture to terminate on its own. On expiration of the 258 timeout the sniffer is stopped explicitly using the 259 stop_capture() function. 260 261 Raises: 262 NoPermissionError: No permission when trying to stop a capture and 263 save the capture file. 264 """ 265 raise NotImplementedError("Base class should not be called directly!") 266 267 268class ActiveCaptureContext(object): 269 """This class defines an object representing an active sniffer capture. 270 271 The object is returned by a Sniffer.start_capture() command and terminates 272 the capture when the 'with' clause exits. It is syntactic sugar for 273 try/finally. 274 """ 275 276 _sniffer = None 277 _timeout = None 278 279 def __init__(self, sniffer, timeout=None): 280 self._sniffer = sniffer 281 self._timeout = timeout 282 283 def __enter__(self): 284 pass 285 286 def __exit__(self, type, value, traceback): 287 if self._sniffer is not None: 288 if self._timeout is None: 289 self._sniffer.stop_capture() 290 else: 291 self._sniffer.wait_for_capture(self._timeout) 292 self._sniffer = None 293