1#!/usr/bin/env python3.4 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 18 19ACTS_CONTROLLER_CONFIG_NAME = "Sniffer" 20ACTS_CONTROLLER_REFERENCE_NAME = "sniffers" 21 22def create(configs, logger): 23 """Initializes the sniffer structures based on the JSON configuration. The 24 expected keys are: 25 26 Type: A first-level type of sniffer. Planned to be 'local' for sniffers 27 running on the local machine, or 'remote' for sniffers running 28 remotely. 29 SubType: The specific sniffer type to be used. 30 Interface: The WLAN interface used to configure the sniffer. 31 BaseConfigs: A dictionary specifying baseline configurations of the 32 sniffer. Configurations can be overridden when starting a capture. 33 The keys must be one of the Sniffer.CONFIG_KEY_* values. 34 """ 35 objs = [] 36 for c in configs: 37 sniffer_type = c["Type"] 38 sniffer_subtype = c["SubType"] 39 interface = c["Interface"] 40 base_configs = c["BaseConfigs"] 41 module_name = "acts.controllers.sniffer_lib.{}.{}".format(sniffer_type, 42 sniffer_subtype) 43 module = importlib.import_module(module_name) 44 objs.append(module.Sniffer(interface, logger, 45 base_configs=base_configs)) 46 return objs 47 48 49def destroy(objs): 50 """Destroys the sniffers and terminates any ongoing capture sessions. 51 """ 52 for sniffer in objs: 53 try: 54 sniffer.stop_capture() 55 except SnifferError: 56 pass 57 58 59class SnifferError(Exception): 60 """This is the Exception class defined for all errors generated by 61 Sniffer-related modules. 62 """ 63 pass 64 65 66class InvalidDataError(Exception): 67 """This exception is thrown when invalid configuration data is passed 68 to a method. 69 """ 70 pass 71 72 73class ExecutionError(SnifferError): 74 """This exception is thrown when trying to configure the capture device 75 or when trying to execute the capture operation. 76 77 When this exception is seen, it is possible that the sniffer module is run 78 without sudo (for local sniffers) or keys are out-of-date (for remote 79 sniffers). 80 """ 81 pass 82 83 84class InvalidOperationError(SnifferError): 85 """Certain methods may only be accessed when the instance upon which they 86 are invoked is in a certain state. This indicates that the object is not 87 in the correct state for a method to be called. 88 """ 89 pass 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, override_configs=None, additional_args=None, 171 duration=None, packet_count=None): 172 """This function starts a capture which is saved to the specified file 173 path. 174 175 Depending on the type/subtype and configuration of the sniffer the 176 capture may terminate on its own or may require an explicit call to the 177 stop_capture() function. 178 179 This is a non-blocking function so a terminating function must be 180 called - either explicitly or implicitly: 181 - Explicitly: call either stop_capture() or wait_for_capture() 182 - Implicitly: use with a with clause. The wait_for_capture() function 183 will be called if a duration is specified (i.e. is not 184 None), otherwise a stop_capture() will be called. 185 186 The capture is saved to a file in the log path of the logger. Use 187 the get_capture_file() to get the full path to the current or most 188 recent capture. 189 190 Args: 191 override_configs: A dictionary which is combined with the 192 base_configs ("BaseConfigs" in the sniffer configuration). The 193 keys (specified by Sniffer.CONFIG_KEY_*) determine the 194 configuration of the sniffer for this specific capture. 195 additional_args: A string specifying additional raw 196 command-line arguments to pass to the underlying sniffer. The 197 interpretation of these flags is sniffer-dependent. 198 duration: An integer specifying the number of seconds over which to 199 capture packets. The sniffer will be terminated after this 200 duration. Used in implicit mode when using a 'with' clause. In 201 explicit control cases may have to be performed using a 202 sleep+stop or as the timeout argument to the wait function. 203 packet_count: An integer specifying the number of packets to capture 204 before terminating. Should be used with duration to guarantee 205 that capture terminates at some point (even if did not capture 206 the specified number of packets). 207 208 Returns: 209 An ActiveCaptureContext process which can be used with a 'with' 210 clause. 211 212 Raises: 213 InvalidDataError: for invalid configurations 214 NoPermissionError: if an error occurs while configuring and running 215 the sniffer. 216 """ 217 raise NotImplementedError("Base class should not be called directly!") 218 219 def stop_capture(self): 220 """This function stops a capture and guarantees that the capture is 221 saved to the capture file configured during the start_capture() method. 222 Depending on the type of the sniffer the file may previously contain 223 partial results (e.g. for a local sniffer) or may not exist until the 224 stop_capture() method is executed (e.g. for a remote sniffer). 225 226 Depending on the type/subtype and configuration of the sniffer the 227 capture may terminate on its own without requiring a call to this 228 function. In such a case it is still necessary to call either this 229 function or the wait_for_capture() function to make sure that the 230 capture file is moved to the correct location. 231 232 Raises: 233 NoPermissionError: No permission when trying to stop a capture 234 and save the capture file. 235 """ 236 raise NotImplementedError("Base class should not be called directly!") 237 238 def wait_for_capture(self, timeout=None): 239 """This function waits for a capture to terminate and guarantees that 240 the capture is saved to the capture file configured during the 241 start_capture() method. Depending on the type of the sniffer the file 242 may previously contain partial results (e.g. for a local sniffer) or 243 may not exist until the stop_capture() method is executed (e.g. for a 244 remote sniffer). 245 246 Depending on the type/subtype and configuration of the sniffer the 247 capture may terminate on its own without requiring a call to this 248 function. In such a case it is still necessary to call either this 249 function or the stop_capture() function to make sure that the capture 250 file is moved to the correct location. 251 252 Args: 253 timeout: An integer specifying the number of seconds to wait for 254 the capture to terminate on its own. On expiration of the 255 timeout the sniffer is stopped explicitly using the 256 stop_capture() function. 257 258 Raises: 259 NoPermissionError: No permission when trying to stop a capture and 260 save the capture file. 261 """ 262 raise NotImplementedError("Base class should not be called directly!") 263 264 265class ActiveCaptureContext(object): 266 """This class defines an object representing an active sniffer capture. 267 268 The object is returned by a Sniffer.start_capture() command and terminates 269 the capture when the 'with' clause exits. It is syntactic sugar for 270 try/finally. 271 """ 272 273 _sniffer = None 274 _timeout = None 275 276 def __init__(self, sniffer, timeout=None): 277 self._sniffer = sniffer 278 self._timeout = timeout 279 280 def __enter__(self): 281 pass 282 283 def __exit__(self, type, value, traceback): 284 if self._sniffer is not None: 285 if self._timeout is None: 286 self._sniffer.stop_capture() 287 else: 288 self._sniffer.wait_for_capture(self._timeout) 289 self._sniffer = None 290