11
11
from io import BytesIO
12
12
# Test will be skipped if we don't have bcc installed
13
13
try :
14
- from bcc import BPF , USDT # type: ignore[import]
14
+ from bcc import BPF , USDT # type: ignore[import]
15
15
except ImportError :
16
16
pass
17
17
from test_framework .messages import msg_version
27
27
# larger messanges see contrib/tracing/log_raw_p2p_msgs.py
28
28
MAX_MSG_DATA_LENGTH = 150
29
29
30
+ # from net_address.h
31
+ NETWORK_TYPE_UNROUTABLE = 0
32
+
30
33
net_tracepoints_program = """
31
34
#include <uapi/linux/ptrace.h>
32
35
53
56
u8 msg[MAX_MSG_DATA_LENGTH];
54
57
};
55
58
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
+
56
73
BPF_PERF_OUTPUT(inbound_messages);
57
74
int trace_inbound_message(struct pt_regs *ctx) {
58
75
struct p2p_message msg = {};
78
95
outbound_messages.perf_submit(ctx, &msg, sizeof(msg));
79
96
return 0;
80
97
};
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
+
81
114
"""
82
115
83
116
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
+
84
138
class NetTracepointTest (BitcoinTestFramework ):
85
139
def set_test_params (self ):
86
140
self .num_nodes = 1
@@ -92,6 +146,10 @@ def skip_test_if_missing_module(self):
92
146
self .skip_if_no_bpf_permissions ()
93
147
94
148
def run_test (self ):
149
+ self .p2p_message_tracepoint_test ()
150
+ self .inbound_conn_tracepoint_test ()
151
+
152
+ def p2p_message_tracepoint_test (self ):
95
153
# Tests the net:inbound_message and net:outbound_message tracepoints
96
154
# See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net
97
155
@@ -166,7 +224,44 @@ def handle_outbound(_, data, __):
166
224
167
225
168
226
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
169
238
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 ()
170
265
171
266
if __name__ == '__main__' :
172
267
NetTracepointTest (__file__ ).main ()
0 commit comments