From db84acfc5f99ac1ddf4962879b4cbaf1af9952dd Mon Sep 17 00:00:00 2001 From: bitflash Date: Tue, 18 Aug 2026 10:04:28 -0300 Subject: net: cap inbound connections per address A single IP could hold every inbound slot up to MAX_CONNECTIONS -- which is what a connection-exhaustion flood did to the rendezvous relays, one address parking hundreds of sockets and starving real peers. Add MAX_CONNECTIONS_PER_IP (32): the accept path counts existing connections from the same address and refuses a new one past the cap. The relays' firewall connlimit does the same a layer lower; this makes every build carry the defense, including future poll()-based relays with no FD ceiling. --- src/net.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/net.h') diff --git a/src/net.h b/src/net.h index 984588b..4fdd1b3 100644 --- a/src/net.h +++ b/src/net.h @@ -112,6 +112,15 @@ static const int BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS = 2 * 60; // 125 is Bitcoin's number and remains a conservative public-node default. static const unsigned int MAX_CONNECTIONS = 125; +// Cap inbound connections from any single address. Without it one source can +// hold every slot up to MAX_CONNECTIONS -- exactly what happened to the +// rendezvous relays, where a single IP parked hundreds of connections and +// starved real peers. A legitimate peer, even a relay talking to a relay, +// needs only a handful; 32 leaves generous room while denying any one address +// more than a quarter of the table. The firewall connlimit on the relays does +// the same thing a layer lower; this makes every build carry the defense. +static const unsigned int MAX_CONNECTIONS_PER_IP = 32; + // A node marked for disconnect is normally held until its buffers drain, so // a last message still goes out. But vSend cannot drain through a socket // whose far end is gone, and that made such a node immortal: it stayed in -- cgit v1.2.3