aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Zumsande <mzumsande@gmail.com>2023-02-14 17:40:14 -0500
committerAmiti Uttarwar <amiti@uttarwar.org>2023-08-03 19:27:23 -0600
commit1b52d16d07be3b5d968157913f04d9cd1e2d3678 (patch)
treeade1e95d9bc6fab93ca3e1506783bfcbb2e8bbcc /src
parent65cff00ceea48ac8a887ffea79aedb4251aa097f (diff)
downloadbitcoin-1b52d16d07be3b5d968157913f04d9cd1e2d3678.tar.xz
p2p: network-specific management of outbound connections
Diversify outbound connections with respect to networks: Every ~5 minutes, try to add an extra connection to a reachable network which we currently don't have a connection to. This is done defensively - only try management with respect to networks after all existing outbound slots are filled. The resulting situation with an extra outbound peer will be handled by the extra outbound eviction logic, which protects peers from eviction if they are the only ones for their network. Co-authored-by: Amiti Uttarwar <amiti@uttarwar.org>
Diffstat (limited to 'src')
-rw-r--r--src/net.cpp40
-rw-r--r--src/net.h12
2 files changed, 51 insertions, 1 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 00ee873c69..228d081aa6 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -90,6 +90,9 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization.
static constexpr auto FEELER_SLEEP_WINDOW{1s};
+/** Frequency to attempt extra connections to reachable networks we're not connected to yet **/
+static constexpr auto EXTRA_NETWORK_PEER_INTERVAL{5min};
+
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
@@ -1613,6 +1616,22 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
return m_network_conn_counts[net] > 1;
}
+bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
+{
+ std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
+ Shuffle(nets.begin(), nets.end(), FastRandomContext());
+
+ LOCK(m_nodes_mutex);
+ for (const auto net : nets) {
+ if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
+ network = net;
+ return true;
+ }
+ }
+
+ return false;
+}
+
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
@@ -1644,6 +1663,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Minimum time before next feeler connection (in microseconds).
auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ auto next_extra_network_peer{GetExponentialRand(start, EXTRA_NETWORK_PEER_INTERVAL)};
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
@@ -1755,6 +1775,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
auto now = GetTime<std::chrono::microseconds>();
bool anchor = false;
bool fFeeler = false;
+ std::optional<Network> preferred_net;
// Determine what type of connection to open. Opening
// BLOCK_RELAY connections to addresses from anchors.dat gets the highest
@@ -1804,6 +1825,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
fFeeler = true;
+ } else if (nOutboundFullRelay == m_max_outbound_full_relay &&
+ m_max_outbound_full_relay == MAX_OUTBOUND_FULL_RELAY_CONNECTIONS &&
+ now > next_extra_network_peer &&
+ MaybePickPreferredNetwork(preferred_net)) {
+ // Full outbound connection management: Attempt to get at least one
+ // outbound peer from each reachable network by making extra connections
+ // and then protecting "only" peers from a network during outbound eviction.
+ // This is not attempted if the user changed -maxconnections to a value
+ // so low that less than MAX_OUTBOUND_FULL_RELAY_CONNECTIONS are made,
+ // to prevent interactions with otherwise protected outbound peers.
+ next_extra_network_peer = GetExponentialRand(now, EXTRA_NETWORK_PEER_INTERVAL);
} else {
// skip to next iteration of while loop
continue;
@@ -1857,7 +1889,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
} else {
// Not a feeler
- std::tie(addr, addr_last_try) = addrman.Select();
+ // If preferred_net has a value set, pick an extra outbound
+ // peer from that network. The eviction logic in net_processing
+ // ensures that a peer from another network will be evicted.
+ std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
}
// Require outbound IPv4/IPv6 connections, other than feelers, to be to distinct network groups
@@ -1904,6 +1939,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
}
+
+ if (preferred_net != std::nullopt) LogPrint(BCLog::NET, "Making network specific connection to %s on %s.\n", addrConnect.ToStringAddrPort(), GetNetworkName(preferred_net.value()));
+
// Record addrman failure attempts when node has at least 2 persistent outbound connections to peers with
// different netgroups in ipv4/ipv6 networks + all peers in Tor/I2P/CJDNS networks.
// Don't record addrman failure attempts when node is offline. This can be identified since all local
diff --git a/src/net.h b/src/net.h
index a4c810b0ae..39829f7f5b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1031,6 +1031,18 @@ private:
*/
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
+ /**
+ * Search for a "preferred" network, a reachable network to which we
+ * currently don't have any OUTBOUND_FULL_RELAY or MANUAL connections.
+ * There needs to be at least one address in AddrMan for a preferred
+ * network to be picked.
+ *
+ * @param[out] network Preferred network, if found.
+ *
+ * @return bool Whether a preferred network was found.
+ */
+ bool MaybePickPreferredNetwork(std::optional<Network>& network);
+
// Whether the node should be passed out in ForEach* callbacks
static bool NodeFullyConnected(const CNode* pnode);