aboutsummaryrefslogtreecommitdiff
path: root/src/net.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/net.h')
-rw-r--r--src/net.h231
1 files changed, 167 insertions, 64 deletions
diff --git a/src/net.h b/src/net.h
index c72eada3ff..7c63516394 100644
--- a/src/net.h
+++ b/src/net.h
@@ -10,12 +10,14 @@
#include <addrman.h>
#include <amount.h>
#include <bloom.h>
+#include <chainparams.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <hash.h>
#include <limitedmap.h>
-#include <netaddress.h>
#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -114,16 +116,75 @@ struct CSerializedNetMsg
std::string m_type;
};
+const std::vector<std::string> CONNECTION_TYPE_DOC{
+ "outbound-full-relay (default automatic connections)",
+ "block-relay-only (does not relay transactions or addresses)",
+ "inbound (initiated by the peer)",
+ "manual (added via addnode RPC or -addnode/-connect configuration options)",
+ "addr-fetch (short-lived automatic connection for soliciting addresses)",
+ "feeler (short-lived automatic connection for testing addresses)"};
+
/** 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. */
enum class ConnectionType {
- INBOUND, /**< peer initiated connections */
- OUTBOUND, /**< full relay connections (blocks, addrs, txns) made automatically. Addresses selected from AddrMan. */
- MANUAL, /**< connections to addresses added via addnode or the connect command line argument */
- FEELER, /**< short lived connections used to test address validity */
- BLOCK_RELAY, /**< only relay blocks to these automatic outbound connections. Addresses selected from AddrMan. */
- ADDR_FETCH, /**< short lived connections used to solicit addrs when starting the node without a populated AddrMan */
+ /**
+ * 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 inputted
+ * via the addnode RPC, or the -connect command line argument. 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 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_CONNECTIONS using addresses from our AddrMan.
+ */
+ 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,
};
class NetEventsInterface;
@@ -159,6 +220,7 @@ public:
std::vector<NetWhitelistPermissions> vWhitelistedRange;
std::vector<NetWhitebindPermissions> vWhiteBinds;
std::vector<CService> vBinds;
+ std::vector<CService> onion_binds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -191,6 +253,7 @@ public:
LOCK(cs_vAddedNodes);
vAddedNodes = connOptions.m_added_nodes;
}
+ m_onion_binds = connOptions.onion_binds;
}
CConnman(uint64_t seed0, uint64_t seed1, bool network_active = true);
@@ -209,15 +272,15 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, ConnectionType conn_type = ConnectionType::OUTBOUND);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
- template<typename Callable>
- void ForEachNode(Callable&& func)
+ using NodeFn = std::function<void(CNode*)>;
+ void ForEachNode(const NodeFn& func)
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
@@ -226,8 +289,7 @@ public:
}
};
- template<typename Callable>
- void ForEachNode(Callable&& func) const
+ void ForEachNode(const NodeFn& func) const
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
@@ -269,7 +331,7 @@ public:
* A non-malicious call (from RPC or a peer with addr permission) should
* call the function without a parameter to avoid using the cache.
*/
- std::vector<CAddress> GetAddresses(Network requestor_network, size_t max_addresses, size_t max_pct);
+ std::vector<CAddress> GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct);
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
@@ -357,7 +419,11 @@ private:
bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
+ bool InitBinds(
+ const std::vector<CService>& binds,
+ const std::vector<NetWhitebindPermissions>& whiteBinds,
+ const std::vector<CService>& onion_binds);
+
void ThreadOpenAddedConnections();
void AddAddrFetch(const std::string& strDest);
void ProcessAddrFetch();
@@ -442,20 +508,24 @@ private:
*/
struct CachedAddrResponse {
std::vector<CAddress> m_addrs_response_cache;
- std::chrono::microseconds m_update_addr_response{0};
+ std::chrono::microseconds m_cache_entry_expiration{0};
};
/**
* Addr responses stored in different caches
- * per network prevent cross-network node identification.
+ * per (network, local socket) prevent cross-network node identification.
* If a node for example is multi-homed under Tor and IPv6,
* a single cache (or no cache at all) would let an attacker
* to easily detect that it is the same node by comparing responses.
- * The used memory equals to 1000 CAddress records (or around 32 bytes) per
+ * Indexing by local socket prevents leakage when a node has multiple
+ * listening addresses on the same network.
+ *
+ * The used memory equals to 1000 CAddress records (or around 40 bytes) per
* distinct Network (up to 5) we have/had an inbound peer from,
- * resulting in at most ~160 KB.
+ * resulting in at most ~196 KB. Every separate local socket may
+ * add up to ~196 KB extra.
*/
- std::map<Network, CachedAddrResponse> m_addr_response_caches;
+ std::map<uint64_t, CachedAddrResponse> m_addr_response_caches;
/**
* Services this instance offers.
@@ -517,6 +587,12 @@ private:
std::atomic<int64_t> m_next_send_inv_to_incoming{0};
+ /**
+ * A vector of -bind=<address>:<port>=onion arguments each of which is
+ * an address and port that are designated for incoming Tor connections.
+ */
+ std::vector<CService> m_onion_binds;
+
friend struct CConnmanTest;
friend struct ConnmanTestMsg;
};
@@ -526,21 +602,6 @@ void InterruptMapPort();
void StopMapPort();
uint16_t GetListenPort();
-struct CombinerAll
-{
- typedef bool result_type;
-
- template<typename I>
- bool operator()(I first, I last) const
- {
- while (first != last) {
- if (!(*first)) return false;
- ++first;
- }
- return true;
- }
-};
-
/**
* Interface for message handling
*/
@@ -646,6 +707,7 @@ public:
// Bind address of our side of the connection
CAddress addrBind;
uint32_t m_mapped_as;
+ std::string m_conn_type_string;
};
@@ -658,11 +720,8 @@ class CNetMessage {
public:
CDataStream m_recv; //!< received message data
std::chrono::microseconds m_time{0}; //!< time of message receipt
- bool m_valid_netmagic = false;
- bool m_valid_header = false;
- bool m_valid_checksum = false;
- uint32_t m_message_size{0}; //!< size of the payload
- uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
+ uint32_t m_message_size{0}; //!< size of the payload
+ uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
std::string m_command;
CNetMessage(CDataStream&& recv_in) : m_recv(std::move(recv_in)) {}
@@ -686,13 +745,15 @@ public:
// read and deserialize data
virtual int Read(const char *data, unsigned int bytes) = 0;
// decomposes a message from the context
- virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) = 0;
+ virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
virtual ~TransportDeserializer() {}
};
class V1TransportDeserializer final : public TransportDeserializer
{
private:
+ const CChainParams& m_chain_params;
+ const NodeId m_node_id; // Only for logging
mutable CHash256 hasher;
mutable uint256 data_hash;
bool in_data; // parsing header (false) or data (true)
@@ -718,8 +779,12 @@ private:
}
public:
-
- V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
+ V1TransportDeserializer(const CChainParams& chain_params, const NodeId node_id, int nTypeIn, int nVersionIn)
+ : m_chain_params(chain_params),
+ m_node_id(node_id),
+ hdrbuf(nTypeIn, nVersionIn),
+ vRecv(nTypeIn, nVersionIn)
+ {
Reset();
}
@@ -739,7 +804,7 @@ public:
if (ret < 0) Reset();
return ret;
}
- CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) override;
+ Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -785,7 +850,6 @@ public:
std::deque<CInv> vRecvGetData;
uint64_t nRecvBytes GUARDED_BY(cs_vRecv){0};
- std::atomic<int> nRecvVersion{INIT_PROTO_VERSION};
std::atomic<int64_t> nLastSend{0};
std::atomic<int64_t> nLastRecv{0};
@@ -810,6 +874,11 @@ public:
bool m_legacyWhitelisted{false};
bool fClient{false}; // set by version message
bool m_limited_node{false}; //after BIP159, set by version message
+ /**
+ * Whether the peer has signaled support for receiving ADDRv2 (BIP155)
+ * messages, implying a preference to receive ADDRv2 instead of ADDR ones.
+ */
+ std::atomic_bool m_wants_addrv2{false};
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
@@ -823,8 +892,8 @@ public:
std::atomic_bool fPauseSend{false};
bool IsOutboundOrBlockRelayConn() const {
- switch(m_conn_type) {
- case ConnectionType::OUTBOUND:
+ switch (m_conn_type) {
+ case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
return true;
case ConnectionType::INBOUND:
@@ -832,13 +901,13 @@ public:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
return false;
- }
+ } // no default case, so the compiler can warn about missing cases
assert(false);
}
bool IsFullOutboundConn() const {
- return m_conn_type == ConnectionType::OUTBOUND;
+ return m_conn_type == ConnectionType::OUTBOUND_FULL_RELAY;
}
bool IsManualConn() const {
@@ -861,21 +930,39 @@ public:
return m_conn_type == ConnectionType::INBOUND;
}
+ /* Whether we send addr messages over this connection */
+ bool RelayAddrsWithConn() const
+ {
+ return m_conn_type != ConnectionType::BLOCK_RELAY;
+ }
+
bool ExpectServicesFromConn() const {
- switch(m_conn_type) {
+ switch (m_conn_type) {
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
case ConnectionType::FEELER:
return false;
- case ConnectionType::OUTBOUND:
+ case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
return true;
- }
+ } // no default case, so the compiler can warn about missing cases
assert(false);
}
+ /**
+ * Get network the peer connected through.
+ *
+ * Returns Network::NET_ONION for *inbound* onion connections,
+ * and CNetAddr::GetNetClass() otherwise. The latter cannot be used directly
+ * because it doesn't detect the former, and it's not the responsibility of
+ * the CNetAddr class to know the actual network a peer is connected through.
+ *
+ * @return network the peer connected through.
+ */
+ Network ConnectedThroughNetwork() const;
+
protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
@@ -886,13 +973,11 @@ public:
// flood relay
std::vector<CAddress> vAddrToSend;
- std::unique_ptr<CRollingBloomFilter> m_addr_known = nullptr;
+ std::unique_ptr<CRollingBloomFilter> m_addr_known{nullptr};
bool fGetAddr{false};
std::chrono::microseconds m_next_addr_send GUARDED_BY(cs_sendProcessing){0};
std::chrono::microseconds m_next_local_addr_send GUARDED_BY(cs_sendProcessing){0};
- bool IsAddrRelayPeer() const { return m_addr_known != nullptr; }
-
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
@@ -932,8 +1017,17 @@ public:
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
- // Block and TXN accept times
+ /** 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
+ * preliminary validity checks and was saved to disk, even if we don't
+ * connect the block or it eventually fails connection. Used as an inbound
+ * peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastBlockTime{0};
+
+ /** UNIX epoch time of the last transaction received from this peer that we
+ * had not yet seen (e.g. not already received from another peer) and that
+ * was accepted into our mempool. Used as an inbound peer eviction criterium
+ * in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
// Ping time measurement:
@@ -950,7 +1044,7 @@ public:
std::set<uint256> orphan_work_set;
- CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -959,6 +1053,7 @@ private:
const NodeId id;
const uint64_t nLocalHostNonce;
const ConnectionType m_conn_type;
+ std::atomic<int> m_greatest_common_version{INIT_PROTO_VERSION};
//! Services offered to this peer.
//!
@@ -978,7 +1073,6 @@ private:
const ServiceFlags nLocalServices;
const int nMyStartingHeight;
- int nSendVersion{0};
NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
@@ -988,6 +1082,10 @@ private:
// Our address, as reported by the peer
CService addrLocal GUARDED_BY(cs_addrLocal);
mutable RecursiveMutex cs_addrLocal;
+
+ //! Whether this peer connected via our Tor onion service.
+ const bool m_inbound_onion{false};
+
public:
NodeId GetId() const {
@@ -1010,16 +1108,14 @@ public:
bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete);
- void SetRecvVersion(int nVersionIn)
+ void SetCommonVersion(int greatest_common_version)
{
- nRecvVersion = nVersionIn;
+ m_greatest_common_version = greatest_common_version;
}
- int GetRecvVersion() const
+ int GetCommonVersion() const
{
- return nRecvVersion;
+ return m_greatest_common_version;
}
- void SetSendVersion(int nVersionIn);
- int GetSendVersion() const;
CService GetAddrLocal() const;
//! May not be called more than once
@@ -1046,11 +1142,16 @@ public:
void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
{
+ // Whether the peer supports the address in `_addr`. For example,
+ // nodes that do not implement BIP155 cannot receive Tor v3 addresses
+ // because they require ADDRv2 (BIP155) encoding.
+ const bool addr_format_supported = m_wants_addrv2 || _addr.IsAddrV1Compatible();
+
// Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added
// after addresses were pushed.
assert(m_addr_known);
- if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey())) {
+ if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey()) && addr_format_supported) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
@@ -1089,6 +1190,8 @@ public:
std::string GetAddrName() const;
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);
+
+ std::string ConnectionTypeAsString() const;
};
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */