eBPF 程序的挂载和执行在 Linux 内核中遵循一个统一的模式:
用户态工具 (bpftool / libbpf)
│
│ BPF_PROG_LOAD (加载 & 验证)
▼
BPF syscall (kernel/bpf/syscall.c)
│
│ link_create() / BPF_RAW_TRACEPOINT_OPEN / perf_event_open
▼
Attach Logic (根据 prog_type 分发到不同子系统)
│
├── bpf_tracing_prog_attach() → trampoline (fentry/fexit/lsm)
├── cgroup_bpf_link_attach() → cgroup BPF array
├── bpf_xdp_link_attach() → net_device XDP prog
├── bpf_perf_link_attach() → perf_event / kprobe
├── bpf_kprobe_multi_link_attach()→ fprobe
├── bpf_nf_link_attach() → netfilter hook
├── tcx_link_attach() → TC ingress/egress
├── netns_bpf_link_create() → netns (sk_lookup/flow_dissector)
├── bpf_struct_ops_link_create() → struct_ops map
└── sock_map_link_create() → sockmap
│
▼
Runtime Execution (内核执行流触发)
│
├── bpf_prog_run() / __bpf_prog_run() → 直接执行 JIT/解释器
├── bpf_prog_run_array() → 遍历 prog 数组执行
├── trampoline (__bpf_prog_enter + call) → fentry/fexit/lsm
└── bpf_prog_run_pin_on_cpu() → 绑定 CPU 执行
内核中定义了十几种 bpf_prog_type 和几十种 bpf_attach_type。
// include/uapi/linux/bpf.h: 1040-1074
enum bpf_prog_type {
BPF_PROG_TYPE_UNSPEC, // 0
BPF_PROG_TYPE_SOCKET_FILTER, // 1 经典 socket filter
BPF_PROG_TYPE_KPROBE, // 2 kprobe / kprobe_multi
BPF_PROG_TYPE_SCHED_CLS, // 3 TC cls/act, TCX, Netkit
BPF_PROG_TYPE_SCHED_ACT, // 4 TC action (已废弃,合并到 SCHED_CLS)
BPF_PROG_TYPE_TRACEPOINT, // 5 静态 tracepoint
BPF_PROG_TYPE_XDP, // 6 XDP: native/offload/generic
BPF_PROG_TYPE_PERF_EVENT, // 7 perf_event (uprobe/uretprobe)
BPF_PROG_TYPE_CGROUP_SKB, // 8 cgroup ingress/egress
BPF_PROG_TYPE_CGROUP_SOCK, // 9 cgroup sock_create/sock_release
BPF_PROG_TYPE_LWT_IN, // 10 轻量级隧道 ingress
BPF_PROG_TYPE_LWT_OUT, // 11 轻量级隧道 egress
BPF_PROG_TYPE_LWT_XMIT, // 12 轻量级隧道 transmit
BPF_PROG_TYPE_SOCK_OPS, // 13 TCP sock ops
BPF_PROG_TYPE_SK_SKB, // 14 sockmap stream parser/verdict
BPF_PROG_TYPE_CGROUP_DEVICE, // 15 cgroup device 访问控制
BPF_PROG_TYPE_SK_MSG, // 16 sockmap message verdict/redirect
BPF_PROG_TYPE_RAW_TRACEPOINT, // 17 raw tracepoint
BPF_PROG_TYPE_CGROUP_SOCK_ADDR, // 18 cgroup bind/connect/sendmsg/recvmsg
BPF_PROG_TYPE_LWT_SEG6LOCAL, // 19 SRv6 local endpoint
BPF_PROG_TYPE_LIRC_MODE2, // 20 红外遥控
BPF_PROG_TYPE_SK_REUSEPORT, // 21 SO_REUSEPORT 选择/迁移
BPF_PROG_TYPE_FLOW_DISSECTOR, // 22 flow dissector
BPF_PROG_TYPE_CGROUP_SYSCTL, // 23 cgroup sysctl
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, // 24 可写 raw tracepoint
BPF_PROG_TYPE_CGROUP_SOCKOPT, // 25 cgroup getsockopt/setsockopt
BPF_PROG_TYPE_TRACING, // 26 fentry/fexit/fmod_ret
BPF_PROG_TYPE_STRUCT_OPS, // 27 struct_ops (TCP CC 等)
BPF_PROG_TYPE_EXT, // 28 freplace (程序替换)
BPF_PROG_TYPE_LSM, // 29 Linux Security Module
BPF_PROG_TYPE_SK_LOOKUP, // 30 socket lookup 重定向
BPF_PROG_TYPE_SYSCALL, // 31 可在 BPF 中调用 syscall
BPF_PROG_TYPE_NETFILTER, // 32 netfilter hook
__MAX_BPF_PROG_TYPE
};
// include/uapi/linux/bpf.h: 1077-1136
enum bpf_attach_type {
BPF_CGROUP_INET_INGRESS, // cgroup ingress
BPF_CGROUP_INET_EGRESS, // cgroup egress
BPF_CGROUP_INET_SOCK_CREATE, // cgroup socket create
BPF_CGROUP_SOCK_OPS, // cgroup sock ops
BPF_SK_SKB_STREAM_PARSER, // sockmap stream parser
BPF_SK_SKB_STREAM_VERDICT, // sockmap stream verdict
BPF_CGROUP_DEVICE, // cgroup device
BPF_SK_MSG_VERDICT, // sk_msg verdict
BPF_CGROUP_INET4_BIND, // cgroup IPv4 bind
BPF_CGROUP_INET6_BIND, // cgroup IPv6 bind
BPF_CGROUP_INET4_CONNECT, // cgroup IPv4 connect
BPF_CGROUP_INET6_CONNECT, // cgroup IPv6 connect
BPF_CGROUP_INET4_POST_BIND, // cgroup IPv4 post-bind
BPF_CGROUP_INET6_POST_BIND, // cgroup IPv6 post-bind
BPF_CGROUP_UDP4_SENDMSG, // cgroup UDPv4 sendmsg
BPF_CGROUP_UDP6_SENDMSG, // cgroup UDPv6 sendmsg
BPF_LIRC_MODE2, // LIRC
BPF_FLOW_DISSECTOR, // flow dissector
BPF_CGROUP_SYSCTL, // cgroup sysctl
BPF_CGROUP_UDP4_RECVMSG, // cgroup UDPv4 recvmsg
BPF_CGROUP_UDP6_RECVMSG, // cgroup UDPv6 recvmsg
BPF_CGROUP_GETSOCKOPT, // cgroup getsockopt
BPF_CGROUP_SETSOCKOPT, // cgroup setsockopt
BPF_TRACE_RAW_TP, // raw tracepoint
BPF_TRACE_FENTRY, // fentry
BPF_TRACE_FEXIT, // fexit
BPF_MODIFY_RETURN, // fmod_ret
BPF_LSM_MAC, // LSM MAC hook
BPF_TRACE_ITER, // BPF iterator
BPF_CGROUP_INET4_GETPEERNAME, // cgroup getpeername
BPF_CGROUP_INET6_GETPEERNAME,
BPF_CGROUP_INET4_GETSOCKNAME, // cgroup getsockname
BPF_CGROUP_INET6_GETSOCKNAME,
BPF_XDP_DEVMAP, // XDP devmap 程序
BPF_CGROUP_INET_SOCK_RELEASE, // cgroup sock release
BPF_XDP_CPUMAP, // XDP cpumap 程序
BPF_SK_LOOKUP, // sk_lookup
BPF_XDP, // XDP 程序
BPF_SK_SKB_VERDICT, // sk_skb verdict (sockmap)
BPF_SK_REUSEPORT_SELECT, // reuseport select
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,// reuseport migrate
BPF_PERF_EVENT, // perf_event (kprobe/uprobe 传统方式)
BPF_TRACE_KPROBE_MULTI, // kprobe_multi
BPF_LSM_CGROUP, // LSM cgroup
BPF_STRUCT_OPS, // struct_ops
BPF_NETFILTER, // netfilter
BPF_TCX_INGRESS, // TCX ingress
BPF_TCX_EGRESS, // TCX egress
BPF_TRACE_UPROBE_MULTI, // uprobe_multi
BPF_CGROUP_UNIX_CONNECT, // cgroup UNIX connect
BPF_CGROUP_UNIX_SENDMSG, // cgroup UNIX sendmsg
BPF_CGROUP_UNIX_RECVMSG, // cgroup UNIX recvmsg
BPF_CGROUP_UNIX_GETPEERNAME, // cgroup UNIX getpeername
BPF_CGROUP_UNIX_GETSOCKNAME, // cgroup UNIX getsockname
BPF_NETKIT_PRIMARY, // Netkit primary
BPF_NETKIT_PEER, // Netkit peer
BPF_TRACE_KPROBE_SESSION, // kprobe session
BPF_TRACE_UPROBE_SESSION, // uprobe session
};
KPROBE | PERF_EVENT | perf_event_create_kernel_counter | kernel/events/core.c |
KPROBE | TRACE_KPROBE_MULTI | kprobe_multi_link_handler() | kernel/trace/bpf_trace.c |
KPROBE | TRACE_KPROBE_SESSION | kernel/trace/bpf_trace.c | |
KPROBE | TRACE_UPROBE_MULTI | kernel/trace/bpf_trace.c | |
PERF_EVENT | PERF_EVENT | perf_trace_run_bpf_submit() | kernel/events/core.cinclude/trace/perf.h |
TRACEPOINT | PERF_EVENT | trace_call_bpf()perf_trace_run_bpf_submit() | kernel/trace/bpf_trace.c |
RAW_TRACEPOINT | TRACE_RAW_TP | bpf_trace_runX()__bpf_trace_run() | kernel/trace/bpf_trace.c |
TRACING | TRACE_FENTRY | __bpf_prog_enter → prog→bpf_func | kernel/bpf/trampoline.carch/x86/net/bpf_jit_comp.c |
TRACING | TRACE_FEXIT | ||
TRACING | MODIFY_RETURN | ||
EXT | kernel/bpf/trampoline.c | ||
LSM | LSM_MAC | security/security.ckernel/bpf/bpf_lsm.c | |
LSM | LSM_CGROUP | bpf_prog_run_array_cg() | kernel/bpf/cgroup.c |
STRUCT_OPS | STRUCT_OPS | tcp_congestion_ops) | kernel/bpf/bpf_struct_ops.cnet/ipv4/bpf_tcp_ca.c |
XDP | XDP | bpf_prog_run_xdp() / bpf_prog_run_generic_xdp() | include/net/xdp.hnet/core/dev.c |
XDP | XDP_DEVMAP | kernel/bpf/devmap.c | |
XDP | XDP_CPUMAP | kernel/bpf/cpumap.c | |
SCHED_CLS | TCX_INGRESS | sch_handle_ingress()tcx_run() | net/core/dev.c |
SCHED_CLS | TCX_EGRESS | sch_handle_egress()tcx_run() | net/core/dev.c |
SCHED_CLS | NETKIT_PRIMARY/PEER | drivers/net/netkit.c | |
CGROUP_SKB | INET_INGRESS | __cgroup_bpf_run_filter_skb(sk, skb, CGROUP_INET_INGRESS) | net/core/filter.c:150 |
CGROUP_SKB | INET_EGRESS | __cgroup_bpf_run_filter_skb(sk, skb, CGROUP_INET_EGRESS) | net/ipv4/ip_output.c:320 |
CGROUP_SOCK | INET_SOCK_CREATE | BPF_CGROUP_RUN_PROG_INET_SOCK() | net/ipv4/af_inet.c |
CGROUP_SOCK | INET_SOCK_RELEASE | ||
CGROUP_SOCK_ADDR | INET4_BIND | ||
CGROUP_SOCKOPT | GETSOCKOPT/SETSOCKOPT | __cgroup_bpf_run_filter_{get,set}sockopt() | kernel/bpf/cgroup.c |
SOCK_OPS | CGROUP_SOCK_OPS | BPF_CGROUP_RUN_PROG_SOCK_OPS() | net/ipv4/tcp_input.c:177 |
CGROUP_SYSCTL | CGROUP_SYSCTL | __cgroup_bpf_run_filter_sysctl() | kernel/bpf/cgroup.c |
CGROUP_DEVICE | CGROUP_DEVICE | __cgroup_bpf_run_filter_dev() | kernel/bpf/cgroup.c |
SOCKET_FILTER | sk_filter_trim_cap()bpf_prog_run_save_cb() | net/core/filter.c:134 | |
SK_SKB | STREAM_PARSER/VERDICT | net/core/skmsg.cnet/core/sock_map.c | |
SK_MSG | SK_MSG_VERDICT | net/core/skmsg.c | |
SK_LOOKUP | SK_LOOKUP | bpf_sk_lookup_run_v4/v6()__inet_lookup_listener() 等 | include/linux/filter.h:1664 |
SK_REUSEPORT | REUSEPORT_SELECT | reuseport_select_sock()run_bpf_filter() | net/core/sock_reuseport.c:568 |
FLOW_DISSECTOR | FLOW_DISSECTOR | __skb_flow_dissect() | net/core/flow_dissector.c |
NETFILTER | NETFILTER | nf_hook_run_bpf() | net/netfilter/nf_bpf_link.c:11 |
LWT_IN/OUT/XMIT | net/core/lwt_bpf.c | ||
SYSCALL | bpf_sys_bpf() helper 执行 | kernel/bpf/syscall.c |
所有 eBPF 程序的最终执行都经过以下几个核心函数。
// include/linux/filter.h: 697-721
// 所有 bpf_prog_run* 函数的共同底层
static __always_inline u32 __bpf_prog_run(const struct bpf_prog *prog,
constvoid *ctx,
bpf_dispatcher_fn dfunc)
{
u32 ret;
cant_migrate();
if (static_branch_unlikely(&bpf_stats_enabled_key)) {
structbpf_prog_stats *stats;
u64 duration, start = sched_clock();
ret = dfunc(ctx, prog->insnsi, prog->bpf_func);
duration = sched_clock() - start;
stats = this_cpu_ptr(prog->stats);
// ... update stats ...
} else {
ret = dfunc(ctx, prog->insnsi, prog->bpf_func);
}
return ret;
}
关键点: prog->bpf_func 是 JIT 编译后的二进制代码入口;若未 JIT 则走解释器路径。dfunc 是分发器函数(bpf_dispatcher_nop_func 或 BPF_DISPATCHER_FUNC)。
// include/linux/filter.h: 723-726
static __always_inline u32 bpf_prog_run(const struct bpf_prog *prog, constvoid *ctx)
{
return __bpf_prog_run(prog, ctx, bpf_dispatcher_nop_func);
}
// include/linux/bpf.h: 2271-2298
static __always_inline u32
bpf_prog_run_array(const struct bpf_prog_array *array,
constvoid *ctx, bpf_prog_run_fn run_prog)
{
conststructbpf_prog_array_item *item;
conststructbpf_prog *prog;
structbpf_run_ctx *old_run_ctx;
structbpf_trace_run_ctxrun_ctx;
u32 ret = 1;
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "no rcu lock held");
if (unlikely(!array))
return ret;
migrate_disable();
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
item = &array->items[0];
while ((prog = READ_ONCE(item->prog))) {
run_ctx.bpf_cookie = item->bpf_cookie;
ret &= run_prog(prog, ctx); // 结果按位与!
item++;
}
bpf_reset_run_ctx(old_run_ctx);
migrate_enable();
return ret;
}
典型用法:
ret = bpf_prog_run_array(rcu_dereference(&bpf_prog_array), ctx, bpf_prog_run);
此函数是 cgroup、tracepoint、kprobe_multi 等多个子系统的核心调度函数。所有程序的返回值做按位与(ret &= run_prog(...))处理——只要有一个程序返回 0,最终结果就是 0。
Trampoline 是最复杂的执行路径,它动态生成汇编代码,在目标函数前后插入 BPF 程序的调用。
// kernel/bpf/trampoline.c: 886-893
/* The logic is similar to bpf_prog_run(), but with an explicit
* rcu_read_lock() and migrate_disable() which are required
* for the trampoline. The macro is split into
* call __bpf_prog_enter
* call prog->bpf_func
* call __bpf_prog_exit
*
* __bpf_prog_enter returns:
* 0 - skip execution of the bpf prog
* 1 - execute bpf prog
* [2..MAX_U64] - execute bpf prog and record execution time.
*/
FENTRY trampoline 汇编结构 (x86):
push rbp
mov rbp, rsp
sub rsp, frame_size // 分配栈帧,保存 ctx 参数
push rbx
// 保存原始函数参数到栈上
// === 每个 fentry prog 执行 ===
call __bpf_prog_enter // rcu_read_lock + migrate_disable
mov rbx, rax // 保存 start_time
test rax, rax // 检查是否应该跳过
je skip_prog // 重入检测失败 → 跳过
lea rdi, [rbp - frame_size] // R1 = ctx
call prog->bpf_func // 调用 JIT 后的 BPF 程序
call __bpf_prog_exit // rcu_read_unlock + migrate_enable + stats
// === prog 执行结束 ===
// ==== 调用原始函数 ====
call original_func+5 // 跳过 ftrace nop,调用被 hook 的函数体
// === 每个 fexit prog 执行 === (结构同上)
// ...
leave
ret
同步保护机制:trampoline 对每个程序提供三种 enter 策略:
// kernel/bpf/trampoline.c: 1065-1078
bpf_trampoline_enter_tbpf_trampoline_enter(const struct bpf_prog *prog)
{
if (bpf_prog_check_recur(prog))
return sleepable ? __bpf_prog_enter_sleepable_recur :
__bpf_prog_enter_recur; // 可重入程序:带 recursion counter
if (resolve_prog_type(prog) == BPF_PROG_TYPE_LSM &&
prog->expected_attach_type == BPF_LSM_CGROUP)
return __bpf_prog_enter_lsm_cgroup; // LSM cgroup 特殊路径
return sleepable ? __bpf_prog_enter_sleepable : __bpf_prog_enter;
// sleepable 使用 rcu_read_lock_trace(),否则使用 rcu_read_lock()
}
追踪类 BPF 程序覆盖内核的观测/调试/性能分析场景。
// kernel/bpf/syscall.c: 5608-5617 (link_create 中的分发)
case BPF_PROG_TYPE_KPROBE:
if (attr->link_create.attach_type == BPF_PERF_EVENT)
ret = bpf_perf_link_attach(attr, prog); // 传统 perf kprobe
elseif (attr->link_create.attach_type == BPF_TRACE_KPROBE_MULTI ||
attr->link_create.attach_type == BPF_TRACE_KPROBE_SESSION)
ret = bpf_kprobe_multi_link_attach(attr, prog); // kprobe_multi
elseif (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI ||
attr->link_create.attach_type == BPF_TRACE_UPROBE_SESSION)
ret = bpf_uprobe_multi_link_attach(attr, prog); // uprobe_multi
break;
kprobe ftrace callback → perf_trace_buf_submit() → trace_call_bpf()
// kernel/trace/bpf_trace.c: 110-140
unsignedinttrace_call_bpf(struct trace_event_call *call, void *ctx)
{
unsignedint ret;
cant_sleep();
if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) {
// 同一 CPU 已有 BPF 程序在运行,跳过(防止无限递归)
rcu_read_lock();
bpf_prog_inc_misses_counters(rcu_dereference(call->prog_array));
rcu_read_unlock();
ret = 0;
goto out;
}
// 通过 perf_trace_buf_submit 调用
// ...
}
// kernel/trace/bpf_trace.c: 2752-2764
staticintkprobe_multi_link_handler(struct fprobe *fp, unsignedlong fentry_ip,
unsignedlong ret_ip, struct ftrace_regs *fregs,
void *data)
{
structbpf_kprobe_multi_link *link;
link = container_of(fp, struct bpf_kprobe_multi_link, fp);
// ...
err = kprobe_multi_link_prog_run(link, ftrace_get_entry_ip(fentry_ip), ...);
// ...
}
// kernel/trace/bpf_trace.c: 2715-2749
staticintkprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
unsignedlong entry_ip, ...)
{
structbpf_kprobe_multi_run_ctxrun_ctx = { ... };
// ...
migrate_disable();
rcu_read_lock();
regs = ftrace_partial_regs(fregs, bpf_kprobe_multi_pt_regs_ptr());
old_run_ctx = bpf_set_run_ctx(&run_ctx.session_ctx.run_ctx);
err = bpf_prog_run(link->link.prog, regs); // ctx = pt_regs
bpf_reset_run_ctx(old_run_ctx);
rcu_read_unlock();
migrate_enable();
return err;
}
性能优势: kprobe_multi 基于 fprobe(批量 ftrace hook),一次注册可以挂载到多个函数,比逐个注册 perf_event kprobe 效率更高。
内核函数入口指令
│
│ ftrace (nop → call)
▼
ftrace 回调
│
├─[传统 kprobe]
│ kprobe 处理 → kretprobe_handler / pre_handler
│ └─ perf_trace_buf_submit(perf_call_bpf_enter)
│ └─ trace_call_bpf(call, regs)
│ └─ bpf_prog_run_array(array, regs, bpf_prog_run)
│
└─[kprobe_multi]
fprobe → kprobe_multi_link_handler(fentry_ip, fregs)
└─ kprobe_multi_link_prog_run(link, entry_ip, fregs)
└─ bpf_prog_run(prog, ftrace_partial_regs(fregs))
trace_xxx() 宏展开
│
│ if (static_key_false(&xxx.key))
▼
__DO_TRACE() → trace_xxx 回调数组
│
│ 如果有 BPF program_array
▼
trace_call_bpf(call, entry) // kernel/trace/bpf_trace.c:110
└─ bpf_prog_run_array(prog_array, entry, bpf_prog_run)
// kernel/trace/bpf_trace.c: 2289-2296
// 通过宏展开为 bpf_trace_run1 ~ bpf_trace_run12
void bpf_trace_run##x(struct bpf_raw_tp_link *link, ...) {
u64 args[x];
// 收集参数
__bpf_trace_run(link, args);
}
EXPORT_SYMBOL_GPL(bpf_trace_run##x)
// kernel/trace/bpf_trace.c: 2241
void __bpf_trace_run(struct bpf_raw_tp_link *link, u64 *args)
{
structbpf_trace_run_ctxrun_ctx;
// ...
bpf_prog_run(link->link.prog, args); // args 直接作为 ctx
}
// kernel/events/core.c
// perf_event_create_kernel_counter → event->prog = prog
// 当 perf event 触发时:
// perf_event_overflow → perf_event_output_forward → bpf_overflow_handler
// └─ bpf_prog_run(event->prog, &ctx) // ctx = bpf_perf_event_data_kern
这是内核中最精妙的 BPF 挂载机制。通过动态生成 trampoline 代码,在目标函数的前/后插入 BPF 程序。
bpf(BPF_LINK_CREATE, { prog_fd, target_fd, attach_type=BPF_TRACE_FENTRY })
│
│ link_create() → kernel/bpf/syscall.c:5524
│ case BPF_PROG_TYPE_TRACING:
│ if (expected_attach_type == BPF_TRACE_FENTRY/FEXIT/MODIFY_RETURN)
│ └─ bpf_tracing_prog_attach() // syscall.c:3452
│ │
│ ├─ bpf_check_attach_target() // 验证目标函数签名
│ ├─ bpf_trampoline_get(key, &tgt_info) // 获取或创建 trampoline
│ │ │
│ │ ├─ bpf_trampoline_lookup(key) // 查找已有 trampoline
│ │ └─ 如不存在,创建新 trampoline 分配 executable memory
│ │
│ ├─ bpf_trampoline_link_prog(&link->link, tr) // 链接到 trampoline
│ │ │
│ │ ├─ 根据 attach_type 决定 tr->progs_hlist 分组:
│ │ │ BPF_TRACE_FENTRY → BPF_TRAMP_FENTRY slot
│ │ │ BPF_TRACE_FEXIT → BPF_TRAMP_FEXIT slot
│ │ │ BPF_MODIFY_RETURN → BPF_TRAMP_MODIFY_RETURN slot
│ │ │
│ │ └─ bpf_trampoline_update(tr) // 调用 ++
│ │ └─ arch_prepare_bpf_trampoline()
│ │ │
│ │ │ // x86: arch/x86/net/bpf_jit_comp.c:3066
│ │ │ __arch_prepare_bpf_trampoline()
│ │ │ 生成如下汇编结构:
│ │ │ ┌─────────────────────────────────┐
│ │ │ │ 保存原函数参数到栈 │
│ │ │ │ for each FENTRY prog: │
│ │ │ │ call __bpf_prog_enter │
│ │ │ │ je skip │
│ │ │ │ call prog->bpf_func │
│ │ │ │ call __bpf_prog_exit │
│ │ │ │ call original_func+X86_PATCH_SIZE│
│ │ │ │ for each FEXIT prog: │
│ │ │ │ call __bpf_prog_enter │
│ │ │ │ je skip │
│ │ │ │ call prog->bpf_func │
│ │ │ │ call __bpf_prog_exit │
│ │ │ │ 恢复返回值 & 返回 │
│ │ │ └──────────────────────────────────┘
│ │ │
│ │ └─ register_ftrace_direct_multi()
│ │ 将目标函数开头的 5 字节 nop 替换为
│ │ call <trampoline>
│ │
│ └─ bpf_link_prime → 返回 link fd 给用户
// arch/x86/net/bpf_jit_comp.c: 3010-3065
// 注释详细描述了生成的汇编结构:
/*
* push rbp
* mov rbp, rsp
* sub rsp, 16 // space for skb and dev
* push rbx // temp regs to pass start time
* mov qword ptr [rbp - 16], rdi // save skb pointer to stack
* mov qword ptr [rbp - 8], rsi // save dev pointer to stack
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
* mov rbx, rax // remember start time
* lea rdi, [rbp - 16] // R1==ctx of bpf prog
* call addr_of_jited_FENTRY_prog
* ...
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable
* ...
* call eth_type_trans+5 // execute body of eth_type_trans
* ...
* call addr_of_jited_FEXIT_prog // bpf prog can access return value
* ...
* ret // return to original caller
*/
// kernel/bpf/trampoline.c: 891
// fmod_ret 的 trampoline 不使用 fexit,而是在原始函数返回后
// 调用 fmod_ret 程序,用其返回值替换原函数的返回值。
// 对应的 trampoline flags:
// flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME
// kernel/bpf/syscall.c: 5590-5592
case BPF_PROG_TYPE_XDP:
ret = bpf_xdp_link_attach(attr, prog);
break;
bpf_xdp_link_attach() 在 net/core/dev.c:10443,将程序挂载到 net_device 的 XDP hook 上。
| Native XDP | bpf_prog_run_xdp(prog, xdp_buff) (在 include/net/xdp.h:650) | |
| Generic XDP | netif_receive_skb_core() | bpf_prog_run_generic_xdp(skb, xdp, prog)bpf_prog_run_xdp(prog, xdp) (在 net/core/dev.c:5232) |
| Offload XDP |
// include/net/xdp.h: 650-665
static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
struct xdp_buff *xdp)
{
/* Driver XDP hooks are invoked within a single NAPI poll cycle and thus
* under local_bh_disable(), which provides the needed RCU protection
* for accessing map entries.
*/
u32 act = __bpf_prog_run(prog, xdp, BPF_DISPATCHER_FUNC(xdp));
if (static_branch_unlikely(&bpf_master_redirect_enabled_key)) {
if (act == XDP_TX && netif_is_bond_slave(xdp->rxq->dev))
act = xdp_master_redirect(xdp);
}
return act;
}
关键点: XDP 在 NAPI poll 上下文(软中断)中执行,已有 local_bh_disable() 保护,所以不需要显式的 rcu_read_lock();但不同于其他路径,这里需要使用 BPF_DISPATCHER_FUNC 分发器以支持 map 查找的优化路径。
XDP_PASS | ||
XDP_DROP | ||
XDP_TX | xdp_do_redirect | |
XDP_REDIRECT | xdp_do_redirect() | |
XDP_ABORTED |
// net/core/dev.c: 5232-5280
u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
struct bpf_prog *xdp_prog)
{
// ... 调整 skb 指针、设置 data_hard_start ...
act = bpf_prog_run_xdp(xdp_prog, xdp);
// 根据 act 做相应处理
}
调用链:netif_receive_skb_core() → do_xdp_generic() → bpf_prog_run_generic_xdp()
// kernel/bpf/syscall.c: 5593-5598
case BPF_PROG_TYPE_SCHED_CLS:
if (attr->link_create.attach_type == BPF_TCX_INGRESS ||
attr->link_create.attach_type == BPF_TCX_EGRESS)
ret = tcx_link_attach(attr, prog); // TCX
else
ret = netkit_link_attach(attr, prog); // Netkit
// net/core/dev.c: 4330-4372
static __always_inline enum tcx_action_base
tcx_run(const struct bpf_mprog_entry *entry, struct sk_buff *skb,
constbool needs_mac)
{
conststructbpf_mprog_fp *fp;
conststructbpf_prog *prog;
int ret = TCX_NEXT;
if (needs_mac) __skb_push(skb, skb->mac_len);
bpf_mprog_foreach_prog(entry, fp, prog) {
bpf_compute_data_pointers(skb);
ret = bpf_prog_run(prog, skb); // ctx = sk_buff
if (ret != TCX_NEXT) break; // 直到有程序返回非 TCX_NEXT
}
if (needs_mac) __skb_pull(skb, skb->mac_len);
return tcx_action_code(skb, ret);
}
// 调用者:
// sch_handle_ingress() → tcx_run(entry, skb, true)
// sch_handle_egress() → tcx_run(entry, skb, false)
// net/sched/cls_bpf.c: 101
filter_res = bpf_prog_run(prog->filter, skb);
// net/sched/act_bpf.c:
// tc action 执行: bpf_prog_run(prog, skb)
// kernel/bpf/syscall.c: 5551-5552 (link_create)
case BPF_PROG_TYPE_CGROUP_SKB:
ret = cgroup_bpf_link_attach(attr, prog);
break;
// kernel/bpf/cgroup.c: 1562-1620
int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb,
enum cgroup_bpf_attach_type atype)
{
// ...
cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
bpf_compute_and_save_data_end(skb, &saved_data_end);
if (atype == CGROUP_INET_EGRESS) {
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, skb,
__bpf_prog_run_save_cb, 0, &flags);
// 返回值转换: 1→keep, 0→drop, 2/3→带 CN 标记
} else { // CGROUP_INET_INGRESS
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, skb,
bpf_prog_run_save_cb, 0, NULL);
// 非 1 表示拒绝
}
}
CGROUP_INET_INGRESS | sk_filter_trim_cap() | net/core/filter.c:150 |
CGROUP_INET_EGRESS | __ip_local_out()__ip6_local_out() / ip_output() | net/ipv4/ip_output.c:320 |
// net/core/filter.c: 150
// 在 socket filter 之后立即执行 cgroup ingress BPF
err = BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb);
// net/ipv4/ip_output.c: 320
// 在 IP 层输出路径执行 cgroup egress BPF
ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
// kernel/bpf/cgroup.c: 1640-1648
int __cgroup_bpf_run_filter_sk(struct sock *sk, enum cgroup_bpf_attach_type atype)
{
structcgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
return bpf_prog_run_array_cg(&cgrp->bpf, atype, sk, bpf_prog_run, 0, NULL);
}
CGROUP_INET_SOCK_CREATE | __inet_create()inet6_create() | |
CGROUP_INET4_BIND | inet_bind() | |
CGROUP_INET6_BIND | inet6_bind() | |
CGROUP_INET4_CONNECT | __inet_stream_connect()inet_dgram_connect() | |
CGROUP_INET4_POST_BIND | inet_bind() | |
CGROUP_UDP4_SENDMSG | udp_sendmsg() | |
CGROUP_UDP4_RECVMSG | udp_recvmsg() | |
BPF_CGROUP_INET_SOCK_RELEASE |
// kernel/bpf/cgroup.c: 1732
return bpf_prog_run_array_cg(&cgrp->bpf, atype, sock_ops, bpf_prog_run, 0, NULL);
调用点分散在 TCP 状态机各处:
// net/ipv4/tcp_input.c: 177, 196 等
BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops);
支持的操作阶段包括(通过 BPF_SOCK_OPS_* 枚举):
BPF_SOCK_OPS_TCP_CONNECT_CBBPF_SOCK_OPS_ACTIVE_ESTABLISHED_CBBPF_SOCK_OPS_PASSIVE_ESTABLISHED_CBBPF_SOCK_OPS_STATE_CBBPF_SOCK_OPS_RTT_CBBPF_SOCK_OPS_WRITE_HDR_OPT_CB 等// kernel/bpf/syscall.c: 5581-5583
case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_SK_LOOKUP:
ret = netns_bpf_link_create(attr, prog);
break;
程序挂载在网络命名空间(struct net)上。
// include/linux/filter.h: 1631-1701
// BPF_PROG_SK_LOOKUP_RUN_ARRAY 宏 —— 特殊的遍历逻辑
#define BPF_PROG_SK_LOOKUP_RUN_ARRAY(array, ctx, func) \
({ \
// ... 遍历所有程序
// 特殊逻辑: 每个程序可以选择 selected_sk
// SK_PASS + selected_sk → 使用选择的 socket
// SK_DROP → 拒绝连接
// ...
_all_pass || _selected_sk ? SK_PASS : SK_DROP; \
})
// include/linux/filter.h: 1664 - 具体执行封装
staticinlineboolbpf_sk_lookup_run_v4(...)
{
run_array = rcu_dereference(net->bpf.run_array[NETNS_BPF_SK_LOOKUP]);
if (run_array) {
structbpf_sk_lookup_kernctx = { .family = AF_INET, ... };
act = BPF_PROG_SK_LOOKUP_RUN_ARRAY(run_array, ctx, bpf_prog_run);
if (act == SK_PASS) {
selected_sk = ctx.selected_sk; // BPF 程序选中的 socket
}
}
*psk = selected_sk;
return no_reuseport;
}
在 __inet_lookup_listener() 和 inet6_lookup_listener() 中,在 socket 查找过程中被调用,允许 BPF 程序重定向连接到其他 socket 或直接拒绝。
// net/core/sock_reuseport.c: 497-525
static struct sock *run_bpf_filter(struct sock_reuseport *reuse, u16 socks,
struct bpf_prog *prog, struct sk_buff *skb,
u32 hdr_len)
{
structsk_buff *nskb = NULL;
u32 index;
// 如果需要完整的 L4 header,push skb
if (hdr_len) nskb = skb_clone(skb, GFP_ATOMIC);
index = bpf_prog_run_save_cb(prog, nskb ?: skb);
// 根据 index 选择对应的 socket
if (index >= socks) returnNULL;
return reuse->socks[index];
}
// 调用位置: reuseport_select_sock() → net/core/sock_reuseport.c:568
struct sock *reuseport_select_sock(struct sock *sk, u32 hash,
struct sk_buff *skb, u32 hdr_len)
{
// ...
if (prog)
sk2 = run_bpf_filter(reuse, socks, prog, skb, hdr_len);
else
sk2 = reuseport_select_sock_by_hash(reuse, hash, socks);
// ...
}
// kernel/bpf/syscall.c: 5561-5579
case BPF_PROG_TYPE_LSM:
if (attr->link_create.attach_type != prog->expected_attach_type)
ret = -EINVAL;
if (prog->expected_attach_type == BPF_LSM_CGROUP)
ret = cgroup_bpf_link_attach(attr, prog); // 挂载到 cgroup
else
ret = bpf_tracing_prog_attach(prog, ...); // 挂载到 trampoline
LSM BPF 宏挂载模式采用了一个精巧的集成方案:
static_call 机制static_call 槽位static_call 直接跳转到 BPF trampoline内核中的 LSM hook 调用宏:
// security/security.c: 961-978
#define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...) \
do { \
if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) { \
R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__); \
if (R != LSM_RET_DEFAULT(HOOK)) goto LABEL; \
} \
} while (0);
#define call_int_hook(HOOK, ...) \
({ \
int RC = LSM_RET_DEFAULT(HOOK); \
LSM_LOOP_UNROLL(__CALL_STATIC_INT, RC, HOOK, OUT, __VA_ARGS__); \
OUT: \
RC; \
})
所以完整的 LSM BPF 执行流程为:
内核代码调用 security_xxx() LSM hook
│
│ 展开为 call_int_hook(xxx, ...)
▼
static_call(LSM_STATIC_CALL(xxx, N))(...)
│
│ 如果该 slot 被 BPF LSM 程序占用
▼
bpf_lsm_xxx trampoline (通过 lsm_trampoline 生成)
│
├─ __bpf_prog_enter_lsm_cgroup(prog, run_ctx) // LSM_CGROUP 走此路径
│ └─ rcu_read_lock() + 获取 cgroup bpf prog
│
├─ call prog->bpf_func(ctx) // 执行 BPF LSM 程序
│ ctx = 原始 LSM hook 的参数
│ 返回值: 0=允许, -EPERM=拒绝
│
└─ __bpf_prog_exit(...) // rcu_read_unlock + enable
对于 BPF_LSM_CGROUP 挂载类型,LSM 程序运行在 cgroup 上下文中,使用 bpf_prog_run_array_cg() 遍历 cgroup 层级中的所有 LSM 程序。
// net/netfilter/nf_bpf_link.c: 11-21
staticunsignedintnf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb,
const struct nf_hook_state *s)
{
conststructbpf_prog *prog = bpf_prog;
structbpf_nf_ctxctx = {
.state = s,
.skb = skb,
};
return bpf_prog_run_pin_on_cpu(prog, &ctx);
}
通过 nf_register_net_hook() 注册到 netfilter hook 链中,与其他 netfilter 模块(iptables 等)并列执行。
Struct_Ops 允许用 BPF 程序替换/实现内核中的结构体函数指针。最典型的例子是 TCP 拥塞控制算法。
// kernel/bpf/syscall.c: 5532-5533
if (attr->link_create.attach_type == BPF_STRUCT_OPS)
return bpf_struct_ops_link_create(attr);
// net/ipv4/bpf_tcp_ca.c: 311-338
// 1. 定义一个 tcp_congestion_ops 实例,其函数指针指向 trampoline 入口
staticstructtcp_congestion_ops __bpf_ops_tcp_congestion_ops = {
.ssthresh = bpf_tcp_ca_ssthresh, // trampoline stub
.cong_avoid = bpf_tcp_ca_cong_avoid,
.set_state = bpf_tcp_ca_set_state,
// ...
};
// 2. 注册为 struct_ops
staticstructbpf_struct_opsbpf_tcp_congestion_ops = {
.verifier_ops = &bpf_tcp_ca_verifier_ops,
.reg = bpf_tcp_ca_reg, // 注册到内核: tcp_register_congestion_control()
.unreg = bpf_tcp_ca_unreg, // 注销
.update = bpf_tcp_ca_update, // 更新 CFI stubs
.init_member = bpf_tcp_ca_init_member,
.name = "tcp_congestion_ops",
.cfi_stubs = &__bpf_ops_tcp_congestion_ops,
};
BPF 用户态加载 struct_ops BPF 程序
│
│ bpf(BPF_MAP_CREATE) → BPF_MAP_TYPE_STRUCT_OPS
│ bpf(BPF_LINK_CREATE, attach_type=BPF_STRUCT_OPS)
▼
bpf_struct_ops_link_create()
│
├─ 为每个成员函数生成 trampoline (通过 cfistubs)
├─ st_ops->reg() → tcp_register_congestion_control()
│ └─ 将 BPF 实现的 tcp_congestion_ops 注册到内核
│
└─ 内核调用 TCP 拥塞控制回调时:
tcp_congestion_ops→ssthresh(sk)
│
│ 实际指向 CFI stub
▼
bpf_tcp_ca_ssthresh (trampoline)
└─ __bpf_prog_enter → call prog->bpf_func → __bpf_prog_exit
关键点: cfi_stubs 是内核为每个 BPF struct_ops 创建的 trampoline,实现 CFI (Control Flow Integrity) 保护,同时负责 rcu_read_lock/migrate_disable 等上下文保护。
// kernel/bpf/cgroup.c
// 在用户空间访问设备文件时触发:
// __cgroup_bpf_run_filter_dev(cgrp, dev, access_type)
// └─ bpf_prog_run_array_cg(&cgrp->bpf, CGROUP_DEVICE, ...)
// kernel/bpf/cgroup.c
int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
struct ctl_table *table, int write, ...)
{
// ...
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, &ctx, bpf_prog_run, 0, NULL);
// 返回值 0 = 拒绝修改, 1 = 允许
}
调用位置:/proc/sys/ 的读写操作路径上。
// kernel/bpf/cgroup.c
int __cgroup_bpf_run_filter_setsockopt(struct sock *sock, int *level, int *optname, ...)
int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, int optname, ...)
调用位置:do_sock_setsockopt() / do_sock_getsockopt() 之前。
// net/core/filter.c: 134
intsk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsignedint cap)
{
structsk_filter *filter;
// ...
filter = rcu_dereference(sk->sk_filter);
if (filter) {
pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
// ...
}
}
调用链:sock_queue_rcv_skb() → 协议层接收 → sk_filter_trim_cap()。
通过 sockmap 将 socket 和 BPF 程序关联。程序在数据流入/流出 sockmap 管理的 socket 时触发。
// kernel/bpf/syscall.c: 5581-5583
case BPF_PROG_TYPE_FLOW_DISSECTOR:
ret = netns_bpf_link_create(attr, prog);
执行位置:__skb_flow_dissect() 在解析数据包 flow 信息时调用 BPF 程序。
// include/uapi/linux/bpf.h: 1072
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
这是一个特殊类型,不能像其他 BPF 程序那样 attach 到内核 hook 点。它只能通过 bpf_sys_bpf() helper 在另一个 BPF 程序中被调用,允许 BPF 程序安全地执行 syscall。
用户态: bpftool prog load / bpftool link pin / libbpf
│
│ bpf(BPF_LINK_CREATE, {prog_fd, attach_type, ...})
▼
link_create() // kernel/bpf/syscall.c:5524
│
├─ prog = bpf_prog_get(prog_fd) // 获取已验证的程序
├─ bpf_prog_attach_check_attach_type() // 验证 attach_type 兼容性
│
└─ switch (prog->type):
│
├─ CGROUP_SKB/SOCK/SOCK_ADDR/SOCK_OPS/DEVICE/SYSCTL/SOCKOPT
│ └─ cgroup_bpf_link_attach(attr, prog) → 挂载到 cgroup bpf array
│
├─ EXT / TRACING (FENTRY/FEXIT/MODIFY_RETURN)
│ └─ bpf_tracing_prog_attach(prog, target_fd, btf_id, ...)
│ ├─ bpf_check_attach_target() → 验证目标函数
│ ├─ bpf_trampoline_get() → 获取/创建 trampoline
│ ├─ bpf_trampoline_link_prog() → 链接程序
│ │ └─ bpf_trampoline_update() → 生成并安装 trampoline
│ │ └─ arch_prepare_bpf_trampoline()
│ │ └─ register_ftrace_direct_multi()
│ └─ bpf_link_prime() → 返回 link fd
│
├─ LSM (LSM_MAC)
│ └─ bpf_tracing_prog_attach() (同上 trampoline 路径)
│
├─ LSM (LSM_CGROUP)
│ └─ cgroup_bpf_link_attach() → cgroup 路径
│
├─ TRACING (RAW_TP)
│ └─ bpf_raw_tp_link_attach() → raw tracepoint
│
├─ TRACING (ITER)
│ └─ bpf_iter_link_attach() → iterator
│
├─ KPROBE (PERF_EVENT)
│ └─ bpf_perf_link_attach(attr, prog) → perf_event kprobe
│
├─ KPROBE (KPROBE_MULTI/KPROBE_SESSION)
│ └─ bpf_kprobe_multi_link_attach() → fprobe
│
├─ KPROBE (UPROBE_MULTI/UPROBE_SESSION)
│ └─ bpf_uprobe_multi_link_attach() → fprobe
│
├─ PERF_EVENT / TRACEPOINT
│ └─ bpf_perf_link_attach() → perf_event
│
├─ XDP
│ └─ bpf_xdp_link_attach() → 挂载到 net_device
│
├─ SCHED_CLS (TCX_INGRESS/EGRESS)
│ └─ tcx_link_attach() → 挂载到 net_device
│
├─ SCHED_CLS (NETKIT)
│ └─ netkit_link_attach() → 挂载到 netkit 设备
│
├─ NETFILTER
│ └─ bpf_nf_link_attach() → 挂载到 netfilter hook
│
├─ FLOW_DISSECTOR / SK_LOOKUP
│ └─ netns_bpf_link_create() → 挂载到 net namespace
│
├─ STRUCT_OPS
│ └─ bpf_struct_ops_link_create() → 注册 struct_ops 到子系统
│
└─ SK_MSG / SK_SKB
└─ sock_map_link_create() → 挂载到 sockmap
内核执行流到达 hook 点
│
├───[Trampoline 类] (fentry/fexit/lsm/modify_ret)
│ │
│ │ 目标函数开头 5-byte nop 已被替换为 call <trampoline>
│ ▼
│ trampoline asm code (动态生成)
│ ├─ 保存寄存器 & 参数到栈
│ ├─ for each fentry prog:
│ │ ├─ call __bpf_prog_enter(prog, run_ctx)
│ │ │ ├─ rcu_read_lock() / rcu_read_lock_trace() (sleepable)
│ │ │ ├─ migrate_disable()
│ │ │ ├─ 检查重入保护 (per-CPU prog->active counter)
│ │ │ └─ return start_time or 0 (skip)
│ │ ├─ if (start_time != 0): call prog->bpf_func(ctx)
│ │ │ └─ ctx = 原始函数参数 / pt_regs 等
│ │ └─ __bpf_prog_exit(prog, start_time, run_ctx)
│ │ ├─ bpf_reset_run_ctx()
│ │ ├─ update_prog_stats()
│ │ ├─ migrate_enable()
│ │ └─ rcu_read_unlock()
│ ├─ call 原始函数体 (fentry only)
│ ├─ for each fexit / fmod_ret prog: (同上)
│ └─ 恢复寄存器 & ret
│
├───[Array Dispatch 类] (cgroup, tracepoint, kprobe_multi, etc.)
│ │
│ ▼
│ bpf_prog_run_array_cg() / bpf_prog_run_array()
│ ├─ rcu_read_lock()
│ ├─ migrate_disable()
│ ├─ bpf_set_run_ctx()
│ ├─ for each prog in array:
│ │ └─ run_prog(prog, ctx) // bpf_prog_run() or __bpf_prog_run_save_cb()
│ │ └─ __bpf_prog_run(prog, ctx, dispatcher)
│ │ └─ dispensher(ctx, insnsi, prog->bpf_func)
│ │ ├─ JIT'd: 直接调用 prog->bpf_func 机器码
│ │ └─ 解释器: BPF_PROG_RUN() 宏
│ ├─ bpf_reset_run_ctx()
│ ├─ migrate_enable()
│ └─ rcu_read_unlock()
│
├───[Direct Run 类] (socket filter, reuseport, XDP, TCX, etc.)
│ │
│ ▼
│ bpf_prog_run(prog, ctx) / bpf_prog_run_save_cb(prog, skb) / bpf_prog_run_xdp(prog, xdp)
│ └─ __bpf_prog_run(prog, ctx, dispatcher)
│ └─ (同上)
│
└───[Struct_Ops 类]
│
▼
内核调用 struct 函数指针 (如 tcp_congestion_ops->ssthresh(sk))
└─ cfi_stub (trampoline)
└─ __bpf_prog_enter → call prog->bpf_func → __bpf_prog_exit
bpf_prog | include/linux/filter.h:535+ | |
bpf_prog_array | include/linux/bpf.h:2184 | |
bpf_prog_array_item | include/linux/bpf.h:2176 | |
bpf_trampoline | include/linux/bpf.h:1330+ | |
bpf_tramp_link | kernel/bpf/trampoline.c | |
bpf_tramp_image | include/linux/bpf.h:1345+ | |
bpf_tramp_run_ctx | kernel/bpf/trampoline.c | |
bpf_trace_run_ctx | include/linux/bpf.h | |
bpf_prog_stats | include/linux/filter.h | |
cgroup_bpf | include/linux/bpf-cgroup-defs.h | |
bpf_mprog_entry | include/linux/bpf_mprog.h |
include/uapi/linux/bpf.h | |
include/linux/bpf.h | bpf_prog_run_arraybpf_prog_array 等核心结构 |
include/linux/filter.h | __bpf_prog_runbpf_prog_run, BPF_PROG_SK_LOOKUP_RUN_ARRAY |
kernel/bpf/syscall.c | link_create() |
kernel/bpf/trampoline.c | |
arch/x86/net/bpf_jit_comp.c | |
kernel/bpf/cgroup.c | |
kernel/trace/bpf_trace.c | |
kernel/bpf/bpf_lsm.c | |
security/security.c | call_int_hook) |
net/core/dev.c | |
include/net/xdp.h | bpf_prog_run_xdp |
net/core/filter.c | |
net/core/sock_reuseport.c | |
net/netfilter/nf_bpf_link.c | |
kernel/bpf/bpf_struct_ops.c | |
net/ipv4/bpf_tcp_ca.c |
所有 BPF 程序在执行时都受到以下安全保护:
bpf_check() 验证器(kernel/bpf/verifier.c),确保无越界访问、循环有界、类型安全等bpf_prog_active 计数器防止无限递归array_index_nospec() 等缓解措施本文基于linux-master源码