
兄弟们!还在用 iptables 苦哈哈限速?还在为内核模块崩溃头疼?今天带你直奔 eBPF 黑科技巅峰:Rust + Aya 一键写 XDP 程序,直接在网卡驱动层拦截数据包,速度快到离谱!
这个教程从零到一,教你:
写完你就能在简历上加一句“熟练掌握 Rust eBPF & XDP”,面试官直接爱了!
适用场景:DDoS 防护、入侵检测、流量监控、自定义 NAT……干货走起!
系统:Ubuntu 22.04+ / Debian 12+(内核 ≥ 5.10 最佳,支持 XDP 多驱动)
# 1. Rust(最新 stable 即可)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
# 2. LLVM/Clang(Aya 需要,推荐 15+ 或 18)
sudo apt update
sudo apt install -y clang llvm libelf-dev zlib1g-dev pkg-config libssl-dev build-essential
# 3. bpf-linker(Aya 构建必须)
cargo install bpf-linker
# 4. 可选:用官方模板起步(强烈推荐!)
cargo install cargo-generate检查:rustc --version、clang --version、uname -r(内核版本)
cargo new xdp-aya-firewall
cd xdp-aya-firewallCargo.toml(完整版,基于 2026 年主流):
[package]
name = "xdp-aya-firewall"
version = "0.1.0"
edition = "2021"
[dependencies]
aya = { version = "0.13", features = ["async_tokio"] }
aya-log = "0.2"
tokio = { version = "1", features = ["full", "macros"] }
anyhow = "1.0"
network-types = "0.1" # 安全解析 Ethernet/IP 头
bytes = "1"
[build-dependencies]
aya-build = "0.13"项目结构:
xdp-aya-firewall/
├── Cargo.toml
├── build.rs # 构建 eBPF
├── src/
│ └── main.rs # 用户空间
└── ebpf/
└── src/
└── main.rs # eBPF 内核代码真实解析包头 + 规则匹配 + Perf 发事件 + 日志
#![no_std]
#![no_main]
use aya_ebpf::{
bindings::xdp_action,
macros::{map, xdp},
maps::PerfEventArray,
programs::XdpContext,
};
use aya_log_ebpf::{info, warn};
use network_types::{
eth::{EthHdr, EtherType},
ip::{Ipv4Hdr},
};
use core::mem;
// Perf Buffer 事件结构体(更实用)
#[repr(C)]
#[derive(Copy, Clone)]
struct PacketEvent {
src_ip: u32, // 网络字节序源 IP
action: u32, // 0 = PASS, 1 = DROP
}
// Perf Map:8192 条足够大多数场景
#[map(name = "PACKET_EVENTS")]
static mut PACKET_EVENTS: PerfEventArray<PacketEvent> = PerfEventArray::with_max_entries(8192, 0);
#[xdp(name = "firewall_xdp")]
pub fn firewall_xdp(ctx: XdpContext) -> u32 {
match try_firewall_xdp(ctx) {
Ok(action) => action as u32,
Err(err) => {
warn!(&ctx, "处理失败: {:?}", err);
xdp_action::XDP_ABORTED as u32
}
}
}
fn try_firewall_xdp(ctx: XdpContext) -> Result<xdp_action, ()> {
// 1. 读取以太网头部(从数据包开头)
let eth_hdr: EthHdr = ctx.load(0)?;
// 只处理 IPv4,其他放行
if eth_hdr.ether_type != EtherType::Ipv4 {
info!(&ctx, "非 IPv4 包,直接 PASS");
return Ok(xdp_action::XDP_PASS);
}
// 2. 读取 IPv4 头部(以太网头后)
let ip_offset = EthHdr::LEN;
let ip_hdr: Ipv4Hdr = ctx.load(ip_offset)?;
// 源 IP(网络字节序 → u32)
let src_ip = u32::from_be_bytes(ip_hdr.src);
// 示例黑名单 IP:192.168.1.100
let blocked_ip: u32 = 0xC0A80164; // 192.168.1.100
let action = if src_ip == blocked_ip {
info!(&ctx, "拦截可疑 IP: {:i} → DROP", src_ip);
xdp_action::XDP_DROP
} else {
// info!(&ctx, "正常流量: {:i} → PASS", src_ip); // 调试时打开
xdp_action::XDP_PASS
};
// 3. 发送事件到用户空间
let event = PacketEvent {
src_ip: ip_hdr.src, // 保持网络序,用户空间再转换
action: action as u32,
};
unsafe { PACKET_EVENTS.output(&ctx, &event, 0); }
Ok(action)
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe { core::hint::unreachable_unchecked() }
}小彩蛋:{:i} 格式化直接把 u32 打印成 192.168.x.x,Aya-Log 真香!
fn main() -> anyhow::Result<()> {
aya_build::build_ebpf("ebpf")?;
Ok(())
}use aya::{include_bytes_aligned, Bpf};
use aya::maps::perf::AsyncPerfEventArray;
use aya::programs::{Xdp, XdpFlags};
use aya_log::EbpfLogger;
use anyhow::Context;
use std::net::Ipv4Addr;
use tokio::signal;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. 加载 eBPF 对象(路径根据 target 调整)
let mut bpf = Bpf::load(include_bytes_aligned!(
"../../target/bpf-el.elf/x86_64-unknown-none/debug/ebpf"
))?;
// 2. 初始化 Aya-Log(内核日志 → dmesg)
if let Err(e) = EbpfLogger::init(&mut bpf) {
eprintln!("日志初始化失败(非致命):{e}");
}
// 3. 加载并挂载 XDP 程序(改成你的网卡,如 enp1s0)
let prog: &mut Xdp = bpf.program_mut("firewall_xdp").context("找不到程序")?.try_into()?;
prog.load()?;
prog.attach("eth0", XdpFlags::default())?; // 可加 XDP_FLAGS_SKB_MODE 等
// 4. Perf Buffer 读取(多 CPU 异步)
let mut perf_array = AsyncPerfEventArray::try_from(bpf.map_mut("PACKET_EVENTS")?)?;
for cpu in aya::util::online_cpus()? {
let mut reader = perf_array.open(cpu, None)?;
tokio::spawn(async move {
let mut bufs = vec![vec![0u8; 4096]; 10];
loop {
let cnt = reader.read_events(&mut bufs).await.unwrap_or(0);
for i in 0..cnt.read as usize {
let buf = &bufs[i];
if buf.len() < core::mem::size_of::<super::ebpf::PacketEvent>() {
continue;
}
let event = unsafe { *(buf.as_ptr() as *const super::ebpf::PacketEvent) };
let ip = Ipv4Addr::from(event.src_ip.to_be());
let act = if event.action == xdp_action::XDP_DROP as u32 { "DROP" } else { "PASS" };
println!("[CPU {cpu}] 检测到包!源 IP: {ip} 动作: {act}");
}
}
});
}
println!("XDP 防火墙已启动!Ctrl+C 退出");
signal::ctrl_c().await?;
println!("卸载中...");
Ok(())
}cargo build --release
sudo target/release/xdp-aya-firewall测试:
sudo dmesg -w | grep firewallip link 查看恭喜!从零掌握了 XDP + Perf Buffer + Aya-Log 全套流程。想进阶?加 HashMap 动态黑名单、用 Ring Buffer 替换 Perf、追踪 TC 层、甚至改写包内容做负载均衡……
觉得干货满满?点个在看、转发、收藏三连!咱们继续冲!🚀