Skip to content

Commit b906b64

Browse files
committed
i2p: reuse created I2P sessions if not used
In the case of `i2pacceptincoming=0` we use transient addresses (destinations) for ourselves for each outbound connection. It may happen that we * create the session (and thus our address/destination too) * fail to connect to the particular peer (e.g. if they are offline) * dispose the unused session. This puts unnecessary load on the I2P network because session creation is not cheap. Is exaggerated if `onlynet=i2p` is used in which case we will be trying to connect to I2P peers more often. To help with this, save the created but unused sessions and pick them later instead of creating new ones. Alleviates: bitcoin/bitcoin#26754
1 parent 296e882 commit b906b64

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

src/net.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ static CAddress GetBindAddress(const Sock& sock)
436436

437437
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
438438
{
439+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
439440
assert(conn_type != ConnectionType::INBOUND);
440441

441442
if (pszDest == nullptr) {
@@ -496,8 +497,23 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
496497
if (m_i2p_sam_session) {
497498
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
498499
} else {
499-
i2p_transient_session = std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
500+
{
501+
LOCK(m_unused_i2p_sessions_mutex);
502+
if (m_unused_i2p_sessions.empty()) {
503+
i2p_transient_session =
504+
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
505+
} else {
506+
i2p_transient_session.swap(m_unused_i2p_sessions.front());
507+
m_unused_i2p_sessions.pop();
508+
}
509+
}
500510
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
511+
if (!connected) {
512+
LOCK(m_unused_i2p_sessions_mutex);
513+
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
514+
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
515+
}
516+
}
501517
}
502518

503519
if (connected) {
@@ -1047,6 +1063,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
10471063

10481064
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
10491065
{
1066+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
10501067
std::optional<int> max_connections;
10511068
switch (conn_type) {
10521069
case ConnectionType::INBOUND:
@@ -1509,6 +1526,7 @@ void CConnman::DumpAddresses()
15091526

15101527
void CConnman::ProcessAddrFetch()
15111528
{
1529+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
15121530
std::string strDest;
15131531
{
15141532
LOCK(m_addr_fetches_mutex);
@@ -1577,6 +1595,7 @@ int CConnman::GetExtraBlockRelayCount() const
15771595

15781596
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
15791597
{
1598+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
15801599
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
15811600
FastRandomContext rng;
15821601
// Connect to specific addresses
@@ -1928,6 +1947,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
19281947

19291948
void CConnman::ThreadOpenAddedConnections()
19301949
{
1950+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
19311951
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
19321952
while (true)
19331953
{
@@ -1957,6 +1977,7 @@ void CConnman::ThreadOpenAddedConnections()
19571977
// if successful, this moves the passed grant to the constructed node
19581978
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
19591979
{
1980+
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
19601981
assert(conn_type != ConnectionType::INBOUND);
19611982

19621983
//

src/net.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <map>
3939
#include <memory>
4040
#include <optional>
41+
#include <queue>
4142
#include <thread>
4243
#include <vector>
4344

@@ -743,7 +744,7 @@ class CConnman
743744
bool GetNetworkActive() const { return fNetworkActive; };
744745
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
745746
void SetNetworkActive(bool active);
746-
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
747+
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
747748
bool CheckIncomingNonce(uint64_t nonce);
748749

749750
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -819,7 +820,7 @@ class CConnman
819820
* - Max total outbound connection capacity filled
820821
* - Max connection capacity for type is filled
821822
*/
822-
bool AddConnection(const std::string& address, ConnectionType conn_type);
823+
bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
823824

824825
size_t GetNodeCount(ConnectionDirection) const;
825826
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
@@ -885,10 +886,10 @@ class CConnman
885886
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
886887
bool InitBinds(const Options& options);
887888

888-
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
889+
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_unused_i2p_sessions_mutex);
889890
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
890-
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
891-
void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex);
891+
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_unused_i2p_sessions_mutex);
892+
void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex, !m_unused_i2p_sessions_mutex);
892893
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
893894
void ThreadI2PAcceptIncoming();
894895
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -955,7 +956,7 @@ class CConnman
955956
bool AlreadyConnectedToAddress(const CAddress& addr);
956957

957958
bool AttemptToEvictConnection();
958-
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
959+
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
959960
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
960961

961962
void DeleteNode(CNode* pnode);
@@ -1126,6 +1127,26 @@ class CConnman
11261127
*/
11271128
std::vector<CService> m_onion_binds;
11281129

1130+
/**
1131+
* Mutex protecting m_i2p_sam_sessions.
1132+
*/
1133+
Mutex m_unused_i2p_sessions_mutex;
1134+
1135+
/**
1136+
* A pool of created I2P SAM transient sessions that should be used instead
1137+
* of creating new ones in order to reduce the load on the I2P network.
1138+
* Creating a session in I2P is not cheap, thus if this is not empty, then
1139+
* pick an entry from it instead of creating a new session. If connecting to
1140+
* a host fails, then the created session is put to this pool for reuse.
1141+
*/
1142+
std::queue<std::unique_ptr<i2p::sam::Session>> m_unused_i2p_sessions GUARDED_BY(m_unused_i2p_sessions_mutex);
1143+
1144+
/**
1145+
* Cap on the size of `m_unused_i2p_sessions`, to ensure it does not
1146+
* unexpectedly use too much memory.
1147+
*/
1148+
static constexpr size_t MAX_UNUSED_I2P_SESSIONS_SIZE{10};
1149+
11291150
/**
11301151
* RAII helper to atomically create a copy of `m_nodes` and add a reference
11311152
* to each of the nodes. The nodes are released when this object is destroyed.

0 commit comments

Comments
 (0)