mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01:00
Merge #20187: Addrman: test-before-evict bugfix and improvements for block-relay-only peers
16d9bfc417
Avoid test-before-evict evictions of current peers (Suhas Daftuar)e8b215a086
Refactor test for existing peer connection into own function (Suhas Daftuar)4fe338ab3e
Call CAddrMan::Good() on block-relay-only peer addresses (Suhas Daftuar)daf5553126
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: utACK16d9bfc417
amitiuttarwar: code review ACK16d9bfc417
mzumsande: Code-Review ACK16d9bfc417
. jnewbery: utACK16d9bfc417
ariard: Code Review ACK16d9bfc
. jonatack: Tested ACK16d9bfc417
Tree-SHA512: 188ccb814e436937cbb91d29d73c316ce83f4b9c22f1cda56747f0949a093e10161ae724e87e4a2d85ac40f85f5f6b4e87e97d350a1ac44f80c57783f4423324
This commit is contained in:
commit
8387f832d6
5 changed files with 65 additions and 25 deletions
36
src/net.cpp
36
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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue