146146
147147BPF_PERF_OUTPUT(evicted_inbound_connections);
148148int trace_evicted_inbound_connection(struct pt_regs *ctx) {
149+ struct ClosedConnection evicted = {};
149150 void *conn_type_pointer = NULL, *address_pointer = NULL;
150- void* address_pointer;
151151 bpf_usdt_readarg(1, ctx, &evicted.conn.id);
152152 bpf_usdt_readarg(2, ctx, &address_pointer);
153153 bpf_usdt_readarg(3, ctx, &conn_type_pointer);
169169 misbehaving_connections.perf_submit(ctx, &misbehaving, sizeof(misbehaving));
170170 return 0;
171171};
172+
173+ BPF_PERF_OUTPUT(closed_connections);
174+ int trace_closed_connection(struct pt_regs *ctx) {
175+ struct ClosedConnection closed = {};
176+ void *conn_type_pointer = NULL, *address_pointer = NULL;
177+ bpf_usdt_readarg(1, ctx, &closed.conn.id);
178+ bpf_usdt_readarg(2, ctx, &address_pointer);
179+ bpf_usdt_readarg(3, ctx, &conn_type_pointer);
180+ bpf_usdt_readarg(4, ctx, &closed.conn.network);
181+ bpf_usdt_readarg(5, ctx, &closed.time_established);
182+ bpf_probe_read_user_str(&closed.conn.addr, sizeof(closed.conn.addr), address_pointer);
183+ bpf_probe_read_user_str(&closed.conn.type, sizeof(closed.conn.type), conn_type_pointer);
184+ closed_connections.perf_submit(ctx, &closed, sizeof(closed));
185+ return 0;
186+ };
172187"""
173188
174189
@@ -193,6 +208,7 @@ class NewConnection(ctypes.Structure):
193208 def __repr__ (self ):
194209 return f"NewConnection(conn={ self .conn } , existing={ self .existing } )"
195210
211+
196212class ClosedConnection (ctypes .Structure ):
197213 _fields_ = [
198214 ("conn" , Connection ),
@@ -230,6 +246,7 @@ def run_test(self):
230246 self .outbound_conn_tracepoint_test ()
231247 self .evicted_inbound_conn_tracepoint_test ()
232248 self .misbehaving_conn_tracepoint_test ()
249+ self .closed_conn_tracepoint_test ()
233250
234251 def p2p_message_tracepoint_test (self ):
235252 # Tests the net:inbound_message and net:outbound_message tracepoints
@@ -457,5 +474,43 @@ def handle_misbehaving_connection(_, data, __):
457474
458475 bpf .cleanup ()
459476
477+ def closed_conn_tracepoint_test (self ):
478+ self .log .info ("hook into the net:closed_connection tracepoint" )
479+ ctx = USDT (pid = self .nodes [0 ].process .pid )
480+ ctx .enable_probe (probe = "net:closed_connection" ,
481+ fn_name = "trace_closed_connection" )
482+ bpf = BPF (text = net_tracepoints_program , usdt_contexts = [ctx ], debug = 0 , cflags = ["-Wno-error=implicit-function-declaration" ])
483+
484+ EXPECTED_CLOSED_CONNECTIONS = 2
485+ closed_connections = []
486+
487+ def handle_closed_connection (_ , data , __ ):
488+ event = ctypes .cast (data , ctypes .POINTER (ClosedConnection )).contents
489+ self .log .info (f"handle_closed_connection(): { event } " )
490+ closed_connections .append (event )
491+
492+ bpf ["closed_connections" ].open_perf_buffer (handle_closed_connection )
493+
494+ self .log .info (
495+ f"connect { EXPECTED_CLOSED_CONNECTIONS } P2P test nodes to our bitcoind node" )
496+ testnodes = list ()
497+ for p2p_idx in range (EXPECTED_CLOSED_CONNECTIONS ):
498+ testnode = P2PInterface ()
499+ self .nodes [0 ].add_p2p_connection (testnode )
500+ testnodes .append (testnode )
501+ for node in testnodes :
502+ node .peer_disconnect ()
503+ self .wait_until (lambda : len (self .nodes [0 ].getpeerinfo ()) == 0 )
504+ bpf .perf_buffer_poll (timeout = 400 )
505+
506+ assert_equal (EXPECTED_CLOSED_CONNECTIONS , len (closed_connections ))
507+ for closed_connection in closed_connections :
508+ assert closed_connection .conn .id > 0
509+ assert_equal ("inbound" , closed_connection .conn .conn_type .decode ('utf-8' ))
510+ assert_equal (0 , closed_connection .conn .network )
511+ assert closed_connection .time_established > 0
512+
513+ bpf .cleanup ()
514+
460515if __name__ == '__main__' :
461516 NetTracepointTest (__file__ ).main ()
0 commit comments