From c16ed372bdc71795fbebc5dbf519a1d748a136dc Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Tue, 20 Oct 2020 08:28:56 +0200 Subject: [PATCH] p2p: protect 1/4 inbound onion peers in SelectNodeToEvict() --- src/net.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 6e9e67906db357..ddf33c38ae5c66 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -860,6 +860,12 @@ static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const return a.nTimeConnected > b.nTimeConnected; } +static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) +{ + if (a.m_is_onion != b.m_is_onion) return b.m_is_onion; + return a.nTimeConnected > b.nTimeConnected; +} + static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nKeyedNetGroup < b.nKeyedNetGroup; } @@ -923,16 +929,25 @@ static void EraseLastKElements(std::vector &elements, Comparator comparator, // 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. - // Reserve half of these protected spots for localhost peers, even if + // Reserve half of these protected spots for onion and 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(); + const 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()); + // Pick out up to 1/4 peers that are connected via our tor onion service, sorted by longest uptime. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareOnionTimeConnected); + const 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_onion; }), vEvictionCandidates.end()); + + // If no onion peers were removed, extend the same protection to localhost peers, as manually + // configured hidden services not using -bind will not be detected as inbound onion connections. + if (initial_size == vEvictionCandidates.size()) { + // Pick out up to 1/4 peers that are localhost, sorted by longest uptime. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected); + 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();