1 package edu.uci.iotproject;
3 import org.pcap4j.core.PcapPacket;
4 import org.pcap4j.packet.IpV4Packet;
5 import org.pcap4j.packet.TcpPacket;
8 * Groups a FIN packet and its corresponding ACK packet. <b>Immutable and thread safe</b>.
10 * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
11 * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
13 public class FinAckPair {
15 private final PcapPacket mFinPacket;
16 private final PcapPacket mCorrespondingAckPacket;
19 * Constructs a {@code FinAckPair} given a FIN packet.
20 * The corresponding ACK packet field is set to {@code null}.
21 * @param finPacket A FIN packet.
23 public FinAckPair(PcapPacket finPacket) {
24 if (!finPacket.get(TcpPacket.class).getHeader().getFin()) {
25 throw new IllegalArgumentException("not a FIN packet");
27 mFinPacket = finPacket;
28 mCorrespondingAckPacket = null;
32 * Constructs a {@code FinAckPair} given a FIN and an ACK packet.
33 * @param finPacket A FIN packet.
34 * @param correspondingAckPacket The ACK packet corresponding to {@code finPacket}.
36 public FinAckPair(PcapPacket finPacket, PcapPacket correspondingAckPacket) {
37 // Enforce class invariant, i.e. that the FIN and ACK are related.
38 // Note that it is indirectly checked whether finPacket is indeed a FIN packet
39 // as isCorrespondingAckPacket calls the single parameter constructor.
40 if (!FinAckPair.isCorrespondingAckPacket(finPacket, correspondingAckPacket)) {
41 throw new IllegalArgumentException("FIN and ACK not related");
43 mFinPacket = finPacket;
44 mCorrespondingAckPacket = correspondingAckPacket;
48 * Get the FIN packet of this pair.
49 * @return the FIN packet of this pair.
51 public PcapPacket getFinPacket() {
56 * Get the corresponding ACK packet of this pair, if any.
57 * @return the corresponding ACK packet of this pair, if any.
59 public PcapPacket getCorrespondingAckPacket() {
60 return mCorrespondingAckPacket;
64 * Was the FIN in this {@code FinAckPair} acknowledged?
66 * @return {@code true} if the corresponding ACK has been set in this {@code FinAckPair}.
68 public boolean isAcknowledged() {
69 return mFinPacket != null && mCorrespondingAckPacket != null;
73 * Checks if a given packet is an ACK corresponding to the FIN packet in this {@code FinAckPair}.
74 * @return {@code true} if {@code packet} is an ACK that corresponds to the FIN in this pair, {@code false} otherwise.
76 public boolean isCorrespondingAckPacket(PcapPacket packet) {
77 IpV4Packet inputIpPacket = packet.get(IpV4Packet.class);
78 TcpPacket inputTcpPacket = packet.get(TcpPacket.class);
79 if (inputIpPacket == null || inputTcpPacket == null || !inputTcpPacket.getHeader().getAck()) {
83 IpV4Packet finIpPacket = mFinPacket.get(IpV4Packet.class);
84 TcpPacket finTcpPacket = mFinPacket.get(TcpPacket.class);
86 // Extract (srcIp:port,dstIp:port) for input and member (FIN) packets.
87 String inputPacketIpSrc = inputIpPacket.getHeader().getSrcAddr().getHostAddress();
88 String inputPacketIpDst = inputIpPacket.getHeader().getDstAddr().getHostAddress();
89 int inputPacketPortSrc = inputTcpPacket.getHeader().getSrcPort().valueAsInt();
90 int inputPacketPortDst = inputTcpPacket.getHeader().getDstPort().valueAsInt();
91 String finPacketIpSrc = finIpPacket.getHeader().getSrcAddr().getHostAddress();
92 String finPacketIpDst = finIpPacket.getHeader().getDstAddr().getHostAddress();
93 int finPacketPortSrc = finTcpPacket.getHeader().getSrcPort().valueAsInt();
94 int finPacketPortDst = finTcpPacket.getHeader().getDstPort().valueAsInt();
96 // For the two packets to be related, the dst of one must be the src of the other.
97 // Split into multiple if statements for readability. First check IP fields, then ports.
98 if (!(inputPacketIpDst.equals(finPacketIpSrc) && finPacketIpDst.equals(inputPacketIpSrc))) {
101 if (!(inputPacketPortDst == finPacketPortSrc && finPacketPortDst == inputPacketPortSrc)) {
105 // Packets are (most likely) related (part of same conversation/stream).
106 // Now all that is left for us to check is if the sequence numbers match.
107 // Note: recall that the FIN packet advances the seq numbers by 1,
108 // so the ACK number will be one larger than the seq. number in the FIN packet.
109 return inputTcpPacket.getHeader().getAcknowledgmentNumber() == finTcpPacket.getHeader().getSequenceNumber() + 1;
113 * Static method to check if two given packets are a FIN and the corresponding ACK packet.
114 * The purpose of this method is a workaround to enforce the class invariant in the two parameter constructor.
115 * Specifically, the following should be avoided:
117 * public FinAckPair(PcapPacket finPacket, PcapPacket correspondingAckPacket) {
118 * mFinPacket = finPacket;
119 * // Below line is considered bad practice as the object has not been fully initialized at this stage.
120 * if (!this.isCorrespondingAckPacket(correspondingAckPacket)) {
121 * // ... throw exception
125 * @param finPacket The FIN packet.
126 * @param ackPacket The ACK packet that is to be checked if it corresponds to the given FIN packet.
127 * @return {@code true} if the ACK corresponds to the FIN, {@code false} otherwise.
129 private static boolean isCorrespondingAckPacket(PcapPacket finPacket, PcapPacket ackPacket) {
130 FinAckPair tmp = new FinAckPair(finPacket);
131 return tmp.isCorrespondingAckPacket(ackPacket);