TcpConversationUtils.java: added Javadoc
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / analysis / TcpConversationUtils.java
1 package edu.uci.iotproject.analysis;
2
3 import edu.uci.iotproject.Conversation;
4 import edu.uci.iotproject.DnsMap;
5 import edu.uci.iotproject.util.PcapPacketUtils;
6 import org.pcap4j.core.PcapPacket;
7 import org.pcap4j.packet.IpV4Packet;
8 import org.pcap4j.packet.TcpPacket;
9
10 import java.util.*;
11
12 /**
13  * Utility functions for analyzing and structuring (sets of) {@link Conversation}s.
14  *
15  * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
16  * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
17  */
18 public class TcpConversationUtils {
19
20
21     /**
22      * <p>
23      *      Given a {@link Conversation}, extract its set of "packet pairs", i.e., pairs of request-reply packets.
24      * </p>
25      *
26      * <b>Note:</b> in the current implementation, if one endpoint sends multiple packets back-to-back with no
27      * interleaved reply packets from the other endpoint, such packets are converted to one-item pairs (i.e., instances
28      * of {@lin PcapPacketPair} where {@link PcapPacketPair#getSecond()} is {@code null}).
29      *
30      * @param conv The {@code Conversation} for which packet pairs are to be extracted.
31      * @return The packet pairs extracted from {@code conv}.
32      */
33     public static List<PcapPacketPair> extractPacketPairs(Conversation conv) {
34         List<PcapPacket> packets = conv.getPackets();
35         List<PcapPacketPair> pairs = new ArrayList<>();
36         int i = 0;
37         while (i < packets.size()) {
38             PcapPacket p1 = packets.get(i);
39             String p1SrcIp = p1.get(IpV4Packet.class).getHeader().getSrcAddr().getHostAddress();
40             int p1SrcPort = p1.get(TcpPacket.class).getHeader().getSrcPort().valueAsInt();
41             if (i+1 < packets.size()) {
42                 PcapPacket p2 = packets.get(i+1);
43                 if (PcapPacketUtils.isSource(p2, p1SrcIp, p1SrcPort)) {
44                     // Two packets in a row going in the same direction -> create one item pair for p1
45                     pairs.add(new PcapPacketPair(p1, null));
46                     // Advance one packet as the following two packets may form a valid two-item pair.
47                     i++;
48                 } else {
49                     // The two packets form a response-reply pair, create two-item pair.
50                     pairs.add(new PcapPacketPair(p1, p2));
51                     // Advance two packets as we have already processed the packet at index i+1 in order to create the pair.
52                     i += 2;
53                 }
54             } else {
55                 // Last packet of conversation => one item pair
56                 pairs.add(new PcapPacketPair(p1, null));
57                 // Advance i to ensure termination.
58                 i++;
59             }
60         }
61         return pairs;
62         // TODO: what if there is long time between response and reply packet? Should we add a threshold and exclude those cases?
63     }
64
65
66     /**
67      * Given a list of TCP conversations and associated DNS mappings, groups the conversations by hostname.
68      * @param tcpConversations The list of TCP conversations.
69      * @param ipHostnameMappings The associated DNS mappings.
70      * @return A map where each key is a hostname and its associated value is a list of conversations where one of the
71      *         two communicating hosts is that hostname (i.e. its IP maps to the hostname).
72      */
73     public static Map<String, List<Conversation>> groupConversationsByHostname(List<Conversation> tcpConversations, DnsMap ipHostnameMappings) {
74         HashMap<String, List<Conversation>> result = new HashMap<>();
75         for (Conversation c : tcpConversations) {
76             if (c.getPackets().size() == 0) {
77                 String warningStr = String.format("Detected a %s [%s] with no payload packets.",
78                         c.getClass().getSimpleName(), c.toString());
79                 System.err.println(warningStr);
80                 continue;
81             }
82             IpV4Packet firstPacketIp = c.getPackets().get(0).get(IpV4Packet.class);
83             String ipSrc = firstPacketIp.getHeader().getSrcAddr().getHostAddress();
84             String ipDst = firstPacketIp.getHeader().getDstAddr().getHostAddress();
85             // Check if src or dst IP is associated with one or more hostnames.
86             Set<String> hostnames = ipHostnameMappings.getHostnamesForIp(ipSrc);
87             if (hostnames == null) {
88                 // No luck with src ip (possibly because it's a client->srv packet), try dst ip.
89                 hostnames = ipHostnameMappings.getHostnamesForIp(ipDst);
90             }
91             if (hostnames != null) {
92                 // Put a reference to the conversation for each of the hostnames that the conversation's IP maps to.
93                 for (String hostname : hostnames) {
94                     List<Conversation> newValue = new ArrayList<>();
95                     newValue.add(c);
96                     result.merge(hostname, newValue, (l1, l2) -> { l1.addAll(l2); return l1; });
97                 }
98                 if (hostnames.size() > 1) {
99                     // Print notice of IP mapping to multiple hostnames (debugging)
100                     System.err.println(String.format("%s: encountered an IP that maps to multiple (%d) hostnames",
101                             TcpConversationUtils.class.getSimpleName(), hostnames.size()));
102                 }
103             } else {
104                 // If no hostname mapping, store conversation under the key that is the concatenation of the two IPs.
105                 // In order to ensure consistency when mapping conversations, use lexicographic order to select which IP
106                 // goes first.
107                 String delimiter = "_";
108                 // Note that the in case the comparison returns 0, the strings are equal, so it doesn't matter which of
109                 // ipSrc and ipDst go first (also, this case should not occur in practice as it means that the device is
110                 // communicating with itself!)
111                 String key = ipSrc.compareTo(ipDst) <= 0 ? ipSrc + delimiter + ipDst : ipDst + delimiter + ipSrc;
112                 List<Conversation> newValue = new ArrayList<>();
113                 newValue.add(c);
114                 result.merge(key, newValue, (l1, l2) -> { l1.addAll(l2); return l1; });
115             }
116         }
117         return result;
118     }
119
120
121 }