4. 动作(Action)配置:QUEUE动作、RSS动作、DROP动作、MARK动作、JUMP动作

在DPDK流分类(rte_flow)中,动作(Action)定义了匹配到特定规则的报文应当如何处理。动作配置的正确性直接决定了转发行为的成败。本章深入剖析五种核心动作:QUEUE、RSS、DROP、MARK、JUMP,涵盖其数据结构、配置要点、常见错误及调试方法。

核心原则:每个流规则必须包含一个终止动作(Terminating Action),如 QUEUE、DROP、RSS,或通过 JUMP 跳转到其他规则表。遗漏终止动作会导致规则被硬件拒绝或行为未定义。

4.1 动作配置全景图

下图展示了报文从匹配到执行动作的完整路径,以及各动作在转发流程中的位置关系:

DPDK rte_flow 动作执行流程 rte_flow 动作执行流程 入站报文 模式匹配(Pattern Matching) 动作决策(Action Decision) QUEUE RSS DROP JUMP 跳转到其他规则表 指定队列接收 多队列负载均衡 报文丢弃

4.2 QUEUE 动作:定向转发

QUEUE 动作将匹配的报文直接送入指定的硬件队列(RX Queue),是最基础、最常用的转发动作。适用于流量分类后由特定核心处理。

4.2.1 数据结构与配置

struct rte_flow_action_queue {
    uint16_t index;  /* 目标硬件队列索引 */
};

配置示例:将匹配的报文送入队列 3:

struct rte_flow_action actions[2];
struct rte_flow_action_queue queue_conf = { .index = 3 };

actions[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
actions[0].conf = &queue_conf;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;  /* 终止标记 */

4.2.2 常见问题与调试

问题现象可能原因调试方法
规则创建成功,但报文未进入指定队列队列索引超出硬件支持范围检查 rte_eth_dev_info_get() 的 nb_rx_queues 字段
报文进入错误队列队列索引与 RSS 配置冲突确认该队列未被 RSS 配置占用
规则创建失败(ENOSPC)硬件动作资源耗尽减少规则数量或使用 JUMP 分组
避坑提示:QUEUE 动作的队列索引必须小于实际配置的 RX 队列数。例如,若仅配置了 4 个队列(索引 0-3),设置 index=4 会导致规则创建失败。建议在创建规则前通过 rte_eth_dev_info_get() 验证队列容量。

4.3 RSS 动作:多队列负载均衡

RSS(Receive Side Scaling)动作将匹配的报文流分散到一组队列中,实现多核并行处理。与 QUEUE 动作的“固定队列”不同,RSS 基于哈希计算动态选择队列。

4.3.1 数据结构与配置

struct rte_flow_action_rss {
    enum rte_eth_hash_function   func;    /* 哈希函数 */
    uint64_t                     types;   /* 哈希字段类型 */
    uint32_t                     level;   /* 隧道层级 */
    uint32_t                     key_len; /* 哈希密钥长度 */
    const uint8_t               *key;     /* 哈希密钥 */
    uint32_t                     queue_num; /* 目标队列数 */
    const uint16_t              *queue;    /* 队列索引数组 */
};

配置示例:将流量分散到队列 2、3、4、5:

struct rte_flow_action_rss rss_conf = {
    .func   = RTE_ETH_HASH_FUNCTION_DEFAULT,
    .types  = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP,
    .level  = 0,
    .key_len = 40,
    .key    = NULL,  /* 使用默认密钥 */
    .queue_num = 4,
    .queue  = (uint16_t[]){2, 3, 4, 5},
};

struct rte_flow_action actions[2];
actions[0].type = RTE_FLOW_ACTION_TYPE_RSS;
actions[0].conf = &rss_conf;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;

4.3.2 常见问题与调试

问题现象可能原因调试方法
RSS 规则创建失败哈希类型(types)不被硬件支持使用 rte_flow_validate() 预校验;查阅网卡 PMD 文档
流量分布不均匀哈希密钥(key)设置不当使用随机密钥或硬件默认密钥
隧道报文未正确哈希level 参数未设置或设置错误外层哈希设 level=0,内层设 level=1
性能要点:RSS 动作的 queue 数组应尽量连续,且数量为 2 的幂次(如 2、4、8),以最大化哈希均匀性。非 2 的幂次队列数可能导致部分队列负载偏高。

4.4 DROP 动作:报文丢弃

DROP 动作直接丢弃匹配的报文,常用于安全过滤、黑名单、无效流量抑制等场景。该动作无需额外配置参数。

4.4.1 配置示例

struct rte_flow_action actions[2];
actions[0].type = RTE_FLOW_ACTION_TYPE_DROP;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;

4.4.2 常见问题与调试

问题现象可能原因调试方法
DROP 规则未生效,报文仍被接收规则优先级低于其他匹配规则检查规则创建顺序或显式设置优先级
误丢弃正常流量模式匹配过于宽泛使用 rte_flow_query() 统计匹配计数
性能下降大量 DROP 规则导致硬件 TCAM 满载合并规则或使用 JUMP 分组
避坑提示:DROP 动作是终止动作,不能与其他转发动作(QUEUE/RSS)共存于同一条规则中。若需要“丢弃并计数”,应使用 MARK + DROP 组合,或通过 rte_flow_query() 获取硬件统计。

4.5 MARK 动作:报文标记

MARK 动作在报文的元数据(mbuf 结构)中设置一个整数标记,不修改报文内容。标记值可在后续软件处理中通过 mbuf->hash.fdir.hi 或 rte_flow_mark 获取。

4.5.1 数据结构与配置

struct rte_flow_action_mark {
    uint32_t id;  /* 标记值,范围由硬件决定 */
};

配置示例:为匹配的报文设置标记值 100:

struct rte_flow_action_mark mark_conf = { .id = 100 };
struct rte_flow_action actions[3];

actions[0].type = RTE_FLOW_ACTION_TYPE_MARK;
actions[0].conf = &mark_conf;
actions[1].type = RTE_FLOW_ACTION_TYPE_QUEUE;
actions[1].conf = &(struct rte_flow_action_queue){ .index = 1 };
actions[2].type = RTE_FLOW_ACTION_TYPE_END;

4.5.2 常见问题与调试

问题现象可能原因调试方法
标记值在 mbuf 中未出现未使能 MARK 相关元数据设置 rte_eth_rxmode.offloads |= RTE_ETH_RX_OFFLOAD_FLOW_MARK
标记值被覆盖多条规则命中同一报文检查规则优先级,确保高优先级规则先处理
标记值范围错误id 超出硬件支持范围查阅 PMD 文档,通常为 0-0xFFFF
调试技巧:在接收路径中打印 mbuf->hash.fdir.hi 确认标记值。注意:只有使能 RTE_ETH_RX_OFFLOAD_FLOW_MARK 后,该字段才有效。

4.6 JUMP 动作:规则表跳转

JUMP 动作将匹配的报文跳转到指定的规则表(Group),实现多级流水线处理。这是构建复杂转发逻辑的关键动作,可有效减少单表规则数量。

4.6.1 数据结构与配置

struct rte_flow_action_jump {
    uint32_t group;  /* 目标规则表 ID */
};

配置示例:从组 0 跳转到组 1:

/* 组 0 规则:匹配后跳转到组 1 */
struct rte_flow_action_jump jump_conf = { .group = 1 };
struct rte_flow_action actions[2];
actions[0].type = RTE_FLOW_ACTION_TYPE_JUMP;
actions[0].conf = &jump_conf;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;

/* 组 1 规则:匹配后转发到队列 */
struct rte_flow_action actions_g1[2];
actions_g1[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
actions_g1[0].conf = &(struct rte_flow_action_queue){ .index = 5 };
actions_g1[1].type = RTE_FLOW_ACTION_TYPE_END;

/* 创建规则时指定组 ID */
struct rte_flow_attr attr_g0 = { .group = 0, .priority = 0 };
struct rte_flow_attr attr_g1 = { .group = 1, .priority = 0 };

4.6.2 常见问题与调试

问题现象可能原因调试方法
JUMP 后规则未匹配目标组不存在或无匹配规则确认目标组已创建规则;使用 rte_flow_validate() 检查
无限循环跳转组间跳转形成环路设计时确保无环;使用 rte_flow_isolate() 隔离测试
性能下降跳转层级过深限制跳转深度 ≤ 4 层;优先使用单表
避坑提示:JUMP 动作不能跳转到当前组或已访问过的组,否则可能形成死循环。硬件通常有最大跳转深度限制(如 4 层),超出会导致规则创建失败。建议使用 rte_flow_validate() 预校验所有跳转路径。

4.7 动作组合与优先级规则

实际应用中,动作常组合使用。下表总结了合法组合及注意事项:

动作组合是否合法说明
MARK + QUEUE合法先标记,再转发到指定队列
MARK + RSS合法标记后参与 RSS 负载均衡
MARK + DROP合法标记后丢弃,用于审计
QUEUE + RSS非法不能同时指定固定队列和 RSS
JUMP + QUEUE非法JUMP 是终止动作,不能与其他转发动作共存
DROP + QUEUE非法丢弃和转发互斥
最佳实践:复杂转发场景建议采用“MARK + JUMP”模式:先在入口组标记报文类型,再跳转到不同处理组执行具体动作。这种方式既保持了规则表的简洁性,又便于调试和扩展。

4.8 动作配置调试清单

当动作配置出现问题时,按以下清单逐项排查:

  1. 终止动作检查:确认规则以 RTE_FLOW_ACTION_TYPE_END 结尾。
  2. 资源容量检查:使用 rte_flow_info_get() 获取硬件动作资源上限。
  3. 参数范围检查:队列索引、标记值、组 ID 是否在硬件支持范围内。
  4. 组合合法性检查:参考上表确认动作组合是否合法。
  5. 优先级检查:高优先级规则是否意外覆盖了低优先级规则的动作。
  6. 硬件 offload 检查:确认 rte_eth_dev_configure() 中使能了必要的 RX offload(如 RTE_ETH_RX_OFFLOAD_FLOW_MARK)。
  7. 日志检查:启用 --log-level=pmd,8 查看 PMD 驱动日志,定位硬件拒绝原因。

掌握这五种动作的配置要点和调试方法,是构建稳定、高性能 DPDK 转发应用的基础。在实际项目中,建议从简单的 QUEUE/DROP 开始验证,逐步引入 RSS、MARK 和 JUMP 构建复杂流水线。