2367 字
12 分钟
ovs-ofctl中的action之ct

ovs-ofctl 流表中的 ct 动作 (actions=ct(...)) 是 Open vSwitch (OVS) 连接追踪 (Connection Tracking, conntrack) 功能的核心指令。它的核心作用是 ​​ 将数据包交给内核的连接追踪子系统进行处理 ​​。ct 动作不仅允许 OVS 感知连接状态(通过后续规则匹配 ct_state),还允许修改连接状态和属性(如执行 NAT),是实现有状态防火墙、NAT、负载均衡等高级网络功能的基础。

ct 动作的核心作用#

  1. ​ 启用连接追踪:​​ 触发内核连接追踪模块检查该数据包。
  2. ​ 建立/更新连接状态:​​ 对于新连接,commit 参数会创建一个新的连接追踪条目;对于现有连接的后续包,会更新该连接的状态(如超时、序列号、状态标志等)。
  3. ​ 提供状态信息:​​ 连接追踪子系统处理后的结果(ct_state, ct_mark, ct_label, ct_zone, ct_nw_proto 等)会设置到数据包的元数据中,供后续的流表规则匹配和决策。
  4. ​ 执行有状态操作:​​ 直接执行 NAT 操作(源 NAT/SNAT、目的 NAT/DNAT、同时 DNAT 和 SNAT)。
  5. ​ 强制策略检查:​​(commit, exec(allow/block)) 强制对连接的第一个数据包应用策略(通常是防火墙的“默认拒绝”策略的例外)。

ct 动作的主要参数及用法详解#

ct 动作接受多个参数,通过括号 () 内逗号分隔指定。主要参数如下:

1. ​commit(提交连接)​​#

  • ​ 作用:​​ 对于 +new 状态的数据包(即新连接的第一个包),commit 指示连接追踪子系统 ​​ 创建 ​​ 一个持久化的连接追踪条目记录这条连接。连接建立后,后续属于该连接的包(+est, +rel)都会自动匹配和更新这个条目。​​ 对于 +new 的包,commit 是必须的,否则追踪状态不会被保存。​
  • ​ 用法:​
Terminal window
actions=ct(commit, ...其他参数...) # 创建/提交连接
actions=ct(...) # 不带 commit 通常只做检查,不持久化新连接(适用于 `+est`/`+rel` 包)
  • ​ 典型场景:​
    • 放行并记录内部客户端发起的新出站连接。
    • 在 DNAT 规则后提交转换后的连接,确保回包能正确匹配。

2. ​zone[=](连接追踪域)​​#

  • ​ 作用:​​ 指定连接追踪发生的 ​​ 域(Zone)​​。每个 zone 拥有独立的连接追踪表。这允许 ​​ 逻辑隔离 ​​ 不同的网络策略或租户(例如 VRF 或租户隔离)。​zone 参数对 ct 动作的行为至关重要。​
  • ​ 用法:​
Terminal window
actions=ct(zone=5, commit) # 在 zone 5 中追踪并提交连接
actions=ct(zone=10) # 在 zone 10 中检查连接状态
actions=ct(zone=0) # 在默认 zone 0 中操作(与不指定 zone 等效)
actions=ct() # 隐含在默认 zone 0 中操作
  • ​ 典型场景:​
    • 多租户云环境中,不同租户或不同逻辑网络的流量在不同 zone 中追踪,避免冲突。
    • 需要在不同策略点(如物理网卡入口和虚拟机 vNIC 入口)独立跟踪连接状态。

