1#!/usr/bin/env python3 2# 3# Copyright (c) 2022, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29 30import argparse 31import logging 32import signal 33import time 34import pcap_codec 35import sys 36import threading 37 38import sniffer_transport 39 40 41class Sniffer: 42 """ Class representing the Sniffing node, whose main task is listening. 43 """ 44 45 logger = logging.getLogger('sniffer.Sniffer') 46 47 RECV_BUFFER_SIZE = 4096 48 49 def __init__(self, filename, channel): 50 self._pcap = pcap_codec.PcapCodec(filename, channel) 51 52 # Create transport 53 transport_factory = sniffer_transport.SnifferTransportFactory() 54 self._transport = transport_factory.create_transport() 55 56 self._thread = None 57 self._thread_alive = threading.Event() 58 self._thread_alive.clear() 59 60 def _sniffer_main_loop(self): 61 """ Sniffer main loop. """ 62 63 self.logger.debug('Sniffer started.') 64 65 while self._thread_alive.is_set(): 66 data, nodeid = self._transport.recv(self.RECV_BUFFER_SIZE) 67 self._pcap.append(data) 68 69 self.logger.debug('Sniffer stopped.') 70 71 def start(self): 72 """ Start sniffing. """ 73 74 self._thread = threading.Thread(target=self._sniffer_main_loop) 75 self._thread.daemon = True 76 77 self._transport.open() 78 79 self._thread_alive.set() 80 self._thread.start() 81 82 def stop(self): 83 """ Stop sniffing. """ 84 85 self._thread_alive.clear() 86 87 self._transport.close() 88 89 self._thread.join(timeout=1) 90 self._thread = None 91 92 def close(self): 93 """ Close the pcap file. """ 94 95 self._pcap.close() 96 97 98def run_sniffer(): 99 parser = argparse.ArgumentParser() 100 parser.add_argument('-o', 101 '--output', 102 dest='output', 103 type=str, 104 required=True, 105 help='the path of the output .pcap file') 106 parser.add_argument('-c', 107 '--channel', 108 dest='channel', 109 type=int, 110 required=True, 111 help='the channel which is sniffered') 112 args = parser.parse_args() 113 114 sniffer = Sniffer(args.output, args.channel) 115 sniffer.start() 116 117 def atexit(signum, frame): 118 sniffer.stop() 119 sniffer.close() 120 sys.exit(0) 121 122 signal.signal(signal.SIGTERM, atexit) 123 124 while sniffer._thread_alive.is_set(): 125 time.sleep(0.5) 126 127 sniffer.stop() 128 sniffer.close() 129 130 131if __name__ == '__main__': 132 run_sniffer() 133