![JAR search and dependency download from the Maven repository](/logo.png)
net.i2p.router.tunnel.TunnelParticipant Maven / Gradle / Ivy
The newest version!
package net.i2p.router.tunnel;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterInfo;
import net.i2p.data.TunnelId;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.TunnelDataMessage;
import net.i2p.router.JobImpl;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
* Participate in a tunnel at a location other than the gateway or outbound
* endpoint. This participant should be provided with the necessary processor
* if it is an inbound tunnel endpoint, and that will enable the
* InboundMessageDistributor to receive defragmented and decrypted messages,
* which it will then selectively forward.
*/
class TunnelParticipant {
private final RouterContext _context;
private final Log _log;
private final HopConfig _config;
private final HopProcessor _processor;
private final InboundEndpointProcessor _inboundEndpointProcessor;
private final InboundMessageDistributor _inboundDistributor;
private final FragmentHandler _handler;
private RouterInfo _nextHopCache;
private static final long MAX_LOOKUP_TIME = 15*1000;
/** for next hop when a tunnel is first created */
private static final long LONG_MAX_LOOKUP_TIME = 30*1000;
private static final int PRIORITY = OutNetMessage.PRIORITY_PARTICIPATING;
/** not an inbound endpoint */
public TunnelParticipant(RouterContext ctx, HopConfig config, HopProcessor processor) {
this(ctx, config, processor, null);
}
/** inbound endpoint */
public TunnelParticipant(RouterContext ctx, InboundEndpointProcessor inEndProc) {
this(ctx, null, null, inEndProc);
}
/**
* @param config may be null (inbound endpoint if null)
* @param processor may be null (inbound endpoint if null)
* @param inEndProc may be null (inbound endpoint if non-null)
*/
private TunnelParticipant(RouterContext ctx, HopConfig config, HopProcessor processor, InboundEndpointProcessor inEndProc) {
_context = ctx;
_log = ctx.logManager().getLog(TunnelParticipant.class);
_config = config;
_processor = processor;
if ( (config == null) || (config.getSendTo() == null) )
_handler = new FragmentHandler(ctx, new DefragmentedHandler(), true);
else
_handler = null; // final
_inboundEndpointProcessor = inEndProc;
if (inEndProc != null)
_inboundDistributor = new InboundMessageDistributor(ctx, inEndProc.getDestination());
else
_inboundDistributor = null; // final
if ( (_config != null) && (_config.getSendTo() != null) ) {
_nextHopCache = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
if (_nextHopCache == null)
_context.netDb().lookupRouterInfo(_config.getSendTo(), new Found(_context), null, LONG_MAX_LOOKUP_TIME);
}
// all createRateStat() in TunnelDispatcher
}
private class Found extends JobImpl {
public Found(RouterContext ctx) { super(ctx); }
public String getName() { return "Next hop info found"; }
public void runJob() {
if (_nextHopCache == null) {
_nextHopCache = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
// nothing for failure since fail job is null
_context.statManager().addRateData("tunnel.participantLookupSuccess", 1);
}
}
}
public void dispatch(TunnelDataMessage msg, Hash recvFrom) {
boolean ok = false;
byte[] data = msg.getData();
if (_processor != null)
ok = _processor.process(data, 0, data.length, recvFrom);
else if (_inboundEndpointProcessor != null)
ok = _inboundEndpointProcessor.retrievePreprocessedData(data, 0, data.length, recvFrom);
if (!ok) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to dispatch " + msg + ": processor=" + _processor
+ " inboundEndpoint=" + _inboundEndpointProcessor);
if (_config != null)
_config.incrementProcessedMessages();
return;
}
if ( (_config != null) && (_config.getSendTo() != null) ) {
_config.incrementProcessedMessages();
RouterInfo ri = _nextHopCache;
if (ri == null)
ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
if (ri != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send off to nextHop directly (" + _config.getSendTo()
+ " for " + msg);
send(_config, msg, ri);
// see comments below
//if (_config != null)
// incrementThroughput(_config.getReceiveFrom());
} else {
// It should be rare to forget the router info for the next peer
if (_log.shouldLog(Log.WARN))
_log.warn("Lookup the nextHop (" + _config.getSendTo()
+ " for " + msg);
_context.netDb().lookupRouterInfo(_config.getSendTo(), new SendJob(_context, msg),
new TimeoutJob(_context, msg), MAX_LOOKUP_TIME);
}
} else {
// IBEP
TunnelCreatorConfig cfg = _inboundEndpointProcessor.getConfig();
cfg.incrementProcessedMessages();
ok = _handler.receiveTunnelMessage(data, 0, data.length);
if (ok) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive fragment: on " + _config + ": " + msg);
} else {
// blame everybody equally
int lenm1 = cfg.getLength() - 1;
if (lenm1 > 0) {
int pct = 100 / (lenm1);
for (int i = 0; i < lenm1; i++) {
Hash h = cfg.getPeer(i);
if (_log.shouldLog(Log.WARN))
_log.warn(toString() + ": Blaming " + h + ' ' + pct + '%');
_context.profileManager().tunnelFailed(h, pct);
}
}
}
}
}
/****
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
* fast. That peer would have to actually be quite fast, but having a remote peer
* influence who we spend our time profiling is dangerous, so this will be disabled for
* now.
*/
/****
private void incrementThroughput(Hash prev) {
if (true) return;
long now = System.currentTimeMillis();
long timeSince = now - _lastCoallesced;
if (timeSince >= 60*1000) {
int amount = 1024 * _periodMessagesTransferred;
int normalized = (int)((double)amount * 60d*1000d / (double)timeSince);
_periodMessagesTransferred = 0;
_lastCoallesced = now;
_context.profileManager().tunnelDataPushed1m(prev, normalized);
} else {
_periodMessagesTransferred++;
}
}
****/
/** getCompleteCount */
public int getCompleteCount() {
if (_handler != null)
return _handler.getCompleteCount();
else
return 0;
}
public int getFailedCount() {
if (_handler != null)
return _handler.getFailedCount();
else
return 0;
}
private class DefragmentedHandler implements FragmentHandler.DefragmentedReceiver {
public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive complete: on " + _config + ": " + msg);
_inboundDistributor.distribute(msg, toRouter, toTunnel);
}
}
private void send(HopConfig config, TunnelDataMessage msg, RouterInfo ri) {
if (_context.tunnelDispatcher().shouldDropParticipatingMessage(TunnelDispatcher.Location.PARTICIPANT,
TunnelDataMessage.MESSAGE_TYPE, 1024))
return;
//_config.incrementSentMessages();
long oldId = msg.getUniqueId();
long newId = _context.random().nextLong(I2NPMessage.MAX_ID_VALUE);
_context.messageHistory().wrap("TunnelDataMessage", oldId, "TunnelDataMessage", newId);
msg.setUniqueId(newId);
msg.setMessageExpiration(_context.clock().now() + 10*1000);
msg.setTunnelId(config.getSendTunnel());
OutNetMessage m = new OutNetMessage(_context, msg, msg.getMessageExpiration(), PRIORITY, ri);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Forward on from " + _config + ": " + msg);
_context.outNetMessagePool().add(m);
}
private class SendJob extends JobImpl {
private final TunnelDataMessage _msg;
public SendJob(RouterContext ctx, TunnelDataMessage msg) {
super(ctx);
_msg = msg;
}
public String getName() { return "Participant send after lookup"; }
public void runJob() {
if (_nextHopCache != null) {
send(_config, _msg, _nextHopCache);
} else {
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
int stat;
if (ri != null) {
_nextHopCache = ri;
send(_config, _msg, ri);
stat = 1;
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Lookup the nextHop (" + _config.getSendTo()
+ " failed! where do we go for " + _config + "? msg dropped: " + _msg);
stat = 0;
}
_context.statManager().addRateData("tunnel.participantLookupSuccess", stat);
}
}
}
private class TimeoutJob extends JobImpl {
private final TunnelDataMessage _msg;
public TimeoutJob(RouterContext ctx, TunnelDataMessage msg) {
super(ctx);
_msg = msg;
}
public String getName() { return "Participant next hop lookup timeout"; }
public void runJob() {
if (_nextHopCache != null)
return;
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
if (ri != null) {
_nextHopCache = ri;
if (_log.shouldLog(Log.WARN))
_log.warn("Lookup the nextHop (" + _config.getSendTo()
+ " failed, but we found it!! where do we go for " + _config + "? msg dropped: " + _msg);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Lookup the nextHop (" + _config.getSendTo()
+ " failed! where do we go for " + _config + "? msg dropped: " + _msg);
}
_context.statManager().addRateData("tunnel.participantLookupSuccess", 0);
}
}
@Override
public String toString() {
if (_config != null) {
StringBuilder buf = new StringBuilder(64);
buf.append("participant at ").append(_config.toString());
return buf.toString();
} else {
return "inbound endpoint";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy