/******************************************************************************* * 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.dataStructure; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.RandomAccessFile; import java.io.Writer; import java.nio.channels.Channels; import java.util.PriorityQueue; import java.util.Vector; import staticContent.evaluation.traceParser.engine.Protocol; import staticContent.evaluation.traceParser.engine.TraceInfo; import staticContent.evaluation.traceParser.engine.comparator.TraceEntry; import staticContent.evaluation.traceParser.engine.fileReader.CountingBufferedReader; import staticContent.framework.util.Util; public class Flow { public enum Restriction {NONE, SIMPLE_DELAY, NOT_BEFORE_END_OF_OTHER_FLOW, NOT_BEFORE_END_OF_TRANSACTION}; public enum FlowDirection {UNKNOWN, TO_WAN, FROM_WAN}; // when captured on an internet gateway: who established the connection? a local host or one in the WAN? public static int flowIdCounter = 0; public int senderId; public int flowId; public long startOfFlow; // start of flow (as offset from startOftrace) (ms accuracy) public long endOfFlow; // end of flow (as offset from startOftrace) (ms accuracy) public int requestSize; // payload of highest level protocol only (e.g. HTTP) public int replySize; public String senderAddress; // e.g. IPv4 or IPv6 address as string public String receiverAddress; // e.g. IPv4 or IPv6 address as string public int receiverID; public int senderPort; public int receiverPort; // e.g. 80 for http public Protocol layer4protocol; public String protocolAsString; public FlowDirection flowDirection; public Vector<ExtendedTransaction> transactions = new Vector<ExtendedTransaction>(); // not serialized: public Restriction restriction; public int idOfRestrictingFlow = Util.NOT_SET; public int idOfRestrictingTransaction = Util.NOT_SET; public int idOfRestrictingReply = Util.NOT_SET; public int offsetFromRestriction = Util.NOT_SET; // last flow, last restricting flow or last restricting transaction (depends on restriction) //public int notBeforeRestriction = Util.NOT_SET; // contains the flowId of the latest ENDING flow that must be completed before this flow can be replayed //public int offsetFromRestrictingFlow = Util.NOT_SET; // if notBeforeRestriction is given, this variable indicates the time that should be waited after the restricting flow is finished until this flow is replayed //public int offsetFromLastFlow = Util.NOT_SET; // if notBeforeRestriction is NOT given, this variable indicates the time that should be waited after starting the replaying of the last flow until this flow is replayed public Flow() { this.flowId = flowIdCounter++; } public Flow(RandomAccessFile raf, long offsetInTraceFile) { try { raf.seek(offsetInTraceFile); // use BufferedReader to read line instead of raf.readLine() for performance reasons: BufferedReader reader = new BufferedReader(Channels.newReader(raf.getChannel(), "ISO-8859-1")); String line = reader.readLine(); init(line); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("could not load flows from " +raf); } } public Flow(String serializedFlow) { init(serializedFlow); } public void init(String serializedFlow) { serializedFlow = Util.removeLineBreakAtEnd(serializedFlow); String[] columns = serializedFlow.split("#"); if (columns.length < 15) throw new RuntimeException("unrecognized trace file format: " +serializedFlow); this.senderId = Integer.parseInt(columns[0]); this.flowId = Integer.parseInt(columns[1]); this.startOfFlow = Long.parseLong(columns[2]); this.endOfFlow = Long.parseLong(columns[3]); this.requestSize = Integer.parseInt(columns[4]); this.replySize = Integer.parseInt(columns[5]); this.senderAddress = columns[6]; this.receiverAddress = columns[7]; this.receiverID = Integer.parseInt(columns[8]); this.senderPort = Integer.parseInt(columns[9]); this.receiverPort = Integer.parseInt(columns[10]); this.layer4protocol = Protocol.valueOf(columns[11]); this.protocolAsString = columns[12]; this.flowDirection = FlowDirection.valueOf(columns[13]); int numberOfTransactions = columns.length - 14; for (int i=0; i<numberOfTransactions; i++) transactions.add(new ExtendedTransaction(columns[14 + i])); } /** * returns whether this flow ends before the START of the bypassed flow or * not (not the end of the bypassed flow) */ public boolean endsBefore(Flow flow) { return this.endOfFlow <= flow.startOfFlow; } /** * returns whether this flow ends after (the end of) the bypassed flow or * not */ public boolean endsAfter(Flow flow) { return this.endOfFlow >= flow.endOfFlow; } /** * returns whether this flow start before the bypassed flow or not */ public boolean startsBefore(Flow flow) { return this.startOfFlow <= flow.startOfFlow; } /** * returns whether this flow starts after the END of the bypassed flow or * not (not the start of the bypassed flow) */ public boolean startsAfter(Flow flow) { return this.startOfFlow >= flow.endOfFlow; } /** * returns whether this and the bypassed flow were open at the same time. */ public boolean isOverlapping(Flow flow) { if (startsAfter(flow) || endsBefore(flow)) return false; return true; } public Vector<Flow> getOverlapping(Vector<Flow> flowsToCompare) { Vector<Flow> overlapping = new Vector<Flow>(); for (Flow flow: flowsToCompare) if (isOverlapping(flow)) overlapping.add(flow); return overlapping; } public void resetStart(long newStart) { long dif = -1 * (this.startOfFlow - newStart); this.startOfFlow += dif; this.endOfFlow += dif; for (ExtendedTransaction transaction: transactions) transaction.resetStart(transaction.startOfRequest + dif); } /** * removes any transactions of this flow that start after "maxEnd". * * @return returns whether the cut off was successful (true) or not * (false). a not successful cut (false) means that the flow could * not be cut to the desired length as the resulting flow would * contain no more transactions */ public boolean cutOff(long maxEnd) { if (maxEnd >= this.endOfFlow) // already short enough return true; int cutCounter = 0; int newRequestSize = requestSize; int newReplySize = replySize; boolean fill = false; for (int i=transactions.size()-1; i>=0; i--) { ExtendedTransaction transaction = transactions.get(i); if (transaction.startOfRequest >= maxEnd) { // transaction starts later than (or equal to) max end -> drop whole transaction cutCounter++; newRequestSize -= transaction.requestSize; newReplySize -= transaction.getTotalReplySize(); } else if (transaction.getTimestampOfLastActivity() > maxEnd) { // transaction starts before maxEnd and ends later than maxEnd -> try to cut transaction boolean cutSuccessful = transaction.cutOff(maxEnd); if (!cutSuccessful) { // drop whole transaction cutCounter++; newRequestSize -= transaction.requestSize; newReplySize -= transaction.getTotalReplySize(); } break; } else { // no data in the flow a that time fill = true; break; } } if (cutCounter == transactions.size()) { // no transactions left return false; } else { for (int i=0; i<cutCounter; i++) // remove transactions transactions.remove(transactions.size()-1); endOfFlow = fill ? maxEnd : transactions.get(transactions.size()-1).getTimestampOfLastActivity(); requestSize = newRequestSize; replySize = newReplySize; return true; } } public static Flow loadFlow(String pathToTraceFolder, long offset) { return loadFlow(new TraceInfo(pathToTraceFolder), offset); } public static Flow loadFlow(TraceInfo traceInfo, long offset) { String path = Util.removeFileExtension(traceInfo.getPathToTraceFile()) +".gmf"; Flow af = null; try { RandomAccessFile raf = new RandomAccessFile(path, "r"); af = loadFlow(raf, offset); raf.close(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("could not load flows from " +path); } return af; } public static Flow loadFlow(RandomAccessFile raf, long offset) { return new Flow(raf, offset); } @Override public String toString() { return "flow "+flowId +": " +serialize(); } public String serialize() { StringBuffer sb = new StringBuffer(); serialize(sb); return sb.toString(); } public StringBuffer serialize(StringBuffer bufferToAppend) { bufferToAppend.append(senderId +"#"); bufferToAppend.append(flowId +"#"); bufferToAppend.append(startOfFlow +"#"); bufferToAppend.append(endOfFlow +"#"); bufferToAppend.append(requestSize +"#"); bufferToAppend.append(replySize +"#"); bufferToAppend.append(senderAddress +"#"); bufferToAppend.append(receiverAddress +"#"); bufferToAppend.append(receiverID +"#"); bufferToAppend.append(senderPort +"#"); bufferToAppend.append(receiverPort +"#"); bufferToAppend.append(layer4protocol +"#"); bufferToAppend.append(protocolAsString +"#"); bufferToAppend.append(flowDirection.toString() +"#"); for (int i=0; i<transactions.size(); i++) { ExtendedTransaction message = transactions.get(i); bufferToAppend.append(message.serializeExtended()); if (i<(transactions.size()-1)) bufferToAppend.append("#"); } return bufferToAppend; } public void serialize(Writer destination) throws IOException { destination.write(senderId +"#"); destination.write(flowId +"#"); destination.write(startOfFlow +"#"); destination.write(endOfFlow +"#"); destination.write(requestSize +"#"); destination.write(replySize +"#"); destination.write(senderAddress +"#"); destination.write(receiverAddress +"#"); destination.write(receiverID +"#"); destination.write(senderPort +"#"); destination.write(receiverPort +"#"); destination.write(layer4protocol +"#"); destination.write(protocolAsString +"#"); destination.write(flowDirection.toString() +"#"); for (int i=0; i<transactions.size(); i++) { ExtendedTransaction message = transactions.get(i); message.serializeExtended(destination); if (i<(transactions.size()-1)) destination.write("#"); } } public static void sort(String inputFilePath, String outputFilePath) throws IOException { CountingBufferedReader in = null; Writer resultTrace = null; RandomAccessFile raf = null; try { // read and sort index: in = new CountingBufferedReader(inputFilePath); resultTrace = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(new FileOutputStream(outputFilePath)))); PriorityQueue<TraceEntry> entries = new PriorityQueue<TraceEntry>(1000); String currentLine; while (true) { currentLine = in.readLine(); if (currentLine == null) break; entries.add(new TraceEntry(currentLine, in.getPositionOfLastLine())); } in.close(); // write result trace (restore flows from source trace through index and write to dest. trace): raf = new RandomAccessFile(inputFilePath, "r"); int flowCounter = 0; while (true) { TraceEntry currentEntry = entries.poll(); if (currentEntry == null) break; Flow af = loadFlow(raf, currentEntry.offset); af.flowId = flowCounter++; af.serialize(resultTrace); resultTrace.write("\n"); } raf.close(); } catch (IOException e) { // close reader + writer and forward exception try { if (in != null) in.close(); if (raf != null) raf.close(); if (resultTrace != null) resultTrace.close(); } catch (Exception e1) {} throw e; } resultTrace.close(); } public void reuse() { this.flowId = 0; this.senderId = 0; this.startOfFlow = 0; this.requestSize = 0; this.replySize = 0; this.senderAddress = null; this.receiverAddress = null; this.receiverID = 0; this.senderPort = 0; this.receiverPort = 0; this.layer4protocol = null; this.protocolAsString = null; this.flowDirection = null; this.restriction = null; this.idOfRestrictingFlow = Util.NOT_SET; this.idOfRestrictingTransaction = Util.NOT_SET; this.offsetFromRestriction = Util.NOT_SET; this.transactions = new Vector<ExtendedTransaction>(); } /** * for fast access of fields of a serialized flow (without creating an instance) * pos 0: senderId (type: int) (get value with Integer.parseInt(String)) * pos 1: flowId (type: int) (get value with Integer.parseInt(String)) * pos 2: startOfFlow (type: long) (get value with Long.parseLong(String)) * pos 3: endOfFlow (type: long) (get value with Long.parseLong(String)) * pos 4: requestSize (type: int) (get value with Integer.parseInt(String)) * pos 5: replySize (type: int) (get value with Integer.parseInt(String)) * pos 6: senderAddress (type: String) * pos 7: receiverAddress (type: String) * pos 8: receiverID (type: int) (get value with Integer.parseInt(String)) * pos 9: senderPort (type: int) (get value with Integer.parseInt(String)) * pos 10: receiverPort (type: int) (get value with Integer.parseInt(String)) * pos 11: layer4protocol (type: Protocol) (get value with Protocol.valueOf(String)) * pos 12: protocolAsString (type: String) * pos 13: flowDirection (type: FlowDirection) (get value with FlowDirection.valueOf(String)) * pos 14 + i: ith transaction (type: ExtendedTransaction) (get value with new ExtendedTransaction(String)) */ public static String extractField(int position, String serializedFlow) { return Util.extractField(position, "#", serializedFlow); } }