Source code for whad.common.monitors.pcap

from whad.common.monitors import WhadMonitor
from scapy.all import conf
from scapy.layers.bluetooth4LE import *
from scapy.utils import PcapWriter,PcapReader
from scapy.error import Scapy_Exception
from os.path import exists
from whad.common.pcap import patch_pcap_metadata, extract_pcap_metadata
from time import time
from os import stat, remove
from stat import S_ISFIFO
from threading import Lock

import logging
logger = logging.getLogger(__name__)

[docs] class PcapWriterMonitor(WhadMonitor): """ PcapWriterMonitor. Monitor allowing to export the traffic transmitted and received by the targeted connector to a PCAP file with appropriate header. :Usage: >>> monitor = PcapWriterMonitor("mypcapfile.pcap") >>> monitor.attach(connector) >>> monitor.start() """ def __init__(self, pcap_file, monitor_reception=True, monitor_transmission=True, append=True): super().__init__(monitor_reception, monitor_transmission) self._pcap_file = pcap_file self._writer = None self._formatter = None self._reference_time = None self._start_time = None self._nb_pkts_written = 0 self._writer_lock = Lock() self._append = append @property def packets_written(self): '''Return the number of packets already written ''' return self._nb_pkts_written
[docs] def setup(self): # First, we check if the pcap already exists existing_pcap_file = exists(self._pcap_file) sync = False # If it exists, we have two cases: # - it is a named pipe and we have to provide sync=True to PcapWriter # - it is an already existing pcap file and we have to provide append=True to PcapWriter if existing_pcap_file: # Checks if it is a named FIFO if S_ISFIFO(stat(self._pcap_file).st_mode): logger.info("[i] Named pipe %s detected, syncing.", self._pcap_file) sync = True elif not self._append: logger.info("[i] PCAP file %s exists, overwriting.", self._pcap_file) remove(self._pcap_file) existing_pcap_file = False else: # Checks if it is an already existing pcap file. logger.info("[i] PCAP file %s exists, appending new packets.", self._pcap_file) try: # We collect the first packet timestamp to use it as reference time self._start_time = PcapReader(self._pcap_file).read_packet().time * 1000000 except (EOFError, Scapy_Exception): # Pcap is empty, remove it and open a new one remove(self._pcap_file) existing_pcap_file = False # Instantiate the PCAP Writer with the appropriate parameters self._writer_lock.acquire() self._writer = PcapWriter( self._pcap_file, append=existing_pcap_file and not sync, sync=sync ) self._writer_lock.release() # Checks if there is a scapy packet formatter associated with the connector. # A formatter allows to describe manually how to build the packet, it is mainly # useful to populate a relevant header for PCAP export. self._formatter = self.default_formatter if ( hasattr(self._connector, "format") and callable(getattr(self._connector, "format")) ): self._formatter = getattr(self._connector, "format")
[docs] def close(self): # Acquire lock on writer self._writer_lock.acquire() if hasattr(self, "_writer") and self._writer is not None: # Close writer try: self._writer.close() patch_pcap_metadata(self._pcap_file, self._connector.domain) except BrokenPipeError: pass # Mark writer as not available anymore self._writer = None # Release lock on writer self._writer_lock.release()
[docs] def default_formatter(self, packet): """ Formatter used by default, if no formatter is found in the targeted connector. It only extracts the accurate timestamp if one is available in metadata. """ if ( hasattr(packet, "metadata") and hasattr(packet.metadata, "timestamp") ): return packet, packet.metadata.timestamp else: return packet, None
[docs] def process_packet(self, packet): # Acquire lock on writer lock acquire multithread error self._writer_lock.acquire() if self._processing: # Note the current local clock timestamp in us now = time() * 1000000 packet, timestamp = self._formatter(packet) # Relative time synchronization if timestamp is None: timestamp = now # Process accurate timestamp if available, else use local clock if self._reference_time is None: if self._start_time is None: self._reference_time = (now, timestamp) else: self._reference_time = (self._start_time, timestamp - (now - self._start_time)) timestamp = now else: timestamp = self._reference_time[0] + (timestamp - self._reference_time[1]) # Convert timestamp to second (float) packet.time = timestamp / 1000000 try: if self._writer is not None: # Write packet self._writer.write(packet) self._nb_pkts_written += 1 else: # We are trying to write to a closed PCAP monitor, # issue a warning message logger.warning('cannot write to PCAP: file has already been closed') except BrokenPipeError: pass # Release lock self._writer_lock.release()