From a51b6072be014639ada67bd43cf3b62ae5f9ebf3 Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Mon, 17 Sep 2018 11:00:41 -0700 Subject: [PATCH] Add Apache Commons Math as dependency and implement first version of PcapPacketPairWrapper to allow for clustering in Java --- .../.idea/modules/SmartPlugDetector_test.iml | 5 +- Code/Projects/SmartPlugDetector/build.gradle | 3 + .../iotproject/analysis/PcapPacketPair.java | 13 ++- .../clustering/PcapPacketPairWrapper.java | 101 ++++++++++++++++++ 4 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/clustering/PcapPacketPairWrapper.java diff --git a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml index b332c40..8d4ae84 100644 --- a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml +++ b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml @@ -10,13 +10,14 @@ - + + + - \ No newline at end of file diff --git a/Code/Projects/SmartPlugDetector/build.gradle b/Code/Projects/SmartPlugDetector/build.gradle index 582dede..72c6b71 100644 --- a/Code/Projects/SmartPlugDetector/build.gradle +++ b/Code/Projects/SmartPlugDetector/build.gradle @@ -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' } diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapPacketPair.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapPacketPair.java index cec81df..a0918e4 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapPacketPair.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/PcapPacketPair.java @@ -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). + *

+ * A simple wrapper for holding a pair of packets (e.g., a request and associated reply packet). + *

+ * + * Note: we use the deprecated version * * @author Janus Varmarken {@literal } * @author Rahmadi Trimananda {@literal } */ -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 index 0000000..1545905 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/clustering/PcapPacketPairWrapper.java @@ -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 } + * @author Rahmadi Trimananda {@literal } + */ +public class PcapPacketPairWrapper implements Clusterable { + + /** + * 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 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 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 = ""; + // src = mapToHostname(src); // src is now either a hostname or the original ip. + return ip; + } + +} -- 2.34.1