Skip to content

Commit caa5486

Browse files
committed
tracing: connection closed tracepoint
1 parent b2ad6ed commit caa5486

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

doc/tracing.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ Arguments passed:
137137
1. Peer ID as `int64`.
138138
2. Reason why the peer is misbehaving as `pointer to C-style String` (max. length 128 characters).
139139

140+
#### Tracepoint `net:closed_connection`
141+
142+
Is called when a connection is closed. Passes information about the closed peer
143+
and the time at connection establishment.
144+
145+
Arguments passed:
146+
1. Peer ID as `int64`
147+
2. Peer address and port (IPv4, IPv6, Tor v3, I2P, ...) as `pointer to C-style String` (max. length 68 characters)
148+
3. Connection Type (inbound, feeler, outbound-full-relay, ...) as `pointer to C-style String` (max. length 20 characters)
149+
4. Network the peer connects from as `uint32` (1 = IPv4, 2 = IPv6, 3 = Onion, 4 = I2P, 5 = CJDNS). See `Network` enum in `netaddress.h`.
150+
5. Connection established UNIX epoch timestamp in seconds as `uint64`.
151+
140152
### Context `validation`
141153

142154
#### Tracepoint `validation:block_connected`

src/net.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include <optional>
5454
#include <unordered_map>
5555

56+
TRACEPOINT_SEMAPHORE(net, closed_connection);
5657
TRACEPOINT_SEMAPHORE(net, evicted_inbound_connection);
5758
TRACEPOINT_SEMAPHORE(net, inbound_connection);
5859
TRACEPOINT_SEMAPHORE(net, outbound_connection);
@@ -563,6 +564,13 @@ void CNode::CloseSocketDisconnect()
563564
if (m_sock) {
564565
LogDebug(BCLog::NET, "Resetting socket for peer=%d%s", GetId(), LogIP(fLogIPs));
565566
m_sock.reset();
567+
568+
TRACEPOINT(net, closed_connection,
569+
GetId(),
570+
m_addr_name.c_str(),
571+
ConnectionTypeAsString().c_str(),
572+
ConnectedThroughNetwork(),
573+
Ticks<std::chrono::seconds>(m_connected));
566574
}
567575
m_i2p_sam_session.reset();
568576
}

test/functional/interface_usdt_net.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@
146146
147147
BPF_PERF_OUTPUT(evicted_inbound_connections);
148148
int 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);
@@ -169,6 +169,21 @@
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+
196212
class 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+
460515
if __name__ == '__main__':
461516
NetTracepointTest(__file__).main()

0 commit comments

Comments
 (0)