Support for starting a live capture.
authorJanus Varmarken <varmarken@gmail.com>
Wed, 19 Sep 2018 22:11:10 +0000 (15:11 -0700)
committerJanus Varmarken <varmarken@gmail.com>
Wed, 19 Sep 2018 22:15:58 +0000 (15:15 -0700)
Support for terminating PcapHandleReader.

Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/LiveCapture.java [new file with mode: 0644]
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/io/PcapHandleReader.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 (file)
index 0000000..2539415
--- /dev/null
@@ -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 <jvarmark@uci.edu>}
+ * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
+ */
+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);
+    }
+
+}
index e01f712546ce939e10e464c23bb1c17cc476bcd4..e52ce2539ff8b4476775fb66c2547b7c81c0bc5c 100644 (file)
@@ -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 <em>initiates</em> 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();
+    }
+
 }