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

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

相关推荐
sxstj1 小时前
老旧电脑 Linux 系统完整推荐(按内存分档,新手友好)
linux
西安景驰电子2 小时前
PCIe 授时卡原理及应用
运维·服务器·windows·单片机·嵌入式硬件·fpga开发
Lyra_Infra2 小时前
OpenClaw 服务异常故障分析报告
linux·人工智能
aixingpan3 小时前
aixingpan.cn API开发文档:api_docs_trichart_natal_solararc_transit2接口指南
前端·php
完美火龙篇 四月的友3 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
开发语言·php
xywww1684 小时前
开发团队接入 Opus 5 / Sonnet 5:模型路由、升级阈值和成本账怎么算
服务器·网络·p2p
爱写代码的森4 小时前
鸿蒙三方库 | harmony-utils之EncryptUtil URL编解码详解
服务器·华为·harmonyos·鸿蒙·huawei
冷莫溪4 小时前
Zabbix——认识及部署Zabbix(基于Rocky9.7)
linux·运维·php·zabbix
风曦Kisaki4 小时前
Kubernetes(K8s)笔记Day03: Pod命名空间,标签,Pod 的调度,污点与容忍度,Pod 常见状态和重启策略,Pod 生命周期
linux·运维·笔记·docker·云原生·容器·kubernetes