为何高流量下调用bpf_skb_change_head的eBPF网络过滤代码会崩溃
问题成因
- 未处理
bpf_skb_change_head的返回值:该helper在内存不足、skb结构异常等场景下会返回非0错误值,此时skb的head指针并未完成调整,后续直接调用bpf_skb_store_bytes往偏移0写入数据,会非法访问skb内存区域,高流量下错误触发概率大幅上升,直接导致内核踩内存panic。 - 高流量下大量skb会被克隆(如TCP ACK包复制、多路由镜像、连接跟踪逻辑都会触发skb克隆),
bpf_skb_change_head默认不会主动执行skb unshare操作,直接修改共享skb的head区域会触发多CPU核的竞态写入,直接引发页错误panic。 - 你在路由封装配置中已经指定了
headroom 14的参数,内核已经提前为skb预留了14字节的L2头空间,额外调用bpf_skb_change_head属于多余操作,会频繁触发skb内存重分配,高流量下会和网卡DMA逻辑产生内存地址冲突,引发崩溃。
解决建议
- 第一时间移除
bpf_skb_change_head调用,改用内核预留的headroom完成L2头写入:直接调用bpf_skb_adjust_room(skb, -14, BPF_ADJ_ROOM_MAC, 0)调整skb数据偏移,再往偏移0位置写入以太网头即可,整个过程不会触发内存重分配,性能远高于原有实现。 - 所有eBPF helper调用必须补全返回值校验,任何helper返回非0错误时,直接丢弃当前报文或者返回对应错误码,禁止执行后续的内存操作。
- 调整以太网头变量定义:将
unsigned short headers[7]改为unsigned char headers[14],避免未对齐访问问题,适配不同架构的内存访问要求。 - 如果因为业务逻辑必须调用
bpf_skb_change_head,需要先调用bpf_skb_unsharehelper(内核5.15及以上版本支持)确保skb为私有状态再修改头部,同时调整网卡的接收缓冲区大小,降低高流量下的内存不足概率。
内容的提问来源于stack exchange,提问作者junglearcher
相关产品推荐
相关产品推荐

