Add Apache Commons Math as dependency and implement first version of PcapPacketPairWr...
authorJanus Varmarken <varmarken@gmail.com>
Mon, 17 Sep 2018 18:00:41 +0000 (11:00 -0700)
committerJanus Varmarken <varmarken@gmail.com>
Mon, 17 Sep 2018 18:16:52 +0000 (11:16 -0700)
Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml
Code/Projects/SmartPlugDetector/build.gradle
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapPacketPair.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/clustering/PcapPacketPairWrapper.java [new file with mode: 0644]

index b332c40ce1731f704f4b9b00fa9c7c545f54745a..8d4ae84e7bb669546c9cd219c3748bd37f5964ba 100644 (file)
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="module" module-name="SmartPlugDetector_main" />
-    <orderEntry type="library" name="Gradle: org.pcap4j:pcap4j-core:2.0.0-alpha" level="project" />
     <orderEntry type="library" name="Gradle: org.pcap4j:pcap4j-packetfactory-static:2.0.0-alpha" level="project" />
+    <orderEntry type="library" name="Gradle: org.pcap4j:pcap4j-core:2.0.0-alpha" level="project" />
     <orderEntry type="library" name="Gradle: org.slf4j:slf4j-jdk14:1.8.0-beta2" level="project" />
+    <orderEntry type="library" name="Gradle: org.apache.commons:commons-math3:3.6.1" level="project" />
     <orderEntry type="library" name="Gradle: junit:junit:4.11" level="project" />
+    <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.8.0-beta2" level="project" />
     <orderEntry type="library" name="Gradle: net.java.dev.jna:jna:4.2.1" level="project" />
     <orderEntry type="library" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
-    <orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.8.0-beta2" level="project" />
   </component>
   <component name="TestModuleProperties" production-module="SmartPlugDetector_main" />
 </module>
\ No newline at end of file
index 582dede51c21bff9d053398930ecd1f0c7479905..72c6b713e36eb4f85f9d2c85110b5f4c7bb052fa 100644 (file)
@@ -23,4 +23,7 @@ dependencies {
 
     // pcap4j logging dependency
     compile 'org.slf4j:slf4j-jdk14:1.8.0-beta2'
+
+    // Apache Commons Math for clustering
+    compile 'org.apache.commons:commons-math3:3.6.1'
 }
index cec81dfce78b529f2003a87f29b92c96c65a5e0d..a0918e4db830d704a8ab2c79c41b7a7ea7babcbe 100644 (file)
@@ -1,7 +1,6 @@
 package edu.uci.iotproject.analysis;
 
 import edu.uci.iotproject.util.PcapPacketUtils;
-import org.apache.commons.math3.ml.clustering.Clusterable;
 import org.pcap4j.core.PcapPacket;
 
 import java.net.InetAddress;
@@ -9,12 +8,16 @@ import java.net.UnknownHostException;
 import java.util.Optional;
 
 /**
- * A simple wrapper for holding a pair of packets (e.g., a request and associated reply packet).
+ * <p>
+ *     A simple wrapper for holding a pair of packets (e.g., a request and associated reply packet).
+ * </p>
+ *
+ * <b>Note:</b> we use the deprecated version
  *
  * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
  * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
  */
-public class PcapPacketPair implements Clusterable {
+public class PcapPacketPair {
 
     private final PcapPacket mFirst;
 
@@ -64,8 +67,4 @@ public class PcapPacketPair implements Clusterable {
                 getSecond().map(pkt -> Integer.toString(pkt.getOriginalLength())).orElse("null"));
     }
 
-    @Override
-    public double[] getPoint() {
-        return new double[0];
-    }
 }
diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/clustering/PcapPacketPairWrapper.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/clustering/PcapPacketPairWrapper.java
new file mode 100644 (file)
index 0000000..1545905
--- /dev/null
@@ -0,0 +1,101 @@
+package edu.uci.iotproject.analysis.clustering;
+
+import edu.uci.iotproject.DnsMap;
+import edu.uci.iotproject.analysis.PcapPacketPair;
+import org.apache.commons.math3.stat.clustering.Clusterable;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static edu.uci.iotproject.util.PcapPacketUtils.getSourceIp;
+
+/**
+ * A wrapper for a {@link PcapPacketPair}, allowing it to be clustered using
+ * {@link org.apache.commons.math3.stat.clustering.DBSCANClusterer}. Specifically, this wrapper implements
+ * {@link org.apache.commons.math3.stat.clustering.Clusterable}, so that the interface of {@link PcapPacketPair}
+ * is not cluttered up by this helper method of the clustering API.
+ *
+ * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
+ * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
+ */
+public class PcapPacketPairWrapper implements Clusterable<PcapPacketPair> {
+
+    /**
+     * The wrapped {@link PcapPacketPair}.
+     */
+    private final PcapPacketPair mPktPair;
+
+    /**
+     * IP to hostname mappings.
+     * Allows for grouping packets with different source IPs that map to the same hostname into one cluster.
+     */
+    private final DnsMap mDnsMap;
+
+    public PcapPacketPairWrapper(PcapPacketPair wrappedObject, DnsMap ipHostnameMap) {
+        mPktPair = wrappedObject;
+        mDnsMap = ipHostnameMap;
+    }
+
+    @Override
+    public double distanceFrom(PcapPacketPair that) {
+        // Extract src ips of both packets of each pair.
+        String thisSrc1 = getSourceIp(mPktPair.getFirst());
+        String thisSrc2 = mPktPair.getSecond().map(pp -> getSourceIp(pp)).orElse("");
+        String thatSrc1 = getSourceIp(that.getFirst());
+        String thatSrc2 = that.getSecond().map(pp -> getSourceIp(pp)).orElse("");
+
+        // Replace IPs with hostnames if possible.
+        thisSrc1 = mapToHostname(thisSrc1);
+        thisSrc2 = mapToHostname(thisSrc2);
+        thatSrc1 = mapToHostname(thatSrc1);
+        thatSrc2 = mapToHostname(thatSrc2);
+
+        if(!thisSrc1.equals(thatSrc1) || !thisSrc2.equals(thatSrc2)) {
+            // Distance is maximal if sources differ.
+            return Double.MAX_VALUE;
+        }
+
+        // If the sources match, the distance is the Euclidean distance between each pair of packet lengths.
+        int thisLen1 = mPktPair.getFirst().getOriginalLength();
+        // TODO should discard pairs w/o second packet from clustering; replace below with getSecond().get() when done.
+        int thisLen2 = mPktPair.getSecond().map(pp -> pp.getOriginalLength()).orElse(0);
+        int thatLen1 = that.getFirst().getOriginalLength();
+        // TODO should discard pairs w/o second packet from clustering; replace below with getSecond().get() when done.
+        int thatLen2 = that.getSecond().map(pp -> pp.getOriginalLength()).orElse(0);
+        return Math.sqrt(
+                Math.pow(thisLen1 - thatLen1, 2) +
+                        Math.pow(thisLen2 - thatLen2, 2)
+        );
+    }
+
+    @Override
+    public PcapPacketPair centroidOf(Collection<PcapPacketPair> p) {
+        // No notion of centroid in DBSCAN
+        throw new UnsupportedOperationException("Not implemented; no notion of a centroid in DBSCAN.");
+    }
+
+
+    private String mapToHostname(String ip) {
+        Set<String> hostnames = mDnsMap.getHostnamesForIp(ip);
+        if (hostnames != null && hostnames.size() > 0) {
+            // append hostnames back-to-back separated by a delimiter if more than one item in set
+            // note: use sorted() to ensure that output remains consistent (as Set has no internal ordering of elements)
+            String result = hostnames.stream().sorted().collect(Collectors.joining(" "));
+            if (hostnames.size() > 1) {
+                // One IP can map to multiple hostnames, although that is rare. For now just raise a warning.
+                String warningStr = String.format(
+                        "%s.mapToHostname(): encountered an IP (%s) that maps to multiple hostnames (%s)",
+                        getClass().getSimpleName(), ip, result);
+                System.err.println(warningStr);
+            }
+            return result;
+        }
+        // If unable to map to a hostname, return ip for ease of use; caller can overwrite input value, defaulting to
+        // the original value if no mapping is found:
+        // String src = "<some-ip>";
+        // src = mapToHostname(src); // src is now either a hostname or the original ip.
+        return ip;
+    }
+
+}