1 package edu.uci.iotproject.util;
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;
14 * Utility methods for inspecting {@link PcapPacket} properties.
16 * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
17 * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
19 public final class PcapPacketUtils {
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.
26 private static final int SIGNATURE_MERGE_THRESHOLD = 5;
29 * Determines if a given {@link PcapPacket} wraps a {@link TcpPacket}.
30 * @param packet The {@link PcapPacket} to inspect.
31 * @return {@code true} if {@code packet} wraps a {@link TcpPacket}, {@code false} otherwise.
33 public static boolean isTcp(PcapPacket packet) {
34 return packet.get(TcpPacket.class) != null;
38 * Gets the source IP (in decimal format) of an IPv4 packet.
39 * @param packet The packet for which the IPv4 source address is to be extracted.
40 * @return The decimal representation of the source IP of {@code packet} <em>iff</em> {@code packet} wraps an
41 * {@link IpV4Packet}, otherwise {@code null}.
43 public static String getSourceIp(PcapPacket packet) {
44 IpV4Packet ipPacket = packet.get(IpV4Packet.class);
45 return ipPacket == null ? null : ipPacket.getHeader().getSrcAddr().getHostAddress();
49 * Gets the source port of a TCP packet.
50 * @param packet The packet for which the source port is to be extracted.
51 * @return The source port of the {@link TcpPacket} encapsulated by {@code packet}.
52 * @throws IllegalArgumentException if {@code packet} does not encapsulate a {@link TcpPacket}.
54 public static int getSourcePort(PcapPacket packet) {
55 TcpPacket tcpPacket = packet.get(TcpPacket.class);
56 if (tcpPacket == null) {
57 throw new IllegalArgumentException("not a TCP packet");
59 return tcpPacket.getHeader().getSrcPort().valueAsInt();
63 * Gets the destination port of a TCP packet.
64 * @param packet The packet for which the destination port is to be extracted.
65 * @return The destination port of the {@link TcpPacket} encapsulated by {@code packet}.
66 * @throws IllegalArgumentException if {@code packet} does not encapsulate a {@link TcpPacket}.
68 public static int getDestinationPort(PcapPacket packet) {
69 TcpPacket tcpPacket = packet.get(TcpPacket.class);
70 if (tcpPacket == null) {
71 throw new IllegalArgumentException("not a TCP packet");
73 return tcpPacket.getHeader().getDstPort().valueAsInt();
77 * Helper method to determine if the given combination of IP and port matches the source of the given packet.
78 * @param packet The packet to check.
79 * @param ip The IP to look for in the ip.src field of {@code packet}.
80 * @param port The port to look for in the tcp.port field of {@code packet}.
81 * @return {@code true} if the given ip+port match the corresponding fields in {@code packet}.
83 public static boolean isSource(PcapPacket packet, String ip, int port) {
84 IpV4Packet ipPacket = Objects.requireNonNull(packet.get(IpV4Packet.class));
85 // For now we only support TCP flows.
86 TcpPacket tcpPacket = Objects.requireNonNull(packet.get(TcpPacket.class));
87 String ipSrc = ipPacket.getHeader().getSrcAddr().getHostAddress();
88 int srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt();
89 return ipSrc.equals(ip) && srcPort == port;
93 * Helper method to determine if the given combination of IP and port matches the destination of the given packet.
94 * @param packet The packet to check.
95 * @param ip The IP to look for in the ip.dst field of {@code packet}.
96 * @param port The port to look for in the tcp.dstport field of {@code packet}.
97 * @return {@code true} if the given ip+port match the corresponding fields in {@code packet}.
99 public static boolean isDestination(PcapPacket packet, String ip, int port) {
100 IpV4Packet ipPacket = Objects.requireNonNull(packet.get(IpV4Packet.class));
101 // For now we only support TCP flows.
102 TcpPacket tcpPacket = Objects.requireNonNull(packet.get(TcpPacket.class));
103 String ipDst = ipPacket.getHeader().getDstAddr().getHostAddress();
104 int dstPort = tcpPacket.getHeader().getDstPort().valueAsInt();
105 return ipDst.equals(ip) && dstPort == port;
109 * Checks if {@code packet} wraps a TCP packet that has the SYN flag set.
110 * @param packet A {@link PcapPacket} that is suspected to contain a {@link TcpPacket} for which the SYN flag is set.
111 * @return {@code true} <em>iff</em> {@code packet} contains a {@code TcpPacket} for which the SYN flag is set,
112 * {@code false} otherwise.
114 public static boolean isSyn(PcapPacket packet) {
115 TcpPacket tcp = packet.get(TcpPacket.class);
116 return tcp != null && tcp.getHeader().getSyn();
120 * Checks if {@code packet} wraps a TCP packet that has the ACK flag set.
121 * @param packet A {@link PcapPacket} that is suspected to contain a {@link TcpPacket} for which the ACK flag is set.
122 * @return {@code true} <em>iff</em> {@code packet} contains a {@code TcpPacket} for which the ACK flag is set,
123 * {@code false} otherwise.
125 public static boolean isAck(PcapPacket packet) {
126 TcpPacket tcp = packet.get(TcpPacket.class);
127 return tcp != null && tcp.getHeader().getAck();
131 * Transform a {@code Cluster} of {@code PcapPacketPair} objects into a {@code List} of {@code List} of
132 * {@code PcapPacket} objects.
133 * @param cluster A {@link Cluster} of {@link PcapPacketPair} objects that needs to be transformed.
134 * @return A {@link List} of {@link List} of {@link PcapPacket} objects as the result of the transformation.
136 public static List<List<PcapPacket>> clusterToListOfPcapPackets(Cluster<PcapPacketPair> cluster) {
137 List<List<PcapPacket>> ppListOfList = new ArrayList<>();
138 for (PcapPacketPair ppp: cluster.getPoints()) {
139 // Create a list of PcapPacket objects (list of two members)
140 List<PcapPacket> ppList = new ArrayList<>();
141 ppList.add(ppp.getFirst());
142 if(ppp.getSecond().isPresent())
143 ppList.add(ppp.getSecond().get());
146 // Create a list of list of PcapPacket objects
147 ppListOfList.add(ppList);
149 // Sort the list of lists based on the first packet's timestamp!
150 Collections.sort(ppListOfList, (p1, p2) -> p1.get(0).getTimestamp().compareTo(p2.get(0).getTimestamp()));
155 * Merge signatures in {@code List} of {@code List} of {@code List} of {@code PcapPacket} objects.
156 * We cross-check these with {@code List} of {@code Conversation} objects to see
157 * if two {@code List} of {@code PcapPacket} objects actually belong to the same {@code Conversation}.
159 * @param signatures A {@link List} of {@link List} of {@link List} of
160 * {@link PcapPacket} objects that needs to be checked and merged.
161 * @param conversations A {@link List} of {@link Conversation} objects as reference for merging.
162 * @return A {@link List} of {@link List} of {@link List} of
163 * {@link PcapPacket} objects as the result of the merging.
165 public static List<List<List<PcapPacket>>>
166 mergeSignatures(List<List<List<PcapPacket>>> signatures, List<Conversation> conversations) {
168 List<List<List<PcapPacket>>> copySignatures = new ArrayList<>(signatures);
169 // Traverse and look into the pairs of signatures
170 for (int first = 0; first < signatures.size(); first++) {
171 List<List<PcapPacket>> firstList = signatures.get(first);
172 for (int second = first+1; second < signatures.size(); second++) {
173 int maxSignatureEl = 0; // Number of maximum signature elements
174 List<List<PcapPacket>> secondList = signatures.get(second);
175 int initialSecondListMembers = secondList.size();
176 // Iterate over the signatures in the first list
177 for (List<PcapPacket> signature : firstList) {
178 signature.removeIf(el -> el == null); // Clean up null elements
179 // Return the Conversation that the signature is part of
180 Conversation conv = TcpConversationUtils.returnConversation(signature, conversations);
181 // Find the element of the second list that is a match for that Conversation
182 for (List<PcapPacket> ppList : secondList) {
183 ppList.removeIf(el -> el == null); // Clean up null elements
184 // Check if they are part of a Conversation and are adjacent to the first signature
185 // If yes then merge into the first list
186 TcpConversationUtils.SignaturePosition position =
187 TcpConversationUtils.isPartOfConversationAndAdjacent(signature, ppList, conv);
188 if (position == TcpConversationUtils.SignaturePosition.LEFT_ADJACENT) {
189 // Merge to the left side of the first signature
190 ppList.addAll(signature);
192 maxSignatureEl = signature.size() > maxSignatureEl ? signature.size() : maxSignatureEl;
193 secondList.remove(ppList); // Remove as we merge
194 //System.out.println("LEFT_ADJACENT!");
196 } else if (position == TcpConversationUtils.SignaturePosition.RIGHT_ADJACENT) {
197 // Merge to the right side of the first signature
198 signature.addAll(ppList);
199 maxSignatureEl = signature.size() > maxSignatureEl ? signature.size() : maxSignatureEl;
200 secondList.remove(ppList); // Remove as we merge
201 //System.out.println("RIGHT_ADJACENT!");
203 } // TcpConversationUtils.SignaturePosition.NOT_ADJACENT
204 //System.out.println("NOT_ADJACENT!");
207 // Call it a successful merging if there are only less than 5 elements from the second list that
209 if (secondList.size() < SIGNATURE_MERGE_THRESHOLD) {
210 // Prune the unsuccessfully merged signatures (i.e., these will have size() < maxSignatureEl)
211 final int maxNumOfEl = maxSignatureEl;
212 firstList.removeIf(el -> el.size() < maxNumOfEl);
213 // Remove the merged set of signatures when successful
214 signatures.remove(secondList);
215 } else if (secondList.size() < initialSecondListMembers) {
216 // If only some of the signatures from the second list are merged, this means UNSUCCESSFUL merging
217 // Return the original copy of the signatures object
218 return copySignatures;