diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
index 8b039732e..42290cb15 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
@@ -27,6 +27,7 @@ public class ConfigNetHandler extends FormHandler {
private boolean _guessRequested;
private boolean _reseedRequested;
private boolean _saveRequested;
+ private boolean _recheckReachabilityRequested;
private boolean _timeSyncEnabled;
private String _tcpPort;
private String _udpPort;
@@ -44,6 +45,8 @@ public class ConfigNetHandler extends FormHandler {
reseed();
} else if (_saveRequested) {
saveChanges();
+ } else if (_recheckReachabilityRequested) {
+ recheckReachability();
} else {
// noop
}
@@ -53,6 +56,7 @@ public class ConfigNetHandler extends FormHandler {
public void setReseed(String moo) { _reseedRequested = true; }
public void setSave(String moo) { _saveRequested = true; }
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
+ public void setRecheckReachability(String moo) { _recheckReachabilityRequested = true; }
public void setHostname(String hostname) {
_hostname = (hostname != null ? hostname.trim() : null);
@@ -195,6 +199,11 @@ public class ConfigNetHandler extends FormHandler {
fos.close();
}
+ private void recheckReachability() {
+ _context.commSystem().recheckReachability();
+ addFormNotice("Rechecking router reachability...");
+ }
+
/**
* The user made changes to the network config and wants to save them, so
* lets go ahead and do so.
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index 26be53d92..7e33c5111 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -12,6 +12,7 @@ import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
@@ -97,6 +98,23 @@ public class SummaryHelper {
return (_context.netDb().getKnownRouters() < 10);
}
+ public int getAllPeers() { return _context.netDb().getKnownRouters(); }
+
+ public String getReachability() {
+ int status = _context.commSystem().getReachabilityStatus();
+ switch (status) {
+ case CommSystemFacade.STATUS_OK:
+ return "OK";
+ case CommSystemFacade.STATUS_DIFFERENT:
+ return "ERR-SymmetricNAT";
+ case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
+ return "ERR-Reject";
+ case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
+ default:
+ return "Unknown";
+ }
+ }
+
/**
* Retrieve amount of used memory.
*
diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp
index 99735f68b..0472807ee 100644
--- a/apps/routerconsole/jsp/config.jsp
+++ b/apps/routerconsole/jsp/config.jsp
@@ -35,6 +35,8 @@ this port from arbitrary peers (this requirement will be removed in i2p 0.6.1, b
TCP port: " /> You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
connections on it (this requirement will be removed in i2p 0.6.1, but is necessary now)
+
+
Bandwidth limiter
diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp
index 741717c39..4fa834e5a 100644
--- a/apps/routerconsole/jsp/summary.jsp
+++ b/apps/routerconsole/jsp/summary.jsp
@@ -14,7 +14,8 @@
Version: Uptime: Now:
- Memory: <%
+ Memory:
+ Status: <%
if (helper.updateAvailable()) {
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) {
out.print(update.getStatus());
@@ -39,7 +40,8 @@
High capacity: Well integrated: Failing:
- Shitlisted: <%
+ Shitlisted:
+ Known: <%
if (helper.getActivePeers() <= 0) {
%>check your NAT/firewall <%
}
diff --git a/history.txt b/history.txt
index aa2878b2d..eda862f00 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,16 @@
-$Id: history.txt,v 1.223 2005/08/07 14:31:58 jrandom Exp $
+$Id: history.txt,v 1.224 2005/08/08 15:35:51 jrandom Exp $
+
+2005-08-10 jrandom
+ * Deployed the peer testing implementation to be run every few minutes on
+ each router, as well as any time the user requests a test manually. The
+ tests do not reconfigure the ports at the moment, merely determine under
+ what conditions the local router is reachable. The status shown in the
+ top left will be "ERR-SymmetricNAT" if the user's IP and port show up
+ differently for different peers, "ERR-Reject" if the router cannot
+ receive unsolicited packets or the peer helping test could not find a
+ collaborator, "Unknown" if the test has not been run or the test
+ participants were unreachable, or "OK" if the router can receive
+ unsolicited connections and those connections use the same IP and port.
* 2005-08-08 0.6.0.2 released
diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java
index 9a03ebcb9..f1e39ba21 100644
--- a/router/java/src/net/i2p/router/CommSystemFacade.java
+++ b/router/java/src/net/i2p/router/CommSystemFacade.java
@@ -30,6 +30,34 @@ public abstract class CommSystemFacade implements Service {
public int countActivePeers() { return 0; }
public List getMostRecentErrorMessages() { return Collections.EMPTY_LIST; }
+
+ /**
+ * Determine under what conditions we are remotely reachable.
+ *
+ */
+ public short getReachabilityStatus() { return STATUS_OK; }
+ public void recheckReachability() {}
+
+ /**
+ * We are able to receive unsolicited connections
+ */
+ public static final short STATUS_OK = 0;
+ /**
+ * We are behind a symmetric NAT which will make our 'from' address look
+ * differently when we talk to multiple people
+ *
+ */
+ public static final short STATUS_DIFFERENT = 1;
+ /**
+ * We are able to talk to peers that we initiate communication with, but
+ * cannot receive unsolicited connections
+ */
+ public static final short STATUS_REJECT_UNSOLICITED = 2;
+ /**
+ * Our reachability is unknown
+ */
+ public static final short STATUS_UNKNOWN = 3;
+
}
class DummyCommSystemFacade extends CommSystemFacade {
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 10e487c5e..2759afd4a 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
- public final static String ID = "$Revision: 1.212 $ $Date: 2005/08/07 14:31:58 $";
+ public final static String ID = "$Revision: 1.213 $ $Date: 2005/08/08 15:35:50 $";
public final static String VERSION = "0.6.0.2";
- public final static long BUILD = 0;
+ public final static long BUILD = 1;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index beaf46f26..dbb684119 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -57,7 +57,6 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
private boolean _initialized;
/** Clock independent time of when we started up */
private long _started;
- private int _knownRouters;
private StartExplorersJob _exploreJob;
private HarvesterJob _harvestJob;
/** when was the last time an exploration found something new? */
@@ -128,7 +127,6 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
_peerSelector = new PeerSelector(_context);
_publishingLeaseSets = new HashMap(8);
_lastExploreNew = 0;
- _knownRouters = 0;
_activeRequests = new HashMap(8);
_enforceNetId = DEFAULT_ENFORCE_NETID;
}
@@ -359,7 +357,21 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
return rv;
}
- public int getKnownRouters() { return _knownRouters; }
+ public int getKnownRouters() {
+ CountRouters count = new CountRouters();
+ _kb.getAll(count);
+ return count.size();
+ }
+
+ private class CountRouters implements SelectionCollector {
+ private int _count;
+ public int size() { return _count; }
+ public void add(Hash entry) {
+ Object o = _ds.get(entry);
+ if (o instanceof RouterInfo)
+ _count++;
+ }
+ }
public void lookupLeaseSet(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs) {
if (!_initialized) return;
@@ -650,7 +662,6 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
+ routerInfo.getOptions().size() + " options on "
+ new Date(routerInfo.getPublished()));
- _knownRouters++;
_ds.put(key, routerInfo);
synchronized (_lastSent) {
if (!_lastSent.containsKey(key))
@@ -712,8 +723,6 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
synchronized (_passiveSendKeys) {
_passiveSendKeys.remove(dbEntry);
}
- if (isRouterInfo)
- _knownRouters--;
}
public void unpublish(LeaseSet localLeaseSet) {
diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
index 9ce8976f8..b13fb2754 100644
--- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
+++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
@@ -74,7 +74,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
public List getMostRecentErrorMessages() {
return _manager.getMostRecentErrorMessages();
}
-
+
+ public short getReachabilityStatus() { return _manager.getReachabilityStatus(); }
+ public void recheckReachability() { _manager.recheckReachability(); }
+
public void renderStatusHTML(Writer out) throws IOException {
_manager.renderStatusHTML(out);
}
diff --git a/router/java/src/net/i2p/router/transport/Transport.java b/router/java/src/net/i2p/router/transport/Transport.java
index c60c03ce4..e7cc3fa0e 100644
--- a/router/java/src/net/i2p/router/transport/Transport.java
+++ b/router/java/src/net/i2p/router/transport/Transport.java
@@ -41,4 +41,6 @@ public interface Transport {
public List getMostRecentErrorMessages();
public void renderStatusHTML(Writer out) throws IOException;
+ public short getReachabilityStatus();
+ public void recheckReachability();
}
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index c24abd3a0..3ca0c140a 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -22,6 +22,7 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.Job;
import net.i2p.router.MessageSelector;
import net.i2p.router.OutNetMessage;
@@ -351,4 +352,7 @@ public abstract class TransportImpl implements Transport {
public void renderStatusHTML(Writer out) throws IOException {}
public RouterContext getContext() { return _context; }
+ public short getReachabilityStatus() { return CommSystemFacade.STATUS_UNKNOWN; }
+ public void recheckReachability() {}
+
}
diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java
index 551c9b9f3..06eca971f 100644
--- a/router/java/src/net/i2p/router/transport/TransportManager.java
+++ b/router/java/src/net/i2p/router/transport/TransportManager.java
@@ -10,6 +10,7 @@ package net.i2p.router.transport;
import java.io.IOException;
import java.io.Writer;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
@@ -22,6 +23,7 @@ import net.i2p.data.RouterAddress;
import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.router.OutNetMessage;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.tcp.TCPTransport;
import net.i2p.router.transport.udp.UDPTransport;
@@ -115,6 +117,24 @@ public class TransportManager implements TransportEventListener {
return peers;
}
+ public short getReachabilityStatus() {
+ if (_transports.size() <= 0) return CommSystemFacade.STATUS_UNKNOWN;
+ short status[] = new short[_transports.size()];
+ for (int i = 0; i < _transports.size(); i++) {
+ status[i] = ((Transport)_transports.get(i)).getReachabilityStatus();
+ }
+ // the values for the statuses are increasing for their 'badness'
+ Arrays.sort(status);
+ return status[0];
+ }
+
+ public void recheckReachability() {
+ for (int i = 0; i < _transports.size(); i++)
+ ((Transport)_transports.get(i)).recheckReachability();
+ }
+
+
+
Map getAddresses() {
Map rv = new HashMap(_transports.size());
for (int i = 0; i < _transports.size(); i++) {
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
index 37de93cba..98145dff8 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
@@ -456,6 +456,9 @@ public class PacketBuilder {
* @return ready to send packet, or null if there was a problem
*/
public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toIntroKey, long nonce, SessionKey aliceIntroKey) {
+ return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey);
+ }
+ public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey) {
UDPPacket packet = UDPPacket.acquire(_context);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
@@ -486,7 +489,7 @@ public class PacketBuilder {
if ( (off % 16) != 0)
off += 16 - (off % 16);
packet.getPacket().setLength(off);
- authenticate(packet, toIntroKey, toIntroKey);
+ authenticate(packet, toCipherKey, toMACKey);
setTo(packet, toIP, toPort);
return packet;
}
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
index 5eacf8736..19c891ab0 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
@@ -398,6 +398,8 @@ public class PacketHandler {
break;
case UDPPacket.PAYLOAD_TYPE_TEST:
_state = 51;
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Received test packet: " + reader + " from " + from);
_testManager.receiveTest(from, reader);
break;
default:
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index b0cb3417a..3184ba198 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -4,6 +4,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.RouterContext;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterInfo;
@@ -32,6 +33,8 @@ class PeerTestManager {
private InetAddress _bobIP;
private int _bobPort;
private SessionKey _bobIntroKey;
+ private SessionKey _bobCipherKey;
+ private SessionKey _bobMACKey;
private long _testBeginTime;
private long _lastSendTime;
private long _receiveBobReplyTime;
@@ -44,7 +47,7 @@ class PeerTestManager {
/** longest we will keep track of a Charlie nonce for */
private static final int MAX_CHARLIE_LIFETIME = 10*1000;
-
+
public PeerTestManager(RouterContext context, UDPTransport transport) {
_context = context;
_transport = transport;
@@ -57,11 +60,14 @@ class PeerTestManager {
private static final int RESEND_TIMEOUT = 5*1000;
private static final int MAX_TEST_TIME = 30*1000;
private static final long MAX_NONCE = (1l << 32) - 1l;
- public void runTest(InetAddress bobIP, int bobPort, SessionKey bobIntroKey) {
+ //public void runTest(InetAddress bobIP, int bobPort, SessionKey bobIntroKey) {
+ public void runTest(InetAddress bobIP, int bobPort, SessionKey bobCipherKey, SessionKey bobMACKey) {
_currentTestNonce = _context.random().nextLong(MAX_NONCE);
_bobIP = bobIP;
_bobPort = bobPort;
- _bobIntroKey = bobIntroKey;
+ //_bobIntroKey = bobIntroKey;
+ _bobCipherKey = bobCipherKey;
+ _bobMACKey = bobMACKey;
_charlieIP = null;
_charliePort = -1;
_charlieIntroKey = null;
@@ -72,6 +78,9 @@ class PeerTestManager {
_receiveBobReplyPort = -1;
_receiveCharlieReplyPort = -1;
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Running test with bob = " + bobIP + ":" + bobPort);
+
sendTestToBob();
SimpleTimer.getInstance().addEvent(new ContinueTest(), RESEND_TIMEOUT);
@@ -104,10 +113,14 @@ class PeerTestManager {
}
private void sendTestToBob() {
- _transport.send(_packetBuilder.buildPeerTestFromAlice(_bobIP, _bobPort, _bobIntroKey,
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Sending test to bob: " + _bobIP + ":" + _bobPort);
+ _transport.send(_packetBuilder.buildPeerTestFromAlice(_bobIP, _bobPort, _bobCipherKey, _bobMACKey, //_bobIntroKey,
_currentTestNonce, _transport.getIntroKey()));
}
private void sendTestToCharlie() {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Sending test to charlie: " + _charlieIP + ":" + _charliePort);
_transport.send(_packetBuilder.buildPeerTestFromAlice(_charlieIP, _charliePort, _charlieIntroKey,
_currentTestNonce, _transport.getIntroKey()));
}
@@ -118,20 +131,28 @@ class PeerTestManager {
* test
*/
private void receiveTestReply(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo) {
- if (DataHelper.eq(from.getIP(), _bobIP.getAddress())) {
+ if ( (DataHelper.eq(from.getIP(), _bobIP.getAddress())) && (from.getPort() == _bobPort) ) {
_receiveBobReplyTime = _context.clock().now();
_receiveBobReplyPort = testInfo.readPort();
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive test reply from bob @ " + _bobIP + " on " + _receiveBobReplyPort);
} else {
if (_receiveCharlieReplyTime > 0) {
// this is our second charlie, yay!
_receiveCharlieReplyPort = testInfo.readPort();
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive test reply from charlie @ " + _charlieIP + " on " + _receiveCharlieReplyPort);
testComplete();
} else {
// ok, first charlie. send 'em a packet
_receiveCharlieReplyTime = _context.clock().now();
+ _charlieIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
+ testInfo.readIntroKey(_charlieIntroKey.getData(), 0);
_charliePort = from.getPort();
try {
_charlieIP = InetAddress.getByAddress(from.getIP());
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive test from charlie @ " + _charlieIP + " on " + _charliePort);
sendTestToCharlie();
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
@@ -141,12 +162,6 @@ class PeerTestManager {
}
}
- private static final short STATUS_REACHABLE_OK = 0;
- private static final short STATUS_REACHABLE_DIFFERENT = 1;
- private static final short STATUS_CHARLIE_DIED = 2;
- private static final short STATUS_REJECT_UNSOLICITED = 3;
- private static final short STATUS_BOB_SUCKS = 4;
-
/**
* Evaluate the info we have and act accordingly, since the test has either timed out or
* we have successfully received the second PeerTest from a Charlie.
@@ -157,19 +172,19 @@ class PeerTestManager {
if (_receiveCharlieReplyPort > 0) {
// we received a second message from charlie
if (_receiveBobReplyPort == _receiveCharlieReplyPort) {
- status = STATUS_REACHABLE_OK;
+ status = CommSystemFacade.STATUS_OK;
} else {
- status = STATUS_REACHABLE_DIFFERENT;
+ status = CommSystemFacade.STATUS_DIFFERENT;
}
} else if (_receiveCharlieReplyTime > 0) {
// we received only one message from charlie
- status = STATUS_CHARLIE_DIED;
+ status = CommSystemFacade.STATUS_UNKNOWN;
} else if (_receiveBobReplyTime > 0) {
// we received a message from bob but no messages from charlie
- status = STATUS_REJECT_UNSOLICITED;
+ status = CommSystemFacade.STATUS_REJECT_UNSOLICITED;
} else {
// we never received anything from bob - he is either down or ignoring us
- status = STATUS_BOB_SUCKS;
+ status = CommSystemFacade.STATUS_UNKNOWN;
}
honorStatus(status);
@@ -179,6 +194,8 @@ class PeerTestManager {
_bobIP = null;
_bobPort = -1;
_bobIntroKey = null;
+ _bobCipherKey = null;
+ _bobMACKey = null;
_charlieIP = null;
_charliePort = -1;
_charlieIntroKey = null;
@@ -196,15 +213,9 @@ class PeerTestManager {
*
*/
private void honorStatus(short status) {
- switch (status) {
- case STATUS_REACHABLE_OK:
- case STATUS_REACHABLE_DIFFERENT:
- case STATUS_CHARLIE_DIED:
- case STATUS_REJECT_UNSOLICITED:
- case STATUS_BOB_SUCKS:
- if (_log.shouldLog(Log.INFO))
- _log.info("Test results: status = " + status);
- }
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Test results: status = " + status);
+ _transport.setReachabilityStatus(status);
}
/**
@@ -230,10 +241,17 @@ class PeerTestManager {
if ( ( (fromIP == null) && (fromPort <= 0) ) || // info is unknown or...
(DataHelper.eq(fromIP, from.getIP()) && (fromPort == from.getPort())) ) { // info matches sender
+ int knownIndex = -1;
boolean weAreCharlie = false;
synchronized (_receiveAsCharlie) {
- weAreCharlie = (Arrays.binarySearch(_receiveAsCharlie, nonce) != -1);
+ for (int i = 0; (i < _receiveAsCharlie.length) && (knownIndex == -1); i++)
+ if (_receiveAsCharlie[i] == nonce)
+ knownIndex = i;
}
+ weAreCharlie = (knownIndex != -1);
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive test with nonce " + nonce + ", known as charlie @ " + knownIndex);
+
if (weAreCharlie) {
receiveFromAliceAsCharlie(from, testInfo, nonce);
} else {
@@ -261,18 +279,38 @@ class PeerTestManager {
return;
}
+ boolean isNew = true;
int index = -1;
synchronized (_receiveAsCharlie) {
- index = _receiveAsCharlieIndex;
- _receiveAsCharlie[index] = nonce;
- _receiveAsCharlieIndex = (index + 1) % _receiveAsCharlie.length;
+ for (int i = 0; i < _receiveAsCharlie.length; i++) {
+ if (_receiveAsCharlie[i] == nonce) {
+ index = i;
+ isNew = false;
+ break;
+ }
+ }
+ if (index == -1) {
+ // ok, new nonce, store 'er
+ index = (_receiveAsCharlieIndex + 1) % _receiveAsCharlie.length;
+ _receiveAsCharlie[index] = nonce;
+ _receiveAsCharlieIndex = index;
+ }
}
- SimpleTimer.getInstance().addEvent(new RemoveCharlie(nonce, index), MAX_CHARLIE_LIFETIME);
+
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive test as charlie nonce " + nonce + ", stored at index " + index);
+
+ if (isNew)
+ SimpleTimer.getInstance().addEvent(new RemoveCharlie(nonce, index), MAX_CHARLIE_LIFETIME);
try {
InetAddress aliceIP = InetAddress.getByAddress(fromIP);
- SessionKey aliceIntroKey = new SessionKey();
+ SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
UDPPacket packet = _packetBuilder.buildPeerTestToAlice(aliceIP, fromPort, aliceIntroKey, _transport.getIntroKey(), nonce);
+
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive from bob as charlie and send to alice @ " + aliceIP + " on " + fromPort);
+
_transport.send(packet);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
@@ -288,12 +326,26 @@ class PeerTestManager {
private void receiveFromAliceAsBob(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce) {
// we are Bob, so send Alice her PeerTest, pick a Charlie, and
// send Charlie Alice's info
- PeerState charlie = _transport.getPeerState(UDPAddress.CAPACITY_TESTING);
+ PeerState charlie = null;
+ for (int i = 0; i < 5; i++) {
+ charlie = _transport.getPeerState(UDPAddress.CAPACITY_TESTING);
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Picking charlie as " + charlie + " for alice of " + from);
+ if ( (charlie != null) && (!charlie.getRemoteHostId().equals(from)) ) {
+ break;
+ }
+ charlie = null;
+ }
+ if (charlie == null) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Unable to pick a charlie");
+ return;
+ }
InetAddress aliceIP = null;
SessionKey aliceIntroKey = null;
try {
aliceIP = InetAddress.getByAddress(from.getIP());
- aliceIntroKey = new SessionKey();
+ aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
RouterInfo info = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
@@ -314,6 +366,11 @@ class PeerTestManager {
charlie.getRemotePort(),
charlie.getCurrentCipherKey(),
charlie.getCurrentMACKey());
+
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive from alice as bob, picking charlie @ " + charlie.getRemoteIPAddress() + ":"
+ + charlie.getRemotePort() + " for alice @ " + aliceIP + ":" + from.getPort());
+
_transport.send(packet);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
@@ -328,9 +385,13 @@ class PeerTestManager {
private void receiveFromAliceAsCharlie(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce) {
try {
InetAddress aliceIP = InetAddress.getByAddress(from.getIP());
- SessionKey aliceIntroKey = new SessionKey();
+ SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
UDPPacket packet = _packetBuilder.buildPeerTestToAlice(aliceIP, from.getPort(), aliceIntroKey, _transport.getIntroKey(), nonce);
+
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Receive from alice as charlie, w/ alice @ " + aliceIP + ":" + from.getPort() + " and nonce " + nonce);
+
_transport.send(packet);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
index d5f91083a..d7de46022 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
@@ -20,7 +20,7 @@ public class UDPAddress {
public static final String PROP_HOST = "host";
public static final String PROP_INTRO_KEY = "key";
- public static final String PROP_CAPACITY = "opts";
+ public static final String PROP_CAPACITY = "caps";
public static final char CAPACITY_TESTING = 'A';
public static final char CAPACITY_INTRODUCER = 'B';
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
index f71dc178a..4a22875ba 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
@@ -104,6 +104,8 @@ public class UDPPacketReader {
return "Session created packet";
case UDPPacket.PAYLOAD_TYPE_SESSION_REQUEST:
return "Session request packet";
+ case UDPPacket.PAYLOAD_TYPE_TEST:
+ return "Peer test packet";
default:
return "Other packet type...";
}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index dd71a3eaf..1325c779c 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -16,6 +16,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
@@ -26,6 +27,7 @@ import net.i2p.data.RouterIdentity;
import net.i2p.data.SessionKey;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.DatabaseStoreMessage;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.Transport;
@@ -63,6 +65,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private UDPFlooder _flooder;
private PeerTestManager _testManager;
private ExpirePeerEvent _expireEvent;
+ private PeerTestEvent _testEvent;
+ private short _reachabilityStatus;
/** list of RelayPeer objects for people who will relay to us */
private List _relayPeers;
@@ -82,7 +86,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private TransportBid _slowBid;
/** shared slow bid for unconnected peers when we want to prefer UDP */
private TransportBid _slowPreferredBid;
-
+
public static final String STYLE = "SSU";
public static final String PROP_INTERNAL_PORT = "i2np.udp.internalPort";
@@ -116,6 +120,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private static final int MAX_CONSECUTIVE_FAILED = 5;
+ private static final int TEST_FREQUENCY = 3*60*1000;
+
public UDPTransport(RouterContext ctx) {
super(ctx);
_context = ctx;
@@ -141,6 +147,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_inboundFragments = new InboundMessageFragments(_context, _fragments, this);
_flooder = new UDPFlooder(_context, this);
_expireEvent = new ExpirePeerEvent();
+ _testEvent = new PeerTestEvent();
+ _reachabilityStatus = CommSystemFacade.STATUS_UNKNOWN;
_context.statManager().createRateStat("udp.droppedPeer", "How long ago did we receive from a dropped peer (duration == session lifetime", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.droppedPeerInactive", "How long ago did we receive from a dropped peer (duration == session lifetime)", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
@@ -234,6 +242,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_refiller.startup();
_flooder.startup();
_expireEvent.setIsAlive(true);
+ _testEvent.setIsAlive(true);
}
public void shutdown() {
@@ -254,6 +263,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (_inboundFragments != null)
_inboundFragments.shutdown();
_expireEvent.setIsAlive(false);
+ _testEvent.setIsAlive(false);
}
/**
@@ -361,29 +371,41 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
*
*/
public void messageReceived(I2NPMessage inMsg, RouterIdentity remoteIdent, Hash remoteIdentHash, long msToReceive, int bytesReceived) {
+
if (inMsg instanceof DatabaseStoreMessage) {
DatabaseStoreMessage dsm = (DatabaseStoreMessage)inMsg;
- if (dsm.getType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) {
+ if (dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) {
Hash from = remoteIdentHash;
if (from == null)
from = remoteIdent.getHash();
+
if (from.equals(dsm.getKey())) {
// db info received directly from the peer - inject it into the peersByCapacity
RouterInfo info = dsm.getRouterInfo();
- Properties opts = info.getOptions();
- if ( (opts != null) && (info.isValid()) ) {
- String capacities = opts.getProperty(UDPAddress.PROP_CAPACITY);
- if (capacities != null) {
- PeerState peer = getPeerState(from);
- for (int i = 0; i < capacities.length(); i++) {
- char capacity = capacities.charAt(i);
- List peers = _peersByCapacity[capacity];
- synchronized (peers) {
- if ( (peers.size() < MAX_PEERS_PER_CAPACITY) && (!peers.contains(peer)) )
- peers.add(peer);
+ Set addresses = info.getAddresses();
+ for (Iterator iter = addresses.iterator(); iter.hasNext(); ) {
+ RouterAddress addr = (RouterAddress)iter.next();
+ if (!STYLE.equals(addr.getTransportStyle()))
+ continue;
+ Properties opts = addr.getOptions();
+ if ( (opts != null) && (info.isValid()) ) {
+ String capacities = opts.getProperty(UDPAddress.PROP_CAPACITY);
+ if (capacities != null) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Intercepting and storing the capacities for " + from.toBase64() + ": " + capacities);
+ PeerState peer = getPeerState(from);
+ for (int i = 0; i < capacities.length(); i++) {
+ char capacity = capacities.charAt(i);
+ List peers = _peersByCapacity[capacity-'A'];
+ synchronized (peers) {
+ if ( (peers.size() < MAX_PEERS_PER_CAPACITY) && (!peers.contains(peer)) )
+ peers.add(peer);
+ }
}
}
}
+ // this was an SSU address so we're done now
+ break;
}
}
}
@@ -532,7 +554,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (capacities != null) {
for (int i = 0; i < capacities.length(); i++) {
char capacity = capacities.charAt(i);
- List peers = _peersByCapacity[capacity];
+ List peers = _peersByCapacity[capacity-'A'];
synchronized (peers) {
peers.remove(peer);
}
@@ -665,6 +687,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if ( (_externalListenPort > 0) && (_externalListenHost != null) ) {
options.setProperty(UDPAddress.PROP_PORT, String.valueOf(_externalListenPort));
options.setProperty(UDPAddress.PROP_HOST, _externalListenHost.getHostAddress());
+ // if we have explicit external addresses, they had better be reachable
+ options.setProperty(UDPAddress.PROP_CAPACITY, ""+UDPAddress.CAPACITY_TESTING);
} else {
// grab 3 relays randomly
synchronized (_relayPeers) {
@@ -728,8 +752,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
public void succeeded(OutNetMessage msg) {
if (msg == null) return;
- if (_log.shouldLog(Log.INFO))
- _log.info("Sending message succeeded: " + msg);
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Sending message succeeded: " + msg);
super.afterSend(msg, true);
}
@@ -966,4 +990,60 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
}
}
+
+ void setReachabilityStatus(short status) { _reachabilityStatus = status; }
+ public short getReachabilityStatus() { return _reachabilityStatus; }
+ public void recheckReachability() {
+ _testEvent.runTest();
+ }
+
+ private static final String PROP_SHOULD_TEST = "i2np.udp.shouldTest";
+
+ private boolean shouldTest() {
+ if (true) return true;
+ String val = _context.getProperty(PROP_SHOULD_TEST);
+ return ( (val != null) && ("true".equals(val)) );
+ }
+
+ private class PeerTestEvent implements SimpleTimer.TimedEvent {
+ private boolean _alive;
+ /** when did we last test our reachability */
+ private long _lastTested;
+
+ public void timeReached() {
+ if (shouldTest()) {
+ long now = _context.clock().now();
+ if (now - _lastTested >= TEST_FREQUENCY) {
+ runTest();
+ }
+ }
+ if (_alive) {
+ long delay = _context.random().nextInt(2*TEST_FREQUENCY);
+ SimpleTimer.getInstance().addEvent(PeerTestEvent.this, delay);
+ }
+ }
+
+ private void runTest() {
+ PeerState bob = getPeerState(UDPAddress.CAPACITY_TESTING);
+ if (bob != null) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Running periodic test with bob = " + bob);
+ _testManager.runTest(bob.getRemoteIPAddress(), bob.getRemotePort(), bob.getCurrentCipherKey(), bob.getCurrentMACKey());
+ } else {
+ if (_log.shouldLog(Log.ERROR))
+ _log.error("Unable to run a periodic test, as there are no peers with the capacity required");
+ }
+ _lastTested = _context.clock().now();
+ }
+
+ public void setIsAlive(boolean isAlive) {
+ _alive = isAlive;
+ if (isAlive) {
+ long delay = _context.random().nextInt(2*TEST_FREQUENCY);
+ SimpleTimer.getInstance().addEvent(PeerTestEvent.this, delay);
+ } else {
+ SimpleTimer.getInstance().removeEvent(PeerTestEvent.this);
+ }
+ }
+ }
}