Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2021-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.6""" Demonstration of eBPF limitations and the effect on USDT with the7net:inbound_message and net:outbound_message tracepoints. """9# This script shows a limitation of eBPF when data larger than 32kb is passed to10# user-space. It uses BCC (https://github.com/iovisor/bcc) to load a sandboxed11# eBPF program into the Linux kernel (root privileges are required). The eBPF12# program attaches to two statically defined tracepoints. The tracepoint13# 'net:inbound_message' is called when a new P2P message is received, and14# 'net:outbound_message' is called on outbound P2P messages. The eBPF program15# submits the P2P messages to this script via a BPF ring buffer. The submitted16# messages are printed.18# eBPF Limitations:19#20# Bitcoin P2P messages can be larger than 32kb (e.g. tx, block, ...). The eBPF21# VM's stack is limited to 512 bytes, and we can't allocate more than about 32kb22# for a P2P message in the eBPF VM. The message data is cut off when the message23# is larger than MAX_MSG_DATA_LENGTH (see definition below). This can be detected24# in user-space by comparing the data length to the message length variable. The25# message is cut off when the data length is smaller than the message length.26# A warning is included with the printed message data.27#28# Data is submitted to user-space (i.e. to this script) via a ring buffer. The29# throughput of the ring buffer is limited. Each p2p_message is about 32kb in30# size. In- or outbound messages submitted to the ring buffer in rapid31# succession fill the ring buffer faster than it can be read. Some messages are32# lost.33#34# BCC prints: "Possibly lost 2 samples" on lost messages.36import sys37from bcc import BPF, USDT39# BCC: The C program to be compiled to an eBPF program (by BCC) and loaded into40# a sandboxed Linux kernel VM.41program = """42#include <uapi/linux/ptrace.h>44// A min() macro. Prefixed with _TRACEPOINT_TEST to avoid collision with other MIN macros.45#define _TRACEPOINT_TEST_MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })47// Maximum possible allocation size48// from include/linux/percpu.h in the Linux kernel49#define PCPU_MIN_UNIT_SIZE (32 << 10)51// Tor v3 addresses are 62 chars + 6 chars for the port (':12345').52#define MAX_PEER_ADDR_LENGTH 62 + 653#define MAX_PEER_CONN_TYPE_LENGTH 2054#define MAX_MSG_TYPE_LENGTH 2055#define MAX_MSG_DATA_LENGTH PCPU_MIN_UNIT_SIZE - 20057struct p2p_message58{59u64 peer_id;60char peer_addr[MAX_PEER_ADDR_LENGTH];61char peer_conn_type[MAX_PEER_CONN_TYPE_LENGTH];62char msg_type[MAX_MSG_TYPE_LENGTH];63u64 msg_size;64u8 msg[MAX_MSG_DATA_LENGTH];65};67// We can't store the p2p_message struct on the eBPF stack as it is limited to68// 512 bytes and P2P message can be bigger than 512 bytes. However, we can use69// an BPF-array with a length of 1 to allocate up to 32768 bytes (this is70// defined by PCPU_MIN_UNIT_SIZE in include/linux/percpu.h in the Linux kernel).71// Also see https://github.com/iovisor/bcc/issues/230672BPF_ARRAY(msg_arr, struct p2p_message, 1);74// Two BPF perf buffers for pushing data (here P2P messages) to user-space.75BPF_PERF_OUTPUT(inbound_messages);76BPF_PERF_OUTPUT(outbound_messages);78int trace_inbound_message(struct pt_regs *ctx) {79int idx = 0;80struct p2p_message *msg = msg_arr.lookup(&idx);81void *paddr = NULL, *pconn_type = NULL, *pmsg_type = NULL, *pmsg = NULL;83// lookup() does not return a NULL pointer. However, the BPF verifier84// requires an explicit check that that the `msg` pointer isn't a NULL85// pointer. See https://github.com/iovisor/bcc/issues/259586if (msg == NULL) return 1;88bpf_usdt_readarg(1, ctx, &msg->peer_id);89bpf_usdt_readarg(2, ctx, &paddr);90bpf_probe_read_user_str(&msg->peer_addr, sizeof(msg->peer_addr), paddr);91bpf_usdt_readarg(3, ctx, &pconn_type);92bpf_probe_read_user_str(&msg->peer_conn_type, sizeof(msg->peer_conn_type), pconn_type);93bpf_usdt_readarg(4, ctx, &pmsg_type);94bpf_probe_read_user_str(&msg->msg_type, sizeof(msg->msg_type), pmsg_type);95bpf_usdt_readarg(5, ctx, &msg->msg_size);96bpf_usdt_readarg(6, ctx, &pmsg);97bpf_probe_read_user(&msg->msg, _TRACEPOINT_TEST_MIN(msg->msg_size, MAX_MSG_DATA_LENGTH), pmsg);99inbound_messages.perf_submit(ctx, msg, sizeof(*msg));100return 0;101};103int trace_outbound_message(struct pt_regs *ctx) {104int idx = 0;105struct p2p_message *msg = msg_arr.lookup(&idx);107void *paddr = NULL, *pconn_type = NULL, *pmsg_type = NULL, *pmsg = NULL;109// lookup() does not return a NULL pointer. However, the BPF verifier110// requires an explicit check that that the `msg` pointer isn't a NULL111// pointer. See https://github.com/iovisor/bcc/issues/2595112if (msg == NULL) return 1;114bpf_usdt_readarg(1, ctx, &msg->peer_id);115bpf_usdt_readarg(2, ctx, &paddr);116bpf_probe_read_user_str(&msg->peer_addr, sizeof(msg->peer_addr), paddr);117bpf_usdt_readarg(3, ctx, &pconn_type);118bpf_probe_read_user_str(&msg->peer_conn_type, sizeof(msg->peer_conn_type), pconn_type);119bpf_usdt_readarg(4, ctx, &pmsg_type);120bpf_probe_read_user_str(&msg->msg_type, sizeof(msg->msg_type), pmsg_type);121bpf_usdt_readarg(5, ctx, &msg->msg_size);122bpf_usdt_readarg(6, ctx, &pmsg);123bpf_probe_read_user(&msg->msg, _TRACEPOINT_TEST_MIN(msg->msg_size, MAX_MSG_DATA_LENGTH), pmsg);125outbound_messages.perf_submit(ctx, msg, sizeof(*msg));126return 0;127};128"""131def print_message(event, inbound):132print("{} {} msg '{}' from peer {} ({}, {}) with {} bytes: {}".format(134"Warning: incomplete message (only {} out of {} bytes)!".format(135len(event.msg), event.msg_size) if len(event.msg) < event.msg_size else "",136"inbound" if inbound else "outbound",137event.msg_type.decode("utf-8"),138event.peer_id,139event.peer_conn_type.decode("utf-8"),140event.peer_addr.decode("utf-8"),141event.msg_size,142bytes(event.msg[:event.msg_size]).hex(),143)144)147def main(pid):148print(f"Hooking into bitcoind with pid {pid}")149bitcoind_with_usdts = USDT(pid=int(pid))151# attaching the trace functions defined in the BPF program to the tracepoints152bitcoind_with_usdts.enable_probe(153probe="inbound_message", fn_name="trace_inbound_message")154bitcoind_with_usdts.enable_probe(155probe="outbound_message", fn_name="trace_outbound_message")156bpf = BPF(text=program, usdt_contexts=[bitcoind_with_usdts])158# BCC: perf buffer handle function for inbound_messages159def handle_inbound(_, data, size):160""" Inbound message handler.162Called each time a message is submitted to the inbound_messages BPF table."""164event = bpf["inbound_messages"].event(data)165print_message(event, True)167# BCC: perf buffer handle function for outbound_messages169def handle_outbound(_, data, size):170""" Outbound message handler.172Called each time a message is submitted to the outbound_messages BPF table."""174event = bpf["outbound_messages"].event(data)175print_message(event, False)177# BCC: add handlers to the inbound and outbound perf buffers178bpf["inbound_messages"].open_perf_buffer(handle_inbound)179bpf["outbound_messages"].open_perf_buffer(handle_outbound)181print("Logging raw P2P messages.")182print("Messages larger than about 32kb will be cut off!")183print("Some messages might be lost!")184while True:185try:186bpf.perf_buffer_poll()187except KeyboardInterrupt:188exit()191if __name__ == "__main__":192if len(sys.argv) != 2:193print("USAGE:", sys.argv[0], "<pid of bitcoind>")194exit()195pid = sys.argv[1]196main(pid)
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.