net: add options to AttemptToEvictConnection
What changed, and why it matters
This commit adds two new optional controls to Bitcoin Core's peer eviction logic: a flag to only evict transaction-relaying peers, and a way to protect a specific peer from being evicted. The existing call site still behaves exactly as before (the new flag is set to false). The change is a preparatory refactor for future commits and does not, by itself, alter live network behavior or fix a known vulnerability.
No immediate action required. Treat as a normal refactor. Review future commits that actually use the new parameters to ensure the eviction protections against network partitioning remain sound and that the new options cannot be abused by an attacker to pin or isolate a node.
Security signals we found
Touches peer eviction logic, which is security-sensitive because improper eviction can enable network partitioning attacks.
Adds a 'protect_peer' bypass that could, if misused in future callers, allow a specific peer to be exempted from eviction.
Adds an 'evict_tx-relay-peer-only' mode that could, if enabled, leave non-tx-relay peers permanently connected while tx-relay peers are evicted.
No current caller enables the new restrictive mode, so no immediate behavior change.
Evidence from the diff
The patch modifies CConnman::AttemptToEvictConnection() in src/net.cpp and src/net.h to accept two new parameters: a bool evict_tx_relay_peer_only and an optional NodeId protect_peer. The function’s candidate loop now skips nodes matching protect_peer and, when evict_tx_relay_peer_only is true, skips non-tx-relay peers. The only current caller, CreateNodeFromAcceptedSocket(), passes false for evict_tx_relay_peer_only, preserving prior behavior. The comment is also updated from ‘a connection’ to ‘an inbound connection’.
Changed components
src/net.cppsrc/net.hCConnman::AttemptToEvictConnection()CConnman::CreateNodeFromAcceptedSocket()Inspect captured patch +16 / −4
diff --git a/src/net.cpp b/src/net.cpp
index 951e804f..74746ac1 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1683,7 +1683,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
return {nSentSize, data_left};
}
-/** Try to find a connection to evict when the node is full.
+/** Try to find an inbound connection to evict.
* Extreme care must be taken to avoid opening the node to attacker
* triggered network partitioning.
* The strategy used here is to protect a small number of peers
@@ -1691,7 +1691,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
* to forge. In order to partition a node the attacker must be
* simultaneously better at all of them than honest peers.
*/
-bool CConnman::AttemptToEvictConnection()
+bool CConnman::AttemptToEvictConnection(bool evict_tx_relay_peer_only, std::optional<NodeId> protect_peer)
{
AssertLockNotHeld(m_nodes_mutex);
@@ -1702,6 +1702,12 @@ bool CConnman::AttemptToEvictConnection()
for (const CNode* node : m_nodes) {
if (node->fDisconnect)
continue;
+ if (protect_peer.has_value() && node->GetId() == protect_peer) {
+ continue;
+ }
+ if (evict_tx_relay_peer_only && !node->m_relays_txs) {
+ continue;
+ }
NodeEvictionCandidate candidate{
.id = node->GetId(),
.m_connected = node->m_connected,
@@ -1830,7 +1836,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
if (nInbound >= m_max_inbound)
{
- if (!AttemptToEvictConnection()) {
+ if (!AttemptToEvictConnection(/*evict_tx_relay_peer_only=*/false)) {
// No connection to evict, disconnect the new connection
LogDebug(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
return;
diff --git a/src/net.h b/src/net.h
index d0cfe7c9..22f44df4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1541,7 +1541,13 @@ private:
*/
bool AlreadyConnectedToAddress(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
- bool AttemptToEvictConnection() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
+ /**
+ * Try to find an inbound connection to evict.
+ * @param[in] evict_tx_relay_peer_only Whether to only select full relay peers for eviction
+ * @param[in] protect_peer Protect peer with node id
+ * @return True if a node was marked for disconnect
+ */
+ bool AttemptToEvictConnection(bool evict_tx_relay_peer_only, std::optional<NodeId> protect_peer = std::nullopt) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Open a new P2P connection.
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.