Some work-in-progress code for extracting trigger traffic
authorJanus Varmarken <varmarken@gmail.com>
Thu, 19 Jul 2018 04:09:18 +0000 (21:09 -0700)
committerJanus Varmarken <varmarken@gmail.com>
Thu, 19 Jul 2018 04:09:18 +0000 (21:09 -0700)
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapHandleReader.java [new file with mode: 0644]
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TriggerTrafficExtractor.java [new file with mode: 0644]

diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapHandleReader.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapHandleReader.java
new file mode 100644 (file)
index 0000000..a0d28d2
--- /dev/null
@@ -0,0 +1,58 @@
+package edu.uci.iotproject.analysis;
+
+import org.pcap4j.core.*;
+
+import java.io.EOFException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Reads packets from a {@link PcapHandle} (online or offline) and delivers those packets that pass the test exercised
+ * by the provided {@link PcapPacketFilter} onto the provided {@link PacketListener}s.
+ *
+ * @author Janus Varmarken
+ */
+public class PcapHandleReader {
+
+    private final PcapPacketFilter mPacketFilter;
+    private final PcapHandle mHandle;
+    private final PacketListener[] mPacketListeners;
+
+    public PcapHandleReader(PcapHandle handle, PcapPacketFilter packetFilter, PacketListener... packetListeners) {
+        mHandle = handle;
+        mPacketFilter = packetFilter;
+        mPacketListeners = packetListeners;
+    }
+
+
+    /**
+     * 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 {
+        try {
+            PcapPacket prevPacket = null;
+            PcapPacket packet;
+            while ((packet = mHandle.getNextPacketEx()) != null) {
+                if (prevPacket != null && packet.getTimestamp().isBefore(prevPacket.getTimestamp())) {
+                    // Fail early if assumption doesn't hold.
+                    mHandle.close();
+                    throw new AssertionError("Packets not in ascending temporal order");
+                }
+                if (mPacketFilter.shouldIncludePacket(packet)) {
+                    // Packet accepted for inclusion; deliver it to observing client code.
+                    for (PacketListener consumer : mPacketListeners) {
+                        consumer.gotPacket(packet);
+                    }
+                }
+                prevPacket = packet;
+            }
+        } catch (EOFException eof) {
+            // Reached end of file. All good.
+            System.out.println(String.format("%s: finished reading pcap file", getClass().getSimpleName()));
+        }
+        mHandle.close();
+    }
+
+}
diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TriggerTrafficExtractor.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TriggerTrafficExtractor.java
new file mode 100644 (file)
index 0000000..aa01f97
--- /dev/null
@@ -0,0 +1,169 @@
+package edu.uci.iotproject.analysis;
+
+import org.pcap4j.core.*;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * TODO add class documentation.
+ *
+ * @author Janus Varmarken
+ */
+public class TriggerTrafficExtractor implements PcapPacketFilter {
+
+    private final String mPcapFilePath;
+    private final List<Instant> mTriggerTimes;
+    private final String mDeviceIp;
+
+    private int mTriggerIndex = 0;
+
+
+    private static final int INCLUSION_WINDOW_MILLIS = 3_000;
+
+    public TriggerTrafficExtractor(String pcapFilePath, List<Instant> triggerTimes, String deviceIp) throws PcapNativeException, NotOpenException {
+        mPcapFilePath = pcapFilePath;
+        // Ensure that trigger times are sorted in ascending as we rely on this fact in the logic that works out if a
+        // packet is related to a trigger.
+        Collections.sort(triggerTimes, (i1, i2) -> {
+            if (i1.isBefore(i2)) return -1;
+            else if (i2.isBefore(i1)) return 1;
+            else return 0;
+        });
+        mTriggerTimes = Collections.unmodifiableList(triggerTimes);
+        mDeviceIp = deviceIp;
+    }
+
+
+    public void performExtraction(PacketListener... extractedPacketsConsumers) throws PcapNativeException, NotOpenException, TimeoutException {
+        PcapHandle handle;
+        try {
+            handle = Pcaps.openOffline(mPcapFilePath, PcapHandle.TimestampPrecision.NANO);
+        } catch (PcapNativeException pne) {
+            handle = Pcaps.openOffline(mPcapFilePath);
+        }
+        // Use the native support for BPF to immediately filter irrelevant traffic.
+        handle.setFilter("ip host " + mDeviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
+        PcapHandleReader pcapReader = new PcapHandleReader(handle, this, extractedPacketsConsumers);
+        pcapReader.readFromHandle();
+        // Reset trigger index (in case client code chooses to rerun the extraction)
+        mTriggerIndex = 0;
+    }
+
+//    public TriggerTrafficExtractor(String deviceIp, PcapHandle handle, List<Instant> triggerTimes) throws PcapNativeException, NotOpenException {
+//        mDeviceIp = deviceIp;
+//        mHandle = handle;
+//        mHandle.setFilter("ip host " + deviceIp, BpfProgram.BpfCompileMode.OPTIMIZE);
+//        // Sort in ascending order.
+//        Collections.sort(triggerTimes, (i1, i2) -> {
+//            if (i1.isBefore(i2)) return -1;
+//            else if (i2.isBefore(i1)) return 1;
+//            else return 0;
+//        });
+//        mTriggerTimes = Collections.unmodifiableList(triggerTimes);
+//    }
+
+
+
+    //    private void process() {
+//        try {
+//            PcapPacket prevPacket = null;
+//            PcapPacket packet;
+//            while ((packet = mHandle.getNextPacketEx()) != null) {
+//                if (prevPacket != null && packet.getTimestamp().isBefore(prevPacket.getTimestamp())) {
+//                    // Fail early if assumption doesn't hold.
+//                    throw new RuntimeException("Packets not in ascending temporal order");
+//                }
+//                if (shouldIncludePacket(packet)) {
+//                    // TODO output packet
+//                }
+//                prevPacket = packet;
+//            }
+//        } catch (PcapNativeException | TimeoutException | NotOpenException e) {
+//            e.printStackTrace();
+//        } catch (EOFException e) {
+//            System.out.println("Reached end of pcap file");
+//        }
+//    }
+
+    @Override
+    public boolean shouldIncludePacket(PcapPacket packet) {
+        // TODO hmm, is this correct?
+        Instant trigger = mTriggerTimes.get(mTriggerIndex);
+        if (trigger.isBefore(packet.getTimestamp()) &&
+                packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
+            // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
+            return true;
+        } else {
+            if (!trigger.isBefore(packet.getTimestamp())) {
+                // Packet is before currently considered trigger, so it shouldn't be included
+                return false;
+            } else {
+                // Packet is >= INCLUSION_WINDOW_MILLIS after currently considered trigger.
+                // Proceed to next trigger to see if it lies in range of that.
+                // Note that there's an assumption here that no two trigger intervals don't overlap!
+                mTriggerIndex++;
+                return shouldIncludePacket(packet);
+            }
+        }
+
+
+
+
+
+
+        /*
+        if (mTriggerIndex >= mTriggerTimes.size()) {
+            // Don't include packet if we've exhausted the list of trigger timestamps.
+            return false;
+        }
+        Instant trigger = mTriggerTimes.get(mTriggerIndex);
+        if (trigger.isBefore(packet.getTimestamp()) &&
+                packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS))) {
+            // Packet lies within INCLUSION_WINDOW_MILLIS after currently considered trigger, include it.
+            return true;
+        }
+        if (mTriggerIndex >= mTriggerTimes.size()-1) {
+            // No additional triggers left to be considered.
+            return false;
+        }
+        trigger = mTriggerTimes.get(mTriggerIndex + 1);
+        if (packet.getTimestamp().isBefore(trigger)) {
+            return false;
+        } else {
+            mTriggerIndex++;
+            return includePacket(packet);
+        }
+        */
+
+
+//        else if (trigger.isBefore(packet.getTimestamp()) &&
+//                !packet.getTimestamp().isBefore(trigger.plusMillis(INCLUSION_WINDOW_MILLIS)) {
+//
+//        }
+    }
+
+
+    private Instant findTriggerTime(PcapPacket packet) {
+        mTriggerTimes.stream().filter(i ->
+                i.isBefore(packet.getTimestamp()) && packet.getTimestamp().isBefore(i.plusMillis(3000)));
+
+        while (mTriggerIndex < mTriggerTimes.size() &&
+                !(mTriggerTimes.get(mTriggerIndex).isBefore(packet.getTimestamp()) &&
+                        packet.getTimestamp().isBefore(mTriggerTimes.get(mTriggerIndex).plusMillis(3_000)))
+                ) {
+            mTriggerIndex++;
+        }
+        return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;
+        /*
+        // Trigger time must logically be BEFORE packet timestamp, so advance
+        while (mTriggerIndex < mTriggerTimes.size() &&
+                mTriggerTimes.get(mTriggerIndex).isAfter(packet.getTimestamp())) {
+            mTriggerIndex++;
+        }
+        return mTriggerIndex < mTriggerTimes.size() ? mTriggerTimes.get(mTriggerIndex) : null;
+        */
+    }
+}