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