diff --git a/core/java/src/net/i2p/time/Timestamper.java b/core/java/src/net/i2p/time/Timestamper.java index 6758ab466..356689f85 100644 --- a/core/java/src/net/i2p/time/Timestamper.java +++ b/core/java/src/net/i2p/time/Timestamper.java @@ -17,17 +17,20 @@ import net.i2p.util.Log; public class Timestamper implements Runnable { private I2PAppContext _context; private Log _log; - private List _servers; - private List _listeners; + private List _servers; + private List _priorityServers; + private List _listeners; private int _queryFrequency; private int _concurringServers; private volatile boolean _disabled; private boolean _daemon; private boolean _initialized; + private boolean _wellSynced; + private static final int MIN_QUERY_FREQUENCY = 5*60*1000; private static final int DEFAULT_QUERY_FREQUENCY = 5*60*1000; - private static final String DEFAULT_SERVER_LIST = "0.pool.ntp.org, 1.pool.ntp.org, 2.pool.ntp.org"; - private static final boolean DEFAULT_DISABLED = true; + private static final String DEFAULT_SERVER_LIST = "0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org"; + private static final String DEFAULT_DISABLED = "true"; /** how many times do we have to query if we are changing the clock? */ private static final int DEFAULT_CONCURRING_SERVERS = 3; @@ -35,6 +38,7 @@ public class Timestamper implements Runnable { public static final String PROP_SERVER_LIST = "time.sntpServerList"; public static final String PROP_DISABLED = "time.disabled"; public static final String PROP_CONCURRING_SERVERS = "time.concurringServers"; + public static final String PROP_IP_COUNTRY = "i2np.lastCountry"; /** if different SNTP servers differ by more than 10s, someone is b0rked */ private static final int MAX_VARIANCE = 10*1000; @@ -50,7 +54,8 @@ public class Timestamper implements Runnable { _context = ctx; _daemon = daemon; _initialized = false; - _servers = new ArrayList(1); + _wellSynced = false; + _servers = new ArrayList(3); _listeners = new ArrayList(1); if (lsnr != null) _listeners.add(lsnr); @@ -115,36 +120,47 @@ public class Timestamper implements Runnable { _log = _context.logManager().getLog(Timestamper.class); if (_log.shouldLog(Log.INFO)) _log.info("Starting timestamper"); - - if (_log.shouldLog(Log.INFO)) - _log.info("Starting up timestamper"); boolean lastFailed = false; try { while (true) { updateConfig(); if (!_disabled) { - String serverList[] = null; - synchronized (_servers) { - serverList = new String[_servers.size()]; - for (int i = 0; i < serverList.length; i++) - serverList[i] = (String)_servers.get(i); + // first the servers for our country, if we know what country we're in... + if (_priorityServers != null) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Querying servers " + _priorityServers); + try { + lastFailed = !queryTime(_priorityServers.toArray(new String[_priorityServers.size()])); + } catch (IllegalArgumentException iae) { + if ( (!lastFailed) && (_log.shouldLog(Log.WARN)) ) + _log.warn("Unable to reach country-specific NTP servers"); + lastFailed = true; + } } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Querying servers " + _servers); - try { - lastFailed = !queryTime(serverList); - } catch (IllegalArgumentException iae) { - if ( (!lastFailed) && (_log.shouldLog(Log.ERROR)) ) - _log.error("Unable to reach any of the NTP servers - network disconnected?"); - lastFailed = true; + // ... and then the global list, if that failed + if (_priorityServers == null || lastFailed) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Querying servers " + _servers); + try { + lastFailed = !queryTime(_servers.toArray(new String[_servers.size()])); + } catch (IllegalArgumentException iae) { + if ( (!lastFailed) && (_log.shouldLog(Log.ERROR)) ) + _log.error("Unable to reach any of the NTP servers - network disconnected?"); + lastFailed = true; + } } } _initialized = true; synchronized (this) { notifyAll(); } - long sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency; - if (lastFailed) + long sleepTime; + if (lastFailed) { sleepTime = 30*1000; + } else { + sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency; + if (_wellSynced) + sleepTime *= 3; + } try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {} } } catch (Throwable t) { @@ -160,6 +176,7 @@ public class Timestamper implements Runnable { long found[] = new long[_concurringServers]; long now = -1; long expectedDelta = 0; + _wellSynced = false; for (int i = 0; i < _concurringServers; i++) { try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} now = NtpClient.currentTime(serverList); @@ -169,6 +186,8 @@ public class Timestamper implements Runnable { if (Math.abs(delta) < MAX_VARIANCE) { if (_log.shouldLog(Log.INFO)) _log.info("a single SNTP query was within the tolerance (" + delta + "ms)"); + // If less than a half second on the first try, we're in good shape + _wellSynced = Math.abs(delta) < 500; break; } else { // outside the tolerance, lets iterate across the concurring queries @@ -224,56 +243,36 @@ public class Timestamper implements Runnable { */ private void updateConfig() { String serverList = _context.getProperty(PROP_SERVER_LIST); - if ( (serverList == null) || (serverList.trim().length() <= 0) ) + if ( (serverList == null) || (serverList.trim().length() <= 0) ) { serverList = DEFAULT_SERVER_LIST; - synchronized (_servers) { - _servers.clear(); - StringTokenizer tok = new StringTokenizer(serverList, ","); - while (tok.hasMoreTokens()) { - String val = (String)tok.nextToken(); - val = val.trim(); - if (val.length() > 0) - _servers.add(val); - } - } - - String freq = _context.getProperty(PROP_QUERY_FREQUENCY); - if ( (freq == null) || (freq.trim().length() <= 0) ) - freq = DEFAULT_QUERY_FREQUENCY + ""; - try { - int ms = Integer.parseInt(freq); - if (ms > 60*1000) { - _queryFrequency = ms; + String country = _context.getProperty(PROP_IP_COUNTRY); + if (country != null) { + _priorityServers = new ArrayList(3); + for (int i = 0; i < 3; i++) + _priorityServers.add(i + "." + country + ".pool.ntp.org"); } else { - if ( (_log != null) && (_log.shouldLog(Log.ERROR)) ) - _log.error("Query frequency once every " + ms + "ms is too fast!"); - _queryFrequency = DEFAULT_QUERY_FREQUENCY; + _priorityServers = null; } - } catch (NumberFormatException nfe) { - if ( (_log != null) && (_log.shouldLog(Log.WARN)) ) - _log.warn("Invalid query frequency [" + freq + "], falling back on " + DEFAULT_QUERY_FREQUENCY); - _queryFrequency = DEFAULT_QUERY_FREQUENCY; + } else { + _priorityServers = null; + } + _servers.clear(); + StringTokenizer tok = new StringTokenizer(serverList, ", "); + while (tok.hasMoreTokens()) { + String val = (String)tok.nextToken(); + val = val.trim(); + if (val.length() > 0) + _servers.add(val); } - String disabled = _context.getProperty(PROP_DISABLED); - if (disabled == null) - disabled = DEFAULT_DISABLED + ""; + _queryFrequency = Math.max(MIN_QUERY_FREQUENCY, + _context.getProperty(PROP_QUERY_FREQUENCY, DEFAULT_QUERY_FREQUENCY)); + + String disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED); _disabled = Boolean.valueOf(disabled).booleanValue(); - String concurring = _context.getProperty(PROP_CONCURRING_SERVERS); - if (concurring == null) { - _concurringServers = DEFAULT_CONCURRING_SERVERS; - } else { - try { - int servers = Integer.parseInt(concurring); - if ( (servers > 0) && (servers < 5) ) - _concurringServers = servers; - else - _concurringServers = DEFAULT_CONCURRING_SERVERS; - } catch (NumberFormatException nfe) { - _concurringServers = DEFAULT_CONCURRING_SERVERS; - } - } + _concurringServers = Math.min(4, Math.max(1, + _context.getProperty(PROP_CONCURRING_SERVERS, DEFAULT_CONCURRING_SERVERS))); } public static void main(String args[]) { @@ -297,4 +296,4 @@ public class Timestamper implements Runnable { */ public void setNow(long now); } -} \ No newline at end of file +} diff --git a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java index f50e7b368..e192e7752 100644 --- a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java @@ -58,6 +58,7 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade { _routers.remove(dbEntry); } + public Set getAllRouters() { return new HashSet(_routers.keySet()); } public Set findNearestRouters(Hash key, int maxNumRouters, Set peersToIgnore) { return new HashSet(_routers.values()); } public void renderStatusHTML(Writer out) throws IOException {} diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index ded9bee26..1ff7af132 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -55,6 +55,7 @@ public abstract class NetworkDatabaseFacade implements Service { public abstract void unpublish(LeaseSet localLeaseSet); public abstract void fail(Hash dbEntry); + public abstract Set getAllRouters(); public int getKnownRouters() { return 0; } public int getKnownLeaseSets() { return 0; } public void renderRouterInfoHTML(Writer out, String s) throws IOException {} 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 4c935be54..37503ecac 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -311,7 +311,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { } /** get the hashes for all known routers */ - Set getAllRouters() { + public Set getAllRouters() { if (!_initialized) return new HashSet(0); Set keys = _ds.getKeys(); Set rv = new HashSet(keys.size()); diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index 906df2036..d75706a02 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -76,6 +76,7 @@ public class ProfileOrganizer { */ public static final String PROP_MINIMUM_FAST_PEERS = "profileOrganizer.minFastPeers"; public static final int DEFAULT_MINIMUM_FAST_PEERS = 8; + private static final int DEFAULT_MAXIMUM_FAST_PEERS = 16; /** * Defines the minimum number of 'high capacity' peers that the organizer should * select when using the mean - if less than this many are available, select the @@ -1141,10 +1142,14 @@ public class ProfileOrganizer { * This parameter should help deal with a lack of diversity in the tunnels created when some * peers are particularly fast. * + * Increase default by two for every local destination, up to a max. + * * @return minimum number of peers to be placed in the 'fast' group */ protected int getMinimumFastPeers() { - return _context.getProperty(PROP_MINIMUM_FAST_PEERS, DEFAULT_MINIMUM_FAST_PEERS); + int def = Math.min(DEFAULT_MAXIMUM_FAST_PEERS, + (2 *_context.clientManager().listClients().size()) + DEFAULT_MINIMUM_FAST_PEERS - 2); + return _context.getProperty(PROP_MINIMUM_FAST_PEERS, def); } diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index 53db9062a..2bddc3982 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -375,15 +375,13 @@ public class CommSystemFacadeImpl extends CommSystemFacade { /** * Collect the IPs for all routers in the DB, and queue them for lookup, * then fire off the periodic lookup task for the first time. - * - * We could use getAllRouters() if it were public, and that would be faster, but - * we only do this once. */ private class QueueAll implements SimpleTimer.TimedEvent { public void timeReached() { - Set routers = _context.netDb().findNearestRouters(_context.routerHash(), _context.netDb().getKnownRouters(), null); - for (Iterator iter = routers.iterator(); iter.hasNext(); ) { - RouterInfo ri = (RouterInfo) iter.next(); + for (Iterator iter = _context.netDb().getAllRouters().iterator(); iter.hasNext(); ) { + RouterInfo ri = _context.netDb().lookupRouterInfoLocally(iter.next()); + if (ri == null) + continue; String host = getIPString(ri); if (host == null) continue; @@ -406,6 +404,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade { /** * Uses the transport IP first because that lookup is fast, * then the SSU IP from the netDb. + * + * @return two-letter lower-case country code or null */ public String getCountry(Hash peer) { byte[] ip = TransportImpl.getIP(peer); diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java index 20261e968..a7da9fad8 100644 --- a/router/java/src/net/i2p/router/transport/GeoIP.java +++ b/router/java/src/net/i2p/router/transport/GeoIP.java @@ -15,8 +15,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; +import net.i2p.router.RouterContext; import net.i2p.util.ConcurrentHashSet; import net.i2p.util.Log; @@ -37,14 +37,14 @@ import net.i2p.util.Log; */ public class GeoIP { private Log _log; - private I2PAppContext _context; + private RouterContext _context; private final Map _codeToName; private final Map _IPToCountry; private final Set _pendingSearch; private final Set _notFound; private final AtomicBoolean _lock; - public GeoIP(I2PAppContext context) { + public GeoIP(RouterContext context) { _context = context; _log = context.logManager().getLog(GeoIP.class); _codeToName = new ConcurrentHashMap(); @@ -59,6 +59,7 @@ public class GeoIP { static final String GEOIP_DIR_DEFAULT = "geoip"; static final String GEOIP_FILE_DEFAULT = "geoip.txt"; static final String COUNTRY_FILE_DEFAULT = "countries.txt"; + public static final String PROP_IP_COUNTRY = "i2np.lastCountry"; /** * Fire off a thread to lookup all pending IPs. @@ -87,6 +88,7 @@ public class GeoIP { } LookupJob j = new LookupJob(); j.run(); + updateOurCountry(); } private class LookupJob implements Runnable { @@ -232,6 +234,19 @@ public class GeoIP { return rv; } + /** + * Put our country code in the config, where others (such as Timestamper) can get it, + * and it will be there next time at startup. + */ + private void updateOurCountry() { + String oldCountry = _context.router().getConfigSetting(PROP_IP_COUNTRY); + String country = _context.commSystem().getCountry(_context.routerHash()); + if (country != null && !country.equals(oldCountry)) { + _context.router().setConfigSetting(PROP_IP_COUNTRY, country); + _context.router().saveConfig(); + } + } + /** * Add to the list needing lookup */ @@ -296,6 +311,7 @@ public class GeoIP { return _codeToName.get(code); } +/*** doesn't work since switched to RouterContext above public static void main(String args[]) { GeoIP g = new GeoIP(new I2PAppContext()); String tests[] = {"0.0.0.0", "0.0.0.1", "0.0.0.2", "0.0.0.255", "1.0.0.0", @@ -309,4 +325,5 @@ public class GeoIP { System.out.println(tests[i] + " : " + g.get(tests[i])); } +***/ } diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java index 9bf20e334..e30783768 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java @@ -103,7 +103,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { private long _nextInfoTime; private static final int META_FREQUENCY = 10*60*1000; - private static final int INFO_FREQUENCY = 6*60*60*1000; + /** how often we send our routerinfo unsolicited */ + private static final int INFO_FREQUENCY = 90*60*1000; /** * Why this is 16K, and where it is documented, good question? * We claim we can do 32K datagrams so this is a problem. @@ -200,7 +201,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _transport.inboundEstablished(this); _establishState = null; _nextMetaTime = System.currentTimeMillis() + _context.random().nextInt(META_FREQUENCY); - _nextInfoTime = System.currentTimeMillis() + INFO_FREQUENCY + _context.random().nextInt(INFO_FREQUENCY); + _nextInfoTime = System.currentTimeMillis() + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY); } public long getClockSkew() { return _clockSkew; } public long getUptime() { @@ -370,7 +371,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _context.statManager().addRateData("ntcp.infoMessageEnqueued", 1, 0); send(infoMsg); - enqueueFloodfillMessage(target); + // See comment below + //enqueueFloodfillMessage(target); } else { if (_isInbound) { // ok, we shouldn't have enqueued it yet, as we havent received their info @@ -380,12 +382,18 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { } } - private static final int PEERS_TO_FLOOD = 3; + //private static final int PEERS_TO_FLOOD = 3; /** * to prevent people from losing track of the floodfill peers completely, lets periodically * send those we are connected to references to the floodfill peers that we know + * + * Do we really need this anymore??? Peers shouldn't lose track anymore, and if they do, + * FloodOnlyLookupJob should recover. + * The bandwidth isn't so much, but it is a lot of extra data at connection startup, which + * hurts latency of new connections. */ +/********** private void enqueueFloodfillMessage(RouterInfo target) { FloodfillNetworkDatabaseFacade fac = (FloodfillNetworkDatabaseFacade)_context.netDb(); List peers = fac.getFloodfillPeers(); @@ -414,6 +422,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { send(infoMsg); } } +***********/ /** * @param clockSkew alice's clock minus bob's clock in seconds (may be negative, obviously, but |val| should @@ -439,7 +448,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { msgs = (_outbound.size() > 0); } _nextMetaTime = System.currentTimeMillis() + _context.random().nextInt(META_FREQUENCY); - _nextInfoTime = System.currentTimeMillis() + INFO_FREQUENCY + _context.random().nextInt(INFO_FREQUENCY); + _nextInfoTime = System.currentTimeMillis() + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY); if (msgs) _transport.getWriter().wantsWrite(this, "outbound established"); } @@ -465,11 +474,12 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { * */ synchronized void prepareNextWrite() { - if (FAST_LARGE) + //if (FAST_LARGE) prepareNextWriteFast(); - else - prepareNextWriteSmall(); + //else + // prepareNextWriteSmall(); } +/********** nobody's tried this one in years private void prepareNextWriteSmall() { if (_log.shouldLog(Log.DEBUG)) _log.debug("prepare next write w/ isInbound? " + _isInbound + " established? " + _established); @@ -563,9 +573,10 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { // the floodfill netDb servers, but they may...) if (_nextInfoTime <= System.currentTimeMillis()) { enqueueInfoMessage(); - _nextInfoTime = System.currentTimeMillis() + INFO_FREQUENCY + _context.random().nextInt(INFO_FREQUENCY); + _nextInfoTime = System.currentTimeMillis() + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY); } } +**********/ /** * prepare the next i2np message for transmission. this should be run from @@ -647,7 +658,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { if (_nextInfoTime <= System.currentTimeMillis()) { // perhaps this should check to see if we are bw throttled, etc? enqueueInfoMessage(); - _nextInfoTime = System.currentTimeMillis() + INFO_FREQUENCY + _context.random().nextInt(INFO_FREQUENCY); + _nextInfoTime = System.currentTimeMillis() + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY); } } 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 7cd94f053..b438d115f 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -474,6 +474,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _context.statManager().addRateData("udp.addressUpdated", 1, 0); if (!fixedPort) _context.router().setConfigSetting(PROP_EXTERNAL_PORT, ourPort+""); + // queue a country code lookup of the new IP + _context.commSystem().queueLookup(ourIP); // store these for laptop-mode (change ident on restart... or every time... when IP changes) _context.router().setConfigSetting(PROP_IP, _externalListenHost.getHostAddress()); _context.router().setConfigSetting(PROP_IP_CHANGE, "" + _context.clock().now());