Skip to content

Commit b815cce

Browse files
sipadhruv
andcommitted
net: expose transport types/session IDs of connections in RPC and logs
Co-authored-by: Dhruv Mehta <856960+dhruv@users.noreply.github.com>
1 parent 432a62c commit b815cce

File tree

9 files changed

+104
-4
lines changed

9 files changed

+104
-4
lines changed

src/net.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,9 @@ void CNode::CopyStats(CNodeStats& stats)
667667
LOCK(cs_vRecv);
668668
X(mapRecvBytesPerMsgType);
669669
X(nRecvBytes);
670+
Transport::Info info = m_transport->GetInfo();
671+
stats.m_transport_type = info.transport_type;
672+
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
670673
}
671674
X(m_permission_flags);
672675

@@ -734,6 +737,11 @@ V1Transport::V1Transport(const NodeId node_id, int nTypeIn, int nVersionIn) noex
734737
Reset();
735738
}
736739

740+
Transport::Info V1Transport::GetInfo() const noexcept
741+
{
742+
return {.transport_type = TransportProtocolType::V1, .session_id = {}};
743+
}
744+
737745
int V1Transport::readHeader(Span<const uint8_t> msg_bytes)
738746
{
739747
AssertLockHeld(m_recv_mutex);
@@ -1582,6 +1590,27 @@ size_t V2Transport::GetSendMemoryUsage() const noexcept
15821590
return sizeof(m_send_buffer) + memusage::DynamicUsage(m_send_buffer);
15831591
}
15841592

1593+
Transport::Info V2Transport::GetInfo() const noexcept
1594+
{
1595+
AssertLockNotHeld(m_recv_mutex);
1596+
LOCK(m_recv_mutex);
1597+
if (m_recv_state == RecvState::V1) return m_v1_fallback.GetInfo();
1598+
1599+
Transport::Info info;
1600+
1601+
// Do not report v2 and session ID until the version packet has been received
1602+
// and verified (confirming that the other side very likely has the same keys as us).
1603+
if (m_recv_state != RecvState::KEY_MAYBE_V1 && m_recv_state != RecvState::KEY &&
1604+
m_recv_state != RecvState::GARB_GARBTERM && m_recv_state != RecvState::VERSION) {
1605+
info.transport_type = TransportProtocolType::V2;
1606+
info.session_id = uint256(MakeUCharSpan(m_cipher.GetSessionID()));
1607+
} else {
1608+
info.transport_type = TransportProtocolType::DETECTING;
1609+
}
1610+
1611+
return info;
1612+
}
1613+
15851614
std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
15861615
{
15871616
auto it = node.vSendMsg.begin();

src/net.h

+16
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ class CNodeStats
232232
Network m_network;
233233
uint32_t m_mapped_as;
234234
ConnectionType m_conn_type;
235+
/** Transport protocol type. */
236+
TransportProtocolType m_transport_type;
237+
/** BIP324 session id string in hex, if any. */
238+
std::string m_session_id;
235239
};
236240

237241

@@ -268,6 +272,15 @@ class Transport {
268272
public:
269273
virtual ~Transport() {}
270274

275+
struct Info
276+
{
277+
TransportProtocolType transport_type;
278+
std::optional<uint256> session_id;
279+
};
280+
281+
/** Retrieve information about this transport. */
282+
virtual Info GetInfo() const noexcept = 0;
283+
271284
// 1. Receiver side functions, for decoding bytes received on the wire into transport protocol
272285
// agnostic CNetMessage (message type & payload) objects.
273286

@@ -426,6 +439,8 @@ class V1Transport final : public Transport
426439
return WITH_LOCK(m_recv_mutex, return CompleteInternal());
427440
}
428441

442+
Info GetInfo() const noexcept override;
443+
429444
bool ReceivedBytes(Span<const uint8_t>& msg_bytes) override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex)
430445
{
431446
AssertLockNotHeld(m_recv_mutex);
@@ -664,6 +679,7 @@ class V2Transport final : public Transport
664679

665680
// Miscellaneous functions.
666681
bool ShouldReconnectV1() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex, !m_send_mutex);
682+
Info GetInfo() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex);
667683
};
668684

669685
struct CNodeOptions

src/net_processing.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -3585,13 +3585,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
35853585
return;
35863586
}
35873587

3588-
if (!pfrom.IsInboundConn()) {
3588+
// Log succesful connections unconditionally for outbound, but not for inbound as those
3589+
// can be triggered by an attacker at high rate.
3590+
if (!pfrom.IsInboundConn() || LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
35893591
const auto mapped_as{m_connman.GetMappedAS(pfrom.addr)};
3590-
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s%s (%s)\n",
3592+
LogPrintf("New %s %s peer connected: version: %d, blocks=%d, peer=%d%s%s\n",
3593+
pfrom.ConnectionTypeAsString(),
3594+
TransportTypeAsString(pfrom.m_transport->GetInfo().transport_type),
35913595
pfrom.nVersion.load(), peer->m_starting_height,
35923596
pfrom.GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToStringAddrPort()) : ""),
3593-
(mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""),
3594-
pfrom.ConnectionTypeAsString());
3597+
(mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""));
35953598
}
35963599

35973600
if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) {

src/node/connection_types.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,17 @@ std::string ConnectionTypeAsString(ConnectionType conn_type)
2424

2525
assert(false);
2626
}
27+
28+
std::string TransportTypeAsString(TransportProtocolType transport_type)
29+
{
30+
switch (transport_type) {
31+
case TransportProtocolType::DETECTING:
32+
return "detecting";
33+
case TransportProtocolType::V1:
34+
return "v1";
35+
case TransportProtocolType::V2:
36+
return "v2";
37+
} // no default case, so the compiler can warn about missing cases
38+
39+
assert(false);
40+
}

