package com.netifera.platform.net.internal.sniffing.managers;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import com.netifera.platform.api.log.ILogger;
import com.netifera.platform.net.internal.sniffing.stream.TCPReassemblyConfig;
import com.netifera.platform.net.internal.sniffing.stream.TCPSession;
import com.netifera.platform.net.internal.sniffing.stream.TCPSessionKey;
import com.netifera.platform.net.packets.IPacketHeader;
import com.netifera.platform.net.packets.tcpip.IP;
import com.netifera.platform.net.packets.tcpip.IPv4;
import com.netifera.platform.net.packets.tcpip.IPv6;
import com.netifera.platform.net.packets.tcpip.TCP;
import com.netifera.platform.net.pcap.ICaptureInterface;
import com.netifera.platform.net.sniffing.IPacketContext;
import com.netifera.platform.net.sniffing.IPacketFilter;
import com.netifera.platform.net.sniffing.IPacketSniffer;
import com.netifera.platform.net.sniffing.IPacketSnifferHandle;
import com.netifera.platform.net.sniffing.ISnifferHandle;
import com.netifera.platform.net.sniffing.stream.IBlockSnifferHandle;
import com.netifera.platform.net.sniffing.stream.IStreamSnifferHandle;
import com.netifera.platform.net.sniffing.util.ISniffingEngineEx;
public class TCPManager {
private final Set<IStreamSnifferHandle> streamHandles =
Collections.synchronizedSet( new HashSet<IStreamSnifferHandle>());
private final Set <IBlockSnifferHandle> blockHandles =
Collections.synchronizedSet( new HashSet<IBlockSnifferHandle>());
/* Currently active sessions that we are tracking */
private final Map<TCPSessionKey, TCPSession> sessionMap =
new ConcurrentHashMap<TCPSessionKey, TCPSession>();
/* Sniffer handle for receiving incoming IP level traffic */
private final IPacketSnifferHandle<IPv4> ipv4Handle;
private final IPacketSnifferHandle<IPv6> ipv6Handle;
/* The interface this manager belongs to */
private final ICaptureInterface captureInterface;
/* The sniffing engine this manager belongs to */
private final ISniffingEngineEx engine;
private final TCPReassemblyConfig config;
/* When BPF is finally working, we'll use this */
private final IPacketFilter filter = null;
private final ILogger logger;
private boolean disposed;
private boolean started;
public TCPManager(IPacketManager<IPacketHeader> packetManager,
IPv4Manager ipv4Manager, IPv6Manager ipv6Manager) {
this.engine = packetManager.getSniffingEngine();
logger = engine.getLogger();
if(System.getProperty("netifera.debug.tcp") != null){
logger.enableDebug();
}
this.captureInterface = packetManager.getInterface();
ipv4Handle = ipv4Manager == null ? null :
new SnifferHandle<IPv4>(ipv4Manager, filter,
new IPacketSniffer<IPv4>() {
public void handlePacket(IPv4 packet, IPacketContext ctx) {
handleIP(packet, ctx);
}
});
ipv6Handle = ipv6Manager == null ? null :
new SnifferHandle<IPv6>(ipv6Manager, filter,
new IPacketSniffer<IPv6>() {
public void handlePacket(IPv6 packet, IPacketContext ctx) {
handleIP(packet, ctx);
}
});
config = new TCPReassemblyConfig();
}
ISniffingEngineEx getSniffingEngine() {
return engine;
}
ICaptureInterface getInterface() {
return captureInterface;
}
/*
* Called when IP packets arrive.
*/
private void handleIP(IP ip, IPacketContext ctx) {
final TCP tcp = (TCP) ip.findHeader(TCP.class);
if(tcp == null) {
return;
}
final TCPSessionKey key = new TCPSessionKey(ip, tcp);
final long currentTimestamp = ctx.getCaptureHeader().getSeconds();
assert(currentTimestamp != 0);
/*
* If a session does not already exist for this key,
* and this is a SYN packet, create a new session.
*/
if(!sessionMap.containsKey(key)) {
/* ignore the packet if isn't part of a session and SYN isn't set */
if(!tcp.getSYN()) {
return;
}
if(!isBelowSessionLimit()) {
logger.warning("Failed to create TCPSession because limit of " + config.getMaximumSessionCount()
+ " sessions has been reached");
return;
}
createSession(key, currentTimestamp, ctx.getPacketTag());
}
assert(sessionMap.containsKey(key));
final TCPSession session = sessionMap.get(key);
if(session.addSegment(tcp, currentTimestamp)) {
logger.debug("Session closed normally for " + key + " (" + sessionMap.size() + ")");
sessionMap.remove(key);
}
processSessionTimeouts(currentTimestamp);
}
private void processSessionTimeouts(long currentTimestamp) {
for(Iterator<Entry<TCPSessionKey, TCPSession>> iter = sessionMap.entrySet().iterator(); iter.hasNext(); ) {
TCPSession session = iter.next().getValue();
if(session.isClosedOnTimeout(currentTimestamp)) {
iter.remove();
}
}
}
private boolean isBelowSessionLimit() {
return sessionMap.size() < config.getMaximumSessionCount();
}
private synchronized void createSession(TCPSessionKey key, long timestamp, Object packetTag) {
sessionMap.put(key, new TCPSession(key, config, logger, timestamp, packetTag, streamHandles, blockHandles));
logger.debug("Session added for " + key + " (" + sessionMap.size() + ")");
}
/* Start manager and begin receiving packets from IP layer */
private void start() {
if(started) {
return;
}
if (ipv4Handle != null) {
ipv4Handle.register();
}
if (ipv6Handle != null) {
ipv6Handle.register();
}
started = true;
}
/* Stop manager and stop receiving packets from IP layer */
private void stop() {
if(!started) {
return;
}
if (ipv4Handle != null) {
ipv4Handle.unregister();
}
if (ipv6Handle != null) {
ipv6Handle.unregister();
}
started = false;
}
public synchronized void registerStreamHandle(IStreamSnifferHandle handle) {
if(canRegisterHandle(handle, streamHandles)) {
streamHandles.add(handle);
if(!started) {
start();
}
}
}
public synchronized void registerBlockHandle(IBlockSnifferHandle handle) {
if(canRegisterHandle(handle, blockHandles)) {
blockHandles.add(handle);
if(!started) {
start();
}
}
}
public synchronized void unregisterStreamHandle(IStreamSnifferHandle handle) {
if(!unregisterHandle(handle, streamHandles)) {
return;
}
for(TCPSession session : sessionMap.values()) {
session.unregisterStreamHandle(handle);
}
}
public synchronized void unregisterBlockHandle(IBlockSnifferHandle handle) {
if(!unregisterHandle(handle, blockHandles)) {
return;
}
for(TCPSession session : sessionMap.values()) {
session.unregisterBlockHandle(handle);
}
}
private boolean canRegisterHandle(ISnifferHandle handle, Set<? extends ISnifferHandle> set) {
if(disposed) {
return false;
}
if(!captureInterface.equals(handle.getInterface())) {
throw new IllegalArgumentException("Mismatched interface registering handle");
}
if(set.contains(handle)) {
logger.warning("Ignoring duplicate registration of sniffer handle");
return false;
}
return true;
}
private boolean unregisterHandle(ISnifferHandle handle, Set<? extends ISnifferHandle> set) {
if(disposed) {
return false;
}
if(!captureInterface.equals(handle.getInterface())) {
throw new IllegalArgumentException("Mismatched interface unregistering handle");
}
if(!set.contains(handle)) {
logger.warning("Ignoring attempt to unregister a handle which was not previously registered");
return false;
}
set.remove(handle);
if(streamHandles.isEmpty() && blockHandles.isEmpty() && started) {
stop();
}
return true;
}
}