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

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

相关推荐
TangGeeA1 小时前
命令安全设置
java·服务器·安全
水饺编程1 小时前
第2章:Linux 基础,rm 命令
linux·c语言·汇编
范同学~1 小时前
固定表头+第一列+最后一列
java·服务器·前端
雪落漂泊1 小时前
Linux基础开发工具(三)gcc/g++与make/makefile篇
linux·运维·服务器
爱吃辣椒的小豆豆1 小时前
Linux-sshd-实战-白名单与跳板机
linux·运维·服务器
气质、小青年!1 小时前
进程1学习
linux·学习
强哥聊算力2 小时前
《算力江湖·国产GPU突围:摩尔线程故事》第四集:从图形到AI:摩尔线程为什么必须冲进大模型赛道?
运维·服务器·人工智能·ai·gpu算力
blueSatchel2 小时前
framebuffer注册源码解读
linux·嵌入式
2401_862880822 小时前
嵌入式Linux系统移植与驱动开发完整指南
linux·c语言·驱动开发
坤坤子吖2 小时前
Linux 线程同步与互斥——互斥锁
linux·服务器·c++·笔记·学习