流量洪水来了,iptables 已经溺水
——聊聊我用 XDP 做高性能 DDoS 缓解的那些实践和体会
先说个很现实的场景。
某天凌晨,你正睡得香,手机突然开始震:
❌ 监控报警:CPU 100%
❌ 网络流量暴涨
❌ 业务接口 5xx 飙升
你爬起来第一反应是啥?
- 看 nginx
- 看 iptables
- 看 firewalld
- 看是不是被 DDoS 了
然后你会发现一个扎心的事实:
包都已经打到内核里了,你再拦,已经晚了。
这就是我第一次真正理解 XDP 存在意义 的时刻。
一、传统 DDoS 防护,为啥越来越“顶不住”?
我们先把锅甩清楚。
iptables / nftables 的问题不在“慢”
而在于 位置太靠后。
数据包路径大概是这样:
网卡 → 内核协议栈 → netfilter → iptables → socket → 应用
当你用 iptables DROP 一个包时:
👉 这个包已经消耗了一堆 CPU 资源
在小流量时代没问题,在 百万 PPS(packet per second) 面前:
CPU 直接原地升天 🔥
二、XDP 是啥?一句话给你讲明白
XDP(eXpress Data Path) 的核心优势只有一个:
在数据包“刚进网卡”那一刻就把它干掉
路径变成:
网卡驱动 → XDP 程序 → DROP / PASS
注意这个关键词:网卡驱动层。
- 不进内核协议栈
- 不走 netfilter
- 不触发 socket
这就是为什么 XDP 能扛 千万级 PPS。
三、XDP 抗 DDoS,本质上在干什么?
别被 eBPF 吓到,XDP 干的事其实很“朴素”:
- 看包头
- 判断条件
- 决定命运
核心返回值只有几个:
XDP_PASS // 放行
XDP_DROP // 丢弃
XDP_ABORTED
四、一个最简单的 XDP 防 DDoS 示例
我们直接上一个能跑、能理解的例子。
目标
👉 丢弃所有 ICMP Flood(ping 洪水)
XDP 程序(C)
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int xdp_drop_icmp(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if ((void*)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void*)(eth + 1);
if ((void*)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol == IPPROTO_ICMP) {
return XDP_DROP;
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
加载 XDP 程序
ip link set dev eth0 xdp obj xdp_drop_icmp.o sec xdp
这一步完成后:
ICMP 包直接在网卡层被干掉
ping 你都 ping 不通,但 CPU 几乎不动。
五、真实 DDoS 场景下,我们怎么用 XDP?
实际工程里,我一般把 XDP 当 第一道闸门。
常见策略组合
1️⃣ 基于协议快速丢弃
- ICMP flood
- UDP flood(非业务端口)
2️⃣ 基于源 IP 黑名单(BPF Map)
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100000);
__type(key, __u32);
__type(value, __u8);
} blacklist SEC(".maps");
- 动态更新
- 用户态随时加 IP
3️⃣ 基于端口 / 五元组
- 非法端口直 drop
- 明显异常流量优先拦截
六、XDP + 传统防护,才是完整答案
我要说句很重要的话:
XDP 不是用来“替代一切”的。
而是:
替代“第一层粗过滤”。
我自己常用的分层模型是:
XDP → 抗大流量、粗暴过滤
iptables → 精细规则
WAF → HTTP 层攻击
应用层 → 业务逻辑校验
七、XDP 的坑,也必须提前说清楚
不然你照着用,容易翻车。
1️⃣ 写 XDP,心态要“极度克制”
- 不能 malloc
- 不能循环太复杂
- 每多一行,都是在赌性能
👉 XDP 代码越简单越好
2️⃣ 调试非常不友好
没有 printf
只有:
- bpf_trace_printk
- perf / bpftool
第一次写 XDP 的人,十有八九会骂街 😅
3️⃣ 不同网卡,支持程度不同
- 驱动不支持 XDP native
- 自动 fallback 到 generic
- 性能直接打折
生产环境一定要确认:
ip -details link show eth0
八、我个人对 XDP 的真实看法
说点掏心窝子的。
XDP 很强,但不是“运维必选项”
如果你:
- 日常流量不大
- 没遇到真正的 DDoS
- 主要是应用层攻击
👉 别为了炫技硬上 XDP
什么时候 XDP 值得你花精力?
在我看来只有一句话:
当“包一进来你就想弄死它”的时候
比如:
- UDP/ICMP 洪水
- 明显异常流量
- 大规模扫描
这时候,XDP 是真·救命。
九、写在最后
很多人聊安全,聊防护,喜欢往“复杂”上走。
但我越来越觉得:
真正的高性能防护,往往是“越靠前,越简单”。
XDP 的价值就在于:
- 把战场前移
- 把成本压低
- 给系统留一口气
如果哪天你也遇到那种:
“iptables 已经跪了,但流量还在往里灌”
你会突然意识到:
XDP,不是高端玩具,而是生存技能。