Linux nftables 与 iptables 对比分析

2次阅读

新建规则不该再用 iptables,因内核3.13起nftables已是官方默认框架,2026年主流发行版将停用iptables原生后端,仅通过兼容层运行,带来性能损耗与行为差异。

Linux nftables 与 iptables 对比分析

为什么现在新建规则不该再用 iptables

内核从 3.13 开始,nftables 就已是官方推荐的默认防火墙框架;2026 年主流发行版(如 RHEL 9、debian 12+、ubuntu 22.04 LTS 及更新)已默认停用 iptables 命令的原生后端,转而通过 iptables-compat 层模拟运行——这层翻译不仅引入额外开销,还屏蔽了连接跟踪状态、计数器精度等底层行为差异。

  • 线性匹配 vs 哈希/树结构:iptables 的 input 链规则是纯顺序遍历,1000 条规则平均要检查 500 次才能命中;nftables 在内核中用高效数据结构索引,同规模下匹配延迟可压到亚微秒级
  • udp 高频场景尤其明显:DNS、QUIC、gRPC-Web 等服务依赖无状态快速放行,iptables 的 conntrack 强制介入常导致 CPU 占用突增,而 nftables 支持 ct state invalid drop + udp dport @allowed_ports 这类原子组合,避免状态表膨胀
  • iptables-save 导出的规则无法直接复用于 nftables;反向转换要用 iptables-translate,但该工具不处理自定义模块(如 ipset 替代方案需手动重写为 set

nftables 中 inet 表怎么真正实现 IPv4/IPv6 双统一

很多人以为加个 inet 地址簇就能“自动兼容双栈”,实际不是:它只表示“此表可同时注册 IPv4 和 IPv6 hook”,但规则本身仍需显式声明协议上下文。漏掉这点,会导致 IPv6 流量静默丢弃。

  • 正确写法是:在匹配条件中明确使用 ipip6,或用 meta nfproto 区分;例如 tcp dport 22 不生效,必须写成 ip tcp dport 22ip6 tcp dport 22
  • 若想一条规则覆盖双栈,得用 inet 表 + 协议无关匹配,比如:nft add rule inet Filter input meta nfproto { ip, ip6 } tcp dport 22 accept
  • 回环地址也要分开处理:ip saddr 127.0.0.1ip6 saddr ::1 不能合并;更稳妥的做法是用 meta iifname "lo",它不依赖地址族

添加 INPUT 规则时最容易被忽略的三个 hook 依赖

nftables 的基链(base chain)不是“摆设”,它绑定到 netfilter 的具体 hook 点,而某些关键动作(如连接跟踪初始化、raw 表 bypass)只在特定 hook 才生效。跳过它们,ct state established 类规则可能永远不匹配。

  • prerouting hook 是 conntrack 初始化必经之路:没在这里注册 ct 相关 chain,后续所有 ct state 判断都会返回 invalid
  • input hook 处理本机接收包,但若你启用了 nf_conntrack_helper(如 FTP、SIP),helper 模块必须在 raw 表的 prerouting 中启用,否则 ALG 不工作
  • 策略(policy)设置有陷阱:执行 nft add chain inet filter input { type filter hook input priority 0 ; policy drop ; } 后,所有未显式 accept 的包立即被丢弃——包括你还没来得及加的 ssh 规则。建议先设 policy accept,规则就位后再改

从 iptables 迁移时最痛的三处语法断层

翻译工具能搞定 80% 的基础规则,但以下三类必须手改,否则运行时报错或逻辑错位:

  • -m multiport --dports 22,80,443 → 必须转为 nftables setnft add set inet filter ssh_http { type inet_service ; },再用 tcp dport @ssh_http 引用;直接写逗号列表会报错
  • -j LOG --log-prefix "DROP: " → nftables 没有内置 log prefix,得用 log prefix "DROP: " level warn,且 level 参数不可省略,否则日志不输出
  • -j REJECT --reject-with icmp-host-prohibited → nftables 中 reject 是独立表达式,写法是 reject with icmp type host-prohibited(注意 type 关键字和连字符位置)

复杂点在于:nftables 的规则是“原子提交”的,但它的错误提示极其简陋——比如少一个分号、括号不匹配,nft 命令只会报 Error: syntax error,连第几行都懒得说。建议用 nft -f /tmp/rules.nft 分文件调试,别硬敲长命令。

text=ZqhQzanResources