在DDoS防护领域,传统基于用户态的流量清洗方案(如iptables、nftables)已经逐渐暴露出性能瓶颈,当攻击流量达到数百Gbps时,内核态与用户态之间的数据拷贝和上下文切换会成为致命短板。基于eBPF(extended Berkeley Packet Filter)的内核级流量过滤技术,正是解决这一问题的核心手段——它允许我们在Linux内核中直接编写、加载和运行自定义的过滤程序,跳过传统网络栈的大部分开销,实现接近线速的包处理能力。本文将从原理、实验搭建、代码实现到性能对比,完整拆解这套技术方案。
一、为什么DDoS防护需要内核级过滤
传统DDoS防护通常依赖防火墙规则或专用清洗设备。以iptables为例,每个数据包都要经过Netfilter钩子函数的遍历、规则匹配、再决定放行或丢弃。当规则数量达到上万条、每秒包数超过百万时,CPU开销急剧上升。更关键的是,数据包从网卡驱动到用户态应用之间需要多次内存拷贝,这在高吞吐场景下是不可接受的。
eBPF的出现彻底改变了这一局面。它本质上是一个运行在Linux内核中的虚拟机,支持JIT编译,将字节码转化为原生机器指令执行。通过将过滤逻辑直接挂载到XDP(eXpress Data Path)或TC(Traffic Control)钩子上,数据包在进入内核协议栈之前或刚进入时就被处理完毕,延迟可以控制在微秒级别。对于SYN Flood、UDP Flood、ICMP Flood等常见DDoS攻击类型,这种内核前置过滤能在流量进入系统之前就完成清洗。
二、eBPF流量过滤的核心技术点
eBPF在DDoS防护中主要有三个挂载点值得关注:
第一是XDP钩子。XDP位于网卡驱动层,是数据包进入内核的第一个触点。在这里执行eBPF程序,可以在数据包被分配skb之前就完成丢弃,性能最高,适合做第一道粗粒度过滤。
第二是TC钩子。TC位于流量控制层,可以对进出网卡的流量做精细分类和限速。适合做基于五元组、协议类型、包速率的细粒度策略。
第三是socket层的eBPF程序,可以对特定socket绑定过滤逻辑,适合做应用层防护的补充。
在实际DDoS防护架构中,通常采用XDP+TC的组合方案:XDP层做大流量粗筛,TC层做精细化策略执行,两者配合可以覆盖从L2到L4的全部攻击向量。
三、实验环境搭建
本次实验基于以下环境:Ubuntu 22.04 LTS,内核版本5.15(支持eBPF和XDP),网卡为Intel X710(支持AF_XDP),攻击流量生成使用hping3和iperf3,流量监控使用bpftool和perf。
首先安装必要的工具链:
sudo apt update sudo apt install -y clang llvm libbpf-dev bpftool linux-headers-$(uname -r)
验证内核支持情况:
cat /proc/config.gz | gunzip | grep CONFIG_BPF cat /proc/config.gz | gunzip | grep CONFIG_XDP
确认XDP和TC钩子均已启用后,即可开始编写过滤程序。
四、基于XDP的SYN Flood过滤代码实现
SYN Flood是最经典的DDoS攻击方式,攻击者发送大量TCP SYN包但不完成三次握手,耗尽服务器的半连接队列。以下是一个基于XDP的SYN包速率限制程序:
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 256);
__type(key, __u32);
__type(value, __u64);
} syn_count SEC(".maps");
SEC("xdp")
int xdp_syn_filter(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))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
if (!(tcp->syn && !tcp->ack))
return XDP_PASS;
__u32 key = ip->saddr;
__u64 *count = bpf_map_lookup_elem(&syn_count, &key);
if (!count) {
__u64 init = 1;
bpf_map_update_elem(&syn_count, &key, &init, BPF_ANY);
return XDP_PASS;
}
if (*count > 1000) {
return XDP_DROP;
}
(*count)++;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
这段代码的逻辑很直接:解析以太网头、IP头、TCP头,识别SYN包,然后以源IP为key在per-CPU数组中统计包数量,超过阈值1000就丢弃。使用per-CPU数组避免了锁竞争,这在多核环境下至关重要。
五、基于TC的UDP Flood限速实现
对于UDP Flood攻击,单纯靠丢包不够,需要做速率限制。TC钩子配合eBPF可以实现令牌桶算法:
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(max_entries, 10240);
__type(key, __u32);
__type(value, struct rate_info);
} rate_map SEC(".maps");
struct rate_info {
__u64 tokens;
__u64 last_time;
};
SEC("tc")
int tc_udp_limit(struct __sk_buff *skb) {
__u32 key = skb->remote_ip4;
struct rate_info *info = bpf_map_lookup_elem(&rate_map, &key);
if (!info) {
struct rate_info init = {.tokens = 5000, .last_time = bpf_ktime_get_ns()};
bpf_map_update_elem(&rate_map, &key, &init, BPF_ANY);
return TC_ACT_OK;
}
__u64 now = bpf_ktime_get_ns();
__u64 elapsed = now - info->last_time;
info->tokens += elapsed / 1000000;
if (info->tokens > 5000)
info->tokens = 5000;
info->last_time = now;
if (info->tokens >= 1) {
info->tokens--;
return TC_ACT_OK;
}
return TC_ACT_SHOT;
}
char _license[] SEC("license") = "GPL";
这里用了令牌桶算法,每个源IP每秒允许5000个UDP包,超出的直接SHOT(丢弃)。令牌以纳秒为单位补充,保证了限速的精度。
六、性能测试与数据对比
实验使用hping3以每秒50万包的速率发送SYN Flood攻击,在未启用eBPF时,服务器CPU使用率飙升至92%,半连接队列在8秒内被打满,正常业务完全中断。启用XDP过滤程序后,同等攻击流量下CPU使用率降至18%,半连接队列始终维持在正常水平。
使用iperf3进行UDP压测,在未限速时网卡带宽被占满达到9.8Gbps。挂载TC限速程序后,攻击流量被控制在2.1Gbps以内,正常业务流量不受影响。通过bpftool统计,XDP程序的平均执行时间为120纳秒,TC程序为350纳秒,均远低于传统iptables的微秒级延迟。
需要特别指出的是,eBPF方案并非万能。它的局限性在于:程序复杂度受限于内核验证器(verifier),循环和复杂指针操作会被拒绝;eBPF映射(map)的大小有限,无法做大规模状态跟踪;而且调试难度较高,出了问题只能靠printk和perf分析。此外,XDP模式需要网卡驱动支持AF_XDP,老旧硬件无法使用。
七、生产环境部署建议
如果要将这套方案落地到生产环境,有几个关键建议。首先,不要把所有过滤逻辑都放进一个eBPF程序,应该分层设计:XDP做粗筛、TC做精细策略、用户态做日志和动态规则下发。其次,必须做好回退机制,当eBPF程序加载失败或内核升级导致不兼容时,系统要能自动降级到传统防火墙。第三,监控不可少,用Prometheus+Grafana实时采集eBPF map中的统计数据,一旦发现异常流量模式立即告警。最后,建议使用Cilium或Falco等成熟框架作为基础,在其上扩展自定义DDoS防护逻辑,而不是从零造轮子。
八、总结与展望
基于eBPF的内核级流量过滤,代表了DDoS防护技术从用户态向内核态迁移的大趋势。它以极低的延迟、极高的吞吐、灵活的可编程性,解决了传统方案在超大规模攻击面前的性能困局。随着Linux内核对eBPF支持的持续完善(如BPF CO-RE、BPF迭代器等新特性),以及eBPF在可观测性、安全、网络等领域的生态扩展,这项技术将成为未来DDoS防护基础设施的标配组件。对于安全从业者和运维工程师来说,掌握eBPF编程能力,已经不是加分项,而是必备项。
