diff options
Diffstat (limited to 'src/net.cpp')
-rw-r--r-- | src/net.cpp | 645 |
1 files changed, 169 insertions, 476 deletions
diff --git a/src/net.cpp b/src/net.cpp index 5a0c057c3e..c4aaac4986 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -13,15 +13,16 @@ #include <addrman.h> #include <banman.h> #include <clientversion.h> -#include <compat.h> +#include <compat/compat.h> #include <consensus/consensus.h> #include <crypto/sha256.h> +#include <node/eviction.h> #include <fs.h> #include <i2p.h> #include <net_permissions.h> #include <netaddress.h> #include <netbase.h> -#include <node/ui_interface.h> +#include <node/interface_ui.h> #include <protocol.h> #include <random.h> #include <scheduler.h> @@ -84,8 +85,8 @@ static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // "many" vs "few" pe /** The default timeframe for -maxuploadtarget. 1 day. */ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24}; -// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. -#define FEELER_SLEEP_WINDOW 1 +// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization. +static constexpr auto FEELER_SLEEP_WINDOW{1s}; /** Used to pass flags to the Bind() function */ enum BindFlags { @@ -103,7 +104,7 @@ enum BindFlags { // The sleep time needs to be small to avoid new sockets stalling static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50; -const std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; +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] @@ -113,7 +114,7 @@ static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256 // bool fDiscover = true; bool fListen = true; -Mutex g_maplocalhost_mutex; +GlobalMutex g_maplocalhost_mutex; std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex); static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {}; std::string strSubVersion; @@ -186,7 +187,7 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn) // it'll get a pile of addresses with newer timestamps. // Seed nodes are given a random 'last seen time' of between one and two // weeks ago. - const int64_t nOneWeek = 7*24*60*60; + const auto one_week{7 * 24h}; std::vector<CAddress> vSeedsOut; FastRandomContext rng; CDataStream s(vSeedsIn, SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT); @@ -194,7 +195,7 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn) CService endpoint; s >> endpoint; CAddress addr{endpoint, GetDesirableServiceFlags(NODE_NONE)}; - addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek; + addr.nTime = rng.rand_uniform_delay(Now<NodeSeconds>() - one_week, -one_week); LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToString()); vSeedsOut.push_back(addr); } @@ -205,15 +206,13 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn) // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) +CService GetLocalAddress(const CNetAddr& addrPeer) { - CAddress ret(CService(CNetAddr(),GetListenPort()), nLocalServices); + CService ret{CNetAddr(), GetListenPort()}; CService addr; - if (GetLocal(addr, paddrPeer)) - { - ret = CAddress(addr, nLocalServices); + if (GetLocal(addr, &addrPeer)) { + ret = CService{addr}; } - ret.nTime = GetAdjustedTime(); return ret; } @@ -232,35 +231,35 @@ bool IsPeerAddrLocalGood(CNode *pnode) IsReachable(addrLocal.GetNetwork()); } -std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode) +std::optional<CService> GetLocalAddrForPeer(CNode& node) { - CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); + CService addrLocal{GetLocalAddress(node.addr)}; if (gArgs.GetBoolArg("-addrmantest", false)) { // use IPv4 loopback during addrmantest - addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices()); + addrLocal = CService(LookupNumeric("127.0.0.1", GetListenPort())); } // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. FastRandomContext rng; - if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || + if (IsPeerAddrLocalGood(&node) && (!addrLocal.IsRoutable() || rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0)) { - if (pnode->IsInboundConn()) { + if (node.IsInboundConn()) { // For inbound connections, assume both the address and the port // as seen from the peer. - addrLocal = CAddress{pnode->GetAddrLocal(), addrLocal.nServices}; + addrLocal = CService{node.GetAddrLocal()}; } else { // For outbound connections, assume just the address as seen from // the peer and leave the port in `addrLocal` as returned by // `GetLocalAddress()` above. The peer has no way to observe our // listening port when we have initiated the connection. - addrLocal.SetIP(pnode->GetAddrLocal()); + addrLocal.SetIP(node.GetAddrLocal()); } } if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false)) { - LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), pnode->GetId()); + LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), node.GetId()); return addrLocal; } // Address is unroutable. Don't advertise. @@ -421,16 +420,16 @@ bool CConnman::CheckIncomingNonce(uint64_t nonce) } /** Get the bind address for a socket as CAddress */ -static CAddress GetBindAddress(SOCKET sock) +static CAddress GetBindAddress(const Sock& sock) { CAddress addr_bind; struct sockaddr_storage sockaddr_bind; socklen_t sockaddr_bind_len = sizeof(sockaddr_bind); - if (sock != INVALID_SOCKET) { - if (!getsockname(sock, (struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) { + if (sock.Get() != INVALID_SOCKET) { + if (!sock.GetSockName((struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) { addr_bind.SetSockAddr((const struct sockaddr*)&sockaddr_bind); } else { - LogPrint(BCLog::NET, "Warning: getsockname failed\n"); + LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n"); } } return addr_bind; @@ -453,10 +452,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo } } - /// debug print - LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n", + LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + Ticks<HoursDouble>(pszDest ? 0h : AdjustedTime() - addrConnect.nTime)); // Resolve const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) : @@ -539,10 +537,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); if (!addr_bind.IsValid()) { - addr_bind = GetBindAddress(sock->Get()); + addr_bind = GetBindAddress(*sock); } CNode* pnode = new CNode(id, - nLocalServices, std::move(sock), addrConnect, CalculateKeyedNetGroup(addrConnect), @@ -575,26 +572,6 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet } } -std::string ConnectionTypeAsString(ConnectionType conn_type) -{ - switch (conn_type) { - case ConnectionType::INBOUND: - return "inbound"; - case ConnectionType::MANUAL: - return "manual"; - case ConnectionType::FEELER: - return "feeler"; - case ConnectionType::OUTBOUND_FULL_RELAY: - return "outbound-full-relay"; - case ConnectionType::BLOCK_RELAY: - return "block-relay-only"; - case ConnectionType::ADDR_FETCH: - return "addr-fetch"; - } // no default case, so the compiler can warn about missing cases - - assert(false); -} - CService CNode::GetAddrLocal() const { AssertLockNotHeld(m_addr_local_mutex); @@ -622,7 +599,6 @@ Network CNode::ConnectedThroughNetwork() const void CNode::CopyStats(CNodeStats& stats) { stats.nodeid = this->GetId(); - X(nServices); X(addr); X(addrBind); stats.m_network = ConnectedThroughNetwork(); @@ -643,12 +619,12 @@ void CNode::CopyStats(CNodeStats& stats) X(m_bip152_highbandwidth_from); { LOCK(cs_vSend); - X(mapSendBytesPerMsgCmd); + X(mapSendBytesPerMsgType); X(nSendBytes); } { LOCK(cs_vRecv); - X(mapRecvBytesPerMsgCmd); + X(mapRecvBytesPerMsgType); X(nRecvBytes); } X(m_permissionFlags); @@ -684,19 +660,19 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete) bool reject_message{false}; CNetMessage msg = m_deserializer->GetMessage(time, reject_message); if (reject_message) { - // Message deserialization failed. Drop the message but don't disconnect the peer. + // Message deserialization failed. Drop the message but don't disconnect the peer. // store the size of the corrupt message - mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += msg.m_raw_message_size; + mapRecvBytesPerMsgType.at(NET_MESSAGE_TYPE_OTHER) += msg.m_raw_message_size; continue; } - // Store received bytes per message command - // to prevent a memory DOS, only allow valid commands - auto i = mapRecvBytesPerMsgCmd.find(msg.m_type); - if (i == mapRecvBytesPerMsgCmd.end()) { - i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER); + // Store received bytes per message type. + // To prevent a memory DOS, only allow known message types. + auto i = mapRecvBytesPerMsgType.find(msg.m_type); + if (i == mapRecvBytesPerMsgType.end()) { + i = mapRecvBytesPerMsgType.find(NET_MESSAGE_TYPE_OTHER); } - assert(i != mapRecvBytesPerMsgCmd.end()); + assert(i != mapRecvBytesPerMsgType.end()); i->second += msg.m_raw_message_size; // push the message to the process queue, @@ -781,7 +757,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds // decompose a single CNetMessage from the TransportDeserializer CNetMessage msg(std::move(vRecv)); - // store command string, time, and sizes + // store message type string, time, and sizes msg.m_type = hdr.GetCommand(); msg.m_time = time; msg.m_message_size = hdr.nMessageSize; @@ -792,7 +768,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds // We just received a message off the wire, harvest entropy from the time (and the message checksum) RandAddEvent(ReadLE32(hash.begin())); - // Check checksum and header command string + // Check checksum and header message type string if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n", SanitizeString(msg.m_type), msg.m_message_size, @@ -876,210 +852,6 @@ size_t CConnman::SocketSendData(CNode& node) const return nSentSize; } -static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ - return a.m_min_ping_time > b.m_min_ping_time; -} - -static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ - return a.m_connected > b.m_connected; -} - -static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { - return a.nKeyedNetGroup < b.nKeyedNetGroup; -} - -static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ - // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block. - if (a.m_last_block_time != b.m_last_block_time) return a.m_last_block_time < b.m_last_block_time; - if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices; - return a.m_connected > b.m_connected; -} - -static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ - // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn. - if (a.m_last_tx_time != b.m_last_tx_time) return a.m_last_tx_time < b.m_last_tx_time; - if (a.m_relay_txs != b.m_relay_txs) return b.m_relay_txs; - if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter; - return a.m_connected > b.m_connected; -} - -// Pick out the potential block-relay only peers, and sort them by last block time. -static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ - if (a.m_relay_txs != b.m_relay_txs) return a.m_relay_txs; - if (a.m_last_block_time != b.m_last_block_time) return a.m_last_block_time < b.m_last_block_time; - if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices; - return a.m_connected > b.m_connected; -} - -/** - * Sort eviction candidates by network/localhost and connection uptime. - * Candidates near the beginning are more likely to be evicted, and those - * near the end are more likely to be protected, e.g. less likely to be evicted. - * - First, nodes that are not `is_local` and that do not belong to `network`, - * sorted by increasing uptime (from most recently connected to connected longer). - * - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime. - */ -struct CompareNodeNetworkTime { - const bool m_is_local; - const Network m_network; - CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {} - bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const - { - if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local; - if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network; - return a.m_connected > b.m_connected; - }; -}; - -//! Sort an array by the specified comparator, then erase the last K elements where predicate is true. -template <typename T, typename Comparator> -static void EraseLastKElements( - std::vector<T>& elements, Comparator comparator, size_t k, - std::function<bool(const NodeEvictionCandidate&)> predicate = [](const NodeEvictionCandidate& n) { return true; }) -{ - std::sort(elements.begin(), elements.end(), comparator); - size_t eraseSize = std::min(k, elements.size()); - elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end()); -} - -void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates) -{ - // Protect the half of the remaining nodes which have been connected the longest. - // This replicates the non-eviction implicit behavior, and precludes attacks that start later. - // To favorise the diversity of our peer connections, reserve up to half of these protected - // spots for Tor/onion, localhost, I2P, and CJDNS peers, even if they're not longest uptime - // overall. This helps protect these higher-latency peers that tend to be otherwise - // disadvantaged under our eviction criteria. - const size_t initial_size = eviction_candidates.size(); - const size_t total_protect_size{initial_size / 2}; - - // Disadvantaged networks to protect. In the case of equal counts, earlier array members - // have the first opportunity to recover unused slots from the previous iteration. - struct Net { bool is_local; Network id; size_t count; }; - std::array<Net, 4> networks{ - {{false, NET_CJDNS, 0}, {false, NET_I2P, 0}, {/*localhost=*/true, NET_MAX, 0}, {false, NET_ONION, 0}}}; - - // Count and store the number of eviction candidates per network. - for (Net& n : networks) { - n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(), - [&n](const NodeEvictionCandidate& c) { - return n.is_local ? c.m_is_local : c.m_network == n.id; - }); - } - // Sort `networks` by ascending candidate count, to give networks having fewer candidates - // the first opportunity to recover unused protected slots from the previous iteration. - std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; }); - - // Protect up to 25% of the eviction candidates by disadvantaged network. - const size_t max_protect_by_network{total_protect_size / 2}; - size_t num_protected{0}; - - while (num_protected < max_protect_by_network) { - // Count the number of disadvantaged networks from which we have peers to protect. - auto num_networks = std::count_if(networks.begin(), networks.end(), [](const Net& n) { return n.count; }); - if (num_networks == 0) { - break; - } - const size_t disadvantaged_to_protect{max_protect_by_network - num_protected}; - const size_t protect_per_network{std::max(disadvantaged_to_protect / num_networks, static_cast<size_t>(1))}; - // Early exit flag if there are no remaining candidates by disadvantaged network. - bool protected_at_least_one{false}; - - for (Net& n : networks) { - if (n.count == 0) continue; - const size_t before = eviction_candidates.size(); - EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id), - protect_per_network, [&n](const NodeEvictionCandidate& c) { - return n.is_local ? c.m_is_local : c.m_network == n.id; - }); - const size_t after = eviction_candidates.size(); - if (before > after) { - protected_at_least_one = true; - const size_t delta{before - after}; - num_protected += delta; - if (num_protected >= max_protect_by_network) { - break; - } - n.count -= delta; - } - } - if (!protected_at_least_one) { - break; - } - } - - // Calculate how many we removed, and update our total number of peers that - // we want to protect based on uptime accordingly. - assert(num_protected == initial_size - eviction_candidates.size()); - const size_t remaining_to_protect{total_protect_size - num_protected}; - EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect); -} - -[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates) -{ - // Protect connections with certain characteristics - - // Deterministically select 4 peers to protect by netgroup. - // An attacker cannot predict which netgroups will be protected - EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); - // Protect the 8 nodes with the lowest minimum ping time. - // An attacker cannot manipulate this metric without physically moving nodes closer to the target. - EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); - // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool. - // An attacker cannot manipulate this metric without performing useful work. - EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); - // Protect up to 8 non-tx-relay peers that have sent us novel blocks. - EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8, - [](const NodeEvictionCandidate& n) { return !n.m_relay_txs && n.fRelevantServices; }); - - // Protect 4 nodes that most recently sent us novel blocks. - // An attacker cannot manipulate this metric without performing useful work. - EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); - - // Protect some of the remaining eviction candidates by ratios of desirable - // or disadvantaged characteristics. - ProtectEvictionCandidatesByRatio(vEvictionCandidates); - - if (vEvictionCandidates.empty()) return std::nullopt; - - // If any remaining peers are preferred for eviction consider only them. - // This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks) - // then we probably don't want to evict it no matter what. - if (std::any_of(vEvictionCandidates.begin(),vEvictionCandidates.end(),[](NodeEvictionCandidate const &n){return n.prefer_evict;})) { - vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.begin(),vEvictionCandidates.end(), - [](NodeEvictionCandidate const &n){return !n.prefer_evict;}),vEvictionCandidates.end()); - } - - // Identify the network group with the most connections and youngest member. - // (vEvictionCandidates is already sorted by reverse connect time) - uint64_t naMostConnections; - unsigned int nMostConnections = 0; - std::chrono::seconds nMostConnectionsTime{0}; - std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes; - for (const NodeEvictionCandidate &node : vEvictionCandidates) { - std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup]; - group.push_back(node); - const auto grouptime{group[0].m_connected}; - - if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) { - nMostConnections = group.size(); - nMostConnectionsTime = grouptime; - naMostConnections = node.nKeyedNetGroup; - } - } - - // Reduce to the network group with the most connections - vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); - - // Disconnect from the network group with the most connections - return vEvictionCandidates.front().id; -} - /** Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker * triggered network partitioning. @@ -1095,18 +867,24 @@ bool CConnman::AttemptToEvictConnection() LOCK(m_nodes_mutex); for (const CNode* node : m_nodes) { - if (node->HasPermission(NetPermissionFlags::NoBan)) - continue; - if (!node->IsInboundConn()) - continue; if (node->fDisconnect) continue; - NodeEvictionCandidate candidate = {node->GetId(), node->m_connected, node->m_min_ping_time, - node->m_last_block_time, node->m_last_tx_time, - HasAllDesirableServiceFlags(node->nServices), - node->m_relays_txs.load(), node->m_bloom_filter_loaded.load(), - node->nKeyedNetGroup, node->m_prefer_evict, node->addr.IsLocal(), - node->ConnectedThroughNetwork()}; + NodeEvictionCandidate candidate{ + .id = node->GetId(), + .m_connected = node->m_connected, + .m_min_ping_time = node->m_min_ping_time, + .m_last_block_time = node->m_last_block_time, + .m_last_tx_time = node->m_last_tx_time, + .fRelevantServices = node->m_has_all_wanted_services, + .m_relay_txs = node->m_relays_txs.load(), + .fBloomFilter = node->m_bloom_filter_loaded.load(), + .nKeyedNetGroup = node->nKeyedNetGroup, + .prefer_evict = node->m_prefer_evict, + .m_is_local = node->addr.IsLocal(), + .m_network = node->ConnectedThroughNetwork(), + .m_noban = node->HasPermission(NetPermissionFlags::NoBan), + .m_conn_type = node->m_conn_type, + }; vEvictionCandidates.push_back(candidate); } } @@ -1140,12 +918,12 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { } if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) { - LogPrintf("Warning: Unknown socket family\n"); + LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "Unknown socket family\n"); } else { addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE}; } - const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(sock->Get())), NODE_NONE}; + const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(*sock)), NODE_NONE}; NetPermissionFlags permissionFlags = NetPermissionFlags::None; hListenSocket.AddSocketPermissionFlags(permissionFlags); @@ -1231,7 +1009,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock, const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end(); CNode* pnode = new CNode(id, - nodeServices, std::move(sock), addr, CalculateKeyedNetGroup(addr), @@ -1243,7 +1020,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock, pnode->AddRef(); pnode->m_permissionFlags = permissionFlags; pnode->m_prefer_evict = discouraged; - m_msgproc->InitializeNode(pnode); + m_msgproc->InitializeNode(*pnode, nodeServices); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -1395,13 +1172,12 @@ bool CConnman::InactivityCheck(const CNode& node) const return false; } -bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes, - std::set<SOCKET>& recv_set, - std::set<SOCKET>& send_set, - std::set<SOCKET>& error_set) +Sock::EventsPerSock CConnman::GenerateWaitSockets(Span<CNode* const> nodes) { + Sock::EventsPerSock events_per_sock; + for (const ListenSocket& hListenSocket : vhListenSocket) { - recv_set.insert(hListenSocket.sock->Get()); + events_per_sock.emplace(hListenSocket.sock, Sock::Events{Sock::RECV}); } for (CNode* pnode : nodes) { @@ -1428,171 +1204,52 @@ bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes, continue; } - error_set.insert(pnode->m_sock->Get()); + Sock::Event requested{0}; if (select_send) { - send_set.insert(pnode->m_sock->Get()); - continue; - } - if (select_recv) { - recv_set.insert(pnode->m_sock->Get()); - } - } - - return !recv_set.empty() || !send_set.empty() || !error_set.empty(); -} - -#ifdef USE_POLL -void CConnman::SocketEvents(const std::vector<CNode*>& nodes, - std::set<SOCKET>& recv_set, - std::set<SOCKET>& send_set, - std::set<SOCKET>& error_set) -{ - std::set<SOCKET> recv_select_set, send_select_set, error_select_set; - if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) { - interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); - return; - } - - std::unordered_map<SOCKET, struct pollfd> pollfds; - for (SOCKET socket_id : recv_select_set) { - pollfds[socket_id].fd = socket_id; - pollfds[socket_id].events |= POLLIN; - } - - for (SOCKET socket_id : send_select_set) { - pollfds[socket_id].fd = socket_id; - pollfds[socket_id].events |= POLLOUT; - } - - for (SOCKET socket_id : error_select_set) { - pollfds[socket_id].fd = socket_id; - // These flags are ignored, but we set them for clarity - pollfds[socket_id].events |= POLLERR|POLLHUP; - } - - std::vector<struct pollfd> vpollfds; - vpollfds.reserve(pollfds.size()); - for (auto it : pollfds) { - vpollfds.push_back(std::move(it.second)); - } - - if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) return; - - if (interruptNet) return; - - for (struct pollfd pollfd_entry : vpollfds) { - if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd); - if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd); - if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd); - } -} -#else -void CConnman::SocketEvents(const std::vector<CNode*>& nodes, - std::set<SOCKET>& recv_set, - std::set<SOCKET>& send_set, - std::set<SOCKET>& error_set) -{ - std::set<SOCKET> recv_select_set, send_select_set, error_select_set; - if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) { - interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); - return; - } - - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - - for (SOCKET hSocket : recv_select_set) { - FD_SET(hSocket, &fdsetRecv); - hSocketMax = std::max(hSocketMax, hSocket); - } - - for (SOCKET hSocket : send_select_set) { - FD_SET(hSocket, &fdsetSend); - hSocketMax = std::max(hSocketMax, hSocket); - } - - for (SOCKET hSocket : error_select_set) { - FD_SET(hSocket, &fdsetError); - hSocketMax = std::max(hSocketMax, hSocket); - } - - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - - if (interruptNet) - return; - - if (nSelect == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); - for (unsigned int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - if (!interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS))) - return; - } - - for (SOCKET hSocket : recv_select_set) { - if (FD_ISSET(hSocket, &fdsetRecv)) { - recv_set.insert(hSocket); + requested = Sock::SEND; + } else if (select_recv) { + requested = Sock::RECV; } - } - for (SOCKET hSocket : send_select_set) { - if (FD_ISSET(hSocket, &fdsetSend)) { - send_set.insert(hSocket); - } + events_per_sock.emplace(pnode->m_sock, Sock::Events{requested}); } - for (SOCKET hSocket : error_select_set) { - if (FD_ISSET(hSocket, &fdsetError)) { - error_set.insert(hSocket); - } - } + return events_per_sock; } -#endif void CConnman::SocketHandler() { - std::set<SOCKET> recv_set; - std::set<SOCKET> send_set; - std::set<SOCKET> error_set; + AssertLockNotHeld(m_total_bytes_sent_mutex); + + Sock::EventsPerSock events_per_sock; { const NodesSnapshot snap{*this, /*shuffle=*/false}; + const auto timeout = std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS); + // Check for the readiness of the already connected sockets and the // listening sockets in one call ("readiness" as in poll(2) or // select(2)). If none are ready, wait for a short while and return // empty sets. - SocketEvents(snap.Nodes(), recv_set, send_set, error_set); + events_per_sock = GenerateWaitSockets(snap.Nodes()); + if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) { + interruptNet.sleep_for(timeout); + } // Service (send/receive) each of the already connected nodes. - SocketHandlerConnected(snap.Nodes(), recv_set, send_set, error_set); + SocketHandlerConnected(snap.Nodes(), events_per_sock); } // Accept new connections from listening sockets. - SocketHandlerListening(recv_set); + SocketHandlerListening(events_per_sock); } void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes, - const std::set<SOCKET>& recv_set, - const std::set<SOCKET>& send_set, - const std::set<SOCKET>& error_set) + const Sock::EventsPerSock& events_per_sock) { + AssertLockNotHeld(m_total_bytes_sent_mutex); + for (CNode* pnode : nodes) { if (interruptNet) return; @@ -1608,9 +1265,12 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes, if (!pnode->m_sock) { continue; } - recvSet = recv_set.count(pnode->m_sock->Get()) > 0; - sendSet = send_set.count(pnode->m_sock->Get()) > 0; - errorSet = error_set.count(pnode->m_sock->Get()) > 0; + const auto it = events_per_sock.find(pnode->m_sock); + if (it != events_per_sock.end()) { + recvSet = it->second.occurred & Sock::RECV; + sendSet = it->second.occurred & Sock::SEND; + errorSet = it->second.occurred & Sock::ERR; + } } if (recvSet || errorSet) { @@ -1680,13 +1340,14 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes, } } -void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set) +void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock) { for (const ListenSocket& listen_socket : vhListenSocket) { if (interruptNet) { return; } - if (recv_set.count(listen_socket.sock->Get()) > 0) { + const auto it = events_per_sock.find(listen_socket.sock); + if (it != events_per_sock.end() && it->second.occurred & Sock::RECV) { AcceptConnection(listen_socket); } } @@ -1694,6 +1355,8 @@ void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set) void CConnman::ThreadSocketHandler() { + AssertLockNotHeld(m_total_bytes_sent_mutex); + SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET); while (!interruptNet) { @@ -1805,9 +1468,8 @@ void CConnman::ThreadDNSAddressSeed() unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed if (LookupHost(host, vIPs, nMaxIPs, true)) { for (const CNetAddr& ip : vIPs) { - int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); - addr.nTime = GetTime() - 3*nOneDay - rng.randrange(4*nOneDay); // use a random age between 3 and 7 days old + addr.nTime = rng.rand_uniform_delay(Now<NodeSeconds>() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } @@ -1858,7 +1520,13 @@ bool CConnman::GetTryNewOutboundPeer() const void CConnman::SetTryNewOutboundPeer(bool flag) { m_try_another_outbound_peer = flag; - LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false"); + LogPrint(BCLog::NET, "setting try another outbound peer=%s\n", flag ? "true" : "false"); +} + +void CConnman::StartExtraBlockRelayPeers() +{ + LogPrint(BCLog::NET, "enabling extra block-relay-only peers\n"); + m_start_extra_block_relay_peers = true; } // Return the number of peers we have over our outbound connection limit @@ -1898,6 +1566,7 @@ int CConnman::GetExtraBlockRelayCount() const void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION); + FastRandomContext rng; // Connect to specific addresses if (!connect.empty()) { @@ -2001,7 +1670,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) case ConnectionType::BLOCK_RELAY: case ConnectionType::ADDR_FETCH: case ConnectionType::FEELER: - setConnected.insert(pnode->addr.GetGroup(addrman.GetAsmap())); + setConnected.insert(m_netgroupman.GetGroup(pnode->addr)); } // no default case, so the compiler can warn about missing cases } } @@ -2066,7 +1735,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) addrman.ResolveCollisions(); - int64_t nANow = GetAdjustedTime(); + const auto nANow{AdjustedTime()}; int nTries = 0; while (!interruptNet) { @@ -2075,7 +1744,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) m_anchors.pop_back(); if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) || !HasAllDesirableServiceFlags(addr.nServices) || - setConnected.count(addr.GetGroup(addrman.GetAsmap()))) continue; + setConnected.count(m_netgroupman.GetGroup(addr))) continue; addrConnect = addr; LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString()); break; @@ -2089,7 +1758,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) break; CAddress addr; - int64_t addr_last_try{0}; + NodeSeconds addr_last_try{0s}; if (fFeeler) { // First, try to get a tried table collision address. This returns @@ -2116,7 +1785,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) } // Require outbound connections, other than feelers, to be to distinct network groups - if (!fFeeler && setConnected.count(addr.GetGroup(addrman.GetAsmap()))) { + if (!fFeeler && setConnected.count(m_netgroupman.GetGroup(addr))) { break; } @@ -2129,8 +1798,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) continue; // only consider very recently tried nodes after 30 failed attempts - if (nANow - addr_last_try < 600 && nTries < 30) + if (nANow - addr_last_try < 10min && nTries < 30) { continue; + } // for non-feelers, require all the services we'll want, // for feelers, only require they be a full node (only because most @@ -2151,12 +1821,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) } if (addrConnect.IsValid()) { - if (fFeeler) { // Add small amount of random noise before connection to avoid synchronization. - int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); - if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep))) + if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) { return; + } LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString()); } @@ -2289,7 +1958,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (grantOutbound) grantOutbound->MoveTo(pnode->grantOutbound); - m_msgproc->InitializeNode(pnode); + m_msgproc->InitializeNode(*pnode, nLocalServices); { LOCK(m_nodes_mutex); m_nodes.push_back(pnode); @@ -2385,15 +2054,15 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { - strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString()); - LogPrintf("%s\n", strError.original); + strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToString()); + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original); return false; } std::unique_ptr<Sock> sock = CreateSock(addrBind); if (!sock) { - strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError.original); + strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original); return false; } @@ -2422,23 +2091,22 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, #endif } - if (::bind(sock->Get(), (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) - { + if (sock->Bind(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME); else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); - LogPrintf("%s\n", strError.original); + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); // Listen for incoming connections - if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR) + if (sock->Listen(SOMAXCONN) == SOCKET_ERROR) { - strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError.original); + strError = strprintf(_("Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original); return false; } @@ -2512,8 +2180,12 @@ void CConnman::SetNetworkActive(bool active) } } -CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, bool network_active) - : addrman(addrman_in), nSeed0(nSeed0In), nSeed1(nSeed1In) +CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, + const NetGroupManager& netgroupman, bool network_active) + : addrman(addrman_in) + , m_netgroupman{netgroupman} + , nSeed0(nSeed0In) + , nSeed1(nSeed1In) { SetTryNewOutboundPeer(false); @@ -2574,6 +2246,7 @@ bool CConnman::InitBinds(const Options& options) bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) { + AssertLockNotHeld(m_total_bytes_sent_mutex); Init(connOptions); if (fListen && !InitBinds(connOptions)) { @@ -2674,7 +2347,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) class CNetCleanup { public: - CNetCleanup() {} + CNetCleanup() = default; ~CNetCleanup() { @@ -2788,8 +2461,11 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres { auto local_socket_bytes = requestor.addrBind.GetAddrBytes(); uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE) - .Write(requestor.addr.GetNetwork()) + .Write(requestor.ConnectedThroughNetwork()) .Write(local_socket_bytes.data(), local_socket_bytes.size()) + // 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(); const auto current_time = GetTime<std::chrono::microseconds>(); auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{}); @@ -2872,7 +2548,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const for (CNode* pnode : m_nodes) { vstats.emplace_back(); pnode->CopyStats(vstats.back()); - vstats.back().m_mapped_as = pnode->addr.GetMappedAS(addrman.GetAsmap()); + vstats.back().m_mapped_as = m_netgroupman.GetMappedAS(pnode->addr); } } @@ -2926,7 +2602,9 @@ void CConnman::RecordBytesRecv(uint64_t bytes) void CConnman::RecordBytesSent(uint64_t bytes) { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); + nTotalBytesSent += bytes; const auto now = GetTime<std::chrono::seconds>(); @@ -2942,7 +2620,8 @@ void CConnman::RecordBytesSent(uint64_t bytes) uint64_t CConnman::GetMaxOutboundTarget() const { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); return nMaxOutboundLimit; } @@ -2953,7 +2632,15 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); + return GetMaxOutboundTimeLeftInCycle_(); +} + +std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle_() const +{ + AssertLockHeld(m_total_bytes_sent_mutex); + if (nMaxOutboundLimit == 0) return 0s; @@ -2967,14 +2654,15 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); if (nMaxOutboundLimit == 0) return false; if (historicalBlockServingLimit) { // keep a large enough buffer to at least relay each block once - const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); + const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle_(); const uint64_t buffer = timeLeftInCycle / std::chrono::minutes{10} * MAX_BLOCK_SERIALIZED_SIZE; if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) return true; @@ -2987,7 +2675,8 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const uint64_t CConnman::GetOutboundTargetBytesLeft() const { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); if (nMaxOutboundLimit == 0) return 0; @@ -3001,7 +2690,8 @@ uint64_t CConnman::GetTotalBytesRecv() const uint64_t CConnman::GetTotalBytesSent() const { - LOCK(cs_totalBytesSent); + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); return nTotalBytesSent; } @@ -3012,7 +2702,10 @@ ServiceFlags CConnman::GetLocalServices() const unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } -CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> sock, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion) +CNode::CNode(NodeId idIn, std::shared_ptr<Sock> sock, const CAddress& addrIn, + uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, + const CAddress& addrBindIn, const std::string& addrNameIn, + ConnectionType conn_type_in, bool inbound_onion) : m_sock{sock}, m_connected{GetTime<std::chrono::seconds>()}, addr(addrIn), @@ -3022,14 +2715,13 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s nKeyedNetGroup(nKeyedNetGroupIn), id(idIn), nLocalHostNonce(nLocalHostNonceIn), - m_conn_type(conn_type_in), - nLocalServices(nLocalServicesIn) + m_conn_type(conn_type_in) { if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND); for (const std::string &msg : getAllNetMessageTypes()) - mapRecvBytesPerMsgCmd[msg] = 0; - mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; + mapRecvBytesPerMsgType[msg] = 0; + mapRecvBytesPerMsgType[NET_MESSAGE_TYPE_OTHER] = 0; if (fLogIPs) { LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", m_addr_name, id); @@ -3048,6 +2740,7 @@ bool CConnman::NodeFullyConnected(const CNode* pnode) void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) { + AssertLockNotHeld(m_total_bytes_sent_mutex); size_t nMessageSize = msg.data.size(); LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId()); if (gArgs.GetBoolArg("-capturemessages", false)) { @@ -3074,7 +2767,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) bool optimisticSend(pnode->vSendMsg.empty()); //log total amount of bytes per message type - pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize; + pnode->mapSendBytesPerMsgType[msg.m_type] += nTotalSize; pnode->nSendSize += nTotalSize; if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true; @@ -3105,9 +2798,9 @@ CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const return CSipHasher(nSeed0, nSeed1).Write(id); } -uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const +uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& address) const { - std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.GetAsmap())); + std::vector<unsigned char> vchNetGroup(m_netgroupman.GetGroup(address)); return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize(); } @@ -3127,11 +2820,11 @@ void CaptureMessageToFile(const CAddress& addr, std::string clean_addr = addr.ToString(); std::replace(clean_addr.begin(), clean_addr.end(), ':', '_'); - fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr; + fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr); fs::create_directories(base_path); fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat"); - CAutoFile f(fsbridge::fopen(path, "ab"), SER_DISK, CLIENT_VERSION); + AutoFile f{fsbridge::fopen(path, "ab")}; ser_writedata64(f, now.count()); f.write(MakeByteSpan(msg_type)); |