aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/net.cpp36
-rw-r--r--src/net.h8
-rw-r--r--src/net_processing.cpp34
-rw-r--r--src/net_processing.h2
-rw-r--r--src/test/denialofservice_tests.cpp10
5 files changed, 65 insertions, 25 deletions
diff --git a/src/net.cpp b/src/net.cpp
index e8a27c3530..468358a94d 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -354,6 +354,11 @@ CNode* CConnman::FindNode(const CService& addr)
return nullptr;
}
+bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
+{
+ return FindNode(static_cast<CNetAddr>(addr)) || FindNode(addr.ToStringIPPort());
+}
+
bool CConnman::CheckIncomingNonce(uint64_t nonce)
{
LOCK(cs_vNodes);
@@ -1994,11 +1999,30 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (nTries > 100)
break;
- CAddrInfo addr = addrman.SelectTriedCollision();
+ CAddrInfo addr;
- // SelectTriedCollision returns an invalid address if it is empty.
- if (!fFeeler || !addr.IsValid()) {
- addr = addrman.Select(fFeeler);
+ if (fFeeler) {
+ // First, try to get a tried table collision address. This returns
+ // an empty (invalid) address if there are no collisions to try.
+ addr = addrman.SelectTriedCollision();
+
+ if (!addr.IsValid()) {
+ // No tried table collisions. Select a new table address
+ // for our feeler.
+ addr = addrman.Select(true);
+ } else if (AlreadyConnectedToAddress(addr)) {
+ // If test-before-evict logic would have us connect to a
+ // peer that we're already connected to, just mark that
+ // address as Good(). We won't be able to initiate the
+ // connection anyway, so this avoids inadvertently evicting
+ // a currently-connected peer.
+ addrman.Good(addr);
+ // Select a new table address for our feeler instead.
+ addr = addrman.Select(true);
+ }
+ } else {
+ // Not a feeler
+ addr = addrman.Select();
}
// Require outbound connections, other than feelers, to be to distinct network groups
@@ -2160,7 +2184,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
}
if (!pszDest) {
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
- if (IsLocal(addrConnect) || FindNode(static_cast<CNetAddr>(addrConnect)) || banned_or_discouraged || FindNode(addrConnect.ToStringIPPort())) {
+ if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
return;
}
} else if (FindNode(std::string(pszDest)))
@@ -2624,7 +2648,7 @@ void CConnman::DeleteNode(CNode* pnode)
{
assert(pnode);
bool fUpdateConnectionTime = false;
- m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
+ m_msgproc->FinalizeNode(*pnode, fUpdateConnectionTime);
if (fUpdateConnectionTime) {
addrman.Connected(pnode->addr);
}
diff --git a/src/net.h b/src/net.h
index 2652d82ea0..77649247d9 100644
--- a/src/net.h
+++ b/src/net.h
@@ -442,6 +442,12 @@ private:
CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& addr);
+ /**
+ * Determine whether we're already connected to a given address, in order to
+ * avoid initiating duplicate connections.
+ */
+ bool AlreadyConnectedToAddress(const CAddress& addr);
+
bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
@@ -618,7 +624,7 @@ public:
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
virtual bool SendMessages(CNode* pnode) = 0;
virtual void InitializeNode(CNode* pnode) = 0;
- virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
+ virtual void FinalizeNode(const CNode& node, bool& update_connection_time) = 0;
protected:
/**
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 464e3de80a..9dafda43c8 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -837,7 +837,8 @@ void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
-void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) {
+ NodeId nodeid = node.GetId();
fUpdateConnectionTime = false;
LOCK(cs_main);
int misbehavior{0};
@@ -854,7 +855,8 @@ void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
if (state->fSyncStarted)
nSyncStarted--;
- if (misbehavior == 0 && state->fCurrentlyConnected) {
+ if (misbehavior == 0 && state->fCurrentlyConnected && !node.IsBlockOnlyConn()) {
+ // Note: we avoid changing visible addrman state for block-relay-only peers
fUpdateConnectionTime = true;
}
@@ -2405,14 +2407,8 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
// empty and no one will know who we are, so these mechanisms are
// important to help us connect to the network.
//
- // We also update the addrman to record connection success for
- // these peers (which include OUTBOUND_FULL_RELAY and FEELER
- // connections) so that addrman will have an up-to-date notion of
- // which peers are online and available.
- //
- // We skip these operations for BLOCK_RELAY peers to avoid
- // potentially leaking information about our BLOCK_RELAY
- // connections via the addrman or address relay.
+ // We skip this for BLOCK_RELAY peers to avoid potentially leaking
+ // information about our BLOCK_RELAY connections via address relay.
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@@ -2431,9 +2427,23 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
// Get recent addresses
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
pfrom.fGetAddr = true;
+ }
- // Moves address from New to Tried table in Addrman, resolves
- // tried-table collisions, etc.
+ if (!pfrom.IsInboundConn()) {
+ // For non-inbound connections, we update the addrman to record
+ // connection success so that addrman will have an up-to-date
+ // notion of which peers are online and available.
+ //
+ // While we strive to not leak information about block-relay-only
+ // connections via the addrman, not moving an address to the tried
+ // table is also potentially detrimental because new-table entries
+ // are subject to eviction in the event of addrman collisions. We
+ // mitigate the information-leak by never calling
+ // CAddrMan::Connected() on block-relay-only peers; see
+ // FinalizeNode().
+ //
+ // This moves an address from New to Tried table in Addrman,
+ // resolves tried-table collisions, etc.
m_connman.MarkAddressGood(pfrom.addr);
}
diff --git a/src/net_processing.h b/src/net_processing.h
index 578660355a..87eee566de 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -58,7 +58,7 @@ public:
/** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */
void InitializeNode(CNode* pnode) override;
/** Handle removal of a peer by updating various state and removing it from mapNodeState */
- void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
+ void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override;
/**
* Process protocol messages received from a given node
*
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 6743dc0070..c399da900f 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(0);
bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode1, dummy);
}
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
bool dummy;
for (const CNode *node : vNodes) {
- peerLogic->FinalizeNode(node->GetId(), dummy);
+ peerLogic->FinalizeNode(*node, dummy);
}
connman->ClearNodes();
@@ -259,8 +259,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
- peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode1, dummy);
+ peerLogic->FinalizeNode(dummyNode2, dummy);
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
BOOST_CHECK(banman->IsDiscouraged(addr));
bool dummy;
- peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode, dummy);
}
static CTransactionRef RandomOrphan()