nftables复合规则的核心价值在于构造可哈希的唯一键值而非堆叠条件,通过concat将多字段(如IP+端口+协议)拼成单个键实现O(1)查表,跳过线性扫描;需严格按顺序与类型创建映射、插入键值,并在规则中一致拼接字段。

用 concat 拼接字段,实现 O(1) 查找
传统写法如 nft add rule inet filter input ip saddr 192.168.10.5 tcp dport 8080 accept 是逐字段比对,规则越多越慢。而 concat 把字段组合成键,例如 ip saddr . tcp dport → 192.168.10.5 . 8080,内核用哈希表直接定位动作。
- 先建映射:
nft add map inet filter ipport_map { type ipv4_addr . inet_service : verdict; } - 插入键值:
nft add element inet filter ipport_map { 192.168.10.5 . 8080 : accept, 10.0.20.12 . 3306 : drop } - 在链中调用:
nft add rule inet filter input meta l4proto tcp @ipport_map { ip saddr . tcp dport } counter
扩展到三字段:IP + 端口 + 协议
同一端口对 TCP 和 UDP 可设不同策略,必须引入协议类型字段。注意顺序和类型严格对应:
-
ipv4_addr(4 字节)对应ip saddr -
inet_service(2 字节)对应tcp dport或udp dport -
inet_protocol(1 字节)对应ip protocol,值为 6(TCP)或 17(UDP) - 命令:
nft add map inet filter ipportproto_map { type ipv4_addr . inet_service . inet_protocol : verdict; } - 插入:
nft add element inet filter ipportproto_map { 192.168.10.5 . 8080 . 6 : accept, 192.168.10.5 . 8080 . 17 : drop } - 规则中拼接顺序必须一致:
@ipportproto_map { ip saddr . tcp dport . ip protocol }
用匿名集合替代多条单端口规则
当只需匹配多个端口或 IP,且不涉及跨维度组合时,匿名集合更轻量、更高效:
- 一条规则匹配多个端口:
nft add rule ip filter input tcp dport { 80, 443, 8080, 8443 } ct state new accept - 限制多个源 IP 访问多个管理端口:
nft add rule ip filter input ip saddr { 203.0.113.10, 203.0.113.11 } tcp dport { 22, 9000, 9001 } accept - 内核对匿名集合做的是单次哈希或位图匹配,不是逐条规则遍历,尤其在 10+ 端口场景下性能优势明显
避坑要点:长度、类型与验证
concat 不是字符串拼接,而是二进制字段拼接,容易静默失效:
- 所有字段总长不能超过 256 字节(常见组合如 IP+端口+协议仅占 7 字节,安全)
- 字段类型必须兼容:不能混用
tcp dport和udp dport在同一映射中,除非统一用th dport - 验证是否命中:
nft list map inet filter ipportproto_map -n查看实际插入的十六进制值;加counter后用nft monitor trace观察是否真正走映射路径


















