diff options
Diffstat (limited to 'src/net.h')
-rw-r--r-- | src/net.h | 439 |
1 files changed, 138 insertions, 301 deletions
@@ -8,7 +8,8 @@ #include <chainparams.h> #include <common/bloom.h> -#include <compat.h> +#include <compat/compat.h> +#include <node/connection_types.h> #include <consensus/amount.h> #include <crypto/siphash.h> #include <hash.h> @@ -16,6 +17,7 @@ #include <net_permissions.h> #include <netaddress.h> #include <netbase.h> +#include <netgroup.h> #include <policy/feerate.h> #include <protocol.h> #include <random.h> @@ -32,6 +34,7 @@ #include <cstdint> #include <deque> #include <functional> +#include <list> #include <map> #include <memory> #include <optional> @@ -99,91 +102,26 @@ struct AddedNodeInfo class CNodeStats; class CClientUIInterface; -struct CSerializedNetMsg -{ +struct CSerializedNetMsg { CSerializedNetMsg() = default; CSerializedNetMsg(CSerializedNetMsg&&) = default; CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; - // No copying, only moves. + // No implicit copying, only moves. CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; + CSerializedNetMsg Copy() const + { + CSerializedNetMsg copy; + copy.data = data; + copy.m_type = m_type; + return copy; + } + std::vector<unsigned char> data; std::string m_type; }; -/** Different types of connections to a peer. This enum encapsulates the - * information we have available at the time of opening or accepting the - * connection. Aside from INBOUND, all types are initiated by us. - * - * If adding or removing types, please update CONNECTION_TYPE_DOC in - * src/rpc/net.cpp and src/qt/rpcconsole.cpp, as well as the descriptions in - * src/qt/guiutil.cpp and src/bitcoin-cli.cpp::NetinfoRequestHandler. */ -enum class ConnectionType { - /** - * Inbound connections are those initiated by a peer. This is the only - * property we know at the time of connection, until P2P messages are - * exchanged. - */ - INBOUND, - - /** - * These are the default connections that we use to connect with the - * network. There is no restriction on what is relayed; by default we relay - * blocks, addresses & transactions. We automatically attempt to open - * MAX_OUTBOUND_FULL_RELAY_CONNECTIONS using addresses from our AddrMan. - */ - OUTBOUND_FULL_RELAY, - - - /** - * We open manual connections to addresses that users explicitly requested - * via the addnode RPC or the -addnode/-connect configuration options. Even if a - * manual connection is misbehaving, we do not automatically disconnect or - * add it to our discouragement filter. - */ - MANUAL, - - /** - * Feeler connections are short-lived connections made to check that a node - * is alive. They can be useful for: - * - test-before-evict: if one of the peers is considered for eviction from - * our AddrMan because another peer is mapped to the same slot in the tried table, - * evict only if this longer-known peer is offline. - * - move node addresses from New to Tried table, so that we have more - * connectable addresses in our AddrMan. - * Note that in the literature ("Eclipse Attacks on Bitcoin’s Peer-to-Peer Network") - * only the latter feature is referred to as "feeler connections", - * although in our codebase feeler connections encompass test-before-evict as well. - * We make these connections approximately every FEELER_INTERVAL: - * first we resolve previously found collisions if they exist (test-before-evict), - * otherwise we connect to a node from the new table. - */ - FEELER, - - /** - * We use block-relay-only connections to help prevent against partition - * attacks. By not relaying transactions or addresses, these connections - * are harder to detect by a third party, thus helping obfuscate the - * network topology. We automatically attempt to open - * MAX_BLOCK_RELAY_ONLY_ANCHORS using addresses from our anchors.dat. Then - * addresses from our AddrMan if MAX_BLOCK_RELAY_ONLY_CONNECTIONS - * isn't reached yet. - */ - BLOCK_RELAY, - - /** - * AddrFetch connections are short lived connections used to solicit - * addresses from peers. These are initiated to addresses submitted via the - * -seednode command line argument, or under certain conditions when the - * AddrMan is empty. - */ - ADDR_FETCH, -}; - -/** Convert ConnectionType enum to a string value */ -std::string ConnectionTypeAsString(ConnectionType conn_type); - /** * Look up IP addresses from all interfaces on the machine and add them to the * list of local addresses to self-advertise. @@ -206,8 +144,8 @@ enum }; bool IsPeerAddrLocalGood(CNode *pnode); -/** Returns a local address that we should advertise to this peer */ -std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode); +/** Returns a local address that we should advertise to this peer. */ +std::optional<CService> GetLocalAddrForPeer(CNode& node); /** * Mark a network as reachable or unreachable (no automatic connects to it) @@ -225,7 +163,7 @@ void RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); -CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); +CService GetLocalAddress(const CNetAddr& addrPeer); extern bool fDiscover; @@ -239,18 +177,16 @@ struct LocalServiceInfo { uint16_t nPort; }; -extern Mutex g_maplocalhost_mutex; +extern GlobalMutex g_maplocalhost_mutex; extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex); -extern const std::string NET_MESSAGE_COMMAND_OTHER; -typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes +extern const std::string NET_MESSAGE_TYPE_OTHER; +using mapMsgTypeSize = std::map</* message type */ std::string, /* total bytes */ uint64_t>; class CNodeStats { public: NodeId nodeid; - ServiceFlags nServices; - bool fRelayTxes; std::chrono::seconds m_last_send; std::chrono::seconds m_last_recv; std::chrono::seconds m_last_tx_time; @@ -265,13 +201,12 @@ public: bool m_bip152_highbandwidth_from; int m_starting_height; uint64_t nSendBytes; - mapMsgCmdSize mapSendBytesPerMsgCmd; + mapMsgTypeSize mapSendBytesPerMsgType; uint64_t nRecvBytes; - mapMsgCmdSize mapRecvBytesPerMsgCmd; - NetPermissionFlags m_permissionFlags; + mapMsgTypeSize mapRecvBytesPerMsgType; + NetPermissionFlags m_permission_flags; std::chrono::microseconds m_last_ping_time; std::chrono::microseconds m_min_ping_time; - CAmount minFeeFilter; // Our address, as reported by the peer std::string addrLocal; // Address of this peer @@ -307,7 +242,7 @@ public: /** The TransportDeserializer takes care of holding and deserializing the * network receive buffer. It can deserialize the network buffer into a - * transport protocol agnostic CNetMessage (command & payload) + * transport protocol agnostic CNetMessage (message type & payload) */ class TransportDeserializer { public: @@ -390,13 +325,20 @@ public: class TransportSerializer { public: // prepare message for transport (header construction, error-correction computation, payload encryption, etc.) - virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) = 0; + virtual void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) const = 0; virtual ~TransportSerializer() {} }; -class V1TransportSerializer : public TransportSerializer { +class V1TransportSerializer : public TransportSerializer { public: - void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) override; + void prepareForTransport(CSerializedNetMsg& msg, std::vector<unsigned char>& header) const override; +}; + +struct CNodeOptions +{ + NetPermissionFlags permission_flags = NetPermissionFlags::None; + std::unique_ptr<i2p::sam::Session> i2p_sam_session = nullptr; + bool prefer_evict = false; }; /** Information about a peer */ @@ -406,11 +348,10 @@ class CNode friend struct ConnmanTestMsg; public: - std::unique_ptr<TransportDeserializer> m_deserializer; - std::unique_ptr<TransportSerializer> m_serializer; + const std::unique_ptr<TransportDeserializer> m_deserializer; // Used only by SocketHandler thread + const std::unique_ptr<const TransportSerializer> m_serializer; - NetPermissionFlags m_permissionFlags{NetPermissionFlags::None}; - std::atomic<ServiceFlags> nServices{NODE_NONE}; + const NetPermissionFlags m_permission_flags; /** * Socket used for communication with the node. @@ -434,7 +375,7 @@ public: RecursiveMutex cs_vProcessMsg; std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg); - size_t nProcessQueueSize{0}; + size_t nProcessQueueSize GUARDED_BY(cs_vProcessMsg){0}; RecursiveMutex cs_sendProcessing; @@ -459,12 +400,10 @@ public: * from the wire. This cleaned string can safely be logged or displayed. */ std::string cleanSubVer GUARDED_BY(m_subver_mutex){}; - bool m_prefer_evict{false}; // This peer is preferred for eviction. + const bool m_prefer_evict{false}; // This peer is preferred for eviction. bool HasPermission(NetPermissionFlags permission) const { - return NetPermissions::HasFlag(m_permissionFlags, permission); + return NetPermissions::HasFlag(m_permission_flags, permission); } - bool fClient{false}; // set by version message - bool m_limited_node{false}; //after BIP159, set by version message /** fSuccessfullyConnected is set to true on receiving VERACK from the peer. */ std::atomic_bool fSuccessfullyConnected{false}; // Setting fDisconnect to true will cause the node to be disconnected the @@ -548,34 +487,18 @@ public: // Peer selected us as (compact blocks) high-bandwidth peer (BIP152) std::atomic<bool> m_bip152_highbandwidth_from{false}; - struct TxRelay { - mutable RecursiveMutex cs_filter; - // We use fRelayTxes for two purposes - - // a) it allows us to not relay tx invs before receiving the peer's version message - // b) the peer may tell us in its version message that we should not relay tx invs - // unless it loads a bloom filter. - bool fRelayTxes GUARDED_BY(cs_filter){false}; - std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter) GUARDED_BY(cs_filter){nullptr}; - - mutable RecursiveMutex cs_tx_inventory; - CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_tx_inventory){50000, 0.000001}; - // Set of transaction ids we still have to announce. - // They are sorted by the mempool before relay, so the order is not important. - std::set<uint256> setInventoryTxToSend; - // Used for BIP35 mempool sending - bool fSendMempool GUARDED_BY(cs_tx_inventory){false}; - // Last time a "MEMPOOL" request was serviced. - std::atomic<std::chrono::seconds> m_last_mempool_req{0s}; - std::chrono::microseconds nNextInvSend{0}; - - /** Minimum fee rate with which to filter inv's to this node */ - std::atomic<CAmount> minFeeFilter{0}; - CAmount lastSentFeeFilter{0}; - std::chrono::microseconds m_next_send_feefilter{0}; - }; + /** Whether this peer provides all services that we want. Used for eviction decisions */ + std::atomic_bool m_has_all_wanted_services{false}; + + /** Whether we should relay transactions to this peer (their version + * message did not include fRelay=false and this is not a block-relay-only + * connection). This only changes from false to true. It will never change + * back to false. Used only in inbound eviction logic. */ + std::atomic_bool m_relays_txs{false}; - // m_tx_relay == nullptr if we're not relaying transactions with this peer - std::unique_ptr<TxRelay> m_tx_relay; + /** Whether this peer has loaded a bloom filter. Used only in inbound + * eviction logic. */ + std::atomic_bool m_bloom_filter_loaded{false}; /** UNIX epoch time of the last block received from this peer that we had * not yet seen (e.g. not already received from another peer), that passed @@ -597,7 +520,16 @@ public: * criterium in CConnman::AttemptToEvictConnection. */ std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()}; - CNode(NodeId id, 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(NodeId id, + 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, + CNodeOptions&& node_opts = {}); CNode(const CNode&) = delete; CNode& operator=(const CNode&) = delete; @@ -624,7 +556,7 @@ public: * @return True if the peer should stay connected, * False if the peer should be disconnected from. */ - bool ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete); + bool ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete) EXCLUSIVE_LOCKS_REQUIRED(!cs_vRecv); void SetCommonVersion(int greatest_common_version) { @@ -636,9 +568,9 @@ public: return m_greatest_common_version; } - CService GetAddrLocal() const LOCKS_EXCLUDED(m_addr_local_mutex); + CService GetAddrLocal() const EXCLUSIVE_LOCKS_REQUIRED(!m_addr_local_mutex); //! May not be called more than once - void SetAddrLocal(const CService& addrLocalIn) LOCKS_EXCLUDED(m_addr_local_mutex); + void SetAddrLocal(const CService& addrLocalIn) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_local_mutex); CNode* AddRef() { @@ -651,31 +583,9 @@ public: nRefCount--; } - void AddKnownTx(const uint256& hash) - { - if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_tx_inventory); - m_tx_relay->filterInventoryKnown.insert(hash); - } - } - - void PushTxInventory(const uint256& hash) - { - if (m_tx_relay == nullptr) return; - LOCK(m_tx_relay->cs_tx_inventory); - if (!m_tx_relay->filterInventoryKnown.contains(hash)) { - m_tx_relay->setInventoryTxToSend.insert(hash); - } - } - - void CloseSocketDisconnect(); + void CloseSocketDisconnect() EXCLUSIVE_LOCKS_REQUIRED(!m_sock_mutex); - void CopyStats(CNodeStats& stats); - - ServiceFlags GetLocalServices() const - { - return nLocalServices; - } + void CopyStats(CNodeStats& stats) EXCLUSIVE_LOCKS_REQUIRED(!m_subver_mutex, !m_addr_local_mutex, !cs_vSend, !cs_vRecv); std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); } @@ -691,31 +601,26 @@ private: const ConnectionType m_conn_type; std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION}; - //! Services offered to this peer. - //! - //! This is supplied by the parent CConnman during peer connection - //! (CConnman::ConnectNode()) from its attribute of the same name. - //! - //! This is const because there is no protocol defined for renegotiating - //! services initially offered to a peer. The set of local services we - //! offer should not change after initialization. - //! - //! An interesting example of this is NODE_NETWORK and initial block - //! download: a node which starts up from scratch doesn't have any blocks - //! to serve, but still advertises NODE_NETWORK because it will eventually - //! fulfill this role after IBD completes. P2P code is written in such a - //! way that it can gracefully handle peers who don't make good on their - //! service advertisements. - const ServiceFlags nLocalServices; - std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread // Our address, as reported by the peer CService addrLocal GUARDED_BY(m_addr_local_mutex); mutable Mutex m_addr_local_mutex; - mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); - mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); + mapMsgTypeSize mapSendBytesPerMsgType GUARDED_BY(cs_vSend); + mapMsgTypeSize mapRecvBytesPerMsgType GUARDED_BY(cs_vRecv); + + /** + * If an I2P session is created per connection (for outbound transient I2P + * connections) then it is stored here so that it can be destroyed when the + * socket is closed. I2P sessions involve a data/transport socket (in `m_sock`) + * and a control socket (in `m_i2p_sam_session`). For transient sessions, once + * the data socket is closed, the control socket is not going to be used anymore + * and is just taking up resources. So better close it as soon as `m_sock` is + * closed. + * Otherwise this unique_ptr is empty. + */ + std::unique_ptr<i2p::sam::Session> m_i2p_sam_session GUARDED_BY(m_sock_mutex); }; /** @@ -725,7 +630,7 @@ class NetEventsInterface { public: /** Initialize a peer (setup state, queue any initial messages) */ - virtual void InitializeNode(CNode* pnode) = 0; + virtual void InitializeNode(CNode& node, ServiceFlags our_services) = 0; /** Handle removal of a peer (clear state) */ virtual void FinalizeNode(const CNode& node) = 0; @@ -789,7 +694,10 @@ public: bool m_i2p_accept_incoming; }; - void Init(const Options& connOptions) { + void Init(const Options& connOptions) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_total_bytes_sent_mutex) + { + AssertLockNotHeld(m_total_bytes_sent_mutex); + nLocalServices = connOptions.nLocalServices; nMaxConnections = connOptions.nMaxConnections; m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections); @@ -805,7 +713,7 @@ public: nReceiveFloodSize = connOptions.nReceiveFloodSize; m_peer_connect_timeout = std::chrono::seconds{connOptions.m_peer_connect_timeout}; { - LOCK(cs_totalBytesSent); + LOCK(m_total_bytes_sent_mutex); nMaxOutboundLimit = connOptions.nMaxOutboundLimit; } vWhitelistedRange = connOptions.vWhitelistedRange; @@ -816,9 +724,12 @@ public: m_onion_binds = connOptions.onion_binds; } - CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, bool network_active = true); + CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman, + bool network_active = true); + ~CConnman(); - bool Start(CScheduler& scheduler, const Options& options); + + bool Start(CScheduler& scheduler, const Options& options) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !m_added_nodes_mutex, !m_addr_fetches_mutex, !mutexMsgProc); void StopThreads(); void StopNodes(); @@ -828,7 +739,7 @@ public: StopNodes(); }; - void Interrupt(); + void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc); bool GetNetworkActive() const { return fNetworkActive; }; bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; }; void SetNetworkActive(bool active); @@ -837,7 +748,7 @@ public: bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func); - void PushMessage(CNode* pnode, CSerializedNetMsg&& msg); + void PushMessage(CNode* pnode, CSerializedNetMsg&& msg) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); using NodeFn = std::function<void(CNode*)>; void ForEachNode(const NodeFn& func) @@ -880,10 +791,7 @@ public: void SetTryNewOutboundPeer(bool flag); bool GetTryNewOutboundPeer() const; - void StartExtraBlockRelayPeers() { - LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); - m_start_extra_block_relay_peers = true; - } + void StartExtraBlockRelayPeers(); // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set @@ -895,9 +803,9 @@ public: // Count the number of block-relay-only peers we have over our limit. int GetExtraBlockRelayCount() const; - bool AddNode(const std::string& node); - bool RemoveAddedNode(const std::string& node); - std::vector<AddedNodeInfo> GetAddedNodeInfo() const; + bool AddNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); + bool RemoveAddedNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); + std::vector<AddedNodeInfo> GetAddedNodeInfo() const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); /** * Attempts to open a connection. Currently only used from tests. @@ -928,31 +836,29 @@ public: //! that peer during `net_processing.cpp:PushNodeVersion()`. ServiceFlags GetLocalServices() const; - uint64_t GetMaxOutboundTarget() const; + uint64_t GetMaxOutboundTarget() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); std::chrono::seconds GetMaxOutboundTimeframe() const; //! check if the outbound target is reached //! if param historicalBlockServingLimit is set true, the function will //! response true if the limit for serving historical blocks has been reached - bool OutboundTargetReached(bool historicalBlockServingLimit) const; + bool OutboundTargetReached(bool historicalBlockServingLimit) const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); //! response the bytes left in the current max outbound cycle //! in case of no limit, it will always response 0 - uint64_t GetOutboundTargetBytesLeft() const; + uint64_t GetOutboundTargetBytesLeft() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); - //! returns the time left in the current max outbound cycle - //! in case of no limit, it will always return 0 - std::chrono::seconds GetMaxOutboundTimeLeftInCycle() const; + std::chrono::seconds GetMaxOutboundTimeLeftInCycle() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); uint64_t GetTotalBytesRecv() const; - uint64_t GetTotalBytesSent() const; + uint64_t GetTotalBytesSent() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); /** Get a unique deterministic randomizer. */ CSipHasher GetDeterministicRandomizer(uint64_t id) const; unsigned int GetReceiveFloodSize() const; - void WakeMessageHandler(); + void WakeMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc); /** Return true if we should disconnect the peer for failing an inactivity check. */ bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const; @@ -971,15 +877,19 @@ private: NetPermissionFlags m_permissions; }; + //! returns the time left in the current max outbound cycle + //! in case of no limit, it will always return 0 + std::chrono::seconds GetMaxOutboundTimeLeftInCycle_() const EXCLUSIVE_LOCKS_REQUIRED(m_total_bytes_sent_mutex); + bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions); bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions); bool InitBinds(const Options& options); - void ThreadOpenAddedConnections(); - void AddAddrFetch(const std::string& strDest); - void ProcessAddrFetch(); - void ThreadOpenConnections(std::vector<std::string> connect); - void ThreadMessageHandler(); + void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex); + void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex); + void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex); + void ThreadOpenConnections(std::vector<std::string> connect) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex); + void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc); void ThreadI2PAcceptIncoming(); void AcceptConnection(const ListenSocket& hListenSocket); @@ -987,12 +897,12 @@ private: * Create a `CNode` object from a socket that has just been accepted and add the node to * the `m_nodes` member. * @param[in] sock Connected socket to communicate with the peer. - * @param[in] permissionFlags The peer's permissions. + * @param[in] permission_flags The peer's permissions. * @param[in] addr_bind The address and port at our side of the connection. * @param[in] addr The address and port at the peer's side of the connection. */ void CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock, - NetPermissionFlags permissionFlags, + NetPermissionFlags permission_flags, const CAddress& addr_bind, const CAddress& addr); @@ -1004,55 +914,32 @@ private: /** * Generate a collection of sockets to check for IO readiness. * @param[in] nodes Select from these nodes' sockets. - * @param[out] recv_set Sockets to check for read readiness. - * @param[out] send_set Sockets to check for write readiness. - * @param[out] error_set Sockets to check for errors. - * @return true if at least one socket is to be checked (the returned set is not empty) + * @return sockets to check for readiness */ - bool GenerateSelectSet(const std::vector<CNode*>& nodes, - std::set<SOCKET>& recv_set, - std::set<SOCKET>& send_set, - std::set<SOCKET>& error_set); - - /** - * Check which sockets are ready for IO. - * @param[in] nodes Select from these nodes' sockets. - * @param[out] recv_set Sockets which are ready for read. - * @param[out] send_set Sockets which are ready for write. - * @param[out] error_set Sockets which have errors. - * This calls `GenerateSelectSet()` to gather a list of sockets to check. - */ - void SocketEvents(const std::vector<CNode*>& nodes, - std::set<SOCKET>& recv_set, - std::set<SOCKET>& send_set, - std::set<SOCKET>& error_set); + Sock::EventsPerSock GenerateWaitSockets(Span<CNode* const> nodes); /** * Check connected and listening sockets for IO readiness and process them accordingly. */ - void SocketHandler(); + void SocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc); /** * Do the read/write for connected sockets that are ready for IO. - * @param[in] nodes Nodes to process. The socket of each node is checked against - * `recv_set`, `send_set` and `error_set`. - * @param[in] recv_set Sockets that are ready for read. - * @param[in] send_set Sockets that are ready for send. - * @param[in] error_set Sockets that have an exceptional condition (error). + * @param[in] nodes Nodes to process. The socket of each node is checked against `what`. + * @param[in] events_per_sock Sockets that are ready for IO. */ void 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) + EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc); /** * Accept incoming connections, one from each read-ready listening socket. - * @param[in] recv_set Sockets that are ready for read. + * @param[in] events_per_sock Sockets that are ready for IO. */ - void SocketHandlerListening(const std::set<SOCKET>& recv_set); + void SocketHandlerListening(const Sock::EventsPerSock& events_per_sock); - void ThreadSocketHandler(); - void ThreadDNSAddressSeed(); + void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc); + void ThreadDNSAddressSeed() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_nodes_mutex); uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; @@ -1080,7 +967,7 @@ private: // Network stats void RecordBytesRecv(uint64_t bytes); - void RecordBytesSent(uint64_t bytes); + void RecordBytesSent(uint64_t bytes) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); /** * Return vector of current BLOCK_RELAY peers. @@ -1091,14 +978,14 @@ private: static bool NodeFullyConnected(const CNode* pnode); // Network usage totals - mutable RecursiveMutex cs_totalBytesSent; + mutable Mutex m_total_bytes_sent_mutex; std::atomic<uint64_t> nTotalBytesRecv{0}; - uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0}; + uint64_t nTotalBytesSent GUARDED_BY(m_total_bytes_sent_mutex) {0}; // outbound limit & stats - uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent) {0}; - std::chrono::seconds nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent) {0}; - uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(m_total_bytes_sent_mutex) {0}; + std::chrono::seconds nMaxOutboundCycleStartTime GUARDED_BY(m_total_bytes_sent_mutex) {0}; + uint64_t nMaxOutboundLimit GUARDED_BY(m_total_bytes_sent_mutex); // P2P timeout in seconds std::chrono::seconds m_peer_connect_timeout; @@ -1114,6 +1001,7 @@ private: std::atomic<bool> fNetworkActive{true}; bool fAddressesInitialized{false}; AddrMan& addrman; + const NetGroupManager& m_netgroupman; std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex); Mutex m_addr_fetches_mutex; std::vector<std::string> m_added_nodes GUARDED_BY(m_added_nodes_mutex); @@ -1152,16 +1040,14 @@ private: std::map<uint64_t, CachedAddrResponse> m_addr_response_caches; /** - * Services this instance offers. + * Services this node offers. * - * This data is replicated in each CNode instance we create during peer - * connection (in ConnectNode()) under a member also called - * nLocalServices. + * This data is replicated in each Peer instance we create. * * This data is not marked const, but after being set it should not - * change. See the note in CNode::nLocalServices documentation. + * change. * - * \sa CNode::nLocalServices + * \sa Peer::our_services */ ServiceFlags nLocalServices; @@ -1211,7 +1097,8 @@ private: /** * I2P SAM session. - * Used to accept incoming and make outgoing I2P connections. + * Used to accept incoming and make outgoing I2P connections from a persistent + * address. */ std::unique_ptr<i2p::sam::Session> m_i2p_sam_session; @@ -1293,54 +1180,4 @@ extern std::function<void(const CAddress& addr, bool is_incoming)> CaptureMessage; -struct NodeEvictionCandidate -{ - NodeId id; - std::chrono::seconds m_connected; - std::chrono::microseconds m_min_ping_time; - std::chrono::seconds m_last_block_time; - std::chrono::seconds m_last_tx_time; - bool fRelevantServices; - bool fRelayTxes; - bool fBloomFilter; - uint64_t nKeyedNetGroup; - bool prefer_evict; - bool m_is_local; - Network m_network; -}; - -/** - * Select an inbound peer to evict after filtering out (protecting) peers having - * distinct, difficult-to-forge characteristics. The protection logic picks out - * fixed numbers of desirable peers per various criteria, followed by (mostly) - * ratios of desirable or disadvantaged peers. If any eviction candidates - * remain, the selection logic chooses a peer to evict. - */ -[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates); - -/** Protect desirable or disadvantaged inbound peers from eviction by ratio. - * - * This function protects half of the peers which have been connected the - * longest, to replicate the non-eviction implicit behavior and preclude attacks - * that start later. - * - * Half of these protected spots (1/4 of the total) are reserved for the - * following categories of peers, sorted by longest uptime, even if they're not - * longest uptime overall: - * - * - onion peers connected via our tor control service - * - * - localhost peers, as manually configured hidden services not using - * `-bind=addr[:port]=onion` will not be detected as inbound onion connections - * - * - I2P peers - * - * - CJDNS peers - * - * This helps protect these privacy network peers, which tend to be otherwise - * disadvantaged under our eviction criteria for their higher min ping times - * relative to IPv4/IPv6 peers, and favorise the diversity of peer connections. - */ -void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates); - #endif // BITCOIN_NET_H |