package net.i2p.router.tunnel.pool;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.util.ObjectCounter;
import net.i2p.util.SimpleTimer;
/**
* Count how often we have accepted a tunnel with the peer
* as the previous or next hop.
* We limit each peer to a percentage of all participating tunnels,
* subject to minimum and maximum values for the limit.
*
* This offers basic protection against simple attacks
* but is not a complete solution, as by design, we don't know
* the originator of a tunnel request.
*
* This also effectively limits the number of tunnels between
* any given pair of routers, which probably isn't a bad thing.
*
* Note that the actual limits will be higher than specified
* by up to 1 / LIFETIME_PORTION because the counter window resets.
*
* Note that the counts are of previous + next hops, so the total will
* be higher than the participating tunnel count, and will also grow
* as the network uses more 3-hop tunnels.
*
* @since 0.8.4
*/
class ParticipatingThrottler {
private final RouterContext context;
private final ObjectCounter<Hash> counter;
/** portion of the tunnel lifetime */
private static final int LIFETIME_PORTION = 3;
private static final int MIN_LIMIT = 18 / LIFETIME_PORTION;
private static final int MAX_LIMIT = 66 / LIFETIME_PORTION;
private static final int PERCENT_LIMIT = 12 / LIFETIME_PORTION;
private static final long CLEAN_TIME = 11*60*1000 / LIFETIME_PORTION;
ParticipatingThrottler(RouterContext ctx) {
this.context = ctx;
this.counter = new ObjectCounter<Hash>();
ctx.simpleTimer2().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
}
/** increments before checking */
boolean shouldThrottle(Hash h) {
int numTunnels = this.context.tunnelManager().getParticipatingCount();
int limit = Math.max(MIN_LIMIT, Math.min(MAX_LIMIT, numTunnels * PERCENT_LIMIT / 100));
return this.counter.increment(h) > limit;
}
private class Cleaner implements SimpleTimer.TimedEvent {
public void timeReached() {
ParticipatingThrottler.this.counter.clear();
}
}
}