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 * 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}.
34 public static String getSourceIp(PcapPacket packet) {
35 IpV4Packet ipPacket = packet.get(IpV4Packet.class);
36 return ipPacket == null ? null : ipPacket.getHeader().getSrcAddr().getHostAddress();
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}.
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;
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}.
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;
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.
77 public static boolean isSyn(PcapPacket packet) {
78 TcpPacket tcp = packet.get(TcpPacket.class);
79 return tcp != null && tcp.getHeader().getSyn();
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.
88 public static boolean isAck(PcapPacket packet) {
89 TcpPacket tcp = packet.get(TcpPacket.class);
90 return tcp != null && tcp.getHeader().getAck();
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.
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());
109 // Create a list of list of PcapPacket objects
110 ppListOfList.add(ppList);
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()));
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}.
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.
128 public static List<List<List<PcapPacket>>>
129 mergeSignatures(List<List<List<PcapPacket>>> signatures, List<Conversation> conversations) {
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);
155 maxSignatureEl = signature.size() > maxSignatureEl ? signature.size() : maxSignatureEl;
156 secondList.remove(ppList); // Remove as we merge
157 //System.out.println("LEFT_ADJACENT!");
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!");
166 } // TcpConversationUtils.SignaturePosition.NOT_ADJACENT
167 //System.out.println("NOT_ADJACENT!");
170 // Call it a successful merging if there are only less than 5 elements from the second list that
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;