I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • equincey/i2p.i2p
  • kytv/i2p.i2p
  • agentoocat/i2p.i2p
  • marek/i2p.i2p
  • aargh/i2p.i2p
  • longyap/i2p.i2p
  • Kalhintz/i2p.i2p
  • kelare/i2p.i2p
  • loveisgrief/i2p.i2p
  • ashtod/i2p.i2p
  • y2kboy23/i2p.i2p
  • apsoyka/i2p.i2p
  • mesh/i2p.i2p
  • obscuratus/i2p.i2p
  • Lfrr/i2p.i2p
  • anonymousmaybe/i2p.i2p
  • zzz/i2p.i2p
  • lbt/i2p.i2p
  • 31337/i2p.i2p
  • DuncanIdaho/i2p.i2p
  • i2p-hackers/i2p.i2p
  • elde/i2p.i2p
  • thebland/i2p.i2p
  • echelon/i2p.i2p
  • welshlyluvah1967/i2p.i2p
  • sadie/i2p.i2p
  • zlatinb/i2p.i2p
  • pVT0/i2p.i2p
  • idk/i2p.i2p
29 results
Show changes
Commits on Source (6)
  • obscuratus i2p's avatar
    Router/Tunnel: Handling Updates for Inbound Messages. · 18d939bc
    obscuratus i2p authored and idk's avatar idk committed
    18d939bc
  • idk's avatar
    Merge branch '2.2.1-issue397-v2' into 'master' · 76197ce7
    idk authored
    Router/Tunnel: Handling Updates for Inbound Messages.
    
    See merge request !88
    76197ce7
  • idk's avatar
    Router/Tunnel: xor message IDs in order to prevent cross-context leaks. · 687858e7
    idk authored
    Adds unique message ID's per context to bloom filter for safer replay protection.
    
    The transport and client tunnel managers use a message ID in order to prevent
    messages from being replayed. Prior to this checkin, the message ID queue used
    the same IDs in clients and transports. If a message was sent to a transport
    and a client with the same message ID, the message ID in one would cause a replay
    to be detected in the other.
    
    The result would be that the message reply would come back empty, creating a
    point of evidence that a client and a transport were hosted on the same router.
    
    However, there is no way from the attackers POV to determine with certainty that
    the message was dropped because the message was replayed, making it very easy to
    demonstrate a potential information leak using a known router and a known client,
    but more difficult, to use to deanonymize a known client on an unknown router
    (i.e. by trying routers from the local NetDB).
    
    So what we have here is a situation where an attacker observing router behavior
    can say that a message was dropped, and that they have reason to believe it is
    because it contained an ID which was replayed. This constitutes a potential
    information leak and is resolved by this checkin.
    
    patch created by @obscuratus, tested, reviewed and checked in by @obscuratus and @idk
    Unverified
    687858e7
  • idk's avatar
    bump rc · f308ebee
    idk authored
    Unverified
    f308ebee
  • idk's avatar
    43b43649
  • idk's avatar
    Merge branch 'i2p.i2p.congestion-caps' into 'master' · 997644a0
    idk authored
    start implementing congestion caps handling by adding...
    
    See merge request !89
    997644a0
