First (rushed) implementation of pattern seach at the MAC layer. Not pretty, but...
authorJanus Varmarken <varmarken@gmail.com>
Sun, 20 May 2018 00:39:53 +0000 (17:39 -0700)
committerJanus Varmarken <varmarken@gmail.com>
Sun, 20 May 2018 00:39:53 +0000 (17:39 -0700)
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java [new file with mode: 0644]
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java [new file with mode: 0644]

index 624136fd6972cd7d13beab8b7dd109b310acac05..9696bd6d56053e2d34a576e042a08b77a00fe2a6 100644 (file)
@@ -1,5 +1,7 @@
 package edu.uci.iotproject;
 
+import edu.uci.iotproject.maclayer.MacLayerFlowPattern;
+import edu.uci.iotproject.maclayer.MacLayerFlowPatternFinder;
 import org.pcap4j.core.*;
 
 import java.io.EOFException;
@@ -21,34 +23,49 @@ public class Main {
 
 
     public static void main(String[] args) throws PcapNativeException, NotOpenException, EOFException, TimeoutException, UnknownHostException {
-        final String fileName = args.length > 0 ? args[0] : "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.local.remote.dns.pcap";
-        final String trainingFileName = "./pcap/TP_LINK_LOCAL_ON_SUBSET.pcap";
-        //final String trainingFileName = "./pcap/TP_LINK_REMOTE_ON.pcap";
-
-        // ====== Debug code ======
+        // -------------------------------------------------------------------------------------------------------------
+        // Example/debug code for searching for a pattern at the MAC layer.
+        String fileName = "./pcap/mac-tplink.local.pcapng";
         PcapHandle handle;
-        PcapHandle trainingPcap;
         try {
             handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO);
-            trainingPcap = Pcaps.openOffline(trainingFileName, PcapHandle.TimestampPrecision.NANO);
         } catch (PcapNativeException pne) {
             handle = Pcaps.openOffline(fileName);
-            trainingPcap = Pcaps.openOffline(trainingFileName);
         }
+//        Arrays.asList(1590, 1590, 1590, 1001, 337, 197, 636, 1311, 177) // Full pattern (all non-zero payload packets).
+        MacLayerFlowPattern pattern = new MacLayerFlowPattern("TP_LINK_LOCAL_OFF_MAC", "50:c7:bf:33:1f:09", Arrays.asList(637, 1312));
+        MacLayerFlowPatternFinder finder = new MacLayerFlowPatternFinder(handle, pattern);
+        finder.findFlowPattern();
+        // -------------------------------------------------------------------------------------------------------------
 
-        // TODO: The followings are the way to extract multiple hostnames and their associated packet lengths lists
-        //List<String> list = new ArrayList<>();
-        //list.add("events.tplinkra.com");
-        //FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", list, trainingPcap);
-        //List<String> list2 = new ArrayList<>();
-        //list2.add("devs.tplinkcloud.com");
-        //list2.add("events.tplinkra.com");
-        //FlowPattern fp3 = new FlowPattern("TP_LINK_REMOTE_ON", list2, trainingPcap);
-
-        FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", "events.tplinkra.com", trainingPcap);        
-        FlowPatternFinder fpf = new FlowPatternFinder(handle, fp);
-        fpf.start();
-
-        // ========================
+//        final String fileName = args.length > 0 ? args[0] : "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.local.remote.dns.pcap";
+//        final String trainingFileName = "./pcap/TP_LINK_LOCAL_ON_SUBSET.pcap";
+//        //final String trainingFileName = "./pcap/TP_LINK_REMOTE_ON.pcap";
+//
+//        // ====== Debug code ======
+//        PcapHandle handle;
+//        PcapHandle trainingPcap;
+//        try {
+//            handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO);
+//            trainingPcap = Pcaps.openOffline(trainingFileName, PcapHandle.TimestampPrecision.NANO);
+//        } catch (PcapNativeException pne) {
+//            handle = Pcaps.openOffline(fileName);
+//            trainingPcap = Pcaps.openOffline(trainingFileName);
+//        }
+//
+//        // TODO: The followings are the way to extract multiple hostnames and their associated packet lengths lists
+//        //List<String> list = new ArrayList<>();
+//        //list.add("events.tplinkra.com");
+//        //FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", list, trainingPcap);
+//        //List<String> list2 = new ArrayList<>();
+//        //list2.add("devs.tplinkcloud.com");
+//        //list2.add("events.tplinkra.com");
+//        //FlowPattern fp3 = new FlowPattern("TP_LINK_REMOTE_ON", list2, trainingPcap);
+//
+//        FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", "events.tplinkra.com", trainingPcap);
+//        FlowPatternFinder fpf = new FlowPatternFinder(handle, fp);
+//        fpf.start();
+//
+//        // ========================
     }
 }
diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java
new file mode 100644 (file)
index 0000000..7cff733
--- /dev/null
@@ -0,0 +1,46 @@
+package edu.uci.iotproject.maclayer;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * TODO create base class for FlowPattern and derive MacLayer, TCP/IP layer versions from that.
+ *
+ * @author Janus Varmarken
+ */
+public class MacLayerFlowPattern {
+
+    private final List<Integer> mPacketLengthSequence;
+    private final String mMacPrefix;
+    private final String mPatternId;
+    private final byte[] mMacPreixBytes;
+
+    public MacLayerFlowPattern(String patternId, String macPrefix, List<Integer> packetLengthSequence) {
+        mMacPrefix = macPrefix;
+        mPatternId = patternId;
+        mPacketLengthSequence = packetLengthSequence;
+        // Conversion provided by https://stackoverflow.com/a/10839361/1214974
+        String[] addressParts = macPrefix.split(":");
+        mMacPreixBytes = new byte[addressParts.length];
+        for(int i = 0; i < mMacPreixBytes.length; i++) {
+            Integer hex  = Integer.parseInt(addressParts[i], 16);
+            mMacPreixBytes[i] = hex.byteValue();
+        }
+    }
+
+    public String getPatternId() {
+        return mPatternId;
+    }
+
+    public byte[] getMacPrefixRawBytes() {
+        return mMacPreixBytes;
+    }
+
+    public List<Integer> getPacketLengthSequence() {
+        return Collections.unmodifiableList(mPacketLengthSequence);
+    }
+
+    public int getLength() {
+        return mPacketLengthSequence.size();
+    }
+}
diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java
new file mode 100644 (file)
index 0000000..6cca3ad
--- /dev/null
@@ -0,0 +1,102 @@
+package edu.uci.iotproject.maclayer;
+
+import edu.uci.iotproject.FlowPattern;
+import org.pcap4j.core.NotOpenException;
+import org.pcap4j.core.PcapHandle;
+import org.pcap4j.core.PcapNativeException;
+import org.pcap4j.core.PcapPacket;
+import org.pcap4j.packet.RadiotapPacket;
+
+import java.io.EOFException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Performs a search for {@link FlowPattern}
+ * TODO: May want to create an abstract FlowPatternFinder and then derive MacLayer, TcpipLayer FlowPatternFinders from that one.
+ *
+ * @author Janus Varmarken
+ */
+public class MacLayerFlowPatternFinder {
+
+    private final MacLayerFlowPattern mPattern;
+    private final PcapHandle mPcap;
+
+    public MacLayerFlowPatternFinder(PcapHandle pcap, MacLayerFlowPattern pattern) {
+        this.mPcap = Objects.requireNonNull(pcap,
+                String.format("Argument of type '%s' cannot be null", PcapHandle.class.getSimpleName()));
+        this.mPattern = Objects.requireNonNull(pattern,
+                String.format("Argument of type '%s' cannot be null", FlowPattern.class.getSimpleName()));
+    }
+
+    public void findFlowPattern() {
+        PcapPacket packet;
+        try {
+            // Packets matched to flow pattern searched for.
+            List<PcapPacket> patternPackets = new ArrayList<>();
+            while ((packet = mPcap.getNextPacketEx()) != null) {
+                RadiotapPacket radiotapPacket;
+                try {
+                    // Some packets throw an IAE with message "msi must be between 0 and 6 but is actually: 7"
+                    // when accessing the RadiotapPacket.
+                    radiotapPacket = packet.get(RadiotapPacket.class);
+                } catch (IllegalArgumentException iae) {
+                    System.out.println(iae.getMessage());
+                    continue;
+                }
+                if (radiotapPacket == null) {
+                    continue;
+                }
+                // Restart search if pattern not found within reasonable time frame (hardcoded for now).
+                if (patternPackets.size() > 0 && packet.getTimestamp().getEpochSecond() -
+                            patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() > 2) {
+                    patternPackets = new ArrayList<>();
+                }
+
+                byte[] rawData = radiotapPacket.getPayload().getRawData();
+                // Search rawData for MAC of FlowPattern in sender/receiver section
+                // [TODO needs verification that this section is actually the sender/receiver section]
+                if (rawData.length < 16) {
+                    continue;
+                }
+                int prefixLength = mPattern.getMacPrefixRawBytes().length;
+                byte[] mac1 = Arrays.copyOfRange(rawData, 4, prefixLength < 6 ? 4 + prefixLength : 10);
+                byte[] mac2 = Arrays.copyOfRange(rawData, 10, prefixLength < 6 ? 10 + prefixLength : 16);
+                if (!Arrays.equals(mac1, mPattern.getMacPrefixRawBytes()) && !Arrays.equals(mac2, mPattern.getMacPrefixRawBytes())) {
+                    // MAC prefix not present in raw data.
+                    continue;
+                }
+                // Packet related to device associated with the pattern we are looking for.
+                int expectedLength = mPattern.getPacketLengthSequence().get(patternPackets.size());
+                if (packet.length() == expectedLength) {
+                    patternPackets.add(packet);
+                    if (patternPackets.size() == mPattern.getLength()) {
+                        // Full pattern found, declare success if packets are within some reasonable amount of time of
+                        // one another.
+                        // For now, we use a hardcoded value.
+                        if (patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() -
+                                patternPackets.get(0).getTimestamp().getEpochSecond() < 5) {
+                            System.out.println(String.format("[ find ] Detected a COMPLETE MATCH of pattern '%s' at %s!",
+                                    mPattern.getPatternId(), patternPackets.get(0).getTimestamp().toString()));
+                        }
+                        // Reset search by resetting list.
+                        patternPackets = new ArrayList<>();
+                    }
+                } else {
+                    // Discard packet, not relevant to pattern.
+                    continue;
+                }
+            }
+        } catch (EOFException e) {
+            // TODO wait for, and print, results.
+        } catch (PcapNativeException|TimeoutException|NotOpenException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
+
+