interface_usdt_net.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2022-present The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 """ Tests the net:* tracepoint API interface. 7 See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net 8 """ 9 10 import ctypes 11 from io import BytesIO 12 # Test will be skipped if we don't have bcc installed 13 try: 14 from bcc import BPF, USDT # type: ignore[import] 15 except ImportError: 16 pass 17 from test_framework.messages import CBlockHeader, MAX_HEADERS_RESULTS, msg_headers, msg_version 18 from test_framework.p2p import P2PInterface 19 from test_framework.test_framework import BitcoinTestFramework 20 from test_framework.util import ( 21 assert_equal, 22 assert_greater_than, 23 bpf_cflags, 24 ) 25 26 # Tor v3 addresses are 62 chars + 6 chars for the port (':12345'). 27 MAX_PEER_ADDR_LENGTH = 68 28 MAX_PEER_CONN_TYPE_LENGTH = 20 29 MAX_MSG_TYPE_LENGTH = 20 30 MAX_MISBEHAVING_MESSAGE_LENGTH = 128 31 # We won't process messages larger than 150 byte in this test. For reading 32 # larger messanges see contrib/tracing/log_raw_p2p_msgs.py 33 MAX_MSG_DATA_LENGTH = 150 34 35 # from net_address.h 36 NETWORK_TYPE_UNROUTABLE = 0 37 # Use in -maxconnections. Results in a maximum of 21 inbound connections 38 MAX_CONNECTIONS = 32 39 MAX_INBOUND_CONNECTIONS = MAX_CONNECTIONS - 10 - 1 # 10 outbound and 1 feeler 40 41 net_tracepoints_program = """ 42 #include <uapi/linux/ptrace.h> 43 44 #define MAX_PEER_ADDR_LENGTH {} 45 #define MAX_PEER_CONN_TYPE_LENGTH {} 46 #define MAX_MSG_TYPE_LENGTH {} 47 #define MAX_MSG_DATA_LENGTH {} 48 #define MAX_MISBEHAVING_MESSAGE_LENGTH {} 49 """.format( 50 MAX_PEER_ADDR_LENGTH, 51 MAX_PEER_CONN_TYPE_LENGTH, 52 MAX_MSG_TYPE_LENGTH, 53 MAX_MSG_DATA_LENGTH, 54 MAX_MISBEHAVING_MESSAGE_LENGTH, 55 ) + """ 56 // A min() macro. Prefixed with _TRACEPOINT_TEST to avoid collision with other MIN macros. 57 #define _TRACEPOINT_TEST_MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) 58 59 struct p2p_message 60 { 61 u64 peer_id; 62 char peer_addr[MAX_PEER_ADDR_LENGTH]; 63 char peer_conn_type[MAX_PEER_CONN_TYPE_LENGTH]; 64 char msg_type[MAX_MSG_TYPE_LENGTH]; 65 u64 msg_size; 66 u8 msg[MAX_MSG_DATA_LENGTH]; 67 }; 68 69 struct Connection 70 { 71 u64 id; 72 char addr[MAX_PEER_ADDR_LENGTH]; 73 char type[MAX_PEER_CONN_TYPE_LENGTH]; 74 u32 network; 75 }; 76 77 struct NewConnection 78 { 79 struct Connection conn; 80 u64 existing; 81 }; 82 83 struct ClosedConnection 84 { 85 struct Connection conn; 86 u64 time_established; 87 }; 88 89 struct MisbehavingConnection 90 { 91 u64 id; 92 char message[MAX_MISBEHAVING_MESSAGE_LENGTH]; 93 }; 94 95 BPF_PERF_OUTPUT(inbound_messages); 96 int trace_inbound_message(struct pt_regs *ctx) { 97 struct p2p_message msg = {}; 98 void *paddr = NULL, *pconn_type = NULL, *pmsg_type = NULL, *pmsg = NULL; 99 bpf_usdt_readarg(1, ctx, &msg.peer_id); 100 bpf_usdt_readarg(2, ctx, &paddr); 101 bpf_probe_read_user_str(&msg.peer_addr, sizeof(msg.peer_addr), paddr); 102 bpf_usdt_readarg(3, ctx, &pconn_type); 103 bpf_probe_read_user_str(&msg.peer_conn_type, sizeof(msg.peer_conn_type), pconn_type); 104 bpf_usdt_readarg(4, ctx, &pmsg_type); 105 bpf_probe_read_user_str(&msg.msg_type, sizeof(msg.msg_type), pmsg_type); 106 bpf_usdt_readarg(5, ctx, &msg.msg_size); 107 bpf_usdt_readarg(6, ctx, &pmsg); 108 bpf_probe_read_user(&msg.msg, _TRACEPOINT_TEST_MIN(msg.msg_size, MAX_MSG_DATA_LENGTH), pmsg); 109 inbound_messages.perf_submit(ctx, &msg, sizeof(msg)); 110 return 0; 111 } 112 113 BPF_PERF_OUTPUT(outbound_messages); 114 int trace_outbound_message(struct pt_regs *ctx) { 115 struct p2p_message msg = {}; 116 void *paddr = NULL, *pconn_type = NULL, *pmsg_type = NULL, *pmsg = NULL; 117 bpf_usdt_readarg(1, ctx, &msg.peer_id); 118 bpf_usdt_readarg(1, ctx, &msg.peer_id); 119 bpf_usdt_readarg(2, ctx, &paddr); 120 bpf_probe_read_user_str(&msg.peer_addr, sizeof(msg.peer_addr), paddr); 121 bpf_usdt_readarg(3, ctx, &pconn_type); 122 bpf_probe_read_user_str(&msg.peer_conn_type, sizeof(msg.peer_conn_type), pconn_type); 123 bpf_usdt_readarg(4, ctx, &pmsg_type); 124 bpf_probe_read_user_str(&msg.msg_type, sizeof(msg.msg_type), pmsg_type); 125 bpf_usdt_readarg(5, ctx, &msg.msg_size); 126 bpf_usdt_readarg(6, ctx, &pmsg); 127 bpf_probe_read_user(&msg.msg, _TRACEPOINT_TEST_MIN(msg.msg_size, MAX_MSG_DATA_LENGTH), pmsg); 128 outbound_messages.perf_submit(ctx, &msg, sizeof(msg)); 129 return 0; 130 }; 131 132 BPF_PERF_OUTPUT(inbound_connections); 133 int trace_inbound_connection(struct pt_regs *ctx) { 134 struct NewConnection inbound = {}; 135 void *conn_type_pointer = NULL, *address_pointer = NULL; 136 bpf_usdt_readarg(1, ctx, &inbound.conn.id); 137 bpf_usdt_readarg(2, ctx, &address_pointer); 138 bpf_usdt_readarg(3, ctx, &conn_type_pointer); 139 bpf_usdt_readarg(4, ctx, &inbound.conn.network); 140 bpf_usdt_readarg(5, ctx, &inbound.existing); 141 bpf_probe_read_user_str(&inbound.conn.addr, sizeof(inbound.conn.addr), address_pointer); 142 bpf_probe_read_user_str(&inbound.conn.type, sizeof(inbound.conn.type), conn_type_pointer); 143 inbound_connections.perf_submit(ctx, &inbound, sizeof(inbound)); 144 return 0; 145 }; 146 147 BPF_PERF_OUTPUT(outbound_connections); 148 int trace_outbound_connection(struct pt_regs *ctx) { 149 struct NewConnection outbound = {}; 150 void *conn_type_pointer = NULL, *address_pointer = NULL; 151 bpf_usdt_readarg(1, ctx, &outbound.conn.id); 152 bpf_usdt_readarg(2, ctx, &address_pointer); 153 bpf_usdt_readarg(3, ctx, &conn_type_pointer); 154 bpf_usdt_readarg(4, ctx, &outbound.conn.network); 155 bpf_usdt_readarg(5, ctx, &outbound.existing); 156 bpf_probe_read_user_str(&outbound.conn.addr, sizeof(outbound.conn.addr), address_pointer); 157 bpf_probe_read_user_str(&outbound.conn.type, sizeof(outbound.conn.type), conn_type_pointer); 158 outbound_connections.perf_submit(ctx, &outbound, sizeof(outbound)); 159 return 0; 160 }; 161 162 BPF_PERF_OUTPUT(evicted_inbound_connections); 163 int trace_evicted_inbound_connection(struct pt_regs *ctx) { 164 struct ClosedConnection evicted = {}; 165 void *conn_type_pointer = NULL, *address_pointer = NULL; 166 bpf_usdt_readarg(1, ctx, &evicted.conn.id); 167 bpf_usdt_readarg(2, ctx, &address_pointer); 168 bpf_usdt_readarg(3, ctx, &conn_type_pointer); 169 bpf_usdt_readarg(4, ctx, &evicted.conn.network); 170 bpf_usdt_readarg(5, ctx, &evicted.time_established); 171 bpf_probe_read_user_str(&evicted.conn.addr, sizeof(evicted.conn.addr), address_pointer); 172 bpf_probe_read_user_str(&evicted.conn.type, sizeof(evicted.conn.type), conn_type_pointer); 173 evicted_inbound_connections.perf_submit(ctx, &evicted, sizeof(evicted)); 174 return 0; 175 }; 176 177 BPF_PERF_OUTPUT(misbehaving_connections); 178 int trace_misbehaving_connection(struct pt_regs *ctx) { 179 struct MisbehavingConnection misbehaving = {}; 180 void *message_pointer = NULL; 181 bpf_usdt_readarg(1, ctx, &misbehaving.id); 182 bpf_usdt_readarg(2, ctx, &message_pointer); 183 bpf_probe_read_user_str(&misbehaving.message, sizeof(misbehaving.message), message_pointer); 184 misbehaving_connections.perf_submit(ctx, &misbehaving, sizeof(misbehaving)); 185 return 0; 186 }; 187 188 BPF_PERF_OUTPUT(closed_connections); 189 int trace_closed_connection(struct pt_regs *ctx) { 190 struct ClosedConnection closed = {}; 191 void *conn_type_pointer = NULL, *address_pointer = NULL; 192 bpf_usdt_readarg(1, ctx, &closed.conn.id); 193 bpf_usdt_readarg(2, ctx, &address_pointer); 194 bpf_usdt_readarg(3, ctx, &conn_type_pointer); 195 bpf_usdt_readarg(4, ctx, &closed.conn.network); 196 bpf_usdt_readarg(5, ctx, &closed.time_established); 197 bpf_probe_read_user_str(&closed.conn.addr, sizeof(closed.conn.addr), address_pointer); 198 bpf_probe_read_user_str(&closed.conn.type, sizeof(closed.conn.type), conn_type_pointer); 199 closed_connections.perf_submit(ctx, &closed, sizeof(closed)); 200 return 0; 201 }; 202 """ 203 204 205 class Connection(ctypes.Structure): 206 _fields_ = [ 207 ("id", ctypes.c_uint64), 208 ("addr", ctypes.c_char * MAX_PEER_ADDR_LENGTH), 209 ("conn_type", ctypes.c_char * MAX_PEER_CONN_TYPE_LENGTH), 210 ("network", ctypes.c_uint32), 211 ] 212 213 def __repr__(self): 214 return f"Connection(peer={self.id}, addr={self.addr.decode('utf-8')}, conn_type={self.conn_type.decode('utf-8')}, network={self.network})" 215 216 217 class NewConnection(ctypes.Structure): 218 _fields_ = [ 219 ("conn", Connection), 220 ("existing", ctypes.c_uint64), 221 ] 222 223 def __repr__(self): 224 return f"NewConnection(conn={self.conn}, existing={self.existing})" 225 226 227 class ClosedConnection(ctypes.Structure): 228 _fields_ = [ 229 ("conn", Connection), 230 ("time_established", ctypes.c_uint64), 231 ] 232 233 def __repr__(self): 234 return f"ClosedConnection(conn={self.conn}, time_established={self.time_established})" 235 236 237 class MisbehavingConnection(ctypes.Structure): 238 _fields_ = [ 239 ("id", ctypes.c_uint64), 240 ("message", ctypes.c_char * MAX_MISBEHAVING_MESSAGE_LENGTH), 241 ] 242 243 def __repr__(self): 244 return f"MisbehavingConnection(id={self.id}, message={self.message})" 245 246 247 class NetTracepointTest(BitcoinTestFramework): 248 def set_test_params(self): 249 self.num_nodes = 1 250 self.extra_args = [[f'-maxconnections={MAX_CONNECTIONS}']] 251 252 def skip_test_if_missing_module(self): 253 self.skip_if_platform_not_linux() 254 self.skip_if_no_bitcoind_tracepoints() 255 self.skip_if_no_python_bcc() 256 self.skip_if_no_bpf_permissions() 257 self.skip_if_running_under_valgrind() 258 259 def run_test(self): 260 self.p2p_message_tracepoint_test() 261 self.inbound_conn_tracepoint_test() 262 self.outbound_conn_tracepoint_test() 263 self.evicted_inbound_conn_tracepoint_test() 264 self.misbehaving_conn_tracepoint_test() 265 self.closed_conn_tracepoint_test() 266 267 def p2p_message_tracepoint_test(self): 268 # Tests the net:inbound_message and net:outbound_message tracepoints 269 # See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-net 270 271 class P2PMessage(ctypes.Structure): 272 _fields_ = [ 273 ("peer_id", ctypes.c_uint64), 274 ("peer_addr", ctypes.c_char * MAX_PEER_ADDR_LENGTH), 275 ("peer_conn_type", ctypes.c_char * MAX_PEER_CONN_TYPE_LENGTH), 276 ("msg_type", ctypes.c_char * MAX_MSG_TYPE_LENGTH), 277 ("msg_size", ctypes.c_uint64), 278 ("msg", ctypes.c_ubyte * MAX_MSG_DATA_LENGTH), 279 ] 280 281 def __repr__(self): 282 return f"P2PMessage(peer={self.peer_id}, addr={self.peer_addr.decode('utf-8')}, conn_type={self.peer_conn_type.decode('utf-8')}, msg_type={self.msg_type.decode('utf-8')}, msg_size={self.msg_size})" 283 284 self.log.info( 285 "hook into the net:inbound_message and net:outbound_message tracepoints") 286 ctx = USDT(pid=self.nodes[0].process.pid) 287 ctx.enable_probe(probe="net:inbound_message", 288 fn_name="trace_inbound_message") 289 ctx.enable_probe(probe="net:outbound_message", 290 fn_name="trace_outbound_message") 291 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 292 293 EXPECTED_INOUTBOUND_VERSION_MSG = 1 294 checked_inbound_version_msg = 0 295 checked_outbound_version_msg = 0 296 events = [] 297 298 def check_p2p_message(event, is_inbound): 299 nonlocal checked_inbound_version_msg, checked_outbound_version_msg 300 if event.msg_type.decode("utf-8") == "version": 301 self.log.info( 302 f"check_p2p_message(): {'inbound' if is_inbound else 'outbound'} {event}") 303 peer = self.nodes[0].getpeerinfo()[0] 304 msg = msg_version() 305 msg.deserialize(BytesIO(bytes(event.msg[:event.msg_size]))) 306 assert_equal(peer["id"], event.peer_id, peer["id"]) 307 assert_equal(peer["addr"], event.peer_addr.decode("utf-8")) 308 assert_equal(peer["connection_type"], 309 event.peer_conn_type.decode("utf-8")) 310 if is_inbound: 311 checked_inbound_version_msg += 1 312 else: 313 checked_outbound_version_msg += 1 314 315 def handle_inbound(_, data, __): 316 event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents 317 events.append((event, True)) 318 319 def handle_outbound(_, data, __): 320 event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents 321 events.append((event, False)) 322 323 bpf["inbound_messages"].open_perf_buffer(handle_inbound) 324 bpf["outbound_messages"].open_perf_buffer(handle_outbound) 325 326 self.log.info("connect a P2P test node to our bitcoind node") 327 test_node = P2PInterface() 328 self.nodes[0].add_p2p_connection(test_node) 329 bpf.perf_buffer_poll(timeout=200) 330 331 self.log.info( 332 "check receipt and content of in- and outbound version messages") 333 for event, is_inbound in events: 334 check_p2p_message(event, is_inbound) 335 assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG, 336 checked_inbound_version_msg) 337 assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG, 338 checked_outbound_version_msg) 339 340 341 bpf.cleanup() 342 test_node.peer_disconnect() 343 344 def inbound_conn_tracepoint_test(self): 345 self.log.info("hook into the net:inbound_connection tracepoint") 346 ctx = USDT(pid=self.nodes[0].process.pid) 347 ctx.enable_probe(probe="net:inbound_connection", 348 fn_name="trace_inbound_connection") 349 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 350 351 inbound_connections = [] 352 EXPECTED_INBOUND_CONNECTIONS = 2 353 354 def handle_inbound_connection(_, data, __): 355 nonlocal inbound_connections 356 event = ctypes.cast(data, ctypes.POINTER(NewConnection)).contents 357 self.log.info(f"handle_inbound_connection(): {event}") 358 inbound_connections.append(event) 359 360 bpf["inbound_connections"].open_perf_buffer(handle_inbound_connection) 361 362 self.log.info("connect two P2P test nodes to our bitcoind node") 363 testnodes = list() 364 for _ in range(EXPECTED_INBOUND_CONNECTIONS): 365 testnode = P2PInterface() 366 self.nodes[0].add_p2p_connection(testnode) 367 testnodes.append(testnode) 368 bpf.perf_buffer_poll(timeout=200) 369 370 assert_equal(EXPECTED_INBOUND_CONNECTIONS, len(inbound_connections)) 371 for inbound_connection in inbound_connections: 372 assert_greater_than(inbound_connection.conn.id, 0) 373 assert_greater_than(inbound_connection.existing, 0) 374 assert_equal(b'inbound', inbound_connection.conn.conn_type) 375 assert_equal(NETWORK_TYPE_UNROUTABLE, inbound_connection.conn.network) 376 377 bpf.cleanup() 378 for node in testnodes: 379 node.peer_disconnect() 380 381 def outbound_conn_tracepoint_test(self): 382 self.log.info("hook into the net:outbound_connection tracepoint") 383 ctx = USDT(pid=self.nodes[0].process.pid) 384 ctx.enable_probe(probe="net:outbound_connection", 385 fn_name="trace_outbound_connection") 386 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 387 388 # that the handle_* function succeeds. 389 EXPECTED_OUTBOUND_CONNECTIONS = 2 390 EXPECTED_CONNECTION_TYPE = "feeler" 391 outbound_connections = [] 392 393 def handle_outbound_connection(_, data, __): 394 event = ctypes.cast(data, ctypes.POINTER(NewConnection)).contents 395 self.log.info(f"handle_outbound_connection(): {event}") 396 outbound_connections.append(event) 397 398 bpf["outbound_connections"].open_perf_buffer( 399 handle_outbound_connection) 400 401 self.log.info( 402 f"connect {EXPECTED_OUTBOUND_CONNECTIONS} P2P test nodes to our bitcoind node") 403 testnodes = list() 404 for p2p_idx in range(EXPECTED_OUTBOUND_CONNECTIONS): 405 testnode = P2PInterface() 406 self.nodes[0].add_outbound_p2p_connection( 407 testnode, p2p_idx=p2p_idx, connection_type=EXPECTED_CONNECTION_TYPE) 408 testnodes.append(testnode) 409 bpf.perf_buffer_poll(timeout=200) 410 411 assert_equal(EXPECTED_OUTBOUND_CONNECTIONS, len(outbound_connections)) 412 for outbound_connection in outbound_connections: 413 assert_greater_than(outbound_connection.conn.id, 0) 414 assert_greater_than(outbound_connection.existing, 0) 415 assert_equal(EXPECTED_CONNECTION_TYPE, outbound_connection.conn.conn_type.decode('utf-8')) 416 assert_equal(NETWORK_TYPE_UNROUTABLE, outbound_connection.conn.network) 417 418 bpf.cleanup() 419 for node in testnodes: 420 node.peer_disconnect() 421 422 def evicted_inbound_conn_tracepoint_test(self): 423 self.log.info("hook into the net:evicted_inbound_connection tracepoint") 424 ctx = USDT(pid=self.nodes[0].process.pid) 425 ctx.enable_probe(probe="net:evicted_inbound_connection", 426 fn_name="trace_evicted_inbound_connection") 427 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 428 429 EXPECTED_EVICTED_CONNECTIONS = 2 430 evicted_connections = [] 431 432 def handle_evicted_inbound_connection(_, data, __): 433 event = ctypes.cast(data, ctypes.POINTER(ClosedConnection)).contents 434 self.log.info(f"handle_evicted_inbound_connection(): {event}") 435 evicted_connections.append(event) 436 437 bpf["evicted_inbound_connections"].open_perf_buffer(handle_evicted_inbound_connection) 438 439 self.log.info( 440 f"connect {MAX_INBOUND_CONNECTIONS + EXPECTED_EVICTED_CONNECTIONS} P2P test nodes to our bitcoind node and expect {EXPECTED_EVICTED_CONNECTIONS} evictions") 441 testnodes = list() 442 for p2p_idx in range(MAX_INBOUND_CONNECTIONS + EXPECTED_EVICTED_CONNECTIONS): 443 testnode = P2PInterface() 444 self.nodes[0].add_p2p_connection(testnode) 445 testnodes.append(testnode) 446 bpf.perf_buffer_poll(timeout=200) 447 448 assert_equal(EXPECTED_EVICTED_CONNECTIONS, len(evicted_connections)) 449 for evicted_connection in evicted_connections: 450 assert_greater_than(evicted_connection.conn.id, 0) 451 assert_greater_than(evicted_connection.time_established, 0) 452 assert_equal("inbound", evicted_connection.conn.conn_type.decode('utf-8')) 453 assert_equal(NETWORK_TYPE_UNROUTABLE, evicted_connection.conn.network) 454 455 bpf.cleanup() 456 for node in testnodes: 457 node.peer_disconnect() 458 459 def misbehaving_conn_tracepoint_test(self): 460 self.log.info("hook into the net:misbehaving_connection tracepoint") 461 ctx = USDT(pid=self.nodes[0].process.pid) 462 ctx.enable_probe(probe="net:misbehaving_connection", 463 fn_name="trace_misbehaving_connection") 464 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 465 466 EXPECTED_MISBEHAVING_CONNECTIONS = 2 467 misbehaving_connections = [] 468 469 def handle_misbehaving_connection(_, data, __): 470 event = ctypes.cast(data, ctypes.POINTER(MisbehavingConnection)).contents 471 self.log.info(f"handle_misbehaving_connection(): {event}") 472 misbehaving_connections.append(event) 473 474 bpf["misbehaving_connections"].open_perf_buffer(handle_misbehaving_connection) 475 476 self.log.info("connect a misbehaving P2P test nodes to our bitcoind node") 477 msg = msg_headers([CBlockHeader()] * (MAX_HEADERS_RESULTS + 1)) 478 for _ in range(EXPECTED_MISBEHAVING_CONNECTIONS): 479 testnode = P2PInterface() 480 self.nodes[0].add_p2p_connection(testnode) 481 testnode.send_without_ping(msg) 482 bpf.perf_buffer_poll(timeout=500) 483 testnode.peer_disconnect() 484 485 assert_equal(EXPECTED_MISBEHAVING_CONNECTIONS, len(misbehaving_connections)) 486 for misbehaving_connection in misbehaving_connections: 487 assert_greater_than(misbehaving_connection.id, 0) 488 assert_greater_than(len(misbehaving_connection.message), 0) 489 assert_equal(misbehaving_connection.message, b"headers message size = 2001") 490 491 bpf.cleanup() 492 493 def closed_conn_tracepoint_test(self): 494 self.log.info("hook into the net:closed_connection tracepoint") 495 ctx = USDT(pid=self.nodes[0].process.pid) 496 ctx.enable_probe(probe="net:closed_connection", 497 fn_name="trace_closed_connection") 498 bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags()) 499 500 EXPECTED_CLOSED_CONNECTIONS = 2 501 closed_connections = [] 502 503 def handle_closed_connection(_, data, __): 504 event = ctypes.cast(data, ctypes.POINTER(ClosedConnection)).contents 505 self.log.info(f"handle_closed_connection(): {event}") 506 closed_connections.append(event) 507 508 bpf["closed_connections"].open_perf_buffer(handle_closed_connection) 509 510 self.log.info( 511 f"connect {EXPECTED_CLOSED_CONNECTIONS} P2P test nodes to our bitcoind node") 512 testnodes = list() 513 for p2p_idx in range(EXPECTED_CLOSED_CONNECTIONS): 514 testnode = P2PInterface() 515 self.nodes[0].add_p2p_connection(testnode) 516 testnodes.append(testnode) 517 for node in testnodes: 518 node.peer_disconnect() 519 self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) 520 bpf.perf_buffer_poll(timeout=400) 521 522 assert_equal(EXPECTED_CLOSED_CONNECTIONS, len(closed_connections)) 523 for closed_connection in closed_connections: 524 assert_greater_than(closed_connection.conn.id, 0) 525 assert_equal("inbound", closed_connection.conn.conn_type.decode('utf-8')) 526 assert_equal(0, closed_connection.conn.network) 527 assert_greater_than(closed_connection.time_established, 0) 528 529 bpf.cleanup() 530 531 if __name__ == '__main__': 532 NetTracepointTest(__file__).main()