......@@ -119,6 +119,10 @@ public class InNetMessagePool implements Service {
_handlerJobBuilders[i2npMessageType] = builder;
return old;
}
public int add(I2NPMessage messageBody, RouterIdentity fromRouter, Hash fromRouterHash) {
return add(messageBody, fromRouter, fromRouterHash, 0);
}
/**
* Add a new message to the pool.
......@@ -134,7 +138,10 @@ public class InNetMessagePool implements Service {
* @return -1 for some types of errors but not all; 0 otherwise
* (was queue length, long ago)
*/
public int add(I2NPMessage messageBody, RouterIdentity fromRouter, Hash fromRouterHash) {
public int add(I2NPMessage messageBody,
RouterIdentity fromRouter,
Hash fromRouterHash,
long msgIDBloomXor) {
final MessageHistory history = _context.messageHistory();
final boolean doHistory = history.getDoLog();
......@@ -158,7 +165,11 @@ public class InNetMessagePool implements Service {
// just validate the expiration
invalidReason = _context.messageValidator().validateMessage(exp);
} else {
invalidReason = _context.messageValidator().validateMessage(messageBody.getUniqueId(), exp);
if (msgIDBloomXor == 0)
invalidReason = _context.messageValidator().validateMessage(messageBody.getUniqueId(), exp);
else
invalidReason = _context.messageValidator().validateMessage(messageBody.getUniqueId()
^ msgIDBloomXor, exp);
}
if (invalidReason != null) {
......
......@@ -18,10 +18,10 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Git";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 0;
public final static long BUILD = 1;
/** for example "-test" */
public final static String EXTRA = "";
public final static String EXTRA = "rc";
public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;
public static void main(String args[]) {
System.out.println("I2P Router version: " + FULL_VERSION);
......
......@@ -85,7 +85,9 @@ public class TunnelPoolSettings {
private static final int MIN_PRIORITY = -25;
private static final int MAX_PRIORITY = 25;
private static final int EXPLORATORY_PRIORITY = 30;
private final long _msgIdBloomXor;
/**
* Exploratory tunnel
*/
......@@ -116,6 +118,8 @@ public class TunnelPoolSettings {
_IPRestriction = DEFAULT_IP_RESTRICTION;
_unknownOptions = new Properties();
_randomKey = generateRandomKey();
_msgIdBloomXor = RandomSource.getInstance().nextLong();
if (_isExploratory && !_isInbound)
_priority = EXPLORATORY_PRIORITY;
if (!_isExploratory)
......@@ -286,6 +290,8 @@ public class TunnelPoolSettings {
*/
public Properties getUnknownOptions() { return _unknownOptions; }
public long getMsgIdBloomXor() { return _msgIdBloomXor; }
/**
* Defaults in props are NOT honored.
* In-JVM client side must promote defaults to the primary map.
......
......@@ -34,6 +34,11 @@ class CapacityCalculator {
private static final double PENALTY_NO_R_CAP = 1;
private static final double PENALTY_U_CAP = 2;
private static final double PENALTY_LAST_SEND_FAIL = 4;
// congestion cap penalty not yet applied
//private static final double PENALTY_D_CAP = 0;
// congestion cap penalty not yet applied
//private static final double PENALTY_E_CAP = 0;
//private static final double PENALTY_G_CAP = 0;
private static final double PENALTY_RECENT_SEND_FAIL = 4;
private static final double BONUS_LAST_SEND_SUCCESS = 1;
private static final double BONUS_RECENT_SEND_SUCCESS = 1;
......@@ -124,11 +129,13 @@ class CapacityCalculator {
capacity -= PENALTY_U_CAP;
if (caps.indexOf(Router.CAPABILITY_BW32) >= 0)
capacity -= PENALTY_L_CAP;
/* TODO
if (caps.indexOf(Router.CAPABILITY_CONGESTION_MODERATE) >= 0)
/* TODO */
/*if (caps.indexOf(Router.CAPABILITY_CONGESTION_MODERATE) >= 0)
capacity -= PENALTY_D_CAP;
else if (caps.indexOf(Router.CAPABILITY_CONGESTION_SEVERE) >= 0)
capacity -= PENALTY_E_CAP;
capacity -= PENALTY_E_CAP;*/
/* TODO: G caps can be excluded in TunnelPeerSelector by adding it to DEFAULT_EXCLUDE_CAPS
decide what other handling if any is needed here.
else if (caps.indexOf(Router.CAPABILITY_NO_TUNNELS) >= 0)
capacity -= PENALTY_G_CAP;
*/
......
......@@ -111,6 +111,8 @@ public class TransportManager implements TransportEventListener {
private static final long UPNP_REFRESH_TIME = UPnP.LEASE_TIME_SECONDS * 1000L / 3;
private final long _msgIdBloomXor;
public TransportManager(RouterContext context) {
_context = context;
_log = _context.logManager().getLog(TransportManager.class);
......@@ -134,6 +136,7 @@ public class TransportManager implements TransportEventListener {
_dhThread = (_enableUDP || enableNTCP2) ? new DHSessionKeyBuilder.PrecalcRunner(context) : null;
// always created, even if NTCP2 is not enabled, because ratchet needs it
_xdhThread = new X25519KeyFactory(context);
_msgIdBloomXor = _context.random().nextLong();
}
/**
......@@ -965,7 +968,7 @@ public class TransportManager implements TransportEventListener {
if (_log.shouldLog(Log.DEBUG))
_log.debug("I2NPMessage received: " + message.getClass().getSimpleName() /*, new Exception("Where did I come from again?") */ );
try {
_context.inNetMessagePool().add(message, fromRouter, fromRouterHash);
_context.inNetMessagePool().add(message, fromRouter, fromRouterHash, _msgIdBloomXor);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Added to in pool");
} catch (IllegalArgumentException iae) {
......
......@@ -33,7 +33,8 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
private final Log _log;
private final Hash _client;
private final GarlicMessageReceiver _receiver;
private String _clientNickname;
private final long _msgIdBloomXor;
/**
* @param client null for router tunnel
*/
......@@ -43,6 +44,23 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
_log = ctx.logManager().getLog(InboundMessageDistributor.class);
_receiver = new GarlicMessageReceiver(ctx, this, client);
// all createRateStat in TunnelDispatcher
if (_client != null) {
TunnelPoolSettings clienttps = _context.tunnelManager().getInboundSettings(_client);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Initializing client (nickname: "
+ clienttps.getDestinationNickname()
+ " b32: " + _client.toBase32()
+ ") InboundMessageDistributor with tunnel pool settings: " + clienttps);
_clientNickname = clienttps.getDestinationNickname();
_msgIdBloomXor = clienttps.getMsgIdBloomXor();
} else {
_clientNickname = "NULL/Expl";
_msgIdBloomXor = 0;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Initializing null or exploratory InboundMessageDistributor");
}
}
public void distribute(I2NPMessage msg, Hash target) {
......@@ -51,7 +69,9 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
public void distribute(I2NPMessage msg, Hash target, TunnelId tunnel) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("IBMD for " + _client + " to " + target + " / " + tunnel + " : " + msg);
_log.debug("IBMD for " + _clientNickname + " ("
+ ((_client != null) ? _client.toBase32() : "null")
+ ") to " + target + " / " + tunnel + " : " + msg);
// allow messages on client tunnels even after client disconnection, as it may
// include e.g. test messages, etc. DataMessages will be dropped anyway
......@@ -98,7 +118,8 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
// We handle this safely, so we don't ask him again.
// Todo: if peer was ff and RI is not ff, queue for exploration in netdb (but that isn't part of the facade now)
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping DSM down a tunnel for " + _client.toBase32() + ": " + msg);
_log.warn("Inbound DSM received down a tunnel for " + _clientNickname
+ " (" + _client.toBase32() + "): " + msg);
// Handle safely by just updating the caps table, after doing basic validation
Hash key = dsm.getKey();
if (_context.routerHash().equals(key))
......@@ -176,9 +197,12 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
} // switch
} // client != null
if ( (target == null) || ( (tunnel == null) && (_context.routerHash().equals(target) ) ) ) {
// targetting us either implicitly (no target) or explicitly (no tunnel)
// make sure we don't honor any remote requests directly (garlic instructions, etc)
if ( (target == null) && (tunnel == null) ) {
// Since the InboundMessageDistributor handles messages for the endpoint,
// most messages that arrive here have both target==null and tunnel==null.
// Messages with targeting instructions need careful handling, and will
// typically be dropped because we're the endpoint. Especially when they
// specifically target this router (_context.routerHash().equals(target)).
if (type == GarlicMessage.MESSAGE_TYPE) {
// in case we're looking for replies to a garlic message (cough load tests cough)
_context.inNetMessagePool().handleReplies(msg);
......@@ -187,47 +211,43 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
_receiver.receive((GarlicMessage)msg);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("distributing inbound tunnel message into our inNetMessagePool: " + msg);
_context.inNetMessagePool().add(msg, null, null);
_log.info("distributing inbound tunnel message into our inNetMessagePool"
+ " (for client " + _clientNickname + " ("
+ ((_client != null) ? _client.toBase32() : "null")
+ ") to target=NULL/tunnel=NULL " + msg);
if (_msgIdBloomXor == 0)
_context.inNetMessagePool().add(msg, null, null);
else
_context.inNetMessagePool().add(msg, null, null, _msgIdBloomXor);
}
/****** latency measuring attack?
} else if (_context.routerHash().equals(target)) {
// the want to send it to a tunnel, except we are also that tunnel's gateway
// dispatch it directly
if (_log.shouldLog(Log.INFO))
_log.info("distributing inbound tunnel message back out, except we are the gateway");
TunnelGatewayMessage gw = new TunnelGatewayMessage(_context);
gw.setMessage(msg);
gw.setTunnelId(tunnel);
gw.setMessageExpiration(_context.clock().now()+10*1000);
gw.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
_context.tunnelDispatcher().dispatch(gw);
******/
if (type == GarlicMessage.MESSAGE_TYPE)
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping inbound garlic message TARGETED TO OUR ROUTER for client "
+ _clientNickname + " ("
+ ((_client != null) ? _client.toBase32() : "null")
+ ") to " + target + " / " + tunnel);
else
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping inbound message TARGETED TO OUR ROUTER for client "
+ _clientNickname + " (" + ((_client != null) ? _client.toBase32() : "null")
+ ") to " + target + " / " + tunnel + " : " + msg);
return;
} else {
// ok, they want us to send it remotely, but that'd bust our anonymity,
// so we send it out a tunnel first
// TODO use the OCMOSJ cache to pick OB tunnel we are already using?
TunnelInfo out = _context.tunnelManager().selectOutboundTunnel(_client, target);
if (out == null) {
if (type == GarlicMessage.MESSAGE_TYPE)
if (_log.shouldLog(Log.WARN))
_log.warn("no outbound tunnel to send the client message for " + _client + ": " + msg);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("distributing IB tunnel msg type " + type + " back out " + out
+ " targetting " + target);
TunnelId outId = out.getSendTunnelId(0);
if (outId == null) {
if (_log.shouldLog(Log.ERROR))
_log.error("strange? outbound tunnel has no outboundId? " + out
+ " failing to distribute " + msg);
return;
}
long exp = _context.clock().now() + 20*1000;
if (msg.getMessageExpiration() < exp)
msg.setMessageExpiration(exp);
_context.tunnelDispatcher().dispatchOutbound(msg, outId, tunnel, target);
_log.warn("Dropping targeted inbound garlic message for client "
+ _clientNickname + " ("
+ ((_client != null) ? _client.toBase32() : "null")
+ ") to " + target + " / " + tunnel);
else
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping targeted inbound message for client " + _clientNickname
+ " (" + ((_client != null) ? _client.toBase32() : "null")
+ " to " + target + " / " + tunnel + " : " + msg);
return;
}
}
/**
......@@ -269,9 +289,13 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
// ... and inject it.
((LeaseSet)dsm.getEntry()).setReceivedBy(_client);
if (_log.shouldLog(Log.INFO))
_log.info("Storing garlic LS down tunnel for: " + dsm.getKey() + " sent to: " +
(_client != null ? _client.toBase32() : "router"));
_context.inNetMessagePool().add(dsm, null, null);
_log.info("Storing garlic LS down tunnel for: " + dsm.getKey() + " sent to: "
+ _clientNickname + " ("
+ (_client != null ? _client.toBase32() : ") router"));
if (_msgIdBloomXor == 0)
_context.inNetMessagePool().add(dsm, null, null);
else
_context.inNetMessagePool().add(dsm, null, null, _msgIdBloomXor);
} else {
if (_client != null) {
// drop it, since the data we receive shouldn't include router
......@@ -279,7 +303,8 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
// open an attack vector)
_context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1,
DatabaseStoreMessage.MESSAGE_TYPE);
_log.error("Dropped dangerous message down a tunnel for " + _client.toBase32() + ": " + dsm, new Exception("cause"));
_log.error("Dropped dangerous message down a tunnel for " + _clientNickname
+ " ("+ _client.toBase32() + ") : " + dsm, new Exception("cause"));
return;
}
// Case 3:
......@@ -288,11 +313,14 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
// We must send to the InNetMessagePool so the message can be matched
// and the search marked as successful.
// note that encrypted replies to RI lookups is currently disables in ISJ, we won't get here.
// ... and inject it.
if (_log.shouldLog(Log.INFO))
_log.info("Storing garlic RI down tunnel for: " + dsm.getKey());
_context.inNetMessagePool().add(dsm, null, null);
_log.info("Storing garlic RI down tunnel (" + _clientNickname
+ ") for: " + dsm.getKey());
if (_msgIdBloomXor == 0)
_context.inNetMessagePool().add(dsm, null, null);
else
_context.inNetMessagePool().add(dsm, null, null, _msgIdBloomXor);
}
} else if (_client != null && type == DatabaseSearchReplyMessage.MESSAGE_TYPE) {
// DSRMs show up here now that replies are encrypted
......@@ -311,7 +339,10 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
orig = newMsg;
}
****/
_context.inNetMessagePool().add(orig, null, null);
if (_msgIdBloomXor == 0)
_context.inNetMessagePool().add(orig, null, null);
else
_context.inNetMessagePool().add(orig, null, null, _msgIdBloomXor);
} else if (type == DataMessage.MESSAGE_TYPE) {
// a data message targetting the local router is how we send load tests (real
// data messages target destinations)
......@@ -324,9 +355,14 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
// 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.toBase32() + ": " + data, new Exception("cause"));
_log.error("Dropped dangerous message received down a tunnel for "
+ _clientNickname + " (" + _client.toBase32() + ") : "
+ data, new Exception("cause"));
} else {
_context.inNetMessagePool().add(data, null, null);
if (_msgIdBloomXor == 0)
_context.inNetMessagePool().add(data, null, null);
else
_context.inNetMessagePool().add(data, null, null, _msgIdBloomXor);
}
return;
......@@ -370,9 +406,14 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
case DeliveryInstructions.DELIVERY_MODE_ROUTER: // fall through
case DeliveryInstructions.DELIVERY_MODE_TUNNEL:
// Targeted messages are usually dropped, but it is safe to
// allow distribute() to evaluate the message.
if (_log.shouldLog(Log.INFO))
_log.info("clove targetted " + instructions.getRouter() + ":" + instructions.getTunnelId()
+ ", treat recursively to prevent leakage");
_log.info("Recursively handling message from targeted clove (for client:"
+ _clientNickname + " " + ((_client != null) ? _client.toBase32() : "null")
+ ", msg type: " + data.getClass().getSimpleName() + "): " + instructions.getRouter()
+ ":" + instructions.getTunnelId() + " msg: " + data);
distribute(data, instructions.getRouter(), instructions.getTunnelId());
return;
......
......@@ -42,7 +42,7 @@ import net.i2p.util.VersionComparator;
*/
public abstract class TunnelPeerSelector extends ConnectChecker {
private static final String DEFAULT_EXCLUDE_CAPS = String.valueOf(Router.CAPABILITY_BW12);
private static final String DEFAULT_EXCLUDE_CAPS = String.valueOf(Router.CAPABILITY_BW12)+String.valueOf(Router.CAPABILITY_CONGESTION_SEVERE);
protected TunnelPeerSelector(RouterContext context) {
super(context);
......