Skip to content

Commit 8387f83

Browse files
committed
Merge #20187: Addrman: test-before-evict bugfix and improvements for block-relay-only peers
16d9bfc Avoid test-before-evict evictions of current peers (Suhas Daftuar) e8b215a Refactor test for existing peer connection into own function (Suhas Daftuar) 4fe338a Call CAddrMan::Good() on block-relay-only peer addresses (Suhas Daftuar) daf5553 Avoid calling CAddrMan::Connected() on block-relay-only peer addresses (Suhas Daftuar) Pull request description: This PR does two things: * Block-relay-only interaction with addrman. * Calling `CAddrMan::Connected()` on an address that was a block-relay-only peer causes the time we report in `addr` messages containing that peer to be updated; particularly now that we use anchor connections with a our block-relay-only peers, this risks leaking information about those peers. So, stop this. * Avoiding calling `CAddrMan::Good()` on block-relay-only peer addresses causes the addrman logic around maintaining the new and tried table to be less good, and in particular makes it so that block-relay-only peer addresses are more likely to be evicted from the addrman (for no good reason I can think of). So, mark those addresses as good when we connect. * Fix test-before-evict bug. There's a bug where if we get a collision in the tried table with an existing address that is one of our current peers, and the connection is long-lived enough, then `SelectTriedCollisions()` might return that existing peer address to us as a test-before-evict connection candidate. However, our logic for new outbound connections would later prevent us from actually making a connection; the result would be that when we get a collision with a long-lived current peer, that peer's address is likely to get evicted from the tried table. Fix this by checking to see if a test-before-evict candidate is a peer we're currently connected to, and if so, mark it as `Good()`. ACKs for top commit: sipa: utACK 16d9bfc amitiuttarwar: code review ACK 16d9bfc mzumsande: Code-Review ACK 16d9bfc. jnewbery: utACK 16d9bfc ariard: Code Review ACK 16d9bfc. jonatack: Tested ACK 16d9bfc Tree-SHA512: 188ccb814e436937cbb91d29d73c316ce83f4b9c22f1cda56747f0949a093e10161ae724e87e4a2d85ac40f85f5f6b4e87e97d350a1ac44f80c57783f4423324
2 parents ca18860 + 16d9bfc commit 8387f83

File tree

5 files changed

+65
-25
lines changed

5 files changed

+65
-25
lines changed

src/net.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,11 @@ CNode* CConnman::FindNode(const CService& addr)
354354
return nullptr;
355355
}
356356

