1111from io import BytesIO
1212# Test will be skipped if we don't have bcc installed
1313try :
14- from bcc import BPF , USDT # type: ignore[import]
14+ from bcc import BPF , USDT # type: ignore[import]
1515except ImportError :
1616 pass
1717from test_framework .messages import msg_version
2727# larger messanges see contrib/tracing/log_raw_p2p_msgs.py
2828MAX_MSG_DATA_LENGTH = 150
2929
30+ # from net_address.h
31+ NETWORK_TYPE_UNROUTABLE = 0
32+
3033net_tracepoints_program = """
3134#include <uapi/linux/ptrace.h>
3235
5356 u8 msg[MAX_MSG_DATA_LENGTH];
5457};
5558
59+ struct Connection
60+ {
61+ u64 id;
62+ char addr[MAX_PEER_ADDR_LENGTH];
63+ char type[MAX_PEER_CONN_TYPE_LENGTH];
64+ u32 network;
65+ };
66+
67+ struct NewConnection
68+ {
69+ struct Connection conn;
70+ u64 existing;
71+ };
72+
5673BPF_PERF_OUTPUT(inbound_messages);
5774int trace_inbound_message(struct pt_regs *ctx) {
5875 struct p2p_message msg = {};
7895 outbound_messages.perf_submit(ctx, &msg, sizeof(msg));
7996 return 0;
8097};
98+
99+ BPF_PERF_OUTPUT(inbound_connections);
100+ int trace_inbound_connection(struct pt_regs *ctx) {
101+ struct NewConnection inbound = {};
102+ void *conn_type_pointer = NULL, *address_pointer = NULL;
103+ bpf_usdt_readarg(1, ctx, &inbound.conn.id);
104+ bpf_usdt_readarg(2, ctx, &address_pointer);
105+ bpf_usdt_readarg(3, ctx, &conn_type_pointer);
106+ bpf_usdt_readarg(4, ctx, &inbound.conn.network);
107+ bpf_usdt_readarg(5, ctx, &inbound.existing);
108+ bpf_probe_read_user_str(&inbound.conn.addr, sizeof(inbound.conn.addr), address_pointer);
109+ bpf_probe_read_user_str(&inbound.conn.type, sizeof(inbound.conn.type), conn_type_pointer);
110+ inbound_connections.perf_submit(ctx, &inbound, sizeof(inbound));
111+ return 0;
112+ };
113+
81114"""
82115
83116
117+ class Connection (ctypes .Structure ):
118+ _fields_ = [
119+ ("id" , ctypes .c_uint64 ),
120+ ("addr" , ctypes .c_char * MAX_PEER_ADDR_LENGTH ),
121+ ("conn_type" , ctypes .c_char * MAX_PEER_CONN_TYPE_LENGTH ),
122+ ("network" , ctypes .c_uint32 ),
123+ ]
124+
125+ def __repr__ (self ):
126+ return f"Connection(peer={ self .id } , addr={ self .addr .decode ('utf-8' )} , conn_type={ self .conn_type .decode ('utf-8' )} , network={ self .network } )"
127+
128+
129+ class NewConnection (ctypes .Structure ):
130+ _fields_ = [
131+ ("conn" , Connection ),
132+ ("existing" , ctypes .c_uint64 ),
133+ ]
134+
135+ def __repr__ (self ):
136+ return f"NewConnection(conn={ self .conn } , existing={ self .existing } )"
137+
84138class NetTracepointTest (BitcoinTestFramework ):
85139 def set_test_params (self ):
86140 self .num_nodes = 1
@@ -92,6 +146,10 @@ def skip_test_if_missing_module(self):
92146 self .skip_if_no_bpf_permissions ()
93147
94148 def run_test (self ):
149+ self .p2p_message_tracepoint_test ()
150+ self .inbound_conn_tracepoint_test ()
151+
152+ def p2p_message_tracepoint_test (self ):
95153 # Tests the net:inbound_message and net:outbound_message tracepoints
96154 # See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net
97155
@@ -166,7 +224,44 @@ def handle_outbound(_, data, __):
166224
167225
168226 bpf .cleanup ()
227+ test_node .peer_disconnect ()
228+
229+ def inbound_conn_tracepoint_test (self ):
230+ self .log .info ("hook into the net:inbound_connection tracepoint" )
231+ ctx = USDT (pid = self .nodes [0 ].process .pid )
232+ ctx .enable_probe (probe = "net:inbound_connection" ,
233+ fn_name = "trace_inbound_connection" )
234+ bpf = BPF (text = net_tracepoints_program , usdt_contexts = [ctx ], debug = 0 , cflags = ["-Wno-error=implicit-function-declaration" ])
235+
236+ inbound_connections = []
237+ EXPECTED_INBOUND_CONNECTIONS = 2
169238
239+ def handle_inbound_connection (_ , data , __ ):
240+ nonlocal inbound_connections
241+ event = ctypes .cast (data , ctypes .POINTER (NewConnection )).contents
242+ self .log .info (f"handle_inbound_connection(): { event } " )
243+ inbound_connections .append (event )
244+
245+ bpf ["inbound_connections" ].open_perf_buffer (handle_inbound_connection )
246+
247+ self .log .info ("connect two P2P test nodes to our bitcoind node" )
248+ testnodes = list ()
249+ for _ in range (EXPECTED_INBOUND_CONNECTIONS ):
250+ testnode = P2PInterface ()
251+ self .nodes [0 ].add_p2p_connection (testnode )
252+ testnodes .append (testnode )
253+ bpf .perf_buffer_poll (timeout = 200 )
254+
255+ assert_equal (EXPECTED_INBOUND_CONNECTIONS , len (inbound_connections ))
256+ for inbound_connection in inbound_connections :
257+ assert inbound_connection .conn .id > 0
258+ assert inbound_connection .existing > 0
259+ assert_equal (b'inbound' , inbound_connection .conn .conn_type )
260+ assert_equal (NETWORK_TYPE_UNROUTABLE , inbound_connection .conn .network )
261+
262+ bpf .cleanup ()
263+ for node in testnodes :
264+ node .peer_disconnect ()
170265
171266if __name__ == '__main__' :
172267 NetTracepointTest (__file__ ).main ()
0 commit comments