use of net.i2p.router.TunnelPoolSettings in project i2p.i2p by i2p.
the class ClientPeerSelector method selectPeers.
/**
* Returns ENDPOINT FIRST, GATEWAY LAST!!!!
* In: us .. closest .. middle .. IBGW
* Out: OBGW .. middle .. closest .. us
*
* @return ordered list of Hash objects (one per peer) specifying what order
* they should appear in a tunnel (ENDPOINT FIRST). This includes
* the local router in the list. If there are no tunnels or peers
* to build through, and the settings reject 0 hop tunnels, this will
* return null.
*/
public List<Hash> selectPeers(TunnelPoolSettings settings) {
int length = getLength(settings);
if (length < 0)
return null;
if ((length == 0) && (settings.getLength() + settings.getLengthVariance() > 0))
return null;
List<Hash> rv;
boolean isInbound = settings.isInbound();
if (length > 0) {
// special cases
boolean v6Only = isIPv6Only();
boolean ntcpDisabled = isNTCPDisabled();
boolean ssuDisabled = isSSUDisabled();
boolean checkClosestHop = v6Only || ntcpDisabled || ssuDisabled;
boolean hidden = ctx.router().isHidden() || ctx.router().getRouterInfo().getAddressCount() <= 0;
boolean hiddenInbound = hidden && isInbound;
boolean hiddenOutbound = hidden && !isInbound;
if (shouldSelectExplicit(settings))
return selectExplicit(settings, length);
Set<Hash> exclude = getExclude(isInbound, false);
Set<Hash> matches = new HashSet<Hash>(length);
if (length == 1) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(isInbound);
if (moreExclude != null)
exclude.addAll(moreExclude);
}
if (hiddenInbound) {
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(exclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
}
if (matches.isEmpty()) {
// ANFP does not fall back to non-connected
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0);
}
matches.remove(ctx.routerHash());
rv = new ArrayList<Hash>(matches);
} else {
// build a tunnel using 4 subtiers.
// For a 2-hop tunnel, the first hop comes from subtiers 0-1 and the last from subtiers 2-3.
// For a longer tunnels, the first hop comes from subtier 0, the middle from subtiers 2-3, and the last from subtier 1.
rv = new ArrayList<Hash>(length + 1);
Hash randomKey = settings.getRandomKey();
// OBEP or IB last hop
// group 0 or 1 if two hops, otherwise group 0
Set<Hash> lastHopExclude;
if (isInbound) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(false);
if (moreExclude != null) {
moreExclude.addAll(exclude);
lastHopExclude = moreExclude;
} else {
lastHopExclude = exclude;
}
} else {
lastHopExclude = exclude;
}
} else {
lastHopExclude = exclude;
}
if (hiddenInbound) {
// IB closest hop
if (log.shouldInfo())
log.info("CPS SANFP closest IB exclude " + lastHopExclude.size());
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
if (matches.isEmpty()) {
if (log.shouldInfo())
log.info("CPS SFP closest IB exclude " + lastHopExclude.size());
// ANFP does not fall back to non-connected
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else if (hiddenOutbound) {
// OBEP
// check for hidden and outbound, and the paired (inbound) tunnel is zero-hop
// if so, we need the OBEP to be connected to us, so we get the build reply back
// This should be rare except at startup
TunnelManagerFacade tmf = ctx.tunnelManager();
TunnelPool tp = tmf.getInboundPool(settings.getDestination());
boolean pickFurthest;
if (tp != null) {
pickFurthest = true;
TunnelPoolSettings tps = tp.getSettings();
int len = tps.getLength();
if (len <= 0 || tps.getLengthOverride() == 0 || len + tps.getLengthVariance() <= 0) {
// leave it true
} else {
List<TunnelInfo> tunnels = tp.listTunnels();
if (!tunnels.isEmpty()) {
for (TunnelInfo ti : tp.listTunnels()) {
if (ti.getLength() > 1) {
pickFurthest = false;
break;
}
}
} else {
// no tunnels in the paired tunnel pool
// BuildRequester will be using exploratory
tp = tmf.getInboundExploratoryPool();
tps = tp.getSettings();
len = tps.getLength();
if (len <= 0 || tps.getLengthOverride() == 0 || len + tps.getLengthVariance() <= 0) {
// leave it true
} else {
tunnels = tp.listTunnels();
if (!tunnels.isEmpty()) {
for (TunnelInfo ti : tp.listTunnels()) {
if (ti.getLength() > 1) {
pickFurthest = false;
break;
}
}
}
}
}
}
} else {
// shouldn't happen
pickFurthest = false;
}
if (pickFurthest) {
if (log.shouldInfo())
log.info("CPS SANFP OBEP exclude " + lastHopExclude.size());
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
if (matches.isEmpty()) {
// ANFP does not fall back to non-connected
if (log.shouldInfo())
log.info("CPS SFP OBEP exclude " + lastHopExclude.size());
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else {
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else {
// TODO exclude IPv6-only at OBEP? Caught in checkTunnel() below
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
matches.remove(ctx.routerHash());
exclude.addAll(matches);
rv.addAll(matches);
matches.clear();
if (length > 2) {
// middle hop(s)
// group 2 or 3
ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3);
matches.remove(ctx.routerHash());
if (matches.size() > 1) {
// order the middle peers for tunnels >= 4 hops
List<Hash> ordered = new ArrayList<Hash>(matches);
orderPeers(ordered, randomKey);
rv.addAll(ordered);
} else {
rv.addAll(matches);
}
exclude.addAll(matches);
matches.clear();
}
// group 2 or 3 if two hops, otherwise group 1
if (!isInbound) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(true);
if (moreExclude != null)
exclude.addAll(moreExclude);
}
}
// TODO exclude IPv6-only at IBGW? Caught in checkTunnel() below
ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1);
matches.remove(ctx.routerHash());
rv.addAll(matches);
}
} else {
rv = new ArrayList<Hash>(1);
}
// log.debug("EPS result: " + DataHelper.toString(rv));
if (isInbound)
rv.add(0, ctx.routerHash());
else
rv.add(ctx.routerHash());
if (rv.size() > 1) {
if (!checkTunnel(isInbound, rv))
rv = null;
}
return rv;
}
use of net.i2p.router.TunnelPoolSettings in project i2p.i2p by i2p.
the class InboundMessageDistributor method handleClove.
/**
* Handle a clove removed from the garlic message
*/
public void handleClove(DeliveryInstructions instructions, I2NPMessage data) {
int type = data.getType();
switch(instructions.getDeliveryMode()) {
case DeliveryInstructions.DELIVERY_MODE_LOCAL:
if (_log.shouldLog(Log.DEBUG))
_log.debug("local delivery instructions for clove: " + data.getClass().getSimpleName());
if (type == GarlicMessage.MESSAGE_TYPE) {
_receiver.receive((GarlicMessage) data);
} else if (type == DatabaseStoreMessage.MESSAGE_TYPE) {
// Treat db store explicitly here (not in HandleFloodfillDatabaseStoreMessageJob),
// since we don't want to republish (or flood)
// unnecessarily. Reply tokens ignored.
DatabaseStoreMessage dsm = (DatabaseStoreMessage) data;
// Ensure the reply info is cleared, just in case
dsm.setReplyToken(0);
dsm.setReplyTunnel(null);
dsm.setReplyGateway(null);
if (dsm.getEntry().getType() == DatabaseEntry.KEY_TYPE_LEASESET) {
// Case 1:
// store of our own LS.
// This is almost certainly a response to a FloodfillVerifyStoreJob search.
// We must send to the InNetMessagePool so the message can be matched
// and the verify marked as successful.
// Case 2:
// Store of somebody else's LS.
// This could be an encrypted response to an IterativeSearchJob search.
// We must send to the InNetMessagePool so the message can be matched
// and the search marked as successful.
// Or, it's a normal LS bundled with data and a MessageStatusMessage.
// ... and inject it.
((LeaseSet) dsm.getEntry()).setReceivedAsReply();
if (_log.shouldLog(Log.INFO))
_log.info("Storing garlic LS down tunnel for: " + dsm.getKey() + " sent to: " + _client);
_context.inNetMessagePool().add(dsm, null, null);
} else {
if (_client != null) {
// drop it, since the data we receive shouldn't include router
// references, as that might get us to talk to them (and therefore
// open an attack vector)
_context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, DatabaseStoreMessage.MESSAGE_TYPE);
_log.error("Dropped dangerous message down a tunnel for " + _client + ": " + dsm, new Exception("cause"));
return;
}
// ... and inject it.
if (_log.shouldLog(Log.INFO))
_log.info("Storing garlic RI down tunnel for: " + dsm.getKey() + " sent to: " + _client);
_context.inNetMessagePool().add(dsm, null, null);
}
} else if (_client != null && type == DatabaseSearchReplyMessage.MESSAGE_TYPE) {
// DSRMs show up here now that replies are encrypted
// TODO: Strip in IterativeLookupJob etc. instead, depending on
// LS or RI and client or expl., so that we can safely follow references
// in a reply to a LS lookup over client tunnels.
// ILJ would also have to follow references via client tunnels
DatabaseSearchReplyMessage orig = (DatabaseSearchReplyMessage) data;
/**
**
* if (orig.getNumReplies() > 0) {
* if (_log.shouldLog(Log.INFO))
* _log.info("Removing replies from a garlic DSRM down a tunnel for " + _client + ": " + data);
* DatabaseSearchReplyMessage newMsg = new DatabaseSearchReplyMessage(_context);
* newMsg.setFromHash(orig.getFromHash());
* newMsg.setSearchKey(orig.getSearchKey());
* orig = newMsg;
* }
***
*/
_context.inNetMessagePool().add(orig, null, null);
} else if (type == DataMessage.MESSAGE_TYPE) {
// a data message targetting the local router is how we send load tests (real
// data messages target destinations)
_context.statManager().addRateData("tunnel.handleLoadClove", 1);
data = null;
// _context.inNetMessagePool().add(data, null, null);
} else if (_client != null && type != DeliveryStatusMessage.MESSAGE_TYPE) {
// drop it, since the data we receive shouldn't include other stuff,
// as that might open an attack vector
_context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, data.getType());
_log.error("Dropped dangerous message down a tunnel for " + _client + ": " + data, new Exception("cause"));
} else {
_context.inNetMessagePool().add(data, null, null);
}
return;
case DeliveryInstructions.DELIVERY_MODE_DESTINATION:
Hash to = instructions.getDestination();
// Can we route UnknownI2NPMessages to a destination too?
if (type != DataMessage.MESSAGE_TYPE) {
if (_log.shouldLog(Log.ERROR))
_log.error("cant send a " + data.getClass().getSimpleName() + " to a destination");
} else if (_client != null && _client.equals(to)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("data message came down a tunnel for " + _client);
DataMessage dm = (DataMessage) data;
Payload payload = new Payload();
payload.setEncryptedData(dm.getData());
ClientMessage m = new ClientMessage(_client, payload);
_context.clientManager().messageReceived(m);
} else if (_client != null) {
// Shared tunnel?
TunnelPoolSettings tgt = _context.tunnelManager().getInboundSettings(to);
if (tgt != null && _client.equals(tgt.getAliasOf())) {
// same as above, just different log
if (_log.shouldLog(Log.DEBUG))
_log.debug("data message came down a tunnel for " + _client + " targeting shared " + to);
DataMessage dm = (DataMessage) data;
Payload payload = new Payload();
payload.setEncryptedData(dm.getData());
ClientMessage m = new ClientMessage(to, payload);
_context.clientManager().messageReceived(m);
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Data message came down a tunnel for " + _client + " but targetted " + to);
}
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Data message came down an exploratory tunnel targeting " + to);
}
return;
// fall through
case DeliveryInstructions.DELIVERY_MODE_ROUTER:
case DeliveryInstructions.DELIVERY_MODE_TUNNEL:
if (_log.shouldLog(Log.INFO))
_log.info("clove targetted " + instructions.getRouter() + ":" + instructions.getTunnelId() + ", treat recursively to prevent leakage");
distribute(data, instructions.getRouter(), instructions.getTunnelId());
return;
default:
if (_log.shouldLog(Log.ERROR))
_log.error("Unknown instruction " + instructions.getDeliveryMode() + ": " + instructions);
return;
}
}
use of net.i2p.router.TunnelPoolSettings in project i2p.i2p by i2p.
the class ConfigTunnelsHandler method saveChanges.
/**
* The user made changes to the network config and wants to save them, so
* lets go ahead and do so.
*/
private void saveChanges() {
boolean saveRequired = false;
Map<String, String> changes = new HashMap<String, String>();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Saving changes, with props = " + _settings + ".");
int updated = 0;
int index = 0;
while (true) {
Object val = _settings.get("pool." + index);
if (val == null)
break;
Hash client = new Hash();
String poolName = (val instanceof String ? (String) val : ((String[]) val)[0]);
TunnelPoolSettings in = null;
TunnelPoolSettings out = null;
if ("exploratory".equals(poolName)) {
in = _context.tunnelManager().getInboundSettings();
out = _context.tunnelManager().getOutboundSettings();
} else {
try {
client.fromBase64(poolName);
} catch (DataFormatException dfe) {
addFormError("Internal error (pool name could not resolve - " + poolName + ").");
index++;
continue;
}
in = _context.tunnelManager().getInboundSettings(client);
out = _context.tunnelManager().getOutboundSettings(client);
}
if ((in == null) || (out == null)) {
addFormError("Internal error (pool settings cound not be found for " + poolName + ").");
index++;
continue;
}
in.setLength(getInt(_settings.get(index + ".depthInbound")));
out.setLength(getInt(_settings.get(index + ".depthOutbound")));
in.setLengthVariance(getInt(_settings.get(index + ".varianceInbound")));
out.setLengthVariance(getInt(_settings.get(index + ".varianceOutbound")));
in.setQuantity(getInt(_settings.get(index + ".quantityInbound")));
out.setQuantity(getInt(_settings.get(index + ".quantityOutbound")));
in.setBackupQuantity(getInt(_settings.get(index + ".backupInbound")));
out.setBackupQuantity(getInt(_settings.get(index + ".backupOutbound")));
if ("exploratory".equals(poolName)) {
changes.put(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_LENGTH, in.getLength() + "");
changes.put(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_LENGTH, out.getLength() + "");
changes.put(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_LENGTH_VARIANCE, in.getLengthVariance() + "");
changes.put(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_LENGTH_VARIANCE, out.getLengthVariance() + "");
changes.put(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_QUANTITY, in.getQuantity() + "");
changes.put(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_QUANTITY, out.getQuantity() + "");
changes.put(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_BACKUP_QUANTITY, in.getBackupQuantity() + "");
changes.put(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_BACKUP_QUANTITY, out.getBackupQuantity() + "");
}
if ("exploratory".equals(poolName)) {
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Inbound exploratory settings: " + in);
_log.debug("Outbound exploratory settings: " + out);
}
_context.tunnelManager().setInboundSettings(in);
_context.tunnelManager().setOutboundSettings(out);
} else {
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Inbound settings for " + client.toBase64() + ": " + in);
_log.debug("Outbound settings for " + client.toBase64() + ": " + out);
}
_context.tunnelManager().setInboundSettings(client, in);
_context.tunnelManager().setOutboundSettings(client, out);
}
updated++;
saveRequired = true;
index++;
}
if (updated > 0)
// the count isn't really correct anyway, since we don't check for actual changes
// addFormNotice("Updated settings for " + updated + " pools.");
addFormNotice(_t("Updated settings for all pools."));
if (saveRequired) {
boolean saved = _context.router().saveConfig(changes, null);
if (saved)
addFormNotice(_t("Exploratory tunnel configuration saved successfully."));
else
addFormError(_t("Error saving the configuration (applied but not saved) - please see the error logs."));
}
}
use of net.i2p.router.TunnelPoolSettings in project i2p.i2p by i2p.
the class ConfigTunnelsHelper method getForm.
public String getForm() {
StringBuilder buf = new StringBuilder(1024);
// HTML: <input> cannot be inside a <table>
buf.append("<input type=\"hidden\" name=\"pool.0\" value=\"exploratory\" >\n");
int cur = 1;
Set<Destination> clients = _context.clientManager().listClients();
for (Destination dest : clients) {
buf.append("<input type=\"hidden\" name=\"pool.").append(cur).append("\" value=\"");
buf.append(dest.calculateHash().toBase64()).append("\" >\n");
cur++;
}
buf.append("<table id=\"tunnelconfig\">\n");
TunnelPoolSettings exploratoryIn = _context.tunnelManager().getInboundSettings();
TunnelPoolSettings exploratoryOut = _context.tunnelManager().getOutboundSettings();
renderForm(buf, 0, "exploratory", _t("Exploratory tunnels"), exploratoryIn, exploratoryOut);
cur = 1;
for (Destination dest : clients) {
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(dest.calculateHash());
TunnelPoolSettings out = _context.tunnelManager().getOutboundSettings(dest.calculateHash());
if (in == null || in.getAliasOf() != null || out == null || out.getAliasOf() != null)
continue;
String name = in.getDestinationNickname();
if (name == null)
name = out.getDestinationNickname();
if (name == null)
name = dest.calculateHash().toBase64().substring(0, 6);
String prefix = dest.calculateHash().toBase64().substring(0, 4);
renderForm(buf, cur, prefix, _t("Client tunnels for {0}", DataHelper.escapeHTML(_t(name))), in, out);
cur++;
}
buf.append("</table>\n");
return buf.toString();
}
use of net.i2p.router.TunnelPoolSettings in project i2p.i2p by i2p.
the class ConfigKeyringHelper method render.
/**
* @since 0.9.33 moved from PersistentKeyRing
*/
private void render(StringBuilder buf, boolean local) {
buf.append("\n<table class=\"configtable\"><tr><th align=\"left\">").append(_t("Destination")).append("<th align=\"left\">").append(_t("Name")).append("<th align=\"left\">").append(_t("Encryption Key")).append("</tr>");
for (Map.Entry<Hash, SessionKey> e : _context.keyRing().entrySet()) {
Hash h = e.getKey();
if (local != _context.clientManager().isLocal(h))
continue;
buf.append("\n<tr><td>");
buf.append(h.toBase32());
buf.append("</td><td>");
Destination dest = _context.netDb().lookupDestinationLocally(h);
if (dest != null && local) {
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(h);
if (in != null && in.getDestinationNickname() != null)
buf.append(in.getDestinationNickname());
} else {
String host = _context.namingService().reverseLookup(h);
if (host != null)
buf.append(host);
}
buf.append("</td><td>");
SessionKey sk = e.getValue();
buf.append(sk.toBase64());
buf.append("</td>\n");
}
buf.append("</table>\n");
}
Aggregations