aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasil Dimov <vd@FreeBSD.org>2023-01-06 11:23:46 +0100
committerfanquake <fanquake@gmail.com>2023-02-27 13:59:51 +0000
commit5027e93b6a1e5a5c87b9450c60e2a4572eb32653 (patch)
tree7f3556a8716e690a1a58b822f752dbf54467b668
parenta62c541ae8a579d19b8fc55bbb922f0939ab6110 (diff)
downloadbitcoin-5027e93b6a1e5a5c87b9450c60e2a4572eb32653.tar.xz
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 Github-Pull: #26837 Rebased-From: b906b64eb76643feaede1da5987a0c4d466c581b
-rw-r--r--src/net.cpp23
-rw-r--r--src/net.h33
2 files changed, 49 insertions, 7 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 0cc14b1d2a..a6bf2a9dcc 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -436,6 +436,7 @@ static CAddress GetBindAddress(const Sock& sock)
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
if (pszDest == nullptr) {
@@ -496,8 +497,23 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (m_i2p_sam_session) {
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
} 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);
+ 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) {
@@ -1048,6 +1064,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::optional<int> max_connections;
switch (conn_type) {
case ConnectionType::INBOUND:
@@ -1510,6 +1527,7 @@ void CConnman::DumpAddresses()
void CConnman::ProcessAddrFetch()
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::string strDest;
{
LOCK(m_addr_fetches_mutex);
@@ -1578,6 +1596,7 @@ int CConnman::GetExtraBlockRelayCount() const
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
FastRandomContext rng;
// Connect to specific addresses
@@ -1929,6 +1948,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
void CConnman::ThreadOpenAddedConnections()
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
while (true)
{
@@ -1958,6 +1978,7 @@ void CConnman::ThreadOpenAddedConnections()
// 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)
{
+ AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
//
diff --git a/src/net.h b/src/net.h
index 044701a339..73856c11ee 100644
--- a/src/net.h
+++ b/src/net.h
@@ -38,6 +38,7 @@
#include <map>
#include <memory>
#include <optional>
+#include <queue>
#include <thread>
#include <vector>
@@ -744,7 +745,7 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
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 ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -820,7 +821,7 @@ public:
* - Max total outbound connection capacity 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;
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
@@ -886,10 +887,10 @@ private:
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
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 ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
- void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_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, !m_unused_i2p_sessions_mutex);
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
void ThreadI2PAcceptIncoming();
void AcceptConnection(const ListenSocket& hListenSocket);
@@ -956,7 +957,7 @@ private:
bool AlreadyConnectedToAddress(const CAddress& addr);
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 DeleteNode(CNode* pnode);
@@ -1128,6 +1129,26 @@ private:
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
* to each of the nodes. The nodes are released when this object is destroyed.
*/