XDP与eBPF:Linux网络性能优化实战指南

1.XDP与eBPF:Linux网络性能优化实战指南

掌握 XDP(eXpress Data Path)和 eBPF(extended Berkeley Packet Filter)的使用与开发,是深入 Linux 网络、安全、可观测性等领域的重要技能。以下是系统性的学习路径和关键知识点,帮助你从入门到进阶:


一、基础知识准备

1. Linux 内核基础

  • 理解网络栈(如 netdev、sk_buff、NAPI)
  • 系统调用、内核模块机制(虽然 eBPF 不需要写内核模块)
  • 用户态与内核态通信机制(如 BPF map、perf event)

2. C 语言编程

  • eBPF 程序通常用 C 编写(受限子集)
  • 熟悉指针、结构体、位操作等

3. 网络协议基础

  • 以太网帧、IP、TCP/UDP 头部结构
  • 抓包分析(tcpdump、Wireshark)

二、eBPF 核心概念

1. eBPF 是什么?

  • 一种在内核中安全运行沙箱程序的技术
  • 无需修改内核源码或加载内核模块
  • 支持多种 hook 点:网络(XDP、TC)、跟踪(kprobe/uprobe)、安全(LSM)等

2. 关键组件

  • eBPF 程序:运行在内核中的字节码(由 clang/LLVM 编译)
  • BPF Map:用户态与内核态共享数据的高效机制(hash、array、ringbuf 等)
  • Verifier:确保 eBPF 程序安全(无死循环、内存越界等)
  • Helper 函数:内核提供的 API(如 bpf_map_lookup_elem)

3. 开发工具链

  • clang + LLVM:编译 eBPF C 代码为 BPF 字节码
  • libbpf:现代推荐的用户态库(替代 BCC)
  • bpftool:调试、加载、查看 eBPF 程序和 map
  • BCC(BPF Compiler Collection):适合快速原型(Python/C++ 前端),但生产环境推荐 libbpf + CO-RE

三、XDP 专项

1. XDP 是什么?

  • eBPF 的一个高性能网络处理 hook,位于网络驱动层(早于内核网络栈)
  • 支持三种模式:
    • native XDP(驱动支持,性能最高)
    • skb mode(通用,通过 sk_buff)
    • generic XDP(用于测试,走标准网络栈)

2. XDP 返回值

#define XDP_ABORTED 0 #define XDP_DROP 1 #define XDP_PASS 2 #define XDP_TX 3 // 重发回网卡 #define XDP_REDIRECT 4 // 重定向到其他设备或 CPU

3. 典型应用场景

  • DDoS 防御(丢弃恶意包)
  • 负载均衡(L4/L3)
  • 流量采样与监控
  • 快速 NAT 或 ACL

四、开发实践步骤

步骤 1:环境搭建

  • Linux 内核 ≥ 5.8(推荐 ≥ 6.1)
  • 安装 clang、llvm、libbpf-dev、bpftool
  • 启用 CONFIG_BPF、CONFIG_XDP_SOCKETS 等内核选项(大多数发行版默认开启)

步骤 2:编写第一个 XDP 程序(丢包)

// xdp_drop.c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> SEC("xdp") int xdp_drop_func(struct xdp_md *ctx) { return XDP_DROP; } char _license[] SEC("license") = "GPL";

步骤 3:编译(使用 libbpf + Makefile 或 bpftool)

clang -O2 -target bpf -c xdp_drop.c -o xdp_drop.o

步骤 4:加载到网卡

sudo ip link set dev eth0 xdp obj xdp_drop.o sec xdp

步骤 5:验证与卸载

bpftool net show sudo ip link set dev eth0 xdp off

五、进阶方向

1. 使用 BPF Map 实现统计

  • 统计每秒丢包数
  • 使用 per-CPU array 或 hash map 存储状态

2. 用户态交互

  • 用 libbpf 编写用户态程序读取 map 数据
  • 使用 ring buffer 高效上报事件

3. CO-RE(Compile Once – Run Everywhere)

  • 解决内核版本兼容问题
  • 使用bpf_core_read()替代直接指针访问

4. 性能调优

  • 避免复杂逻辑(verifier 限制)
  • 减少 map 查找次数
  • 利用 per-CPU 结构避免锁竞争

六、推荐学习资源

官方文档

  • https://ebpf.io
  • https://docs.cilium.io/en/stable/bpf/
  • Linux Kernel BPF Documentation

书籍

  • 《BPF Performance Tools》by Brendan Gregg
  • 《Learning eBPF》by Liz Rice

工具项目

  • libbpf-bootstrap:快速启动模板(GitHub: libbpf/libbpf-bootstrap)
  • xdp-tutorial:官方 XDP 教程(GitHub: xdp-project/xdp-tutorial)
  • Cilium:生产级 eBPF 网络方案(含大量 XDP/eBPF 实践)

