use of net.i2p.data.TunnelId in project i2p.i2p by i2p.
the class TunnelRenderer method renderPool.
private void renderPool(Writer out, TunnelPool in, TunnelPool outPool) throws IOException {
List<TunnelInfo> tunnels = null;
if (in == null)
tunnels = new ArrayList<TunnelInfo>();
else
tunnels = in.listTunnels();
if (outPool != null)
tunnels.addAll(outPool.listTunnels());
long processedIn = (in != null ? in.getLifetimeProcessed() : 0);
long processedOut = (outPool != null ? outPool.getLifetimeProcessed() : 0);
int live = 0;
int maxLength = 1;
for (int i = 0; i < tunnels.size(); i++) {
TunnelInfo info = tunnels.get(i);
int length = info.getLength();
if (length > maxLength)
maxLength = length;
}
out.write("<table class=\"tunneldisplay tunnels_client\"><tr><th title=\"" + _t("Inbound or outbound?") + ("\">") + _t("In/Out") + "</th><th>" + _t("Expiry") + "</th><th>" + _t("Usage") + "</th><th>" + _t("Gateway") + "</th>");
if (maxLength > 3) {
out.write("<th align=\"center\" colspan=\"" + (maxLength - 2));
out.write("\">" + _t("Participants") + "</th>");
} else if (maxLength == 3) {
out.write("<th>" + _t("Participant") + "</th>");
}
if (maxLength > 1) {
out.write("<th>" + _t("Endpoint") + "</th>");
}
out.write("</tr>\n");
for (int i = 0; i < tunnels.size(); i++) {
TunnelInfo info = tunnels.get(i);
long timeLeft = info.getExpiration() - _context.clock().now();
if (timeLeft <= 0)
// don't display tunnels in their grace period
continue;
live++;
boolean isInbound = info.isInbound();
if (isInbound)
out.write("<tr><td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/inbound.png\" alt=\"Inbound\" title=\"" + _t("Inbound") + "\"></td>");
else
out.write("<tr><td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/outbound.png\" alt=\"Outbound\" title=\"" + _t("Outbound") + "\"></td>");
out.write("<td class=\"cells\" align=\"center\">" + DataHelper.formatDuration2(timeLeft) + "</td>\n");
int count = info.getProcessedMessagesCount() * 1024 / 1000;
out.write("<td class=\"cells\" align=\"center\">" + count + " KB</td>\n");
int length = info.getLength();
for (int j = 0; j < length; j++) {
Hash peer = info.getPeer(j);
TunnelId id = (isInbound ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j));
if (_context.routerHash().equals(peer)) {
if (length < maxLength && length == 1 && isInbound) {
// pad before inbound zero hop
for (int k = 1; k < maxLength; k++) {
out.write("<td class=\"cells\" align=\"center\"> </td>");
}
}
// Add empty content placeholders to force alignment.
out.write(" <td class=\"cells\" align=\"center\"><span class=\"tunnel_peer tunnel_local\" title=\"" + _t("Locally hosted tunnel") + "\">" + _t("Local") + "</span> <span class=\"tunnel_id\" title=\"" + _t("Tunnel identity") + "\">" + (id == null ? "" : "" + id) + "</span><b class=\"tunnel_cap\" title=\"" + _t("Bandwidth tier") + "\"></b></td>");
} else {
String cap = getCapacity(peer);
out.write(" <td class=\"cells\" align=\"center\"><span class=\"tunnel_peer\">" + netDbLink(peer) + "</span> <span class=\"nowrap\"><span class=\"tunnel_id\" title=\"" + _t("Tunnel identity") + "\">" + (id == null ? "" : " " + id) + "</span><b class=\"tunnel_cap\" title=\"" + _t("Bandwidth tier") + "\">" + cap + "</b></span></td>");
}
if (length < maxLength && ((length == 1 && !isInbound) || j == length - 2)) {
// pad out outbound zero hop; non-zero-hop pads in middle
for (int k = length; k < maxLength; k++) {
out.write("<td class=\"cells\" align=\"center\"> </td>");
}
}
}
out.write("</tr>\n");
if (info.isInbound())
processedIn += count;
else
processedOut += count;
}
out.write("</table>\n");
if (in != null) {
// PooledTunnelCreatorConfig
List<?> pending = in.listPending();
if (!pending.isEmpty()) {
out.write("<div class=\"statusnotes\"><center><b>" + _t("Build in progress") + ": " + pending.size() + " " + _t("inbound") + "</b></center></div>\n");
live += pending.size();
}
}
if (outPool != null) {
// PooledTunnelCreatorConfig
List<?> pending = outPool.listPending();
if (!pending.isEmpty()) {
out.write("<div class=\"statusnotes\"><center><b>" + _t("Build in progress") + ": " + pending.size() + " " + _t("outbound") + "</b></center></div>\n");
live += pending.size();
}
}
if (live <= 0)
out.write("<div class=\"statusnotes\"><center><b>" + _t("No tunnels; waiting for the grace period to end.") + "</b></center></div>\n");
out.write("<div class=\"statusnotes\"><center><b>" + _t("Lifetime bandwidth usage") + ": " + DataHelper.formatSize2Decimal(processedIn * 1024) + "B " + _t("in") + ", " + DataHelper.formatSize2Decimal(processedOut * 1024) + "B " + _t("out") + "</b></center></div>");
}
use of net.i2p.data.TunnelId in project i2p.i2p by i2p.
the class RequestLeaseSetMessage method doReadMessage.
@Override
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
_sessionId.readBytes(in);
int numTunnels = (int) DataHelper.readLong(in, 1);
_endpoints.clear();
for (int i = 0; i < numTunnels; i++) {
// Hash router = new Hash();
// router.readBytes(in);
Hash router = Hash.create(in);
TunnelId tunnel = new TunnelId();
tunnel.readBytes(in);
_endpoints.add(new TunnelEndpoint(router, tunnel));
}
_end = DataHelper.readDate(in);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe);
}
}
use of net.i2p.data.TunnelId in project i2p.i2p by i2p.
the class DatabaseStoreMessage method readMessage.
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException {
if (type != MESSAGE_TYPE)
throw new I2NPMessageException("Message type is incorrect for this message");
int curIndex = offset;
_key = Hash.create(data, curIndex);
// Fast-fail here to save resources.
if (_key.equals(Hash.FAKE_HASH)) {
// createRateStat in KNDF
_context.statManager().addRateData("netDb.DSMAllZeros", 1);
throw new I2NPMessageException("DSM all zeros");
}
curIndex += Hash.HASH_LENGTH;
// as of 0.9.18, ignore other 7 bits of the type byte, in preparation for future options
int dbType = data[curIndex] & 0x01;
curIndex++;
_replyToken = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
if (_replyToken > 0) {
long tunnel = DataHelper.fromLong(data, curIndex, 4);
if (tunnel > 0)
_replyTunnel = new TunnelId(tunnel);
curIndex += 4;
_replyGateway = Hash.create(data, curIndex);
curIndex += Hash.HASH_LENGTH;
} else {
_replyTunnel = null;
_replyGateway = null;
}
if (dbType == DatabaseEntry.KEY_TYPE_LEASESET) {
_dbEntry = new LeaseSet();
try {
_dbEntry.readBytes(new ByteArrayInputStream(data, curIndex, data.length - curIndex));
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the leaseSet", dfe);
} catch (IOException ioe) {
throw new I2NPMessageException("Error reading the leaseSet", ioe);
}
} else {
// dbType == DatabaseEntry.KEY_TYPE_ROUTERINFO
_dbEntry = new RouterInfo();
int compressedSize = (int) DataHelper.fromLong(data, curIndex, 2);
curIndex += 2;
if (compressedSize <= 0 || curIndex + compressedSize > data.length || curIndex + compressedSize > dataSize + offset)
throw new I2NPMessageException("Compressed RI length: " + compressedSize + " but remaining bytes: " + Math.min(data.length - curIndex, dataSize + offset - curIndex));
try {
// TODO we could delay decompression, just copy to a new byte array and store in _byteCache
// May not be necessary since the IBGW now uses UnknownI2NPMessage.
// DSMs at the OBEP are generally garlic wrapped, so the OBEP won't see it.
// If we do delay it, getEntry() will have to check if _dbEntry is null and _byteCache
// is non-null, and then decompress.
byte[] decompressed = DataHelper.decompress(data, curIndex, compressedSize);
_dbEntry.readBytes(new ByteArrayInputStream(decompressed));
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the routerInfo", dfe);
} catch (IOException ioe) {
throw new I2NPMessageException("Corrupt compressed routerInfo size = " + compressedSize, ioe);
}
}
// if (!key.equals(_dbEntry.getHash()))
// throw new I2NPMessageException("Hash mismatch in DSM");
}
use of net.i2p.data.TunnelId in project i2p.i2p by i2p.
the class DeliveryInstructionsTest method createDataStructure.
public DataStructure createDataStructure() throws DataFormatException {
DeliveryInstructions instructions = new DeliveryInstructions();
// instructions.setDelayRequested(true);
// instructions.setDelaySeconds(42);
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
// encryption key read/write disabled
// instructions.setEncrypted(true);
// SessionKey key = new SessionKey();
// byte keyData[] = new byte[SessionKey.KEYSIZE_BYTES];
// for (int i = 0; i < keyData.length; i++)
// keyData[i] = (byte)i;
// key.setData(keyData);
// instructions.setEncryptionKey(key);
Hash hash = new Hash();
byte[] hashData = new byte[32];
for (int i = 0; i < hashData.length; i++) hashData[i] = (byte) (i % 32);
hash.setData(hashData);
instructions.setRouter(hash);
TunnelId id = new TunnelId();
id.setTunnelId(666);
instructions.setTunnelId(id);
return instructions;
}
use of net.i2p.data.TunnelId in project i2p.i2p by i2p.
the class BuildHandler method handleReq.
/**
* If we are dropping lots of requests before even trying to handle them,
* I suppose you could call us "overloaded"
*/
/**
** unused, see handleReq() below
* private final static int MAX_PROACTIVE_DROPS = 240;
*
* private int countProactiveDrops() {
* int dropped = 0;
* dropped += countEvents("tunnel.dropLoadProactive", 60*1000);
* dropped += countEvents("tunnel.dropLoad", 60*1000);
* dropped += countEvents("tunnel.dropLoadBacklog", 60*1000);
* dropped += countEvents("tunnel.dropLoadDelay", 60*1000);
* return dropped;
* }
*
* private int countEvents(String stat, long period) {
* RateStat rs = _context.statManager().getRate(stat);
* if (rs != null) {
* Rate r = rs.getRate(period);
* if (r != null)
* return (int)r.getCurrentEventCount();
* }
* return 0;
* }
***
*/
/**
* Actually process the request and send the reply.
*
* Todo: Replies are not subject to RED for bandwidth reasons,
* and the bandwidth is not credited to any tunnel.
* If we did credit the reply to the tunnel, it would
* prevent the classification of the tunnel as 'inactive' on tunnels.jsp.
*/
private void handleReq(RouterInfo nextPeerInfo, BuildMessageState state, BuildRequestRecord req, Hash nextPeer) {
long ourId = req.readReceiveTunnelId();
long nextId = req.readNextTunnelId();
boolean isInGW = req.readIsInboundGateway();
boolean isOutEnd = req.readIsOutboundEndpoint();
Hash from = state.fromHash;
if (from == null && state.from != null)
from = state.from.calculateHash();
if (isInGW && isOutEnd) {
_context.statManager().addRateData("tunnel.rejectHostile", 1);
_log.error("Dropping build request, IBGW+OBEP: " + req);
if (from != null)
_context.commSystem().mayDisconnect(from);
return;
}
if (ourId <= 0 || ourId > TunnelId.MAX_ID_VALUE || nextId <= 0 || nextId > TunnelId.MAX_ID_VALUE) {
_context.statManager().addRateData("tunnel.rejectHostile", 1);
if (_log.shouldWarn())
_log.warn("Dropping build request, bad tunnel ID: " + req);
if (from != null)
_context.commSystem().mayDisconnect(from);
return;
}
// Loop checks
if ((!isOutEnd) && _context.routerHash().equals(nextPeer)) {
_context.statManager().addRateData("tunnel.rejectHostile", 1);
// old i2pd
if (_log.shouldWarn())
_log.warn("Dropping build request, we are the next hop: " + req);
if (from != null)
_context.commSystem().mayDisconnect(from);
return;
}
if (!isInGW) {
// but if not, something is seriously wrong here.
if (from == null || _context.routerHash().equals(from)) {
_context.statManager().addRateData("tunnel.rejectHostile", 1);
if (_log.shouldWarn())
_log.warn("Dropping build request, we are the previous hop: " + req);
return;
}
}
if ((!isOutEnd) && (!isInGW)) {
// A-B-C-A is not preventable
if (nextPeer.equals(from)) {
// i2pd does this
_context.statManager().addRateData("tunnel.rejectHostile", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping build request with the same previous and next hop: " + req);
_context.commSystem().mayDisconnect(from);
return;
}
}
// time is in hours, rounded down.
// tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it was not.
// As of 0.9.16, allow + 5 minutes to - 65 minutes.
long time = req.readRequestTime();
long now = (_context.clock().now() / (60l * 60l * 1000l)) * (60 * 60 * 1000);
long timeDiff = now - time;
if (timeDiff > MAX_REQUEST_AGE) {
_context.statManager().addRateData("tunnel.rejectTooOld", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping build request too old... replay attack? " + DataHelper.formatDuration(timeDiff) + ": " + req);
if (from != null)
_context.commSystem().mayDisconnect(from);
return;
}
if (timeDiff < 0 - MAX_REQUEST_FUTURE) {
_context.statManager().addRateData("tunnel.rejectFuture", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping build request too far in future " + DataHelper.formatDuration(0 - timeDiff) + ": " + req);
if (from != null)
_context.commSystem().mayDisconnect(from);
return;
}
int response;
if (_context.router().isHidden()) {
_context.throttle().setTunnelStatus(_x("Rejecting tunnels: Hidden mode"));
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
} else {
response = _context.throttle().acceptTunnelRequest();
}
// This only checked OUR tunnels, so the log message was wrong.
// Now checked by TunnelDispatcher.joinXXX()
// and returned as success value, checked below.
// if (_context.tunnelManager().getTunnelInfo(new TunnelId(ourId)) != null) {
// if (_log.shouldLog(Log.ERROR))
// _log.error("Already participating in a tunnel with the given Id (" + ourId + "), so gotta reject");
// if (response == 0)
// response = TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
// }
// if ( (response == 0) && (_context.random().nextInt(50) <= 1) )
// response = TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
long recvDelay = _context.clock().now() - state.recvTime;
if (response == 0) {
// unused
// int proactiveDrops = countProactiveDrops();
float pDrop = ((float) recvDelay) / (float) (BuildRequestor.REQUEST_TIMEOUT * 3);
pDrop = (float) Math.pow(pDrop, 16);
if (_context.random().nextFloat() < pDrop) {
// || (proactiveDrops > MAX_PROACTIVE_DROPS) ) ) {
_context.statManager().addRateData("tunnel.rejectOverloaded", recvDelay);
_context.throttle().setTunnelStatus(_x("Rejecting tunnels: Request overload"));
// if (true || (proactiveDrops < MAX_PROACTIVE_DROPS*2))
response = TunnelHistory.TUNNEL_REJECT_TRANSIENT_OVERLOAD;
// else
// response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
} else {
_context.statManager().addRateData("tunnel.acceptLoad", recvDelay);
}
}
/*
* Being a IBGW or OBEP generally leads to more connections, so if we are
* approaching our connection limit (i.e. !haveCapacity()),
* reject this request.
*
* Don't do this for class N or O, under the assumption that they are already talking
* to most of the routers, so there's no reason to reject. This may drive them
* to their conn. limits, but it's hopefully a temporary solution to the
* tunnel build congestion. As the net grows this will have to be revisited.
*/
RouterInfo ri = _context.router().getRouterInfo();
if (response == 0) {
if (ri == null) {
// ?? We should always have a RI
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
} else {
char bw = ri.getBandwidthTier().charAt(0);
if (bw != 'O' && bw != 'N' && bw != 'P' && bw != 'X' && ((isInGW && !_context.commSystem().haveInboundCapacity(87)) || (isOutEnd && !_context.commSystem().haveOutboundCapacity(87)))) {
_context.statManager().addRateData("tunnel.rejectConnLimits", 1);
_context.throttle().setTunnelStatus(_x("Rejecting tunnels: Connection limit"));
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
}
}
// We may need another counter above for requests.
if (response == 0 && !isInGW) {
if (from != null && _throttler.shouldThrottle(from)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting tunnel (hop throttle), previous hop: " + from + ": " + req);
// no setTunnelStatus() indication
_context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
}
if (response == 0 && (!isOutEnd) && _throttler.shouldThrottle(nextPeer)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting tunnel (hop throttle), next hop: " + req);
_context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
// no setTunnelStatus() indication
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
HopConfig cfg = null;
if (response == 0) {
cfg = new HopConfig();
cfg.setCreation(_context.clock().now());
cfg.setExpiration(_context.clock().now() + 10 * 60 * 1000);
cfg.setIVKey(req.readIVKey());
cfg.setLayerKey(req.readLayerKey());
if (isInGW) {
// default
// cfg.setReceiveFrom(null);
} else {
if (from != null) {
cfg.setReceiveFrom(from);
} else {
// b0rk
return;
}
}
cfg.setReceiveTunnelId(DataHelper.toLong(4, ourId));
if (isOutEnd) {
// default
// cfg.setSendTo(null);
// cfg.setSendTunnelId(null);
} else {
cfg.setSendTo(nextPeer);
cfg.setSendTunnelId(DataHelper.toLong(4, nextId));
}
// now "actually" join
boolean success;
if (isOutEnd)
success = _context.tunnelDispatcher().joinOutboundEndpoint(cfg);
else if (isInGW)
success = _context.tunnelDispatcher().joinInboundGateway(cfg);
else
success = _context.tunnelDispatcher().joinParticipant(cfg);
if (success) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Joining: " + req);
} else {
// Dup Tunnel ID. This can definitely happen (birthday paradox).
// Probability in 11 minutes (per hop type):
// 0.1% for 2900 tunnels; 1% for 9300 tunnels
response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
_context.statManager().addRateData("tunnel.rejectDupID", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("DUP ID failure: " + req);
}
}
if (response != 0) {
_context.statManager().addRateData("tunnel.reject." + response, 1);
_context.messageHistory().tunnelRejected(from, new TunnelId(ourId), nextPeer, // (isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
Integer.toString(response));
if (from != null)
_context.commSystem().mayDisconnect(from);
// 81% = between 75% control measures in Transports and 87% rejection above
if ((!_context.routerHash().equals(nextPeer)) && (!_context.commSystem().haveOutboundCapacity(81)) && (!_context.commSystem().isEstablished(nextPeer))) {
_context.statManager().addRateData("tunnel.dropConnLimits", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Not sending rejection due to conn limits: " + req);
return;
}
} else if (isInGW && from != null) {
// we're the start of the tunnel, no use staying connected
_context.commSystem().mayDisconnect(from);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Responding to " + state.msg.getUniqueId() + " after " + recvDelay + " with " + response + " from " + (from != null ? from : "tunnel") + ": " + req);
EncryptedBuildRecord reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
int records = state.msg.getRecordCount();
int ourSlot = -1;
for (int j = 0; j < records; j++) {
if (state.msg.getRecord(j) == null) {
ourSlot = j;
state.msg.setRecord(j, reply);
// + ": " + Base64.encode(reply));
break;
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read slot " + ourSlot + " containing: " + req + " accepted? " + response + " recvDelay " + recvDelay + " replyMessage " + req.readReplyMessageId());
// now actually send the response
long expires = _context.clock().now() + NEXT_HOP_SEND_TIMEOUT;
if (!isOutEnd) {
state.msg.setUniqueId(req.readReplyMessageId());
state.msg.setMessageExpiration(expires);
OutNetMessage msg = new OutNetMessage(_context, state.msg, expires, PRIORITY, nextPeerInfo);
if (response == 0)
msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
_context.outNetMessagePool().add(msg);
} else {
// We are the OBEP.
// send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage
// (enough layers jrandom?)
TunnelBuildReplyMessage replyMsg;
if (records == TunnelBuildMessage.MAX_RECORD_COUNT)
replyMsg = new TunnelBuildReplyMessage(_context);
else
replyMsg = new VariableTunnelBuildReplyMessage(_context, records);
for (int i = 0; i < records; i++) replyMsg.setRecord(i, state.msg.getRecord(i));
replyMsg.setUniqueId(req.readReplyMessageId());
replyMsg.setMessageExpiration(expires);
TunnelGatewayMessage m = new TunnelGatewayMessage(_context);
m.setMessage(replyMsg);
m.setMessageExpiration(expires);
m.setTunnelId(new TunnelId(nextId));
if (_context.routerHash().equals(nextPeer)) {
// ok, we are the gateway, so inject it
if (_log.shouldLog(Log.DEBUG))
_log.debug("We are the reply gateway for " + nextId + " when replying to replyMessage " + req);
_context.tunnelDispatcher().dispatch(m);
} else {
// ok, the gateway is some other peer, shove 'er across
OutNetMessage outMsg = new OutNetMessage(_context, m, expires, PRIORITY, nextPeerInfo);
if (response == 0)
outMsg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
_context.outNetMessagePool().add(outMsg);
}
}
}
Aggregations