From: Janus Varmarken Date: Sun, 13 Jan 2019 05:03:30 +0000 (-0800) Subject: Make original ClusterMatcher inherit from AbstractClusterMatcher. Reorganize code... X-Git-Url: http://plrg.eecs.uci.edu/git/?p=pingpong.git;a=commitdiff_plain;h=1b524a7b2071d6e660164e9f1d61cb80b70ca696;hp=a315a7187514946ab1a40b973dae2e9f6b2dfad3 Make original ClusterMatcher inherit from AbstractClusterMatcher. Reorganize code by adding subpackages for layer2 and layer3 detection. --- diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/AbstractClusterMatcher.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/AbstractClusterMatcher.java index 3815acd..bda6493 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/AbstractClusterMatcher.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/AbstractClusterMatcher.java @@ -6,26 +6,38 @@ import java.util.List; import java.util.Objects; /** - * TODO add class documentation. + * Base class for classes that search a traffic trace for sequences of packets that "belong to" a given cluster (in + * other words, classes that attempt to classify traffic as pertaining to a given cluster). * - * @author Janus Varmarken + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } */ abstract public class AbstractClusterMatcher { + /** + * The cluster that describes the sequence of packets that this {@link AbstractClusterMatcher} is trying to detect + * in the observed traffic. + */ protected final List> mCluster; - protected AbstractClusterMatcher(List> cluster) { // ===================== PRECONDITION SECTION ===================== cluster = Objects.requireNonNull(cluster, "cluster cannot be null"); if (cluster.isEmpty() || cluster.stream().anyMatch(inner -> inner.isEmpty())) { throw new IllegalArgumentException("cluster is empty (or contains an empty inner List)"); } + for (List clusterMember : cluster) { + if (clusterMember.size() != cluster.get(0).size()) { + throw new IllegalArgumentException("All sequences in cluster must contain the same number of packets"); + } + } + // ================================================================ + // Let the subclass prune the provided cluster mCluster = pruneCluster(cluster); } /** - * Allows subclasses to specify how to prune input cluster provided to the constructor. + * Allows subclasses to specify how to prune the input cluster provided to the constructor. * @param cluster The input cluster provided to the constructor. * @return The pruned cluster to use in place of the input cluster. */ diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/ClusterMatcher.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/ClusterMatcher.java deleted file mode 100644 index 1ff2c02..0000000 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/ClusterMatcher.java +++ /dev/null @@ -1,362 +0,0 @@ -package edu.uci.iotproject.detection; - -import edu.uci.iotproject.trafficreassembly.layer3.Conversation; -import edu.uci.iotproject.trafficreassembly.layer3.TcpReassembler; -import edu.uci.iotproject.analysis.TcpConversationUtils; -import edu.uci.iotproject.io.PcapHandleReader; -import edu.uci.iotproject.util.PrintUtils; -import org.pcap4j.core.*; - -import java.time.ZoneId; -import java.util.*; -import java.util.stream.Collectors; - -import static edu.uci.iotproject.util.PcapPacketUtils.*; - -/** - * Searches a traffic trace for sequences of packets "belong to" a given cluster (in other words, attempts to classify - * traffic as pertaining to a given cluster). - * - * @author Janus Varmarken {@literal } - * @author Rahmadi Trimananda {@literal } - */ -public class ClusterMatcher implements PacketListener { - - // Test client - public static void main(String[] args) throws PcapNativeException, NotOpenException { - -// String path = "/scratch/July-2018"; // Rahmadi - String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus - final String inputPcapFile = path + "/2018-07/dlink/dlink.wlan1.local.pcap"; - final String signatureFile = path + "/2018-07/dlink/offSignature1.sig"; - - List> signature = PrintUtils.deserializeClustersFromFile(signatureFile); - ClusterMatcher clusterMatcher = new ClusterMatcher(signature, null, - (sig, match) -> System.out.println( - String.format("[ !!! SIGNATURE DETECTED AT %s !!! ]", - match.get(0).getTimestamp().atZone(ZoneId.of("America/Los_Angeles"))) - ) - ); - - PcapHandle handle; - try { - handle = Pcaps.openOffline(inputPcapFile, PcapHandle.TimestampPrecision.NANO); - } catch (PcapNativeException pne) { - handle = Pcaps.openOffline(inputPcapFile); - } - PcapHandleReader reader = new PcapHandleReader(handle, p -> true, clusterMatcher); - reader.readFromHandle(); - clusterMatcher.performDetection(); - } - - /** - * The cluster that describes the sequence of packets that this {@link ClusterMatcher} is trying to detect in the - * observed traffic. - */ - private final List> mCluster; - - /** - * The ordered directions of packets in the sequences that make up {@link #mCluster}. - */ - private final Conversation.Direction[] mClusterMemberDirections; - - /** - * For reassembling the observed traffic into TCP connections. - */ - private final TcpReassembler mTcpReassembler = new TcpReassembler(); - - /** - * IP of the router's WAN port (if analyzed traffic is captured at the ISP's point of view). - */ - private final String mRouterWanIp; - - private final ClusterMatchObserver[] mObservers; - - /** - * Create a {@link ClusterMatcher}. - * @param cluster The cluster that traffic is matched against. - * @param routerWanIp The router's WAN IP if examining traffic captured at the ISP's point of view (used for - * determining the direction of packets). - * @param detectionObservers Client code that wants to get notified whenever the {@link ClusterMatcher} detects that - * (a subset of) the examined traffic is similar to the traffic that makes up - * {@code cluster}, i.e., when the examined traffic is classified as pertaining to - * {@code cluster}. - */ - public ClusterMatcher(List> cluster, String routerWanIp, ClusterMatchObserver... detectionObservers) { - // ===================== PRECONDITION SECTION ===================== - cluster = Objects.requireNonNull(cluster, "cluster cannot be null"); - if (cluster.isEmpty() || cluster.stream().anyMatch(inner -> inner.isEmpty())) { - throw new IllegalArgumentException("cluster is empty (or contains an empty inner List)"); - } - mObservers = Objects.requireNonNull(detectionObservers, "detectionObservers cannot be null"); - if (mObservers.length == 0) { - throw new IllegalArgumentException("no detectionObservers provided"); - } - // Build the cluster members' direction sequence. - // Note: assumes that the provided cluster was captured within the local network (routerWanIp is set to null). - mClusterMemberDirections = getPacketDirections(cluster.get(0), null); - /* - * Enforce restriction on cluster members: all representatives must exhibit the same direction pattern and - * contain the same number of packets. Note that this is a somewhat heavy operation, so it may be disabled later - * on in favor of performance. However, it is only run once (at instantiation), so the overhead may be warranted - * in order to ensure correctness, especially during the development/debugging phase. - */ - if (cluster.stream(). - anyMatch(inner -> !Arrays.equals(mClusterMemberDirections, getPacketDirections(inner, null)))) { - throw new IllegalArgumentException( - "cluster members must contain the same number of packets and exhibit the same packet direction " + - "pattern" - ); - } - // ================================================================ - // Prune the provided cluster. - mCluster = pruneCluster(cluster); - mRouterWanIp = routerWanIp; - } - - @Override - public void gotPacket(PcapPacket packet) { - // Present packet to TCP reassembler so that it can be mapped to a connection (if it is a TCP packet). - mTcpReassembler.gotPacket(packet); - } - - /** - * Get the cluster that describes the packet sequence that this {@link ClusterMatcher} is searching for. - * @return the cluster that describes the packet sequence that this {@link ClusterMatcher} is searching for. - */ - public List> getCluster() { - return mCluster; - } - - public void performDetection() { - /* - * Let's start out simple by building a version that only works for signatures that do not span across multiple - * TCP conversations... - */ - for (Conversation c : mTcpReassembler.getTcpConversations()) { - if (c.isTls() && c.getTlsApplicationDataPackets().isEmpty() || !c.isTls() && c.getPackets().isEmpty()) { - // Skip empty conversations. - continue; - } - for (List signatureSequence : mCluster) { - if (isTlsSequence(signatureSequence) != c.isTls()) { - // We consider it a mismatch if one is a TLS application data sequence and the other is not. - continue; - } - // Fetch set of packets to examine based on TLS or not. - List cPkts = c.isTls() ? c.getTlsApplicationDataPackets() : c.getPackets(); - /* - * Note: we embed the attempt to detect the signature sequence in a loop in order to capture those cases - * where the same signature sequence appears multiple times in one Conversation. - * - * Note: since we expect all sequences that together make up the signature to exhibit the same direction - * pattern, we can simply pass the precomputed direction array for the signature sequence so that it - * won't have to be recomputed internally in each call to findSubsequenceInSequence(). - */ - Optional> match; - while ((match = findSubsequenceInSequence(signatureSequence, cPkts, mClusterMemberDirections, null)). - isPresent()) { - List matchSeq = match.get(); - // Notify observers about the match. - Arrays.stream(mObservers).forEach(o -> o.onMatch(ClusterMatcher.this, matchSeq)); - /* - * Get the index in cPkts of the last packet in the sequence of packets that matches the searched - * signature sequence. - */ - int matchSeqEndIdx = cPkts.indexOf(matchSeq.get(matchSeq.size()-1)); - // We restart the search for the signature sequence immediately after that index, so truncate cPkts. - cPkts = cPkts.stream().skip(matchSeqEndIdx + 1).collect(Collectors.toList()); - } - } - /* - * TODO: - * if no item in cluster matches, also perform a distance-based matching to cover those cases where we did - * not manage to capture every single mutation of the sequence during training. - * - * Need to compute average/centroid of cluster to do so...? Compute within-cluster variance, then check if - * distance between input conversation and cluster average/centroid is smaller than or equal to the computed - * variance? - */ - } - } - - /** - * Checks if {@code sequence} is a sequence of TLS packets. Note: the current implementation relies on inspection - * of the port numbers when deciding between TLS vs. non-TLS. Therefore, only the first packet of {@code sequence} - * is examined as it is assumed that all packets in {@code sequence} pertain to the same {@link Conversation} and - * hence share the same set of two src/dst port numbers (albeit possibly alternating between which one is the src - * and which one is the dst, as packets in {@code sequence} may be in alternating directions). - * @param sequence The sequence of packets for which it is to be determined if it is a sequence of TLS packets or - * non-TLS packets. - * @return {@code true} if {@code sequence} is a sequence of TLS packets, {@code false} otherwise. - */ - private boolean isTlsSequence(List sequence) { - // NOTE: Assumes ALL packets in sequence pertain to the same TCP connection! - PcapPacket firstPkt = sequence.get(0); - int srcPort = getSourcePort(firstPkt); - int dstPort = getDestinationPort(firstPkt); - return TcpConversationUtils.isTlsPort(srcPort) || TcpConversationUtils.isTlsPort(dstPort); - } - - /** - * Examine if a given sequence of packets ({@code sequence}) contains a given shorter sequence of packets - * ({@code subsequence}). Note: the current implementation actually searches for a substring as it does not allow - * for interleaving packets in {@code sequence} that are not in {@code subsequence}; for example, if - * {@code subsequence} consists of packet lengths [2, 3, 5] and {@code sequence} consists of packet lengths - * [2, 3, 4, 5], the result will be that there is no match (because of the interleaving 4). If we are to allow - * interleaving packets, we need a modified version of - * this. - * - * @param subsequence The sequence to search for. - * @param sequence The sequence to search. - * @param subsequenceDirections The directions of packets in {@code subsequence} such that for all {@code i}, - * {@code subsequenceDirections[i]} is the direction of the packet returned by - * {@code subsequence.get(i)}. May be set to {@code null}, in which this call will - * internally compute the packet directions. - * @param sequenceDirections The directions of packets in {@code sequence} such that for all {@code i}, - * {@code sequenceDirections[i]} is the direction of the packet returned by - * {@code sequence.get(i)}. May be set to {@code null}, in which this call will internally - * compute the packet directions. - * - * @return An {@link Optional} containing the part of {@code sequence} that matches {@code subsequence}, or an empty - * {@link Optional} if no part of {@code sequence} matches {@code subsequence}. - */ - private Optional> findSubsequenceInSequence(List subsequence, - List sequence, - Conversation.Direction[] subsequenceDirections, - Conversation.Direction[] sequenceDirections) { - if (sequence.size() < subsequence.size()) { - // If subsequence is longer, it cannot be contained in sequence. - return Optional.empty(); - } - if (isTlsSequence(subsequence) != isTlsSequence(sequence)) { - // We consider it a mismatch if one is a TLS application data sequence and the other is not. - return Optional.empty(); - } - // If packet directions have not been precomputed by calling code, we need to construct them. - if (subsequenceDirections == null) { - subsequenceDirections = getPacketDirections(subsequence, mRouterWanIp); - } - if (sequenceDirections == null) { - sequenceDirections = getPacketDirections(sequence, mRouterWanIp); - } - int subseqIdx = 0; - int seqIdx = 0; - while (seqIdx < sequence.size()) { - PcapPacket subseqPkt = subsequence.get(subseqIdx); - PcapPacket seqPkt = sequence.get(seqIdx); - // We only have a match if packet lengths and directions match. - if (subseqPkt.getOriginalLength() == seqPkt.getOriginalLength() && - subsequenceDirections[subseqIdx] == sequenceDirections[seqIdx]) { - // A match; advance both indices to consider next packet in subsequence vs. next packet in sequence. - subseqIdx++; - seqIdx++; - if (subseqIdx == subsequence.size()) { - // We managed to match the entire subsequence in sequence. - // Return the sublist of sequence that matches subsequence. - /* - * TODO: - * ASSUMES THE BACKING LIST (i.e., 'sequence') IS _NOT_ STRUCTURALLY MODIFIED, hence may not work - * for live traces! - */ - return Optional.of(sequence.subList(seqIdx - subsequence.size(), seqIdx)); - } - } else { - // Mismatch. - if (subseqIdx > 0) { - /* - * If we managed to match parts of subsequence, we restart the search for subsequence in sequence at - * the index of sequence where the current mismatch occurred. I.e., we must reset subseqIdx, but - * leave seqIdx untouched. - */ - subseqIdx = 0; - } else { - /* - * First packet of subsequence didn't match packet at seqIdx of sequence, so we move forward in - * sequence, i.e., we continue the search for subsequence in sequence starting at index seqIdx+1 of - * sequence. - */ - seqIdx++; - } - } - } - return Optional.empty(); - } - - /** - * Given a cluster, produces a pruned version of that cluster. In the pruned version, there are no duplicate cluster - * members. Two cluster members are considered identical if their packets lengths and packet directions are - * identical. The resulting pruned cluster is unmodifiable (this applies to both the outermost list as well as the - * nested lists) in order to preserve its integrity when exposed to external code (e.g., through - * {@link #getCluster()}). - * - * @param cluster A cluster to prune. - * @return The resulting pruned cluster. - */ - private final List> pruneCluster(List> cluster) { - List> prunedCluster = new ArrayList<>(); - for (List originalClusterSeq : cluster) { - boolean alreadyPresent = false; - for (List prunedClusterSeq : prunedCluster) { - Optional> duplicate = findSubsequenceInSequence(originalClusterSeq, prunedClusterSeq, - mClusterMemberDirections, mClusterMemberDirections); - if (duplicate.isPresent()) { - alreadyPresent = true; - break; - } - } - if (!alreadyPresent) { - prunedCluster.add(Collections.unmodifiableList(originalClusterSeq)); - } - } - return Collections.unmodifiableList(prunedCluster); - } - - /** - * Given a {@code List}, generate a {@code Conversation.Direction[]} such that each entry in the - * resulting {@code Conversation.Direction[]} specifies the direction of the {@link PcapPacket} at the corresponding - * index in the input list. - * @param packets The list of packets for which to construct a corresponding array of packet directions. - * @param routerWanIp The IP of the router's WAN port. This is used for determining the direction of packets when - * the traffic is captured just outside the local network (at the ISP side of the router). Set to - * {@code null} if {@code packets} stem from traffic captured within the local network. - * @return A {@code Conversation.Direction[]} specifying the direction of the {@link PcapPacket} at the - * corresponding index in {@code packets}. - */ - private static Conversation.Direction[] getPacketDirections(List packets, String routerWanIp) { - Conversation.Direction[] directions = new Conversation.Direction[packets.size()]; - for (int i = 0; i < packets.size(); i++) { - PcapPacket pkt = packets.get(i); - if (getSourceIp(pkt).equals(getDestinationIp(pkt))) { - // Sanity check: we shouldn't be processing loopback traffic - throw new AssertionError("loopback traffic detected"); - } - if (isSrcIpLocal(pkt) || getSourceIp(pkt).equals(routerWanIp)) { - directions[i] = Conversation.Direction.CLIENT_TO_SERVER; - } else if (isDstIpLocal(pkt) || getDestinationIp(pkt).equals(routerWanIp)) { - directions[i] = Conversation.Direction.SERVER_TO_CLIENT; - } else { - throw new IllegalArgumentException("no local IP or router WAN port IP found, can't detect direction"); - } - } - return directions; - } - - /** - * Interface used by client code to register for receiving a notification whenever the {@link ClusterMatcher} - * detects traffic that is similar to the traffic that makes up the cluster returned by - * {@link ClusterMatcher#getCluster()}. - */ - interface ClusterMatchObserver { - /** - * Callback that is invoked whenever a sequence that is similar to a sequence associated with the cluster (i.e., - * a sequence is a member of the cluster) is detected in the traffic that the associated {@link ClusterMatcher} - * observes. - * @param clusterMatcher The {@link ClusterMatcher} that detected a match (classified traffic as pertaining to - * its associated cluster). - * @param match The traffic that was deemed to match the cluster associated with {@code clusterMatcher}. - */ - void onMatch(ClusterMatcher clusterMatcher, List match); - } - -} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/Layer2FlowObserver.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/Layer2FlowObserver.java deleted file mode 100644 index 26823de..0000000 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/Layer2FlowObserver.java +++ /dev/null @@ -1,15 +0,0 @@ -package edu.uci.iotproject.detection; - -import edu.uci.iotproject.trafficreassembly.layer2.Layer2Flow; -import org.pcap4j.core.PcapPacket; - -/** - * TODO add class documentation. - * - * @author Janus Varmarken - */ -public interface Layer2FlowObserver { - - void onNewPacket(Layer2Flow flow, PcapPacket newPacket); - -} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java deleted file mode 100644 index fb3e67e..0000000 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java +++ /dev/null @@ -1,662 +0,0 @@ -package edu.uci.iotproject.detection; - -import edu.uci.iotproject.analysis.TriggerTrafficExtractor; -import edu.uci.iotproject.analysis.UserAction; -import edu.uci.iotproject.io.PcapHandleReader; -import edu.uci.iotproject.util.PrintUtils; -import org.jgrapht.GraphPath; -import org.jgrapht.alg.shortestpath.DijkstraShortestPath; -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleDirectedWeightedGraph; -import org.pcap4j.core.*; - -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -/** - * Detects an event signature that spans one or multiple TCP connections. - * - * @author Janus Varmarken {@literal } - * @author Rahmadi Trimananda {@literal } - */ -public class SignatureDetector implements PacketListener, ClusterMatcher.ClusterMatchObserver { - - // Test client - public static void main(String[] args) throws PcapNativeException, NotOpenException { -// if (args.length < 3) { -// String errMsg = String.format("Usage: %s inputPcapFile onSignatureFile offSignatureFile", -// SignatureDetector.class.getSimpleName()); -// System.out.println(errMsg); -// return; -// } -// final String inputPcapFile = args[0]; -// final String onSignatureFile = args[1]; -// final String offSignatureFile = args[2]; - - String path = "/scratch/July-2018"; // Rahmadi -// String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus -// String path = "/home/jvarmark/iot_project/datasets"; // Hera (server) -// String path = "/raid/varmarken/iot_project/datasets"; // Zeus (server) - - // No activity test - //final String inputPcapFile = path + "/evaluation/no-activity/no-activity.wlan1.pcap"; - - // D-Link Siren experiment -// final String inputPcapFile = path + "/evaluation/dlink-siren/dlink-siren.data.wlan1.pcap"; -// final String inputPcapFile = path + "/evaluation/dlink-siren/dlink-siren.eth0.local.pcap"; - // D-Link Siren DEVICE signatures -// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-device.sig"; -// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-device.sig"; - // D-Link Siren PHONE signatures -// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-phone.sig"; -// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-phone.sig"; - // TODO: EXPERIMENT - November 19, 2018 - // Hue Bulb experiment -// final String inputPcapFile = path + "/2018-08/hue-bulb/hue-bulb.wlan1.local.pcap"; - // Hue Bulb PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/hue-bulb/signatures/hue-bulb-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/hue-bulb/signatures/hue-bulb-offSignature-phone-side.sig"; - - /* - // Kwikset Doorlock Sep 12 experiment -// final String inputPcapFile = path + "/evaluation/kwikset-doorlock/kwikset-doorlock.data.wlan1.pcap"; - final String inputPcapFile = path + "/evaluation/kwikset-doorlock/kwikset-doorlock.data.eth0.pcap"; -// // Kwikset Doorlock PHONE signatures - final String onSignatureFile = path + "/2018-08/kwikset-doorlock/onSignature-Kwikset-Doorlock-phone-new.sig"; - final String offSignatureFile = path + "/2018-08/kwikset-doorlock/offSignature-Kwikset-Doorlock-phone-new.sig"; - */ - - // D-Link Plug experiment - //final String inputPcapFile = path + "/evaluation/dlink/dlink-plug.data.wlan1.pcap"; -// final String inputPcapFile = path + "/evaluation/dlink/dlink-plug.data.eth0.pcap"; - - // D-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/2018-07/dlink/onSignature-DLink-Plug-device.sig"; -// final String offSignatureFile = path + "/2018-07/dlink/offSignature-DLink-Plug-device.sig"; - // D-Link Plug PHONE signatures -// final String onSignatureFile = path + "/2018-07/dlink/onSignature-DLink-Plug-phone.sig"; -// final String offSignatureFile = path + "/2018-07/dlink/offSignature-DLink-Plug-phone.sig"; - - // TODO: The following are negative tests against the PCAP file from UNSW -// final String inputPcapFile = path + "/UNSW/16-10-04.pcap"; // TODO: Seems to be broken! Zero-payload! -// final String inputPcapFile = path + "/UNSW/16-10-12.pcap"; - -// final String inputPcapFile = path + "/UNSW/16-09-28.pcap"; // TODO: Seems to be broken! Zero-payload! -// final String inputPcapFile = path + "/UNSW/16-10-02.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-03.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-04-a.pcap"; // TODO: Seems to be broken! Zero-payload! -// final String inputPcapFile = path + "/UNSW/16-10-04-b.pcap"; // TODO: Seems to be broken! Zero-payload! -// final String inputPcapFile = path + "/UNSW/16-10-07.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-08.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-09.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-10.pcap"; // TODO: Seems to be broken! -// final String inputPcapFile = path + "/UNSW/16-10-11.pcap"; // TODO: Seems to be broken! - // TODO: The following one is very long!!! - Split into smaller files! -// final String inputPcapFile = path + "/UNSW/16-10-12-a.pcap"; -// final String inputPcapFile = path + "/UNSW/16-10-12-b.pcap"; -// final String inputPcapFile = path + "/UNSW/16-10-12-c.pcap"; -// final String inputPcapFile = path + "/UNSW/16-10-12-d.pcap"; - -// final String inputPcapFile = path + "/UNSW/16-09-23.pcap"; -// final String inputPcapFile = path + "/UNSW/16-09-24.pcap"; -// final String inputPcapFile = path + "/UNSW/16-09-25.pcap"; -// final String inputPcapFile = path + "/UNSW/16-09-26.pcap"; -// final String inputPcapFile = path + "/UNSW/16-09-27.pcap"; -// final String inputPcapFile = path + "/UNSW/16-09-29.pcap"; -// final String inputPcapFile = path + "/UNSW/16-10-01.pcap"; -// final String inputPcapFile = path + "/UNSW/16-10-06.pcap"; - // Negative test: dataset from UNB -// final String inputPcapFile = path + "/evaluation/negative-datasets/UNB/Monday-WorkingHours_one-local-endpoint.pcap"; - - // TODO: The following are tests for signatures against training data - - // D-Link Plug experiment -// final String inputPcapFile = path + "/training/dlink-plug/wlan1/dlink-plug.wlan1.local.pcap"; - // D-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-offSignature-device-side.sig"; - // D-Link Plug PHONE signatures -// final String onSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-offSignature-phone-side.sig"; - - // TODO: EXPERIMENT - November 7, 2018 - // D-Link Plug experiment - //final String inputPcapFile = path + "/experimental_result/standalone/dlink-plug/wlan1/dlink-plug.wlan1.local.pcap"; - //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-plug/wlan1/dlink-plug.wlan1.detection.pcap"; - //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-plug/eth0/dlink-plug.eth0.detection.pcap"; - // D-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-offSignature-device-side.sig"; - // D-Link Plug PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-offSignature-phone-side.sig"; - - // TODO: EXPERIMENT - November 9, 2018 - // D-Link Siren experiment - //final String inputPcapFile = path + "/experimental_result/standalone/dlink-siren/wlan1/dlink-siren.wlan1.local.pcap"; - //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-siren/wlan1/dlink-siren.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/dlink-siren/eth0/dlink-siren.eth0.detection.pcap"; - // D-Link Siren DEVICE signatures - // TODO: The device signature does not have pairs---only one packet which is 216, so we don't consider this as a signature -// final String onSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-offSignature-device-side.sig"; - // D-Link Siren PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-offSignature-phone-side.sig"; -// final String onSignatureFile = path + "/training/signatures/dlink-siren/dlink-siren-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/signatures/dlink-siren/dlink-siren-offSignature-phone-side.sig"; - - // TP-Link Plug experiment -//// final String inputPcapFile = path + "/training/tplink-plug/wlan1/tplink-plug.wlan1.local.pcap"; -//// final String inputPcapFile = path + "/experimental_result/wifi-Sniffer/tests2/airtool_2019-01-04_11.08.45.AM.pcap"; -// final String inputPcapFile = path + "/experimental_result/wifi-Sniffer/tests2/command-frames-only.pcap"; -// // TP-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/training/tplink-plug/signatures/tplink-plug-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/training/tplink-plug/signatures/tplink-plug-offSignature-device-side.sig"; - // TODO: EXPERIMENT - November 8, 2018 - // TP-Link Plug experiment -// final String inputPcapFile = path + "/experimental_result/standalone/tplink-plug/wlan1/tplink-plug.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/tplink-plug/eth0/tplink-plug.eth0.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-plug/wlan1/tplink-plug.wlan1.detection.pcap"; - //final String inputPcapFile = path + "/experimental_result/smarthome/tplink-plug/eth0/tplink-plug.eth0.detection.pcap"; - // TP-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-device-side.sig"; -// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-device-side-outbound.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-device-side-outbound.sig"; - // TP-Link Plug PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-phone-side.sig"; - - // Arlo camera experiment -// final String inputPcapFile = path + "/training/arlo-camera/wlan1/arlo-camera.wlan1.local.pcap"; -//// // TP-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/training/arlo-camera/signatures/arlo-camera-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/arlo-camera/signatures/arlo-camera-offSignature-phone-side.sig"; - // TODO: EXPERIMENT - November 13, 2018 - // Arlo Camera experiment -// final String inputPcapFile = path + "/experimental_result/standalone/arlo-camera/wlan1/arlo-camera.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/arlo-camera/eth0/arlo-camera.eth0.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/arlo-camera/wlan1/arlo-camera.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/arlo-camera/eth0/arlo-camera.eth0.detection.pcap"; -// final String inputPcapFile = path + "/training/arlo-camera/eth0/arlo-camera.eth0.local.pcap"; - // Arlo Camera PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/arlo-camera/signatures/arlo-camera-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/arlo-camera/signatures/arlo-camera-offSignature-phone-side.sig"; - - // Amazon Alexa experiment -// final String inputPcapFile = path + "/training/amazon-alexa/wlan1/alexa2.wlan1.local.pcap"; -// // TP-Link Plug DEVICE signatures -// final String onSignatureFile = path + "/training/amazon-alexa/signatures/amazon-alexa-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/training/amazon-alexa/signatures/amazon-alexa-offSignature-device-side.sig"; - - // SmartThings Plug experiment -// final String inputPcapFile = path + "/training/st-plug/wlan1/st-plug.wlan1.local.pcap"; -// // SmartThings Plug DEVICE signatures -// //final String onSignatureFile = path + "/training/st-plug/signatures/st-plug-onSignature-device-side.sig"; -// //final String offSignatureFile = path + "/training/st-plug/signatures/st-plug-offSignature-device-side.sig"; -// // SmartThings Plug PHONE signatures -// final String onSignatureFile = path + "/training/st-plug/signatures/st-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/st-plug/signatures/st-plug-offSignature-phone-side.sig"; - // TODO: EXPERIMENT - November 12, 2018 - // SmartThings Plug experiment -// final String inputPcapFile = path + "/experimental_result/standalone/st-plug/wlan1/st-plug.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/st-plug/eth0/st-plug.eth0.local.pcap"; -// //final String inputPcapFile = path + "/experimental_result/smarthome/st-plug/wlan1/st-plug.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/st-plug/eth0/st-plug.eth0.detection.pcap"; -// // SmartThings Plug PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/st-plug/signatures/st-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/st-plug/signatures/st-plug-offSignature-phone-side.sig"; -// final String onSignatureFile = path + "/training/signatures/st-plug/st-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/signatures/st-plug/st-plug-offSignature-phone-side.sig"; - - // TODO: EXPERIMENT - January 9, 2018 - // Blossom Sprinkler experiment -// final String inputPcapFile = path + "/experimental_result/standalone/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/blossom-sprinkler/eth0/blossom-sprinkler.eth0.detection.pcap"; - final String inputPcapFile = path + "/experimental_result/smarthome/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.detection.pcap"; - // Blossom Sprinkler DEVICE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-device-side.sig"; - // Blossom Sprinkler PHONE signatures - final String onSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-phone-side.sig"; - final String offSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-phone-side.sig"; - - // LiFX Bulb experiment -// final String inputPcapFile = path + "/training/lifx-bulb/wlan1/lifx-bulb.wlan1.local.pcap"; -// // LiFX Bulb DEVICE signatures -// final String onSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-offSignature-device-side.sig"; - // LiFX Bulb PHONE signatures -// final String onSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-offSignature-phone-side.sig"; - - // Blossom Sprinkler experiment -// //final String inputPcapFile = path + "/training/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.local.pcap"; -// final String inputPcapFile = path + "/2018-08/blossom/blossom.wlan1.local.pcap"; -// //final String inputPcapFile = path + "/training/blossom-sprinkler/eth0/blossom-sprinkler.eth0.local.pcap"; -// // Blossom Sprinkler DEVICE signatures -// final String onSignatureFile = path + "/training/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-device-side.sig"; -// final String offSignatureFile = path + "/training/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-device-side.sig"; - - // Nest Thermostat experiment -// final String inputPcapFile = path + "/training/nest-thermostat/wlan1/nest-thermostat.wlan1.local.pcap"; -// // Nest Thermostat DEVICE signatures -//// final String onSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-onSignature-device-side.sig"; -//// final String offSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-offSignature-device-side.sig"; -// // Nest Thermostat PHONE signatures -// final String onSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-offSignature-phone-side.sig"; - // TODO: EXPERIMENT - November 15, 2018 - // Nest Thermostat experiment -// final String inputPcapFile = path + "/experimental_result/standalone/nest-thermostat/wlan1/nest-thermostat.wlan1.local.pcap"; -//// final String inputPcapFile = path + "/experimental_result/standalone/nest-thermostat/eth0/nest-thermostat.eth0.local.pcap"; -//// final String inputPcapFile = path + "/experimental_result/smarthome/nest-thermostat/wlan1/nest-thermostat.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/nest-thermostat/eth0/nest-thermostat.eth0.detection.pcap"; -//// // Nest Thermostat PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/nest-thermostat/signatures/nest-thermostat-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/nest-thermostat/signatures/nest-thermostat-offSignature-phone-side.sig"; - - /* - // Hue Bulb experiment - final String inputPcapFile = path + "/training/hue-bulb/wlan1/hue-bulb.wlan1.local.pcap"; - // Hue Bulb PHONE signatures - final String onSignatureFile = path + "/training/hue-bulb/signatures/hue-bulb-onSignature-phone-side.sig"; - final String offSignatureFile = path + "/training/hue-bulb/signatures/hue-bulb-offSignature-phone-side.sig"; - */ - - - - // TP-Link Bulb experiment -// final String inputPcapFile = path + "/training/tplink-bulb/wlan1/tplink-bulb.wlan1.local.pcap"; -// // TP-Link Bulb PHONE signatures -// final String onSignatureFile = path + "/training/tplink-bulb/signatures/tplink-bulb-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/tplink-bulb/signatures/tplink-bulb-offSignature-phone-side.sig"; - // TODO: EXPERIMENT - November 16, 2018 - // TP-Link Bulb experiment -// final String inputPcapFile = path + "/experimental_result/standalone/tplink-bulb/wlan1/tplink-bulb.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/tplink-bulb/eth0/tplink-bulb.eth0.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-bulb/wlan1/tplink-bulb.wlan1.detection.pcap"; -//// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-bulb/eth0/tplink-bulb.eth0.detection.pcap"; -// // TP-Link Bulb PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/tplink-bulb/signatures/tplink-bulb-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/tplink-bulb/signatures/tplink-bulb-offSignature-phone-side.sig"; - - /* - // WeMo Plug experiment - final String inputPcapFile = path + "/training/wemo-plug/wlan1/wemo-plug.wlan1.local.pcap"; - // WeMo Plug PHONE signatures - final String onSignatureFile = path + "/training/wemo-plug/signatures/wemo-plug-onSignature-device-side.sig"; - final String offSignatureFile = path + "/training/wemo-plug/signatures/wemo-plug-offSignature-device-side.sig"; - */ - // TODO: EXPERIMENT - November 20, 2018 - // WeMo Plug experiment -// final String inputPcapFile = path + "/experimental_result/standalone/wemo-plug/wlan1/wemo-plug.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/wemo-plug/eth0/wemo-plug.eth0.local.pcap"; - // TODO: WE HAVE 4 ADDITIONAL EVENTS (TRIGGERED MANUALLY), SO WE JUST IGNORE THEM BECAUSE THEY HAPPENED BEFORE - // TODO: THE ACTUAL TRIGGERS -// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-plug/wlan1/wemo-plug.wlan1.detection.pcap"; -//// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-plug/eth0/wemo-plug.eth0.detection.pcap"; -// // WeMo Plug PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/wemo-plug/signatures/wemo-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/wemo-plug/signatures/wemo-plug-offSignature-phone-side.sig"; - - /* - // WeMo Insight Plug experiment - final String inputPcapFile = path + "/training/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.local.pcap"; - // WeMo Insight Plug PHONE signatures - final String onSignatureFile = path + "/training/wemo-insight-plug/signatures/wemo-insight-plug-onSignature-device-side.sig"; - final String offSignatureFile = path + "/training/wemo-insight-plug/signatures/wemo-insight-plug-offSignature-device-side.sig"; - */ - // TODO: EXPERIMENT - November 21, 2018 - // WeMo Insight Plug experiment -// final String inputPcapFile = path + "/experimental_result/standalone/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.local.pcap"; -// final String inputPcapFile = path + "/experimental_result/standalone/wemo-insight-plug/eth0/wemo-insight-plug.eth0.local.pcap"; - // TODO: WE HAVE 1 ADDITIONAL EVENT (FROM WEMO PLUG) -// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-insight-plug/eth0/wemo-insight-plug.eth0.detection.pcap"; - // WeMo Insight Plug PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/wemo-insight-plug/signatures/wemo-insight-plug-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/wemo-insight-plug/signatures/wemo-insight-plug-offSignature-phone-side.sig"; - - - // Kwikset Doorlock Sep 12 experiment -// final String inputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset3.wlan1.local.pcap"; -// // Kwikset Doorlock PHONE signatures -// final String onSignatureFile = path + "/2018-08/kwikset-doorlock/onSignature-Kwikset-Doorlock-phone.sig"; -// final String offSignatureFile = path + "/2018-08/kwikset-doorlock/offSignature-Kwikset-Doorlock-phone.sig"; - // TODO: EXPERIMENT - November 10, 2018 - // Kwikset Door lock experiment -// final String inputPcapFile = path + "/experimental_result/standalone/kwikset-doorlock/wlan1/kwikset-doorlock.wlan1.local.pcap"; -// //final String inputPcapFile = path + "/experimental_result/smarthome/kwikset-doorlock/wlan1/kwikset-doorlock.wlan1.detection.pcap"; -// final String inputPcapFile = path + "/experimental_result/smarthome/kwikset-doorlock/eth0/kwikset-doorlock.eth0.detection.pcap"; -//// // Kwikset Door lock PHONE signatures -// final String onSignatureFile = path + "/experimental_result/standalone/kwikset-doorlock/signatures/kwikset-doorlock-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/experimental_result/standalone/kwikset-doorlock/signatures/kwikset-doorlock-offSignature-phone-side.sig"; -// final String onSignatureFile = path + "/training/signatures/kwikset-doorlock/kwikset-doorlock-onSignature-phone-side.sig"; -// final String offSignatureFile = path + "/training/signatures/kwikset-doorlock/kwikset-doorlock-offSignature-phone-side.sig"; - - - - // D-Link Siren experiment -// final String inputPcapFile = path + "/2018-08/dlink-siren/dlink-siren.wlan1.local.pcap"; - // D-Link Siren DEVICE signatures - //final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-device.sig"; - //final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-device.sig"; - // D-Link Siren PHONE signatures -// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-phone.sig"; -// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-phone.sig"; - - - // Output file names used (to make it easy to catch if one forgets to change them) - System.out.println("ON signature file in use is " + onSignatureFile); - System.out.println("OFF signature file in use is " + offSignatureFile); - System.out.println("PCAP file that is the target of detection is " + inputPcapFile); - - List>> onSignature = PrintUtils.deserializeSignatureFromFile(onSignatureFile); - List>> offSignature = PrintUtils.deserializeSignatureFromFile(offSignatureFile); - - // LAN - SignatureDetector onDetector = new SignatureDetector(onSignature, null); - SignatureDetector offDetector = new SignatureDetector(offSignature, null); - // WAN -// SignatureDetector onDetector = new SignatureDetector(onSignature, "128.195.205.105"); -// SignatureDetector offDetector = new SignatureDetector(offSignature, "128.195.205.105"); - - final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM). - withLocale(Locale.US).withZone(ZoneId.of("America/Los_Angeles")); - - // Outputs information about a detected event to std.out - final Consumer outputter = ua -> { - String eventDescription; - switch (ua.getType()) { - case TOGGLE_ON: - eventDescription = "ON"; - break; - case TOGGLE_OFF: - eventDescription = "OFF"; - break; - default: - throw new AssertionError("unhandled event type"); - } - //String output = String.format("[ !!! %s SIGNATURE DETECTED at %s !!! ]", - // eventDescription, dateTimeFormatter.format(ua.getTimestamp())); - String output = String.format("%s", - dateTimeFormatter.format(ua.getTimestamp())); - System.out.println(output); - }; - - // Let's create observers that construct a UserAction representing the detected event. - final List detectedEvents = new ArrayList<>(); - onDetector.addObserver((searched, match) -> { - PcapPacket firstPkt = match.get(0).get(0); - detectedEvents.add(new UserAction(UserAction.Type.TOGGLE_ON, firstPkt.getTimestamp())); - }); - offDetector.addObserver((searched, match) -> { - PcapPacket firstPkt = match.get(0).get(0); - detectedEvents.add(new UserAction(UserAction.Type.TOGGLE_OFF, firstPkt.getTimestamp())); - }); - - PcapHandle handle; - try { - handle = Pcaps.openOffline(inputPcapFile, PcapHandle.TimestampPrecision.NANO); - } catch (PcapNativeException pne) { - handle = Pcaps.openOffline(inputPcapFile); - } - PcapHandleReader reader = new PcapHandleReader(handle, p -> true, onDetector, offDetector); - reader.readFromHandle(); - - // TODO: need a better way of triggering detection than this... - onDetector.mClusterMatchers.forEach(cm -> cm.performDetection()); - offDetector.mClusterMatchers.forEach(cm -> cm.performDetection()); - - // Sort the list of detected events by timestamp to make it easier to compare it line-by-line with the trigger - // times file. - Collections.sort(detectedEvents, Comparator.comparing(UserAction::getTimestamp)); - - // Output the detected events - detectedEvents.forEach(outputter); - - System.out.println("Number of detected events of type " + UserAction.Type.TOGGLE_ON + ": " + - detectedEvents.stream().filter(ua -> ua.getType() == UserAction.Type.TOGGLE_ON).count()); - System.out.println("Number of detected events of type " + UserAction.Type.TOGGLE_OFF + ": " + - detectedEvents.stream().filter(ua -> ua.getType() == UserAction.Type.TOGGLE_OFF).count()); - - - // TODO: Temporary clean up until we clean the pipeline -// List cleanedDetectedEvents = SignatureDetector.removeDuplicates(detectedEvents); -// cleanedDetectedEvents.forEach(outputter); - } - - /** - * The signature that this {@link SignatureDetector} is searching for. - */ - private final List>> mSignature; - - /** - * The {@link ClusterMatcher}s in charge of detecting each individual sequence of packets that together make up the - * the signature. - */ - private final List mClusterMatchers; - - /** - * For each {@code i} ({@code i >= 0 && i < pendingMatches.length}), {@code pendingMatches[i]} holds the matches - * found by the {@link ClusterMatcher} at {@code mClusterMatchers.get(i)} that have yet to be "consumed", i.e., - * have yet to be included in a signature detected by this {@link SignatureDetector} (a signature can be encompassed - * of multiple packet sequences occurring shortly after one another on multiple connections). - */ - private final List>[] pendingMatches; - - /** - * Maps a {@link ClusterMatcher} to its corresponding index in {@link #pendingMatches}. - */ - private final Map mClusterMatcherIds; - - private final List mObservers = new ArrayList<>(); - - /** - * Remove duplicates in {@code List} of {@code UserAction} objects. We need to clean this up for user actions - * that appear multiple times. - * TODO: This static method is probably just for temporary and we could get rid of this after we clean up - * TODO: the pipeline - * - * @param listUserAction A {@link List} of {@code UserAction}. - * - */ - public static List removeDuplicates(List listUserAction) { - - // Iterate and check for duplicates (check timestamps) - Set epochSecondSet = new HashSet<>(); - // Create a target list for cleaned up list - List listUserActionClean = new ArrayList<>(); - for(UserAction userAction : listUserAction) { - // Don't insert if any duplicate is found - if(!epochSecondSet.contains(userAction.getTimestamp().getEpochSecond())) { - listUserActionClean.add(userAction); - epochSecondSet.add(userAction.getTimestamp().getEpochSecond()); - } - } - return listUserActionClean; - } - - public SignatureDetector(List>> searchedSignature, String routerWanIp) { - // note: doesn't protect inner lists from changes :'( - mSignature = Collections.unmodifiableList(searchedSignature); - // Generate corresponding/appropriate ClusterMatchers based on the provided signature - List clusterMatchers = new ArrayList<>(); - for (List> cluster : mSignature) { - clusterMatchers.add(new ClusterMatcher(cluster, routerWanIp, this)); - } - mClusterMatchers = Collections.unmodifiableList(clusterMatchers); - - // < exploratory > - pendingMatches = new List[mClusterMatchers.size()]; - for (int i = 0; i < pendingMatches.length; i++) { - pendingMatches[i] = new ArrayList<>(); - } - Map clusterMatcherIds = new HashMap<>(); - for (int i = 0; i < mClusterMatchers.size(); i++) { - clusterMatcherIds.put(mClusterMatchers.get(i), i); - } - mClusterMatcherIds = Collections.unmodifiableMap(clusterMatcherIds); - } - - public void addObserver(SignatureDetectionObserver observer) { - mObservers.add(observer); - } - - public boolean removeObserver(SignatureDetectionObserver observer) { - return mObservers.remove(observer); - } - - @Override - public void gotPacket(PcapPacket packet) { - // simply delegate packet reception to all ClusterMatchers. - mClusterMatchers.forEach(cm -> cm.gotPacket(packet)); - } - - @Override - public void onMatch(ClusterMatcher clusterMatcher, List match) { - // Add the match at the corresponding index - pendingMatches[mClusterMatcherIds.get(clusterMatcher)].add(match); - checkSignatureMatch(); - } - - private void checkSignatureMatch() { - // << Graph-based approach using Balint's idea. >> - // This implementation assumes that the packets in the inner lists (the sequences) are ordered by asc timestamp. - - // There cannot be a signature match until each ClusterMatcher has found a match of its respective sequence. - if (Arrays.stream(pendingMatches).noneMatch(l -> l.isEmpty())) { - // Construct the DAG - final SimpleDirectedWeightedGraph graph = - new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class); - // Add a vertex for each match found by all ClusterMatchers - // And maintain an array to keep track of what cluster matcher each vertex corresponds to - final List[] vertices = new List[pendingMatches.length]; - for (int i = 0; i < pendingMatches.length; i++) { - vertices[i] = new ArrayList<>(); - for (List sequence : pendingMatches[i]) { - Vertex v = new Vertex(sequence); - vertices[i].add(v); // retain reference for later when we are to add edges - graph.addVertex(v); // add to vertex to graph - } - } - // Add dummy source and sink vertices to facilitate search. - final Vertex source = new Vertex(null); - final Vertex sink = new Vertex(null); - graph.addVertex(source); - graph.addVertex(sink); - // The source is connected to all vertices that wrap the sequences detected by ClusterMatcher at index 0. - // Note: zero cost edges as this is just a dummy link to facilitate search from a common start node. - for (Vertex v : vertices[0]) { - DefaultWeightedEdge edge = graph.addEdge(source, v); - graph.setEdgeWeight(edge, 0.0); - } - // Similarly, all vertices that wrap the sequences detected by the last ClusterMatcher of the signature - // are connected to the sink node. - for (Vertex v : vertices[vertices.length-1]) { - DefaultWeightedEdge edge = graph.addEdge(v, sink); - graph.setEdgeWeight(edge, 0.0); - } - // Now link sequences detected by ClusterMatcher at index i to sequences detected by ClusterMatcher at index - // i+1 if they obey the timestamp constraint (i.e., that the latter is later in time than the former). - for (int i = 0; i < vertices.length; i++) { - int j = i + 1; - if (j < vertices.length) { - for (Vertex iv : vertices[i]) { - PcapPacket ivLast = iv.sequence.get(iv.sequence.size()-1); - for (Vertex jv : vertices[j]) { - PcapPacket jvFirst = jv.sequence.get(jv.sequence.size()-1); - if (ivLast.getTimestamp().isBefore(jvFirst.getTimestamp())) { - DefaultWeightedEdge edge = graph.addEdge(iv, jv); - // The weight is the duration of the i'th sequence plus the duration between the i'th - // and i+1'th sequence. - Duration d = Duration. - between(iv.sequence.get(0).getTimestamp(), jvFirst.getTimestamp()); - // Unfortunately weights are double values, so must convert from long to double. - // TODO: need nano second precision? If so, use d.toNanos(). - // TODO: risk of overflow when converting from long to double..? - graph.setEdgeWeight(edge, Long.valueOf(d.toMillis()).doubleValue()); - } - // Alternative version if we cannot assume that sequences are ordered by timestamp: -// if (iv.sequence.stream().max(Comparator.comparing(PcapPacket::getTimestamp)).get() -// .getTimestamp().isBefore(jv.sequence.stream().min( -// Comparator.comparing(PcapPacket::getTimestamp)).get().getTimestamp())) { -// -// } - } - } - } - } - // Graph construction complete, run shortest-path to find a (potential) signature match. - DijkstraShortestPath dijkstra = new DijkstraShortestPath<>(graph); - GraphPath shortestPath = dijkstra.getPath(source, sink); - if (shortestPath != null) { - // The total weight is the duration between the first packet of the first sequence and the last packet - // of the last sequence, so we simply have to compare the weight against the timeframe that we allow - // the signature to span. For now we just use the inclusion window we defined for training purposes. - // Note however, that we must convert back from double to long as the weight is stored as a double in - // JGraphT's API. - if (((long)shortestPath.getWeight()) < TriggerTrafficExtractor.INCLUSION_WINDOW_MILLIS) { - // There's a signature match! - // Extract the match from the vertices - List> signatureMatch = new ArrayList<>(); - for(Vertex v : shortestPath.getVertexList()) { - if (v == source || v == sink) { - // Skip the dummy source and sink nodes. - continue; - } - signatureMatch.add(v.sequence); - // As there is a one-to-one correspondence between vertices[] and pendingMatches[], we know that - // the sequence we've "consumed" for index i of the matched signature is also at index i in - // pendingMatches. We must remove it from pendingMatches so that we don't use it to construct - // another signature match in a later call. - pendingMatches[signatureMatch.size()-1].remove(v.sequence); - } - // Declare success: notify observers - mObservers.forEach(obs -> obs.onSignatureDetected(mSignature, - Collections.unmodifiableList(signatureMatch))); - } - } - } - } - - /** - * Used for registering for notifications of signatures detected by a {@link SignatureDetector}. - */ - interface SignatureDetectionObserver { - - /** - * Invoked when the {@link SignatureDetector} detects the presence of a signature in the traffic that it's - * examining. - * @param searchedSignature The signature that the {@link SignatureDetector} reporting the match is searching - * for. - * @param matchingTraffic The actual traffic trace that matches the searched signature. - */ - void onSignatureDetected(List>> searchedSignature, - List> matchingTraffic); - } - - /** - * Encapsulates a {@code List} so as to allow the list to be used as a vertex in a graph while avoiding - * the expensive {@link AbstractList#equals(Object)} calls when adding vertices to the graph. - * Using this wrapper makes the incurred {@code equals(Object)} calls delegate to {@link Object#equals(Object)} - * instead of {@link AbstractList#equals(Object)}. The net effect is a faster implementation, but the graph will not - * recognize two lists that contain the same items--from a value and not reference point of view--as the same - * vertex. However, this is fine for our purposes -- in fact restricting it to reference equality seems more - * appropriate. - */ - private static class Vertex { - private final List sequence; - private Vertex(List wrappedSequence) { - sequence = wrappedSequence; - } - } -} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2ClusterMatcher.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2ClusterMatcher.java index d50a517..b3a88b0 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2ClusterMatcher.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2ClusterMatcher.java @@ -4,7 +4,7 @@ import edu.uci.iotproject.trafficreassembly.layer2.Layer2FlowReassembler; import edu.uci.iotproject.trafficreassembly.layer2.Layer2Flow; import edu.uci.iotproject.trafficreassembly.layer2.Layer2FlowReassemblerObserver; import edu.uci.iotproject.detection.AbstractClusterMatcher; -import edu.uci.iotproject.detection.Layer2FlowObserver; +import edu.uci.iotproject.trafficreassembly.layer2.Layer2FlowObserver; import edu.uci.iotproject.io.PcapHandleReader; import edu.uci.iotproject.util.PrintUtils; import org.pcap4j.core.*; diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2SequenceMatcher.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2SequenceMatcher.java index de5c14a..db7295d 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2SequenceMatcher.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer2/Layer2SequenceMatcher.java @@ -7,9 +7,11 @@ import java.util.ArrayList; import java.util.List; /** - * TODO add class documentation. + * Attempts to detect the presence of a specific packet sequence in the set of packets provided through multiple calls + * to {@link #matchPacket(PcapPacket)}, considering only layer 2 information. * - * @author Janus Varmarken + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } */ public class Layer2SequenceMatcher { @@ -24,10 +26,21 @@ public class Layer2SequenceMatcher { */ private final List mMatchedPackets = new ArrayList<>(); + /** + * Create a {@code Layer2SequenceMatcher}. + * @param sequence The sequence to match against (search for). + */ public Layer2SequenceMatcher(List sequence) { mSequence = sequence; } + /** + * Attempt to advance this {@code Layer2SequenceMatcher} by matching {@code packet} against the packet that this + * {@code Layer2SequenceMatcher} expects as the next packet of the sequence it is searching for. + * @param packet + * @return {@code true} if this {@code Layer2SequenceMatcher} could advance by adding {@code packet} to its set of + * matched packets, {@code false} otherwise. + */ public boolean matchPacket(PcapPacket packet) { // The packet we want to match next. PcapPacket expected = mSequence.get(mMatchedPackets.size()); diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/Layer3ClusterMatcher.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/Layer3ClusterMatcher.java new file mode 100644 index 0000000..b5e3077 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/Layer3ClusterMatcher.java @@ -0,0 +1,353 @@ +package edu.uci.iotproject.detection.layer3; + +import edu.uci.iotproject.detection.AbstractClusterMatcher; +import edu.uci.iotproject.trafficreassembly.layer3.Conversation; +import edu.uci.iotproject.trafficreassembly.layer3.TcpReassembler; +import edu.uci.iotproject.analysis.TcpConversationUtils; +import edu.uci.iotproject.io.PcapHandleReader; +import edu.uci.iotproject.util.PrintUtils; +import org.pcap4j.core.*; + +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +import static edu.uci.iotproject.util.PcapPacketUtils.*; + +/** + * Searches a traffic trace for sequences of packets "belong to" a given cluster (in other words, attempts to classify + * traffic as pertaining to a given cluster). + * + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } + */ +public class Layer3ClusterMatcher extends AbstractClusterMatcher implements PacketListener { + + // Test client + public static void main(String[] args) throws PcapNativeException, NotOpenException { + +// String path = "/scratch/July-2018"; // Rahmadi + String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus + final String inputPcapFile = path + "/2018-07/dlink/dlink.wlan1.local.pcap"; + final String signatureFile = path + "/2018-07/dlink/offSignature1.sig"; + + List> signature = PrintUtils.deserializeClustersFromFile(signatureFile); + Layer3ClusterMatcher clusterMatcher = new Layer3ClusterMatcher(signature, null, + (sig, match) -> System.out.println( + String.format("[ !!! SIGNATURE DETECTED AT %s !!! ]", + match.get(0).getTimestamp().atZone(ZoneId.of("America/Los_Angeles"))) + ) + ); + + PcapHandle handle; + try { + handle = Pcaps.openOffline(inputPcapFile, PcapHandle.TimestampPrecision.NANO); + } catch (PcapNativeException pne) { + handle = Pcaps.openOffline(inputPcapFile); + } + PcapHandleReader reader = new PcapHandleReader(handle, p -> true, clusterMatcher); + reader.readFromHandle(); + clusterMatcher.performDetection(); + } + + /** + * The ordered directions of packets in the sequences that make up {@link #mCluster}. + */ + private final Conversation.Direction[] mClusterMemberDirections; + + /** + * For reassembling the observed traffic into TCP connections. + */ + private final TcpReassembler mTcpReassembler = new TcpReassembler(); + + /** + * IP of the router's WAN port (if analyzed traffic is captured at the ISP's point of view). + */ + private final String mRouterWanIp; + + private final ClusterMatchObserver[] mObservers; + + /** + * Create a {@link Layer3ClusterMatcher}. + * @param cluster The cluster that traffic is matched against. + * @param routerWanIp The router's WAN IP if examining traffic captured at the ISP's point of view (used for + * determining the direction of packets). + * @param detectionObservers Client code that wants to get notified whenever the {@link Layer3ClusterMatcher} detects that + * (a subset of) the examined traffic is similar to the traffic that makes up + * {@code cluster}, i.e., when the examined traffic is classified as pertaining to + * {@code cluster}. + */ + public Layer3ClusterMatcher(List> cluster, String routerWanIp, ClusterMatchObserver... detectionObservers) { + super(cluster); + // ===================== PRECONDITION SECTION ===================== + mObservers = Objects.requireNonNull(detectionObservers, "detectionObservers cannot be null"); + if (mObservers.length == 0) { + throw new IllegalArgumentException("no detectionObservers provided"); + } + // Build the cluster members' direction sequence. + // Note: assumes that the provided cluster was captured within the local network (routerWanIp is set to null). + mClusterMemberDirections = getPacketDirections(cluster.get(0), null); + /* + * Enforce restriction on cluster members: all representatives must exhibit the same direction pattern and + * contain the same number of packets. Note that this is a somewhat heavy operation, so it may be disabled later + * on in favor of performance. However, it is only run once (at instantiation), so the overhead may be warranted + * in order to ensure correctness, especially during the development/debugging phase. + */ + if (mCluster.stream(). + anyMatch(inner -> !Arrays.equals(mClusterMemberDirections, getPacketDirections(inner, null)))) { + throw new IllegalArgumentException( + "cluster members must contain the same number of packets and exhibit the same packet direction " + + "pattern" + ); + } + // ================================================================ + mRouterWanIp = routerWanIp; + } + + @Override + public void gotPacket(PcapPacket packet) { + // Present packet to TCP reassembler so that it can be mapped to a connection (if it is a TCP packet). + mTcpReassembler.gotPacket(packet); + } + + /** + * Get the cluster that describes the packet sequence that this {@link Layer3ClusterMatcher} is searching for. + * @return the cluster that describes the packet sequence that this {@link Layer3ClusterMatcher} is searching for. + */ + public List> getCluster() { + return mCluster; + } + + public void performDetection() { + /* + * Let's start out simple by building a version that only works for signatures that do not span across multiple + * TCP conversations... + */ + for (Conversation c : mTcpReassembler.getTcpConversations()) { + if (c.isTls() && c.getTlsApplicationDataPackets().isEmpty() || !c.isTls() && c.getPackets().isEmpty()) { + // Skip empty conversations. + continue; + } + for (List signatureSequence : mCluster) { + if (isTlsSequence(signatureSequence) != c.isTls()) { + // We consider it a mismatch if one is a TLS application data sequence and the other is not. + continue; + } + // Fetch set of packets to examine based on TLS or not. + List cPkts = c.isTls() ? c.getTlsApplicationDataPackets() : c.getPackets(); + /* + * Note: we embed the attempt to detect the signature sequence in a loop in order to capture those cases + * where the same signature sequence appears multiple times in one Conversation. + * + * Note: since we expect all sequences that together make up the signature to exhibit the same direction + * pattern, we can simply pass the precomputed direction array for the signature sequence so that it + * won't have to be recomputed internally in each call to findSubsequenceInSequence(). + */ + Optional> match; + while ((match = findSubsequenceInSequence(signatureSequence, cPkts, mClusterMemberDirections, null)). + isPresent()) { + List matchSeq = match.get(); + // Notify observers about the match. + Arrays.stream(mObservers).forEach(o -> o.onMatch(Layer3ClusterMatcher.this, matchSeq)); + /* + * Get the index in cPkts of the last packet in the sequence of packets that matches the searched + * signature sequence. + */ + int matchSeqEndIdx = cPkts.indexOf(matchSeq.get(matchSeq.size()-1)); + // We restart the search for the signature sequence immediately after that index, so truncate cPkts. + cPkts = cPkts.stream().skip(matchSeqEndIdx + 1).collect(Collectors.toList()); + } + } + /* + * TODO: + * if no item in cluster matches, also perform a distance-based matching to cover those cases where we did + * not manage to capture every single mutation of the sequence during training. + * + * Need to compute average/centroid of cluster to do so...? Compute within-cluster variance, then check if + * distance between input conversation and cluster average/centroid is smaller than or equal to the computed + * variance? + */ + } + } + + /** + * Checks if {@code sequence} is a sequence of TLS packets. Note: the current implementation relies on inspection + * of the port numbers when deciding between TLS vs. non-TLS. Therefore, only the first packet of {@code sequence} + * is examined as it is assumed that all packets in {@code sequence} pertain to the same {@link Conversation} and + * hence share the same set of two src/dst port numbers (albeit possibly alternating between which one is the src + * and which one is the dst, as packets in {@code sequence} may be in alternating directions). + * @param sequence The sequence of packets for which it is to be determined if it is a sequence of TLS packets or + * non-TLS packets. + * @return {@code true} if {@code sequence} is a sequence of TLS packets, {@code false} otherwise. + */ + private boolean isTlsSequence(List sequence) { + // NOTE: Assumes ALL packets in sequence pertain to the same TCP connection! + PcapPacket firstPkt = sequence.get(0); + int srcPort = getSourcePort(firstPkt); + int dstPort = getDestinationPort(firstPkt); + return TcpConversationUtils.isTlsPort(srcPort) || TcpConversationUtils.isTlsPort(dstPort); + } + + /** + * Examine if a given sequence of packets ({@code sequence}) contains a given shorter sequence of packets + * ({@code subsequence}). Note: the current implementation actually searches for a substring as it does not allow + * for interleaving packets in {@code sequence} that are not in {@code subsequence}; for example, if + * {@code subsequence} consists of packet lengths [2, 3, 5] and {@code sequence} consists of packet lengths + * [2, 3, 4, 5], the result will be that there is no match (because of the interleaving 4). If we are to allow + * interleaving packets, we need a modified version of + * this. + * + * @param subsequence The sequence to search for. + * @param sequence The sequence to search. + * @param subsequenceDirections The directions of packets in {@code subsequence} such that for all {@code i}, + * {@code subsequenceDirections[i]} is the direction of the packet returned by + * {@code subsequence.get(i)}. May be set to {@code null}, in which this call will + * internally compute the packet directions. + * @param sequenceDirections The directions of packets in {@code sequence} such that for all {@code i}, + * {@code sequenceDirections[i]} is the direction of the packet returned by + * {@code sequence.get(i)}. May be set to {@code null}, in which this call will internally + * compute the packet directions. + * + * @return An {@link Optional} containing the part of {@code sequence} that matches {@code subsequence}, or an empty + * {@link Optional} if no part of {@code sequence} matches {@code subsequence}. + */ + private Optional> findSubsequenceInSequence(List subsequence, + List sequence, + Conversation.Direction[] subsequenceDirections, + Conversation.Direction[] sequenceDirections) { + if (sequence.size() < subsequence.size()) { + // If subsequence is longer, it cannot be contained in sequence. + return Optional.empty(); + } + if (isTlsSequence(subsequence) != isTlsSequence(sequence)) { + // We consider it a mismatch if one is a TLS application data sequence and the other is not. + return Optional.empty(); + } + // If packet directions have not been precomputed by calling code, we need to construct them. + if (subsequenceDirections == null) { + subsequenceDirections = getPacketDirections(subsequence, mRouterWanIp); + } + if (sequenceDirections == null) { + sequenceDirections = getPacketDirections(sequence, mRouterWanIp); + } + int subseqIdx = 0; + int seqIdx = 0; + while (seqIdx < sequence.size()) { + PcapPacket subseqPkt = subsequence.get(subseqIdx); + PcapPacket seqPkt = sequence.get(seqIdx); + // We only have a match if packet lengths and directions match. + if (subseqPkt.getOriginalLength() == seqPkt.getOriginalLength() && + subsequenceDirections[subseqIdx] == sequenceDirections[seqIdx]) { + // A match; advance both indices to consider next packet in subsequence vs. next packet in sequence. + subseqIdx++; + seqIdx++; + if (subseqIdx == subsequence.size()) { + // We managed to match the entire subsequence in sequence. + // Return the sublist of sequence that matches subsequence. + /* + * TODO: + * ASSUMES THE BACKING LIST (i.e., 'sequence') IS _NOT_ STRUCTURALLY MODIFIED, hence may not work + * for live traces! + */ + return Optional.of(sequence.subList(seqIdx - subsequence.size(), seqIdx)); + } + } else { + // Mismatch. + if (subseqIdx > 0) { + /* + * If we managed to match parts of subsequence, we restart the search for subsequence in sequence at + * the index of sequence where the current mismatch occurred. I.e., we must reset subseqIdx, but + * leave seqIdx untouched. + */ + subseqIdx = 0; + } else { + /* + * First packet of subsequence didn't match packet at seqIdx of sequence, so we move forward in + * sequence, i.e., we continue the search for subsequence in sequence starting at index seqIdx+1 of + * sequence. + */ + seqIdx++; + } + } + } + return Optional.empty(); + } + + /** + * Given a cluster, produces a pruned version of that cluster. In the pruned version, there are no duplicate cluster + * members. Two cluster members are considered identical if their packets lengths and packet directions are + * identical. The resulting pruned cluster is unmodifiable (this applies to both the outermost list as well as the + * nested lists) in order to preserve its integrity when exposed to external code (e.g., through + * {@link #getCluster()}). + * + * @param cluster A cluster to prune. + * @return The resulting pruned cluster. + */ + @Override + protected List> pruneCluster(List> cluster) { + List> prunedCluster = new ArrayList<>(); + for (List originalClusterSeq : cluster) { + boolean alreadyPresent = false; + for (List prunedClusterSeq : prunedCluster) { + Optional> duplicate = findSubsequenceInSequence(originalClusterSeq, prunedClusterSeq, + mClusterMemberDirections, mClusterMemberDirections); + if (duplicate.isPresent()) { + alreadyPresent = true; + break; + } + } + if (!alreadyPresent) { + prunedCluster.add(Collections.unmodifiableList(originalClusterSeq)); + } + } + return Collections.unmodifiableList(prunedCluster); + } + + /** + * Given a {@code List}, generate a {@code Conversation.Direction[]} such that each entry in the + * resulting {@code Conversation.Direction[]} specifies the direction of the {@link PcapPacket} at the corresponding + * index in the input list. + * @param packets The list of packets for which to construct a corresponding array of packet directions. + * @param routerWanIp The IP of the router's WAN port. This is used for determining the direction of packets when + * the traffic is captured just outside the local network (at the ISP side of the router). Set to + * {@code null} if {@code packets} stem from traffic captured within the local network. + * @return A {@code Conversation.Direction[]} specifying the direction of the {@link PcapPacket} at the + * corresponding index in {@code packets}. + */ + private static Conversation.Direction[] getPacketDirections(List packets, String routerWanIp) { + Conversation.Direction[] directions = new Conversation.Direction[packets.size()]; + for (int i = 0; i < packets.size(); i++) { + PcapPacket pkt = packets.get(i); + if (getSourceIp(pkt).equals(getDestinationIp(pkt))) { + // Sanity check: we shouldn't be processing loopback traffic + throw new AssertionError("loopback traffic detected"); + } + if (isSrcIpLocal(pkt) || getSourceIp(pkt).equals(routerWanIp)) { + directions[i] = Conversation.Direction.CLIENT_TO_SERVER; + } else if (isDstIpLocal(pkt) || getDestinationIp(pkt).equals(routerWanIp)) { + directions[i] = Conversation.Direction.SERVER_TO_CLIENT; + } else { + throw new IllegalArgumentException("no local IP or router WAN port IP found, can't detect direction"); + } + } + return directions; + } + + /** + * Interface used by client code to register for receiving a notification whenever the {@link Layer3ClusterMatcher} + * detects traffic that is similar to the traffic that makes up the cluster returned by + * {@link Layer3ClusterMatcher#getCluster()}. + */ + interface ClusterMatchObserver { + /** + * Callback that is invoked whenever a sequence that is similar to a sequence associated with the cluster (i.e., + * a sequence is a member of the cluster) is detected in the traffic that the associated {@link Layer3ClusterMatcher} + * observes. + * @param clusterMatcher The {@link Layer3ClusterMatcher} that detected a match (classified traffic as pertaining to + * its associated cluster). + * @param match The traffic that was deemed to match the cluster associated with {@code clusterMatcher}. + */ + void onMatch(Layer3ClusterMatcher clusterMatcher, List match); + } + +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/SignatureDetector.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/SignatureDetector.java new file mode 100644 index 0000000..ad47106 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/layer3/SignatureDetector.java @@ -0,0 +1,660 @@ +package edu.uci.iotproject.detection.layer3; + +import edu.uci.iotproject.analysis.TriggerTrafficExtractor; +import edu.uci.iotproject.analysis.UserAction; +import edu.uci.iotproject.io.PcapHandleReader; +import edu.uci.iotproject.util.PrintUtils; +import org.jgrapht.GraphPath; +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleDirectedWeightedGraph; +import org.pcap4j.core.*; + +import java.time.Duration; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.*; +import java.util.function.Consumer; + +/** + * Detects an event signature that spans one or multiple TCP connections. + * + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } + */ +public class SignatureDetector implements PacketListener, Layer3ClusterMatcher.ClusterMatchObserver { + + // Test client + public static void main(String[] args) throws PcapNativeException, NotOpenException { +// if (args.length < 3) { +// String errMsg = String.format("Usage: %s inputPcapFile onSignatureFile offSignatureFile", +// SignatureDetector.class.getSimpleName()); +// System.out.println(errMsg); +// return; +// } +// final String inputPcapFile = args[0]; +// final String onSignatureFile = args[1]; +// final String offSignatureFile = args[2]; + + String path = "/scratch/July-2018"; // Rahmadi +// String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus +// String path = "/home/jvarmark/iot_project/datasets"; // Hera (server) +// String path = "/raid/varmarken/iot_project/datasets"; // Zeus (server) + + // No activity test + //final String inputPcapFile = path + "/evaluation/no-activity/no-activity.wlan1.pcap"; + + // D-Link Siren experiment +// final String inputPcapFile = path + "/evaluation/dlink-siren/dlink-siren.data.wlan1.pcap"; +// final String inputPcapFile = path + "/evaluation/dlink-siren/dlink-siren.eth0.local.pcap"; + // D-Link Siren DEVICE signatures +// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-device.sig"; +// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-device.sig"; + // D-Link Siren PHONE signatures +// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-phone.sig"; +// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-phone.sig"; + // TODO: EXPERIMENT - November 19, 2018 + // Hue Bulb experiment +// final String inputPcapFile = path + "/2018-08/hue-bulb/hue-bulb.wlan1.local.pcap"; + // Hue Bulb PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/hue-bulb/signatures/hue-bulb-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/hue-bulb/signatures/hue-bulb-offSignature-phone-side.sig"; + + /* + // Kwikset Doorlock Sep 12 experiment +// final String inputPcapFile = path + "/evaluation/kwikset-doorlock/kwikset-doorlock.data.wlan1.pcap"; + final String inputPcapFile = path + "/evaluation/kwikset-doorlock/kwikset-doorlock.data.eth0.pcap"; +// // Kwikset Doorlock PHONE signatures + final String onSignatureFile = path + "/2018-08/kwikset-doorlock/onSignature-Kwikset-Doorlock-phone-new.sig"; + final String offSignatureFile = path + "/2018-08/kwikset-doorlock/offSignature-Kwikset-Doorlock-phone-new.sig"; + */ + + // D-Link Plug experiment + //final String inputPcapFile = path + "/evaluation/dlink/dlink-plug.data.wlan1.pcap"; +// final String inputPcapFile = path + "/evaluation/dlink/dlink-plug.data.eth0.pcap"; + + // D-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/2018-07/dlink/onSignature-DLink-Plug-device.sig"; +// final String offSignatureFile = path + "/2018-07/dlink/offSignature-DLink-Plug-device.sig"; + // D-Link Plug PHONE signatures +// final String onSignatureFile = path + "/2018-07/dlink/onSignature-DLink-Plug-phone.sig"; +// final String offSignatureFile = path + "/2018-07/dlink/offSignature-DLink-Plug-phone.sig"; + + // TODO: The following are negative tests against the PCAP file from UNSW +// final String inputPcapFile = path + "/UNSW/16-10-04.pcap"; // TODO: Seems to be broken! Zero-payload! +// final String inputPcapFile = path + "/UNSW/16-10-12.pcap"; + +// final String inputPcapFile = path + "/UNSW/16-09-28.pcap"; // TODO: Seems to be broken! Zero-payload! +// final String inputPcapFile = path + "/UNSW/16-10-02.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-03.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-04-a.pcap"; // TODO: Seems to be broken! Zero-payload! +// final String inputPcapFile = path + "/UNSW/16-10-04-b.pcap"; // TODO: Seems to be broken! Zero-payload! +// final String inputPcapFile = path + "/UNSW/16-10-07.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-08.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-09.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-10.pcap"; // TODO: Seems to be broken! +// final String inputPcapFile = path + "/UNSW/16-10-11.pcap"; // TODO: Seems to be broken! + // TODO: The following one is very long!!! - Split into smaller files! +// final String inputPcapFile = path + "/UNSW/16-10-12-a.pcap"; +// final String inputPcapFile = path + "/UNSW/16-10-12-b.pcap"; +// final String inputPcapFile = path + "/UNSW/16-10-12-c.pcap"; +// final String inputPcapFile = path + "/UNSW/16-10-12-d.pcap"; + +// final String inputPcapFile = path + "/UNSW/16-09-23.pcap"; +// final String inputPcapFile = path + "/UNSW/16-09-24.pcap"; +// final String inputPcapFile = path + "/UNSW/16-09-25.pcap"; +// final String inputPcapFile = path + "/UNSW/16-09-26.pcap"; +// final String inputPcapFile = path + "/UNSW/16-09-27.pcap"; +// final String inputPcapFile = path + "/UNSW/16-09-29.pcap"; +// final String inputPcapFile = path + "/UNSW/16-10-01.pcap"; +// final String inputPcapFile = path + "/UNSW/16-10-06.pcap"; + // Negative test: dataset from UNB +// final String inputPcapFile = path + "/evaluation/negative-datasets/UNB/Monday-WorkingHours_one-local-endpoint.pcap"; + + // TODO: The following are tests for signatures against training data + + // D-Link Plug experiment +// final String inputPcapFile = path + "/training/dlink-plug/wlan1/dlink-plug.wlan1.local.pcap"; + // D-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-offSignature-device-side.sig"; + // D-Link Plug PHONE signatures +// final String onSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/dlink-plug/signatures/dlink-plug-offSignature-phone-side.sig"; + + // TODO: EXPERIMENT - November 7, 2018 + // D-Link Plug experiment + //final String inputPcapFile = path + "/experimental_result/standalone/dlink-plug/wlan1/dlink-plug.wlan1.local.pcap"; + //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-plug/wlan1/dlink-plug.wlan1.detection.pcap"; + //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-plug/eth0/dlink-plug.eth0.detection.pcap"; + // D-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-offSignature-device-side.sig"; + // D-Link Plug PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/dlink-plug/signatures/dlink-plug-offSignature-phone-side.sig"; + + // TODO: EXPERIMENT - November 9, 2018 + // D-Link Siren experiment + //final String inputPcapFile = path + "/experimental_result/standalone/dlink-siren/wlan1/dlink-siren.wlan1.local.pcap"; + //final String inputPcapFile = path + "/experimental_result/smarthome/dlink-siren/wlan1/dlink-siren.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/dlink-siren/eth0/dlink-siren.eth0.detection.pcap"; + // D-Link Siren DEVICE signatures + // TODO: The device signature does not have pairs---only one packet which is 216, so we don't consider this as a signature +// final String onSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-offSignature-device-side.sig"; + // D-Link Siren PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/dlink-siren/signatures/dlink-siren-offSignature-phone-side.sig"; +// final String onSignatureFile = path + "/training/signatures/dlink-siren/dlink-siren-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/signatures/dlink-siren/dlink-siren-offSignature-phone-side.sig"; + + // TP-Link Plug experiment +//// final String inputPcapFile = path + "/training/tplink-plug/wlan1/tplink-plug.wlan1.local.pcap"; +//// final String inputPcapFile = path + "/experimental_result/wifi-Sniffer/tests2/airtool_2019-01-04_11.08.45.AM.pcap"; +// final String inputPcapFile = path + "/experimental_result/wifi-Sniffer/tests2/command-frames-only.pcap"; +// // TP-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/training/tplink-plug/signatures/tplink-plug-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/training/tplink-plug/signatures/tplink-plug-offSignature-device-side.sig"; + // TODO: EXPERIMENT - November 8, 2018 + // TP-Link Plug experiment +// final String inputPcapFile = path + "/experimental_result/standalone/tplink-plug/wlan1/tplink-plug.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/tplink-plug/eth0/tplink-plug.eth0.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-plug/wlan1/tplink-plug.wlan1.detection.pcap"; + //final String inputPcapFile = path + "/experimental_result/smarthome/tplink-plug/eth0/tplink-plug.eth0.detection.pcap"; + // TP-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-device-side.sig"; +// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-device-side-outbound.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-device-side-outbound.sig"; + // TP-Link Plug PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/tplink-plug/signatures/tplink-plug-offSignature-phone-side.sig"; + + // Arlo camera experiment +// final String inputPcapFile = path + "/training/arlo-camera/wlan1/arlo-camera.wlan1.local.pcap"; +//// // TP-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/training/arlo-camera/signatures/arlo-camera-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/arlo-camera/signatures/arlo-camera-offSignature-phone-side.sig"; + // TODO: EXPERIMENT - November 13, 2018 + // Arlo Camera experiment +// final String inputPcapFile = path + "/experimental_result/standalone/arlo-camera/wlan1/arlo-camera.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/arlo-camera/eth0/arlo-camera.eth0.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/arlo-camera/wlan1/arlo-camera.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/arlo-camera/eth0/arlo-camera.eth0.detection.pcap"; +// final String inputPcapFile = path + "/training/arlo-camera/eth0/arlo-camera.eth0.local.pcap"; + // Arlo Camera PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/arlo-camera/signatures/arlo-camera-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/arlo-camera/signatures/arlo-camera-offSignature-phone-side.sig"; + + // Amazon Alexa experiment +// final String inputPcapFile = path + "/training/amazon-alexa/wlan1/alexa2.wlan1.local.pcap"; +// // TP-Link Plug DEVICE signatures +// final String onSignatureFile = path + "/training/amazon-alexa/signatures/amazon-alexa-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/training/amazon-alexa/signatures/amazon-alexa-offSignature-device-side.sig"; + + // SmartThings Plug experiment +// final String inputPcapFile = path + "/training/st-plug/wlan1/st-plug.wlan1.local.pcap"; +// // SmartThings Plug DEVICE signatures +// //final String onSignatureFile = path + "/training/st-plug/signatures/st-plug-onSignature-device-side.sig"; +// //final String offSignatureFile = path + "/training/st-plug/signatures/st-plug-offSignature-device-side.sig"; +// // SmartThings Plug PHONE signatures +// final String onSignatureFile = path + "/training/st-plug/signatures/st-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/st-plug/signatures/st-plug-offSignature-phone-side.sig"; + // TODO: EXPERIMENT - November 12, 2018 + // SmartThings Plug experiment +// final String inputPcapFile = path + "/experimental_result/standalone/st-plug/wlan1/st-plug.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/st-plug/eth0/st-plug.eth0.local.pcap"; +// //final String inputPcapFile = path + "/experimental_result/smarthome/st-plug/wlan1/st-plug.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/st-plug/eth0/st-plug.eth0.detection.pcap"; +// // SmartThings Plug PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/st-plug/signatures/st-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/st-plug/signatures/st-plug-offSignature-phone-side.sig"; +// final String onSignatureFile = path + "/training/signatures/st-plug/st-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/signatures/st-plug/st-plug-offSignature-phone-side.sig"; + + // TODO: EXPERIMENT - January 9, 2018 + // Blossom Sprinkler experiment +// final String inputPcapFile = path + "/experimental_result/standalone/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/blossom-sprinkler/eth0/blossom-sprinkler.eth0.detection.pcap"; + final String inputPcapFile = path + "/experimental_result/smarthome/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.detection.pcap"; + // Blossom Sprinkler DEVICE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-device-side.sig"; + // Blossom Sprinkler PHONE signatures + final String onSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-phone-side.sig"; + final String offSignatureFile = path + "/experimental_result/standalone/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-phone-side.sig"; + + // LiFX Bulb experiment +// final String inputPcapFile = path + "/training/lifx-bulb/wlan1/lifx-bulb.wlan1.local.pcap"; +// // LiFX Bulb DEVICE signatures +// final String onSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-offSignature-device-side.sig"; + // LiFX Bulb PHONE signatures +// final String onSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/lifx-bulb/signatures/lifx-bulb-offSignature-phone-side.sig"; + + // Blossom Sprinkler experiment +// //final String inputPcapFile = path + "/training/blossom-sprinkler/wlan1/blossom-sprinkler.wlan1.local.pcap"; +// final String inputPcapFile = path + "/2018-08/blossom/blossom.wlan1.local.pcap"; +// //final String inputPcapFile = path + "/training/blossom-sprinkler/eth0/blossom-sprinkler.eth0.local.pcap"; +// // Blossom Sprinkler DEVICE signatures +// final String onSignatureFile = path + "/training/blossom-sprinkler/signatures/blossom-sprinkler-onSignature-device-side.sig"; +// final String offSignatureFile = path + "/training/blossom-sprinkler/signatures/blossom-sprinkler-offSignature-device-side.sig"; + + // Nest Thermostat experiment +// final String inputPcapFile = path + "/training/nest-thermostat/wlan1/nest-thermostat.wlan1.local.pcap"; +// // Nest Thermostat DEVICE signatures +//// final String onSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-onSignature-device-side.sig"; +//// final String offSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-offSignature-device-side.sig"; +// // Nest Thermostat PHONE signatures +// final String onSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/nest-thermostat/signatures/nest-thermostat-offSignature-phone-side.sig"; + // TODO: EXPERIMENT - November 15, 2018 + // Nest Thermostat experiment +// final String inputPcapFile = path + "/experimental_result/standalone/nest-thermostat/wlan1/nest-thermostat.wlan1.local.pcap"; +//// final String inputPcapFile = path + "/experimental_result/standalone/nest-thermostat/eth0/nest-thermostat.eth0.local.pcap"; +//// final String inputPcapFile = path + "/experimental_result/smarthome/nest-thermostat/wlan1/nest-thermostat.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/nest-thermostat/eth0/nest-thermostat.eth0.detection.pcap"; +//// // Nest Thermostat PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/nest-thermostat/signatures/nest-thermostat-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/nest-thermostat/signatures/nest-thermostat-offSignature-phone-side.sig"; + + /* + // Hue Bulb experiment + final String inputPcapFile = path + "/training/hue-bulb/wlan1/hue-bulb.wlan1.local.pcap"; + // Hue Bulb PHONE signatures + final String onSignatureFile = path + "/training/hue-bulb/signatures/hue-bulb-onSignature-phone-side.sig"; + final String offSignatureFile = path + "/training/hue-bulb/signatures/hue-bulb-offSignature-phone-side.sig"; + */ + + + + // TP-Link Bulb experiment +// final String inputPcapFile = path + "/training/tplink-bulb/wlan1/tplink-bulb.wlan1.local.pcap"; +// // TP-Link Bulb PHONE signatures +// final String onSignatureFile = path + "/training/tplink-bulb/signatures/tplink-bulb-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/tplink-bulb/signatures/tplink-bulb-offSignature-phone-side.sig"; + // TODO: EXPERIMENT - November 16, 2018 + // TP-Link Bulb experiment +// final String inputPcapFile = path + "/experimental_result/standalone/tplink-bulb/wlan1/tplink-bulb.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/tplink-bulb/eth0/tplink-bulb.eth0.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-bulb/wlan1/tplink-bulb.wlan1.detection.pcap"; +//// final String inputPcapFile = path + "/experimental_result/smarthome/tplink-bulb/eth0/tplink-bulb.eth0.detection.pcap"; +// // TP-Link Bulb PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/tplink-bulb/signatures/tplink-bulb-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/tplink-bulb/signatures/tplink-bulb-offSignature-phone-side.sig"; + + /* + // WeMo Plug experiment + final String inputPcapFile = path + "/training/wemo-plug/wlan1/wemo-plug.wlan1.local.pcap"; + // WeMo Plug PHONE signatures + final String onSignatureFile = path + "/training/wemo-plug/signatures/wemo-plug-onSignature-device-side.sig"; + final String offSignatureFile = path + "/training/wemo-plug/signatures/wemo-plug-offSignature-device-side.sig"; + */ + // TODO: EXPERIMENT - November 20, 2018 + // WeMo Plug experiment +// final String inputPcapFile = path + "/experimental_result/standalone/wemo-plug/wlan1/wemo-plug.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/wemo-plug/eth0/wemo-plug.eth0.local.pcap"; + // TODO: WE HAVE 4 ADDITIONAL EVENTS (TRIGGERED MANUALLY), SO WE JUST IGNORE THEM BECAUSE THEY HAPPENED BEFORE + // TODO: THE ACTUAL TRIGGERS +// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-plug/wlan1/wemo-plug.wlan1.detection.pcap"; +//// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-plug/eth0/wemo-plug.eth0.detection.pcap"; +// // WeMo Plug PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/wemo-plug/signatures/wemo-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/wemo-plug/signatures/wemo-plug-offSignature-phone-side.sig"; + + /* + // WeMo Insight Plug experiment + final String inputPcapFile = path + "/training/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.local.pcap"; + // WeMo Insight Plug PHONE signatures + final String onSignatureFile = path + "/training/wemo-insight-plug/signatures/wemo-insight-plug-onSignature-device-side.sig"; + final String offSignatureFile = path + "/training/wemo-insight-plug/signatures/wemo-insight-plug-offSignature-device-side.sig"; + */ + // TODO: EXPERIMENT - November 21, 2018 + // WeMo Insight Plug experiment +// final String inputPcapFile = path + "/experimental_result/standalone/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.local.pcap"; +// final String inputPcapFile = path + "/experimental_result/standalone/wemo-insight-plug/eth0/wemo-insight-plug.eth0.local.pcap"; + // TODO: WE HAVE 1 ADDITIONAL EVENT (FROM WEMO PLUG) +// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/wemo-insight-plug/eth0/wemo-insight-plug.eth0.detection.pcap"; + // WeMo Insight Plug PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/wemo-insight-plug/signatures/wemo-insight-plug-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/wemo-insight-plug/signatures/wemo-insight-plug-offSignature-phone-side.sig"; + + + // Kwikset Doorlock Sep 12 experiment +// final String inputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset3.wlan1.local.pcap"; +// // Kwikset Doorlock PHONE signatures +// final String onSignatureFile = path + "/2018-08/kwikset-doorlock/onSignature-Kwikset-Doorlock-phone.sig"; +// final String offSignatureFile = path + "/2018-08/kwikset-doorlock/offSignature-Kwikset-Doorlock-phone.sig"; + // TODO: EXPERIMENT - November 10, 2018 + // Kwikset Door lock experiment +// final String inputPcapFile = path + "/experimental_result/standalone/kwikset-doorlock/wlan1/kwikset-doorlock.wlan1.local.pcap"; +// //final String inputPcapFile = path + "/experimental_result/smarthome/kwikset-doorlock/wlan1/kwikset-doorlock.wlan1.detection.pcap"; +// final String inputPcapFile = path + "/experimental_result/smarthome/kwikset-doorlock/eth0/kwikset-doorlock.eth0.detection.pcap"; +//// // Kwikset Door lock PHONE signatures +// final String onSignatureFile = path + "/experimental_result/standalone/kwikset-doorlock/signatures/kwikset-doorlock-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/experimental_result/standalone/kwikset-doorlock/signatures/kwikset-doorlock-offSignature-phone-side.sig"; +// final String onSignatureFile = path + "/training/signatures/kwikset-doorlock/kwikset-doorlock-onSignature-phone-side.sig"; +// final String offSignatureFile = path + "/training/signatures/kwikset-doorlock/kwikset-doorlock-offSignature-phone-side.sig"; + + + + // D-Link Siren experiment +// final String inputPcapFile = path + "/2018-08/dlink-siren/dlink-siren.wlan1.local.pcap"; + // D-Link Siren DEVICE signatures + //final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-device.sig"; + //final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-device.sig"; + // D-Link Siren PHONE signatures +// final String onSignatureFile = path + "/2018-08/dlink-siren/onSignature-DLink-Siren-phone.sig"; +// final String offSignatureFile = path + "/2018-08/dlink-siren/offSignature-DLink-Siren-phone.sig"; + + + // Output file names used (to make it easy to catch if one forgets to change them) + System.out.println("ON signature file in use is " + onSignatureFile); + System.out.println("OFF signature file in use is " + offSignatureFile); + System.out.println("PCAP file that is the target of detection is " + inputPcapFile); + + List>> onSignature = PrintUtils.deserializeSignatureFromFile(onSignatureFile); + List>> offSignature = PrintUtils.deserializeSignatureFromFile(offSignatureFile); + + // LAN + SignatureDetector onDetector = new SignatureDetector(onSignature, null); + SignatureDetector offDetector = new SignatureDetector(offSignature, null); + // WAN +// SignatureDetector onDetector = new SignatureDetector(onSignature, "128.195.205.105"); +// SignatureDetector offDetector = new SignatureDetector(offSignature, "128.195.205.105"); + + final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM). + withLocale(Locale.US).withZone(ZoneId.of("America/Los_Angeles")); + + // Outputs information about a detected event to std.out + final Consumer outputter = ua -> { + String eventDescription; + switch (ua.getType()) { + case TOGGLE_ON: + eventDescription = "ON"; + break; + case TOGGLE_OFF: + eventDescription = "OFF"; + break; + default: + throw new AssertionError("unhandled event type"); + } + //String output = String.format("[ !!! %s SIGNATURE DETECTED at %s !!! ]", + // eventDescription, dateTimeFormatter.format(ua.getTimestamp())); + String output = String.format("%s", + dateTimeFormatter.format(ua.getTimestamp())); + System.out.println(output); + }; + + // Let's create observers that construct a UserAction representing the detected event. + final List detectedEvents = new ArrayList<>(); + onDetector.addObserver((searched, match) -> { + PcapPacket firstPkt = match.get(0).get(0); + detectedEvents.add(new UserAction(UserAction.Type.TOGGLE_ON, firstPkt.getTimestamp())); + }); + offDetector.addObserver((searched, match) -> { + PcapPacket firstPkt = match.get(0).get(0); + detectedEvents.add(new UserAction(UserAction.Type.TOGGLE_OFF, firstPkt.getTimestamp())); + }); + + PcapHandle handle; + try { + handle = Pcaps.openOffline(inputPcapFile, PcapHandle.TimestampPrecision.NANO); + } catch (PcapNativeException pne) { + handle = Pcaps.openOffline(inputPcapFile); + } + PcapHandleReader reader = new PcapHandleReader(handle, p -> true, onDetector, offDetector); + reader.readFromHandle(); + + // TODO: need a better way of triggering detection than this... + onDetector.mClusterMatchers.forEach(cm -> cm.performDetection()); + offDetector.mClusterMatchers.forEach(cm -> cm.performDetection()); + + // Sort the list of detected events by timestamp to make it easier to compare it line-by-line with the trigger + // times file. + Collections.sort(detectedEvents, Comparator.comparing(UserAction::getTimestamp)); + + // Output the detected events + detectedEvents.forEach(outputter); + + System.out.println("Number of detected events of type " + UserAction.Type.TOGGLE_ON + ": " + + detectedEvents.stream().filter(ua -> ua.getType() == UserAction.Type.TOGGLE_ON).count()); + System.out.println("Number of detected events of type " + UserAction.Type.TOGGLE_OFF + ": " + + detectedEvents.stream().filter(ua -> ua.getType() == UserAction.Type.TOGGLE_OFF).count()); + + + // TODO: Temporary clean up until we clean the pipeline +// List cleanedDetectedEvents = SignatureDetector.removeDuplicates(detectedEvents); +// cleanedDetectedEvents.forEach(outputter); + } + + /** + * The signature that this {@link SignatureDetector} is searching for. + */ + private final List>> mSignature; + + /** + * The {@link Layer3ClusterMatcher}s in charge of detecting each individual sequence of packets that together make up the + * the signature. + */ + private final List mClusterMatchers; + + /** + * For each {@code i} ({@code i >= 0 && i < pendingMatches.length}), {@code pendingMatches[i]} holds the matches + * found by the {@link Layer3ClusterMatcher} at {@code mClusterMatchers.get(i)} that have yet to be "consumed", i.e., + * have yet to be included in a signature detected by this {@link SignatureDetector} (a signature can be encompassed + * of multiple packet sequences occurring shortly after one another on multiple connections). + */ + private final List>[] pendingMatches; + + /** + * Maps a {@link Layer3ClusterMatcher} to its corresponding index in {@link #pendingMatches}. + */ + private final Map mClusterMatcherIds; + + private final List mObservers = new ArrayList<>(); + + /** + * Remove duplicates in {@code List} of {@code UserAction} objects. We need to clean this up for user actions + * that appear multiple times. + * TODO: This static method is probably just for temporary and we could get rid of this after we clean up + * TODO: the pipeline + * + * @param listUserAction A {@link List} of {@code UserAction}. + * + */ + public static List removeDuplicates(List listUserAction) { + + // Iterate and check for duplicates (check timestamps) + Set epochSecondSet = new HashSet<>(); + // Create a target list for cleaned up list + List listUserActionClean = new ArrayList<>(); + for(UserAction userAction : listUserAction) { + // Don't insert if any duplicate is found + if(!epochSecondSet.contains(userAction.getTimestamp().getEpochSecond())) { + listUserActionClean.add(userAction); + epochSecondSet.add(userAction.getTimestamp().getEpochSecond()); + } + } + return listUserActionClean; + } + + public SignatureDetector(List>> searchedSignature, String routerWanIp) { + // note: doesn't protect inner lists from changes :'( + mSignature = Collections.unmodifiableList(searchedSignature); + // Generate corresponding/appropriate ClusterMatchers based on the provided signature + List clusterMatchers = new ArrayList<>(); + for (List> cluster : mSignature) { + clusterMatchers.add(new Layer3ClusterMatcher(cluster, routerWanIp, this)); + } + mClusterMatchers = Collections.unmodifiableList(clusterMatchers); + + // < exploratory > + pendingMatches = new List[mClusterMatchers.size()]; + for (int i = 0; i < pendingMatches.length; i++) { + pendingMatches[i] = new ArrayList<>(); + } + Map clusterMatcherIds = new HashMap<>(); + for (int i = 0; i < mClusterMatchers.size(); i++) { + clusterMatcherIds.put(mClusterMatchers.get(i), i); + } + mClusterMatcherIds = Collections.unmodifiableMap(clusterMatcherIds); + } + + public void addObserver(SignatureDetectionObserver observer) { + mObservers.add(observer); + } + + public boolean removeObserver(SignatureDetectionObserver observer) { + return mObservers.remove(observer); + } + + @Override + public void gotPacket(PcapPacket packet) { + // simply delegate packet reception to all ClusterMatchers. + mClusterMatchers.forEach(cm -> cm.gotPacket(packet)); + } + + @Override + public void onMatch(Layer3ClusterMatcher clusterMatcher, List match) { + // Add the match at the corresponding index + pendingMatches[mClusterMatcherIds.get(clusterMatcher)].add(match); + checkSignatureMatch(); + } + + private void checkSignatureMatch() { + // << Graph-based approach using Balint's idea. >> + // This implementation assumes that the packets in the inner lists (the sequences) are ordered by asc timestamp. + + // There cannot be a signature match until each Layer3ClusterMatcher has found a match of its respective sequence. + if (Arrays.stream(pendingMatches).noneMatch(l -> l.isEmpty())) { + // Construct the DAG + final SimpleDirectedWeightedGraph graph = + new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class); + // Add a vertex for each match found by all ClusterMatchers + // And maintain an array to keep track of what cluster matcher each vertex corresponds to + final List[] vertices = new List[pendingMatches.length]; + for (int i = 0; i < pendingMatches.length; i++) { + vertices[i] = new ArrayList<>(); + for (List sequence : pendingMatches[i]) { + Vertex v = new Vertex(sequence); + vertices[i].add(v); // retain reference for later when we are to add edges + graph.addVertex(v); // add to vertex to graph + } + } + // Add dummy source and sink vertices to facilitate search. + final Vertex source = new Vertex(null); + final Vertex sink = new Vertex(null); + graph.addVertex(source); + graph.addVertex(sink); + // The source is connected to all vertices that wrap the sequences detected by Layer3ClusterMatcher at index 0. + // Note: zero cost edges as this is just a dummy link to facilitate search from a common start node. + for (Vertex v : vertices[0]) { + DefaultWeightedEdge edge = graph.addEdge(source, v); + graph.setEdgeWeight(edge, 0.0); + } + // Similarly, all vertices that wrap the sequences detected by the last Layer3ClusterMatcher of the signature + // are connected to the sink node. + for (Vertex v : vertices[vertices.length-1]) { + DefaultWeightedEdge edge = graph.addEdge(v, sink); + graph.setEdgeWeight(edge, 0.0); + } + // Now link sequences detected by Layer3ClusterMatcher at index i to sequences detected by Layer3ClusterMatcher at index + // i+1 if they obey the timestamp constraint (i.e., that the latter is later in time than the former). + for (int i = 0; i < vertices.length; i++) { + int j = i + 1; + if (j < vertices.length) { + for (Vertex iv : vertices[i]) { + PcapPacket ivLast = iv.sequence.get(iv.sequence.size()-1); + for (Vertex jv : vertices[j]) { + PcapPacket jvFirst = jv.sequence.get(jv.sequence.size()-1); + if (ivLast.getTimestamp().isBefore(jvFirst.getTimestamp())) { + DefaultWeightedEdge edge = graph.addEdge(iv, jv); + // The weight is the duration of the i'th sequence plus the duration between the i'th + // and i+1'th sequence. + Duration d = Duration. + between(iv.sequence.get(0).getTimestamp(), jvFirst.getTimestamp()); + // Unfortunately weights are double values, so must convert from long to double. + // TODO: need nano second precision? If so, use d.toNanos(). + // TODO: risk of overflow when converting from long to double..? + graph.setEdgeWeight(edge, Long.valueOf(d.toMillis()).doubleValue()); + } + // Alternative version if we cannot assume that sequences are ordered by timestamp: +// if (iv.sequence.stream().max(Comparator.comparing(PcapPacket::getTimestamp)).get() +// .getTimestamp().isBefore(jv.sequence.stream().min( +// Comparator.comparing(PcapPacket::getTimestamp)).get().getTimestamp())) { +// +// } + } + } + } + } + // Graph construction complete, run shortest-path to find a (potential) signature match. + DijkstraShortestPath dijkstra = new DijkstraShortestPath<>(graph); + GraphPath shortestPath = dijkstra.getPath(source, sink); + if (shortestPath != null) { + // The total weight is the duration between the first packet of the first sequence and the last packet + // of the last sequence, so we simply have to compare the weight against the timeframe that we allow + // the signature to span. For now we just use the inclusion window we defined for training purposes. + // Note however, that we must convert back from double to long as the weight is stored as a double in + // JGraphT's API. + if (((long)shortestPath.getWeight()) < TriggerTrafficExtractor.INCLUSION_WINDOW_MILLIS) { + // There's a signature match! + // Extract the match from the vertices + List> signatureMatch = new ArrayList<>(); + for(Vertex v : shortestPath.getVertexList()) { + if (v == source || v == sink) { + // Skip the dummy source and sink nodes. + continue; + } + signatureMatch.add(v.sequence); + // As there is a one-to-one correspondence between vertices[] and pendingMatches[], we know that + // the sequence we've "consumed" for index i of the matched signature is also at index i in + // pendingMatches. We must remove it from pendingMatches so that we don't use it to construct + // another signature match in a later call. + pendingMatches[signatureMatch.size()-1].remove(v.sequence); + } + // Declare success: notify observers + mObservers.forEach(obs -> obs.onSignatureDetected(mSignature, + Collections.unmodifiableList(signatureMatch))); + } + } + } + } + + /** + * Used for registering for notifications of signatures detected by a {@link SignatureDetector}. + */ + interface SignatureDetectionObserver { + + /** + * Invoked when the {@link SignatureDetector} detects the presence of a signature in the traffic that it's + * examining. + * @param searchedSignature The signature that the {@link SignatureDetector} reporting the match is searching + * for. + * @param matchingTraffic The actual traffic trace that matches the searched signature. + */ + void onSignatureDetected(List>> searchedSignature, + List> matchingTraffic); + } + + /** + * Encapsulates a {@code List} so as to allow the list to be used as a vertex in a graph while avoiding + * the expensive {@link AbstractList#equals(Object)} calls when adding vertices to the graph. + * Using this wrapper makes the incurred {@code equals(Object)} calls delegate to {@link Object#equals(Object)} + * instead of {@link AbstractList#equals(Object)}. The net effect is a faster implementation, but the graph will not + * recognize two lists that contain the same items--from a value and not reference point of view--as the same + * vertex. However, this is fine for our purposes -- in fact restricting it to reference equality seems more + * appropriate. + */ + private static class Vertex { + private final List sequence; + private Vertex(List wrappedSequence) { + sequence = wrappedSequence; + } + } +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2Flow.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2Flow.java index f1b7190..2d804bd 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2Flow.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2Flow.java @@ -1,6 +1,5 @@ package edu.uci.iotproject.trafficreassembly.layer2; -import edu.uci.iotproject.detection.Layer2FlowObserver; import org.pcap4j.core.PcapPacket; import org.pcap4j.packet.EthernetPacket; import org.pcap4j.util.MacAddress; @@ -10,15 +9,26 @@ import java.util.Collections; import java.util.List; /** - * The packets exchanged between two endpoints (MAC addresses). + * Models a layer 2 flow: groups packets exchanged between two specific endpoints (MAC addresses). * - * @author Janus Varmarken + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } */ public class Layer2Flow { + /** + * The first endpoint of this layer 2 flow. + */ private final MacAddress mEndpoint1; + + /** + * The second endpoint of this layer 2 flow. + */ private final MacAddress mEndpoint2; + /** + * Clients observing for changes to this layer 2 flow. + */ private final List mFlowObservers = new ArrayList<>(); public Layer2Flow(MacAddress endpoint1, MacAddress endpoint2) { @@ -26,10 +36,18 @@ public class Layer2Flow { mEndpoint2 = endpoint2; } + /** + * Register as an observer of this flow. + * @param observer The client that is to be notified whenever this flow changes (has new packets added). + */ public void addFlowObserver(Layer2FlowObserver observer) { mFlowObservers.add(observer); } + /** + * Deregister as an observer of this flow. + * @param observer The client that no longer wishes to be notified whenever this flow changes. + */ public void removeFlowObserver(Layer2FlowObserver observer) { mFlowObservers.remove(observer); } @@ -50,10 +68,18 @@ public class Layer2Flow { mFlowObservers.forEach(o -> o.onNewPacket(this, packet)); } + /** + * Get the packets pertaining to this flow. + * @return The packets pertaining to this flow. + */ public List getPackets() { return Collections.unmodifiableList(mPackets); } + /** + * Verify that a packet pertains to this flow. + * @param packet The packet that is to be verified. + */ private void verifyAddresses(PcapPacket packet) { EthernetPacket ethPkt = packet.get(EthernetPacket.class); MacAddress srcAddr = ethPkt.getHeader().getSrcAddr(); @@ -67,13 +93,3 @@ public class Layer2Flow { } } - - - -/* - - - Packet stream -> flow reassembler -> flow1, flow2, flow3... -> for each flow, keep a sequence matcher for each sequence of cluster - - - */ \ No newline at end of file diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2FlowObserver.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2FlowObserver.java new file mode 100644 index 0000000..e1648ba --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/trafficreassembly/layer2/Layer2FlowObserver.java @@ -0,0 +1,20 @@ +package edu.uci.iotproject.trafficreassembly.layer2; + +import org.pcap4j.core.PcapPacket; + +/** + * Interface for observing a {@link Layer2Flow}. + * + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } + */ +public interface Layer2FlowObserver { + + /** + * Invoked when a new packet is added to the observed flow. + * @param flow The observed flow. + * @param newPacket The packet that was added to the flow. + */ + void onNewPacket(Layer2Flow flow, PcapPacket newPacket); + +}