因为iptables的length模块仅匹配ip层总长度,无法识别协议语义层面的不合理长度,如tcp头部data offset与实际payload矛盾;需用-m u32按偏移提取字段进行协议级校验。

为什么不能直接用 -m length 过滤“非常规”包长?
因为 iptables 的 length 模块只匹配 IP 层总长度(ip->tot_len),不区分上层协议载荷是否异常。比如一个 TCP SYN 包本应很短(通常 40–60 字节),但攻击者可能塞入超长 options 或 padding,使 IP 总长达到 1500 字节——这种包在 length 看来只是“大”,不是“非法”。真正需要识别的是:**协议语义层面的不合理长度**,例如 TCP 头部声明的 data offset 和实际 payload 长度矛盾、UDP 包长小于头部最小尺寸等。
iptables -m u32 是唯一靠谱的底层方案
u32 模块允许按位/字节偏移提取并比较原始数据包字段,是实现协议级长度校验的最直接方式。它绕过内核连接跟踪和协议栈解析,直读 skb 数据,适合做早期丢弃。
- 必须配合
-p tcp或-p udp使用,否则无法定位协议头偏移 - TCP 包长校验关键点:
tcp header length(data offset 字段,bit 12–15)× 4 得出 TCP 头长度,再与 IP 总长减去 IP 头长的结果比对 - 典型命令示例(丢弃 TCP 头长度声明 > 实际剩余包长的包):
iptables -A INPUT -p tcp -m u32 --u32 "0>>22&0x3C@12>>28&0xF * 4 >22&0x3C@0 & 0xFFFF = 0" -j DROP
(说明:提取 IP 总长、TCP data offset、计算 TCP 头长,并验证是否 ≤ IP 总长 − IP 头长) - UDP 校验更简单:
0>>22&0x3C@8 & 0xFFFF 可匹配 UDP 长度字段
别碰 -m string 做包长过滤
string 模块设计目标是匹配应用层内容(如 HTTP User-Agent),它会触发 skb 分片重组和内存拷贝,在高吞吐场景下极易引发软中断瓶颈。更严重的是:它默认只检查前 1500 字节,而畸形包往往在末尾做手脚;且无法获取协议头中动态计算所需的字段(如 TCP offset)。实测在 10Gbps 流量下,启用 string 规则会使 CPU softirq 占用率飙升 40%+,得不偿失。
真正生效的前提:关闭 conntrack 对目标链路的干扰
如果你在 INPUT 链用 u32 过滤,但系统启用了 nf_conntrack,部分包可能已被提前标记为 INVALID 或 ESTABLISHED,导致规则不命中。必须确保:
- 在 raw 表的
PREROUTING链插入u32规则(iptables -t raw -A PREROUTING ...),避开 conntrack 初始化路径 - 或显式跳过 conntrack:
-j NOTRACK配合-t raw,但需注意这会影响后续 NAT 和状态防火墙逻辑 - 验证是否生效:
cat /proc/net/nf_conntrack | grep -c "src=.*dst=.*"应明显下降,同时iptables -t raw -L -v中对应规则的 packet 计数应持续增长
协议头字段偏移和掩码计算容易手误,一个 bit 错位就会让整条规则静默失效;建议先用 tcpdump -s 0 -w debug.pcap 抓包,用 Wireshark 查看具体字段位置再反推 u32 表达式。











