0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-03-05 14:06:27 -05:00

tracing: add inbound connection tracepoint

This commit is contained in:
0xb10c 2022-05-02 16:26:22 +02:00 committed by 0xb10c
parent 1172bc4157
commit 85b2603eec
No known key found for this signature in database
GPG key ID: E2FFD5B1D88CA97D
3 changed files with 115 additions and 1 deletions

View file

@ -93,6 +93,18 @@ to user-space in full. Messages longer than a 32kb might be cut off. This can
be detected in tracing scripts by comparing the message size to the length of be detected in tracing scripts by comparing the message size to the length of
the passed message. the passed message.
#### Tracepoint `net:inbound_connection`
Is called when a new inbound connection is opened to us. Passes information about
the peer and the number of inbound connections including the newly opened connection.
Arguments passed:
1. Peer ID as `int64`
2. Peer address and port (IPv4, IPv6, Tor v3, I2P, ...) as `pointer to C-style String` (max. length 68 characters)
3. Connection Type (inbound, feeler, outbound-full-relay, ...) as `pointer to C-style String` (max. length 20 characters)
4. Network the peer connects from as `uint32` (1 = IPv4, 2 = IPv6, 3 = Onion, 4 = I2P, 5 = CJDNS). See `Network` enum in `netaddress.h`.
5. Number of existing inbound connections as `uint64` including the newly opened inbound connection.
### Context `validation` ### Context `validation`
#### Tracepoint `validation:block_connected` #### Tracepoint `validation:block_connected`

View file

@ -53,6 +53,7 @@
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
TRACEPOINT_SEMAPHORE(net, inbound_connection);
TRACEPOINT_SEMAPHORE(net, outbound_message); TRACEPOINT_SEMAPHORE(net, outbound_message);
/** Maximum number of block-relay-only anchor connections */ /** Maximum number of block-relay-only anchor connections */
@ -1833,6 +1834,12 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
m_nodes.push_back(pnode); m_nodes.push_back(pnode);
} }
LogDebug(BCLog::NET, "connection from %s accepted\n", addr.ToStringAddrPort()); LogDebug(BCLog::NET, "connection from %s accepted\n", addr.ToStringAddrPort());
TRACEPOINT(net, inbound_connection,
pnode->GetId(),
pnode->m_addr_name.c_str(),
pnode->ConnectionTypeAsString().c_str(),
pnode->ConnectedThroughNetwork(),
GetNodeCount(ConnectionDirection::In));
// We received a new connection, harvest entropy from the time (and our peer count) // We received a new connection, harvest entropy from the time (and our peer count)
RandAddEvent((uint32_t)id); RandAddEvent((uint32_t)id);

View file

