From b47a5df7320db769787d69398edce57a39ec2e2b Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Sat, 28 Apr 2018 18:46:40 -0700 Subject: [PATCH] First small step towards pattern search: separate packets related to packet into separate lists, one list for each conversation/session. --- .../java/edu/uci/iotproject/FlowPattern.java | 43 +++++++ .../edu/uci/iotproject/FlowPatternFinder.java | 111 ++++++++++++++++++ .../main/java/edu/uci/iotproject/Main.java | 17 ++- 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPattern.java create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPatternFinder.java diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPattern.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPattern.java new file mode 100644 index 0000000..4b5b8fe --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPattern.java @@ -0,0 +1,43 @@ +package edu.uci.iotproject; + +import java.util.Collections; +import java.util.List; + +/** + * TODO add class documentation. + * + * @author Janus Varmarken + */ +public class FlowPattern { + + private final String patternId; + + /** + * The hostname that this {@code FlowPattern} is associated with. + */ + private final String hostname; + + /** + * The order of packet lengths that defines this {@link FlowPattern} + */ + private final List flowPacketOrder; + + public FlowPattern(String patternId, String hostname, List flowPacketOrder) { + this.patternId = patternId; + this.hostname = hostname; + this.flowPacketOrder = Collections.unmodifiableList(flowPacketOrder); + } + + public String getHostname() { + return hostname; + } + + /** + * Get the the sequence of packet lengths that defines this {@code FlowPattern}. + * @return the the sequence of packet lengths that defines this {@code FlowPattern}. + */ + public List getPacketOrder() { + return flowPacketOrder; + } + +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPatternFinder.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPatternFinder.java new file mode 100644 index 0000000..1732b99 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/FlowPatternFinder.java @@ -0,0 +1,111 @@ +package edu.uci.iotproject; + +import org.pcap4j.core.NotOpenException; +import org.pcap4j.core.PcapHandle; +import org.pcap4j.core.PcapNativeException; +import org.pcap4j.packet.IpV4Packet; +import org.pcap4j.packet.Packet; +import org.pcap4j.packet.TcpPacket; + +import java.io.EOFException; +import java.util.*; +import java.util.concurrent.TimeoutException; + +/** + * Provides functionality for searching for the presence of a {@link FlowPattern} in a PCAP trace. + * + * @author Janus Varmarken + */ +public class FlowPatternFinder { + + private final Map> dnsMap; + private final Map> connections = new HashMap<>(); + + public FlowPatternFinder(Map> dnsMap) { + this.dnsMap = Objects.requireNonNull(dnsMap); + } + + private static final Set EMPTY_SET = Collections.unmodifiableSet(new HashSet<>()); + + // TODO clean up exceptions etc. + public void findFlowPattern(PcapHandle pcap, FlowPattern pattern) + throws PcapNativeException, NotOpenException, TimeoutException { + try { + Packet packet; + while ((packet = pcap.getNextPacketEx()) != null) { + + + // For now, we only work support pattern search in TCP over IPv4. + IpV4Packet ipPacket = packet.get(IpV4Packet.class); + TcpPacket tcpPacket = packet.get(TcpPacket.class); + if (ipPacket == null || tcpPacket == null) { + continue; + } + String srcAddress = ipPacket.getHeader().getSrcAddr().getHostAddress(); + String dstAddress = ipPacket.getHeader().getDstAddr().getHostAddress(); + int srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt(); + int dstPort = tcpPacket.getHeader().getDstPort().valueAsInt(); + // Is this packet related to the pattern and coming from the cloud server? + boolean fromServer = dnsMap.getOrDefault(srcAddress, EMPTY_SET).contains(pattern.getHostname()); + // Is this packet related to the pattern and going to the cloud server? + boolean fromClient = dnsMap.getOrDefault(dstAddress, EMPTY_SET).contains(pattern.getHostname()); + if (!fromServer && !fromClient) { + // Packet not related to pattern, skip it. + continue; + } + // Identify conversations (connections/sessions) by the four-tuple (clientIp, clientPort, serverIp, serverPort). + // TODO: this is strictly not sufficient to differentiate one TCP session from another, but should suffice for now. + Conversation conversation = fromClient ? new Conversation(srcAddress, srcPort, dstAddress, dstPort) : + new Conversation(dstAddress, dstPort, srcAddress, srcPort); + List listWrappedPacket = new ArrayList<>(); + listWrappedPacket.add(packet); + // Create new conversation entry, or append packet to existing. + connections.merge(conversation, listWrappedPacket, (v1, v2) -> { + v1.addAll(v2); + return v1; + }); + } + } catch (EOFException eofe) { + System.out.println("findFlowPattern: finished processing entire file"); + } + } + + /** + * Immutable class used for identifying a conversation/connection/session/flow (packet's belonging to the same + * session between a client and a server). + */ + private static class Conversation { + + private final String clientIp; + private final int clientPort; + private final String serverIp; + private final int serverPort; + + public Conversation(String clientIp, int clientPort, String serverIp, int serverPort) { + this.clientIp = clientIp; + this.clientPort = clientPort; + this.serverIp = serverIp; + this.serverPort = serverPort; + } + + + // ========================================================================================================= + // We simply reuse equals and hashCode methods of String.class to be able to use this immutable class as a key + // in a Map. + @Override + public boolean equals(Object obj) { + return obj instanceof Conversation && this.toString().equals(obj.toString()); + } + @Override + public int hashCode() { + return toString().hashCode(); + } + // ========================================================================================================= + + @Override + public String toString() { + return String.format("%s:%d %s:%d", clientIp, clientPort, serverIp, serverPort); + } + } + +} 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 d82e134..18bb582 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 @@ -29,7 +29,20 @@ public class Main { final String fileName = "/users/varmarken/Desktop/wlan1.local.dns.pcap"; List dnsPackets = extractDnsAnswerPackets(fileName); Map> ipToHostnameMap = constructIpToHostnameMap(dnsPackets); - ipToHostnameMap.forEach((k,v) -> System.out.println(String.format("%s => %s", k, v.toString()))); +// ipToHostnameMap.forEach((k,v) -> System.out.println(String.format("%s => %s", k, v.toString()))); + + + // ====== Debug code ====== + PcapHandle handle; + try { + handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO); + } catch (PcapNativeException pne) { + handle = Pcaps.openOffline(fileName); + } + FlowPatternFinder fpf = new FlowPatternFinder(ipToHostnameMap); + fpf.findFlowPattern(handle, new FlowPattern("TP_LINK_LOCAL_ON", "events.tplinkra.com", new ArrayList<>())); + + // ======================== } /** @@ -108,6 +121,8 @@ public class Main { return result; } + + // /** // * Private class properties // */ -- 2.34.1