3. ​NAT 参数(nat)​​#

  • ​ 作用:​​​​ 直接在 ct 动作内部执行网络地址转换 (NAT)​​。这是 ct 动作执行有状态操作的关键。

  • ​ 子参数:​

    • src[=]:​​ 执行 ​​ 源 NAT (SNAT)​​。将匹配数据包的 ​​ 源 IP 地址 ​​ 替换为指定的 IP 地址或 IP 范围/端口范围。
    Terminal window
    actions=ct(commit, nat(src=203.0.113.1)) # SNAT 到单个 IP (端口自动分配)
    actions=ct(commit, nat(src=203.0.113.2:1024-2048)) # SNAT 到指定 IP 和端口范围
    actions=ct(commit, nat(src=192.168.1.0/24)) # SNAT 到 IP 范围(通常配合哈希)
    • dst[=]:​​ 执行 ​​ 目的 NAT (DNAT)​​。将匹配数据包的 ​​ 目标 IP 地址(和可选的端口)​​ 替换为指定的 IP 地址(和端口)。
    Terminal window
    actions=ct(commit, nat(dst=192.168.1.100)) # DNAT 目标 IP (端口不变)
    actions=ct(commit, nat(dst=192.168.1.100:8080)) # DNAT 目标 IP 和端口
    • dst[=]: + src[=] (组合):​​ 执行 ​​ 同时进行的 DNAT 和 SNAT​​(有时也称为 Full NAT)。先做 DNAT,再做 SNAT。
    Terminal window
    actions=ct(commit, nat(dst=192.168.1.100, src=10.0.0.1)) # DNAT + SNAT
    actions=ct(commit, nat(dst=192.168.1.100:80, src=203.0.113.1:1000-2000)) # 详细地址/端口
    • persistent:​​ 用于 NAT 网关多出口 IP 的场景,使得同一个内部连接的 ​​ 不同会话 ​​ 尽可能使用同一个外部 IP(对于特定协议如 FTP/PASV)。通常和 src= 范围一起使用。
    Terminal window
    actions=ct(commit, nat(src=203.0.113.10-203.0.113.20, persistent))
  • ​ 典型场景:​

    • 使内部私有 IP (192.168.1.x) 访问互联网时共享一个或多个公共 IP (SNAT)。
    • 将外部访问 VIP (203.0.113.10) 的流量重定向到内部真实服务器 (192.168.1.100) (DNAT)。
    • 在 DNAT 后,进一步修改源地址以确保回包路径(DNAT + SNAT)。

4. ​force(强制进入连接追踪)​​#

  • ​ 作用:​​ 强制 ​​ 重新进行 ​​ 连接追踪处理,即使该数据包之前可能已经过 ct 处理。​​ 使用需谨慎,常见于特殊转发路径或调试。​
  • ​ 用法:​
Terminal window
actions=ct(force, ...其他参数...)

5. ​table=(跳转到指定流表)​​#

  • ​ 作用:​​ 在连接追踪处理(包括可能的 NAT)​​ 完成后 ​​,将数据包 ​​ 重新注入 ​​ 到 OVS 的指定编号的流表 (table=N) 的 ​​ 起始处 ​​,进行新一轮匹配。​​ 这是 ct 动作与流表形成“环回”或“迭代”处理的关键。​
  • ​ 用法:​
Terminal window
actions=ct(table=1, zone=5, commit, nat(src=203.0.113.1)) # 提交连接、执行 SNAT 后跳转到 table 1
actions=ct(table=2) # 检查连接状态后跳转到 table 2
  • ​ 典型场景:​
    • ​ 多阶段策略处理:​​ 例如,在 table=0 提交连接并执行 DNAT,然后跳转到 table=1 匹配 ct_state 并应用防火墙规则。
    • ​ 复杂路径:​​ 在同一个流表中使用 ct + goto_table 可能导致处理顺序不清;ct(table=N) 使逻辑更清晰。
    • ​ 处理回包方向:​​ 在 DNAT 后的 table 中匹配 ct_state=+trk+est 允许已建立连接的回包通过防火墙。

