Issue Details

Number
27843
Title
ThreadI2PAcceptIncoming temporarily bypasses 125 connection ceiling
Description
- The i2p accept code is in `ThreadI2PAcceptIncoming`, instead of in `ThreadSocketHandler` like the non-i2p accept code. - When accepting non-i2p connections, a connection is accepted in `AcceptConnection` and if there are more than `maxInbound` outstanding connections, one is marked for disconnect. The next iteration of the loop in `ThreadSocketHandler` will call `DisconnectNodes` and remove it. - Since the i2p code resides in its own thread, it's possible for i2p connections to stack up past 125 (I was able to hit 190 outstanding connections when running a local script to connect to my bitcoind i2p address). I think the higher numbered fd's could cause a slight slowdown in `select`. - How many connections it can go past 125 is a race between `DisconnectNodes` and `ThreadI2PAcceptIncoming`. <details> <summary>Local test</summary> <br> ``` diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp index b2e1ae43b..f1f0b2a60 100644 --- a/src/test/i2p_tests.cpp +++ b/src/test/i2p_tests.cpp @@ -10,14 +10,52 @@ #include <test/util/net.h> #include <test/util/setup_common.h> #include <util/threadinterrupt.h> +#include <netbase.h> +#include <protocol.h> + +#include <chrono> +#include <thread> #include <boost/test/unit_test.hpp> #include <memory> #include <string> +#include <stdio.h> BOOST_FIXTURE_TEST_SUITE(i2p_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(spam) +{ + CThreadInterrupt interrupt; + + in_addr ipv4Addr; + ipv4Addr.s_addr = inet_addr("127.0.0.1"); + + Proxy p = Proxy(CService(ipv4Addr, 7656), false); + + auto i2p_transient_session = std::make_unique<i2p::sam::Session>(p.proxy, &interrupt); + + const char* pszDest = "tfshhokzifn2g46dc6dxwrwwxwbeo4l5eeepequ52kpvrflm3foq.b32.i2p"; + + const std::vector<CService> resolved{Lookup(pszDest, 0, false, 256)}; + + CAddress addrConnect = CAddress(resolved[0], NODE_NONE); + + std::vector<std::unique_ptr<Sock>> v; + v.reserve(1000); + + for (int i = 0; i < 1000; i++) { + i2p::Connection conn; + bool proxy_error; + auto connected = i2p_transient_session->Connect(addrConnect, conn, proxy_error); + if (connected) { + v.push_back(std::move(conn.sock)); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50000)); +} + BOOST_AUTO_TEST_CASE(unlimited_recv) { const auto prev_log_level{LogInstance().LogLevel()}; ``` I then called it from a script: ``` #!/bin/bash # for i in {1..10} do ./src/test/test_bitcoin --log_level=all --run_test=i2p_tests/spam & done ``` </details> When testing this, I also ran into a reproducible error when simultaneously using `bitcoin-cli`: ``` error: timeout on transient error: Could not connect to the server 127.0.0.1:18443 (error code 1 - "EOF reached") Make sure the bitcoind server is running and that you are connecting to the correct RPC port. ``` Wireshark showed that a TCP port was reused which led to a RST being sent back to bitcoin-cli. It happened when the i2p sam bridge ran out of memory, but as far as I can tell the rpc server should be totally unaffected by that. This error message also appeared in bitcoind's logs at the same time: `2023-06-09T15:02:58Z connect() to 127.0.0.1:7656 failed: Can't assign requested address (49)`
URL
https://github.com/bitcoin/bitcoin/issue/27843
Closed by
#27912
Back to List