七、实战建议

  1. 从简单开始:先写一个统计 TCP 包数量的 XDP 程序
  2. 结合 tcpdump/Wireshark验证行为
  3. 使用 bpftool prog dump查看加载的字节码
  4. 阅读内核源码中的 samples/bpf/
  5. 参与社区:eBPF Summit、xdp-project Slack

如果你有具体目标(如“实现一个 L4 负载均衡器”或“用 XDP 做 SYN flood 防护”),我可以提供针对性代码示例和架构建议。欢迎继续提问!

2.先写一个统计 TCP 包数量的 XDP 程序

好的!下面是一个完整的统计 TCP 包数量的 XDP 程序,包含:

  • 内核态 eBPF/XDP 程序(C 语言)
  • 用户态加载与读取程序(使用libbpf
  • 编译与运行说明

该程序会在 XDP 层解析以太网帧 → IP 头 → 判断是否为 TCP,如果是,则在 BPF map 中计数,并将包XDP_PASS给内核继续处理。


一、目录结构(推荐)

tcp_counter/ ├── src/ │ ├── tcp_counter_kern.c # 内核态 XDP 程序 │ └── tcp_counter_user.c # 用户态加载/读取程序 ├── Makefile └── bpftool-obj/ # 编译输出(可选)

二、内核态程序:src/tcp_counter_kern.c

// src/tcp_counter_kern.c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/tcp.h> // 定义一个 BPF_MAP_TYPE_ARRAY 类型的 map,用于计数 struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } tcp_count SEC(".maps"); SEC("xdp") int xdp_tcp_counter(struct xdp_md *ctx) { void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end; struct ethhdr *eth = data; if (data + sizeof(*eth) > data_end) return XDP_PASS; // 只处理 IPv4 if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS; struct iphdr *ip = (struct iphdr *)(eth + 1); if ((void *)(ip + 1) > data_end) return XDP_PASS; // 检查是否为 TCP if (ip->protocol != IPPROTO_TCP) return XDP_PASS; // 增加计数 __u32 key = 0; __u64 *count = bpf_map_lookup_elem(&tcp_count, &key); if (count) { __sync_fetch_and_add(count, 1); // 原子加(多 CPU 安全) } return XDP_PASS; // 让包继续进入内核网络栈 } char _license[] SEC("license") = "GPL";

✅ 注意:

  • 使用__sync_fetch_and_add是因为BPF_MAP_TYPE_ARRAY不自动原子操作。
  • 如果追求更高性能且允许多核计数分离,可用BPF_MAP_TYPE_PERCPU_ARRAY

三、用户态程序:src/tcp_counter_user.c

// src/tcp_counter_user.c #include <stdio.h> #include <unistd.h> #include <signal.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> #include "tcp_counter_kern.skel.h" // 自动生成的 skeleton static volatile bool exiting = false; void sig_handler(int sig) { exiting = true; } int main(int argc, char **argv) { struct tcp_counter_kern *skel; int err; if (argc != 2) { fprintf(stderr, "Usage: %s <ifname>\n", argv[0]); return 1; } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); // 打开并加载 BPF 程序 skel = tcp_counter_kern__open_and_load(); if (!skel) { fprintf(stderr, "Failed to open and load BPF skeleton\n"); return 1; } // 将 XDP 程序 attach 到指定网卡 err = bpf_xdp_attach(if_nametoindex(argv[1]), bpf_program__fd(skel->progs.xdp_tcp_counter), 0, NULL); if (err) { fprintf(stderr, "Failed to attach XDP program to %s: %d\n", argv[1], err); goto cleanup; } printf("Successfully started! Counting TCP packets on %s. Press Ctrl+C to exit.\n", argv[1]); // 每秒打印一次计数 while (!exiting) { __u32 key = 0; __u64 value = 0; err = bpf_map__lookup_elem(skel->maps.tcp_count, &key, sizeof(key), &value, sizeof(value), 0); if (err) { fprintf(stderr, "Failed to lookup map\n"); } else { printf("TCP packets counted: %llu\n", value); } sleep(1); } printf("\nDetaching XDP program...\n"); cleanup: bpf_xdp_detach(if_nametoindex(argv[1]), 0, NULL); tcp_counter_kern__destroy(skel); return 0; }

四、Makefile(基于 libbpf-bootstrap 风格)

# Makefile BPFTOOL ?= bpftool CC := gcc CLANG ?= clang LLVM_STRIP ?= llvm-strip ARCH := $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/') VMLINUX := /sys/kernel/btf/vmlinux ifeq ($(VMLINUX),) $(error Please enable CONFIG_DEBUG_INFO_BTF and install vmlinux) endif APP := tcp_counter XDP_OBJ := src/$(APP)_kern.o .PHONY: all clean run all: $(APP) $(APP): src/$(APP)_user.c $(XDP_OBJ) $(CC) -g -O2 -Wall -I . src/$(APP)_user.c -o $(APP) \ -lelf -lz -lbpf src/$(APP)_kern.o: src/$(APP)_kern.c $(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ -I /usr/include/bpf \ -c $< -o $@ $(LLVM_STRIP) -g $@ # 移除调试符号(可选) clean: rm -f $(APP) src/*.o src/*.skel.h run: $(APP) sudo ./tcp_counter eth0 # 替换 eth0 为你的网卡名

💡 提示:首次编译前需生成 skeleton 头文件。更推荐使用 libbpf-bootstrap 模板自动生成 Makefile 和 skel。


五、快速体验(简化版:不用 skeleton)

如果你不想用 skeleton,也可以手动加载:

内核程序不变,用户态改用bpftool+ 简单 C:

1. 编译内核程序
clang -O2 -g -target bpf -c src/tcp_counter_kern.c -o tcp_counter_kern.o
2. 加载到网卡
sudo ip link set dev eth0 xdp obj tcp_counter_kern.o sec xdp
3. 查看 map 值(每秒)
while true; do sudo bpftool map dump name tcp_count sleep 1 done

注意:map 名字必须全局唯一,或通过bpftool map list找 ID。


六、注意事项

  1. 网卡支持:XDP native 模式需要驱动支持(如ixgbe,i40e,virtio_net)。虚拟机可用vethlo测试(但 lo 不支持 XDP,建议用vethpair)。
  2. 权限:需要 root 权限加载 XDP。
  3. 卸载:务必ip link set dev eth0 xdp off,否则程序退出后 XDP 仍生效。
  4. 性能:此程序只做简单解析,性能极高(百万 PPS 级别)。

七、测试方法

# 终端1:运行程序 sudo ./tcp_counter eth0 # 终端2:发送 TCP 包(例如 HTTP 请求) curl http://example.com # 或用 hping3 发 TCP 包 sudo hping3 -S -p 80 -c 10 example.com

你应该看到计数增加!


3.实现一个 L4 负载均衡器

实现一个L4(传输层)负载均衡器使用XDP + eBPF是一个非常经典且高性能的场景。我们将构建一个基于 XDP 的 TCP/UDP 四层负载均衡器,其核心功能包括:

  • 监听一个虚拟 VIP(Virtual IP)
  • 将发往 VIP 的流量 DNAT 到后端真实服务器(Real Server)列表
  • 使用简单轮询(Round Robin)选择后端
  • 支持连接跟踪(可选,简化版先不维护完整 conntrack)

⚠️ 注意:由于 XDP 位于网络栈极早期(驱动层),不能直接修改目的 MAC 地址以外的包内容并重发(如修改 IP/TCP 头需 careful 处理 checksum)。但我们可以:

  • 修改目的 MAC(二层转发)
  • 或使用XDP_REDIRECT到另一个设备(如 veth pair)
  • 或在tc (traffic control) BPF中做 L3/L4 修改(更灵活)

但为了真正实现 L4 负载均衡(修改 IP+端口)XDP 并不是最佳位置—— 更推荐使用tc BPF(clsact qdisc),因为它可以安全地修改 L3/L4 头部并更新 checksum。

不过,我们仍可以用XDP 实现“L4 负载均衡”的核心逻辑,并将包redirect 到本机 loopback 或 veth,由另一个 tc 程序或用户态程序完成 NAT。但为简化,这里采用“XDP + 修改 MAC 层” 的 DSR(Direct Server Return)模式,或仅做调度决策 + 重定向到后端(假设后端在同一 L2 网络)


✅ 方案选择:L2 DSR 模式(推荐用于 XDP)

  • 前端:客户端 → VIP(如10.0.0.100
  • 负载均衡器(XDP)收到包后:
    • 若目的 IP == VIP,则选择一个后端(如10.0.0.10,10.0.0.11
    • 只修改目的 MAC 地址为后端服务器的 MAC
    • IP 和 TCP/UDP 头不变
  • 后端服务器必须:
    • 配置 VIP(10.0.0.100)在 lo 接口(ip addr add 10.0.0.100/32 dev lo
    • 开启arp_ignore=1,arp_announce=2
    • 直接响应客户端(DSR:回程不经过 LB)

这是XDP 最适合的负载均衡模型,性能极高(百万 PPS),被 Cilium、Cloudflare 等广泛使用。


一、目标

  • VIP:10.0.0.100
  • 后端列表(Real Servers):
    • 10.0.0.10→ MAC:aa:bb:cc:dd:ee:01
    • 10.0.0.11→ MAC:aa:bb:cc:dd:ee:02
  • 协议:TCP & UDP
  • 调度算法:简单轮询(per-CPU)

二、eBPF/XDP 程序(内核态)

文件:lb_xdp_kern.c

// lb_xdp_kern.c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <arpa/inet.h> #ifndef VIP #define VIP 0x6400000a // 10.0.0.100 in network byte order (hex: 0a.00.00.64 -> 0x6400000a) #endif // 后端结构 struct real_definition { __u32 ip; unsigned char mac[6]; }; // 最多支持 8 个后端 #define MAX_REALS 8 struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, MAX_REALS); __type(key, __u32); __type(value, struct real_definition); } reals_map SEC(".maps"); // 每 CPU 的索引,用于轮询 struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u32); } rr_index SEC(".maps"); SEC("xdp") int xdp_lb_func(struct xdp_md *ctx) { void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end; struct ethhdr *eth = data; if (data + sizeof(*eth) > data_end) return XDP_PASS; if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS; struct iphdr *ip = (struct iphdr *)(eth + 1); if ((void *)(ip + 1) > data_end) return XDP_PASS; // 只处理发往 VIP 的包 if (ip->daddr != VIP) return XDP_PASS; // 获取后端数量(硬编码为 2,也可用 map 存 count) const __u32 nr_reals = 2; // 获取轮询索引 __u32 key = 0; __u32 *index = bpf_map_lookup_elem(&rr_index, &key); if (!index) return XDP_PASS; __u32 backend_idx = *index % nr_reals; (*index)++; // 获取后端信息 struct real_definition *real = bpf_map_lookup_elem(&reals_map, &backend_idx); if (!real) return XDP_PASS; // 修改目的 MAC __builtin_memcpy(eth->h_dest, real->mac, 6); // 注意:源 MAC 不变(仍是 LB 的 MAC),后端需能处理 return XDP_TX; // 从接收网卡直接发回(要求网卡支持) } char _license[] SEC("license") = "GPL";

三、用户态加载程序(填充后端)

文件:lb_xdp_user.c

// lb_xdp_user.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <net/if.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> #include "lb_xdp_kern.skel.h" // 后端服务器(IP + MAC) struct backend { const char *ip_str; const char *mac_str; // aa:bb:cc:dd:ee:ff }; static struct backend backends[] = { {"10.0.0.10", "aa:bb:cc:dd:ee:01"}, {"10.0.0.11", "aa:bb:cc:dd:ee:02"}, }; static void parse_mac(const char *mac_str, unsigned char *mac) { int values[6]; if (sscanf(mac_str, "%x:%x:%x:%x:%x:%x", &values[0], &values[1], &values[2], &values[3], &values[4], &values[5]) != 6) { fprintf(stderr, "Invalid MAC: %s\n", mac_str); exit(1); } for (int i = 0; i < 6; i++) mac[i] = (unsigned char) values[i]; } int main(int argc, char **argv) { struct lb_xdp_kern *skel; int ifindex; if (argc != 2) { fprintf(stderr, "Usage: %s <ifname>\n", argv[0]); return 1; } ifindex = if_nametoindex(argv[1]); if (!ifindex) { perror("if_nametoindex"); return 1; } skel = lb_xdp_kern__open(); if (!skel) { fprintf(stderr, "Failed to open BPF skeleton\n"); return 1; } // 设置 VIP(可选,也可在编译时定义) // 这里我们已在内核代码中硬编码 VIP = 10.0.0.100 if (lb_xdp_kern__load(skel)) { fprintf(stderr, "Failed to load BPF skeleton\n"); goto cleanup; } // 填充后端 map for (int i = 0; i < sizeof(backends)/sizeof(backends[0]); i++) { struct real_definition real = {0}; if (inet_pton(AF_INET, backends[i].ip_str, &real.ip) != 1) { fprintf(stderr, "Invalid IP: %s\n", backends[i].ip_str); goto cleanup; } parse_mac(backends[i].mac_str, real.mac); if (bpf_map_update_elem(bpf_map__fd(skel->maps.reals_map), &i, &real, BPF_ANY)) { perror("bpf_map_update_elem reals_map"); goto cleanup; } } // Attach XDP if (bpf_xdp_attach(ifindex, bpf_program__fd(skel->progs.xdp_lb_func), XDP_FLAGS_DRV_MODE, NULL)) { fprintf(stderr, "Failed to attach XDP\n"); goto cleanup; } printf("L4 Load Balancer (DSR mode) running on %s for VIP 10.0.0.100\n", argv[1]); printf("Backends: %s (%s), %s (%s)\n", backends[0].ip_str, backends[0].mac_str, backends[1].ip_str, backends[1].mac_str); printf("Press Ctrl+C to stop.\n"); pause(); // 等待信号 cleanup: bpf_xdp_detach(ifindex, XDP_FLAGS_DRV_MODE, NULL); lb_xdp_kern__destroy(skel); return 0; }

四、编译与运行

1. 安装依赖(Ubuntu/Debian)

sudo apt install libbpf-dev clang llvm make gcc

2. 使用 libbpf-bootstrap 生成项目(推荐)

或者手动编译:

# 生成 skeleton bpftool gen skeleton lb_xdp_kern.o > lb_xdp_kern.skel.h # 编译用户态 gcc -g -O2 -Wall -I . -lelf -lbpf lb_xdp_user.c -o lb_xdp

3. 运行

sudo ./lb_xdp eth0

五、后端服务器配置(DSR 模式)

在每台后端服务器上执行:

# 添加 VIP 到 loopback sudo ip addr add 10.0.0.100/32 dev lo # 防止 ARP 泄露 echo 1 | sudo tee /proc/sys/net/ipv4/conf/all/arp_ignore echo 2 | sudo tee /proc/sys/net/ipv4/conf/all/arp_announce

这样,后端可以直接响应客户端,回程流量不经过负载均衡器


六、进阶方向

  1. 支持连接亲和性(Session Affinity):用BPF_MAP_TYPE_LRU_HASH存储 src_ip+src_port → backend
  2. 健康检查:用户态定期探测后端,动态更新reals_map
  3. 支持 UDP:当前代码已兼容(只要 IP 层匹配 VIP)
  4. 使用 XDP_REDIRECT 到 AF_XDP:将包交给用户态做复杂处理
  5. 迁移到 tc BPF:如果需要修改 IP/Port(非 DSR 模式)

七、验证

# 从客户端访问 VIP curl http://10.0.0.100 # 在后端服务器抓包 tcpdump -i eth0 host 10.0.0.100 # 应看到请求,且目的 IP 仍是 10.0.0.100(DSR 特征)

如果你希望我提供非 DSR 模式(修改 IP+Port)的 tc BPF 实现,或者带连接跟踪的完整 L4 LB,也可以继续提问!

4.用 XDP 做 SYN flood 防护

使用XDP(eXpress Data Path)实现SYN Flood 防护是一个非常高效且实用的场景。XDP 位于网络驱动层,可以在内核协议栈之前极早地丢弃恶意流量,从而极大减轻系统负载。

下面我们将实现一个基于源 IP 的 SYN 包速率限制的 XDP 防护程序:

  • 检测 TCP SYN 包(非 SYN+ACK)
  • 对每个源 IP 统计单位时间内的 SYN 包数量
  • 超过阈值则丢弃(XDP_DROP
  • 使用BPF_MAP_TYPE_LRU_HASH自动淘汰不活跃 IP
  • 支持配置阈值(如每秒 100 个 SYN)

✅ 设计要点

特性说明
Hook 点XDP(驱动层,最早处理)
匹配条件IPv4 + TCP +tcp.flags & TCP_FLAG_SYN && !(tcp.flags & TCP_FLAG_ACK)
限速机制每个源 IP 的滑动窗口计数(简化为“每秒计数 + 定时清零”)
数据结构BPF_MAP_TYPE_LRU_HASH:自动淘汰冷 IP,防内存耗尽
用户态控制可动态设置阈值、查看被拦截 IP

⚠️ 注意:XDP 中无法使用定时器直接清零 map,因此我们采用“带时间戳的计数”方式实现滑动窗口。


一、内核态 XDP 程序:syn_flood_xdp_kern.c

// syn_flood_xdp_kern.c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/in.h> #ifndef THRESHOLD #define THRESHOLD 100 // 默认每秒 10 个 SYN 包(测试时可调低) #endif #define MAX_ENTRIES 65536 // 最多跟踪 65K 个源 IP struct syn_entry { __u64 last_seen; // 最后一次看到的时间(纳秒) __u32 count; // 当前周期内的 SYN 计数 }; struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_ENTRIES); __type(key, __u32); // src IP (network byte order) __type(value, struct syn_entry); } syn_track SEC(".maps"); // 全局阈值(可通过用户态更新) struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u32); } config_map SEC(".maps"); static __always_inline bool is_syn_packet(struct tcphdr *tcp) { return (tcp->syn == 1) && (tcp->ack == 0); } SEC("xdp") int xdp_syn_flood_filter(struct xpd_md *ctx) { void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end; struct ethhdr *eth = data; if (data + sizeof(*eth) > data_end) return XDP_PASS; if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS; struct iphdr *ip = (struct iphdr *)(eth + 1); if ((void *)(ip + 1) > data_end) return XDP_PASS; if (ip->protocol != IPPROTO_TCP) return XDP_PASS; struct tcphdr *tcp = (struct tcphdr *)(ip + 1); if ((void *)(tcp + 1) > data_end) return XDP_PASS; // 只处理纯 SYN 包(非握手响应) if (!is_syn_packet(tcp)) return XDP_PASS; __u32 src_ip = ip->saddr; // 获取当前时间(纳秒) __u64 now = bpf_ktime_get_ns(); __u64 window_size = 1000000000ULL; // 1 second in nanoseconds // 获取配置阈值 __u32 cfg_key = 0; __u32 *threshold = bpf_map_lookup_elem(&config_map, &cfg_key); __u32 thresh = threshold ? *threshold : THRESHOLD; struct syn_entry *entry = bpf_map_lookup_elem(&syn_track, &src_ip); if (!entry) { // 第一次见到该 IP,初始化 struct syn_entry new_entry = { .last_seen = now, .count = 1 }; bpf_map_update_elem(&syn_track, &src_ip, &new_entry, BPF_ANY); return XDP_PASS; } // 检查是否在窗口期内 if (now - entry->last_seen > window_size) { // 窗口已过,重置计数 entry->last_seen = now; entry->count = 1; return XDP_PASS; } // 窗口期内,增加计数 entry->count++; // 超过阈值?丢弃! if (entry->count > thresh) { return XDP_DROP; } return XDP_PASS; } char _license[] SEC("license") = "GPL";

🔍 注意:

  • 使用bpf_ktime_get_ns()获取高精度时间
  • LRU hash 自动淘汰长时间未访问的条目,防止 DoS 导致内存爆炸
  • 仅过滤纯 SYNSYN=1, ACK=0),避免误杀正常连接

二、用户态控制程序:syn_flood_xdp_user.c

功能:

  • 加载 XDP 程序
  • 设置 SYN 阈值(默认 100)
  • 打印被拦截的 IP(可选,需 ring buffer)
  • 支持 Ctrl+C 优雅退出
// syn_flood_xdp_user.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <net/if.h> #include <arpa/inet.h> #include <bpf/libbpf.h> #include "syn_flood_xdp_kern.skel.h" static volatile bool exiting = false; void sig_handler(int sig) { exiting = true; } int main(int argc, char **argv) { struct syn_flood_xdp_kern *skel; int ifindex; __u32 key = 0; __u32 threshold = 50; // 测试时设为 50 SYN/秒 if (argc != 2) { fprintf(stderr, "Usage: %s <interface>\n", argv[0]); return 1; } ifindex = if_nametoindex(argv[1]); if (!ifindex) { perror("Invalid interface"); return 1; } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); skel = syn_flood_xdp_kern__open_and_load(); if (!skel) { fprintf(stderr, "Failed to open/load BPF program\n"); return 1; } // 设置阈值 bpf_map_update_elem(bpf_map__fd(skel->maps.config_map), &key, &threshold, BPF_ANY); // Attach XDP if (bpf_xdp_attach(ifindex, bpf_program__fd(skel->progs.xdp_syn_flood_filter), XDP_FLAGS_DRV_MODE, NULL)) { fprintf(stderr, "Failed to attach XDP\n"); goto cleanup; } printf("SYN Flood protection active on %s (threshold: %u SYN/sec)\n", argv[1], threshold); printf("Press Ctrl+C to exit.\n"); while (!exiting) { sleep(1); } cleanup: bpf_xdp_detach(ifindex, XDP_FLAGS_DRV_MODE, NULL); syn_flood_xdp_kern__destroy(skel); return 0; }

三、编译(使用 libbpf + skeleton)

# 编译内核程序 clang -O2 -g -target bpf -D__TARGET_ARCH_x86 \ -I /usr/include/bpf \ -c syn_flood_xdp_kern.c -o syn_flood_xdp_kern.o # 生成 skeleton bpftool gen skeleton syn_flood_xdp_kern.o > syn_flood_xdp_kern.skel.h # 编译用户态 gcc -g -O2 -Wall -lelf -lbpf syn_flood_xdp_user.c -o syn_flood_xdp

四、运行与测试

1. 启动防护

sudo ./syn_flood_xdp eth0

2. 模拟 SYN Flood(另一台机器或本机)

# 使用 hping3 发送大量 SYN sudo hping3 -S -p 80 --flood --rand-source 192.168.1.100

💡 本机测试注意:XDP 不处理 loopback (lo),建议用两台机器,或创建 veth pair。

3. 验证效果

  • 正常连接(少量 SYN):应成功建立 TCP
  • 洪水攻击(高频 SYN):连接超时,netstat -s | grep -i listen中 overflow 不增加
  • 查看 map 内容(调试):
    sudo bpftool map dump name syn_track | head -20

五、增强建议

功能实现方式
日志记录攻击 IP使用BPF_MAP_TYPE_RINGBUF上报到用户态
动态调整阈值用户态监听信号或 socket 控制
白名单增加BPF_MAP_TYPE_HASH存放可信 IP
支持 IPv6添加 IPv6 解析分支
更精确的滑动窗口使用 per-CPU counter + 定时清理(需 workqueue,XDP 不支持)

六、注意事项

  1. 性能:此程序每包只做一次 hash 查找,性能极高(>10M PPS)
  2. 内存安全:LRU hash 防止内存耗尽
  3. 误杀风险:高并发合法客户端(如爬虫)可能被限,可结合白名单
  4. 卸载:务必ip link set dev eth0 xdp off或程序正常退出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1179800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12款智能论文写作工具全面评测:数学建模论文高效还原与专业排版优化策略

还在为论文写作头痛&#xff1f;特别是数学建模的优秀论文复现与排版&#xff0c;时间紧、任务重&#xff0c;AI工具能帮上大忙吗&#xff1f;今天&#xff0c;我们评测10款热门AI论文写作工具&#xff0c;帮你精准筛选最适合的助手。 aibiye&#xff1a;专注于语法润色与结构…

9D VR体验馆设备多少钱的投资分析与运营策略探讨

9D VR体验馆设备投资成本详解与市场分析 在考虑9D VR体验馆设备的投资成本时&#xff0c;首先需要评估几个关键因素&#xff0c;包括设施建设、设备采购及日常运营等方面。通常&#xff0c;初期投资大约在10万至15万元之间&#xff0c;这包括了VR双人蛋椅、VR魔力互动设备和VR3…

从用户体验到提示工程:架构师分享如何将UX研究方法应用于提示词优化

从用户体验到提示工程:架构师分享如何将UX研究方法应用于提示词优化 关键词 UX研究、提示工程、用户需求、交互设计、AI提示优化、用户旅程地图、Persona 摘要 当我们谈论AI交互时,“提示词”就像一把钥匙——它决定了AI能否听懂你的需求,能否输出符合预期的结果。但现实…

12款智能论文辅助工具评测:数学建模论文精准复现与专业排版优化策略

还在为论文写作头痛&#xff1f;特别是数学建模的优秀论文复现与排版&#xff0c;时间紧、任务重&#xff0c;AI工具能帮上大忙吗&#xff1f;今天&#xff0c;我们评测10款热门AI论文写作工具&#xff0c;帮你精准筛选最适合的助手。 aibiye&#xff1a;专注于语法润色与结构…

12款智能论文辅助软件测评:数学建模论文快速复现与格式精细调整指南

还在为论文写作头痛&#xff1f;特别是数学建模的优秀论文复现与排版&#xff0c;时间紧、任务重&#xff0c;AI工具能帮上大忙吗&#xff1f;今天&#xff0c;我们评测10款热门AI论文写作工具&#xff0c;帮你精准筛选最适合的助手。 aibiye&#xff1a;专注于语法润色与结构优…

基于51/STM32单片机太阳能路灯台灯光伏风能追光光通信无线设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

09-太阳能光伏 基于51/STM32单片机太阳能路灯台灯光伏风能追光可见光通信无线设计 太阳能板风能风机充电管理升压锂电池光敏高亮LEDC51-19 WIFI无线太阳能板充电管理升压锂电池光敏AD转换高亮LEDPWM档位C51-21 蓝牙无线太阳能板充电管理升压锂电池光敏AD转换高亮LEDPWM档位C51-…

12款AI论文工具对比分析:数学建模论文高效重现与专业排版优化策略

还在为论文写作头痛&#xff1f;特别是数学建模的优秀论文复现与排版&#xff0c;时间紧、任务重&#xff0c;AI工具能帮上大忙吗&#xff1f;今天&#xff0c;我们评测10款热门AI论文写作工具&#xff0c;帮你精准筛选最适合的助手。 aibiye&#xff1a;专注于语法润色与结构…

基于51/STM32单片机老人防跌智能拐杖GSM短信北斗定位地图无线设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

15-跌倒报警拐杖 基于51/STM32单片机老人防跌智能拐杖GSM短信北斗定位地图无线设计 跌倒状态检测蜂鸣器报警C51-13 蓝牙无线跌倒状态检测蜂鸣器报警C51-14 GSM短信远程心率DS1302时钟校时监控状态LCD1602液晶按键C51-105 GSM短信远程跌倒状态检测蜂鸣器报警灯指示按键防误报号码…

12款智能写作助手评测:数学建模论文精准复现与格式优化完整方案

AI工具已成为数学建模论文写作的高效助手&#xff0c;尤其在复现优秀论文和精准排版方面表现突出。本次评测聚焦10款热门AI写作工具&#xff0c;从代码生成、公式编辑到排版优化进行深度对比&#xff0c;帮助研究者快速匹配最适合的解决方案&#xff0c;显著提升学术产出效率。…

12款AI论文写作软件深度测评:数学建模论文高效还原与排版优化技巧

还在为数学建模论文的复现和排版焦头烂额&#xff1f;AI工具能高效解决时间紧迫、任务繁重的问题。本文深度评测10款热门AI论文写作工具&#xff0c;从代码生成、公式排版到逻辑优化&#xff0c;助你快速匹配最适合的智能助手&#xff0c;轻松攻克学术难关。 aibiye&#xff1…

基于51/STM32单片机智能台灯灯光控制PWM调光语音识别坐姿设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

51-C01-人走灯灭AD采集自动手动10档调节坐姿监测蜂鸣器OLED屏(无线方式选择) 51-C01N无无线-无APP板: 51-C01B蓝牙无线-APP版: 51-C01W-WIFI无线-APP版: 51-C01CAN-视频监控WIFI无线-APP版: 产品功能描述&#xff1a; 本系统由STC89C52单片机最小系统电路、OLED液晶显示、&…

两步实现在ThinkLink平台上的告警功能

1. 功能概述 ThinkLink 的告警通知功能是基于平台强大的 RPC (Remote Procedure Call) 机制 实现的。通过将告警逻辑封装在触发联动模型中&#xff0c;平台能够根据设备上报的数据实时判断是否达到告警条件&#xff0c;并调用预设的 alarm 类型 RPC 消息&#xff0c;实现告警的…

Windows 11 + WSL2 + vLLM 本地部署 Qwen3 8B AWQ 指南

Windows 11 WSL2 vLLM 本地部署 Qwen3 8B AWQ 指南 目录 系统要求环境准备WSL2 Ubuntu 安装配置NVIDIA 驱动配置CUDA 环境安装Python 环境搭建vLLM 安装Qwen3 8B AWQ 模型下载&#xff08;国内加速&#xff09;启动 vLLM 服务API 测试验证常见问题解决性能优化建议 系统要求…

导师严选!专科生必看8款AI论文平台测评

导师严选&#xff01;专科生必看8款AI论文平台测评 2026年专科生论文写作工具测评&#xff1a;为何需要这份榜单&#xff1f; 随着AI技术在学术领域的广泛应用&#xff0c;越来越多的专科生开始借助AI平台提升论文写作效率。然而&#xff0c;面对市场上五花八门的AI论文工具&am…

基于51/STM32单片机智能水杯保温杯恒温温度控制防干烧水质设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

STM32-S264-水量监测保温常温温度灯光指示定时提醒定时开关加热防干烧参数可设OLED屏声光提醒(无线方式选择)STM32-S264N无无线-无APP板(硬件操作详细): STM32-S264B蓝牙无线-APP版: STM32-S264W-WIFI无线-APP版: STM32-S264CAN-视频监控WIFI无线-APP版: STM32-S264I-云平台-AP…

文明十字路口的协同创生:《人类命运共同体》文明逻辑与“AI元人文”构想的思想共鸣

文明十字路口的协同创生:《人类命运共同体》文明逻辑与“AI元人文”构想的思想共鸣 在人类文明发展的关键节点,两种看似不同维度的思想探索——沈湘平教授对“构建人类命运共同体”的哲学奠基与岐金兰的“AI元人文”…

【LGR-262-Div.2】洛谷 1 月月赛 III 题解

感觉都挺典的LG14987 全等给定 \(a,b,c,d\),问能否将这四个整数填入 \(2 \times 2\) 的矩阵中,使得每一行所有数之和都相等,且每一列所有数之和也都相等。\(a,b,c,d\) 的情况一共排列方式只有 \(3\) 种,全部计算即…

2026国产制动卡钳优质产品推荐榜全场景适配 - 改装小龙

2026国产制动卡钳优质产品推荐榜全场景适配2026国产制动卡钳优质产品推荐榜全场景适配 一、行业背景与筛选维度说明 据《2025中国汽车改装行业白皮书》数据显示,国内汽车改装市场规模已突破1800亿元,其中制动系统改装…

数据清洗:大数据领域的必备技能

数据清洗&#xff1a;大数据领域的必备技能 1. 引入与连接&#xff1a;数据世界的"清洁革命" “2.3亿美元的错误&#xff1a;一个小数点引发的灾难” 1999年&#xff0c;NASA的火星气候轨道器在进入火星大气层时意外解体。调查结果令人震惊&#xff1a;洛克希德马丁公…

致敬:国家有突出贡献电影艺术家、中华人民共和国电影电视艺术家大典入选者陶玉玲

国家有突出贡献电影艺术家、《中国电影电视艺术家辞典》首批入选者、中华影星、中国电影百年百位优秀演员、大众电影百花奖终身成就奖和中国电影表演艺术学会金凤凰奖终身成就奖获得者、《中华人民共和国电影电视艺术家大典》入选者陶玉玲女士1月15日晚在北京逝世&#xff0c;享…