357+
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
358+
{
359+
return FindNode(static_cast<CNetAddr>(addr)) || FindNode(addr.ToStringIPPort());
360+
}
361+
357362
bool CConnman::CheckIncomingNonce(uint64_t nonce)
358363
{
359364
LOCK(cs_vNodes);
@@ -1994,11 +1999,30 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
19941999
if (nTries > 100)
19952000
break;
19962001

1997-
CAddrInfo addr = addrman.SelectTriedCollision();
2002+
CAddrInfo addr;
19982003

1999-
// SelectTriedCollision returns an invalid address if it is empty.
2000-
if (!fFeeler || !addr.IsValid()) {
2001-
addr = addrman.Select(fFeeler);
2004+
if (fFeeler) {
2005+
// First, try to get a tried table collision address. This returns
2006+
// an empty (invalid) address if there are no collisions to try.
2007+
addr = addrman.SelectTriedCollision();
2008+
2009+
if (!addr.IsValid()) {
2010+
// No tried table collisions. Select a new table address
2011+
// for our feeler.
2012+
addr = addrman.Select(true);
2013+
} else if (AlreadyConnectedToAddress(addr)) {
2014+
// If test-before-evict logic would have us connect to a
2015+
// peer that we're already connected to, just mark that
2016+
// address as Good(). We won't be able to initiate the
2017+
// connection anyway, so this avoids inadvertently evicting
2018+
// a currently-connected peer.
2019+
addrman.Good(addr);
2020+
// Select a new table address for our feeler instead.
2021+
addr = addrman.Select(true);
2022+
}
2023+
} else {
2024+
// Not a feeler
2025+
addr = addrman.Select();
20022026
}
20032027

20042028
// Require outbound connections, other than feelers, to be to distinct network groups
@@ -2160,7 +2184,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
21602184
}
21612185
if (!pszDest) {
21622186
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
2163-
if (IsLocal(addrConnect) || FindNode(static_cast<CNetAddr>(addrConnect)) || banned_or_discouraged || FindNode(addrConnect.ToStringIPPort())) {
2187+
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
21642188
return;
21652189
}
21662190
} else if (FindNode(std::string(pszDest)))
@@ -2624,7 +2648,7 @@ void CConnman::DeleteNode(CNode* pnode)
26242648
{
26252649
assert(pnode);
26262650
bool fUpdateConnectionTime = false;
2627-
m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
2651+
m_msgproc->FinalizeNode(*pnode, fUpdateConnectionTime);
26282652
if (fUpdateConnectionTime) {
26292653
addrman.Connected(pnode->addr);
26302654
}

src/net.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ class CConnman
442442
CNode* FindNode(const std::string& addrName);
443443
CNode* FindNode(const CService& addr);
444444

445+
/**
446+
* Determine whether we're already connected to a given address, in order to
447+
* avoid initiating duplicate connections.
448+
*/
449+
bool AlreadyConnectedToAddress(const CAddress& addr);
450+
445451
bool AttemptToEvictConnection();
446452
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type);
447453
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
@@ -618,7 +624,7 @@ class NetEventsInterface
618624
virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
619625
virtual bool SendMessages(CNode* pnode) = 0;
620626
virtual void InitializeNode(CNode* pnode) = 0;
621-
virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
627+
virtual void FinalizeNode(const CNode& node, bool& update_connection_time) = 0;
622628

623629
protected:
624630
/**

src/net_processing.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,8 @@ void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const
837837
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
838838
}
839839

840-
void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
840+
void PeerManager::FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) {
841+
NodeId nodeid = node.GetId();
841842
fUpdateConnectionTime = false;
842843
LOCK(cs_main);
843844
int misbehavior{0};
@@ -854,7 +855,8 @@ void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
854855
if (state->fSyncStarted)
855856
nSyncStarted--;
856857

857-
if (misbehavior == 0 && state->fCurrentlyConnected) {
858+
if (misbehavior == 0 && state->fCurrentlyConnected && !node.IsBlockOnlyConn()) {
859+
// Note: we avoid changing visible addrman state for block-relay-only peers
858860
fUpdateConnectionTime = true;
859861
}
860862

@@ -2405,14 +2407,8 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
24052407
// empty and no one will know who we are, so these mechanisms are
24062408
// important to help us connect to the network.
24072409
//
2408-
// We also update the addrman to record connection success for
2409-
// these peers (which include OUTBOUND_FULL_RELAY and FEELER
2410-
// connections) so that addrman will have an up-to-date notion of
2411-
// which peers are online and available.
2412-
//
2413-
// We skip these operations for BLOCK_RELAY peers to avoid
2414-
// potentially leaking information about our BLOCK_RELAY
2415-
// connections via the addrman or address relay.
2410+
// We skip this for BLOCK_RELAY peers to avoid potentially leaking
2411+
// information about our BLOCK_RELAY connections via address relay.
24162412
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
24172413
{
24182414
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@@ -2431,9 +2427,23 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
24312427
// Get recent addresses
24322428
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
24332429
pfrom.fGetAddr = true;
2430+
}
24342431

2435-
// Moves address from New to Tried table in Addrman, resolves
2436-
// tried-table collisions, etc.
2432+
if (!pfrom.IsInboundConn()) {
2433+
// For non-inbound connections, we update the addrman to record
2434+
// connection success so that addrman will have an up-to-date
2435+
// notion of which peers are online and available.
2436+
//
2437+
// While we strive to not leak information about block-relay-only
2438+
// connections via the addrman, not moving an address to the tried
2439+
// table is also potentially detrimental because new-table entries
2440+
// are subject to eviction in the event of addrman collisions. We
2441+
// mitigate the information-leak by never calling
2442+
// CAddrMan::Connected() on block-relay-only peers; see
2443+
// FinalizeNode().
2444+
//
2445+
// This moves an address from New to Tried table in Addrman,
2446+
// resolves tried-table collisions, etc.
24372447
m_connman.MarkAddressGood(pfrom.addr);
24382448
}
24392449

src/net_processing.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class PeerManager final : public CValidationInterface, public NetEventsInterface
5858
/** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */
5959
void InitializeNode(CNode* pnode) override;
6060
/** Handle removal of a peer by updating various state and removing it from mapNodeState */
61-
void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override;
61+
void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override;
6262
/**
6363
* Process protocol messages received from a given node
6464
*

src/test/denialofservice_tests.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
129129
SetMockTime(0);
130130

131131
bool dummy;
132-
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
132+
peerLogic->FinalizeNode(dummyNode1, dummy);
133133
}
134134

135135
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
211211

212212
bool dummy;
213213
for (const CNode *node : vNodes) {
214-
peerLogic->FinalizeNode(node->GetId(), dummy);
214+
peerLogic->FinalizeNode(*node, dummy);
215215
}
216216

217217
connman->ClearNodes();
@@ -259,8 +259,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
259259
BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
260260

261261
bool dummy;
262-
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
263-
peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
262+
peerLogic->FinalizeNode(dummyNode1, dummy);
263+
peerLogic->FinalizeNode(dummyNode2, dummy);
264264
}
265265

266266
BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
288288
BOOST_CHECK(banman->IsDiscouraged(addr));
289289

290290
bool dummy;
291-
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
291+
peerLogic->FinalizeNode(dummyNode, dummy);
292292
}
293293

294294
static CTransactionRef RandomOrphan()

0 commit comments

Comments
 (0)