* BuildHandler: Prelmiinary participating tunnel throttler

This commit is contained in:
zzz
2011-02-22 23:34:02 +00:00
parent 4967f8de3c
commit 79c68c5636
4 changed files with 96 additions and 8 deletions

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 16;
public final static long BUILD = 17;
/** for example "-test" */
public final static String EXTRA = "-rc";

View File

@ -37,18 +37,19 @@ import net.i2p.util.Log;
*
*/
class BuildHandler {
private RouterContext _context;
private Log _log;
private BuildExecutor _exec;
private Job _buildMessageHandlerJob;
private Job _buildReplyMessageHandlerJob;
private final RouterContext _context;
private final Log _log;
private final BuildExecutor _exec;
private final Job _buildMessageHandlerJob;
private final Job _buildReplyMessageHandlerJob;
/** list of BuildMessageState, oldest first */
private final List<BuildMessageState> _inboundBuildMessages;
/** list of BuildReplyMessageState, oldest first - unused unless HANDLE_REPLIES_INLINE == false */
private final List<BuildReplyMessageState> _inboundBuildReplyMessages;
/** list of BuildEndMessageState, oldest first - unused unless HANDLE_REPLIES_INLINE == false */
private final List<BuildEndMessageState> _inboundBuildEndMessages;
private BuildMessageProcessor _processor;
private final BuildMessageProcessor _processor;
private final ParticipatingThrottler _throttler;
private static final boolean HANDLE_REPLIES_INLINE = true;
@ -101,6 +102,7 @@ class BuildHandler {
ctx.inNetMessagePool().registerHandlerJobBuilder(TunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
_throttler = new ParticipatingThrottler(ctx);
}
private static final int MAX_HANDLE_AT_ONCE = 2;
@ -498,6 +500,7 @@ class BuildHandler {
long nextId = req.readNextTunnelId();
boolean isInGW = req.readIsInboundGateway();
boolean isOutEnd = req.readIsOutboundEndpoint();
// time is in hours, and only for log below - what's the point?
// tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it is not.
long time = req.readRequestTime();
@ -531,7 +534,7 @@ class BuildHandler {
_context.statManager().addRateData("tunnel.acceptLoad", recvDelay, recvDelay);
}
}
/*
* Being a IBGW or OBEP generally leads to more connections, so if we are
* approaching our connection limit (i.e. !haveCapacity()),
@ -550,6 +553,28 @@ class BuildHandler {
_context.throttle().setTunnelStatus(_x("Rejecting tunnels: Connection limit"));
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
// Check participating throttle counters for previous and next hops
// This is at the end as it compares to a percentage of created tunnels.
// We may need another counter above for requests.
if (response == 0 && !isInGW) {
Hash from = state.fromHash;
if (from == null)
from = state.from.calculateHash();
if (_throttler.shouldThrottle(from)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting tunnel (hop throttle), previous hop: " + from);
// no setTunnelStatus() indication
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
}
if (response == 0 && (!isOutEnd) &&
_throttler.shouldThrottle(req.readNextIdentity())) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting tunnel (hop throttle), next hop: " + req.readNextIdentity());
// no setTunnelStatus() indication
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Responding to " + state.msg.getUniqueId() + "/" + ourId

View File

@ -0,0 +1,57 @@
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.SimpleScheduler;
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 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 = 81 / LIFETIME_PORTION;
private static final int PERCENT_LIMIT = 12 / LIFETIME_PORTION;
private static final long CLEAN_TIME = 10*60*1000 / LIFETIME_PORTION;
ParticipatingThrottler(RouterContext ctx) {
this.context = ctx;
this.counter = new ObjectCounter();
SimpleScheduler.getInstance().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();
}
}
}