6. ​​exec(ACTION) (执行元数据动作)​​#

  • ​ 作用:​​ 在连接追踪处理 ​​ 之后 ​​(可能创建了新连接条目),​​ 对连接 ​​(而当前数据包)​​ 执行一个指定的动作 (ACTION)​​。ACTION 的结果会影响当前连接以及所有属于该连接的后续数据包。

  • ​ 支持的 ACTION:​

    • exec(allow) / exec(allow_src) / exec(allow_dst):​​ 为 ​​ 整个连接 ​​(或仅源端/目标端)​​ 添加允许策略标记 ​​。这通常在配置了 --default-deny 的有状态防火墙中用于明确放行匹配 +new 的有效连接。exec(allow) 通常 ​​ 需要配合 commit 一起使用在 +new 的数据包上 ​​。
    Terminal window
    priority=100, tcp, ct_state=+new, actions=ct(commit, exec(allow)) # 提交新连接并允许它及其后续/回包
    • exec(drop) / exec(reject):​​ 为连接添加拒绝或拒绝并发送 TCP RST / ICMP 不可达的策略标记(不常用)。
    • exec(set_field:FIELD=VALUE):​​ ​​ 设置连接的 ct_markct_label​。ct_mark/ct_label 是连接追踪条目的元数据,可以被后续规则匹配或修改(即使 IP/Port 转换了!)。
    Terminal window
    actions=ct(commit, exec(set_field:ct_mark=0x10)) # 设置连接的 ct_mark 为 0x10
    actions=ct(exec(set_field:ct_label=0x123/0x123)) # 设置连接的 ct_label 位 (0x123 是值,0x123 是掩码)
  • ​ 典型场景:​

    • ​ 有状态防火墙:​​ 在提交新连接时执行 exec(allow) 允许其通信。
    • ​ 基于连接的标记:​​ 设置 ct_mark/ct_label 来标记连接属性(如 QoS 等级、来自哪个租户),后续基于这些标记应用策略(限速、计费、ACL)。

7. ​alg= (指定应用层网关协议)​​#

  • ​ 作用:​​ 为 ​​ 特定复杂协议 ​​(如 ftp, tftp, sip)​​ 显式启用应用层网关 (ALG)​​。ALG 会深度解析协议内容,发现协议内部协商的 IP/Port,并为这些协商出的衍生连接动态打开端口或修改其地址信息(通常需要配合 helper 内核模块)。

  • ​ 用法:​

Terminal window
actions=ct(commit, alg=ftp) # 提交 FTP 控制连接,并启用 FTP ALG
actions=ct(commit, alg=sip) # 提交 SIP 连接,启用 SIP ALG
  • ​ 典型场景:​
    • 允许被动 FTP (PASV/PORT) 或支持 FTP 穿越 NAT 时自动打开数据连接端口。
    • 解析 SIP 信令中的 SDP 媒体地址,确保 RTP 媒体流能正确建立。

ct 动作使用的关键点总结#

  1. ct_state 的依赖:​​ 所有基于 ct_state(如 +new, +est, +trk)的匹配规则,​​ 其数据包必须已经经过至少一次 ct 动作的处理 ​​。
  2. ​Stateful 与 Stateless:​ ct 动作是 OVS 实现有状态功能的触发点执行者。没有 ct,流表只能做无状态操作。
  3. ​NAT 的最佳实践:​​ 必须在同一个 ct 动作中结合 commitnat 参数执行 ​(如 ct(commit, nat(src=IP)))。分离 nat 动作(如过时的 mod_nw_src)会导致 NAT 失效或无状态。
  4. zone 隔离:​​ 正确使用 zone 是实现复杂网络策略或多租户隔离的基础。不同物理接口、不同 VRF 或不同虚拟机(vNIC)的流量应尽量使用不同 zone 追踪。
  5. ​ 多阶段处理 (table=):​​ 使用 ct(table=N) 将流量在流表之间跳转是构建 ​​ 多阶段、清晰逻辑 ​​ 的防火墙/NAT 规则的推荐方式。
  6. ​ 安全策略 (exec(allow)):​​ 在有状态防火墙设计中,在 +new 数据包上使用 ct(commit, exec(allow)) 是告诉防火墙“允许这个新连接”的标准做法。
  7. ct_mark/ct_label 的威力:​​ 它们是连接级别的标记,​​ 在 IP/Port 转换后依然有效 ​​,为高级策略提供了强大的锚点。

​ 简单来说:ct 动作是 OVS 连接追踪机制的“门户”。它让无状态的 SDN 交换机拥有了感知和管理网络连接状态的能力,是实现生产级、安全、可靠的网络基础设施的关键组件。​