src/node/connection_types.h

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_NODE_CONNECTION_TYPES_H
77

88
#include <string>
9+
#include <stdint.h>
910

1011
/** Different types of connections to a peer. This enum encapsulates the
1112
* information we have available at the time of opening or accepting the
@@ -79,4 +80,14 @@ enum class ConnectionType {
7980
/** Convert ConnectionType enum to a string value */
8081
std::string ConnectionTypeAsString(ConnectionType conn_type);
8182

83+
/** Transport layer version */
84+
enum class TransportProtocolType : uint8_t {
85+
DETECTING, //!< Peer could be v1 or v2
86+
V1, //!< Unencrypted, plaintext protocol
87+
V2, //!< BIP324 protocol
88+
};
89+
90+
/** Convert TransportProtocolType enum to a string value */
91+
std::string TransportTypeAsString(TransportProtocolType transport_type);
92+
8293
#endif // BITCOIN_NODE_CONNECTION_TYPES_H

src/rpc/net.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{
4545
"feeler (short-lived automatic connection for testing addresses)"
4646
};
4747

48+
const std::vector<std::string> TRANSPORT_TYPE_DOC{
49+
"detecting (peer could be v1 or v2)",
50+
"v1 (plaintext transport protocol)",
51+
"v2 (BIP324 encrypted transport protocol)"
52+
};
53+
4854
static RPCHelpMan getconnectioncount()
4955
{
5056
return RPCHelpMan{"getconnectioncount",
@@ -164,6 +170,8 @@ static RPCHelpMan getpeerinfo()
164170
{RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n"
165171
"Please note this output is unlikely to be stable in upcoming releases as we iterate to\n"
166172
"best capture connection behaviors."},
173+
{RPCResult::Type::STR, "transport_protocol_type", "Type of transport protocol: \n" + Join(TRANSPORT_TYPE_DOC, ",\n") + ".\n"},
174+
{RPCResult::Type::STR, "session_id", "The session ID for this connection, or \"\" if there is none (\"v2\" transport protocol only).\n"},
167175
}},
168176
}},
169177
},
@@ -268,6 +276,8 @@ static RPCHelpMan getpeerinfo()
268276
}
269277
obj.pushKV("bytesrecv_per_msg", recvPerMsgType);
270278
obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type));
279+
obj.pushKV("transport_protocol_type", TransportTypeAsString(stats.m_transport_type));
280+
obj.pushKV("session_id", stats.m_session_id);
271281

272282
ret.push_back(obj);
273283
}

src/test/fuzz/p2p_transport_serialization.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
328328
// Make sure all expected messages were received.
329329
assert(expected[0].empty());
330330
assert(expected[1].empty());
331+
332+
// Compare session IDs.
333+
assert(transports[0]->GetInfo().session_id == transports[1]->GetInfo().session_id);
331334
}
332335

333336
std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept

src/test/net_tests.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,14 @@ class V2TransportTester
13211321
SendPacket(contents);
13221322
}
13231323

1324+
/** Test whether the transport's session ID matches the session ID we expect. */
1325+
void CompareSessionIDs() const
1326+
{
1327+
auto info = m_transport.GetInfo();
1328+
BOOST_CHECK(info.session_id);
1329+
BOOST_CHECK(uint256(MakeUCharSpan(m_cipher.GetSessionID())) == *info.session_id);
1330+
}
1331+
13241332
/** Introduce a bit error in the data scheduled to be sent. */
13251333
void Damage()
13261334
{
@@ -1346,6 +1354,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
13461354
BOOST_REQUIRE(ret && ret->empty());
13471355
tester.ReceiveGarbage();
13481356
tester.ReceiveVersion();
1357+
tester.CompareSessionIDs();
13491358
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(100000));
13501359
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
13511360
tester.SendMessage(uint8_t(4), msg_data_1); // cmpctblock short id
@@ -1386,6 +1395,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
13861395
BOOST_REQUIRE(ret && ret->empty());
13871396
tester.ReceiveGarbage();
13881397
tester.ReceiveVersion();
1398+
tester.CompareSessionIDs();
13891399
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(100000));
13901400
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
13911401
tester.SendMessage(uint8_t(14), msg_data_1); // inv short id
@@ -1439,6 +1449,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
14391449
BOOST_REQUIRE(ret && ret->empty());
14401450
tester.ReceiveGarbage();
14411451
tester.ReceiveVersion();
1452+
tester.CompareSessionIDs();
14421453
for (unsigned d = 0; d < num_decoys_1; ++d) {
14431454
auto decoy_data = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(1000));
14441455
tester.SendPacket(/*content=*/decoy_data, /*aad=*/{}, /*ignore=*/true);
@@ -1516,6 +1527,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
15161527
BOOST_REQUIRE(ret && ret->empty());
15171528
tester.ReceiveGarbage();
15181529
tester.ReceiveVersion();
1530+
tester.CompareSessionIDs();
15191531
auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(4000000); // test that receiving 4M payload works
15201532
auto msg_data_2 = g_insecure_rand_ctx.randbytes<uint8_t>(4000000); // test that sending 4M payload works
15211533
tester.SendMessage(uint8_t(InsecureRandRange(223) + 33), {}); // unknown short id

test/functional/rpc_net.py

+2
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,13 @@ def test_getpeerinfo(self):
142142
"relaytxes": False,
143143
"services": "0000000000000000",
144144
"servicesnames": [],
145+
"session_id": "",
145146
"startingheight": -1,
146147
"subver": "",
147148
"synced_blocks": -1,
148149
"synced_headers": -1,
149150
"timeoffset": 0,
151+
"transport_protocol_type": "v1",
150152
"version": 0,
151153
},
152154
)

0 commit comments

Comments
 (0)