2. rte_flow 规则结构:rte_flow_attr、rte_flow_item、rte_flow_action 结构体详解
DPDK rte_flow 规则是流分类与转发控制的核心数据结构。一条完整的 rte_flow 规则由三个独立的结构体组合而成:属性(Attributes)、匹配项(Items) 和 动作(Actions)。理解这三个结构体的内存布局、字段含义和组合规则,是编写正确、高性能流表规则的前提。
核心概念:rte_flow 规则采用“三段式”设计——
rte_flow_attr 定义规则的作用域和方向,rte_flow_item 定义匹配条件(支持多级协议栈串联),rte_flow_action 定义匹配后的处理动作。三者通过指针数组串联,形成一条完整的转发规则。
2.1 rte_flow_attr:规则属性
rte_flow_attr 定义规则的应用方向、优先级和分组信息。该结构体位于 rte_flow.h,字段较少但直接影响规则匹配行为。
struct rte_flow_attr {
uint32_t group; /* 规则所属组号,用于规则分组 */
uint32_t priority; /* 规则优先级,数值越小优先级越高 */
uint32_t ingress:1; /* 1 = 匹配入方向流量 */
uint32_t egress:1; /* 1 = 匹配出方向流量 */
uint32_t transfer:1; /* 1 = 规则应用于硬件卸载 */
uint32_t reserved:29;/* 保留位,必须置0 */
};
| 字段 | 说明 | 典型值 | 注意事项 |
|---|---|---|---|
| group | 规则分组编号,用于多级流表跳转 | 0 ~ 65535 | 硬件卸载时 group 用于流水线跳转 |
| priority | 规则匹配优先级,0 为最高 | 0 ~ 65535 | 同一 group 内 priority 决定匹配顺序 |
| ingress | 入方向匹配标志 | 1 | 通常与 egress 互斥,不可同时为 1 |
| egress | 出方向匹配标志 | 0 | 部分 PMD 不支持 egress 规则 |
| transfer | 硬件卸载标志 | 0 / 1 | transfer=1 时规则由硬件处理 |
调试经验:创建规则时,
ingress 和 egress 不能同时为 1。如果 PMD 返回 EINVAL,首先检查这两个标志位是否冲突。对于纯软件转发(如 testpmd),transfer 必须置 0。
2.2 rte_flow_item:匹配项结构
rte_flow_item 定义规则匹配的协议字段条件。每个 item 对应一个协议层,多个 item 通过数组串联形成完整的匹配链。item 数组必须以 RTE_FLOW_ITEM_TYPE_END 结尾。
struct rte_flow_item {
enum rte_flow_item_type type; /* 匹配项类型,如 ETH / IPV4 / UDP */
const void *spec; /* 精确匹配值 */
const void *last; /* 范围匹配上限(可选) */
const void *mask; /* 掩码,指定 spec 中哪些位参与匹配 */
};
2.2.1 常用 item 类型与配置
| item 类型 | 对应结构体 | 关键字段 | 典型用途 |
|---|---|---|---|
RTE_FLOW_ITEM_TYPE_ETH | rte_flow_item_eth | dst / src / type | 匹配源/目的 MAC 地址 |
RTE_FLOW_ITEM_TYPE_IPV4 | rte_flow_item_ipv4 | hdr.dst_addr / src_addr / next_proto_id | 匹配 IP 五元组中的源/目的 IP |
RTE_FLOW_ITEM_TYPE_UDP | rte_flow_item_udp | hdr.dst_port / src_port | 匹配 UDP 端口号 |
RTE_FLOW_ITEM_TYPE_TCP | rte_flow_item_tcp | hdr.dst_port / src_port | 匹配 TCP 端口号 |
RTE_FLOW_ITEM_TYPE_VLAN | rte_flow_item_vlan | tpid / tci | 匹配 VLAN 标签 |
RTE_FLOW_ITEM_TYPE_END | 无 | — | 标记 item 数组结束 |
常见错误:item 数组中协议顺序必须与报文实际封装顺序一致。例如匹配 TCP 端口时,必须按 ETH → IPV4 → TCP → END 的顺序排列。如果顺序颠倒或遗漏中间协议层,PMD 会返回
EINVAL 或 ENOTSUP。
2.3 rte_flow_action:动作结构
rte_flow_action 定义规则匹配后执行的动作。与 item 类似,action 也以数组形式组织,以 RTE_FLOW_ACTION_TYPE_END 结尾。每个 action 可以携带一个配置结构体指针 conf。
struct rte_flow_action {
enum rte_flow_action_type type; /* 动作类型,如 QUEUE / DROP / MARK */
const void *conf; /* 动作配置参数(可选) */
};
2.3.1 常用 action 类型与配置
| action 类型 | 配置结构体 | 功能说明 | 典型场景 |
|---|---|---|---|
RTE_FLOW_ACTION_TYPE_DROP | 无(conf = NULL) | 丢弃匹配报文 | 安全过滤、黑名单 |
RTE_FLOW_ACTION_TYPE_QUEUE | rte_flow_action_queue | 将报文转发到指定硬件队列 | RSS 分流、队列绑定 |
RTE_FLOW_ACTION_TYPE_MARK | rte_flow_action_mark | 给报文打上 32 位标记 | 后续软件处理识别 |
RTE_FLOW_ACTION_TYPE_COUNT | rte_flow_action_count | 统计匹配报文数量 | 流量监控、计费 |
RTE_FLOW_ACTION_TYPE_RSS | rte_flow_action_rss | 将报文分发到 RSS 队列组 | 多队列负载均衡 |
RTE_FLOW_ACTION_TYPE_END | 无 | 标记 action 数组结束 | — |
/* 示例:将匹配报文转发到队列 3,并打上标记 100 */
struct rte_flow_action actions[3];
struct rte_flow_action_queue queue_conf = {
.index = 3, /* 目标队列编号 */
};
struct rte_flow_action_mark mark_conf = {
.id = 100, /* 标记值 */
};
actions[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
actions[0].conf = &queue_conf;
actions[1].type = RTE_FLOW_ACTION_TYPE_MARK;
actions[1].conf = &mark_conf;
actions[2].type = RTE_FLOW_ACTION_TYPE_END;
actions[2].conf = NULL;
2.4 三段式组合规则示例
以下代码展示如何将 attr、items、actions 组合成一条完整的 rte_flow 规则,并调用 rte_flow_create() 提交给 PMD。
/* 1. 定义属性:入方向,优先级 0,组 0 */
struct rte_flow_attr attr = {
.group = 0,
.priority = 0,
.ingress = 1,
.egress = 0,
.transfer = 0,
};
/* 2. 定义匹配项:匹配 UDP 端口 5005 */
struct rte_flow_item_eth eth_spec = { .type = htons(0x0800) };
struct rte_flow_item_eth eth_mask = { .type = htons(0xFFFF) };
struct rte_flow_item_ipv4 ipv4_spec = { .hdr.next_proto_id = 17 };
struct rte_flow_item_ipv4 ipv4_mask = { .hdr.next_proto_id = 0xFF };
struct rte_flow_item_udp udp_spec = { .hdr.dst_port = htons(5005) };
struct rte_flow_item_udp udp_mask = { .hdr.dst_port = 0xFFFF };
struct rte_flow_item items[4] = {
{ .type = RTE_FLOW_ITEM_TYPE_ETH, .spec = ð_spec, .mask = ð_mask, .last = NULL },
{ .type = RTE_FLOW_ITEM_TYPE_IPV4, .spec = &ipv4_spec, .mask = &ipv4_mask, .last = NULL },
{ .type = RTE_FLOW_ITEM_TYPE_UDP, .spec = &udp_spec, .mask = &udp_mask, .last = NULL },
{ .type = RTE_FLOW_ITEM_TYPE_END, .spec = NULL, .mask = NULL, .last = NULL },
};
/* 3. 定义动作:转发到队列 2 */
struct rte_flow_action_queue queue_conf = { .index = 2 };
struct rte_flow_action actions[2] = {
{ .type = RTE_FLOW_ACTION_TYPE_QUEUE, .conf = &queue_conf },
{ .type = RTE_FLOW_ACTION_TYPE_END, .conf = NULL },
};
/* 4. 创建规则 */
struct rte_flow_error error;
struct rte_flow *flow = rte_flow_create(port_id, &attr, items, actions, &error);
if (!flow) {
printf("规则创建失败: %s\n", error.message);
}
调试经验:创建规则失败时,优先检查
rte_flow_error 结构体中的 message 字段。常见错误包括:ENOTSUP(PMD 不支持该类型)、EINVAL(参数无效)、ENOMEM(硬件流表满)。建议在开发阶段开启 PMD 的调试日志(--log-level=pmd,debug)以获取更详细的错误信息。
2.5 结构体使用避坑清单
- 掩码必须显式设置:即使要匹配完整字段,也必须提供 mask。不设置 mask 可能导致匹配行为未定义。
- 字节序转换:网络字节序字段(如 MAC 地址、IP 地址、端口号)必须使用
htons()/htonl()转换。 - item 顺序不可颠倒:必须按 L2 → L3 → L4 的顺序排列,否则 PMD 拒绝创建。
- action 链顺序敏感:某些硬件要求 QUEUE 动作必须在 MARK 之前,具体参考 PMD 文档。
- 保留位必须清零:
rte_flow_attr.reserved必须为 0,否则 PMD 可能返回EINVAL。 - END 类型不可省略:items 和 actions 数组必须以对应的 END 类型结尾,否则
rte_flow_create()会遍历越界。
性能提示:在数据面频繁创建/删除规则时,建议预分配
rte_flow_item 和 rte_flow_action 数组,避免在热路径上动态分配内存。对于硬件卸载场景,规则创建后应缓存 struct rte_flow * 指针,销毁时通过 rte_flow_destroy() 释放硬件资源。