0
0
Fork 0
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:
Pieter Wuille 2023-07-30 23:26:04 -04:00
parent 432a62c4dc
commit b815cce50e
9 changed files with 104 additions and 4 deletions

View file

@ -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();

View file

@ -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

View file

@ -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) {

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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,
},
)