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 定义匹配后的处理动作。三者通过指针数组串联,形成一条完整的转发规则。
rte_flow 规则三段式结构 rte_flow_attr ingress / egress transfer / reserved priority group (规则作用域) rte_flow_item[] item[0]: ETH item[1]: IPV4 item[2]: UDP item[3]: END (协议栈匹配链) rte_flow_action[] action[0]: QUEUE action[1]: MARK action[2]: COUNT action[3]: END (处理动作链) 规则创建时,attr / items / actions 三者缺一不可;items 和 actions 必须以 END 类型终止

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 / 1transfer=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 中哪些位参与匹配 */
};
rte_flow_item 匹配链与掩码机制 ETH IPV4 UDP END mask 决定 spec 中哪些位参与匹配:mask 中为 1 的位,spec 对应位必须匹配;mask 中为 0 的位,spec 对应位被忽略 spec.dst_addr = 192.168.1.0 mask.dst_addr = 255.255.255.0 匹配结果:192.168.1.x 网段 (仅匹配前 24 位)

2.2.1 常用 item 类型与配置

item 类型对应结构体关键字段典型用途
RTE_FLOW_ITEM_TYPE_ETHrte_flow_item_ethdst / src / type匹配源/目的 MAC 地址
RTE_FLOW_ITEM_TYPE_IPV4rte_flow_item_ipv4hdr.dst_addr / src_addr / next_proto_id匹配 IP 五元组中的源/目的 IP
RTE_FLOW_ITEM_TYPE_UDPrte_flow_item_udphdr.dst_port / src_port匹配 UDP 端口号
RTE_FLOW_ITEM_TYPE_TCPrte_flow_item_tcphdr.dst_port / src_port匹配 TCP 端口号
RTE_FLOW_ITEM_TYPE_VLANrte_flow_item_vlantpid / 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_QUEUErte_flow_action_queue将报文转发到指定硬件队列RSS 分流、队列绑定
RTE_FLOW_ACTION_TYPE_MARKrte_flow_action_mark给报文打上 32 位标记后续软件处理识别
RTE_FLOW_ACTION_TYPE_COUNTrte_flow_action_count统计匹配报文数量流量监控、计费
RTE_FLOW_ACTION_TYPE_RSSrte_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;
rte_flow_action 动作链执行流程 报文匹配 (硬件/软件) QUEUE 队列 3 MARK 标记 100 END 动作链结束 动作按数组顺序依次执行:先 QUEUE 指定队列,再 MARK 打标记,最后 END 终止

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() 释放硬件资源。