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 策略。
  • 统计与镜像: 对特定流进行计数或流量镜像到监控端口。
流分类在 DPDK 数据路径中的位置 网卡硬件 流分类引擎 RX 队列 DPDK 应用 说明:流分类引擎在网卡内部完成匹配,匹配结果决定报文进入哪个 RX 队列。 DPDK 应用通过 rte_flow API 向引擎下发规则,实现硬件卸载。 ① 规则下发 ② 硬件匹配 ③ 队列分发 ④ 应用处理

1.2 DPDK 中的流分类框架

DPDK 的流分类框架并非单一实现,而是通过 rte_flow 抽象层统一不同网卡厂商的硬件流分类能力。框架分为三层:

  1. 应用层: 用户调用 rte_flow_create() / rte_flow_destroy() / rte_flow_query() 等 API。
  2. 抽象层: librte_flow 库将通用规则描述转换为网卡私有格式。
  3. 驱动层: PMD(Poll Mode Driver)实现硬件操作,如 i40e、ice、mlx5、bnxt 等。
DPDK 流分类框架核心组件
组件路径/库职责
rte_flow APIlib/librte_flow提供规则创建、匹配、计数、删除的通用接口
rte_flow_itemlib/librte_flow定义匹配字段(如 ETH、IPV4、UDP、VXLAN)
rte_flow_actionlib/librte_flow定义动作(如 QUEUE、DROP、COUNT、MARK)
rte_flow_attrlib/librte_flow规则属性(如 ingress/egress、优先级、组)
PMD 回调drivers/net/*将通用规则翻译为 NIC 寄存器/描述符配置
调试经验: 不同 PMD 对 rte_flow 的支持程度不同。例如,mlx5 支持大量匹配字段和动作,而某些老款 Intel 网卡(如 82599)仅支持有限五元组。开发前务必查阅 rte_flow_info_get() 获取网卡能力。
DPDK 流分类框架分层架构 应用层:用户调用 rte_flow_create / destroy / query 抽象层:librte_flow 规则解析与校验 驱动层:PMD 硬件适配(i40e / mlx5 / bnxt ...)

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);
}
常用 rte_flow 匹配项与动作速查
类型枚举值说明
ETHRTE_FLOW_ITEM_TYPE_ETH匹配源/目的 MAC 地址、EtherType
VLANRTE_FLOW_ITEM_TYPE_VLAN匹配 VLAN ID、PCP、DEI
IPV4RTE_FLOW_ITEM_TYPE_IPV4匹配源/目的 IP、TOS、TTL
IPV6RTE_FLOW_ITEM_TYPE_IPV6匹配源/目的 IPv6 地址、Flow Label
UDPRTE_FLOW_ITEM_TYPE_UDP匹配源/目的端口
TCPRTE_FLOW_ITEM_TYPE_TCP匹配源/目的端口、TCP 标志
VXLANRTE_FLOW_ITEM_TYPE_VXLAN匹配 VXLAN VNI
GTPRTE_FLOW_ITEM_TYPE_GTP匹配 GTP TEID
QUEUERTE_FLOW_ACTION_TYPE_QUEUE转发到指定队列
DROPRTE_FLOW_ACTION_TYPE_DROP丢弃报文
COUNTRTE_FLOW_ACTION_TYPE_COUNT统计匹配报文数/字节数
MARKRTE_FLOW_ACTION_TYPE_MARK设置 32 位元数据,供应用读取
RSSRTE_FLOW_ACTION_TYPE_RSS将流分发到 RSS 队列组
rte_flow 规则结构:匹配 + 动作 一条 rte_flow 规则 属性 (attr) 匹配项链表 (items) 动作链表 (actions) ETH IPV4 UDP QUEUE END 匹配项顺序必须与协议栈一致;动作链以 END 结尾
调试要点: 创建规则后务必检查返回值。如果 rte_flow_create 返回 NULL,通过 rte_flow_error.message 获取错误描述。常见错误包括:硬件资源不足、不支持的匹配字段组合、优先级冲突、动作与属性不匹配(如在 ingress 规则中使用 VXLAN 解封装动作但网卡不支持)。