• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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