nftables 规则加载成功但流量不匹配的 hook / priority 问题

9次阅读

最常见原因是规则挂载的 hook 和 priority 与数据包实际经过的网络路径不一致:每条规则仅在指定 hook(如 input、forward)和 priority 位置生效,且仅对对应方向及协议栈阶段的包可见。

nftables 规则加载成功但流量不匹配的 hook / priority 问题

为什么 nftables 规则加载成功却完全不匹配流量

最常见原因是规则挂载的 hookpriority 与实际数据包经过的网络路径不一致。nftables 不是“全局生效”,每条规则只在指定 hook(如 inputforwardprerouting)和优先级位置生效,且仅对对应方向、协议栈阶段的包可见。

典型误判场景:

  • input hook 写规则想拦截外部访问本机的 TCP 连接,但目标端口已被 conntrack 或早期 netFilter 模块(如 nf_nat)提前处理或跳过
  • forward 中加规则,但容器或 bridge 流量根本没走 host 的 forward 链(例如使用 host.docker.internalipv6 SLAAC 场景)
  • prerouting 做 DNAT,但规则 priority 太高(如 -300),被 nf_conntrack(默认 priority -200)之后才执行,导致连接跟踪已建立,DNAT 失效

nft list ruleset 看不到你的规则?检查 hook 和 priority 是否被覆盖

nft list ruleset 输出中,每条 chain 都明确标注了 type filter hook input priority 0 这类信息。如果规则“加载成功”但没出现在输出里,大概率是:你重复声明了同名 chain,后加载的覆盖了前一个;或者用了错误的 family(inet vs ip vs ip6),导致规则落在另一个 Namespace 下。

实操建议:

  • nft list ruleset -a 查看 handle 编号,确认规则是否真被插入到目标 chain 底部
  • 检查 chain 定义是否带 type filter hook input priority 0,而不是漏写 priority——省略时默认为 0,但很多内核模块(如 nf_tables_ipv4)注册的内置 chain 是 priority -200,0 反而排在它们之后
  • 避免混用 inet(双栈)和 ip(IPv4 only)family:IPv6 流量不会匹配 ip family 的 input chain,即使规则语法完全一样

抓包验证 packet path:哪个 hook 实际收到了这个包

光看规则列表不够,必须确认数据包到底走到哪一级。推荐用 tcpdump + nft monitor trace 联合定位:

  • 先用 tcpdump -i any port 80 确认包确实到达 host(排除路由、网卡、offload 问题)
  • 再开一个终端运行 nft monitor trace,然后发测试请求——你会看到每条匹配的规则 handle、hook 名称、priority 数值,甚至跳转 chain 的完整路径
  • 若 trace 输出里完全没出现你的规则 handle,说明包没走到那个 hook,或在更早的 priority 被 accept/drop 终止了

注意:nft monitor trace 默认只显示被规则显式 trace 的包,需先给 chain 加 meta nftrace set 1 才能开启全量追踪(临时调试可用,线上慎用)。

priority 数值越小真的越早执行?别被直觉骗了

priority 是按数值升序执行的:-300 比 -200 更早,0 比 100 更早。但关键陷阱在于——不同 hook 的 priority 空间是独立的,且内核模块注册的内置 chain 有固定优先级,比如:

  • nf_conntrack:prerouting priority -200,output priority -200
  • nf_nat:prerouting priority -100,output priority -100
  • 你自己定义的 filter chain 若设为 priority 0,在 prerouting 中就排在 nat 之后,此时做 DNAT 已无效

所以不是“随便设个负数就行”,而是要查清依赖模块的 priority(cat /proc/net/nf_tables_hooks 或翻内核文档),再决定你的规则该插在哪一档。比如做早期包标记,用 priority -300;做最终过滤,用 priority 100 更安全。

真正容易被忽略的是:同一 priority 下多条规则的执行顺序,取决于它们插入的先后(handle 递增),而不是你在 nft 命令里写的顺序。批量加载时务必用 nft -f 并确保 chain 创建和 rule 插入在同一个事务里。

text=ZqhQzanResources