nftables 状态对象(Stateful Objects)
🧠 何为状态对象 (Stateful Objects)
nftables 中的状态对象是其实现“有状态”流量跟踪的核心。简单来说,它们是一类可以在规则中创建并引用,并能持续跟踪和记录状态数据的对象。其核心特性如下:
- 像变量一样命名:每个状态对象在所属的
table内都有一个独特的名字,方便引用和管理。 - 状态贯穿始终:它们记录的是“状态”,而非瞬时结果。数据包每匹配一次规则,对象的计数器或配额记录就会被更新。这种状态持久存在,直到管理员主动重置。
- 复用性与灵活性:一旦创建,一个
counter或quota对象就可以在table内任意chain的多条规则中被重复调用,从而避免重复配置。
🗂️ 状态对象类型详解:counter 与 quota
目前,nftables 主要支持两种核心类型的状态对象,即计数器 (counter) 和配额 (quota)。
计数器 (counter)
counter 是用于统计包的数量和字节数的精确计算器,简单可靠。每个 counter 对象主要包含 packets 和 bytes 两个累计数值字段。
场景示例
一个典型的计数场景是统计来源为特定IP地址的SSH流量:
1# 1. 创建用于计数SSH流量的计数器 2nft add table filter 3nft add counter filter incoming-ssh-count
引用对象: 在链中添加一条规则,让所有匹配的流量都经过 incoming-ssh-count 计数器处理。
1nft add chain filter input { type filter hook input priority 0; } 2nft add rule filter input tcp dport 22 counter name incoming-ssh-count
配额 (quota)
quota 用于设置一个最大流量阈值,并按包数或字节数累积统计。当达到阈值时自动匹配,通常用于限制流量,可与 drop 或 reject 结合实现访问控制。创建时须指定单位(bytes、kbytes、mbytes 等),支持 <(up to) 或 >(over) 条件匹配动作。
行为示例
下面通过一个完整示例展示配额的使用:达到100MB后丢弃匹配的UDP 5060流量。
1# 1. 创建配额对象 2nft add quota inet foo sip-quota { over 100 mbytes used 0 bytes } 3 4# 2. 创建链和规则 5nft add chain inet foo sip-drop { type filter hook postrouting priority 0; policy accept; } 6nft add rule inet foo sip-drop udp dport 5060 quota name "sip-quota" drop
注:上述表中的“原子性更新”指当数据包匹配时,计数器的增加和配额的消耗是原子操作,不会出现因并发导致计数不准的问题。锁定机制由内核保证。
⚙️ 对象管理核心命令一览
我们可以通过 nft 命令灵活地管理这些状态对象。常用的管理操作如下:
| 操作 | Counter 命令示例 | Quota 命令示例 |
|---|---|---|
| 创建 | nft add counter inet filter incoming-ssh | nft add quota inet filter http-quota { 25 mbytes } |
| 列出 (所有) | nft list counters | nft list quotas |
| 列出 (表内) | nft list counters table inet filter | nft list quotas table inet filter |
| 重置 (所有) | nft reset counters | nft reset quotas |
| 重置 (特定) | nft reset counter inet filter incoming-ssh | nft reset quota inet filter http-quota |
| 删除 | nft delete counter inet filter incoming-ssh | nft delete quota inet filter http-quota |
如何监控配额使用状态
对于 quota 对象,在创建时可以设置 used 初始值。你可以通过 nft reset 命令原子性地查看并归零 quota 的 used 值,这在定期查看流量消耗时非常有用。
🕹️ Meter 的演变与最佳实践
在较老的 nftables 文档和配置中,会遇到 meter 这个关键词,用于实现更复杂的、以不同流量特征为“键(key)”的有状态限速(如限制每个 IP 的连接数)。但从 nft v0.8.1、Linux kernel 4.3 开始,meter 关键词已废弃,官方和社区推荐使用动态 set 语法来替代,以获得更强的可读性和一致性。
下面是两种方式的对比(效果等价,但动态 set 是推荐做法):
| 功能 (每源IP限速) | 动态 set (推荐) | 老式 meter (已废弃) |
|---|---|---|
| 创建集合 | nft add set my_filter my_ssh_meter { type ipv4_addr; flags dynamic; } | N/A |
| 添加规则 | nft add rule ... add @my_ssh_meter { ip saddr limit rate 10/second } accept | nft add rule ... meter my_ssh_meter { ip saddr limit rate 10/second } accept |
| 查看状态 | nft list set my_filter my_ssh_meter | nft list meter my_filter my_ssh_meter |
| 管理灵活性 | 高,支持 timeout、size、flags 等完整语法。 | 受限,功能不够完善。 |
| 推荐程度 | ✅ 强烈推荐 | ❌ 已淘汰,不建议使用 |
📝 总结
状态对象是 nftables 的高级功能,它将“状态跟踪”这个核心任务从规则本身中解耦出来。通过掌握 counter 和 quota,再结合动态 set 的强大能力,我们可以构建出非常灵活、清晰且强大的有状态防火墙策略。
在 counter 和 quota 之外,nftables 状态对象家族其实还包括很多成员,它们共同支撑起更复杂和精细的网络控制。
下面是一份更完整的清单,供你参考:
| 状态对象类型 | 命令缩写/关键词 | 功能描述 |
|---|---|---|
counter | counter | 用于精确统计匹配数据包的数量和字节数。 |
quota | quota | 设置一个流量阈值(按字节数),达到后即可触发后续动作(如丢弃)。 |
limit | limit | 实现基于数据包或字节数的速率限制,是流量整形的基础。 |
connlimit | connlimit | 限制匹配特定条件(如源IP、目标端口)的连接数量。 |
ct helper | ct helper | 为连接跟踪系统指定一个“助手”,用于动态识别和跟踪复杂协议(如FTP、SIP)的关联连接。 |
ct timeout | ct timeout | 为连接跟踪条目自定义超时时间,替代系统默认的超时策略。 |
ct expect | ct expect | 预先在连接跟踪表中设置“期望”的连接,通常用于处理动态协商的协议。 |
synproxy | synproxy | 代理并验证TCP SYN请求,以减轻SYN Flood攻击的影响。 |
secmark | secmark | 将数据包与预定义的安全上下文标记关联,用于与 Linux 安全模块(如 SELinux)进行交互。 |
tunnel | tunnel | 用于配置和管理网络隧道。 |
除了上述独立对象,
nftables还支持flowtable这种硬件加速路径对象。它并非状态对象,而是定义了一个“快速路径”,让符合条件的数据包可以绕过常规的防火墙处理,直接通过硬件(如果支持)或软件进行高速转发,显著提升性能。
