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