在维护一台对外暴露服务的云主机时,遇到过几次典型的网络层攻击:SYN 半开连接把连接表打满、UDP 小包把带宽和 CPU 软中断拉高、ICMP Flood 让机器响应变慢。当时尝试用 iptables limit 做限流,效果有限,因为包已经进了内核网络栈,再 drop 也消耗了协议栈处理资源。
背景:为什么想到做这个
eBPF/XDP 主机层 DDoS 防护实践记录
本文记录一个基于 eBPF/XDP 的开源项目 eShield 的设计思路、实现过程与当前状态,供对内核网络层防护感兴趣的开发者参考。
项目地址:https://github.com/BlkSword/eShield
后来了解到 XDP(eXpress Data Path)可以在网卡驱动层做包处理,决定用 eBPF 写一个能在流量最前端拦截恶意包的实验性项目,也就是 eShield。
项目定位
eShield 是一个主机级的 L3-L4 网络层过滤程序,运行在 Linux 内核 XDP 钩子上。它不是一个完整的应用防火墙,也不提供企业级安全服务,而是聚焦于在单台 Linux 主机上缓解 SYN Flood、UDP Flood、ICMP Flood、端口扫描等网络层流量压力。
项目经历了几个版本的迭代后,v0.3.1 移除了早期实验性的 HTTP WAF 和 JS Challenge 模块,把注意力重新收回到网络层清洗上,以降低 eBPF verifier 的复杂度和运行时开销。
技术选型
为什么用 eBPF/XDP
XDP 的决策点位于网卡驱动接收路径的前端,可以通过返回 XDP_DROP 让恶意包不进入内核网络协议栈。与 iptables/nftables 相比,这条路径更短,适合处理高 pps 的异常流量。
eBPF 程序会被 JIT 编译为本地机器码执行,配合 BPF Map 可以在内核态维护状态表,减少用户态和内核态之间的上下文切换。
为什么用 Rust + Aya
用户态控制面选择 Rust + Tokio + axum,主要是出于工程可维护性和内存安全考虑。eBPF 部分使用 Aya 框架加载和管理,避免直接维护复杂的 libbpf C 代码。
数据面与控制面之间通过 BPF Map 和 Ring Buffer 通信。v0.3.1 把核心计数器从纯 Ring Buffer 事件统计改为 eBPF Per-CPU GLOBAL_STATS 同步,减少了事件丢失或重复对统计结果的影响。
架构设计
┌─────────────────────────────────────────────────────────────┐
│ 管理面 │
│ Web Dashboard (axum) │ TUI (ratatui) │ CLI (clap) │
└──────────────────────────────┬──────────────────────────────┘
│ REST API / Config Watch
┌──────────────────────────────▼──────────────────────────────┐
│ 控制面 — Rust 用户态 │
│ 配置管理 │ 事件消费 │ 自适应阈值 │ 持久化 │ 指标聚合 │
└──────────────────────────────┬──────────────────────────────┘
│ BPF Maps / Ring Buffer
┌──────────────────────────────▼──────────────────────────────┐
│ 数据面 — eBPF/XDP 内核态 │
│ 包解析 → 白名单 → 端口 ACL → GeoIP → SYN Proxy → UDP/ICMP │
│ Flood → L7 扫描 → 速率限制 → 黑名单 → 决策 │
└─────────────────────────────────────────────────────────────┘
数据面按照优先级处理:先匹配白名单,再执行端口/协议 ACL、GeoIP 策略、SYN Cookie 代理,最后走速率限制和黑名单判定。命中黑名单或 ACL drop 规则的包直接返回 XDP_DROP。
控制面负责加载/卸载 eBPF 程序、管理动态规则、消费审计事件、提供 REST API 和 Web 界面,并把规则持久化到 redb。
主要功能模块
白名单与黑名单
白名单基于 LPM Trie,支持 IPv4/IPv6 CIDR;黑名单使用 LRU Hash,命中后会在指定时间内拦截该源地址,到期自动解封。
速率限制
使用指数衰减滑动窗口统计每个源 IP 的包速率,用于识别 UDP Flood、ICMP Flood 和 CC 慢速压测类流量。
SYN Cookie 代理
针对 IPv4 TCP SYN Flood,eShield 回复 SYN-ACK Cookie,只有完成完整三次握手的合法连接才会被放行。
TCP RST 回包
v0.3.1 新增的可选功能。对于被丢弃的 TCP 连接,主动回复 RST,避免客户端持续重传造成半开连接堆积。
GeoIP 与威胁情报
支持加载自定义 CSV 格式的 CIDR 列表,按国家或 ASN 执行放行/封禁;也支持定时拉取自定义威胁情报 feed,拦截已知恶意地址。
L7 轻量指纹扫描
检查 TCP 载荷前若干字节,匹配预设特征时执行 drop。这个模块比较轻量,主要用于识别常见扫描和探测行为,不替代专业 DPI 工具。
自适应阈值
对重复触发防御规则的源 IP,自动延长封禁时长,减少同一批地址反复试探的情况。
部署与使用
环境要求
Linux 内核 >= 5.10,且启用 BTF:
ls /sys/kernel/btf/vmlinux
root 权限或具备 CAP_BPF、CAP_NET_ADMIN 等 capabilities
安装方式
项目 Release 提供了 musl 静态链接的二进制文件,可以通过 install.sh 一键安装:
curl -sSL https://raw.githubusercontent.com/BlkSword/eShield/main/scripts/install.sh | sudo bash
也可以从源码构建:
sudo bash scripts/install.sh --build
常用操作
# 查看运行状态
eshield status
# 临时封禁某个 IP
eshield block 192.0.2.1 --duration 300
# 解封
eshield unblock 192.0.2.1
# 热加载配置
sudo systemctl reload eshield
测试情况
我们在普通 VM + veth 环境下做了基础测试,工具使用 hping3 和 wrk,覆盖 UDP Flood、ICMP Flood、SYN Flood 等场景。测试脚本放在 tests/full_attack_test.sh 和 tests/netns_test.sh 中。
单项数据方面,在单核 VM 上 XDP PASS 路径可以达到约 24 万 pps;DROP 路径因为提前返回,开销比基线还略低。正常流量的额外延迟典型小于 1 µs。这些数字来自实验环境,实际生产中的表现与网卡、CPU、内核版本、攻击模型都有关系,仅供参考。
与其他技术路线的区别
与 iptables/nftables
iptables/nftables 是通用防火墙,适合做细粒度的访问控制,但 drop 动作发生在内核网络栈内部。eShield 只做一件事:在网络栈最前端快速清掉高 pps 的恶意流量。两者可以共存,互不冲突。
与 Suricata/Zeek
Suricata 和 Zeek 更偏向深度包检测和威胁分析,能力强但资源占用更高。eShield 不追求 DPI,而是用最小开销完成快速判定和 drop,适合作为第一道粗筛。
与 DPDK
DPDK 可以实现极高的包处理性能,但需要接管网卡、绑定 CPU 核心、绕过内核网络栈,部署和运维成本较高。eBPF/XDP 的侵入性小得多,能和现有 Linux 网络组件共存,但极限性能通常低于专门调优的 DPDK。
与云厂商高防
云厂商高防提供大带宽和专业运营,适合大规模攻击;eShield 是主机层自托管方案,延迟更低、策略自主,但带宽受限于单台机器网卡。二者属于不同层级的防护手段。
当前局限
需要 Linux 5.10+ 和 BTF,老内核无法运行
防护项目目前在控制面做策略分组和持久化,数据面暂未按项目维度做独立匹配
L7 指纹扫描能力有限,不替代专业 WAF 或 IDS
文档和测试还在持续完善中
后续方向
接下来会围绕网络层清洗继续打磨:
优化 SYN Cookie 代理在复杂场景下的稳定性
完善 UDP/ICMP Flood 的阈值模型
增强威胁情报 feed 的解析和更新机制
补充更多自动化测试和基准测试数据
结语
eShield 是一个还在持续迭代的开源项目,初衷是探索 eBPF/XDP 在主机层 DDoS 防护中的可行性。如果对这个方向感兴趣,可以到 GitHub 查看源码和文档,也欢迎通过 issue 或 PR 交流。
项目地址:https://github.com/BlkSword/eShield
免责声明:eShield 为开源技术项目,不提供商业安全服务承诺。在生产环境使用前,请充分测试并根据自身业务需求评估风险。