Skip to content

Commit

Permalink
Merge #20187: Addrman: test-before-evict bugfix and improvements for …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
fanquake committed Nov 3, 2020
2 parents ca18860 + 16d9bfc commit 8387f83
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 25 deletions.
36 changes: 30 additions & 6 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 7 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ class CConnman
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;
Expand Down Expand Up @@ -618,7 +624,7 @@ class NetEventsInterface
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:
/**
Expand Down
34 changes: 22 additions & 12 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
}

Expand Down Expand Up @@ -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());
Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/net_processing.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class PeerManager final : public CValidationInterface, public NetEventsInterface
/** 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
*
Expand Down
10 changes: 5 additions & 5 deletions src/test/denialofservice_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 8387f83

Please sign in to comment.