Skip to content

Commit 69a13eb

Browse files
committed
Merge #19670: Protect localhost and block-relay-only peers from eviction
752e6ad 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 752e6ad Tree-SHA512: dbf089c77c1f747aa1dbbbc2e9c2799c628028b0918d0c336d8d0e5338acedd573b530eb3b689c7f603a17221e557268a9f5c3f585f204bfb12e5d2e76de39a3
2 parents 4053de0 + 752e6ad commit 69a13eb

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

src/net.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ struct NodeEvictionCandidate
816816
CAddress addr;
817817
uint64_t nKeyedNetGroup;
818818
bool prefer_evict;
819+
bool m_is_local;
819820
};
820821

821822
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -828,6 +829,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
828829
return a.nTimeConnected > b.nTimeConnected;
829830
}
830831

832+
static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
833+
{
834+
if (a.m_is_local != b.m_is_local) return b.m_is_local;
835+
return a.nTimeConnected > b.nTimeConnected;
836+
}
837+
831838
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
832839
return a.nKeyedNetGroup < b.nKeyedNetGroup;
833840
}
@@ -849,6 +856,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
849856
return a.nTimeConnected > b.nTimeConnected;
850857
}
851858

859+
// Pick out the potential block-relay only peers, and sort them by last block time.
860+
static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
861+
{
862+
if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
863+
if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
864+
if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
865+
return a.nTimeConnected > b.nTimeConnected;
866+
}
852867

853868
//! Sort an array by the specified comparator, then erase the last K elements.
854869
template<typename T, typename Comparator>
@@ -891,7 +906,7 @@ bool CConnman::AttemptToEvictConnection()
891906
node->nLastBlockTime, node->nLastTXTime,
892907
HasAllDesirableServiceFlags(node->nServices),
893908
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
894-
node->m_prefer_evict};
909+
node->m_prefer_evict, node->addr.IsLocal()};
895910
vEvictionCandidates.push_back(candidate);
896911
}
897912
}
@@ -907,12 +922,31 @@ bool CConnman::AttemptToEvictConnection()
907922
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
908923
// An attacker cannot manipulate this metric without performing useful work.
909924
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
925+
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
926+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
927+
size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
928+
vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
929+
910930
// Protect 4 nodes that most recently sent us novel blocks.
911931
// An attacker cannot manipulate this metric without performing useful work.
912932
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
933+
913934
// Protect the half of the remaining nodes which have been connected the longest.
914935
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
915-
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
936+
// Reserve half of these protected spots for localhost peers, even if
937+
// they're not longest-uptime overall. This helps protect tor peers, which
938+
// tend to be otherwise disadvantaged under our eviction criteria.
939+
size_t initial_size = vEvictionCandidates.size();
940+
size_t total_protect_size = initial_size / 2;
941+
942+
// Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
943+
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
944+
size_t local_erase_size = total_protect_size / 2;
945+
vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
946+
// Calculate how many we removed, and update our total number of peers that
947+
// we want to protect based on uptime accordingly.
948+
total_protect_size -= initial_size - vEvictionCandidates.size();
949+
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
916950

917951
if (vEvictionCandidates.empty()) return false;
918952

0 commit comments

Comments
 (0)