Skip to content

Commit 1b52d16

Browse files
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>
1 parent 65cff00 commit 1b52d16

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

src/net.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
9090
// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization.
9191
static constexpr auto FEELER_SLEEP_WINDOW{1s};
9292

93+
/** Frequency to attempt extra connections to reachable networks we're not connected to yet **/
94+
static constexpr auto EXTRA_NETWORK_PEER_INTERVAL{5min};
95+
9396
/** Used to pass flags to the Bind() function */
9497
enum BindFlags {
9598
BF_NONE = 0,
@@ -1613,6 +1616,22 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
16131616
return m_network_conn_counts[net] > 1;
16141617
}
16151618

1619+
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
1620+
{
1621+
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
1622+
Shuffle(nets.begin(), nets.end(), FastRandomContext());
1623+
1624+
LOCK(m_nodes_mutex);
1625+
for (const auto net : nets) {
1626+
if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
1627+
network = net;
1628+
return true;
1629+
}
1630+
}
1631+
1632+
return false;
1633+
}
1634+
16161635
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
16171636
{
16181637
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
@@ -1644,6 +1663,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
16441663
// Minimum time before next feeler connection (in microseconds).
16451664
auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
16461665
auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
1666+
auto next_extra_network_peer{GetExponentialRand(start, EXTRA_NETWORK_PEER_INTERVAL)};
16471667
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
16481668
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
16491669

@@ -1755,6 +1775,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
17551775
auto now = GetTime<std::chrono::microseconds>();
17561776
bool anchor = false;
17571777
bool fFeeler = false;
1778+
std::optional<Network> preferred_net;
17581779

17591780
// Determine what type of connection to open. Opening
17601781
// BLOCK_RELAY connections to addresses from anchors.dat gets the highest
@@ -1804,6 +1825,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
18041825
next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
18051826
conn_type = ConnectionType::FEELER;
18061827
fFeeler = true;
1828+
} else if (nOutboundFullRelay == m_max_outbound_full_relay &&
1829+
m_max_outbound_full_relay == MAX_OUTBOUND_FULL_RELAY_CONNECTIONS &&
1830+
now > next_extra_network_peer &&
1831+
MaybePickPreferredNetwork(preferred_net)) {
1832+
// Full outbound connection management: Attempt to get at least one
1833+
// outbound peer from each reachable network by making extra connections
1834+
// and then protecting "only" peers from a network during outbound eviction.
1835+
// This is not attempted if the user changed -maxconnections to a value
1836+
// so low that less than MAX_OUTBOUND_FULL_RELAY_CONNECTIONS are made,
1837+
// to prevent interactions with otherwise protected outbound peers.
1838+
next_extra_network_peer = GetExponentialRand(now, EXTRA_NETWORK_PEER_INTERVAL);
18071839
} else {
18081840
// skip to next iteration of while loop
18091841
continue;
@@ -1857,7 +1889,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
18571889
}
18581890
} else {
18591891
// Not a feeler
1860-
std::tie(addr, addr_last_try) = addrman.Select();
1892+
// If preferred_net has a value set, pick an extra outbound
1893+
// peer from that network. The eviction logic in net_processing
1894+
// ensures that a peer from another network will be evicted.
1895+
std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
18611896
}
18621897

18631898
// 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)
19041939
}
19051940
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
19061941
}
1942+
1943+
if (preferred_net != std::nullopt) LogPrint(BCLog::NET, "Making network specific connection to %s on %s.\n", addrConnect.ToStringAddrPort(), GetNetworkName(preferred_net.value()));
1944+
19071945
// Record addrman failure attempts when node has at least 2 persistent outbound connections to peers with
19081946
// different netgroups in ipv4/ipv6 networks + all peers in Tor/I2P/CJDNS networks.
19091947
// Don't record addrman failure attempts when node is offline. This can be identified since all local

src/net.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,18 @@ class CConnman
10311031
*/
10321032
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
10331033

1034+
/**
1035+
* Search for a "preferred" network, a reachable network to which we
1036+
* currently don't have any OUTBOUND_FULL_RELAY or MANUAL connections.
1037+
* There needs to be at least one address in AddrMan for a preferred
1038+
* network to be picked.
1039+
*
1040+
* @param[out] network Preferred network, if found.
1041+
*
1042+
* @return bool Whether a preferred network was found.
1043+
*/
1044+
bool MaybePickPreferredNetwork(std::optional<Network>& network);
1045+
10341046
// Whether the node should be passed out in ForEach* callbacks
10351047
static bool NodeFullyConnected(const CNode* pnode);
10361048

0 commit comments

Comments
 (0)