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.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/net.cpp') diff --git a/src/net.cpp b/src/net.cpp index 3660735..6e74cbb 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1622,12 +1622,29 @@ void ThreadSocketHandler2(void* parg) unsigned int nNodes = 0; CRITICAL_BLOCK(cs_vNodes) nNodes = (unsigned int)vNodes.size(); + // Also count how many connections this one address already + // holds. A single IP that keeps opening sockets -- a broken + // node or a deliberate connection-exhaustion flood -- must not + // be able to take the whole table, which is what starved the + // relays. + unsigned int nFromThisIP = 0; + CRITICAL_BLOCK(cs_vNodes) + for (vector::iterator it = vNodes.begin(); it != vNodes.end(); ++it) + if ((*it)->addr.ip == addr.ip) + nFromThisIP++; + if (nNodes >= MAX_CONNECTIONS) { LogPrint("net", "refusing connection from %s, already at %u\n", addr.ToString().c_str(), nNodes); BtfCloseSocket(hSocket); } + else if (nFromThisIP >= MAX_CONNECTIONS_PER_IP) + { + LogPrint("net", "refusing connection from %s, already %u from that address\n", + addr.ToString().c_str(), nFromThisIP); + BtfCloseSocket(hSocket); + } else { LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str()); -- cgit v1.2.3