mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
net: expose transport types/session IDs of connections in RPC and logs
Co-authored-by: Dhruv Mehta <856960+dhruv@users.noreply.github.com>
This commit is contained in:
parent
432a62c4dc
commit
b815cce50e
9 changed files with 104 additions and 4 deletions
29
src/net.cpp
29
src/net.cpp
|
@ -667,6 +667,9 @@ void CNode::CopyStats(CNodeStats& stats)
|
|||
LOCK(cs_vRecv);
|
||||
X(mapRecvBytesPerMsgType);
|
||||
X(nRecvBytes);
|
||||
Transport::Info info = m_transport->GetInfo();
|
||||
stats.m_transport_type = info.transport_type;
|
||||
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
|
||||
}
|
||||
X(m_permission_flags);
|
||||
|
||||
|
@ -734,6 +737,11 @@ V1Transport::V1Transport(const NodeId node_id, int nTypeIn, int nVersionIn) noex
|
|||
Reset();
|
||||
}
|
||||
|
||||
Transport::Info V1Transport::GetInfo() const noexcept
|
||||
{
|
||||
return {.transport_type = TransportProtocolType::V1, .session_id = {}};
|
||||
}
|
||||
|
||||
int V1Transport::readHeader(Span<const uint8_t> msg_bytes)
|
||||
{
|
||||
AssertLockHeld(m_recv_mutex);
|
||||
|
@ -1582,6 +1590,27 @@ size_t V2Transport::GetSendMemoryUsage() const noexcept
|
|||
return sizeof(m_send_buffer) + memusage::DynamicUsage(m_send_buffer);
|
||||
}
|
||||
|
||||
Transport::Info V2Transport::GetInfo() const noexcept
|
||||
{
|
||||
AssertLockNotHeld(m_recv_mutex);
|
||||
LOCK(m_recv_mutex);
|
||||
if (m_recv_state == RecvState::V1) return m_v1_fallback.GetInfo();
|
||||
|
||||
Transport::Info info;
|
||||
|
||||
// Do not report v2 and session ID until the version packet has been received
|
||||
// and verified (confirming that the other side very likely has the same keys as us).
|
||||
if (m_recv_state != RecvState::KEY_MAYBE_V1 && m_recv_state != RecvState::KEY &&
|
||||
m_recv_state != RecvState::GARB_GARBTERM && m_recv_state != RecvState::VERSION) {
|
||||
info.transport_type = TransportProtocolType::V2;
|
||||
info.session_id = uint256(MakeUCharSpan(m_cipher.GetSessionID()));
|
||||
} else {
|
||||
info.transport_type = TransportProtocolType::DETECTING;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
|
||||
{
|
||||
auto it = node.vSendMsg.begin();
|
||||
|
|
16
src/net.h
16
src/net.h
|
@ -232,6 +232,10 @@ public:
|
|||
Network m_network;
|
||||
uint32_t m_mapped_as;
|
||||
ConnectionType m_conn_type;
|
||||
/** Transport protocol type. */
|
||||
TransportProtocolType m_transport_type;
|
||||
/** BIP324 session id string in hex, if any. */
|
||||
std::string m_session_id;
|
||||
};
|
||||
|
||||
|
||||
|
@ -268,6 +272,15 @@ class Transport {
|
|||
public:
|
||||
virtual ~Transport() {}
|
||||
|
||||
struct Info
|
||||
{
|
||||
TransportProtocolType transport_type;
|
||||
std::optional<uint256> session_id;
|
||||
};
|
||||
|
||||
/** Retrieve information about this transport. */
|
||||
virtual Info GetInfo() const noexcept = 0;
|
||||
|
||||
// 1. Receiver side functions, for decoding bytes received on the wire into transport protocol
|
||||
// agnostic CNetMessage (message type & payload) objects.
|
||||
|
||||
|
@ -426,6 +439,8 @@ public:
|
|||
return WITH_LOCK(m_recv_mutex, return CompleteInternal());
|
||||
}
|
||||
|
||||
Info GetInfo() const noexcept override;
|
||||
|
||||
bool ReceivedBytes(Span<const uint8_t>& msg_bytes) override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex)
|
||||
{
|
||||
AssertLockNotHeld(m_recv_mutex);
|
||||
|
@ -664,6 +679,7 @@ public:
|
|||
|
||||
// Miscellaneous functions.
|
||||
bool ShouldReconnectV1() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex, !m_send_mutex);
|
||||
Info GetInfo() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex);
|
||||
};
|
||||
|
||||
struct CNodeOptions
|
||||
|
|
|
@ -3585,13 +3585,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!pfrom.IsInboundConn()) {
|
||||
// Log succesful connections unconditionally for outbound, but not for inbound as those
|
||||
// can be triggered by an attacker at high rate.
|
||||
if (!pfrom.IsInboundConn() || LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
|
||||
const auto mapped_as{m_connman.GetMappedAS(pfrom.addr)};
|
||||
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s%s (%s)\n",
|
||||
LogPrintf("New %s %s peer connected: version: %d, blocks=%d, peer=%d%s%s\n",
|
||||
pfrom.ConnectionTypeAsString(),
|
||||
TransportTypeAsString(pfrom.m_transport->GetInfo().transport_type),
|
||||
pfrom.nVersion.load(), peer->m_starting_height,
|
||||
pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToStringAddrPort()) : ""),
|
||||
(mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""),
|
||||
pfrom.ConnectionTypeAsString());
|
||||
(mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""));
|
||||
}
|
||||
|
||||
if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) {
|
||||
|
|
|
@ -24,3 +24,17 @@ std::string ConnectionTypeAsString(ConnectionType conn_type)
|
|||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
std::string TransportTypeAsString(TransportProtocolType transport_type)
|
||||
{
|
||||
switch (transport_type) {
|
||||
case TransportProtocolType::DETECTING:
|
||||
return "detecting";
|
||||
case TransportProtocolType::V1:
|
||||
return "v1";
|
||||
case TransportProtocolType::V2:
|
||||
return "v2";
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_NODE_CONNECTION_TYPES_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
/** Different types of connections to a peer. This enum encapsulates the
|
||||
* information we have available at the time of opening or accepting the
|
||||
|
@ -79,4 +80,14 @@ enum class ConnectionType {
|
|||
/** Convert ConnectionType enum to a string value */
|
||||
std::string ConnectionTypeAsString(ConnectionType conn_type);
|
||||
|
||||
/** Transport layer version */
|
||||
enum class TransportProtocolType : uint8_t {
|
||||
DETECTING, //!< Peer could be v1 or v2
|
||||
V1, //!< Unencrypted, plaintext protocol
|
||||
V2, //!< BIP324 protocol
|
||||
};
|
||||
|
||||
/** Convert TransportProtocolType enum to a string value */
|
||||
std::string TransportTypeAsString(TransportProtocolType transport_type);
|
||||
|
||||
#endif // BITCOIN_NODE_CONNECTION_TYPES_H
|
||||
|
|
|
@ -45,6 +45,12 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{
|
|||
"feeler (short-lived automatic connection for testing addresses)"
|
||||
};
|
||||
|
||||
const std::vector<std::string> TRANSPORT_TYPE_DOC{
|
||||
"detecting (peer could be v1 or v2)",
|
||||
"v1 (plaintext transport protocol)",
|
||||
"v2 (BIP324 encrypted transport protocol)"
|
||||
};
|
||||
|
||||
static RPCHelpMan getconnectioncount()
|
||||
{
|
||||
return RPCHelpMan{"getconnectioncount",
|
||||
|
@ -164,6 +170,8 @@ static RPCHelpMan getpeerinfo()
|
|||
{RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n"
|
||||
"Please note this output is unlikely to be stable in upcoming releases as we iterate to\n"
|
||||
"best capture connection behaviors."},
|
||||
{RPCResult::Type::STR, "transport_protocol_type", "Type of transport protocol: \n" + Join(TRANSPORT_TYPE_DOC, ",\n") + ".\n"},
|
||||
{RPCResult::Type::STR, "session_id", "The session ID for this connection, or \"\" if there is none (\"v2\" transport protocol only).\n"},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
|
@ -268,6 +276,8 @@ static RPCHelpMan getpeerinfo()
|
|||
}
|
||||
obj.pushKV("bytesrecv_per_msg", recvPerMsgType);
|
||||
obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type));
|
||||
obj.pushKV("transport_protocol_type", TransportTypeAsString(stats.m_transport_type));
|
||||
obj.pushKV("session_id", stats.m_session_id);
|
||||
|
||||
ret.push_back(obj);
|
||||
}
|
||||
|
|
|
@ -328,6 +328,9 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
|
|||
// Make sure all expected messages were received.
|
||||
assert(expected[0].empty());
|
||||
assert(expected[1].empty());
|
||||
|
||||
// Compare session IDs.
|
||||
assert(transports[0]->GetInfo().session_id == transports[1]->GetInfo().session_id);
|
||||
}
|
||||
|
||||
std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
|
||||
|
|
|
@ -1321,6 +1321,14 @@ public:
|
|||
SendPacket(contents);
|
||||
}
|
||||
|
||||
/** Test whether the transport's session ID matches the session ID we expect. */
|
||||
void CompareSessionIDs() const
|
||||
{
|
||||
auto info = m_transport.GetInfo();
|
||||
BOOST_CHECK(info.session_id);
|
||||
BOOST_CHECK(uint256(MakeUCharSpan(m_cipher.GetSessionID())) == *info.session_id);
|
||||
}
|
||||
|
||||
/** Introduce a bit error in the data scheduled to be sent. */
|
||||
void Damage()
|
||||
{
|
||||
|
@ -1346,6 +1354,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
|
|||
BOOST_REQUIRE(ret && ret->empty());
|
||||
tester.ReceiveGarbage();
|
||||
tester.ReceiveVersion();
|
||||
tester.CompareSessionIDs();
|
||||
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(100000));
|
||||
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
|
||||
tester.SendMessage(uint8_t(4), msg_data_1); // cmpctblock short id
|
||||
|
@ -1386,6 +1395,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
|
|||
BOOST_REQUIRE(ret && ret->empty());
|
||||
tester.ReceiveGarbage();
|
||||
tester.ReceiveVersion();
|
||||
tester.CompareSessionIDs();
|
||||
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(100000));
|
||||
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
|
||||
tester.SendMessage(uint8_t(14), msg_data_1); // inv short id
|
||||
|
@ -1439,6 +1449,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
|
|||
BOOST_REQUIRE(ret && ret->empty());
|
||||
tester.ReceiveGarbage();
|
||||
tester.ReceiveVersion();
|
||||
tester.CompareSessionIDs();
|
||||
for (unsigned d = 0; d < num_decoys_1; ++d) {
|
||||
auto decoy_data = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
|
||||
tester.SendPacket(/*content=*/decoy_data, /*aad=*/{}, /*ignore=*/true);
|
||||
|
@ -1516,6 +1527,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
|
|||
BOOST_REQUIRE(ret && ret->empty());
|
||||
tester.ReceiveGarbage();
|
||||
tester.ReceiveVersion();
|
||||
tester.CompareSessionIDs();
|
||||
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(4000000); // test that receiving 4M payload works
|
||||
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(4000000); // test that sending 4M payload works
|
||||
tester.SendMessage(uint8_t(InsecureRandRange(223) + 33), {}); // unknown short id
|
||||
|
|
|
@ -142,11 +142,13 @@ class NetTest(BitcoinTestFramework):
|
|||
"relaytxes": False,
|
||||
"services": "0000000000000000",
|
||||
"servicesnames": [],
|
||||
"session_id": "",
|
||||
"startingheight": -1,
|
||||
"subver": "",
|
||||
"synced_blocks": -1,
|
||||
"synced_headers": -1,
|
||||
"timeoffset": 0,
|
||||
"transport_protocol_type": "v1",
|
||||
"version": 0,
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue