Netfilter 的流表基础设施

本文档描述了 Netfilter 流表基础设施,它允许您定义通过流表数据通路的快速路径。此基础设施还提供硬件卸载支持。流表支持第 3 层 IPv4 和 IPv6 以及第 4 层 TCP 和 UDP 协议。

概述

一旦流的第一个数据包成功通过 IP 转发路径,从第二个数据包开始,您可以决定通过您的规则集将该流卸载到流表。流表基础设施提供了一个规则动作,允许您指定何时将流添加到流表。

在流表中找到匹配条目的数据包(即,流表命中)通过 neigh_xmit() 传输到输出网络设备,因此,数据包绕过传统的 IP 转发路径(可见的效果是您不会在入口之后的任何 Netfilter 钩子中看到这些数据包)。如果流表中没有匹配的条目(即,流表未命中),则数据包将遵循传统的 IP 转发路径。

流表使用可调整大小的哈希表。查找基于以下 n 元组选择器:第 2 层协议封装(VLAN 和 PPPoE)、第 3 层源和目标、第 4 层源和目标端口以及输入接口(如果存在多个连接跟踪区域,则很有用)。

“flow add”动作允许您填充流表,用户有选择地指定将哪些流放置到流表中。因此,除非用户明确指示流通过策略使用此新的替代转发路径,否则数据包将遵循传统的 IP 转发路径。

流表数据通路如图 1 所示,其中描述了包括 Netfilter 钩子和流表快速路径绕过的传统 IP 转发路径。

                                       userspace process
                                        ^              |
                                        |              |
                                   _____|____     ____\/___
                                  /          \   /         \
                                  |   input   |  |  output  |
                                  \__________/   \_________/
                                       ^               |
                                       |               |
    _________      __________      ---------     _____\/_____
   /         \    /          \     |Routing |   /            \
-->  ingress  ---> prerouting ---> |decision|   | postrouting |--> neigh_xmit
   \_________/    \__________/     ----------   \____________/          ^
     |      ^                          |               ^                |
 flowtable  |                     ____\/___            |                |
     |      |                    /         \           |                |
  __\/___   |                    | forward |------------                |
  |-----|   |                    \_________/                            |
  |-----|   |                 'flow offload' rule                       |
  |-----|   |                   adds entry to                           |
  |_____|   |                     flowtable                             |
     |      |                                                           |
    / \     |                                                           |
   /hit\_no_|                                                           |
   \ ? /                                                                |
    \ /                                                                 |
     |__yes_________________fastpath bypass ____________________________|

             Fig.1 Netfilter hooks and flowtable interactions

流表条目还存储 NAT 配置,因此所有数据包都根据从传统 IP 转发路径指定的 NAT 策略进行修改。TTL 在调用 neigh_xmit() 之前递减。考虑到传输标头丢失,分段流量被传递到传统的 IP 转发路径,在这种情况下,无法进行流表查找。TCP RST 和 FIN 数据包也传递到传统的 IP 转发路径以正常释放流。超出 MTU 的数据包也被传递到传统的转发路径,以便向发送者报告数据包过大 ICMP 错误。

配置示例

启用流表绕过相对容易,您只需要创建一个流表并在您的 forward 链中添加一个规则

table inet x {
        flowtable f {
                hook ingress priority 0; devices = { eth0, eth1 };
        }
        chain y {
                type filter hook forward priority 0; policy accept;
                ip protocol tcp flow add @f
                counter packets 0 bytes 0
        }
}

此示例将流表“f”添加到 eth0 和 eth1 网络设备的入口钩子。如果您需要执行资源分区,则可以创建任意数量的流表。流表优先级定义了钩子在管道中运行的顺序,如果您已经有一个 nftables 入口链,这很方便(请确保流表优先级小于 nftables 入口链,因此流表在管道中提前运行)。

forward 链“y”中的“flow offload”动作将一个条目添加到流表中,用于在回复方向上进入的 TCP syn-ack 数据包。一旦流被卸载,您将观察到上述示例中的计数器规则不会针对通过转发绕路转发的数据包进行更新。

您可以通过列出连接跟踪表时显示的 [OFFLOAD] 标签来识别已卸载的流。

# conntrack -L
tcp      6 src=10.141.10.2 dst=192.168.10.2 sport=52728 dport=5201 src=192.168.10.2 dst=192.168.10.1 sport=5201 dport=52728 [OFFLOAD] mark=0 use=2

第 2 层封装

自 Linux 内核 5.13 起,流表基础设施会发现 VLAN 和 PPPoE 网络设备背后的真实网络设备。流表软件数据通路解析 VLAN 和 PPPoE 第 2 层标头,以提取以太类型和 VLAN ID / PPPoE 会话 ID,这些 ID 用于流表查找。流表数据通路还处理第 2 层解封装。

您无需将 PPPoE 和 VLAN 设备添加到您的流表,相反,真实设备足以让流表跟踪您的流。

桥接和 IP 转发

自 Linux 内核 5.13 起,您可以将桥接端口添加到流表。流表基础设施会发现桥接设备背后的拓扑结构。这允许流表在交换机/路由器中的桥接端口(在下面的示例图中表示为 eth1 和 eth2)和网关设备(表示为 eth0)之间定义快速路径绕过。

        fastpath bypass
 .-------------------------.
/                           \
|           IP forwarding   |
|          /             \ \/
|       br0               eth0 ..... eth0
.       / \                          *host B*
 -> eth1  eth2
     .           *switch/router*
     .
     .
   eth0
 *host A*

流表基础设施还支持桥接 VLAN 过滤操作,例如 PVID 和未标记。您还可以在桥接端口之上堆叠传统的 VLAN 设备。

如果您希望流表在桥接端口和 IP 转发路径之间定义快速路径,则必须将桥接端口(由真实网络设备表示)添加到流表定义中。

计数器

流表可以通过在流表定义中指定计数器语句来将数据包和字节计数器与现有的连接跟踪条目同步,例如

table inet x {
        flowtable f {
                hook ingress priority 0; devices = { eth0, eth1 };
                counter
        }
}

自 Linux 内核 5.7 起提供计数器支持。

硬件卸载

如果您的网络设备提供硬件卸载支持,您可以通过流表定义中的“offload”标志来启用它,例如

table inet x {
        flowtable f {
                hook ingress priority 0; devices = { eth0, eth1 };
                flags offload;
        }
}

有一个工作队列将流添加到硬件。请注意,在工作队列有机会将流卸载到网络设备之前,仍有一些数据包可能在流表软件路径上运行。

您可以通过列出连接跟踪表时显示的 [HW_OFFLOAD] 标签来识别硬件卸载的流。请注意,[OFFLOAD] 标签指的是软件卸载模式,因此 [OFFLOAD] 指的是软件流表快速路径,而 [HW_OFFLOAD] 指的是流正在使用的硬件卸载数据通路,两者之间存在区别。

流表硬件卸载基础设施还支持 DSA(分布式交换机架构)。

限制

流表的行为类似于缓存。如果用于传输的目标 MAC 地址或出口网络设备发生变化,流表条目可能会过时。

如果出现以下情况,这可能会成为问题

  • 您在软件模式下运行流表,并且在您的设置中组合了桥接和 IP 转发。

  • 启用了硬件卸载。

更多阅读

本文档基于 LWN.net 文章 [1][2]。Rafal Milecki 还做了一个非常完整和全面的总结,称为“网络加速状态”,其中描述了这个基础设施被纳入主线之前的情况[3],并且它也对这项工作进行了粗略的总结[4]