@ -27,6 +27,9 @@ MAX_MSG_TYPE_LENGTH = 20
# larger messanges see contrib/tracing/log_raw_p2p_msgs.py # larger messanges see contrib/tracing/log_raw_p2p_msgs.py
MAX_MSG_DATA_LENGTH = 150 MAX_MSG_DATA_LENGTH = 150
# from net_address.h
NETWORK_TYPE_UNROUTABLE = 0
net_tracepoints_program = """ net_tracepoints_program = """
#include <uapi/linux/ptrace.h> #include <uapi/linux/ptrace.h>
@ -53,6 +56,20 @@ struct p2p_message
u8 msg[MAX_MSG_DATA_LENGTH]; u8 msg[MAX_MSG_DATA_LENGTH];
}; };
struct Connection
{
u64 id;
char addr[MAX_PEER_ADDR_LENGTH];
char type[MAX_PEER_CONN_TYPE_LENGTH];
u32 network;
};
struct NewConnection
{
struct Connection conn;
u64 existing;
};
BPF_PERF_OUTPUT(inbound_messages); BPF_PERF_OUTPUT(inbound_messages);
int trace_inbound_message(struct pt_regs *ctx) { int trace_inbound_message(struct pt_regs *ctx) {
struct p2p_message msg = {}; struct p2p_message msg = {};
@ -78,9 +95,46 @@ int trace_outbound_message(struct pt_regs *ctx) {
outbound_messages.perf_submit(ctx, &msg, sizeof(msg)); outbound_messages.perf_submit(ctx, &msg, sizeof(msg));
return 0; return 0;
}; };
BPF_PERF_OUTPUT(inbound_connections);
int trace_inbound_connection(struct pt_regs *ctx) {
struct NewConnection inbound = {};
void *conn_type_pointer = NULL, *address_pointer = NULL;
bpf_usdt_readarg(1, ctx, &inbound.conn.id);
bpf_usdt_readarg(2, ctx, &address_pointer);
bpf_usdt_readarg(3, ctx, &conn_type_pointer);
bpf_usdt_readarg(4, ctx, &inbound.conn.network);
bpf_usdt_readarg(5, ctx, &inbound.existing);
bpf_probe_read_user_str(&inbound.conn.addr, sizeof(inbound.conn.addr), address_pointer);
bpf_probe_read_user_str(&inbound.conn.type, sizeof(inbound.conn.type), conn_type_pointer);
inbound_connections.perf_submit(ctx, &inbound, sizeof(inbound));
return 0;
};
""" """
class Connection(ctypes.Structure):
_fields_ = [
("id", ctypes.c_uint64),
("addr", ctypes.c_char * MAX_PEER_ADDR_LENGTH),
("conn_type", ctypes.c_char * MAX_PEER_CONN_TYPE_LENGTH),
("network", ctypes.c_uint32),
]
def __repr__(self):
return f"Connection(peer={self.id}, addr={self.addr.decode('utf-8')}, conn_type={self.conn_type.decode('utf-8')}, network={self.network})"
class NewConnection(ctypes.Structure):
_fields_ = [
("conn", Connection),
("existing", ctypes.c_uint64),
]
def __repr__(self):
return f"NewConnection(conn={self.conn}, existing={self.existing})"
class NetTracepointTest(BitcoinTestFramework): class NetTracepointTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 self.num_nodes = 1
@ -92,6 +146,10 @@ class NetTracepointTest(BitcoinTestFramework):
self.skip_if_no_bpf_permissions() self.skip_if_no_bpf_permissions()
def run_test(self): def run_test(self):
self.p2p_message_tracepoint_test()
self.inbound_conn_tracepoint_test()
def p2p_message_tracepoint_test(self):
# Tests the net:inbound_message and net:outbound_message tracepoints # Tests the net:inbound_message and net:outbound_message tracepoints
# See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net # See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net
@ -166,7 +224,44 @@ class NetTracepointTest(BitcoinTestFramework):
bpf.cleanup() bpf.cleanup()
test_node.peer_disconnect()
def inbound_conn_tracepoint_test(self):
self.log.info("hook into the net:inbound_connection tracepoint")
ctx = USDT(pid=self.nodes[0].process.pid)
ctx.enable_probe(probe="net:inbound_connection",
fn_name="trace_inbound_connection")
bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"])
inbound_connections = []
EXPECTED_INBOUND_CONNECTIONS = 2
def handle_inbound_connection(_, data, __):
nonlocal inbound_connections
event = ctypes.cast(data, ctypes.POINTER(NewConnection)).contents
self.log.info(f"handle_inbound_connection(): {event}")
inbound_connections.append(event)
bpf["inbound_connections"].open_perf_buffer(handle_inbound_connection)
self.log.info("connect two P2P test nodes to our bitcoind node")
testnodes = list()
for _ in range(EXPECTED_INBOUND_CONNECTIONS):
testnode = P2PInterface()
self.nodes[0].add_p2p_connection(testnode)
testnodes.append(testnode)
bpf.perf_buffer_poll(timeout=200)
assert_equal(EXPECTED_INBOUND_CONNECTIONS, len(inbound_connections))
for inbound_connection in inbound_connections:
assert inbound_connection.conn.id > 0
assert inbound_connection.existing > 0
assert_equal(b'inbound', inbound_connection.conn.conn_type)
assert_equal(NETWORK_TYPE_UNROUTABLE, inbound_connection.conn.network)
bpf.cleanup()
for node in testnodes:
node.peer_disconnect()
if __name__ == '__main__': if __name__ == '__main__':
NetTracepointTest(__file__).main() NetTracepointTest(__file__).main()