diff options
author | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-09-03 17:12:53 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-09-03 17:20:47 +0200 |
commit | 69a13eb2467ae04b001c927fc359d12192199350 (patch) | |
tree | 66c3e361960fc53ec2aaec5e32092f5fbb5776b9 /src | |
parent | 4053de04e23962f793303b1e68cd89d82fe14335 (diff) | |
parent | 752e6ad5336d5af0db9fe16d24c0c6aa25b74a3f (diff) |
Merge #19670: Protect localhost and block-relay-only peers from eviction
752e6ad5336d5af0db9fe16d24c0c6aa25b74a3f Protect localhost and block-relay-only peers from eviction (Suhas Daftuar)
Pull request description:
Onion peers are disadvantaged under our eviction criteria, so prevent eventual
eviction of them in the presence of contention for inbound slots by reserving
some slots for localhost peers (sorted by longest uptime).
Block-relay-only connections exist as a protection against eclipse attacks, by
creating a path for block propagation that may be unknown to adversaries.
Protect against inbound peer connection slot attacks from disconnecting such
peers by attempting to protect up to 8 peers that are not relaying transactions
but have provided us with blocks.
Thanks to gmaxwell for suggesting these strategies.
ACKs for top commit:
laanwj:
Code review ACK 752e6ad5336d5af0db9fe16d24c0c6aa25b74a3f
Tree-SHA512: dbf089c77c1f747aa1dbbbc2e9c2799c628028b0918d0c336d8d0e5338acedd573b530eb3b689c7f603a17221e557268a9f5c3f585f204bfb12e5d2e76de39a3
Diffstat (limited to 'src')
-rw-r--r-- | src/net.cpp | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/src/net.cpp b/src/net.cpp index f483f9922f..e35d05cec0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -816,6 +816,7 @@ struct NodeEvictionCandidate CAddress addr; uint64_t nKeyedNetGroup; bool prefer_evict; + bool m_is_local; }; static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) @@ -828,6 +829,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons return a.nTimeConnected > b.nTimeConnected; } +static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) +{ + if (a.m_is_local != b.m_is_local) return b.m_is_local; + return a.nTimeConnected > b.nTimeConnected; +} + static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nKeyedNetGroup < b.nKeyedNetGroup; } @@ -849,6 +856,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction return a.nTimeConnected > b.nTimeConnected; } +// Pick out the potential block-relay only peers, and sort them by last block time. +static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) +{ + if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes; + if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime; + if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices; + return a.nTimeConnected > b.nTimeConnected; +} //! Sort an array by the specified comparator, then erase the last K elements. template<typename T, typename Comparator> @@ -891,7 +906,7 @@ bool CConnman::AttemptToEvictConnection() node->nLastBlockTime, node->nLastTXTime, HasAllDesirableServiceFlags(node->nServices), peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup, - node->m_prefer_evict}; + node->m_prefer_evict, node->addr.IsLocal()}; vEvictionCandidates.push_back(candidate); } } @@ -907,12 +922,31 @@ bool CConnman::AttemptToEvictConnection() // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); + // Protect up to 8 non-tx-relay peers that have sent us novel blocks. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime); + size_t erase_size = std::min(size_t(8), vEvictionCandidates.size()); + vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end()); + // Protect 4 nodes that most recently sent us novel blocks. // An attacker cannot manipulate this metric without performing useful work. EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); + // Protect the half of the remaining nodes which have been connected the longest. // This replicates the non-eviction implicit behavior, and precludes attacks that start later. - EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2); + // Reserve half of these protected spots for localhost peers, even if + // they're not longest-uptime overall. This helps protect tor peers, which + // tend to be otherwise disadvantaged under our eviction criteria. + size_t initial_size = vEvictionCandidates.size(); + size_t total_protect_size = initial_size / 2; + + // Pick out up to 1/4 peers that are localhost, sorted by longest uptime. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected); + size_t local_erase_size = total_protect_size / 2; + vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end()); + // Calculate how many we removed, and update our total number of peers that + // we want to protect based on uptime accordingly. + total_protect_size -= initial_size - vEvictionCandidates.size(); + EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size); if (vEvictionCandidates.empty()) return false; |