1. DPDK 流分类基础:概念、框架与 rte_flow API 简介
1.1 流分类概念
流分类(Flow Classification)是网络数据平面处理的核心环节,其目标是从高速到达的数据包中识别出属于同一“流”的报文序列,并为后续的转发、过滤、统计或 QoS 操作提供决策依据。在 DPDK 语境下,流分类通常发生在网卡硬件或 PMD 驱动层,利用 NIC 内置的流分类引擎(如 Intel XL710 的 FDIR、Mellanox 的 RTE Flow、Cisco enic 的 Flow Manager)完成线速匹配。
关键概念: 一个“流”由五元组(源 IP、目的 IP、源端口、目的端口、协议号)或更丰富的包头字段(VLAN ID、VXLAN VNI、MPLS 标签、IPv6 Flow Label 等)唯一标识。流分类的粒度决定了转发规则的灵活性与硬件资源消耗的平衡。
流分类在 DPDK 应用中承担以下职责:
- 流量定向: 将特定流分配到指定 RX 队列或 CPU 核心,实现 RSS 增强或精确分发。
- 过滤与丢弃: 匹配恶意或无效流,在硬件层面直接丢弃,避免主机 CPU 处理。
- 标记与计量: 对匹配流设置 VLAN PCP、DSCP 或 Meter 颜色,用于 QoS 策略。
- 统计与镜像: 对特定流进行计数或流量镜像到监控端口。
1.2 DPDK 中的流分类框架
DPDK 的流分类框架并非单一实现,而是通过 rte_flow 抽象层统一不同网卡厂商的硬件流分类能力。框架分为三层:
- 应用层: 用户调用
rte_flow_create()/rte_flow_destroy()/rte_flow_query()等 API。 - 抽象层: librte_flow 库将通用规则描述转换为网卡私有格式。
- 驱动层: PMD(Poll Mode Driver)实现硬件操作,如 i40e、ice、mlx5、bnxt 等。
| 组件 | 路径/库 | 职责 |
|---|---|---|
| rte_flow API | lib/librte_flow | 提供规则创建、匹配、计数、删除的通用接口 |
| rte_flow_item | lib/librte_flow | 定义匹配字段(如 ETH、IPV4、UDP、VXLAN) |
| rte_flow_action | lib/librte_flow | 定义动作(如 QUEUE、DROP、COUNT、MARK) |
| rte_flow_attr | lib/librte_flow | 规则属性(如 ingress/egress、优先级、组) |
| PMD 回调 | drivers/net/* | 将通用规则翻译为 NIC 寄存器/描述符配置 |
调试经验: 不同 PMD 对 rte_flow 的支持程度不同。例如,mlx5 支持大量匹配字段和动作,而某些老款 Intel 网卡(如 82599)仅支持有限五元组。开发前务必查阅
rte_flow_info_get() 获取网卡能力。
1.3 rte_flow API 简介
rte_flow API 是 DPDK 流分类的核心编程接口,采用“匹配+动作”模型。一条规则由三部分组成:
- 属性(Attributes): 指定规则方向(ingress/egress)、优先级、组 ID。
- 匹配项(Items): 一个链表,描述要匹配的协议字段,支持 ETH、VLAN、IPV4、IPV6、UDP、TCP、VXLAN、GTP 等。
- 动作(Actions): 一个链表,描述匹配后的处理行为,如 QUEUE(转发到队列)、DROP(丢弃)、COUNT(计数)、MARK(设置元数据)、RSS(负载均衡)等。
常见陷阱: 匹配项的顺序必须与报文协议栈从外到内一致。例如,先 ETH 后 IPV4 再 UDP,不能颠倒。动作链中 QUEUE 和 RSS 互斥,同时指定会导致规则创建失败。
下面是一个典型的 rte_flow 规则创建代码示例,匹配目的 IP 为 10.0.0.1 的 UDP 报文,并转发到队列 3:
struct rte_flow_attr attr = {
.ingress = 1, // 入方向规则
.priority = 0, // 优先级 0(最高)
};
struct rte_flow_item pattern[4];
struct rte_flow_action actions[2];
struct rte_flow *flow;
struct rte_flow_error error;
// 匹配项 1:以太网(通配)
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[0].spec = NULL;
pattern[0].mask = NULL;
// 匹配项 2:IPv4,目的 IP = 10.0.0.1
struct rte_flow_item_ipv4 ipv4_spec;
struct rte_flow_item_ipv4 ipv4_mask;
memset(&ipv4_spec, 0, sizeof(ipv4_spec));
memset(&ipv4_mask, 0, sizeof(ipv4_mask));
ipv4_spec.hdr.dst_addr = htonl(0x0a000001); // 10.0.0.1
ipv4_mask.hdr.dst_addr = 0xffffffff; // 精确匹配
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[1].spec = &ipv4_spec;
pattern[1].mask = &ipv4_mask;
// 匹配项 3:UDP(通配)
pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
pattern[2].spec = NULL;
pattern[2].mask = NULL;
// 匹配项结束
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
// 动作:转发到队列 3
struct rte_flow_action_queue queue_action = { .index = 3 };
actions[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
actions[0].conf = &queue_action;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;
// 创建规则
flow = rte_flow_create(port_id, &attr, pattern, actions, &error);
if (!flow) {
printf("Flow create failed: %s\n", error.message);
}
| 类型 | 枚举值 | 说明 |
|---|---|---|
| ETH | RTE_FLOW_ITEM_TYPE_ETH | 匹配源/目的 MAC 地址、EtherType |
| VLAN | RTE_FLOW_ITEM_TYPE_VLAN | 匹配 VLAN ID、PCP、DEI |
| IPV4 | RTE_FLOW_ITEM_TYPE_IPV4 | 匹配源/目的 IP、TOS、TTL |
| IPV6 | RTE_FLOW_ITEM_TYPE_IPV6 | 匹配源/目的 IPv6 地址、Flow Label |
| UDP | RTE_FLOW_ITEM_TYPE_UDP | 匹配源/目的端口 |
| TCP | RTE_FLOW_ITEM_TYPE_TCP | 匹配源/目的端口、TCP 标志 |
| VXLAN | RTE_FLOW_ITEM_TYPE_VXLAN | 匹配 VXLAN VNI |
| GTP | RTE_FLOW_ITEM_TYPE_GTP | 匹配 GTP TEID |
| QUEUE | RTE_FLOW_ACTION_TYPE_QUEUE | 转发到指定队列 |
| DROP | RTE_FLOW_ACTION_TYPE_DROP | 丢弃报文 |
| COUNT | RTE_FLOW_ACTION_TYPE_COUNT | 统计匹配报文数/字节数 |
| MARK | RTE_FLOW_ACTION_TYPE_MARK | 设置 32 位元数据,供应用读取 |
| RSS | RTE_FLOW_ACTION_TYPE_RSS | 将流分发到 RSS 队列组 |
调试要点: 创建规则后务必检查返回值。如果
rte_flow_create 返回 NULL,通过 rte_flow_error.message 获取错误描述。常见错误包括:硬件资源不足、不支持的匹配字段组合、优先级冲突、动作与属性不匹配(如在 ingress 规则中使用 VXLAN 解封装动作但网卡不支持)。