From e3fc34ef1f97e5bb2019b91453a5a9f0de310184 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 21 Nov 2021 08:17:43 -0500 Subject: [PATCH] Tunnels: Ban peer on excessive build requests Drop requests if previous or next peer is banned Console: Drop peer when manually banned Update: Drop peer when banlisted --- .../net/i2p/router/update/NewsFetcher.java | 4 ++- .../router/web/helpers/ConfigPeerHandler.java | 1 + history.txt | 7 ++++++ installer/resources/blocklist.txt | 2 ++ .../src/net/i2p/router/RouterVersion.java | 4 +-- .../transport/ntcp/InboundEstablishState.java | 5 ++-- .../i2p/router/tunnel/pool/BuildHandler.java | 21 +++++++++++----- .../tunnel/pool/ParticipatingThrottler.java | 25 ++++++++++++++++++- .../router/tunnel/pool/RequestThrottler.java | 25 ++++++++++++++++++- 9 files changed, 81 insertions(+), 13 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java index 5043ca623..f2376f8ad 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java @@ -651,8 +651,10 @@ class NewsFetcher extends UpdateRunner { continue; } Hash h = Hash.create(b); - if (!ban.isBanlistedForever(h)) + if (!ban.isBanlistedForever(h)) { ban.banlistRouterForever(h, reason); + _context.commSystem().forceDisconnect(h); + } } else { byte[] ip = Addresses.getIP(s); if (ip == null) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHandler.java index 1b29feb4d..243e06b89 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHandler.java @@ -22,6 +22,7 @@ public class ConfigPeerHandler extends FormHandler { Hash h = getHash(); if (h != null) { _context.banlist().banlistRouterForever(h, _t("Manually banned via {0}"), "configpeer"); + _context.commSystem().forceDisconnect(h); addFormNotice(_t("Peer") + " " + _peer + " " + _t("banned until restart") ); return; } diff --git a/history.txt b/history.txt index 5e6321e1d..32916b8e8 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,10 @@ +2021-11-21 zzz + * Console: Drop peer when manually banned + * Tunnels: + - Ban peer on excessive build requests + - Drop requests if previous or next peer is banned + * Update: Drop peer when banlisted + 2021-11-12 zzz * Tunnels: Improve build success when at conn limits diff --git a/installer/resources/blocklist.txt b/installer/resources/blocklist.txt index 82971857e..b034a36ca 100644 --- a/installer/resources/blocklist.txt +++ b/installer/resources/blocklist.txt @@ -101,6 +101,7 @@ Sybil:47.90.120.30 Sybil:47.208.97.112 Sybil:51.211.161.115 Sybil:66.42.58.61 +Tunnels:93.157.12.248 Sybil:95.47.57.0/24 Sybil:95.85.100.0/22 Sybil:95.85.104.0/22 @@ -135,3 +136,4 @@ Sybil:2400;8500;1302;819;a150;95;134;1590 Sybil:2400;8500;1302;824;a150;95;144;951 Sybil:2400;8500;1302;825;150;95;147;89 Sybil:2400;8500;1302;828;a150;95;153;2202 +Tunnels:JbifzqZZqeTXtxK6KDqNUPWaW-phKqeS~tfJT82SIYI= diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 9bc4388a0..cf2cf692a 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -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 = 5; + public final static long BUILD = 6; /** 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); diff --git a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java index c5fba8f04..e06caca8b 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java @@ -33,6 +33,7 @@ import net.i2p.router.RouterContext; import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; import net.i2p.router.transport.crypto.DHSessionKeyBuilder; import static net.i2p.router.transport.ntcp.OutboundNTCP2State.*; +import net.i2p.util.Addresses; import net.i2p.util.ByteArrayStream; import net.i2p.util.ByteCache; import net.i2p.util.Log; @@ -181,8 +182,8 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa InetAddress addr = this._con.getChannel().socket().getInetAddress(); byte[] ip = (addr == null) ? null : addr.getAddress(); if (_context.banlist().isBanlistedForever(aliceHash)) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Dropping inbound connection from permanently banlisted peer: " + aliceHash); + if (_log.shouldWarn()) + _log.warn("Dropping inbound connection from permanently banlisted peer at " + Addresses.toString(ip) + " : " + aliceHash); // So next time we will not accept the con from this IP, // rather than doing the whole handshake if(ip != null) diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java index 6a552b56e..b586b1dbf 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java @@ -467,6 +467,13 @@ class BuildHandler implements Runnable { Hash from = state.fromHash; if (from == null && state.from != null) from = state.from.calculateHash(); + if (from != null && _context.banlist().isBanlisted(from)) { + // Usually won't have connected, but may have been banlisted after connect + if (_log.shouldWarn()) + _log.warn("Drop request, previous peer is banned: " + from); + _context.commSystem().mayDisconnect(from); + return -1; + } if (timeSinceReceived > (BuildRequestor.REQUEST_TIMEOUT*3)) { // don't even bother, since we are so overloaded locally @@ -499,13 +506,15 @@ class BuildHandler implements Runnable { return -1; } - long beforeLookup = System.currentTimeMillis(); Hash nextPeer = req.readNextIdentity(); - long readPeerTime = System.currentTimeMillis()-beforeLookup; + if (_context.banlist().isBanlisted(nextPeer)) { + if (_log.shouldWarn()) + _log.warn("Drop request, next peer is banned: " + nextPeer); + if (from != null) + _context.commSystem().mayDisconnect(from); + return -1; + } RouterInfo nextPeerInfo = _context.netDb().lookupRouterInfoLocally(nextPeer); - long lookupTime = System.currentTimeMillis()-beforeLookup; - if (lookupTime > 500 && _log.shouldLog(Log.WARN)) - _log.warn("Took too long to lookup the request: " + lookupTime + "/" + readPeerTime + " for " + req); if (nextPeerInfo == null) { // limit concurrent next-hop lookups to prevent job queue overload attacks int numTunnels = _context.tunnelManager().getParticipatingCount(); @@ -549,7 +558,7 @@ class BuildHandler implements Runnable { + " ID: " + state.msg.getUniqueId() + " handled and we know the next peer " + nextPeer + " after " + handleTime - + "/" + decryptTime + "/" + lookupTime + "/" + timeSinceReceived); + + "/" + decryptTime + "/" + timeSinceReceived); return handleTime; } } diff --git a/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java b/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java index e3d877725..abd186ca2 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java @@ -2,6 +2,7 @@ package net.i2p.router.tunnel.pool; import net.i2p.data.Hash; import net.i2p.router.RouterContext; +import net.i2p.util.Log; import net.i2p.util.ObjectCounter; import net.i2p.util.SimpleTimer; @@ -30,6 +31,7 @@ import net.i2p.util.SimpleTimer; class ParticipatingThrottler { private final RouterContext context; private final ObjectCounter counter; + private final Log _log; /** portion of the tunnel lifetime */ private static final int LIFETIME_PORTION = 3; @@ -41,6 +43,7 @@ class ParticipatingThrottler { ParticipatingThrottler(RouterContext ctx) { this.context = ctx; this.counter = new ObjectCounter(); + _log = ctx.logManager().getLog(ParticipatingThrottler.class); ctx.simpleTimer2().addPeriodicEvent(new Cleaner(), CLEAN_TIME); } @@ -48,7 +51,16 @@ class ParticipatingThrottler { boolean shouldThrottle(Hash h) { int numTunnels = this.context.tunnelManager().getParticipatingCount(); int limit = Math.max(MIN_LIMIT, Math.min(MAX_LIMIT, numTunnels * PERCENT_LIMIT / 100)); - return this.counter.increment(h) > limit; + int count = counter.increment(h); + boolean rv = count > limit; + if (rv && count == 2 * limit) { + context.banlist().banlistRouter(h, "Excess participating tunnels", null, null, context.clock().now() + 30*60*1000); + // drop after any accepted tunnels have expired + context.simpleTimer2().addEvent(new Disconnector(h), 11*60*1000); + if (_log.shouldWarn()) + _log.warn("Banning router for excess part. tunnels, limit: " + limit + " count: " + count + ' ' + h.toBase64()); + } + return rv; } private class Cleaner implements SimpleTimer.TimedEvent { @@ -56,4 +68,15 @@ class ParticipatingThrottler { ParticipatingThrottler.this.counter.clear(); } } + + /** + * @since 0.9.52 + */ + private class Disconnector implements SimpleTimer.TimedEvent { + private final Hash h; + public Disconnector(Hash h) { this.h = h; } + public void timeReached() { + context.commSystem().forceDisconnect(h); + } + } } diff --git a/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java b/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java index 794808408..7c29a5500 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java +++ b/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java @@ -2,6 +2,7 @@ package net.i2p.router.tunnel.pool; import net.i2p.data.Hash; import net.i2p.router.RouterContext; +import net.i2p.util.Log; import net.i2p.util.ObjectCounter; import net.i2p.util.SimpleTimer; @@ -15,6 +16,7 @@ import net.i2p.util.SimpleTimer; class RequestThrottler { private final RouterContext context; private final ObjectCounter counter; + private final Log _log; /** portion of the tunnel lifetime */ private static final int LIFETIME_PORTION = 6; @@ -26,6 +28,7 @@ class RequestThrottler { RequestThrottler(RouterContext ctx) { this.context = ctx; this.counter = new ObjectCounter(); + _log = ctx.logManager().getLog(RequestThrottler.class); ctx.simpleTimer2().addPeriodicEvent(new Cleaner(), CLEAN_TIME); } @@ -33,7 +36,16 @@ class RequestThrottler { boolean shouldThrottle(Hash h) { int numTunnels = this.context.tunnelManager().getParticipatingCount(); int limit = Math.max(MIN_LIMIT, Math.min(MAX_LIMIT, numTunnels * PERCENT_LIMIT / 100)); - return this.counter.increment(h) > limit; + int count = counter.increment(h); + boolean rv = count > limit; + if (rv && count == 2 * limit) { + context.banlist().banlistRouter(h, "Excess tunnel requests", null, null, context.clock().now() + 30*60*1000); + // drop after any accepted tunnels have expired + context.simpleTimer2().addEvent(new Disconnector(h), 11*60*1000); + if (_log.shouldWarn()) + _log.warn("Banning router for excess tunnel requests, limit: " + limit + " count: " + count + ' ' + h.toBase64()); + } + return rv; } private class Cleaner implements SimpleTimer.TimedEvent { @@ -41,4 +53,15 @@ class RequestThrottler { RequestThrottler.this.counter.clear(); } } + + /** + * @since 0.9.52 + */ + private class Disconnector implements SimpleTimer.TimedEvent { + private final Hash h; + public Disconnector(Hash h) { this.h = h; } + public void timeReached() { + context.commSystem().forceDisconnect(h); + } + } }