Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/netif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t)

std::optional<CNetAddr> QueryDefaultGateway(Network network)
{
Assume(network == NET_IPV4 || network == NET_IPV6);
Assume(IsClearnet(network));

sa_family_t family;
if (network == NET_IPV4) {
Expand Down
7 changes: 6 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-v2onlyclearnet", strprintf("Require v2 transport (BIP324) for connections with IPv4/IPv6 peers (default: %u). Outbound connections to v1-only clearnet peers are not attempted and inbound v1 connections from clearnet peers are disconnected. Tor/I2P/CJDNS peers are unaffected, being encrypted already. Requires -v2transport=1. Enable this only if passive on-path observers such as your ISP are a concern. Note: this encrypts message contents; it does not hide that you are running a Bitcoin node, which the default port (8333) and traffic patterns still reveal.", DEFAULT_V2_ONLY_CLEARNET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
Expand Down Expand Up @@ -787,7 +788,7 @@ void InitParameterInteraction(ArgsManager& args)
if (!onlynets.empty()) {
bool clearnet_reachable = std::any_of(onlynets.begin(), onlynets.end(), [](const auto& net) {
const auto n = ParseNetwork(net);
return n == NET_IPV4 || n == NET_IPV6;
return IsClearnet(n);
});
if (!clearnet_reachable && args.SoftSetBoolArg("-dnsseed", false)) {
LogInfo("parameter interaction: -onlynet excludes IPv4 and IPv6 -> setting -dnsseed=0\n");
Expand Down Expand Up @@ -953,6 +954,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// Signal NODE_P2P_V2 if BIP324 v2 transport is enabled.
if (args.GetBoolArg("-v2transport", DEFAULT_V2_TRANSPORT)) {
g_local_services = ServiceFlags(g_local_services | NODE_P2P_V2);
} else if (args.GetBoolArg("-v2onlyclearnet", DEFAULT_V2_ONLY_CLEARNET)) {
return InitError(_("Cannot set -v2onlyclearnet to true when v2transport is disabled."));
}

// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index are both enabled.
Expand Down Expand Up @@ -1924,6 +1927,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.m_peer_connect_timeout = peer_connect_timeout;
connOptions.whitelist_forcerelay = args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY);
connOptions.whitelist_relay = args.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY);
connOptions.m_capture_messages = args.GetBoolArg("-capturemessages", false);
connOptions.m_v2only_clearnet = args.GetBoolArg("-v2onlyclearnet", DEFAULT_V2_ONLY_CLEARNET);

// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
const uint16_t default_bind_port =
Expand Down
91 changes: 66 additions & 25 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";

static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
//
// Global state variables
//
Expand Down Expand Up @@ -463,6 +463,12 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
std::unique_ptr<i2p::sam::Session> i2p_transient_session;

for (auto& target_addr: connect_to) {
const std::string_view dest_name{pszDest ? pszDest : ""};
if (RequiresV2Dest(target_addr, dest_name) && !use_v2transport) {
LogDebug(BCLog::NET, "skipping v1 connection to %s (-v2onlyclearnet)\n",
target_addr.IsValid() ? target_addr.ToStringAddrPort() : std::string{dest_name});
continue;
}
if (target_addr.IsValid()) {
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
bool proxyConnectionFailed = false;
Expand Down Expand Up @@ -531,6 +537,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (!addr_bind.IsValid()) {
addr_bind = GetBindAddress(*sock);
}
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
.Write(target_addr.GetNetClass())
.Write(addr_bind.GetAddrBytes())
// For outbound connections, the port of the bound address is randomly
// assigned by the OS and would therefore not be useful for seeding.
.Write(0)
.Finalize();
CNode* pnode = new CNode(id,
std::move(sock),
target_addr,
Expand All @@ -540,6 +553,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? pszDest : "",
conn_type,
/*inbound_onion=*/false,
network_id,
CNodeOptions{
.permission_flags = permission_flags,
.i2p_sam_session = std::move(i2p_transient_session),
Expand Down Expand Up @@ -1787,6 +1801,10 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
return std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
}
}();
// Without a -bind=...=onion listener, an onion inbound is an assumption about a listener that
// clearnet peers can reach too. A connection from the bind address itself comes from this host
// (such as a Tor daemon whose onion service points at that address), so it is not in doubt.
const bool inbound_onion_assumed{inbound_onion && m_onion_binds.empty() && CNetAddr{addr} != CNetAddr{addr_bind}};

// Tor inbound connections do not reveal the peer's actual network address.
// Therefore do not apply address-based whitelist permissions to them.
Expand Down Expand Up @@ -1850,6 +1868,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
ServiceFlags local_services = GetLocalServices();
const bool use_v2transport(local_services & NODE_P2P_V2);

uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
.Write(addr_bind.GetAddrBytes())
.Write(addr_bind.GetPort()) // inbound connections use bind port
.Finalize();
CNode* pnode = new CNode(id,
std::move(sock),
CAddress{addr, NODE_NONE},
Expand All @@ -1859,11 +1882,13 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
/*addrNameIn=*/"",
ConnectionType::INBOUND,
inbound_onion,
network_id,
CNodeOptions{
.permission_flags = permission_flags,
.prefer_evict = discouraged,
.recv_flood_size = nReceiveFloodSize,
.use_v2transport = use_v2transport,
.inbound_onion_assumed = inbound_onion_assumed,
});
pnode->AddRef();
m_msgproc->InitializeNode(*pnode, local_services);
Expand Down Expand Up @@ -1932,7 +1957,8 @@ void CConnman::DisconnectNodes()
{
LOCK(m_nodes_mutex);

if (!fNetworkActive) {
const bool network_active{fNetworkActive};
if (!network_active) {
// Disconnect any connected nodes
for (CNode* pnode : m_nodes) {
if (!pnode->fDisconnect) {
Expand All @@ -1954,14 +1980,18 @@ void CConnman::DisconnectNodes()
// Add to reconnection list if appropriate. We don't reconnect right here, because
// the creation of a connection is a blocking operation (up to several seconds),
// and we don't want to hold up the socket handler thread for that long.
if (pnode->m_transport->ShouldReconnectV1()) {
reconnections_to_add.push_back({
.addr_connect = pnode->addr,
.grant = std::move(pnode->grantOutbound),
.destination = pnode->m_dest,
.conn_type = pnode->m_conn_type,
.use_v2transport = false});
LogDebug(BCLog::NET, "retrying with v1 transport protocol for peer=%d\n", pnode->GetId());
if (network_active && pnode->m_transport->ShouldReconnectV1()) {
if (RequiresV2Dest(pnode->addr, pnode->m_dest)) {
LogDebug(BCLog::NET, "not retrying with v1 transport protocol for peer=%d (-v2onlyclearnet)\n", pnode->GetId());
} else {
reconnections_to_add.push_back({
.addr_connect = pnode->addr,
.grant = std::move(pnode->grantOutbound),
.destination = pnode->m_dest,
.conn_type = pnode->m_conn_type,
.use_v2transport = false});
LogDebug(BCLog::NET, "retrying with v1 transport protocol for peer=%d\n", pnode->GetId());
}
}

// release outbound grant (if any)
Expand Down Expand Up @@ -2013,16 +2043,15 @@ void CConnman::NotifyNumConnectionsChanged()
}
}

bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::microseconds now) const
{
return node.m_connected + m_peer_connect_timeout < now;
}

bool CConnman::InactivityCheck(const CNode& node) const
bool CConnman::InactivityCheck(const CNode& node, std::chrono::microseconds now) const
{
// Tests that see disconnects after using mocktime can start nodes with a
// large timeout. For example, -peertimeout=999999999.
const auto now{GetTime<std::chrono::seconds>()};
const auto last_send{node.m_last_send.load()};
const auto last_recv{node.m_last_recv.load()};

Expand All @@ -2046,15 +2075,15 @@ bool CConnman::InactivityCheck(const CNode& node) const

if (now > last_send + TIMEOUT_INTERVAL) {
LogDebug(BCLog::NET,
"socket sending timeout: %is, %s\n", count_seconds(now - last_send),
"socket sending timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_send),
node.DisconnectMsg(fLogIPs)
);
return true;
}

if (now > last_recv + TIMEOUT_INTERVAL) {
LogDebug(BCLog::NET,
"socket receive timeout: %is, %s\n", count_seconds(now - last_recv),
"socket receive timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_recv),
node.DisconnectMsg(fLogIPs)
);
return true;
Expand Down Expand Up @@ -2136,6 +2165,8 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
{
AssertLockNotHeld(m_total_bytes_sent_mutex);

auto now = GetTime<std::chrono::microseconds>();

for (CNode* pnode : nodes) {
if (interruptNet)
return;
Expand Down Expand Up @@ -2226,7 +2257,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
}
}

if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
if (InactivityCheck(*pnode, now)) pnode->fDisconnect = true;
}
}

Expand Down Expand Up @@ -2517,6 +2548,19 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
return m_network_conn_counts[net] > 1;
}

bool CConnman::RequiresV2Peer(Network net) const
{
return m_v2only_clearnet && IsClearnet(net);
}

bool CConnman::RequiresV2Dest(const CNetAddr& addr, std::string_view dest_name) const
{
// A name proxy resolves the destination for us, so we can't tell locally which
// network it belongs to. Assume clearnet, the worst case.
if (!addr.IsValid() && !dest_name.empty()) return m_v2only_clearnet;
return RequiresV2Peer(addr.GetNetClass());
}

bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
{
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
Expand Down Expand Up @@ -3536,15 +3580,9 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
.Write(requestor.ConnectedThroughNetwork())
.Write(local_socket_bytes)
// For outbound connections, the port of the bound address is randomly
// assigned by the OS and would therefore not be useful for seeding.
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
.Finalize();
uint64_t network_id = requestor.m_network_key;
const auto current_time = GetTime<std::chrono::microseconds>();
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
CachedAddrResponse& cache_entry = r.first->second;
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct, /*network=*/std::nullopt);
Expand Down Expand Up @@ -3821,6 +3859,7 @@ CNode::CNode(NodeId idIn,
const std::string& addrNameIn,
ConnectionType conn_type_in,
bool inbound_onion,
uint64_t network_key,
CNodeOptions&& node_opts)
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
m_permission_flags{node_opts.permission_flags},
Expand All @@ -3831,8 +3870,10 @@ CNode::CNode(NodeId idIn,
m_addr_name{addrNameIn.empty() ? addr.ToStringAddrPort() : addrNameIn},
m_dest(addrNameIn),
m_inbound_onion{inbound_onion},
m_inbound_onion_assumed{inbound_onion && node_opts.inbound_onion_assumed},
m_prefer_evict{node_opts.prefer_evict},
nKeyedNetGroup{nKeyedNetGroupIn},
m_network_key{network_key},
m_conn_type{conn_type_in},
id{idIn},
nLocalHostNonce{nLocalHostNonceIn},
Expand Down Expand Up @@ -3894,7 +3935,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
AssertLockNotHeld(m_total_bytes_sent_mutex);
size_t nMessageSize = msg.data.size();
LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
if (gArgs.GetBoolArg("-capturemessages", false)) {
if (m_capture_messages) {
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
}

Expand Down
Loading