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: https://github.com/bitcoin/bitcoin/issues/26754
This commit is contained in:
2 files changed
+49
-7
No files matched your search
+22
-1
@@ -436,6 +436,7 @@ static CAddress GetBindAddress(const Sock& sock)
|
|||||||
|
|
||||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
|
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
assert(conn_type != ConnectionType::INBOUND);
|
assert(conn_type != ConnectionType::INBOUND);
|
||||||
|
|
||||||
if (pszDest == nullptr) {
|
if (pszDest == nullptr) {
|
||||||
@@ -496,8 +497,23 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||||||
if (m_i2p_sam_session) {
|
if (m_i2p_sam_session) {
|
||||||
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
|
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
|
||||||
} else {
|
} else {
|
||||||
i2p_transient_session = std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
|
{
|
||||||
|
LOCK(m_unused_i2p_sessions_mutex);
|
||||||
|
if (m_unused_i2p_sessions.empty()) {
|
||||||
|
i2p_transient_session =
|
||||||
|
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
|
||||||
|
} else {
|
||||||
|
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
||||||
|
m_unused_i2p_sessions.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
|
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
|
||||||
|
if (!connected) {
|
||||||
|
LOCK(m_unused_i2p_sessions_mutex);
|
||||||
|
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
|
||||||
|
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
@@ -1047,6 +1063,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||||||
|
|
||||||
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
|
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
std::optional<int> max_connections;
|
std::optional<int> max_connections;
|
||||||
switch (conn_type) {
|
switch (conn_type) {
|
||||||
case ConnectionType::INBOUND:
|
case ConnectionType::INBOUND:
|
||||||
@@ -1509,6 +1526,7 @@ void CConnman::DumpAddresses()
|
|||||||
|
|
||||||
void CConnman::ProcessAddrFetch()
|
void CConnman::ProcessAddrFetch()
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
std::string strDest;
|
std::string strDest;
|
||||||
{
|
{
|
||||||
LOCK(m_addr_fetches_mutex);
|
LOCK(m_addr_fetches_mutex);
|
||||||
@@ -1577,6 +1595,7 @@ int CConnman::GetExtraBlockRelayCount() const
|
|||||||
|
|
||||||
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
|
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
|
||||||
FastRandomContext rng;
|
FastRandomContext rng;
|
||||||
// Connect to specific addresses
|
// Connect to specific addresses
|
||||||
@@ -1928,6 +1947,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
|
|||||||
|
|
||||||
void CConnman::ThreadOpenAddedConnections()
|
void CConnman::ThreadOpenAddedConnections()
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
|
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -1957,6 +1977,7 @@ void CConnman::ThreadOpenAddedConnections()
|
|||||||
// if successful, this moves the passed grant to the constructed node
|
// if successful, this moves the passed grant to the constructed node
|
||||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
|
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, ConnectionType conn_type)
|
||||||
{
|
{
|
||||||
|
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||||
assert(conn_type != ConnectionType::INBOUND);
|
assert(conn_type != ConnectionType::INBOUND);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <queue>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -743,7 +744,7 @@ public:
|
|||||||
bool GetNetworkActive() const { return fNetworkActive; };
|
bool GetNetworkActive() const { return fNetworkActive; };
|
||||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||||
void SetNetworkActive(bool active);
|
void SetNetworkActive(bool active);
|
||||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
|
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||||
bool CheckIncomingNonce(uint64_t nonce);
|
bool CheckIncomingNonce(uint64_t nonce);
|
||||||
|
|
||||||
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
|
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
|
||||||
@@ -819,7 +820,7 @@ public:
|
|||||||
* - Max total outbound connection capacity filled
|
* - Max total outbound connection capacity filled
|
||||||
* - Max connection capacity for type is filled
|
* - Max connection capacity for type is filled
|
||||||
*/
|
*/
|
||||||
bool AddConnection(const std::string& address, ConnectionType conn_type);
|
bool AddConnection(const std::string& address, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||||
|
|
||||||
size_t GetNodeCount(ConnectionDirection) const;
|
size_t GetNodeCount(ConnectionDirection) const;
|
||||||
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
|
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
|
||||||
@@ -885,10 +886,10 @@ private:
|
|||||||
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
|
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
|
||||||
bool InitBinds(const Options& options);
|
bool InitBinds(const Options& options);
|
||||||
|
|
||||||
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_unused_i2p_sessions_mutex);
|
||||||
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
|
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
|
||||||
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
|
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_unused_i2p_sessions_mutex);
|
||||||
void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex);
|
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);
|
||||||
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
|
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
|
||||||
void ThreadI2PAcceptIncoming();
|
void ThreadI2PAcceptIncoming();
|
||||||
void AcceptConnection(const ListenSocket& hListenSocket);
|
void AcceptConnection(const ListenSocket& hListenSocket);
|
||||||
@@ -955,7 +956,7 @@ private:
|
|||||||
bool AlreadyConnectedToAddress(const CAddress& addr);
|
bool AlreadyConnectedToAddress(const CAddress& addr);
|
||||||
|
|
||||||
bool AttemptToEvictConnection();
|
bool AttemptToEvictConnection();
|
||||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
|
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
|
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
|
||||||
|
|
||||||
void DeleteNode(CNode* pnode);
|
void DeleteNode(CNode* pnode);
|
||||||
@@ -1126,6 +1127,26 @@ private:
|
|||||||
*/
|
*/
|
||||||
std::vector<CService> m_onion_binds;
|
std::vector<CService> m_onion_binds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutex protecting m_i2p_sam_sessions.
|
||||||
|
*/
|
||||||
|
Mutex m_unused_i2p_sessions_mutex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pool of created I2P SAM transient sessions that should be used instead
|
||||||
|
* of creating new ones in order to reduce the load on the I2P network.
|
||||||
|
* Creating a session in I2P is not cheap, thus if this is not empty, then
|
||||||
|
* pick an entry from it instead of creating a new session. If connecting to
|
||||||
|
* a host fails, then the created session is put to this pool for reuse.
|
||||||
|
*/
|
||||||
|
std::queue<std::unique_ptr<i2p::sam::Session>> m_unused_i2p_sessions GUARDED_BY(m_unused_i2p_sessions_mutex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cap on the size of `m_unused_i2p_sessions`, to ensure it does not
|
||||||
|
* unexpectedly use too much memory.
|
||||||
|
*/
|
||||||
|
static constexpr size_t MAX_UNUSED_I2P_SESSIONS_SIZE{10};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RAII helper to atomically create a copy of `m_nodes` and add a reference
|
* RAII helper to atomically create a copy of `m_nodes` and add a reference
|
||||||
* to each of the nodes. The nodes are released when this object is destroyed.
|
* to each of the nodes. The nodes are released when this object is destroyed.
|
||||||
|
|||||||
Reference in new issue
Block a user