eBPF内核编程:从TC到XDP的全栈可观测性

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生产)

核心价值:内核级可观测 + 零开销 + 安全沙箱 + 动态加载。

相关推荐
fanged1 天前
Linux的DD命令
linux·运维·服务器
华清远见成都中心1 天前
FreeRTOS事件组(Event Group)的工作机制分析
服务器·网络·网络协议
张洛闻Eren1 天前
MySQL 管理复制拓扑【MySQL第四课】
linux·运维·数据库·mysql
Meya11271 天前
RFID 机房资产管理系统:告别手工台账,实现资产全流程自动管控
大数据·服务器
西安景驰电子1 天前
《PTP精确时间协议系列》第二篇:工程部署、调试与性能优化
运维·服务器·网络·数据库·windows·性能优化
xiaohua10091 天前
Linux-JVM线程查询
linux·jvm
blueSatchel1 天前
u-boot启动过程(bootROM到SPL到u-boot)
linux·u-boot
祖力551 天前
快速学会数据结构中的链式队列与循环队列
linux·c语言·数据结构·算法
原凉是这样的丶1 天前
Qwen3.8-27B 开源后,我用双 RTX 4090 跑通了:vLLM 0.27.1、128K 上下文与 MTP 实测
linux·算法