eBPF内核编程:从TC到XDP的全栈可观测性
一、引言
eBPF 是 Linux 内核的"超级能力"------无需重新编译内核,动态注入安全沙箱化的程序,实现网络、安全、可观测性的全栈覆盖。Cilium/Falco/Pixie 等明星项目都构建在eBPF之上。
二、eBPF执行模型
用户空间 内核空间
┌──────────┐ BPF syscall ┌────────────┐
│ eBPF程序 │ ──────────────────► │ Verifier │ (安全检查)
│ (C/Rust) │ │ │
└──────────┘ └─────┬──────┘
│
┌────────▼──────┐
│ JIT Compiler │ → 原生机器码
└────────┬──────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ kprobe │ │ tracepoint│ │ XDP/TC │
│ 内核函数 │ │ 跟踪点 │ │ 网络钩子 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└──────────────────┼──────────────────┘
▼
┌──────────────┐
│ BPF Maps │ (内核←→用户共享内存)
│ (Hash/Array)│
└──────┬───────┘
│
▼
用户空间读取/可视化
三、BCC实战
python
from bcc import BPF
# 1. 追踪所有 execve() 系统调用
prog = """
#include
BPF_HASH(last_exec, u32, u64); // BPF Map
int trace_execve(struct pt_regs *ctx) {
u32 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
last_exec.update(&pid, &ts); // 记录时间戳
return 0;
}
"""
b = BPF(text=prog)
b.attach_kprobe(event="__x64_sys_execve", fn_name="trace_execve")
# 打印追踪结果
b.trace_print()
TCP连接追踪
python
prog = """
#include
struct event_t {
u32 pid;
u32 saddr, daddr;
u16 sport, dport;
char comm[16];
};
BPF_PERF_OUTPUT(events);
int trace_connect(struct pt_regs *ctx, struct sock *sk) {
struct event_t event = {};
event.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
// 读取源/目标IP和端口
event.saddr = sk->__sk_common.skc_rcv_saddr;
event.daddr = sk->__sk_common.skc_daddr;
event.sport = sk->__sk_common.skc_num;
event.dport = sk->__sk_common.skc_dport;
events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
def print_event(cpu, data, size):
event = b["events"].event(data)
print(f"PID {event.pid} ({event.comm}) "
f"{event.saddr}:{event.sport} → {event.daddr}:{event.dport}")
"""
b = BPF(text=prog)
b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect")
b["events"].open_perf_buffer(print_event)
while True:
b.perf_buffer_poll()
四、XDP高性能网络
c
// XDP: 网卡驱动层处理数据包(DDoS防护/负载均衡)
// 比iptables快10-100x
#include
#include
#include
#include
// 最简单的XDP DDoS防护:丢弃所有UDP包
SEC("xdp_drop")
int xdp_drop_udp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void*)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto == __constant_htons(ETH_P_IP)) {
struct iphdr *ip = (void*)(eth + 1);
if ((void*)(ip + 1) > data_end) return XDP_PASS;
if (ip->protocol == IPPROTO_UDP) {
return XDP_DROP; // 丢弃!仅需~80ns
}
}
return XDP_PASS;
}
五、Rust + Aya框架
rust
use aya::{programs::Kprobe, Bpf};
use aya_log::BpfLogger;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 加载eBPF程序
let mut bpf = Bpf::load(include_bytes_aligned!(
"../target/bpfel-unknown-none/release/myprog"
))?;
BpfLogger::init(&mut bpf)?;
// 挂载kprobe
let program: &mut Kprobe = bpf.program_mut("trace_openat")
.unwrap().try_into()?;
program.load()?;
program.attach("do_sys_openat2", 0)?;
// 持续运行
tokio::signal::ctrl_c().await?;
Ok(())
}
六、Cilium网络可观测
yaml
# Cilium Hubble: 基于eBPF的服务网格可观测
# kubectl exec -n kube-system ds/cilium -- hubble observe
# 流量日志:
# default/pod-a → default/pod-b:80 FORWARDED (TCP)
# default/pod-a → external:443 FORWARDED (TLS)
# default/pod-x → 10.0.1.5:8080 DROPPED (Policy Denied)
七、eBPF安全防护
c
// Falco: 基于eBPF的运行时安全检测
// 规则示例:检测非标准SSH端口
- rule: SSH outside standard port
condition: >
evt.type=connect and fd.sport!=22 and
proc.name in (sshd, ssh)
output: "SSH connection outside port 22 (user=%user.name)"
priority: WARNING
八、总结
eBPF技术栈:BCC(快速原型) → bpftrace(调试) → Cilium(网络) → Falco(安全) → Aya(Rust生产)
核心价值:内核级可观测 + 零开销 + 安全沙箱 + 动态加载。