Adding signature combining; we can improve the performance later by requiring that...
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / util / PcapPacketUtils.java
1 package edu.uci.iotproject.util;
2
3 import edu.uci.iotproject.Conversation;
4 import edu.uci.iotproject.analysis.PcapPacketPair;
5 import edu.uci.iotproject.analysis.TcpConversationUtils;
6 import org.apache.commons.math3.stat.clustering.Cluster;
7 import org.pcap4j.core.PcapPacket;
8 import org.pcap4j.packet.IpV4Packet;
9 import org.pcap4j.packet.TcpPacket;
10
11 import java.util.*;
12
13 /**
14  * Utility methods for inspecting {@link PcapPacket} properties.
15  *
16  * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
17  * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
18  */
19 public final class PcapPacketUtils {
20
21     /**
22      * This is the threshold value for a signature's number of members
23      * If after a merging the number of members of a signature falls below this threshold, then we can boldly
24      * get rid of that signature.
25      */
26     private static final int SIGNATURE_MERGE_THRESHOLD = 5;
27
28     /**
29      * Gets the source IP (in decimal format) of an IPv4 packet.
30      * @param packet The packet for which the IPv4 source address is to be extracted.
31      * @return The decimal representation of the source IP of {@code packet} <em>iff</em> {@code packet} wraps an
32      *         {@link IpV4Packet}, otherwise {@code null}.
33      */
34     public static String getSourceIp(PcapPacket packet) {
35         IpV4Packet ipPacket = packet.get(IpV4Packet.class);
36         return ipPacket == null ? null : ipPacket.getHeader().getSrcAddr().getHostAddress();
37     }
38
39     /**
40      * Helper method to determine if the given combination of IP and port matches the source of the given packet.
41      * @param packet The packet to check.
42      * @param ip The IP to look for in the ip.src field of {@code packet}.
43      * @param port The port to look for in the tcp.port field of {@code packet}.
44      * @return {@code true} if the given ip+port match the corresponding fields in {@code packet}.
45      */
46     public static boolean isSource(PcapPacket packet, String ip, int port) {
47         IpV4Packet ipPacket = Objects.requireNonNull(packet.get(IpV4Packet.class));
48         // For now we only support TCP flows.
49         TcpPacket tcpPacket = Objects.requireNonNull(packet.get(TcpPacket.class));
50         String ipSrc = ipPacket.getHeader().getSrcAddr().getHostAddress();
51         int srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt();
52         return ipSrc.equals(ip) && srcPort == port;
53     }
54
55     /**
56      * Helper method to determine if the given combination of IP and port matches the destination of the given packet.
57      * @param packet The packet to check.
58      * @param ip The IP to look for in the ip.dst field of {@code packet}.
59      * @param port The port to look for in the tcp.dstport field of {@code packet}.
60      * @return {@code true} if the given ip+port match the corresponding fields in {@code packet}.
61      */
62     public static boolean isDestination(PcapPacket packet, String ip, int port) {
63         IpV4Packet ipPacket = Objects.requireNonNull(packet.get(IpV4Packet.class));
64         // For now we only support TCP flows.
65         TcpPacket tcpPacket = Objects.requireNonNull(packet.get(TcpPacket.class));
66         String ipDst = ipPacket.getHeader().getDstAddr().getHostAddress();
67         int dstPort = tcpPacket.getHeader().getDstPort().valueAsInt();
68         return ipDst.equals(ip) && dstPort == port;
69     }
70
71     /**
72      * Checks if {@code packet} wraps a TCP packet that has the SYN flag set.
73      * @param packet A {@link PcapPacket} that is suspected to contain a {@link TcpPacket} for which the SYN flag is set.
74      * @return {@code true} <em>iff</em> {@code packet} contains a {@code TcpPacket} for which the SYN flag is set,
75      *         {@code false} otherwise.
76      */
77     public static boolean isSyn(PcapPacket packet) {
78         TcpPacket tcp = packet.get(TcpPacket.class);
79         return tcp != null && tcp.getHeader().getSyn();
80     }
81
82     /**
83      * Checks if {@code packet} wraps a TCP packet that has the ACK flag set.
84      * @param packet A {@link PcapPacket} that is suspected to contain a {@link TcpPacket} for which the ACK flag is set.
85      * @return {@code true} <em>iff</em> {@code packet} contains a {@code TcpPacket} for which the ACK flag is set,
86      *         {@code false} otherwise.
87      */
88     public static boolean isAck(PcapPacket packet) {
89         TcpPacket tcp = packet.get(TcpPacket.class);
90         return tcp != null && tcp.getHeader().getAck();
91     }
92
93     /**
94      * Transform a {@code Cluster} of {@code PcapPacketPair} objects into a {@code List} of {@code List} of
95      * {@code PcapPacket} objects.
96      * @param cluster A {@link Cluster} of {@link PcapPacketPair} objects that needs to be transformed.
97      * @return A {@link List} of {@link List} of {@link PcapPacket} objects as the result of the transformation.
98      */
99     public static List<List<PcapPacket>> clusterToListOfPcapPackets(Cluster<PcapPacketPair> cluster) {
100         List<List<PcapPacket>> ppListOfList = new ArrayList<>();
101         for (PcapPacketPair ppp: cluster.getPoints()) {
102             // Create a list of PcapPacket objects (list of two members)
103             List<PcapPacket> ppList = new ArrayList<>();
104             ppList.add(ppp.getFirst());
105             if(ppp.getSecond().isPresent())
106                 ppList.add(ppp.getSecond().get());
107             else
108                 ppList.add(null);
109             // Create a list of list of PcapPacket objects
110             ppListOfList.add(ppList);
111         }
112         // Sort the list of lists based on the first packet's timestamp!
113         Collections.sort(ppListOfList, (p1, p2) -> p1.get(0).getTimestamp().compareTo(p2.get(0).getTimestamp()));
114         return ppListOfList;
115     }
116
117     /**
118      * Merge signatures in {@code List} of {@code List} of {@code List} of {@code PcapPacket} objects.
119      * We cross-check these with {@code List} of {@code Conversation} objects to see
120      * if two {@code List} of {@code PcapPacket} objects actually belong to the same {@code Conversation}.
121      *
122      * @param signatures A {@link List} of {@link List} of {@link List} of
123      *          {@link PcapPacket} objects that needs to be checked and merged.
124      * @param conversations A {@link List} of {@link Conversation} objects as reference for merging.
125      * @return A {@link List} of {@link List} of {@link List} of
126      *          {@link PcapPacket} objects as the result of the merging.
127      */
128     public static List<List<List<PcapPacket>>>
129             mergeSignatures(List<List<List<PcapPacket>>> signatures, List<Conversation> conversations) {
130         // Make a copy first
131         List<List<List<PcapPacket>>> copySignatures = new ArrayList<>(signatures);
132         // Traverse and look into the pairs of signatures
133         for (int first = 0; first < signatures.size(); first++) {
134             List<List<PcapPacket>> firstList = signatures.get(first);
135             for (int second = first+1; second < signatures.size(); second++) {
136                 int maxSignatureEl = 0; // Number of maximum signature elements
137                 List<List<PcapPacket>> secondList = signatures.get(second);
138                 int initialSecondListMembers = secondList.size();
139                 // Iterate over the signatures in the first list
140                 for (List<PcapPacket> signature : firstList) {
141                     signature.removeIf(el -> el == null); // Clean up null elements
142                     // Return the Conversation that the signature is part of
143                     Conversation conv = TcpConversationUtils.returnConversation(signature, conversations);
144                     // Find the element of the second list that is a match for that Conversation
145                     for (List<PcapPacket> ppList : secondList) {
146                         ppList.removeIf(el -> el == null); // Clean up null elements
147                         // Check if they are part of a Conversation and are adjacent to the first signature
148                         // If yes then merge into the first list
149                         TcpConversationUtils.SignaturePosition position =
150                                 TcpConversationUtils.isPartOfConversationAndAdjacent(signature, ppList, conv);
151                         if (position == TcpConversationUtils.SignaturePosition.LEFT_ADJACENT) {
152                             // Merge to the left side of the first signature
153                             ppList.addAll(signature);
154                             signature = ppList;
155                             maxSignatureEl = signature.size() > maxSignatureEl ? signature.size() : maxSignatureEl;
156                             secondList.remove(ppList); // Remove as we merge
157                             //System.out.println("LEFT_ADJACENT!");
158                             break;
159                         } else if (position == TcpConversationUtils.SignaturePosition.RIGHT_ADJACENT) {
160                             // Merge to the right side of the first signature
161                             signature.addAll(ppList);
162                             maxSignatureEl = signature.size() > maxSignatureEl ? signature.size() : maxSignatureEl;
163                             secondList.remove(ppList); // Remove as we merge
164                             //System.out.println("RIGHT_ADJACENT!");
165                             break;
166                         } // TcpConversationUtils.SignaturePosition.NOT_ADJACENT
167                         //System.out.println("NOT_ADJACENT!");
168                     }
169                 }
170                 // Call it a successful merging if there are only less than 5 elements from the second list that
171                 // cannot be merged
172                 if (secondList.size() < SIGNATURE_MERGE_THRESHOLD) {
173                     // Prune the unsuccessfully merged signatures (i.e., these will have size() < maxSignatureEl)
174                     final int maxNumOfEl = maxSignatureEl;
175                     firstList.removeIf(el -> el.size() < maxNumOfEl);
176                     // Remove the merged set of signatures when successful
177                     signatures.remove(secondList);
178                 } else if (secondList.size() < initialSecondListMembers) {
179                     // If only some of the signatures from the second list are merged, this means UNSUCCESSFUL merging
180                     // Return the original copy of the signatures object
181                     return copySignatures;
182                 }
183             }
184         }
185         return signatures;
186     }
187 }