diff options
| author | bitflash <admin@bitflash.network> | 2026-08-18 10:24:01 -0300 |
|---|---|---|
| committer | bitflash <admin@bitflash.network> | 2026-08-18 10:24:01 -0300 |
| commit | 33092ffe8ee5fb5f7c080a44831e5557b63deb63 (patch) | |
| tree | ded284f5c10d89cad8071bcaac2d33dfc2c1012d /src | |
| parent | db84acfc5f99ac1ddf4962879b4cbaf1af9952dd (diff) | |
net: exempt loopback from the per-IP connection cap
Review follow-up. A managed-Tor node receives every inbound onion peer
over its local hidden-service listener, so they all accept as 127.0.0.1.
Capping loopback would cap onion peers as one group and refuse the 33rd
legitimate onion peer -- the wrong limit for a network moving toward
onion-only. Exempt loopback (127.0.0.0/8): the global MAX_CONNECTIONS
still bounds the total, and the direct-IP flood this defends against
never arrives over loopback.
Bench: a 40-connection flood from 127.0.0.1 is now held in full (no
per-IP refusal), while a flood from the machine's LAN address still caps
at exactly 32.
Diffstat (limited to 'src')
| -rw-r--r-- | src/net.cpp | 22 | ||||
| -rw-r--r-- | src/net.h | 8 |
2 files changed, 25 insertions, 5 deletions
diff --git a/src/net.cpp b/src/net.cpp index 6e74cbb..4603d2d 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -1627,11 +1627,23 @@ void ThreadSocketHandler2(void* parg) | |||
| 1627 | // node or a deliberate connection-exhaustion flood -- must not | 1627 | // node or a deliberate connection-exhaustion flood -- must not |
| 1628 | // be able to take the whole table, which is what starved the | 1628 | // be able to take the whole table, which is what starved the |
| 1629 | // relays. | 1629 | // relays. |
| 1630 | // | ||
| 1631 | // Loopback is exempt. Onion peers reach a managed-Tor node | ||
| 1632 | // through its local hidden-service listener, so every one of | ||
| 1633 | // them accepts as 127.0.0.1; capping loopback would cap all | ||
| 1634 | // inbound onion peers as a single group, which is the opposite | ||
| 1635 | // of where the network is headed. The global MAX_CONNECTIONS | ||
| 1636 | // still bounds the total, and the direct-IP flood this defends | ||
| 1637 | // against never arrives over loopback. (Per-onion-identity | ||
| 1638 | // limiting, once a peer's identity is known past accept(), is | ||
| 1639 | // the right tool for onion and is left for later.) | ||
| 1640 | bool fLoopback = (addr.GetByte(3) == 127); | ||
| 1630 | unsigned int nFromThisIP = 0; | 1641 | unsigned int nFromThisIP = 0; |
| 1631 | CRITICAL_BLOCK(cs_vNodes) | 1642 | if (!fLoopback) |
| 1632 | for (vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) | 1643 | CRITICAL_BLOCK(cs_vNodes) |
| 1633 | if ((*it)->addr.ip == addr.ip) | 1644 | for (vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) |
| 1634 | nFromThisIP++; | 1645 | if ((*it)->addr.ip == addr.ip) |
| 1646 | nFromThisIP++; | ||
| 1635 | 1647 | ||
| 1636 | if (nNodes >= MAX_CONNECTIONS) | 1648 | if (nNodes >= MAX_CONNECTIONS) |
| 1637 | { | 1649 | { |
| @@ -1639,7 +1651,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1639 | addr.ToString().c_str(), nNodes); | 1651 | addr.ToString().c_str(), nNodes); |
| 1640 | BtfCloseSocket(hSocket); | 1652 | BtfCloseSocket(hSocket); |
| 1641 | } | 1653 | } |
| 1642 | else if (nFromThisIP >= MAX_CONNECTIONS_PER_IP) | 1654 | else if (!fLoopback && nFromThisIP >= MAX_CONNECTIONS_PER_IP) |
| 1643 | { | 1655 | { |
| 1644 | LogPrint("net", "refusing connection from %s, already %u from that address\n", | 1656 | LogPrint("net", "refusing connection from %s, already %u from that address\n", |
| 1645 | addr.ToString().c_str(), nFromThisIP); | 1657 | addr.ToString().c_str(), nFromThisIP); |
| @@ -119,6 +119,14 @@ static const unsigned int MAX_CONNECTIONS = 125; | |||
| 119 | // needs only a handful; 32 leaves generous room while denying any one address | 119 | // needs only a handful; 32 leaves generous room while denying any one address |
| 120 | // more than a quarter of the table. The firewall connlimit on the relays does | 120 | // more than a quarter of the table. The firewall connlimit on the relays does |
| 121 | // the same thing a layer lower; this makes every build carry the defense. | 121 | // the same thing a layer lower; this makes every build carry the defense. |
| 122 | // | ||
| 123 | // Loopback (127.0.0.0/8) is exempt in the accept path. A managed-Tor node | ||
| 124 | // receives every inbound onion peer over its local hidden-service listener, so | ||
| 125 | // they all appear as 127.0.0.1 -- capping loopback would cap onion peers as a | ||
| 126 | // group, which is exactly wrong for a network moving toward onion-only. The | ||
| 127 | // global MAX_CONNECTIONS still bounds the total; a per-onion-identity limit, | ||
| 128 | // applied once identity is known after accept(), is the right future tool for | ||
| 129 | // onion floods. | ||
| 122 | static const unsigned int MAX_CONNECTIONS_PER_IP = 32; | 130 | static const unsigned int MAX_CONNECTIONS_PER_IP = 32; |
| 123 | 131 | ||
| 124 | // A node marked for disconnect is normally held until its buffers drain, so | 132 | // A node marked for disconnect is normally held until its buffers drain, so |