forked from I2P_Developers/i2p.i2p
* StoreJob: Ensure nonzero token
* Tunnels: Connection limit mitigation:
- Disable tunnel testing
- Implement closest-to-the-key tunnel selection
- Use closest-selection in NetDB lookups, stores, and verifies;
OCMOSJ; and in BuildRequestor
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
* 2011-10-18 zzz
|
||||
* StoreJob: Ensure nonzero token
|
||||
* Tunnels: Connection limit mitigation:
|
||||
- Disable tunnel testing
|
||||
- Implement closest-to-the-key tunnel selection
|
||||
- Use closest-selection in NetDB lookups, stores, and verifies;
|
||||
OCMOSJ; and in BuildRequestor
|
||||
|
||||
* 2011-10-17 zzz
|
||||
* BuildExecutor: Efficiency tweak
|
||||
* Console: Hide tunnel lag if tunnel testing is disabled
|
||||
|
||||
@@ -30,6 +30,11 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade {
|
||||
public TunnelInfo selectInboundTunnel(Hash destination) { return null; }
|
||||
public TunnelInfo selectOutboundTunnel() { return null; }
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination) { return null; }
|
||||
public TunnelInfo selectInboundExploratoryTunnel(Hash closestTo) { return null; }
|
||||
public TunnelInfo selectInboundTunnel(Hash destination, Hash closestTo) { return null; }
|
||||
public TunnelInfo selectOutboundExploratoryTunnel(Hash closestTo) { return null; }
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination, Hash closestTo) { return null; }
|
||||
|
||||
public boolean isValidTunnel(Hash client, TunnelInfo tunnel) { return false; }
|
||||
public int getParticipatingCount() { return 0; }
|
||||
public int getFreeTunnelCount() { return 0; }
|
||||
|
||||
@@ -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 = 2;
|
||||
public final static long BUILD = 3;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -22,6 +22,7 @@ import net.i2p.router.tunnel.pool.TunnelPool;
|
||||
*
|
||||
*/
|
||||
public interface TunnelManagerFacade extends Service {
|
||||
|
||||
/**
|
||||
* Retrieve the information related to a particular tunnel
|
||||
*
|
||||
@@ -29,15 +30,89 @@ public interface TunnelManagerFacade extends Service {
|
||||
*
|
||||
*/
|
||||
TunnelInfo getTunnelInfo(TunnelId id);
|
||||
/** pick an inbound tunnel not bound to a particular destination */
|
||||
|
||||
/**
|
||||
* Pick a random inbound exploratory tunnel
|
||||
*
|
||||
* @return null if none
|
||||
*/
|
||||
TunnelInfo selectInboundTunnel();
|
||||
/** pick an inbound tunnel bound to the given destination */
|
||||
|
||||
/**
|
||||
* Pick a random inbound tunnel from the given destination's pool
|
||||
*
|
||||
* @param destination if null, returns inbound exploratory tunnel
|
||||
* @return null if none
|
||||
*/
|
||||
TunnelInfo selectInboundTunnel(Hash destination);
|
||||
/** pick an outbound tunnel not bound to a particular destination */
|
||||
|
||||
/**
|
||||
* Pick a random outbound exploratory tunnel
|
||||
*
|
||||
* @return null if none
|
||||
*/
|
||||
TunnelInfo selectOutboundTunnel();
|
||||
/** pick an outbound tunnel bound to the given destination */
|
||||
|
||||
/**
|
||||
* Pick a random outbound tunnel from the given destination's pool
|
||||
*
|
||||
* @param destination if null, returns outbound exploratory tunnel
|
||||
* @return null if none
|
||||
*/
|
||||
TunnelInfo selectOutboundTunnel(Hash destination);
|
||||
|
||||
/**
|
||||
* Pick the inbound exploratory tunnel with the gateway closest to the given hash.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectInboundExploratoryTunnel(Hash closestTo);
|
||||
|
||||
/**
|
||||
* Pick the inbound tunnel with the gateway closest to the given hash
|
||||
* from the given destination's pool.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param destination if null, returns inbound exploratory tunnel
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectInboundTunnel(Hash destination, Hash closestTo);
|
||||
|
||||
/**
|
||||
* Pick the outbound exploratory tunnel with the endpoint closest to the given hash.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectOutboundExploratoryTunnel(Hash closestTo);
|
||||
|
||||
/**
|
||||
* Pick the outbound tunnel with the endpoint closest to the given hash
|
||||
* from the given destination's pool.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param destination if null, returns outbound exploratory tunnel
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination, Hash closestTo);
|
||||
|
||||
/** Is a tunnel a valid member of the pool? */
|
||||
public boolean isValidTunnel(Hash client, TunnelInfo tunnel);
|
||||
|
||||
|
||||
@@ -835,13 +835,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
* Pick an arbitrary outbound tunnel to send the message through, or null if
|
||||
* there aren't any around
|
||||
*
|
||||
* TODO - rather than pick one at random, pick the "closest" to the lease,
|
||||
* Rather than pick one at random, pick the "closest" to the lease,
|
||||
* to minimize network OBEP - IBGW connections?
|
||||
* This would also eliminate a connection when OBEP == IBGW.
|
||||
* Anonymity issues?
|
||||
*/
|
||||
private TunnelInfo selectOutboundTunnel() {
|
||||
return getContext().tunnelManager().selectOutboundTunnel(_from.calculateHash());
|
||||
Hash gw = _lease.getGateway();
|
||||
return getContext().tunnelManager().selectOutboundTunnel(_from.calculateHash(), gw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,6 @@ class ExploreJob extends SearchJob {
|
||||
// if this collides with an actual leaseSet's key, neat, but that wouldn't imply we're actually
|
||||
// attempting to send that lease a message!
|
||||
super(context, facade, key, null, null, MAX_EXPLORE_TIME, false, false);
|
||||
_log = context.logManager().getLog(ExploreJob.class);
|
||||
_peerSelector = (FloodfillPeerSelector) (_facade.getPeerSelector());
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* send a netDb lookup to a random floodfill peer - if it is found, great,
|
||||
* Send a netDb lookup to a floodfill peer - If it is found, great,
|
||||
* but if they reply back saying they dont know it, queue up a store of the
|
||||
* key to a random floodfill peer again (via FloodfillStoreJob)
|
||||
*
|
||||
@@ -92,9 +92,9 @@ public class FloodfillVerifyStoreJob extends JobImpl {
|
||||
// Unless it is an encrypted leaseset.
|
||||
TunnelInfo outTunnel;
|
||||
if (_isRouterInfo || getContext().keyRing().get(_key) != null)
|
||||
outTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
||||
outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(_target);
|
||||
else
|
||||
outTunnel = getContext().tunnelManager().selectOutboundTunnel(_key);
|
||||
outTunnel = getContext().tunnelManager().selectOutboundTunnel(_key, _target);
|
||||
if (outTunnel == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No outbound tunnels to verify a store");
|
||||
@@ -153,9 +153,9 @@ public class FloodfillVerifyStoreJob extends JobImpl {
|
||||
// Unless it is an encrypted leaseset.
|
||||
TunnelInfo replyTunnelInfo;
|
||||
if (_isRouterInfo || getContext().keyRing().get(_key) != null)
|
||||
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel();
|
||||
replyTunnelInfo = getContext().tunnelManager().selectInboundExploratoryTunnel(_target);
|
||||
else
|
||||
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel(_key);
|
||||
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel(_key, _target);
|
||||
if (replyTunnelInfo == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No inbound tunnels to get a reply from!");
|
||||
|
||||
@@ -191,8 +191,8 @@ class IterativeSearchJob extends FloodSearchJob {
|
||||
*/
|
||||
private void sendQuery(Hash peer) {
|
||||
DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true);
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundExploratoryTunnel(peer);
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(peer);
|
||||
if ( (replyTunnel == null) || (outTunnel == null) ) {
|
||||
failed();
|
||||
return;
|
||||
|
||||
@@ -38,17 +38,17 @@ import net.i2p.util.Log;
|
||||
* It also does not update peer profile stats.
|
||||
*/
|
||||
class SearchJob extends JobImpl {
|
||||
protected Log _log;
|
||||
protected KademliaNetworkDatabaseFacade _facade;
|
||||
private SearchState _state;
|
||||
private Job _onSuccess;
|
||||
private Job _onFailure;
|
||||
private long _expiration;
|
||||
private long _timeoutMs;
|
||||
private boolean _keepStats;
|
||||
private boolean _isLease;
|
||||
protected final Log _log;
|
||||
protected final KademliaNetworkDatabaseFacade _facade;
|
||||
private final SearchState _state;
|
||||
private final Job _onSuccess;
|
||||
private final Job _onFailure;
|
||||
private final long _expiration;
|
||||
private final long _timeoutMs;
|
||||
private final boolean _keepStats;
|
||||
private final boolean _isLease;
|
||||
private Job _pendingRequeueJob;
|
||||
private PeerSelector _peerSelector;
|
||||
private final PeerSelector _peerSelector;
|
||||
private final List _deferredSearches;
|
||||
private boolean _deferredCleared;
|
||||
private long _startedOn;
|
||||
@@ -89,7 +89,7 @@ class SearchJob extends JobImpl {
|
||||
super(context);
|
||||
if ( (key == null) || (key.getData() == null) )
|
||||
throw new IllegalArgumentException("Search for null key? wtf");
|
||||
_log = getContext().logManager().getLog(SearchJob.class);
|
||||
_log = getContext().logManager().getLog(getClass());
|
||||
_facade = facade;
|
||||
_state = new SearchState(getContext(), key);
|
||||
_onSuccess = onSuccess;
|
||||
@@ -98,11 +98,8 @@ class SearchJob extends JobImpl {
|
||||
_keepStats = keepStats;
|
||||
_isLease = isLease;
|
||||
_deferredSearches = new ArrayList(0);
|
||||
_deferredCleared = false;
|
||||
_peerSelector = facade.getPeerSelector();
|
||||
_startedOn = -1;
|
||||
_floodfillPeersExhausted = false;
|
||||
_floodfillSearchesOutstanding = 0;
|
||||
_expiration = getContext().clock().now() + timeoutMs;
|
||||
getContext().statManager().addRateData("netDb.searchCount", 1, 0);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -405,7 +402,8 @@ class SearchJob extends JobImpl {
|
||||
*
|
||||
*/
|
||||
protected void sendLeaseSearch(RouterInfo router) {
|
||||
TunnelInfo inTunnel = getInboundTunnelId();
|
||||
Hash to = router.getIdentity().getHash();
|
||||
TunnelInfo inTunnel = getContext().tunnelManager().selectInboundExploratoryTunnel(to);
|
||||
if (inTunnel == null) {
|
||||
_log.warn("No tunnels to get search replies through! wtf!");
|
||||
getContext().jobQueue().addJob(new FailedJob(getContext(), router));
|
||||
@@ -423,12 +421,12 @@ class SearchJob extends JobImpl {
|
||||
// return;
|
||||
//}
|
||||
|
||||
int timeout = getPerPeerTimeoutMs(router.getIdentity().getHash());
|
||||
int timeout = getPerPeerTimeoutMs(to);
|
||||
long expiration = getContext().clock().now() + timeout;
|
||||
|
||||
DatabaseLookupMessage msg = buildMessage(inTunnelId, inTunnel.getPeer(0), expiration);
|
||||
|
||||
TunnelInfo outTunnel = getOutboundTunnelId();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(to);
|
||||
if (outTunnel == null) {
|
||||
_log.warn("No tunnels to send search out through! wtf!");
|
||||
getContext().jobQueue().addJob(new FailedJob(getContext(), router));
|
||||
@@ -438,7 +436,7 @@ class SearchJob extends JobImpl {
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Sending search to " + router.getIdentity().getHash().toBase64()
|
||||
_log.debug(getJobId() + ": Sending search to " + to
|
||||
+ " for " + msg.getSearchKey().toBase64() + " w/ replies through ["
|
||||
+ msg.getFrom().toBase64() + "] via tunnel ["
|
||||
+ msg.getReplyTunnel() + "]");
|
||||
@@ -450,7 +448,7 @@ class SearchJob extends JobImpl {
|
||||
if (FloodfillNetworkDatabaseFacade.isFloodfill(router))
|
||||
_floodfillSearchesOutstanding++;
|
||||
getContext().messageRegistry().registerPending(sel, reply, new FailedJob(getContext(), router), timeout);
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnelId, router.getIdentity().getHash());
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnelId, to);
|
||||
}
|
||||
|
||||
/** we're searching for a router, so we can just send direct */
|
||||
@@ -476,23 +474,6 @@ class SearchJob extends JobImpl {
|
||||
}
|
||||
**********/
|
||||
|
||||
/**
|
||||
* what tunnel will we send the search out through?
|
||||
*
|
||||
* @return tunnel id (or null if none are found)
|
||||
*/
|
||||
private TunnelInfo getOutboundTunnelId() {
|
||||
return getContext().tunnelManager().selectOutboundTunnel();
|
||||
}
|
||||
|
||||
/**
|
||||
* what tunnel will we get replies through?
|
||||
*
|
||||
* @return tunnel id (or null if none are found)
|
||||
*/
|
||||
private TunnelInfo getInboundTunnelId() {
|
||||
return getContext().tunnelManager().selectInboundTunnel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database search message
|
||||
@@ -664,19 +645,20 @@ class SearchJob extends JobImpl {
|
||||
* provide us with the data when we asked them.
|
||||
*/
|
||||
private boolean resend(RouterInfo toPeer, LeaseSet ls) {
|
||||
Hash to = toPeer.getIdentity().getHash();
|
||||
DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext());
|
||||
msg.setEntry(ls);
|
||||
msg.setMessageExpiration(getContext().clock().now() + RESEND_TIMEOUT);
|
||||
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(to);
|
||||
|
||||
if (outTunnel != null) {
|
||||
TunnelId targetTunnelId = null; // not needed
|
||||
Job onSend = null; // not wanted
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("resending leaseSet out to " + toPeer.getIdentity().getHash() + " through " + outTunnel + ": " + msg);
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), null, toPeer.getIdentity().getHash());
|
||||
_log.debug("resending leaseSet out to " + to + " through " + outTunnel + ": " + msg);
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), null, to);
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
||||
@@ -36,8 +36,8 @@ class SingleSearchJob extends FloodOnlySearchJob {
|
||||
public void runJob() {
|
||||
_onm = getContext().messageRegistry().registerPending(_replySelector, _onReply, _onTimeout, _timeoutMs);
|
||||
DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true);
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel();
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundExploratoryTunnel(_to);
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(_to);
|
||||
if ( (replyTunnel == null) || (outTunnel == null) ) {
|
||||
failed();
|
||||
return;
|
||||
|
||||
@@ -32,15 +32,18 @@ import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
* Unused directly - see FloodfillStoreJob
|
||||
*/
|
||||
class StoreJob extends JobImpl {
|
||||
protected Log _log;
|
||||
protected final Log _log;
|
||||
private KademliaNetworkDatabaseFacade _facade;
|
||||
protected StoreState _state;
|
||||
private Job _onSuccess;
|
||||
private Job _onFailure;
|
||||
protected final StoreState _state;
|
||||
private final Job _onSuccess;
|
||||
private final Job _onFailure;
|
||||
private long _timeoutMs;
|
||||
private long _expiration;
|
||||
private PeerSelector _peerSelector;
|
||||
private final long _expiration;
|
||||
private final PeerSelector _peerSelector;
|
||||
|
||||
private final static int PARALLELIZATION = 4; // how many sent at a time
|
||||
private final static int REDUNDANCY = 4; // we want the data sent to 6 peers
|
||||
@@ -308,7 +311,7 @@ class StoreJob extends JobImpl {
|
||||
*
|
||||
*/
|
||||
private void sendDirect(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
|
||||
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
long token = 1 + getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
msg.setReplyToken(token);
|
||||
msg.setReplyGateway(getContext().routerHash());
|
||||
|
||||
@@ -343,9 +346,10 @@ class StoreJob extends JobImpl {
|
||||
*
|
||||
*/
|
||||
private void sendStoreThroughGarlic(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
|
||||
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
long token = 1 + getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
|
||||
TunnelInfo replyTunnel = selectInboundTunnel();
|
||||
Hash to = peer.getIdentity().getHash();
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundExploratoryTunnel(to);
|
||||
if (replyTunnel == null) {
|
||||
_log.warn("No reply inbound tunnels available!");
|
||||
return;
|
||||
@@ -358,9 +362,9 @@ class StoreJob extends JobImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send(dbStore) w/ token expected " + token);
|
||||
|
||||
_state.addPending(peer.getIdentity().getHash());
|
||||
_state.addPending(to);
|
||||
|
||||
TunnelInfo outTunnel = selectOutboundTunnel();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(to);
|
||||
if (outTunnel != null) {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to "
|
||||
@@ -375,7 +379,7 @@ class StoreJob extends JobImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sending store to " + peer.getIdentity().getHash() + " through " + outTunnel + ": " + msg);
|
||||
getContext().messageRegistry().registerPending(selector, onReply, onFail, (int)(expiration - getContext().clock().now()));
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), null, peer.getIdentity().getHash());
|
||||
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), null, to);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No outbound tunnels to send a dbStore out!");
|
||||
@@ -383,14 +387,6 @@ class StoreJob extends JobImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelInfo selectOutboundTunnel() {
|
||||
return getContext().tunnelManager().selectOutboundTunnel();
|
||||
}
|
||||
|
||||
private TunnelInfo selectInboundTunnel() {
|
||||
return getContext().tunnelManager().selectInboundTunnel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a leaseset store message out the client tunnel,
|
||||
* with the reply to come back through a client tunnel.
|
||||
@@ -408,10 +404,11 @@ class StoreJob extends JobImpl {
|
||||
* @since 0.7.10
|
||||
*/
|
||||
private void sendStoreThroughClient(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
|
||||
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
long token = 1 + getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
Hash client = msg.getKey();
|
||||
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel(client);
|
||||
Hash to = peer.getIdentity().getHash();
|
||||
TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel(client, to);
|
||||
if (replyTunnel == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No reply inbound tunnels available!");
|
||||
@@ -426,8 +423,7 @@ class StoreJob extends JobImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send(dbStore) w/ token expected " + token);
|
||||
|
||||
Hash to = peer.getIdentity().getHash();
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(client);
|
||||
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(client, to);
|
||||
if (outTunnel != null) {
|
||||
I2NPMessage sent;
|
||||
boolean shouldEncrypt = supportsEncryption(peer);
|
||||
|
||||
@@ -55,6 +55,7 @@ abstract class BuildRequestor {
|
||||
* to send an inbound build request.
|
||||
* This is more secure than using the router's exploratory tunnels, as it
|
||||
* makes correlation of multiple clients more difficult.
|
||||
* @return true always
|
||||
*/
|
||||
private static boolean usePairedTunnels(RouterContext ctx) {
|
||||
return true;
|
||||
@@ -100,25 +101,26 @@ abstract class BuildRequestor {
|
||||
cfg.setTunnelPool(pool);
|
||||
|
||||
TunnelInfo pairedTunnel = null;
|
||||
Hash farEnd = cfg.getFarEnd();
|
||||
if (pool.getSettings().isExploratory() || !usePairedTunnels(ctx)) {
|
||||
if (pool.getSettings().isInbound())
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundTunnel();
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundExploratoryTunnel(farEnd);
|
||||
else
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundTunnel();
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundExploratoryTunnel(farEnd);
|
||||
} else {
|
||||
if (pool.getSettings().isInbound())
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundTunnel(pool.getSettings().getDestination());
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundTunnel(pool.getSettings().getDestination(), farEnd);
|
||||
else
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundTunnel(pool.getSettings().getDestination());
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundTunnel(pool.getSettings().getDestination(), farEnd);
|
||||
}
|
||||
if (pairedTunnel == null) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Couldn't find a paired tunnel for " + cfg + ", fall back on exploratory tunnels for pairing");
|
||||
if (!pool.getSettings().isExploratory() && usePairedTunnels(ctx))
|
||||
if (pool.getSettings().isInbound())
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundTunnel();
|
||||
pairedTunnel = ctx.tunnelManager().selectOutboundExploratoryTunnel(farEnd);
|
||||
else
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundTunnel();
|
||||
pairedTunnel = ctx.tunnelManager().selectInboundExploratoryTunnel(farEnd);
|
||||
}
|
||||
if (pairedTunnel == null) {
|
||||
if (log.shouldLog(Log.ERROR))
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Lease;
|
||||
import net.i2p.data.LeaseSet;
|
||||
@@ -58,6 +59,8 @@ public class TunnelPool {
|
||||
(_settings.isExploratory() ? "exploratory" : _settings.getDestinationNickname()) +
|
||||
(_settings.isInbound() ? ".in" : ".out");
|
||||
refreshSettings();
|
||||
ctx.statManager().createRateStat("tunnel.matchLease", "How often does our OBEP match their IBGW?", "Tunnels",
|
||||
new long[] {60*60*1000});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,6 +146,7 @@ public class TunnelPool {
|
||||
* the pool is configured to allow 0hop tunnels, this builds a fake one
|
||||
* and returns it.
|
||||
*
|
||||
* @return null on failure, but it should always build and return a fallback
|
||||
*/
|
||||
TunnelInfo selectTunnel() { return selectTunnel(true); }
|
||||
|
||||
@@ -221,6 +225,41 @@ public class TunnelPool {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tunnel from the pool that is XOR-closet to the target.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* Does not check for backlogged next peer.
|
||||
* Does not return an expired tunnel.
|
||||
*
|
||||
* @return null on failure
|
||||
* @since 0.8.10
|
||||
*/
|
||||
TunnelInfo selectTunnel(Hash closestTo) {
|
||||
boolean avoidZeroHop = ((getSettings().getLength() + getSettings().getLengthVariance()) > 0);
|
||||
TunnelInfo rv = null;
|
||||
synchronized (_tunnels) {
|
||||
if (!_tunnels.isEmpty()) {
|
||||
Collections.sort(_tunnels, new TunnelInfoComparator(closestTo, avoidZeroHop));
|
||||
for (TunnelInfo info : _tunnels) {
|
||||
if (info.getExpiration() > _context.clock().now()) {
|
||||
rv = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rv != null) {
|
||||
_context.statManager().addRateData("tunnel.matchLease", closestTo.equals(rv) ? 1 : 0);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(toString() + ": No tunnels to select from");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public TunnelInfo getTunnel(TunnelId gatewayId) {
|
||||
synchronized (_tunnels) {
|
||||
for (int i = 0; i < _tunnels.size(); i++) {
|
||||
@@ -495,6 +534,44 @@ public class TunnelPool {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the tunnel with the far-end that is XOR-closest to a given hash
|
||||
*
|
||||
* @since 0.8.10
|
||||
*/
|
||||
private static class TunnelInfoComparator implements Comparator<TunnelInfo> {
|
||||
private final byte[] _base;
|
||||
private final boolean _avoidZero;
|
||||
|
||||
/**
|
||||
* @param target key to compare distances with
|
||||
* @param avoidZeroHop if true, zero-hop tunnels will be put last
|
||||
*/
|
||||
public TunnelInfoComparator(Hash target, boolean avoidZeroHop) {
|
||||
_base = target.getData();
|
||||
_avoidZero = avoidZeroHop;
|
||||
}
|
||||
|
||||
public int compare(TunnelInfo lhs, TunnelInfo rhs) {
|
||||
if (_avoidZero) {
|
||||
// put the zero-hops last
|
||||
int llen = lhs.getLength();
|
||||
int rlen = rhs.getLength();
|
||||
if (llen > 1 && rlen <= 1)
|
||||
return -1;
|
||||
if (rlen > 1 && llen <= 1)
|
||||
return 1;
|
||||
}
|
||||
byte lhsDelta[] = DataHelper.xor(lhs.getFarEnd().getData(), _base);
|
||||
byte rhsDelta[] = DataHelper.xor(rhs.getFarEnd().getData(), _base);
|
||||
int rv = DataHelper.compareTo(lhsDelta, rhsDelta);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
// latest-expiring first as a tie-breaker
|
||||
return (int) (rhs.getExpiration() - lhs.getExpiration());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a leaseSet with the required tunnels that aren't about to expire.
|
||||
* Caller must synchronize on _tunnels.
|
||||
|
||||
@@ -84,7 +84,11 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
RATES);
|
||||
}
|
||||
|
||||
/** pick an inbound tunnel not bound to a particular destination */
|
||||
/**
|
||||
* Pick a random inbound exploratory tunnel
|
||||
*
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectInboundTunnel() {
|
||||
TunnelPool pool = _inboundExploratory;
|
||||
if (pool == null) return null;
|
||||
@@ -97,7 +101,12 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return info;
|
||||
}
|
||||
|
||||
/** pick an inbound tunnel bound to the given destination */
|
||||
/**
|
||||
* Pick a random inbound tunnel from the given destination's pool
|
||||
*
|
||||
* @param destination if null, returns inbound exploratory tunnel
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectInboundTunnel(Hash destination) {
|
||||
if (destination == null) return selectInboundTunnel();
|
||||
TunnelPool pool = _clientInboundPools.get(destination);
|
||||
@@ -105,12 +114,16 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return pool.selectTunnel();
|
||||
}
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Want the inbound tunnel for " + destination.calculateHash().toBase64() +
|
||||
_log.error("Want the inbound tunnel for " + destination.calculateHash() +
|
||||
" but there isn't a pool?");
|
||||
return null;
|
||||
}
|
||||
|
||||
/** pick an outbound tunnel not bound to a particular destination */
|
||||
/**
|
||||
* Pick a random outbound exploratory tunnel
|
||||
*
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectOutboundTunnel() {
|
||||
TunnelPool pool = _outboundExploratory;
|
||||
if (pool == null) return null;
|
||||
@@ -123,7 +136,12 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return info;
|
||||
}
|
||||
|
||||
/** pick an outbound tunnel bound to the given destination */
|
||||
/**
|
||||
* Pick a random outbound tunnel from the given destination's pool
|
||||
*
|
||||
* @param destination if null, returns outbound exploratory tunnel
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination) {
|
||||
if (destination == null) return selectOutboundTunnel();
|
||||
TunnelPool pool = _clientOutboundPools.get(destination);
|
||||
@@ -133,6 +151,95 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the inbound exploratory tunnel with the gateway closest to the given hash.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectInboundExploratoryTunnel(Hash closestTo) {
|
||||
TunnelPool pool = _inboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
if (info == null) {
|
||||
_inboundExploratory.buildFallback();
|
||||
// still can be null, but probably not
|
||||
info = _inboundExploratory.selectTunnel(closestTo);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the inbound tunnel with the gateway closest to the given hash
|
||||
* from the given destination's pool.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param destination if null, returns inbound exploratory tunnel
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectInboundTunnel(Hash destination, Hash closestTo) {
|
||||
if (destination == null) return selectInboundExploratoryTunnel(closestTo);
|
||||
TunnelPool pool = _clientInboundPools.get(destination);
|
||||
if (pool != null) {
|
||||
return pool.selectTunnel(closestTo);
|
||||
}
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Want the inbound tunnel for " + destination.calculateHash() +
|
||||
" but there isn't a pool?");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the outbound exploratory tunnel with the endpoint closest to the given hash.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectOutboundExploratoryTunnel(Hash closestTo) {
|
||||
TunnelPool pool = _outboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
if (info == null) {
|
||||
pool.buildFallback();
|
||||
// still can be null, but probably not
|
||||
info = pool.selectTunnel(closestTo);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the outbound tunnel with the endpoint closest to the given hash
|
||||
* from the given destination's pool.
|
||||
* By using this instead of the random selectTunnel(),
|
||||
* we force some locality in OBEP-IBGW connections to minimize
|
||||
* those connections network-wide.
|
||||
*
|
||||
* @param destination if null, returns outbound exploratory tunnel
|
||||
* @param closestTo non-null
|
||||
* @return null if none
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination, Hash closestTo) {
|
||||
if (destination == null) return selectOutboundExploratoryTunnel(closestTo);
|
||||
TunnelPool pool = _clientOutboundPools.get(destination);
|
||||
if (pool != null) {
|
||||
return pool.selectTunnel(closestTo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TunnelInfo getTunnelInfo(TunnelId id) {
|
||||
TunnelInfo info = null;
|
||||
for (TunnelPool pool : _clientInboundPools.values()) {
|
||||
@@ -151,12 +258,15 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return number of inbound exploratory tunnels */
|
||||
public int getFreeTunnelCount() {
|
||||
if (_inboundExploratory == null)
|
||||
return 0;
|
||||
else
|
||||
return _inboundExploratory.size();
|
||||
}
|
||||
|
||||
/** @return number of outbound exploratory tunnels */
|
||||
public int getOutboundTunnelCount() {
|
||||
if (_outboundExploratory == null)
|
||||
return 0;
|
||||
@@ -355,7 +465,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
void buildComplete(PooledTunnelCreatorConfig cfg) {
|
||||
if (cfg.getLength() > 1 &&
|
||||
(!_context.router().gracefulShutdownInProgress()) &&
|
||||
!Boolean.valueOf(_context.getProperty("router.disableTunnelTesting")).booleanValue()) {
|
||||
!_context.getBooleanPropertyDefaultTrue("router.disableTunnelTesting")) {
|
||||
TunnelPool pool = cfg.getTunnelPool();
|
||||
if (pool == null) {
|
||||
// never seen this before, do we reallly need to bother
|
||||
|
||||
Reference in New Issue
Block a user