package edu.uci.iotproject;
+import org.pcap4j.core.PacketListener;
import org.pcap4j.core.PcapPacket;
import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.TcpPacket;
/**
* Reassembles TCP conversations (streams).
+ * <b>Note: current version only supports TCP over IPv4.</b>
*
* @author Janus Varmarken {@literal <jvarmark@uci.edu>}
* @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
*/
-public class TcpReassembler implements PcapPacketConsumer {
+public class TcpReassembler implements PacketListener {
/**
* Holds <em>open</em> {@link Conversation}s, i.e., {@code Conversation}s that have <em>not</em> been detected as
/**
* Holds <em>terminated</em> {@link Conversation}s.
*/
- private final Map<Conversation, Conversation> mTerminatedConversations = new HashMap<>();
+ private final List<Conversation> mTerminatedConversations = new ArrayList<>();
@Override
- public void consumePacket(PcapPacket pcapPacket) {
+ public void gotPacket(PcapPacket pcapPacket) {
+ IpV4Packet ipPacket = pcapPacket.get(IpV4Packet.class);
TcpPacket tcpPacket = pcapPacket.get(TcpPacket.class);
- if (tcpPacket == null) {
+ if (ipPacket == null || tcpPacket == null) {
return;
}
// ... TODO?
/**
* Get the reassembled TCP connections. Note that if this is called while packets are still being processed (by
- * calls to {@link #consumePacket(PcapPacket)}), the behavior is undefined and the returned list may be inconsistent.
+ * calls to {@link #gotPacket(PcapPacket)}), the behavior is undefined and the returned list may be inconsistent.
* @return The reassembled TCP connections.
*/
public List<Conversation> getTcpConversations() {
ArrayList<Conversation> combined = new ArrayList<>();
- combined.addAll(mTerminatedConversations.values());
+ combined.addAll(mTerminatedConversations);
combined.addAll(mOpenConversations.values());
return combined;
}
// to establish a new conversation with the same four tuple as ongoingConv.
// Mark existing connection as terminated.
// TODO: is this 100% theoretically correct, e.g., if many connection attempts are made back to back? And RST packets?
- mTerminatedConversations.put(ongoingConv, ongoingConv);
+ mTerminatedConversations.add(ongoingConv);
mOpenConversations.remove(ongoingConv);
}
}
if (!conv.isRetransmission(srvSynPacket) && !conv.addSynPacket(srvSynPacket)) {
// For safety/debugging: if NOT a retransmission and add fails,
// something has gone terribly wrong/invariant is broken.
- throw new IllegalStateException("Attempt to add SYN ACK packet that was NOT a retransmission failed." +
+ throw new AssertionError("Attempt to add SYN ACK packet that was NOT a retransmission failed." +
Conversation.class.getSimpleName() + " invariant broken.");
}
}
private void processRstPacket(PcapPacket rstPacket) {
Conversation conv = getOngoingConversationOrCreateNew(rstPacket);
+ // Add RST packet to conversation.
+ conv.addRstPacket(rstPacket);
// Move conversation to set of terminated conversations.
- mTerminatedConversations.put(conv, conv);
+ mTerminatedConversations.add(conv);
mOpenConversations.remove(conv, conv);
}
conv.attemptAcknowledgementOfFin(ackPacket);
if (conv.isGracefullyShutdown()) {
// Move conversation to set of terminated conversations.
- mTerminatedConversations.put(conv, conv);
+ mTerminatedConversations.add(conv);
mOpenConversations.remove(conv);
}
}
conv = Conversation.fromPcapPacket(pcapPacket, false);
} else {
// TODO: can we do anything else but arbitrarily select who is designated as the server in this case?
- conv = Conversation.fromPcapPacket(pcapPacket, false);
+ // We can check if the IP prefix matches a local IP when handling traffic observed inside the local
+ // network, but that obviously won't be a useful strategy for an observer at the WAN port.
+ String srcIp = pcapPacket.get(IpV4Packet.class).getHeader().getSrcAddr().getHostAddress();
+ // TODO: REPLACE THE ROUTER'S IP WITH A PARAMETER!!!
+ boolean clientIsSrc = srcIp.startsWith("10.0.1.") || srcIp.startsWith("192.168.1.") || srcIp.equals("128.195.205.105");
+ conv = Conversation.fromPcapPacket(pcapPacket, clientIsSrc);
}
mOpenConversations.put(conv, conv);
}