Merge branch 'master' of https://github.uci.edu/rtrimana/smart_home_traffic
authorrtrimana <rtrimana@uci.edu>
Mon, 20 Aug 2018 18:59:43 +0000 (11:59 -0700)
committerrtrimana <rtrimana@uci.edu>
Mon, 20 Aug 2018 18:59:43 +0000 (11:59 -0700)
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/SequenceExtraction.java [new file with mode: 0644]
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/TcpReassembler.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java

index 05d97e3..e7ebf3b 100644 (file)
@@ -64,12 +64,17 @@ public class Conversation {
     /**
      * List of SYN packets pertaining to this conversation.
      */
-    private List<PcapPacket> mSynPackets;
+    private final List<PcapPacket> mSynPackets;
 
     /**
      * List of pairs FINs and their corresponding ACKs associated with this conversation.
      */
-    private List<FinAckPair> mFinPackets;
+    private final List<FinAckPair> mFinPackets;
+
+    /**
+     * List of RST packets associated with this conversation.
+     */
+    private final List<PcapPacket> mRstPackets;
     /* End instance properties */
 
     /**
@@ -113,6 +118,7 @@ public class Conversation {
         this.mSeqNumbersSrv = new HashSet<>();
         this.mSynPackets = new ArrayList<>();
         this.mFinPackets = new ArrayList<>();
+        this.mRstPackets = new ArrayList<>();
     }
 
     /**
@@ -281,6 +287,37 @@ public class Conversation {
                 mFinPackets.stream().anyMatch(finAckPair -> finAckPair.isAcknowledged() && PcapPacketUtils.isSource(finAckPair.getFinPacket(), mServerIp, mServerPort));
     }
 
+    /**
+     * Add a TCP segment for which the RST flag is set to this {@code Conversation}.
+     * @param packet A {@link PcapPacket} wrapping a TCP segment pertaining to this {@code Conversation} for which the
+     *               RST flag is set.
+     */
+    public void addRstPacket(PcapPacket packet) {
+        /*
+         * TODO:
+         * When now also keeping track of RST packets, should we also...?
+         * 1) Prevent later packets from being added once a RST segment has been added?
+         * 2) Extend 'isGracefullyShutdown()' to also consider RST segments, or add another method, 'isShutdown()' that
+         *    both considers FIN/ACK (graceful) as well as RST (abrupt/"ungraceful") shutdown?
+         * 3) Should it be impossible to associate more than one RST segment with each Conversation?
+         */
+        onAddPrecondition(packet);
+        TcpPacket tcpPacket = packet.get(TcpPacket.class);
+        if (tcpPacket == null || !tcpPacket.getHeader().getRst()) {
+            throw new IllegalArgumentException("not a RST packet");
+        }
+        mRstPackets.add(packet);
+    }
+
+    /**
+     * Get the TCP segments pertaining to this {@code Conversation} for which it was detected that the RST flag is set.
+     * @return the TCP segments pertaining to this {@code Conversation} for which it was detected that the RST flag is
+     *         set.
+     */
+    public List<PcapPacket> getRstPackets() {
+        return Collections.unmodifiableList(mRstPackets);
+    }
+
     // =========================================================================================================
     // We simply reuse equals and hashCode methods of String.class to be able to use this class as a key
     // in a Map.
@@ -370,7 +407,7 @@ public class Conversation {
             case SERVER_TO_CLIENT:
                 return mSeqNumbersSrv.contains(seqNo);
             default:
-                throw new RuntimeException(String.format("Unexpected value of enum '%s'",
+                throw new AssertionError(String.format("Unexpected value of enum '%s'",
                         Direction.class.getSimpleName()));
         }
     }
@@ -399,17 +436,19 @@ public class Conversation {
                 mSeqNumbersSrv.add(seqNo);
                 break;
             default:
-                throw new RuntimeException(String.format("Unexpected value of enum '%s'",
+                throw new AssertionError(String.format("Unexpected value of enum '%s'",
                         Direction.class.getSimpleName()));
         }
     }
 
     /**
-     * Determine the direction of {@code packet}.
+     * Determine the direction of {@code packet}. An {@link IllegalArgumentException} is thrown if {@code packet} does
+     * not pertain to this conversation.
+     *
      * @param packet The packet whose direction is to be determined.
      * @return A {@link Direction} indicating the direction of the packet.
      */
-    private Direction getDirection(PcapPacket packet) {
+    public Direction getDirection(PcapPacket packet) {
         IpV4Packet ipPacket = packet.get(IpV4Packet.class);
         String ipSrc = ipPacket.getHeader().getSrcAddr().getHostAddress();
         String ipDst = ipPacket.getHeader().getDstAddr().getHostAddress();
@@ -428,8 +467,28 @@ public class Conversation {
     /**
      * Utility enum for expressing the direction of a packet pertaining to this {@code Conversation}.
      */
-    private enum Direction {
-        CLIENT_TO_SERVER, SERVER_TO_CLIENT
+    public enum Direction {
+
+        CLIENT_TO_SERVER {
+            @Override
+            public String toCompactString() {
+                return "C->S";
+            }
+        },
+        SERVER_TO_CLIENT {
+            @Override
+            public String toCompactString() {
+                return "S->C";
+            }
+        };
+
+
+        /**
+         * Get a compact string representation of this {@code Direction}.
+         * @return a compact string representation of this {@code Direction}.
+         */
+        abstract public String toCompactString();
+
     }
 
-}
\ No newline at end of file
+}
index c72fd6e..75eff77 100644 (file)
@@ -47,6 +47,14 @@ public class Main {
 //        final String inputPcapFile = path + "/2018-07/tplink/tplink.wlan1.local.pcap";
 //        final String outputPcapFile = path + "/2018-07/tplink/tplink-processed.pcap";
 //        final String triggerTimesFile = path + "/2018-07/tplink/tplink-july-25-2018.timestamps";
+//        final String deviceIp = "192.168.1.159";
+
+        // 2b) TP-Link July 25 experiment TRUNCATED:
+        // Only contains "true local" events, i.e., before the behavior changes to remote-like behavior.
+        // Last included event is at July 25 10:38:11; file filtered to only include packets with arrival time <= 10:38:27.
+//        final String inputPcapFile = path + "/2018-07/tplink/tplink.wlan1.local.truncated.pcap";
+//        final String outputPcapFile = path + "/2018-07/tplink/tplink-processed.truncated.pcap";
+//        final String triggerTimesFile = path + "/2018-07/tplink/tplink-july-25-2018.truncated.timestamps";
 //        final String deviceIp = "192.168.1.159";
 
         // 3) SmartThings Plug July 25 experiment
diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/SequenceExtraction.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/SequenceExtraction.java
new file mode 100644 (file)
index 0000000..bb1e210
--- /dev/null
@@ -0,0 +1,76 @@
+package edu.uci.iotproject;
+
+import edu.uci.iotproject.comparison.seqalignment.AlignmentPricer;
+import edu.uci.iotproject.comparison.seqalignment.SequenceAlignment;
+import org.pcap4j.core.PcapPacket;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * TODO add class documentation.
+ *
+ * @author Janus Varmarken
+ */
+public class SequenceExtraction {
+
+
+    private final SequenceAlignment<Integer> mAlignmentAlg;
+
+
+    public SequenceExtraction() {
+        mAlignmentAlg = new SequenceAlignment<>(new AlignmentPricer<>((i1,i2) -> Math.abs(i1-i2), i -> 10));
+    }
+
+
+    public SequenceExtraction(SequenceAlignment<Integer> alignmentAlgorithm) {
+        mAlignmentAlg = alignmentAlgorithm;
+    }
+
+    // Initial
+//    /**
+//     *
+//     * @param convsForAction A set of {@link Conversation}s known to be associated with a single type of user action.
+//     */
+//    public void extract(List<Conversation> convsForAction) {
+//        int maxDifference = 0;
+//
+//        for (int i = 0; i < convsForAction.size(); i++) {
+//            for (int j = i+1; j < convsForAction.size(); i++) {
+//                Integer[] sequence1 = getPacketLengthSequence(convsForAction.get(i));
+//                Integer[] sequence2 = getPacketLengthSequence(convsForAction.get(j));
+//                int alignmentCost = mAlignmentAlg.calculateAlignment(sequence1, sequence2);
+//                if (alignmentCost > maxDifference) {
+//                    maxDifference = alignmentCost;
+//                }
+//            }
+//        }
+//
+//    }
+
+
+//    public void extract(Map<String, List<Conversation>> hostnameToConvs) {
+//        int maxDifference = 0;
+//
+//        for (int i = 0; i < convsForAction.size(); i++) {
+//            for (int j = i+1; j < convsForAction.size(); i++) {
+//                Integer[] sequence1 = getPacketLengthSequence(convsForAction.get(i));
+//                Integer[] sequence2 = getPacketLengthSequence(convsForAction.get(j));
+//                int alignmentCost = mAlignmentAlg.calculateAlignment(sequence1, sequence2);
+//                if (alignmentCost > maxDifference) {
+//                    maxDifference = alignmentCost;
+//                }
+//            }
+//        }
+//
+//    }
+
+    private Integer[] getPacketLengthSequence(Conversation c) {
+        List<PcapPacket> packets = c.getPackets();
+        Integer[] packetLengthSequence = new Integer[packets.size()];
+        for (int i = 0; i < packetLengthSequence.length; i++) {
+            packetLengthSequence[i] = packets.get(i).length();
+        }
+        return packetLengthSequence;
+    }
+}
index d54e124..c61223d 100644 (file)
@@ -2,6 +2,7 @@ package edu.uci.iotproject;
 
 import org.pcap4j.core.PacketListener;
 import org.pcap4j.core.PcapPacket;
+import org.pcap4j.packet.IpV4Packet;
 import org.pcap4j.packet.TcpPacket;
 
 import java.util.*;
@@ -148,13 +149,15 @@ public class TcpReassembler implements PacketListener {
         if (!conv.isRetransmission(srvSynPacket) && !conv.addSynPacket(srvSynPacket)) {
             // For safety/debugging: if NOT a retransmission and add fails,
             // something has gone terribly wrong/invariant is broken.
-            throw new IllegalStateException("Attempt to add SYN ACK packet that was NOT a retransmission failed." +
+            throw new AssertionError("Attempt to add SYN ACK packet that was NOT a retransmission failed." +
                     Conversation.class.getSimpleName() + " invariant broken.");
         }
     }
 
     private void processRstPacket(PcapPacket rstPacket) {
         Conversation conv = getOngoingConversationOrCreateNew(rstPacket);
+        // Add RST packet to conversation.
+        conv.addRstPacket(rstPacket);
         // Move conversation to set of terminated conversations.
         mTerminatedConversations.add(conv);
         mOpenConversations.remove(conv, conv);
@@ -219,7 +222,11 @@ public class TcpReassembler implements PacketListener {
                 conv = Conversation.fromPcapPacket(pcapPacket, false);
             } else {
                 // TODO: can we do anything else but arbitrarily select who is designated as the server in this case?
-                conv = Conversation.fromPcapPacket(pcapPacket, false);
+                // We can check if the IP prefix matches a local IP when handling traffic observed inside the local
+                // network, but that obviously won't be a useful strategy for an observer at the WAN port.
+                String srcIp = pcapPacket.get(IpV4Packet.class).getHeader().getSrcAddr().getHostAddress();
+                boolean clientIsSrc = srcIp.startsWith("10.0.1.") || srcIp.startsWith("192.168.1.");
+                conv = Conversation.fromPcapPacket(pcapPacket, clientIsSrc);
             }
             mOpenConversations.put(conv, conv);
         }
index 9c9af70..a27e5fc 100644 (file)
@@ -2,6 +2,7 @@ package edu.uci.iotproject.analysis;
 
 import edu.uci.iotproject.Conversation;
 import edu.uci.iotproject.DnsMap;
+import edu.uci.iotproject.FinAckPair;
 import edu.uci.iotproject.util.PcapPacketUtils;
 import org.pcap4j.core.PcapPacket;
 import org.pcap4j.packet.IpV4Packet;
@@ -17,7 +18,6 @@ import java.util.*;
  */
 public class TcpConversationUtils {
 
-
     /**
      * <p>
      *      Given a {@link Conversation}, extract its set of "packet pairs", i.e., pairs of request-reply packets.
@@ -157,12 +157,38 @@ public class TcpConversationUtils {
                 continue;
             }
             StringBuilder sb = new StringBuilder();
-            for (PcapPacket pp : conv.getPackets()) {
-                if (sb.length() != 0) {
-                    // only add a space if there's preceding content
-                    sb.append(" ");
+            // Add SYN and SYNACK at front of sequence to indicate if we saw the handshake or if recording started in
+            // the middle of the conversation.
+            for (PcapPacket syn : conv.getSynPackets()) {
+                TcpPacket.TcpHeader tcpHeader = syn.get(TcpPacket.class).getHeader();
+                if (tcpHeader.getSyn() && tcpHeader.getAck()) {
+                    // Only append a space if there's preceding content.
+                    appendSpaceIfNotEmpty(sb);
+                    sb.append("SYNACK");
+                } else if (tcpHeader.getSyn()) {
+                    if (sb.length() != 0) {
+                        // If present in the trace, the client's SYN should be at the front of the list, so it should be
+                        // appended as the first item.
+                        throw new AssertionError("StringBuilder had content when appending SYN");
+                    }
+                    sb.append("SYN");
                 }
-                sb.append(pp.length());
+            }
+            // Then append the length of all application data packets.
+            for (PcapPacket pp : conv.getPackets()) {
+                // Only append a space if there's preceding content.
+                appendSpaceIfNotEmpty(sb);
+                sb.append("(" + conv.getDirection(pp).toCompactString() + "_" + pp.length() + ")");
+            }
+            // Then append the logged FINs to indicate if conversation was terminated gracefully.
+            for (FinAckPair fap : conv.getFinAckPairs()) {
+                appendSpaceIfNotEmpty(sb);
+                sb.append(fap.isAcknowledged() ? "FINACK" : "FIN");
+            }
+            // Then append the logged RSTs to indicate if conversation was terminated abruptly.
+            for (PcapPacket pp : conv.getRstPackets()) {
+                appendSpaceIfNotEmpty(sb);
+                sb.append("RST");
             }
             List<Conversation> oneItemList = new ArrayList<>();
             oneItemList.add(conv);
@@ -228,4 +254,14 @@ public class TcpConversationUtils {
         }
         return result;
     }
+
+    /**
+     * Appends a space to {@code sb} <em>iff</em> {@code sb} already contains some content.
+     * @param sb A {@link StringBuilder} that should have a space appended <em>iff</em> it is not empty.
+     */
+    private static void appendSpaceIfNotEmpty(StringBuilder sb) {
+        if (sb.length() != 0) {
+            sb.append(" ");
+        }
+    }
 }