p2p: avoid retrying recently failed seed nodes
What changed, and why it matters
This patch fixes a bug in Monero's peer-to-peer networking code. Previously, when the software tried to connect to 'seed nodes' (hardcoded first-contact servers that help a node find the network), it ignored the cache of recently-failed addresses. That meant an unreachable or rejecting seed node could be retried over and over again, wasting resources and potentially delaying or preventing a node from successfully joining the Monero network. The fix makes seed-node connections respect a shorter 5-minute 'cooldown' before retrying a failed seed, while normal peer connections keep the existing 1-hour cooldown.
Apply the patch. It is a low-risk, defensive hardening fix. No immediate incident response is required, but nodes should be updated to benefit from improved seed-node failure handling.
Security signals we found
Denial-of-service resilience improvement: repeated connection attempts to unreachable/rejected seeds waste sockets, bandwidth, and CPU
Network-partitioning risk reduction: persistent retry storms could delay bootstrap or give an attacker more opportunities to interfere with initial peer discovery
Resource-exhaustion mitigation: bounded retry cooldown reduces churn on connection failures
Evidence from the diff
The change adds a new constant P2P_FAILED_SEED_ADDR_FORGET_SECONDS (5 minutes) alongside the existing P2P_FAILED_ADDR_FORGET_SECONDS (1 hour). It modifies is_addr_recently_failed() to accept an optional forget_seconds parameter defaulting to the original 1-hour value. In the seed-node connection loop, before attempting try_to_connect_and_handshake_with_new_peer(), the code now checks is_addr_recently_failed(pe_seed.adr, P2P_FAILED_SEED_ADDR_FORGET_SECONDS). This causes failed seed addresses to be skipped for 5 minutes, preventing immediate repeated connection attempts while still allowing faster retry than ordinary peers.
Changed components
src/p2p/net_node.inl - seed node connection loopsrc/p2p/net_node.h - is_addr_recently_failed signaturesrc/cryptonote_config.h - new P2P_FAILED_SEED_ADDR_FORGET_SECONDS constantInspect captured patch +5 / −4
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index fea5d30..959e4cb 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -155,6 +155,7 @@
#define P2P_DEFAULT_LIMIT_RATE_DOWN 32768 // kB/s
#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour
+#define P2P_FAILED_SEED_ADDR_FORGET_SECONDS (60*5) //5 minutes
#define P2P_IP_BLOCKTIME (60*60*24) //24 hour
#define P2P_IP_FAILS_BEFORE_BLOCK 10
#define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index b32f79b..c215e8f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -378,7 +378,7 @@ namespace nodetool
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections);
void record_addr_failed(const epee::net_utils::network_address& addr);
- bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
+ bool is_addr_recently_failed(const epee::net_utils::network_address& addr, time_t forget_seconds = P2P_FAILED_ADDR_FORGET_SECONDS);
bool is_priority_node(const epee::net_utils::network_address& na);
std::set<std::string> get_ip_seed_nodes() const;
std::set<std::string> get_dns_seed_nodes();
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index dfe60ff..bf0e79b 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -1496,14 +1496,14 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr)
+ bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr, time_t forget_seconds)
{
CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock);
auto it = m_conn_fails_cache.find(addr.host_str());
if(it == m_conn_fails_cache.end())
return false;
- if(time(NULL) - it->second > P2P_FAILED_ADDR_FORGET_SECONDS)
+ if(time(NULL) - it->second > forget_seconds)
return false;
else
return true;
@@ -1841,7 +1841,7 @@ namespace nodetool
pe_seed.adr = server.m_seed_nodes[current_index];
if (is_peer_used(pe_seed))
is_connected_to_at_least_one_seed_node = true;
- else if (try_to_connect_and_handshake_with_new_peer(server.m_seed_nodes[current_index], true))
+ else if (!is_addr_recently_failed(pe_seed.adr, P2P_FAILED_SEED_ADDR_FORGET_SECONDS) && try_to_connect_and_handshake_with_new_peer(pe_seed.adr, true))
break;
if(++try_count > server.m_seed_nodes.size())
{
Why this scored 39/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.