nftables协议过滤是分层匹配、逐级收敛过程,需明确控制L3(ip saddr/daddr)、L4(tcp dport/udp sport)或连接状态(ct state),不可混用字段;推荐使用inet族统一双栈,结合ct state实现有状态精准放行。

nftables 协议过滤不是简单地“按协议名放行或拦截”,而是分层匹配、逐级收敛的过程。关键在于明确你要控制的是哪一层的流量——网络层(IP)、传输层(TCP/UDP/ICMP)、还是连接状态本身,并据此选择对应字段与机制。
精准区分 L3、L4 与连接状态字段
nftables 的匹配能力覆盖 OSI 模型的第 3 层和第 4 层,但字段不可混用:
- 网络层协议类型 →
ip protocol(如ip protocol icmp)或更简洁的meta l4proto(支持tcp/udp/icmp/icmpv6等符号值) - IPv4 地址 →
ip saddr/ip daddr - IPv6 地址 →
ip6 saddr/ip6 daddr - 传输层端口 → 必须在协议已匹配的前提下使用:
tcp dport 22、udp sport 53
⚠️ 错误写法:tcp dport 22单独出现(未前置协议匹配),nftables 会报错 - 连接跟踪状态 →
ct state established,related,invalid,new,这是无协议依赖的独立判断维度
例如,只允许已建立的 TCP 连接返回流量,不关心端口:
nft add rule inet filter input ct state established,related meta l4proto tcp accept
针对不同协议设计差异化策略
不同协议行为差异大,需区别对待:
-
ICMP:适合用
ip protocol icmp+icmp type细控(如仅放行echo-request和echo-reply,禁用redirect或timestamp) -
UDP:无连接,无法依赖
ct state,应结合目标端口+频率限制(如limit rate 10/second burst 20防 UDP Flood) -
TCP:优先用
ct state区分新建与已有连接;对new状态再做端口白名单(如只允许dport {22, 80, 443}) -
非标准协议(如 SCTP、IGMP):需显式指定
meta l4proto数值(如meta l4proto 132),或启用对应内核模块
用 inet 地址簇统一处理双栈,避免规则重复
不要为 IPv4 和 IPv6 分别建表,改用 inet 协议簇:
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }这样一条规则 ip saddr 192.0.2.0/24 自动生效于 IPv4,而 ip6 saddr 2001:db8::/32 则专用于 IPv6;若想统配,可用 inet saddr(自动适配地址族)。
结合连接跟踪实现“有状态”精准放行
默认 drop 策略下,安全且简洁的通行逻辑是:
- 先放通回环接口:
iifname "lo" accept - 再放通已有连接的响应包:
ct state established,related accept - 最后按需放通新建连接:
ip saddr @trusted_ips tcp dport {22, 443} ct state new accept
这种顺序确保:不依赖源端口开放、不暴露内部服务端口给未知客户端、且所有新连接都经过显式授权。
对协议异常流量做主动识别与阻断
某些协议组合本身可疑,可直接拦截:
- TCP SYN 包但目的端口为非常规高危端口(如
tcp dport {4444, 5555, 6666})且ct state new - UDP 流量目标端口非 DNS/NTP/STUN 等常用端口(如
udp dport != {53, 123, 3478}) - ICMP 类型非
echo-request/echo-reply/destination-unreachable(如icmp type != {8, 0, 3})
这类规则无需日志即可 drop,减少干扰;若需审计,加 counter log prefix "PROTO_BLOCK: " 即可。

















