forked from I2P_Developers/i2p.i2p
* BuildHandler: Prelmiinary participating tunnel throttler
This commit is contained in:
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user