/* Copyright 2007 Freenet Project Inc. * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package freenet.io; import freenet.node.FSParseException; import freenet.support.Logger; import freenet.support.SimpleFieldSet; /** * Tracks communication to/from a specific address. That address can be a specific IP:port, a specific IP, * or some completely different type of address, so we don't store it in this class; subclasses will do. * @author toad */ public class AddressTrackerItem { /** The time at which the first packet was received from this address. */ private long timeFirstReceivedPacket; /** The time at which the first packet was sent to this address. */ private long timeFirstSentPacket; /** The earliest time, before timeFirstReceivedPacket, at which we know for * certain that there was no packet received. This is typically the startup * time of the server socket. It may be later if the cache has to be * flushed. */ private long timeDefinitelyNoPacketsReceived; /** The earliest time, before timeFirstSentPacket, at which we know for * certain that there was no packet sent. This is typically the startup * time of the node. It may be later if the cache has to be flushed. */ private long timeDefinitelyNoPacketsSent; /** The time at which we received the most recent packet */ private long timeLastReceivedPacket; /** The time at which we sent the most recent packet */ private long timeLastSentPacket; /** The total number of packets sent to this address */ private long packetsSent; /** The total number of packets received from this address */ private long packetsReceived; public static final int TRACK_GAPS = 5; private long[] gapLengths; private long[] gapLengthRecvTimes; private static final long GAP_THRESHOLD = AddressTracker.MAYBE_TUNNEL_LENGTH; static final boolean INCLUDE_RECEIVED_PACKETS = true; public AddressTrackerItem(long timeDefinitelyNoPacketsReceived, long timeDefinitelyNoPacketsSent) { timeFirstReceivedPacket = -1; timeFirstSentPacket = -1; timeLastReceivedPacket = -1; timeLastSentPacket = -1; packetsSent = 0; packetsReceived = 0; this.timeDefinitelyNoPacketsReceived = timeDefinitelyNoPacketsReceived; this.timeDefinitelyNoPacketsSent = timeDefinitelyNoPacketsSent; gapLengths = new long[TRACK_GAPS]; gapLengthRecvTimes = new long[TRACK_GAPS]; } public AddressTrackerItem(SimpleFieldSet fs) throws FSParseException { timeFirstReceivedPacket = fs.getLong("TimeFirstReceivedPacket"); timeFirstSentPacket = fs.getLong("TimeFirstSentPacket"); timeDefinitelyNoPacketsSent = fs.getLong("TimeDefinitelyNoPacketsSent"); timeDefinitelyNoPacketsReceived = fs.getLong("TimeDefinitelyNoPacketsReceived"); timeLastReceivedPacket = fs.getLong("TimeLastReceivedPacket"); timeLastSentPacket = fs.getLong("TimeLastSentPacket"); packetsSent = fs.getLong("PacketsSent"); packetsReceived = fs.getLong("PacketsReceived"); SimpleFieldSet gaps = fs.getSubset("Gaps"); gapLengths = new long[TRACK_GAPS]; gapLengthRecvTimes = new long[TRACK_GAPS]; for(int i=0;i<TRACK_GAPS;i++) { SimpleFieldSet gap = gaps.subset(Integer.toString(i)); if(gap == null) { Logger.normal(this, "No more gaps at i="+i+" - TRACK_GAPS changed??"); break; } gapLengths[i] = gap.getLong("Length"); gapLengthRecvTimes[i] = gap.getLong("Received"); } } public synchronized void sentPacket(long now) { packetsSent++; if(timeFirstSentPacket < 0) timeFirstSentPacket = now; timeLastSentPacket = now; } public synchronized void receivedPacket(long now) { packetsReceived++; if(timeFirstReceivedPacket < 0) timeFirstReceivedPacket = now; long oldTimeLastReceivedPacket = timeLastReceivedPacket; timeLastReceivedPacket = now; // Establish the interval long startTime; startTime = timeLastSentPacket; startTime = Math.max(startTime, timeDefinitelyNoPacketsSent); if(INCLUDE_RECEIVED_PACKETS) { startTime = Math.max(startTime, oldTimeLastReceivedPacket); startTime = Math.max(startTime, timeDefinitelyNoPacketsReceived); } if(startTime <= 0) return; // No information if(now - startTime > GAP_THRESHOLD) { // Not necessarily a new gap // If no packets sent since last one, just replace it if(timeLastSentPacket >= gapLengthRecvTimes[0]) { // Rotate gaps array System.arraycopy(gapLengths, 0, gapLengths, 1, TRACK_GAPS-1); System.arraycopy(gapLengthRecvTimes, 0, gapLengthRecvTimes, 1, TRACK_GAPS-1); } else { // else overwrite [0] } gapLengths[0] = (now - startTime); gapLengthRecvTimes[0] = now; } } public synchronized boolean hasLongTunnel(long horizon) { return gapLengthRecvTimes[0] > System.currentTimeMillis() - horizon; } public long longestGap(long horizon, long now) { long longestGap = -1; for(int i=0;i<TRACK_GAPS;i++) { if(gapLengthRecvTimes[i] < now - horizon) break; longestGap = Math.max(longestGap, gapLengths[i]); } return longestGap; } public static class Gap { public final long gapLength; public final long receivedPacketAt; Gap(long gapLength, long receivedPacketAt) { this.gapLength = gapLength; this.receivedPacketAt = receivedPacketAt; } } public synchronized Gap[] getGaps() { Gap[] gaps = new Gap[TRACK_GAPS]; for(int i=0;i<TRACK_GAPS;i++) { gaps[i] = new Gap(gapLengths[i], gapLengthRecvTimes[i]); } return gaps; } public synchronized long firstReceivedPacket() { return timeFirstReceivedPacket; } public synchronized long firstSentPacket() { return timeFirstSentPacket; } public synchronized long lastReceivedPacket() { return timeLastReceivedPacket; } public synchronized long lastSentPacket() { return timeLastSentPacket; } public synchronized long timeDefinitelyNoPacketsSent() { return timeDefinitelyNoPacketsSent; } public synchronized long timeDefinitelyNoPacketsReceived() { return timeDefinitelyNoPacketsReceived; } public synchronized long packetsSent() { return packetsSent; } public synchronized long packetsReceived() { return packetsReceived; } public synchronized boolean weSentFirst() { if(timeFirstReceivedPacket == -1) return true; if(timeFirstSentPacket == -1) return false; return timeFirstSentPacket < timeFirstReceivedPacket; } public synchronized long timeFromStartupToFirstSentPacket() { if(packetsSent == 0) return -1; return timeFirstSentPacket - timeDefinitelyNoPacketsSent; } public synchronized long timeFromStartupToFirstReceivedPacket() { if(packetsReceived == 0) return -1; return timeFirstReceivedPacket - timeDefinitelyNoPacketsReceived; } public SimpleFieldSet toFieldSet() { SimpleFieldSet fs = new SimpleFieldSet(true); fs.put("TimeFirstReceivedPacket", timeFirstReceivedPacket); fs.put("TimeFirstSentPacket", timeFirstSentPacket); fs.put("TimeDefinitelyNoPacketsSent", timeDefinitelyNoPacketsSent); fs.put("TimeDefinitelyNoPacketsReceived", timeDefinitelyNoPacketsReceived); fs.put("TimeLastReceivedPacket", timeLastReceivedPacket); fs.put("TimeLastSentPacket", timeLastSentPacket); fs.put("PacketsSent", packetsSent); fs.put("PacketsReceived", packetsReceived); SimpleFieldSet gaps = new SimpleFieldSet(true); for(int i=0;i<TRACK_GAPS;i++) { SimpleFieldSet gap = new SimpleFieldSet(true); gap.put("Length", gapLengths[i]); gap.put("Received", gapLengthRecvTimes[i]); gaps.put(Integer.toString(i), gap); } fs.put("Gaps", gaps); return fs; } }