merge of '5cbdacfbeb8f09f9ac497016e429eaf6fe7225c6'
and 'dae5a2b76e24fc3215d46d5b96ea0f547c83f63c'
This commit is contained in:
10
INSTALL.txt
10
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)
|
||||
|
@ -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/
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
*********/
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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; */ }
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
Reference in New Issue
Block a user