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

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

相关推荐
网硕互联的小客服5 小时前
Linux服务器磁盘应该如何合理分区?
linux·运维·服务器
andyweike5 小时前
php笔记
开发语言·笔记·php
lisanmengmeng6 小时前
nacos nrpe 监控机及被监控机部署
服务器·nacos·nagios·nrpe
贵沫末6 小时前
Ubuntu——常用软件安装
linux·运维·ubuntu
Doraemomo7 小时前
Linux内核驱动开发——中断与定时器
linux·运维·驱动开发
JSON_L7 小时前
Fastadmin 框架生成条形码
php·fastadmin
2301_808414387 小时前
Linux中动静态库的理解
linux·运维·服务器
M78佐菲8 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习
M78佐菲8 小时前
ARM学习笔记(10)
linux·arm开发·笔记·嵌入式硬件·学习
半仙白桑8 小时前
内核篇第十五讲:system-V共享内存
linux·共享内存·shmget·shmat·shmdt·shmctl