/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package staticContent.evaluation.traceParser.engine.converter; import java.util.HashMap; import java.util.HashSet; import java.util.Vector; import staticContent.evaluation.traceParser.engine.dataStructure.Packet; import staticContent.evaluation.traceParser.engine.dataStructure.Packet.TCPflags; import staticContent.evaluation.traceParser.engine.fileReader.PacketIterator; import staticContent.evaluation.traceParser.engine.fileReader.PacketSource; import staticContent.evaluation.traceParser.engine.filter.PacketFilter; public class TCPflowFinder implements PacketFilter { private HashMap<String, FlowRecord> tempFlows = new HashMap<String, FlowRecord>(250000); private Vector<FlowRecord> removals = new Vector<FlowRecord>(10000); private PacketFilter filter; private HashSet<String> result = new HashSet<String>(250000); public TCPflowFinder() { this(null); } public TCPflowFinder(PacketFilter filter) { this.filter = filter; } @Override public Packet newRecord(Packet packet) { // clean up: if (packet.getSequenceNumber() % 1000000 == 0) { // remove dead entries for (FlowRecord flow:tempFlows.values()) if ( (packet.getTimestamp().getTimeInMillis() - flow.startOfFlow) > 5000 && !flow.handshakeComplete ) // no complete handshake after 5 seconds removals.add(flow); System.out.println("TCPflowFinder: read " +packet.getSequenceNumber() +" packets so far. hm-size: " +tempFlows.size() +" - " +removals.size()); for (FlowRecord flowToRemove:removals) tempFlows.remove(flowToRemove); removals.clear(); } // filter: if (!isAllowed(packet)) // ignore packets filtered by white list return null; boolean isSyn = isSyn(packet); boolean isFin = isFin(packet); if (!isSyn && !isFin) // ignore none connection establish/teardown packets return null; // extract information: String flowIdentifier = getFlowIdentifier(packet); FlowRecord flow = tempFlows.get(flowIdentifier); if (isSyn && flow == null) { // first syn flow = new FlowRecord(); flow.startOfFlow = packet.getTimestamp().getTimeInMillis(); tempFlows.put(flowIdentifier, flow); } else if (isSyn) { // second syn flow.handshakeComplete = true; } else if (isFin && flow == null) { // fin for unknown connection (malformed packet or start of flow not covered in trace) return null; } else if (isFin && flow.finCtr == 0) { // first fin flow.finCtr++; } else if (isFin && flow.finCtr == 1) { // second fin -> store flow identifier tempFlows.remove(flowIdentifier); result.add(flowIdentifier); } else System.err.println("should not happen"); return packet; } @Override public void finished() { tempFlows.clear(); tempFlows = null; removals.clear(); removals = null; if (filter != null) filter.finished(); } private boolean isAllowed(Packet packet) { if (filter == null) return true; return filter.newRecord(packet) != null; } private boolean isSyn(Packet packet) { if (packet.getTCPflags() == TCPflags.SYN) return true; if (packet.getTCPflags() == TCPflags.SYN_ACK) return true; return false; } private boolean isFin(Packet packet) { if (packet.getTCPflags() == TCPflags.FIN) return true; if (packet.getTCPflags() == TCPflags.FIN_ACK) return true; return false; } private class FlowRecord { long startOfFlow; int finCtr = 0; boolean handshakeComplete; } public HashSet<String> getFlows() { return result; } // not able to distinguish between 2 different flows, that are established between the same two hosts, with opposite ports (e.g. both run a web server on port 80 + both operating systems choose port 60015 for the client side... should happen very rarely) public static String getFlowIdentifier(Packet packet) { if (packet.getLayer2srcAddress().compareTo(packet.getLayer2dstAddress()) < 0) // src address is smaller return packet.getLayer2srcAddress() +":" +packet.getLayer3srcAddress() +"-" +packet.getLayer2dstAddress() +":" +packet.getLayer3dstAddress(); else return packet.getLayer2dstAddress() +":" +packet.getLayer3dstAddress() +"-" +packet.getLayer2srcAddress() +":" +packet.getLayer3srcAddress(); } public static HashSet<String> findFlows(PacketSource packetSource) { return findFlows(packetSource, null); } public static HashSet<String> findFlows(PacketSource packetSource, PacketFilter filter) { TCPflowFinder flowFinder = new TCPflowFinder(filter); PacketIterator iterator = new PacketIterator(packetSource, flowFinder); while (iterator.hasNext()) { iterator.next(); } packetSource.close(); if (filter != null) filter.finished(); return flowFinder.getFlows(); } }