diff --git a/INSTALL.txt b/INSTALL.txt index 15a962df2aa38a7dcf475348843e93e0b821b993..adbbc5ad51fd29939ed951953970b3c828b03013 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,10 +1,17 @@ I2P source installation instructions +Prerequisites to build from source: + Java SDK (preferably Sun) 1.5.0 or higher (1.6 recommended) + Apache Ant 1.7.0 or higher + Optional, For multilanguage support: The xgettext, msgfmt, and msgmerge tools installed + from the GNU gettext package http://www.gnu.org/software/gettext/ + To build and install I2P from source, you must first build and package up the appropriate installer by running: ant pkg + This will produce a few key files: * install.jar: the GUI and console installer * i2pinstall.exe: the GUI and console installer wrapped for cross-platform execution @@ -18,9 +25,6 @@ Or run the GUI installer: Or move the update file into an existing installation directory and restart. -You will need to have ant installed from http://ant.apache.org/ -(1.7.0 or newer) - Supported JVMs: Windows: Latest available from http://java.sun.com/ (1.5+ supported) Linux: Latest available from http://java.sun.com/ (1.5+ supported) diff --git a/README.txt b/README.txt index 3aa2141bb475542b5c49b3a1586c3eb7560ba7da..c5ddc12bd89a529fe40e30534ee200446f824b73 100644 --- a/README.txt +++ b/README.txt @@ -1,11 +1,13 @@ Prerequisites to build from source: Java SDK (preferably Sun) 1.5.0 or higher (1.6 recommended) Apache Ant 1.7.0 or higher + Optional, For multilanguage support: The xgettext, msgfmt, and msgmerge tools installed + from the GNU gettext package http://www.gnu.org/software/gettext/ To build: ant pkg Run 'ant' with no arguments to see other build options. - See http://www.i2p2.de/download.html for installation instructions. + See INSTALL.txt or http://www.i2p2.de/download.html for installation instructions. Documentation: http://www.i2p2.de/ diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index 973357baacc8f2204d0bbf405aed984b9dcd8047..8140282d19f491b17f57d38b10c5a1ba0d19a891 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -179,7 +179,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { if (get.fetch()) { String lastmod = get.getLastModified(); if (lastmod != null) { - if (!(_context instanceof RouterContext)) return; + if (!(_context.isRouterContext())) return; long modtime = parse822Date(lastmod); if (modtime <= 0) return; String lastUpdate = _context.getProperty(UpdateHandler.PROP_LAST_UPDATE_TIME); @@ -310,7 +310,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { if (_log.shouldLog(Log.DEBUG)) _log.debug("Policy requests update, so we update"); UpdateHandler handler = null; - if (_context instanceof RouterContext) { + if (_context.isRouterContext()) { handler = new UpdateHandler((RouterContext)_context); } else { List contexts = RouterContext.listContexts(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java index 9ac1ae54225ed2e9b9b6c70677d78e6b437bd405..4ddc6e6f453a89953b69d63922d18bfdce72cf77 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java @@ -40,7 +40,7 @@ public class StatsGenerator { String group = (String)entry.getKey(); Set stats = (Set)entry.getValue(); buf.append("<option value=\"/stats.jsp#").append(group).append("\">"); - buf.append(group).append("</option>\n"); + buf.append(_(group)).append("</option>\n"); for (Iterator statIter = stats.iterator(); statIter.hasNext(); ) { String stat = (String)statIter.next(); buf.append("<option value=\"/stats.jsp#"); @@ -52,7 +52,7 @@ public class StatsGenerator { out.write(buf.toString()); buf.setLength(0); } - buf.append("</select> <input type=\"submit\" value=\"GO\" />"); + buf.append("</select> <input type=\"submit\" value=\"").append(_("GO")).append("\" />"); buf.append("</form>"); buf.append(_("Statistics gathered during this router's uptime")).append(" ("); @@ -69,7 +69,7 @@ public class StatsGenerator { buf.append("<h3><a name=\""); buf.append(group); buf.append("\">"); - buf.append(group); + buf.append(_(group)); buf.append("</a></h3>"); buf.append("<ul>"); out.write(buf.toString()); @@ -104,7 +104,7 @@ public class StatsGenerator { for (int i = 0; i < periods.length; i++) { if (periods[i] > uptime) break; - renderPeriod(buf, periods[i], "frequency"); + renderPeriod(buf, periods[i], _("frequency")); Frequency curFreq = freq.getFrequency(periods[i]); buf.append(" <i>avg per period:</i> ("); buf.append(num(curFreq.getAverageEventsPerPeriod())); @@ -138,7 +138,7 @@ public class StatsGenerator { buf.append("</i><br>"); } if (rate.getLifetimeEventCount() <= 0) { - buf.append("No lifetime events<br>\n"); + buf.append(_("No lifetime events")).append("<br>\n"); return; } long now = _context.clock().now(); @@ -150,9 +150,9 @@ public class StatsGenerator { if (curRate.getLastCoalesceDate() <= curRate.getCreationDate()) break; buf.append("<li>"); - renderPeriod(buf, periods[i], "rate"); + renderPeriod(buf, periods[i], _("rate")); if (curRate.getLastEventCount() > 0) { - buf.append( "<i>avg value:</i> ("); + buf.append( "<i>").append(_("avg value")).append(":</i> ("); buf.append(num(curRate.getAverageValue())); buf.append(" peak "); buf.append(num(curRate.getExtremeAverageValue())); @@ -181,21 +181,21 @@ public class StatsGenerator { buf.append(num(curRate.getExtremeSaturationLimit())); buf.append(")"); } - buf.append(" <i>events:</i> "); + buf.append(" <i>").append(_("events")).append(":</i> "); buf.append(curRate.getLastEventCount()); buf.append(" <i>in this period which ended:</i> "); buf.append(DataHelper.formatDuration(now - curRate.getLastCoalesceDate())); buf.append(" ago "); } else { - buf.append(" <i>No events</i> "); + buf.append(" <i>").append(_("No events")).append("</i> "); } long numPeriods = curRate.getLifetimePeriods(); if (numPeriods > 0) { double avgFrequency = curRate.getLifetimeEventCount() / (double)numPeriods; double peakFrequency = curRate.getExtremeEventCount(); - buf.append(" (lifetime average: "); + buf.append(" (").append(_("lifetime average")).append(": "); buf.append(num(avgFrequency)); - buf.append(", peak average: "); + buf.append(", ").append(_("peak average")).append(": "); buf.append(curRate.getExtremeEventCount()); buf.append(")"); } @@ -213,7 +213,7 @@ public class StatsGenerator { buf.append("</li>\n"); } // Display the strict average - buf.append("<li><b>lifetime average value:</b> "); + buf.append("<li><b>").append(_("lifetime average value")).append(":</b> "); buf.append(num(rate.getLifetimeAverageValue())); buf.append(" over "); buf.append(rate.getLifetimeEventCount()); @@ -240,4 +240,9 @@ public class StatsGenerator { private String _(String s) { return Messages.getString(s, _context); } + + /** translate a string */ + private String _(String s, Object o) { + return Messages.getString(s, o, _context); + } } 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 1fc6c31b7096df0094e0f2942fccbf333e2264b1..516855e2db64279485a8ba8e2aafd139124a7fee 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java @@ -62,6 +62,9 @@ public class SummaryHelper extends HelperBase { return DataHelper.formatDuration(router.getUptime()); } +/** + this displayed offset, not skew - now handled in reachability() + private String timeSkew() { if (_context == null) return ""; //if (!_context.clock().getUpdatedSuccessfully()) @@ -72,6 +75,7 @@ public class SummaryHelper extends HelperBase { return ""; return " (" + DataHelper.formatDuration(diff) + " " + _("skew") + ")"; } +**/ public boolean allowReseed() { return _context.netDb().isInitialized() && @@ -83,15 +87,20 @@ public class SummaryHelper extends HelperBase { public int getAllPeers() { return Math.max(_context.netDb().getKnownRouters() - 1, 0); } public String getReachability() { - return reachability() + timeSkew(); + return reachability(); // + timeSkew(); } private String reachability() { if (_context.router().getUptime() > 60*1000 && (!_context.router().gracefulShutdownInProgress()) && !_context.clientManager().isAlive()) return _("ERR-Client Manager I2CP Error - check logs"); // not a router problem but the user should know - if (!_context.clock().getUpdatedSuccessfully()) - return _("ERR-ClockSkew"); + // Warn based on actual skew from peers, not update status, so if we successfully offset + // the clock, we don't complain. + //if (!_context.clock().getUpdatedSuccessfully()) + Long skew = _context.commSystem().getFramedAveragePeerClockSkew(33); + // Display the actual skew, not the offset + if (skew != null && Math.abs(skew.longValue()) > 45) + return _("ERR-Clock Skew of {0}", DataHelper.formatDuration(Math.abs(skew.longValue()) * 1000)); if (_context.router().isHidden()) return _("Hidden"); @@ -118,7 +127,9 @@ public class SummaryHelper extends HelperBase { default: ra = _context.router().getRouterInfo().getTargetAddress("SSU"); if (ra == null && _context.router().getUptime() > 5*60*1000) { - if (_context.getProperty(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME) == null || + if (getActivePeers() <= 0) + return _("ERR-No Active Peers, Check Network Connection and Firewall"); + else if (_context.getProperty(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME) == null || _context.getProperty(ConfigNetHelper.PROP_I2NP_NTCP_PORT) == null) return _("ERR-UDP Disabled and Inbound TCP host/port not set"); else diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java index 71aca046c339377c3268e625f05188f8bfa62add..523b4d3855bfae4fc78e4c31cfcddf5953396c34 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java @@ -233,7 +233,7 @@ public class TunnelRenderer { Collections.sort(peerList, new CountryComparator(this._context.commSystem())); out.write("<h2><a name=\"peers\"></a>" + _("Tunnel Counts By Peer") + "</h2>\n"); - out.write("<table><tr><th>" + _("Peer") + "</th><th>" + _("Expl. + Client") + "</th><th>" + _("% of total") + "</th><th>" + _("Part. from + to") + "</th><th>" + _("% of total") + "</th></tr>\n"); + out.write("<table><tr><th>" + _("Peer") + "</th><th>" + _("Our Tunnels") + "</th><th>" + _("% of total") + "</th><th>" + _("Participating Tunnels") + "</th><th>" + _("% of total") + "</th></tr>\n"); for (Hash h : peerList) { out.write("<tr> <td class=\"cells\" align=\"center\">"); out.write(netDbLink(h)); @@ -251,7 +251,7 @@ public class TunnelRenderer { out.write('0'); out.write('\n'); } - out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>" + _("Tunnels") + "</b> <td align=\"center\"><b>" + tunnelCount); + out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>" + _("Totals") + "</b> <td align=\"center\"><b>" + tunnelCount); out.write("</b> <td> </td> <td align=\"center\"><b>" + partCount); out.write("</b> <td> </td></tr></table></div>\n"); } diff --git a/apps/routerconsole/java/strings/Strings.java b/apps/routerconsole/java/strings/Strings.java index cddec4197baf89b3443ef6f9f0223b78bd4238fd..0805d479a119730e809bcd2d9a523af8194872d0 100644 --- a/apps/routerconsole/java/strings/Strings.java +++ b/apps/routerconsole/java/strings/Strings.java @@ -52,5 +52,24 @@ class Dummy { _("dark"); _("light"); _("midnight"); + + // stat groups for stats.jsp + _("Bandwidth"); + _("BandwidthLimiter"); + _("ClientMessages"); + _("Encryption"); + _("i2cp"); + _("I2PTunnel"); + _("InNetPool"); + _("JobQueue"); + _("NetworkDatabase"); + _("ntcp"); + _("Peers"); + _("Router"); + _("Stream"); + _("Throttle"); + _("Transport"); + _("Tunnels"); + _("udp"); } } diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 1cdb6b29709789c43700487370bb2615b8895aa9..974b69b84d79362fd37cabaf2a9cce8ef005972e 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -722,4 +722,11 @@ public class I2PAppContext { return new HashSet(_shutdownTasks); } + /** + * Use this instead of context instanceof RouterContext + * @since 0.7.9 + */ + public boolean isRouterContext() { + return false; + } } diff --git a/core/java/src/net/i2p/stat/RateStat.java b/core/java/src/net/i2p/stat/RateStat.java index b021d59f4ef0eeec4aa1a2ea2650835a14721894..79ddec51980efdcf689f9f95b4cade1f25223d18 100644 --- a/core/java/src/net/i2p/stat/RateStat.java +++ b/core/java/src/net/i2p/stat/RateStat.java @@ -170,6 +170,7 @@ public class RateStat { } } +/********* public static void main(String args[]) { RateStat rs = new RateStat("moo", "moo moo moo", "cow trueisms", new long[] { 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000}); @@ -206,4 +207,5 @@ public class RateStat { } catch (InterruptedException ie) { // nop } } +*********/ } diff --git a/core/java/src/net/i2p/time/Timestamper.java b/core/java/src/net/i2p/time/Timestamper.java index e47ecd2599ca66c26f2129d6e37d5946a5e4c483..b1d763a3a731af949e1ab1bc73f77f53d19d012e 100644 --- a/core/java/src/net/i2p/time/Timestamper.java +++ b/core/java/src/net/i2p/time/Timestamper.java @@ -189,7 +189,10 @@ public class Timestamper implements Runnable { long expectedDelta = 0; _wellSynced = false; for (int i = 0; i < _concurringServers; i++) { - try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} + if (i > 0) { + // this delays startup when net is disconnected or the timeserver list is bad, don't make it too long + try { Thread.sleep(2*1000); } catch (InterruptedException ie) {} + } now = NtpClient.currentTime(serverList); long delta = now - _context.clock().now(); found[i] = delta; diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 7e231097b40cb9ac80e4e472438b918981489fba..4ed70a77227de504f385ad50b2defab6893a8a40 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -41,6 +41,7 @@ import net.i2p.stat.Rate; import net.i2p.stat.RateStat; import net.i2p.stat.StatManager; import net.i2p.util.FileUtil; +import net.i2p.util.I2PAppThread; import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.SimpleScheduler; @@ -201,6 +202,8 @@ public class Router { installUpdates(); // Apps may use this as an easy way to determine if they are in the router JVM + // But context.isRouterContext() is even easier... + // Both of these as of 0.7.9 System.setProperty("router.version", RouterVersion.VERSION); // NOW we start all the activity @@ -228,14 +231,10 @@ public class Router { } }; _shutdownHook = new ShutdownHook(_context); - _gracefulShutdownDetector = new I2PThread(new GracefulShutdown()); - _gracefulShutdownDetector.setDaemon(true); - _gracefulShutdownDetector.setName("Graceful shutdown hook"); + _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(), "Graceful shutdown hook", true); _gracefulShutdownDetector.start(); - I2PThread watchdog = new I2PThread(new RouterWatchdog(_context)); - watchdog.setName("RouterWatchdog"); - watchdog.setDaemon(true); + Thread watchdog = new I2PAppThread(new RouterWatchdog(_context), "RouterWatchdog", true); watchdog.start(); } @@ -339,7 +338,7 @@ public class Router { long waited = System.currentTimeMillis() - before; if (_log.shouldLog(Log.INFO)) _log.info("Waited " + waited + "ms to initialize"); - + _context.jobQueue().addJob(new StartupJob(_context)); } diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index 2a3410348c297f9d7ef5d84780d529b9e87bd94e..8f1c240c35af0f167004437568433d3d63f93e9d 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -379,4 +379,12 @@ public class RouterContext extends I2PAppContext { } } + /** + * Use this instead of context instanceof RouterContext + * @return true + * @since 0.7.9 + */ + public boolean isRouterContext() { + return true; + } } diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java index f37f01aa267eaf75f8c1de3857090d5ec8d47949..2e93cb3e02e3723fa26e687bbb52207d7aa4aa8d 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -15,7 +15,7 @@ import java.util.StringTokenizer; import net.i2p.I2PAppContext; import net.i2p.router.RouterContext; import net.i2p.util.EepGet; -import net.i2p.util.I2PThread; +import net.i2p.util.I2PAppThread; import net.i2p.util.Log; /** @@ -52,13 +52,15 @@ public class Reseeder { return; } else { System.setProperty(PROP_INPROGRESS, "true"); - I2PThread reseed = new I2PThread(_reseedRunner, "Reseed"); + // set to daemon so it doesn't hang a shutdown + Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true); reseed.start(); } } } + /** Todo: translate the messages sent via PROP_STATUS */ public class ReseedRunner implements Runnable, EepGet.StatusListener { private boolean _isRunning; diff --git a/router/java/src/net/i2p/router/peermanager/DBHistory.java b/router/java/src/net/i2p/router/peermanager/DBHistory.java index a8e084e491c963baba154a92add12ee2ab38b6be..36b9bc9a3015d08fa1e1f3689a2292fd7d726571 100644 --- a/router/java/src/net/i2p/router/peermanager/DBHistory.java +++ b/router/java/src/net/i2p/router/peermanager/DBHistory.java @@ -105,6 +105,7 @@ public class DBHistory { */ public RateStat getFailedLookupRate() { return _failedLookupRate; } + /** not sure how much this is used, to be investigated */ public RateStat getInvalidReplyRate() { return _invalidReplyRate; } /** @@ -115,6 +116,7 @@ public class DBHistory { public void lookupSuccessful() { _successfulLookups++; _failedLookupRate.addData(0, 0); + _context.statManager().addRateData("peer.failedLookupRate", 0, 0); _lastLookupSuccessful = _context.clock().now(); } @@ -124,6 +126,7 @@ public class DBHistory { public void lookupFailed() { _failedLookups++; _failedLookupRate.addData(1, 0); + _context.statManager().addRateData("peer.failedLookupRate", 1, 0); _lastLookupFailed = _context.clock().now(); } @@ -136,6 +139,7 @@ public class DBHistory { // Fixme, redefined this to include both lookup and store fails, // need to fix the javadocs _failedLookupRate.addData(0, 0); + _context.statManager().addRateData("peer.failedLookupRate", 0, 0); _lastStoreSuccessful = _context.clock().now(); } @@ -275,9 +279,9 @@ public class DBHistory { private void createRates(String statGroup) { if (_failedLookupRate == null) - _failedLookupRate = new RateStat("dbHistory.failedLookupRate", "How often does this peer to respond to a lookup?", statGroup, new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); + _failedLookupRate = new RateStat("dbHistory.failedLookupRate", "How often does this peer to respond to a lookup?", statGroup, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); if (_invalidReplyRate == null) - _invalidReplyRate = new RateStat("dbHistory.invalidReplyRate", "How often does this peer give us a bad (nonexistant, forged, etc) peer?", statGroup, new long[] { 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); + _invalidReplyRate = new RateStat("dbHistory.invalidReplyRate", "How often does this peer give us a bad (nonexistant, forged, etc) peer?", statGroup, new long[] { 30*60*1000l }); _failedLookupRate.setStatLog(_context.statManager().getStatLog()); _invalidReplyRate.setStatLog(_context.statManager().getStatLog()); } diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index 20f0fba3d4b5977c2247efab62056070b3f98a9c..d249dc9687c77116cf3c4a8ea3220958b93e34b7 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -97,10 +97,10 @@ public class ProfileOrganizer { _log = context.logManager().getLog(ProfileOrganizer.class); _comp = new InverseCapacityComparator(); _fastPeers = new HashMap(16); - _highCapacityPeers = new HashMap(16); + _highCapacityPeers = new HashMap(32); _wellIntegratedPeers = new HashMap(16); - _notFailingPeers = new HashMap(64); - _notFailingPeersList = new ArrayList(64); + _notFailingPeers = new HashMap(256); + _notFailingPeersList = new ArrayList(256); _failingPeers = new HashMap(16); _strictCapacityOrder = new TreeSet(_comp); _thresholdSpeedValue = 0.0d; @@ -113,6 +113,8 @@ public class ProfileOrganizer { _context.statManager().createRateStat("peer.profileThresholdTime", "How long the reorg takes determining the tier thresholds", "Peers", new long[] { 10*60*1000 }); _context.statManager().createRateStat("peer.profilePlaceTime", "How long the reorg takes placing peers in the tiers", "Peers", new long[] { 10*60*1000 }); _context.statManager().createRateStat("peer.profileReorgTime", "How long the reorg takes overall", "Peers", new long[] { 10*60*1000 }); + // used in DBHistory + _context.statManager().createRateStat("peer.failedLookupRate", "DB Lookup fail rate", "Peers", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); } private void getReadLock() { diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index 6a7eaca84b4d1e6ae7423a0e6172de229bfbc6f5..f1460b9f7a2bdd09c9f87ba776746e3ee58ef99b 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -77,31 +77,26 @@ public class CommSystemFacadeImpl extends CommSystemFacade { public boolean haveHighOutboundCapacity() { return (_manager == null ? false : _manager.haveHighOutboundCapacity()); } /** - * Framed average clock skew of connected peers in seconds, or null if we cannot answer. + * Framed average clock skew of connected peers in seconds, or the clock offset if we cannot answer. * Average is calculated over the middle "percentToInclude" peers. */ @Override public Long getFramedAveragePeerClockSkew(int percentToInclude) { if (_manager == null) { - if (_log.shouldLog(Log.INFO)) - _log.info("Returning null for framed averege peer clock skew (no transport manager)!"); - return null; + // round toward zero + return Long.valueOf(_context.clock().getOffset() / 1000); } Vector skews = _manager.getClockSkews(); if (skews == null) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Returning null for framed average peer clock skew (no data)!"); - return null; + return Long.valueOf(_context.clock().getOffset() / 1000); } - if (skews.size() < 20) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Returning null for framed average peer clock skew (only " + skews.size() + " peers)!"); - return null; + if (skews.size() < 5) { + return Long.valueOf(_context.clock().getOffset() / 1000); } // Going to calculate, sort them Collections.sort(skews); // Calculate frame size - int frameSize = (skews.size() * percentToInclude / 100); + int frameSize = Math.min((skews.size() * percentToInclude / 100), 2); int first = (skews.size() / 2) - (frameSize / 2); int last = (skews.size() / 2) + (frameSize / 2); // Sum skew values @@ -112,11 +107,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade { _log.debug("Adding clock skew " + i + " valued " + value + " s."); sum = sum + value; } - // Calculate average - Long framedAverageClockSkew = new Long(sum / frameSize); - if (_log.shouldLog(Log.INFO)) - _log.info("Our framed average peer clock skew is " + framedAverageClockSkew + " s."); - return framedAverageClockSkew; + // Calculate average (round toward zero) + return Long.valueOf(sum / frameSize); } public List getBids(OutNetMessage msg) { diff --git a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java index 37c173c75ce0c58a51b3a8df55b9e09fd5c1574c..30fbbdd27bbbdc371479b6e827a0d713490ec0a2 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java @@ -2,7 +2,7 @@ package net.i2p.router.tunnel; import java.util.List; -import net.i2p.I2PAppContext; +import net.i2p.router.RouterContext; import net.i2p.util.Log; /** @@ -45,11 +45,10 @@ import net.i2p.util.Log; * } */ public class BatchedPreprocessor extends TrivialPreprocessor { - private Log _log; private long _pendingSince; private String _name; - public BatchedPreprocessor(I2PAppContext ctx, String name) { + public BatchedPreprocessor(RouterContext ctx, String name) { super(ctx); _log = ctx.logManager().getLog(BatchedPreprocessor.class); _name = name; @@ -98,8 +97,9 @@ public class BatchedPreprocessor extends TrivialPreprocessor { return rv; } + /* See TunnelGateway.QueuePreprocessor for Javadoc */ @Override - public boolean preprocessQueue(List pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { + public boolean preprocessQueue(List<TunnelGateway.Pending> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { StringBuilder timingBuf = null; if (_log.shouldLog(Log.DEBUG)) { _log.debug("Preprocess queue with " + pending.size() + " to send"); @@ -116,12 +116,15 @@ public class BatchedPreprocessor extends TrivialPreprocessor { int batchCount = 0; int beforeLooping = pending.size(); + // loop until the queue is empty while (pending.size() > 0) { int allocated = 0; long beforePendingLoop = System.currentTimeMillis(); + + // loop until we fill up a single message for (int i = 0; i < pending.size(); i++) { long pendingStart = System.currentTimeMillis(); - TunnelGateway.Pending msg = (TunnelGateway.Pending)pending.get(i); + TunnelGateway.Pending msg = pending.get(i); int instructionsSize = getInstructionsSize(msg); instructionsSize += getInstructionAugmentationSize(msg, allocated, instructionsSize); int curWanted = msg.getData().length - msg.getOffset() + instructionsSize; @@ -135,7 +138,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { // the instructions alone exceed the size, so we won't get any // of the message into it. don't include it i--; - msg = (TunnelGateway.Pending)pending.get(i); + msg = pending.get(i); allocated -= curWanted; if (_log.shouldLog(Log.DEBUG)) _log.debug("Pushback of " + curWanted + " (message " + (i+1) + " in " + pending + ")"); @@ -144,6 +147,8 @@ public class BatchedPreprocessor extends TrivialPreprocessor { long waited = _context.clock().now() - _pendingSince; _context.statManager().addRateData("tunnel.batchDelaySent", pending.size(), waited); } + + // Send the message long beforeSend = System.currentTimeMillis(); _pendingSince = 0; send(pending, 0, i, sender, rec); @@ -154,8 +159,9 @@ public class BatchedPreprocessor extends TrivialPreprocessor { + " (last complete? " + (msg.getOffset() >= msg.getData().length) + ", off=" + msg.getOffset() + ", count=" + pending.size() + ")"); + // Remove what we sent from the pending queue for (int j = 0; j < i; j++) { - TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.remove(0); + TunnelGateway.Pending cur = pending.remove(0); if (cur.getOffset() < cur.getData().length) throw new IllegalArgumentException("i=" + i + " j=" + j + " off=" + cur.getOffset() + " len=" + cur.getData().length + " alloc=" + allocated); @@ -167,7 +173,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { } if (msg.getOffset() >= msg.getData().length) { // ok, this last message fit perfectly, remove it too - TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.remove(0); + TunnelGateway.Pending cur = pending.remove(0); if (timingBuf != null) timingBuf.append(" sent perfect fit " + cur).append("."); notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), msg.getData().length, msg.getMessageIds(), "flushed tail, remaining: " + pending); @@ -186,18 +192,18 @@ public class BatchedPreprocessor extends TrivialPreprocessor { + "/" + (beforeSend-start) + " pending current " + (pendingEnd-pendingStart)).append("."); break; - } + } // if >= full size if (timingBuf != null) timingBuf.append(" After pending loop " + (System.currentTimeMillis()-beforePendingLoop)).append("."); - } + } // for long afterCleared = System.currentTimeMillis(); if (_log.shouldLog(Log.INFO)) display(allocated, pending, "after looping to clear " + (beforeLooping - pending.size())); long afterDisplayed = System.currentTimeMillis(); if (allocated > 0) { - // after going through the entire pending list, we still don't - // have enough data to send a full message + // After going through the entire pending list, we have only a partial message. + // We might flush it or might not, but we are returning either way. if ( (pending.size() > FORCE_BATCH_FLUSH) || ( (_pendingSince > 0) && (getDelayAmount() <= 0) ) ) { // not even a full message, but we want to flush it anyway @@ -209,9 +215,10 @@ public class BatchedPreprocessor extends TrivialPreprocessor { send(pending, 0, pending.size()-1, sender, rec); _context.statManager().addRateData("tunnel.batchSmallFragments", FULL_SIZE - allocated, 0); + // Remove everything in the message from the pending queue int beforeSize = pending.size(); for (int i = 0; i < pending.size(); i++) { - TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.get(i); + TunnelGateway.Pending cur = pending.get(i); if (cur.getOffset() >= cur.getData().length) { pending.remove(i); notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed remaining"); @@ -246,7 +253,9 @@ public class BatchedPreprocessor extends TrivialPreprocessor { } return false; } + // won't get here, we returned } else { + // We didn't flush. Note that the messages remain on the pending list. _context.statManager().addRateData("tunnel.batchDelay", pending.size(), 0); if (_pendingSince <= 0) _pendingSince = _context.clock().now(); @@ -262,14 +271,15 @@ public class BatchedPreprocessor extends TrivialPreprocessor { } return true; } + // won't get here, we returned } else { // ok, we sent some, but haven't gone back for another // pass yet. keep looping if (timingBuf != null) timingBuf.append(" Keep looping"); - } - } + } // if allocated + } // while if (_log.shouldLog(Log.DEBUG)) _log.debug("Sent everything on the list (pending=" + pending.size() + ")"); @@ -283,7 +293,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { return false; } - private void display(long allocated, List pending, String title) { + private void display(long allocated, List<TunnelGateway.Pending> pending, String title) { if (_log.shouldLog(Log.INFO)) { long highestDelay = 0; StringBuilder buf = new StringBuilder(); @@ -294,7 +304,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { if (_pendingSince > 0) buf.append(" delay: ").append(getDelayAmount(false)); for (int i = 0; i < pending.size(); i++) { - TunnelGateway.Pending curPending = (TunnelGateway.Pending)pending.get(i); + TunnelGateway.Pending curPending = pending.get(i); buf.append(" pending[").append(i).append("]: "); buf.append(curPending.getOffset()).append("/").append(curPending.getData().length).append('/'); buf.append(curPending.getLifetime()); @@ -314,7 +324,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { * @param startAt first index in pending to send (inclusive) * @param sendThrough last index in pending to send (inclusive) */ - protected void send(List pending, int startAt, int sendThrough, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { + protected void send(List<TunnelGateway.Pending> pending, int startAt, int sendThrough, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending " + startAt + ":" + sendThrough + " out of " + pending); byte preprocessed[] = _dataCache.acquire().getData(); @@ -346,7 +356,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor { long msgId = sender.sendPreprocessed(preprocessed, rec); for (int i = 0; i < pending.size(); i++) { - TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.get(i); + TunnelGateway.Pending cur = pending.get(i); cur.addMessageId(msgId); } if (_log.shouldLog(Log.DEBUG)) @@ -359,9 +369,9 @@ public class BatchedPreprocessor extends TrivialPreprocessor { * * @return new offset into the target for further bytes to be written */ - private int writeFragments(List pending, int startAt, int sendThrough, byte target[], int offset) { + private int writeFragments(List<TunnelGateway.Pending> pending, int startAt, int sendThrough, byte target[], int offset) { for (int i = startAt; i <= sendThrough; i++) { - TunnelGateway.Pending msg = (TunnelGateway.Pending)pending.get(i); + TunnelGateway.Pending msg = pending.get(i); int prevOffset = offset; if (msg.getOffset() == 0) { offset = writeFirstFragment(msg, target, offset); diff --git a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java index 2e5e988855b5d05b072a72ae15fc28d0e0d4d356..6af65e88e2e23991812d1678ac42bb9657016797 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java @@ -11,9 +11,9 @@ import net.i2p.router.RouterContext; * */ public class BatchedRouterPreprocessor extends BatchedPreprocessor { - private RouterContext _routerContext; + protected RouterContext _routerContext; private TunnelCreatorConfig _config; - private HopConfig _hopConfig; + protected HopConfig _hopConfig; /** * How frequently should we flush non-full messages, in milliseconds @@ -79,7 +79,7 @@ public class BatchedRouterPreprocessor extends BatchedPreprocessor { } @Override - protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds, String msg) { + protected void notePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds, String msg) { if (_config != null) _routerContext.messageHistory().fragmentMessage(messageId, numFragments, totalLength, messageIds, _config, msg); else diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index 43e1d13144de93911a3c84eccdb6c1fac94a16ff..f294d5c7855359282067dadb0afc3d2621177420 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -21,6 +21,65 @@ import net.i2p.util.SimpleTimer; * Handle fragments at the endpoint of a tunnel, peeling off fully completed * I2NPMessages when they arrive, and dropping fragments if they take too long * to arrive. + * + * From tunnel-alt.html: + +<p>When the gateway wants to deliver data through the tunnel, it first +gathers zero or more <a href="i2np.html">I2NP</a> messages, selects how much padding will be used, +fragments it across the necessary number of 1KB tunnel messages, and decides how +each I2NP message should be handled by the tunnel endpoint, encoding that +data into the raw tunnel payload:</p> +<ul> +<li>the first 4 bytes of the SHA256 of (the remaining preprocessed data concatenated + with the IV), using the IV as will be seen on the tunnel endpoint (for + outbound tunnels), or the IV as was seen on the tunnel gateway (for inbound + tunnels) (see below for IV processing).</li> +<li>0 or more bytes containing random nonzero integers</li> +<li>1 byte containing 0x00</li> +<li>a series of zero or more { instructions, message } pairs</li> +</ul> + +<p>The instructions are encoded with a single control byte, followed by any +necessary additional information. The first bit in that control byte determines +how the remainder of the header is interpreted - if it is not set, the message +is either not fragmented or this is the first fragment in the message. If it is +set, this is a follow on fragment.</p> + +<p>With the first bit being 0, the instructions are:</p> +<ul> +<li>1 byte control byte:<pre> + bit 0: is follow on fragment? (1 = true, 0 = false, must be 0) + bits 1-2: delivery type + (0x0 = LOCAL, 0x01 = TUNNEL, 0x02 = ROUTER) + bit 3: delay included? (1 = true, 0 = false) + bit 4: fragmented? (1 = true, 0 = false) + bit 5: extended options? (1 = true, 0 = false) + bits 6-7: reserved</pre></li> +<li>if the delivery type was TUNNEL, a 4 byte tunnel ID</li> +<li>if the delivery type was TUNNEL or ROUTER, a 32 byte router hash</li> +<li>if the delay included flag is true, a 1 byte value:<pre> + bit 0: type (0 = strict, 1 = randomized) + bits 1-7: delay exponent (2^value minutes)</pre></li> +<li>if the fragmented flag is true, a 4 byte message ID</li> +<li>if the extended options flag is true:<pre> + = a 1 byte option size (in bytes) + = that many bytes</pre></li> +<li>2 byte size of the I2NP message or this fragment</li> +</ul> + +<p>If the first bit being 1, the instructions are:</p> +<ul> +<li>1 byte control byte:<pre> + bit 0: is follow on fragment? (1 = true, 0 = false, must be 1) + bits 1-6: fragment number + bit 7: is last? (1 = true, 0 = false)</pre></li> +<li>4 byte message ID (same one defined in the first fragment)</li> +<li>2 byte size of this fragment</li> +</ul> + +<p>The I2NP message is encoded in its standard form, and the +preprocessed payload must be padded to a multiple of 16 bytes.</p> + * */ public class FragmentHandler { @@ -149,7 +208,7 @@ public class FragmentHandler { if (_log.shouldLog(Log.WARN)) _log.warn("cannot verify, going past the end [off=" + offset + " len=" + length + " paddingEnd=" - + paddingEnd + " data:\n" + + paddingEnd + " data: " + Base64.encode(preprocessed, offset, length)); return false; } @@ -165,21 +224,19 @@ public class FragmentHandler { _log.debug("endpoint IV: " + Base64.encode(preV, validLength - HopProcessor.IV_LENGTH, HopProcessor.IV_LENGTH)); Hash v = _context.sha().calculateHash(preV, 0, validLength); + _validateCache.release(ba); - //Hash v = _context.sha().calculateHash(preV, 0, validLength); boolean eq = DataHelper.eq(v.getData(), 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4); if (!eq) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Corrupt tunnel message - verification fails: \n" + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4) - + "\n" + Base64.encode(v.getData(), 0, 4)); - if (_log.shouldLog(Log.WARN)) - _log.warn("nomatching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1) + "\n" - + " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + if (_log.shouldLog(Log.WARN)) { + _log.warn("Corrupt tunnel message - verification fails: " + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4) + + " != " + Base64.encode(v.getData(), 0, 4)); + _log.warn("No matching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1) + + " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + ' ' + Base64.encode(preprocessed, offset, length)); + } } - _validateCache.release(ba); - if (eq) { int excessPadding = paddingEnd - (HopProcessor.IV_LENGTH + 4 + 1); if (excessPadding > 0) // suboptimal fragmentation diff --git a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java index 6c572cb426ab90bc7267bbf785d547f8a9017311..7376a5046692207b666f6edccbe3d70c34c9af9f 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java @@ -1,6 +1,5 @@ package net.i2p.router.tunnel; -import net.i2p.I2PAppContext; import net.i2p.data.ByteArray; import net.i2p.data.Hash; import net.i2p.router.RouterContext; @@ -16,7 +15,7 @@ import net.i2p.util.Log; * */ public class InboundEndpointProcessor { - private I2PAppContext _context; + private RouterContext _context; private Log _log; private TunnelCreatorConfig _config; private IVValidator _validator; @@ -24,10 +23,10 @@ public class InboundEndpointProcessor { static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION; private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH); - public InboundEndpointProcessor(I2PAppContext ctx, TunnelCreatorConfig cfg) { + public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) { this(ctx, cfg, DummyValidator.getInstance()); } - public InboundEndpointProcessor(I2PAppContext ctx, TunnelCreatorConfig cfg, IVValidator validator) { + public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg, IVValidator validator) { _context = ctx; _log = ctx.logManager().getLog(InboundEndpointProcessor.class); _config = cfg; @@ -73,23 +72,19 @@ public class InboundEndpointProcessor { _cache.release(ba); - // now for a little bookkeeping - RouterContext ctx = null; - if (_context instanceof RouterContext) - ctx = (RouterContext)_context; - if ( (ctx != null) && (_config.getLength() > 0) ) { + if (_config.getLength() > 0) { int rtt = 0; // dunno... may not be related to an rtt if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a " + length + "byte message through tunnel " + _config); for (int i = 0; i < _config.getLength(); i++) - ctx.profileManager().tunnelDataPushed(_config.getPeer(i), rtt, length); + _context.profileManager().tunnelDataPushed(_config.getPeer(i), rtt, length); _config.incrementVerifiedBytesTransferred(length); } return true; } - private void decrypt(I2PAppContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) { + private void decrypt(RouterContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) { Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class); ByteArray ba = _cache.acquire(); byte cur[] = ba.getData(); // new byte[HopProcessor.IV_LENGTH]; // so we dont malloc diff --git a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java index 4792b14c266bcc66e33e73117288f74dab5d46ad..bd7bc73628c135167d29739138db86beacb81828 100644 --- a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java @@ -3,11 +3,11 @@ package net.i2p.router.tunnel; import java.util.ArrayList; import java.util.List; -import net.i2p.I2PAppContext; import net.i2p.data.Base64; import net.i2p.data.ByteArray; import net.i2p.data.DataHelper; import net.i2p.data.Hash; +import net.i2p.router.RouterContext; import net.i2p.util.ByteCache; import net.i2p.util.Log; @@ -19,8 +19,8 @@ import net.i2p.util.Log; * */ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { - protected I2PAppContext _context; - private Log _log; + protected RouterContext _context; + protected Log _log; public static final int PREPROCESSED_SIZE = 1024; protected static final int IV_SIZE = HopProcessor.IV_LENGTH; @@ -28,7 +28,7 @@ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { protected static final ByteCache _ivCache = ByteCache.getInstance(128, IV_SIZE); protected static final ByteCache _hashCache = ByteCache.getInstance(128, Hash.HASH_LENGTH); - public TrivialPreprocessor(I2PAppContext ctx) { + public TrivialPreprocessor(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(TrivialPreprocessor.class); } @@ -41,7 +41,7 @@ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { * a delayed flush to clear them * */ - public boolean preprocessQueue(List pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { + public boolean preprocessQueue(List<TunnelGateway.Pending> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { long begin = System.currentTimeMillis(); StringBuilder buf = null; if (_log.shouldLog(Log.DEBUG)) { @@ -49,7 +49,7 @@ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { buf.append("Trivial preprocessing of ").append(pending.size()).append(" "); } while (pending.size() > 0) { - TunnelGateway.Pending msg = (TunnelGateway.Pending)pending.remove(0); + TunnelGateway.Pending msg = pending.remove(0); long beforePreproc = System.currentTimeMillis(); byte preprocessed[][] = preprocess(msg); long afterPreproc = System.currentTimeMillis(); @@ -84,7 +84,7 @@ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { return false; } - protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds, String msg) {} + protected void notePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds, String msg) {} private byte[][] preprocess(TunnelGateway.Pending msg) { List fragments = new ArrayList(1); diff --git a/router/java/src/net/i2p/router/tunnel/TrivialRouterPreprocessor.java b/router/java/src/net/i2p/router/tunnel/TrivialRouterPreprocessor.java index ad7322cb61f54a6d5dd3268144f682c98e8ca793..6ddf636e4f77a5e766dafeafdb22e3cf8bd07b0d 100644 --- a/router/java/src/net/i2p/router/tunnel/TrivialRouterPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/TrivialRouterPreprocessor.java @@ -7,6 +7,7 @@ import net.i2p.router.RouterContext; /** * Minor extension to track fragmentation * + * @deprecated unused */ public class TrivialRouterPreprocessor extends TrivialPreprocessor { private RouterContext _routerContext; @@ -16,7 +17,7 @@ public class TrivialRouterPreprocessor extends TrivialPreprocessor { _routerContext = ctx; } - protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds) { + protected void notePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds) { _routerContext.messageHistory().fragmentMessage(messageId, numFragments, totalLength, messageIds, null); } } diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java index 681dadec100e517e05d23b4b9c71512bee1e12d6..527426416e7997855df194d0728ca99e4c9e3ff4 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java @@ -131,17 +131,20 @@ public class TunnelDispatcher implements Service { new long[] { 60*1000l, 10*60*1000l, 60*60*1000l }); } + /** for IBGW */ private TunnelGateway.QueuePreprocessor createPreprocessor(HopConfig cfg) { - if (true) - return new BatchedRouterPreprocessor(_context, cfg); - else - return new TrivialRouterPreprocessor(_context); + //if (true) + return new DroppingBatchedRouterPreprocessor(_context, cfg); + //else + // return new TrivialRouterPreprocessor(_context); } + + /** for OBGW */ private TunnelGateway.QueuePreprocessor createPreprocessor(TunnelCreatorConfig cfg) { - if (true) + //if (true) return new BatchedRouterPreprocessor(_context, cfg); - else - return new TrivialRouterPreprocessor(_context); + //else + // return new TrivialRouterPreprocessor(_context); } /** @@ -605,12 +608,17 @@ public class TunnelDispatcher implements Service { if (pctDrop <= 0) return false; // increase the drop probability for OBEP, + // (except lower it for tunnel build messages (type 21)), // and lower it for IBGW, for network efficiency double len = length; - if (type.startsWith("OBEP")) - len *= 1.5; - else if (type.startsWith("IBGW")) + if (type.startsWith("OBEP")) { + if (type.equals("OBEP 21")) + len /= 1.5; + else + len *= 1.5; + } else if (type.startsWith("IBGW")) { len /= 1.5; + } // drop in proportion to size w.r.t. a standard 1024-byte message // this is a little expensive but we want to adjust the curve between 0 and 1 // Most messages are 1024, only at the OBEP do we see other sizes diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 40c935e795de58a985b6aa02ea3d8935dc35e72f..2b394e6dae5a1d7bb8844b68a681de0ed2568107 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -154,12 +154,18 @@ public class TunnelGateway { public interface QueuePreprocessor { /** + * Caller must synchronize on the list! + * * @param pending list of Pending objects for messages either unsent * or partly sent. This list should be update with any * values removed (the preprocessor owns the lock) + * Messages are not removed from the list until actually sent. + * The status of unsent and partially-sent messages is stored in + * the Pending structure. + * * @return true if we should delay before preprocessing again */ - public boolean preprocessQueue(List pending, Sender sender, Receiver receiver); + public boolean preprocessQueue(List<Pending> pending, Sender sender, Receiver receiver); /** how long do we want to wait before flushing */ public long getDelayAmount(); @@ -173,6 +179,9 @@ public class TunnelGateway { public long receiveEncrypted(byte encrypted[]); } + /** + * Stores all the state for an unsent or partially-sent message + */ public static class Pending { protected Hash _toRouter; protected TunnelId _toTunnel; @@ -182,7 +191,7 @@ public class TunnelGateway { protected int _offset; protected int _fragmentNumber; protected long _created; - private List _messageIds; + private List<Long> _messageIds; public Pending(I2NPMessage message, Hash toRouter, TunnelId toTunnel) { this(message, toRouter, toTunnel, System.currentTimeMillis()); @@ -222,7 +231,7 @@ public class TunnelGateway { _messageIds.add(new Long(id)); } } - public List getMessageIds() { + public List<Long> getMessageIds() { synchronized (Pending.this) { if (_messageIds != null) return new ArrayList(_messageIds); @@ -231,6 +240,8 @@ public class TunnelGateway { } } } + + /** Extend for debugging */ class PendingImpl extends Pending { public PendingImpl(I2NPMessage message, Hash toRouter, TunnelId toTunnel) { super(message, toRouter, toTunnel, _context.clock().now()); diff --git a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java index a73208e3bb49a1060bd2c967f1ac668befed4667..6263f8ec701b98db1102e21d7e5c347295674cbe 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java @@ -101,8 +101,11 @@ public class TunnelParticipant { } } +/**** private int _periodMessagesTransferred; private long _lastCoallesced = System.currentTimeMillis(); +****/ + /** * take note that the peers specified were able to push us data. hmm, is this safe? * this could be easily gamed to get us to rank some peer of their choosing as quite diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java index e1cec3607c3ac206dd33ebb1e9d6d01b1afde3dc..5e4456490f201bf51ef2e2f7b1aea631243ee0de 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java @@ -54,9 +54,7 @@ class BuildExecutor implements Runnable { // Get stat manager, get recognized bandwidth tiers StatManager statMgr = _context.statManager(); - @SuppressWarnings("static-access") - /* FIXME Accessing static field "BW_CAPABILITY_CHARS" FIXME */ - String bwTiers = _context.router().getRouterInfo().BW_CAPABILITY_CHARS; + String bwTiers = RouterInfo.BW_CAPABILITY_CHARS; // For each bandwidth tier, create tunnel build agree/reject/expire stats for (int i = 0; i < bwTiers.length(); i++) { String bwTier = String.valueOf(bwTiers.charAt(i)); diff --git a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java index b2cb7c704fc7232225e08ecc026b1b087f1bb930..dfbdd3ecaccab23e5588b1c58ac5dd9183c565fe 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java +++ b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java @@ -79,8 +79,9 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig { public TunnelPool getTunnelPool() { return _pool; } - /* FIXME Exporting non-public type through public API FIXME */ - public void setTestJob(TestJob job) { _testJob = job; } + /** @deprecated unused, which makes _testJob unused - why is it here */ + void setTestJob(TestJob job) { _testJob = job; } + /** does nothing, to be deprecated */ public void setExpireJob(Job job) { /* _expireJob = job; */ } /** diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 1eae1de451b8ab883b0f9c6aac8ee56c629a4a0a..bbee14a4840ca9e7175025e0739a7d787052086b 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -29,6 +29,8 @@ import net.i2p.stat.RateStat; import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.ObjectCounter; +import net.i2p.util.SimpleScheduler; +import net.i2p.util.SimpleTimer; /** * @@ -42,7 +44,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { private final Map<Hash, TunnelPool> _clientOutboundPools; private TunnelPool _inboundExploratory; private TunnelPool _outboundExploratory; - private BuildExecutor _executor; + private final BuildExecutor _executor; private boolean _isShutdown; public TunnelPoolManager(RouterContext ctx) { @@ -263,6 +265,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { TunnelPool inbound = null; TunnelPool outbound = null; // should we share the clientPeerSelector across both inbound and outbound? + // or just one for all clients? why separate? synchronized (_clientInboundPools) { inbound = _clientInboundPools.get(dest); if (inbound == null) { @@ -284,11 +287,22 @@ public class TunnelPoolManager implements TunnelManagerFacade { } } inbound.startup(); - try { Thread.sleep(3*1000); } catch (InterruptedException ie) {} - outbound.startup(); + SimpleScheduler.getInstance().addEvent(new DelayedStartup(outbound), 3*1000); } + private static class DelayedStartup implements SimpleTimer.TimedEvent { + private TunnelPool pool; + + public DelayedStartup(TunnelPool p) { + this.pool = p; + } + + public void timeReached() { + this.pool.startup(); + } + } + public void removeTunnels(Hash destination) { if (destination == null) return; if (_context.clientManager().isLocal(destination)) { @@ -361,12 +375,11 @@ public class TunnelPoolManager implements TunnelManagerFacade { _inboundExploratory = new TunnelPool(_context, this, inboundSettings, selector); _inboundExploratory.startup(); - try { Thread.sleep(3*1000); } catch (InterruptedException ie) {} TunnelPoolSettings outboundSettings = new TunnelPoolSettings(); outboundSettings.setIsExploratory(true); outboundSettings.setIsInbound(false); _outboundExploratory = new TunnelPool(_context, this, outboundSettings, selector); - _outboundExploratory.startup(); + SimpleScheduler.getInstance().addEvent(new DelayedStartup(_outboundExploratory), 3*1000); // try to build up longer tunnels _context.jobQueue().addJob(new BootstrapPool(_context, _inboundExploratory));