forked from I2P_Developers/i2p.i2p
* Tunnels: Implement random discard to enforce share limit
This commit is contained in:
@ -53,7 +53,7 @@ public class StatManager {
|
|||||||
"tunnel.buildRatio.*,tunnel.buildFailure,tunnel.buildSuccess,tunnel.corruptMessage," +
|
"tunnel.buildRatio.*,tunnel.buildFailure,tunnel.buildSuccess,tunnel.corruptMessage," +
|
||||||
"tunnel.decryptRequestTime,tunnel.fragmentedDropped,tunnel.participatingMessageCount,"+
|
"tunnel.decryptRequestTime,tunnel.fragmentedDropped,tunnel.participatingMessageCount,"+
|
||||||
"tunnel.participatingTunnels,tunnel.testFailedTime,tunnel.testSuccessTime," +
|
"tunnel.participatingTunnels,tunnel.testFailedTime,tunnel.testSuccessTime," +
|
||||||
"udp.sendPacketSize,udp.packetsRetransmitted" ;
|
"tunnel.participatingBandwidth,udp.sendPacketSize,udp.packetsRetransmitted" ;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The stat manager should only be constructed and accessed through the
|
* The stat manager should only be constructed and accessed through the
|
||||||
|
14
history.txt
14
history.txt
@ -1,3 +1,17 @@
|
|||||||
|
2008-10-10 zzz
|
||||||
|
* Profiles: Reduce reject penalty in
|
||||||
|
capacity calculation to avoid a congestion collapse
|
||||||
|
* Throttle: Change reject to BANDWIDTH from CRIT on shutdown
|
||||||
|
for improved anonymity
|
||||||
|
* Tunnels: Implement random discard to enforce share limit
|
||||||
|
* Tunnel Tests: Add time for outbound delay, to avoid
|
||||||
|
congestion collapse
|
||||||
|
* UDPPacketReader: Adjust logging
|
||||||
|
* build files: Change to source=1.5, target=1.5
|
||||||
|
* configpeer.jsp: Table cleanup
|
||||||
|
* i2psnark: Change default tunnel length from 1+1 to 2+0
|
||||||
|
* peers.jsp: Change <,> to in,out for UDP
|
||||||
|
|
||||||
2008-10-09 sponge
|
2008-10-09 sponge
|
||||||
* Update version to -3
|
* Update version to -3
|
||||||
* BOB database threadlocking fixes
|
* BOB database threadlocking fixes
|
||||||
|
@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
|
|||||||
public class RouterVersion {
|
public class RouterVersion {
|
||||||
public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
|
public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
|
||||||
public final static String VERSION = "0.6.4";
|
public final static String VERSION = "0.6.4";
|
||||||
public final static long BUILD = 3;
|
public final static long BUILD = 4;
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||||
System.out.println("Router ID: " + RouterVersion.ID);
|
System.out.println("Router ID: " + RouterVersion.ID);
|
||||||
|
@ -28,6 +28,8 @@ public class HopConfig {
|
|||||||
private Map _options;
|
private Map _options;
|
||||||
private long _messagesProcessed;
|
private long _messagesProcessed;
|
||||||
private long _oldMessagesProcessed;
|
private long _oldMessagesProcessed;
|
||||||
|
private long _messagesSent;
|
||||||
|
private long _oldMessagesSent;
|
||||||
|
|
||||||
/** IV length for {@link #getReplyIV} */
|
/** IV length for {@link #getReplyIV} */
|
||||||
public static final int REPLY_IV_LENGTH = 16;
|
public static final int REPLY_IV_LENGTH = 16;
|
||||||
@ -44,6 +46,8 @@ public class HopConfig {
|
|||||||
_options = null;
|
_options = null;
|
||||||
_messagesProcessed = 0;
|
_messagesProcessed = 0;
|
||||||
_oldMessagesProcessed = 0;
|
_oldMessagesProcessed = 0;
|
||||||
|
_messagesSent = 0;
|
||||||
|
_oldMessagesSent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** what tunnel ID are we receiving on? */
|
/** what tunnel ID are we receiving on? */
|
||||||
@ -115,6 +119,7 @@ public class HopConfig {
|
|||||||
public void setOptions(Map options) { _options = options; }
|
public void setOptions(Map options) { _options = options; }
|
||||||
|
|
||||||
/** take note of a message being pumped through this tunnel */
|
/** take note of a message being pumped through this tunnel */
|
||||||
|
/** "processed" is for incoming and "sent" is for outgoing (could be dropped in between) */
|
||||||
public void incrementProcessedMessages() { _messagesProcessed++; }
|
public void incrementProcessedMessages() { _messagesProcessed++; }
|
||||||
public long getProcessedMessagesCount() { return _messagesProcessed; }
|
public long getProcessedMessagesCount() { return _messagesProcessed; }
|
||||||
public long getRecentMessagesCount() {
|
public long getRecentMessagesCount() {
|
||||||
@ -122,6 +127,13 @@ public class HopConfig {
|
|||||||
_oldMessagesProcessed = _messagesProcessed;
|
_oldMessagesProcessed = _messagesProcessed;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
public void incrementSentMessages() { _messagesSent++; }
|
||||||
|
public long getSentMessagesCount() { return _messagesSent; }
|
||||||
|
public long getRecentSentMessagesCount() {
|
||||||
|
long rv = _messagesSent - _oldMessagesSent;
|
||||||
|
_oldMessagesSent = _messagesSent;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer buf = new StringBuffer(64);
|
StringBuffer buf = new StringBuffer(64);
|
||||||
|
@ -22,6 +22,8 @@ public class InboundGatewayReceiver implements TunnelGateway.Receiver {
|
|||||||
return receiveEncrypted(encrypted, false);
|
return receiveEncrypted(encrypted, false);
|
||||||
}
|
}
|
||||||
public long receiveEncrypted(byte[] encrypted, boolean alreadySearched) {
|
public long receiveEncrypted(byte[] encrypted, boolean alreadySearched) {
|
||||||
|
if (!alreadySearched)
|
||||||
|
_config.incrementProcessedMessages();
|
||||||
if (_target == null) {
|
if (_target == null) {
|
||||||
_target = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
|
_target = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
|
||||||
if (_target == null) {
|
if (_target == null) {
|
||||||
@ -33,7 +35,9 @@ public class InboundGatewayReceiver implements TunnelGateway.Receiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_config.incrementProcessedMessages();
|
if (_context.tunnelDispatcher().shouldDropParticipatingMessage())
|
||||||
|
return -1;
|
||||||
|
_config.incrementSentMessages();
|
||||||
TunnelDataMessage msg = new TunnelDataMessage(_context);
|
TunnelDataMessage msg = new TunnelDataMessage(_context);
|
||||||
msg.setData(encrypted);
|
msg.setData(encrypted);
|
||||||
msg.setTunnelId(_config.getSendTunnel());
|
msg.setTunnelId(_config.getSendTunnel());
|
||||||
|
@ -41,6 +41,11 @@ public class OutboundTunnelEndpoint {
|
|||||||
+ " to be forwarded on to "
|
+ " to be forwarded on to "
|
||||||
+ (toRouter != null ? toRouter.toBase64().substring(0,4) : "")
|
+ (toRouter != null ? toRouter.toBase64().substring(0,4) : "")
|
||||||
+ (toTunnel != null ? toTunnel.getTunnelId() + "" : ""));
|
+ (toTunnel != null ? toTunnel.getTunnelId() + "" : ""));
|
||||||
|
// don't drop it if we are the target
|
||||||
|
if ((!_context.routerHash().equals(toRouter)) &&
|
||||||
|
_context.tunnelDispatcher().shouldDropParticipatingMessage())
|
||||||
|
return;
|
||||||
|
_config.incrementSentMessages();
|
||||||
_outDistributor.distribute(msg, toRouter, toTunnel);
|
_outDistributor.distribute(msg, toRouter, toTunnel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ import net.i2p.router.Router;
|
|||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.Service;
|
import net.i2p.router.Service;
|
||||||
import net.i2p.router.peermanager.PeerProfile;
|
import net.i2p.router.peermanager.PeerProfile;
|
||||||
|
import net.i2p.stat.Rate;
|
||||||
|
import net.i2p.stat.RateStat;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,6 +112,12 @@ public class TunnelDispatcher implements Service {
|
|||||||
ctx.statManager().createRateStat("tunnel.participatingBandwidth",
|
ctx.statManager().createRateStat("tunnel.participatingBandwidth",
|
||||||
"Participating traffic", "Tunnels",
|
"Participating traffic", "Tunnels",
|
||||||
new long[] { 60*1000l, 60*10*1000l });
|
new long[] { 60*1000l, 60*10*1000l });
|
||||||
|
ctx.statManager().createRateStat("tunnel.participatingBandwidthOut",
|
||||||
|
"Participating traffic", "Tunnels",
|
||||||
|
new long[] { 60*1000l, 60*10*1000l });
|
||||||
|
ctx.statManager().createRateStat("tunnel.participatingMessageDropped",
|
||||||
|
"Dropped for exceeding share limit", "Tunnels",
|
||||||
|
new long[] { 60*1000l, 60*10*1000l });
|
||||||
ctx.statManager().createRateStat("tunnel.participatingMessageCount",
|
ctx.statManager().createRateStat("tunnel.participatingMessageCount",
|
||||||
"How many messages are sent through a participating tunnel?", "Tunnels",
|
"How many messages are sent through a participating tunnel?", "Tunnels",
|
||||||
new long[] { 60*1000l, 60*10*1000l, 60*60*1000l });
|
new long[] { 60*1000l, 60*10*1000l, 60*60*1000l });
|
||||||
@ -550,6 +558,7 @@ public class TunnelDispatcher implements Service {
|
|||||||
int size = participating.size();
|
int size = participating.size();
|
||||||
long count = 0;
|
long count = 0;
|
||||||
long bw = 0;
|
long bw = 0;
|
||||||
|
long bwOut = 0;
|
||||||
long tcount = 0;
|
long tcount = 0;
|
||||||
long tooYoung = _context.clock().now() - 60*1000;
|
long tooYoung = _context.clock().now() - 60*1000;
|
||||||
long tooOld = tooYoung - 9*60*1000;
|
long tooOld = tooYoung - 9*60*1000;
|
||||||
@ -557,6 +566,7 @@ public class TunnelDispatcher implements Service {
|
|||||||
HopConfig cfg = (HopConfig)participating.get(i);
|
HopConfig cfg = (HopConfig)participating.get(i);
|
||||||
long c = cfg.getRecentMessagesCount();
|
long c = cfg.getRecentMessagesCount();
|
||||||
bw += c;
|
bw += c;
|
||||||
|
bwOut += cfg.getRecentSentMessagesCount();
|
||||||
long created = cfg.getCreation();
|
long created = cfg.getCreation();
|
||||||
if (created > tooYoung || created < tooOld)
|
if (created > tooYoung || created < tooOld)
|
||||||
continue;
|
continue;
|
||||||
@ -567,9 +577,64 @@ public class TunnelDispatcher implements Service {
|
|||||||
count = count * 30 / tcount;
|
count = count * 30 / tcount;
|
||||||
_context.statManager().addRateData("tunnel.participatingMessageCount", count, 20*1000);
|
_context.statManager().addRateData("tunnel.participatingMessageCount", count, 20*1000);
|
||||||
_context.statManager().addRateData("tunnel.participatingBandwidth", bw*1024/20, 20*1000);
|
_context.statManager().addRateData("tunnel.participatingBandwidth", bw*1024/20, 20*1000);
|
||||||
|
_context.statManager().addRateData("tunnel.participatingBandwidthOut", bwOut*1024/20, 20*1000);
|
||||||
_context.statManager().addRateData("tunnel.participatingTunnels", size, 0);
|
_context.statManager().addRateData("tunnel.participatingTunnels", size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement random early discard (RED) to enforce the share bandwidth limit.
|
||||||
|
* For now, this does not enforce the available bandwidth,
|
||||||
|
* we leave that to Throttle.
|
||||||
|
* This is similar to the code in ../RouterThrottleImpl.java
|
||||||
|
* We drop in proportion to how far over the limit we are.
|
||||||
|
* Perhaps an exponential function would be better?
|
||||||
|
*/
|
||||||
|
public boolean shouldDropParticipatingMessage() {
|
||||||
|
RateStat rs = _context.statManager().getRate("tunnel.participatingBandwidth");
|
||||||
|
if (rs == null)
|
||||||
|
return false;
|
||||||
|
Rate r = rs.getRate(60*1000);
|
||||||
|
if (r == null)
|
||||||
|
return false;
|
||||||
|
// weight current period higher
|
||||||
|
long count = r.getLastEventCount() + (3 * r.getCurrentEventCount());
|
||||||
|
int bw = 0;
|
||||||
|
if (count > 0)
|
||||||
|
bw = (int) ((r.getLastTotalValue() + (3 * r.getCurrentTotalValue())) / count);
|
||||||
|
else
|
||||||
|
bw = (int) r.getLifetimeAverageValue();
|
||||||
|
|
||||||
|
int usedIn = Math.min(_context.router().get1sRateIn(), _context.router().get15sRateIn());
|
||||||
|
usedIn = Math.min(usedIn, bw);
|
||||||
|
if (usedIn <= 0)
|
||||||
|
return false;
|
||||||
|
int usedOut = Math.min(_context.router().get1sRate(true), _context.router().get15sRate(true));
|
||||||
|
usedOut = Math.min(usedOut, bw);
|
||||||
|
if (usedOut <= 0)
|
||||||
|
return false;
|
||||||
|
int used = Math.min(usedIn, usedOut);
|
||||||
|
int maxKBps = Math.min(_context.bandwidthLimiter().getInboundKBytesPerSecond(),
|
||||||
|
_context.bandwidthLimiter().getOutboundKBytesPerSecond());
|
||||||
|
float share = (float) _context.router().getSharePercentage();
|
||||||
|
|
||||||
|
// start dropping at 95% of the limit
|
||||||
|
float maxBps = maxKBps * share * 1024f * 0.95f;
|
||||||
|
float pctDrop = (used - maxBps) / used;
|
||||||
|
if (pctDrop <= 0)
|
||||||
|
return false;
|
||||||
|
float rand = _context.random().nextFloat();
|
||||||
|
boolean reject = rand <= pctDrop;
|
||||||
|
if (reject) {
|
||||||
|
if (_log.shouldLog(Log.WARN)) {
|
||||||
|
int availBps = (int) (((maxKBps*1024)*share) - used);
|
||||||
|
_log.warn("Drop part. msg. avail/max/used " + availBps + "/" + (int) maxBps + "/"
|
||||||
|
+ used + " %Drop = " + pctDrop);
|
||||||
|
}
|
||||||
|
_context.statManager().addRateData("tunnel.participatingMessageDropped", 1, 0);
|
||||||
|
}
|
||||||
|
return reject;
|
||||||
|
}
|
||||||
|
|
||||||
private static final int DROP_BASE_INTERVAL = 40 * 1000;
|
private static final int DROP_BASE_INTERVAL = 40 * 1000;
|
||||||
private static final int DROP_RANDOM_BOOST = 10 * 1000;
|
private static final int DROP_RANDOM_BOOST = 10 * 1000;
|
||||||
|
|
||||||
@ -685,9 +750,9 @@ public class TunnelDispatcher implements Service {
|
|||||||
// already scheduled for the future, and before this expiration
|
// already scheduled for the future, and before this expiration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO)) {
|
if (_log.shouldLog(Log.DEBUG)) {
|
||||||
long now = getContext().clock().now();
|
long now = getContext().clock().now();
|
||||||
_log.info("Scheduling leave in " + DataHelper.formatDuration(dropTime.longValue()-now) +": " + cfg);
|
_log.debug("Scheduling leave in " + DataHelper.formatDuration(dropTime.longValue()-now) +": " + cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ public class TunnelParticipant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( (_config != null) && (_config.getSendTo() != null) ) {
|
if ( (_config != null) && (_config.getSendTo() != null) ) {
|
||||||
|
_config.incrementProcessedMessages();
|
||||||
RouterInfo ri = _nextHopCache;
|
RouterInfo ri = _nextHopCache;
|
||||||
if (ri == null)
|
if (ri == null)
|
||||||
ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
|
ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
|
||||||
@ -82,10 +83,10 @@ public class TunnelParticipant {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Send off to nextHop directly (" + _config.getSendTo().toBase64().substring(0,4)
|
_log.debug("Send off to nextHop directly (" + _config.getSendTo().toBase64().substring(0,4)
|
||||||
+ " for " + msg);
|
+ " for " + msg);
|
||||||
_config.incrementProcessedMessages();
|
|
||||||
send(_config, msg, ri);
|
send(_config, msg, ri);
|
||||||
if (_config != null)
|
// see comments below
|
||||||
incrementThroughput(_config.getReceiveFrom());
|
//if (_config != null)
|
||||||
|
// incrementThroughput(_config.getReceiveFrom());
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Lookup the nextHop (" + _config.getSendTo().toBase64().substring(0,4)
|
_log.warn("Lookup the nextHop (" + _config.getSendTo().toBase64().substring(0,4)
|
||||||
@ -109,6 +110,7 @@ public class TunnelParticipant {
|
|||||||
* influence who we spend our time profiling is dangerous, so this will be disabled for
|
* influence who we spend our time profiling is dangerous, so this will be disabled for
|
||||||
* now.
|
* now.
|
||||||
*/
|
*/
|
||||||
|
/****
|
||||||
private void incrementThroughput(Hash prev) {
|
private void incrementThroughput(Hash prev) {
|
||||||
if (true) return;
|
if (true) return;
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@ -123,6 +125,7 @@ public class TunnelParticipant {
|
|||||||
_periodMessagesTransferred++;
|
_periodMessagesTransferred++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
****/
|
||||||
|
|
||||||
public int getCompleteCount() {
|
public int getCompleteCount() {
|
||||||
if (_handler != null)
|
if (_handler != null)
|
||||||
@ -147,6 +150,9 @@ public class TunnelParticipant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void send(HopConfig config, TunnelDataMessage msg, RouterInfo ri) {
|
private void send(HopConfig config, TunnelDataMessage msg, RouterInfo ri) {
|
||||||
|
if (_context.tunnelDispatcher().shouldDropParticipatingMessage())
|
||||||
|
return;
|
||||||
|
_config.incrementSentMessages();
|
||||||
long oldId = msg.getUniqueId();
|
long oldId = msg.getUniqueId();
|
||||||
long newId = _context.random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
long newId = _context.random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||||
_context.messageHistory().wrap("TunnelDataMessage", oldId, "TunnelDataMessage", newId);
|
_context.messageHistory().wrap("TunnelDataMessage", oldId, "TunnelDataMessage", newId);
|
||||||
@ -221,4 +227,4 @@ public class TunnelParticipant {
|
|||||||
return "inbound endpoint";
|
return "inbound endpoint";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user