From 6c8b27ed9430116d35e4d1644c17785dd809e154 Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Wed, 19 Sep 2018 15:11:10 -0700 Subject: [PATCH] Support for starting a live capture. Support for terminating PcapHandleReader. --- .../edu/uci/iotproject/io/LiveCapture.java | 102 ++++++++++++++++++ .../uci/iotproject/io/PcapHandleReader.java | 45 +++++++- 2 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/LiveCapture.java diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/LiveCapture.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/LiveCapture.java new file mode 100644 index 0000000..2539415 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/LiveCapture.java @@ -0,0 +1,102 @@ +package edu.uci.iotproject.io; + +import org.pcap4j.core.*; +import org.pcap4j.packet.namednumber.DataLinkType; +import org.pcap4j.util.NifSelector; + +import java.io.IOException; +import java.util.Objects; + +/** + * Utility methods for setting up a {@link PcapHandleReader} that reads live traffic from a network interface card. + * + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } + */ +public class LiveCapture { + + // This main method is just for experimental purposes! + public static void main(String[] args) throws PcapNativeException, NotOpenException, InterruptedException { + // ================================================ EXAMPLE USE ================================================ + final String outputPcapFile = System.getProperty("user.home") + "/temp/livecapture42.pcap"; + final PcapDumper outputter = Pcaps.openDead(DataLinkType.EN10MB, 65536).dumpOpen(outputPcapFile); + // Prompt user to select what interface we should be listening to; dump packets to a file. + PcapHandleReader reader = fromCliNicSelection( + p -> { + try { + outputter.dump(p); + } catch (NotOpenException noe) { + noe.printStackTrace(); + } + } + ); + + // Read on separate thread so that we can get a chance to terminate the reader on this thread. + Thread readerThread = new Thread(() -> { + try { + reader.readFromHandle(); + } catch (PcapNativeException e) { + e.printStackTrace(); + } catch (NotOpenException e) { + e.printStackTrace(); + } + }); + readerThread.start(); + + // Pause to let reader read some packets before we terminate it. + Thread.sleep(30_000); + + // Shutdown reader. + reader.stopReading(); + System.out.println("Waiting for " + reader.getClass().getSimpleName() + " to terminate..."); + while (!reader.hasTerminated()); + // remember to flush any buffered output + outputter.flush(); + System.out.println(reader.getClass().getSimpleName() + " terminated."); + // ============================================================================================================= + } + + /** + * Prompts the user to pick a Network Interface Card (NIC) for which live traffic is to be captured, then creates a + * {@link PcapHandleReader} that is ready to start capturing live traffic from that NIC. + * + * @param listeners One or more {@link PacketListener}s to which packets read from the NIC will be delivered. + * + * @return A {@link PcapHandleReader} that is ready to start capturing live traffic from the selected NIC or + * {@code null} if no NICs can be found. + * + * @throws PcapNativeException if an error occurs in the pcap native library. + */ + public static PcapHandleReader fromCliNicSelection(PacketListener... listeners) throws PcapNativeException { + PcapNetworkInterface networkInterface = null; + try { + networkInterface = new NifSelector().selectNetworkInterface(); + } catch (IOException ioe) { + System.err.println("No network interfaces found."); + ioe.printStackTrace(); + } + return networkInterface != null ? fromNic(networkInterface, listeners) : null; + } + + /** + * Creates a {@link PcapHandleReader} that is ready to start capturing live traffic from the provided Network + * Interface Card (NIC). + * + * @param networkInterface The target NIC. + * @param listeners One or more {@link PacketListener}s to which packets read from the NIC will be delivered. + * + * @return A {@link PcapHandleReader} that is ready to start capturing live traffic from the provided NIC. + * + * @throws PcapNativeException if an error occurs in the pcap native library. + */ + public static PcapHandleReader fromNic(PcapNetworkInterface networkInterface, PacketListener... listeners) + throws PcapNativeException { + Objects.requireNonNull(networkInterface); + int snapshotLength = 65536; // in bytes + int readTimeout = 10000; // 0 is infinite on all systems but Solaris + PcapHandle handle = networkInterface.openLive(snapshotLength, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, readTimeout); + // Supply a filter that accepts all packets (p -> true) as we want to examine all traffic. + return new PcapHandleReader(handle, p -> true, listeners); + } + +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/PcapHandleReader.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/PcapHandleReader.java index e01f712..e52ce25 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/PcapHandleReader.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/PcapHandleReader.java @@ -18,6 +18,7 @@ public class PcapHandleReader { private final PcapPacketFilter mPacketFilter; private final PcapHandle mHandle; private final PacketListener[] mPacketListeners; + private volatile boolean mTerminated = false; /** * Create a {@code PcapHandleReader}. @@ -40,14 +41,28 @@ public class PcapHandleReader { * Start reading (and filtering) packets from the provided {@link PcapHandle}. * @throws PcapNativeException if an error occurs in the pcap native library. * @throws NotOpenException if the provided {@code PcapHandle} is not open. - * @throws TimeoutException if packets are being read from a live capture and the timeout expired. */ - public void readFromHandle() throws PcapNativeException, NotOpenException, TimeoutException { + public void readFromHandle() throws PcapNativeException, NotOpenException { int outOfOrderPackets = 0; try { PcapPacket prevPacket = null; - PcapPacket packet; - while ((packet = mHandle.getNextPacketEx()) != null) { + PcapPacket packet = null; + + while (!mTerminated) { + try { + packet = mHandle.getNextPacketEx(); + } catch (TimeoutException te) { + System.err.println("timeout occurred while reading from network interface"); + // No need to check termination flag here. Can defer it to the loop condition as it is the next + // instruction anyway. + continue; + } + + if (packet == null) { + System.err.println("null-packet read from handle"); + continue; + } + if (prevPacket != null && packet.getTimestamp().isBefore(prevPacket.getTimestamp())) { outOfOrderPackets++; /* @@ -76,4 +91,26 @@ public class PcapHandleReader { mHandle.close(); } + /** + * Stop reading from the wrapped {@link PcapHandle}. Note that this call only initiates the shutdown by + * setting a termination flag. Shutdown will be deferred until the time at which this flag can be checked by + * {@link #readFromHandle()}. For example, if {@link #readFromHandle()} is currently in the middle of a blocking + * call to {@link PcapHandle#getNextPacketEx()}, shutdown will not occur until the next packet is returned from the + * wrapped {@link PcapHandle} or its read timeout expires. Use {@link #hasTerminated()} to check if the shutdown + * has completed. + */ + public void stopReading() { + mTerminated = true; + } + + /** + * Checks if this {@link PcapHandleReader} has gracefully terminated, i.e., that the wrapped {@link PcapHandle} has + * been closed. + * + * @return {@code true} if this {@link PcapHandleReader} has terminated, {@code false} otherwise. + */ + public boolean hasTerminated() { + return mTerminated && !mHandle.isOpen(); + } + } -- 2.34.1