X-Git-Url: http://plrg.eecs.uci.edu/git/?p=pingpong.git;a=blobdiff_plain;f=Code%2FProjects%2FSmartPlugDetector%2Fsrc%2Fmain%2Fjava%2Fedu%2Fuci%2Fiotproject%2FMain.java;h=0f837b29f41ad193b827bee13768a84c610bf43b;hp=75eff77f76d9e72a24494adc4ffb8b8bd8cd04e7;hb=f314a6e02dad77f006f7acea08d3304569fc5ea9;hpb=b43149a6c6459c21ac1b5a5d243099f9d96b1a95 diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java index 75eff77..0f837b2 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java @@ -2,19 +2,26 @@ package edu.uci.iotproject; import static edu.uci.iotproject.analysis.UserAction.Type; -import edu.uci.iotproject.analysis.TcpConversationUtils; -import edu.uci.iotproject.analysis.TrafficLabeler; -import edu.uci.iotproject.analysis.TriggerTrafficExtractor; -import edu.uci.iotproject.analysis.UserAction; +import edu.uci.iotproject.analysis.*; +import edu.uci.iotproject.comparison.seqalignment.ExtractedSequence; +import edu.uci.iotproject.comparison.seqalignment.SequenceAlignment; +import edu.uci.iotproject.comparison.seqalignment.SequenceExtraction; import edu.uci.iotproject.io.TriggerTimesFileReader; +import edu.uci.iotproject.util.PrintUtils; +import org.apache.commons.math3.stat.clustering.Cluster; +import org.apache.commons.math3.stat.clustering.DBSCANClusterer; import org.pcap4j.core.*; import org.pcap4j.packet.namednumber.DataLinkType; import java.io.EOFException; +import java.io.File; +import java.io.PrintWriter; import java.net.UnknownHostException; import java.time.Instant; import java.util.*; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This is a system that reads PCAP files to compare @@ -34,14 +41,17 @@ public class Main { // ------------ # Code for extracting traffic generated by a device within x seconds of a trigger # ------------ // Paths to input and output files (consider supplying these as arguments instead) and IP of the device for // which traffic is to be extracted: - String path = "/scratch/July-2018"; // Rahmadi - //String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus +// String path = "/scratch/July-2018"; // Rahmadi + String path = "/Users/varmarken/temp/UCI IoT Project/experiments"; // Janus + boolean verbose = true; + final String onPairsPath = "/scratch/July-2018/on.txt"; + final String offPairsPath = "/scratch/July-2018/off.txt"; // 1) D-Link July 26 experiment // final String inputPcapFile = path + "/2018-07/dlink/dlink.wlan1.local.pcap"; // final String outputPcapFile = path + "/2018-07/dlink/dlink-processed.pcap"; // final String triggerTimesFile = path + "/2018-07/dlink/dlink-july-26-2018.timestamps"; -// final String deviceIp = "192.168.1.199"; // .246 == phone; .199 == dlink plug? +// final String deviceIp = "192.168.1.246"; // .246 == phone; .199 == dlink plug? // 2) TP-Link July 25 experiment // final String inputPcapFile = path + "/2018-07/tplink/tplink.wlan1.local.pcap"; @@ -79,7 +89,7 @@ public class Main { // final String inputPcapFile = path + "/2018-08/tplink-bulb/tplinkbulb.wlan1.local.pcap"; // final String outputPcapFile = path + "/2018-08/tplink-bulb/tplinkbulb-processed.pcap"; // final String triggerTimesFile = path + "/2018-08/tplink-bulb/tplink-bulb-aug-3-2018.timestamps"; -// final String deviceIp = "192.168.1.140"; +// final String deviceIp = "192.168.1.140"; // .246 == phone; .140 == TP-Link bulb // 7) Kwikset Doorlock August 6 experiment // final String inputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset-doorlock.wlan1.local.pcap"; @@ -87,6 +97,13 @@ public class Main { // final String triggerTimesFile = path + "/2018-08/kwikset-doorlock/kwikset-doorlock-aug-6-2018.timestamps"; // final String deviceIp = "192.168.1.246"; // .246 == phone; .142 == SmartThings Hub (note: use eth0 capture for this!) + // September 12, 2018 - includes both wlan1 and eth1 interfaces + //final String inputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset3.wlan1.local.pcap"; +// final String inputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset3.eth1.local.pcap"; +// final String outputPcapFile = path + "/2018-08/kwikset-doorlock/kwikset3-processed.pcap"; +// final String triggerTimesFile = path + "/2018-08/kwikset-doorlock/kwikset-doorlock-sept-12-2018.timestamps"; +// final String deviceIp = "192.168.1.142"; // .246 == phone; .142 == SmartThings Hub (note: use eth0 capture for this!) + // 8) Hue Bulb August 7 experiment // final String inputPcapFile = path + "/2018-08/hue-bulb/hue-bulb.wlan1.local.pcap"; // final String outputPcapFile = path + "/2018-08/hue-bulb/hue-bulb-processed.pcap"; @@ -106,22 +123,22 @@ public class Main { // final String deviceIp = "192.168.1.246"; // .246 == phone; .235 == camera // 11) Arlo Camera August 10 experiment - final String inputPcapFile = path + "/2018-08/arlo-camera/arlo-camera.wlan1.local.pcap"; - final String outputPcapFile = path + "/2018-08/arlo-camera/arlo-camera-processed.pcap"; - final String triggerTimesFile = path + "/2018-08/arlo-camera/arlo-camera-aug-10-2018.timestamps"; - final String deviceIp = "192.168.1.140"; // .246 == phone; .140 == camera +// final String inputPcapFile = path + "/2018-08/arlo-camera/arlo-camera.wlan1.local.pcap"; +// final String outputPcapFile = path + "/2018-08/arlo-camera/arlo-camera-processed.pcap"; +// final String triggerTimesFile = path + "/2018-08/arlo-camera/arlo-camera-aug-10-2018.timestamps"; +// final String deviceIp = "192.168.1.140"; // .246 == phone; .140 == camera // 12) Blossom sprinkler August 13 experiment // final String inputPcapFile = path + "/2018-08/blossom/blossom.wlan1.local.pcap"; // final String outputPcapFile = path + "/2018-08/blossom/blossom-processed.pcap"; // final String triggerTimesFile = path + "/2018-08/blossom/blossom-aug-13-2018.timestamps"; -// final String deviceIp = "192.168.1.229"; // .246 == phone; .229 == sprinkler +// final String deviceIp = "192.168.1.246"; // .246 == phone; .229 == sprinkler - // 13) DLink siren August 14 experiment -// final String inputPcapFile = path + "/2018-08/dlink-siren/dlink-siren.wlan1.local.pcap"; -// final String outputPcapFile = path + "/2018-08/dlink-siren/dlink-siren-processed.pcap"; -// final String triggerTimesFile = path + "/2018-08/dlink-siren/dlink-siren-aug-14-2018.timestamps"; -// final String deviceIp = "192.168.1.183"; // .246 == phone; .183 == siren +// // 13) DLink siren August 14 experiment + final String inputPcapFile = path + "/2018-08/dlink-siren/dlink-siren.wlan1.local.pcap"; + final String outputPcapFile = path + "/2018-08/dlink-siren/dlink-siren-processed.pcap"; + final String triggerTimesFile = path + "/2018-08/dlink-siren/dlink-siren-aug-14-2018.timestamps"; + final String deviceIp = "192.168.1.183"; // .246 == phone; .183 == siren // 14) Nest thermostat August 15 experiment // final String inputPcapFile = path + "/2018-08/nest/nest.wlan1.local.pcap"; @@ -140,6 +157,12 @@ public class Main { // final String triggerTimesFile = path + "/2018-08/alexa/alexa-aug-17-2018.timestamps"; // final String deviceIp = "192.168.1.225"; // .246 == phone; .225 == Alexa + // September 17 +// final String inputPcapFile = path + "/2018-08/noise/noise.eth1.pcap"; +// final String outputPcapFile = path + "/2018-08/noise/noise-processed.pcap"; +// final String triggerTimesFile = path + "/2018-08/noise/noise-sept-17-2018.timestamps"; +// final String deviceIp = "192.168.1.142"; // .142 == SmartThings Hub; .199 == dlink plug; .183 == siren + TriggerTimesFileReader ttfr = new TriggerTimesFileReader(); List triggerTimes = ttfr.readTriggerTimes(triggerTimesFile, false); // Tag each trigger with "ON" or "OFF", assuming that the first trigger is an "ON" and that they alternate. @@ -193,6 +216,12 @@ public class Main { + /* + * NOTE: no need to generate these more complex on/off maps that also contain mappings from hostname and + * sequence identifiers as we do not care about hostnames and sequences during clustering. + * We can simply use the UserAction->List map to generate ON/OFF groupings of conversations. + */ + /* // Contains all ON events: hostname -> sequence identifier -> list of conversations with that sequence Map>> ons = new HashMap<>(); // Contains all OFF events: hostname -> sequence identifier -> list of conversations with that sequence @@ -201,7 +230,7 @@ public class Main { Map>> outer = ua.getType() == Type.TOGGLE_ON ? ons : offs; hostnameToConvs.forEach((host, convs) -> { Map> seqsToConvs = TcpConversationUtils. - groupConversationsByPacketSequence(convs); + groupConversationsByPacketSequence(convs, verbose); outer.merge(host, seqsToConvs, (oldMap, newMap) -> { newMap.forEach((sequence, cs) -> oldMap.merge(sequence, cs, (list1, list2) -> { list1.addAll(list2); @@ -211,11 +240,183 @@ public class Main { }); }); }); - - System.out.println(""); - - // ------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------- + */ + + // ================================================ CLUSTERING ================================================ + // Note: no need to use the more convoluted on/off maps; can simply use the UserAction->List map + // when don't care about hostnames and sequences (see comment earlier). + List onConversations = userActionToConversations.entrySet().stream(). + filter(e -> e.getKey().getType() == Type.TOGGLE_ON). // drop all OFF events from stream + map(e -> e.getValue()). // no longer interested in the UserActions + flatMap(List::stream). // flatten List> to a List + collect(Collectors.toList()); + List offConversations = userActionToConversations.entrySet().stream(). + filter(e -> e.getKey().getType() == Type.TOGGLE_OFF). + map(e -> e.getValue()). + flatMap(List::stream). + collect(Collectors.toList()); + List onPairs = onConversations.stream(). + map(c -> c.isTls() ? TcpConversationUtils.extractTlsAppDataPacketPairs(c) : + TcpConversationUtils.extractPacketPairs(c)). + flatMap(List::stream). // flatten List> to List<> + collect(Collectors.toList()); + List offPairs = offConversations.stream(). + map(c -> c.isTls() ? TcpConversationUtils.extractTlsAppDataPacketPairs(c) : + TcpConversationUtils.extractPacketPairs(c)). + flatMap(List::stream). // flatten List> to List<> + collect(Collectors.toList()); + // Note: need to update the DnsMap of all PcapPacketPairs if we want to use the IP/hostname-sensitive distance. + Stream.concat(Stream.of(onPairs), Stream.of(offPairs)).flatMap(List::stream).forEach(p -> p.setDnsMap(dnsMap)); + // Perform clustering on conversation logged as part of all ON events. + DBSCANClusterer onClusterer = new DBSCANClusterer<>(10.0, 5); + List> onClusters = onClusterer.cluster(onPairs); + // Perform clustering on conversation logged as part of all OFF events. + DBSCANClusterer offClusterer = new DBSCANClusterer<>(10.0, 5); + List> offClusters = offClusterer.cluster(offPairs); + // Output clusters + System.out.println("========================================"); + System.out.println(" Clustering results for ON "); + System.out.println(" Number of clusters: " + onClusters.size()); + int count = 0; + for (Cluster c : onClusters) { + System.out.println(String.format("<<< Cluster #%02d (%03d points) >>>", ++count, c.getPoints().size())); + System.out.print(PrintUtils.toSummaryString(c)); + } + System.out.println("========================================"); + System.out.println(" Clustering results for OFF "); + System.out.println(" Number of clusters: " + offClusters.size()); + count = 0; + for (Cluster c : offClusters) { + System.out.println(String.format("<<< Cluster #%03d (%06d points) >>>", ++count, c.getPoints().size())); + System.out.print(PrintUtils.toSummaryString(c)); + } + System.out.println("========================================"); + // ============================================================================================================ + + /* + System.out.println("==== ON ===="); + // Print out all the pairs into a file for ON events + File fileOnEvents = new File(onPairsPath); + PrintWriter pwOn = null; + try { + pwOn = new PrintWriter(fileOnEvents); + } catch(Exception ex) { + ex.printStackTrace(); + } + for(Map.Entry>> entry : ons.entrySet()) { + Map> seqsToConvs = entry.getValue(); + for(Map.Entry> entryConv : seqsToConvs.entrySet()) { + List listConv = entryConv.getValue(); + // Just get the first Conversation because all Conversations in this group + // should have the same pairs of Application Data. + for(Conversation conv : listConv) { + // Process only if it is a TLS packet + if (conv.isTls()) { + List tlsAppDataList = TcpConversationUtils.extractTlsAppDataPacketPairs(conv); + for(PcapPacketPair pair: tlsAppDataList) { + System.out.println(PrintUtils.toCsv(pair, dnsMap)); + pwOn.println(PrintUtils.toCsv(pair, dnsMap)); + } + } else { // Non-TLS conversations + List packetList = TcpConversationUtils.extractPacketPairs(conv); + for(PcapPacketPair pair: packetList) { + System.out.println(PrintUtils.toCsv(pair, dnsMap)); + pwOn.println(PrintUtils.toCsv(pair, dnsMap)); + } + } + } + } + } + pwOn.close(); + + System.out.println("==== OFF ===="); + // Print out all the pairs into a file for ON events + File fileOffEvents = new File(offPairsPath); + PrintWriter pwOff = null; + try { + pwOff = new PrintWriter(fileOffEvents); + } catch(Exception ex) { + ex.printStackTrace(); + } + for(Map.Entry>> entry : offs.entrySet()) { + Map> seqsToConvs = entry.getValue(); + for(Map.Entry> entryConv : seqsToConvs.entrySet()) { + List listConv = entryConv.getValue(); + // Just get the first Conversation because all Conversations in this group + // should have the same pairs of Application Data. + for(Conversation conv : listConv) { + // Process only if it is a TLS packet + if (conv.isTls()) { + List tlsAppDataList = TcpConversationUtils.extractTlsAppDataPacketPairs(conv); + for(PcapPacketPair pair: tlsAppDataList) { + System.out.println(PrintUtils.toCsv(pair, dnsMap)); + pwOff.println(PrintUtils.toCsv(pair, dnsMap)); + } + } else { // Non-TLS conversations + List packetList = TcpConversationUtils.extractPacketPairs(conv); + for (PcapPacketPair pair : packetList) { + System.out.println(PrintUtils.toCsv(pair, dnsMap)); + pwOff.println(PrintUtils.toCsv(pair, dnsMap)); + } + } + } + } + } + pwOff.close(); + */ + +// // ================================================================================================ +// // <<< Some work-in-progress/explorative code that extracts a "representative" sequence >>> +// // +// // Currently need to know relevant hostname in advance :( +// String hostname = "events.tplinkra.com"; +//// String hostname = "rfe-us-west-1.dch.dlink.com"; +// // Conversations with 'hostname' for ON events. +// List onsForHostname = new ArrayList<>(); +// // Conversations with 'hostname' for OFF events. +// List offsForHostname = new ArrayList<>(); +// // "Unwrap" sequence groupings in ons/offs maps. +// ons.get(hostname).forEach((k,v) -> onsForHostname.addAll(v)); +// offs.get(hostname).forEach((k,v) -> offsForHostname.addAll(v)); +// +// +// Map> onsForHostnameGroupedByTlsAppDataSequence = TcpConversationUtils.groupConversationsByTlsApplicationDataPacketSequence(onsForHostname); +// +// +// // Extract representative sequence for ON and OFF by providing the list of conversations with +// // 'hostname' observed for each event type (the training data). +// SequenceExtraction seqExtraction = new SequenceExtraction(); +//// ExtractedSequence extractedSequenceForOn = seqExtraction.extract(onsForHostname); +//// ExtractedSequence extractedSequenceForOff = seqExtraction.extract(offsForHostname); +// +// ExtractedSequence extractedSequenceForOn = seqExtraction.extractByTlsAppData(onsForHostname); +// ExtractedSequence extractedSequenceForOff = seqExtraction.extractByTlsAppData(offsForHostname); +// +// // Let's check how many ONs align with OFFs and vice versa (that is, how many times an event is incorrectly +// // labeled). +// int onsLabeledAsOff = 0; +// Integer[] representativeOnSeq = TcpConversationUtils.getPacketLengthSequence(extractedSequenceForOn.getRepresentativeSequence()); +// Integer[] representativeOffSeq = TcpConversationUtils.getPacketLengthSequence(extractedSequenceForOff.getRepresentativeSequence()); +// SequenceAlignment seqAlg = seqExtraction.getAlignmentAlgorithm(); +// for (Conversation c : onsForHostname) { +// Integer[] onSeq = TcpConversationUtils.getPacketLengthSequence(c); +// if (seqAlg.calculateAlignment(representativeOffSeq, onSeq) <= extractedSequenceForOff.getMaxAlignmentCost()) { +// onsLabeledAsOff++; +// } +// } +// int offsLabeledAsOn = 0; +// for (Conversation c : offsForHostname) { +// Integer[] offSeq = TcpConversationUtils.getPacketLengthSequence(c); +// if (seqAlg.calculateAlignment(representativeOnSeq, offSeq) <= extractedSequenceForOn.getMaxAlignmentCost()) { +// offsLabeledAsOn++; +// } +// } +// System.out.println(""); +// // ================================================================================================ +// +// +// // ------------------------------------------------------------------------------------------------------------- +// // ------------------------------------------------------------------------------------------------------------- } }