3. 匹配项(Pattern)编写:以太网头匹配、VLAN匹配、IP头匹配、TCP/UDP端口匹配

在DPDK流分类(RTE Flow)编程中,匹配项(Pattern)是定义数据包识别规则的核心。一个Pattern由多个连续的匹配层(Item)组成,从L2(以太网)到L4(传输层)逐层指定。编写Pattern时,任何层级的字段偏移、掩码设置或字节序错误,都会导致规则匹配失败或性能骤降。本节将逐一拆解以太网、VLAN、IP、TCP/UDP端口的匹配编写要点与常见陷阱。

核心原则:Pattern中的Item必须按照从外到内的协议栈顺序排列(Eth → VLAN → IP → L4),且每个Item的spec和mask字段必须对齐。DPDK不会自动补全缺失的协议层。

3.1 以太网头匹配

以太网头匹配是最基础的L2匹配,通常用于过滤特定源/目的MAC地址或EtherType。在RTE Flow中,使用rte_flow_item_eth结构体。

字段 类型 说明 常见错误
dst uint8_t[6] 目的MAC地址 字节序错误(MAC地址按网络序,但数组直接赋值时需注意)
src uint8_t[6] 源MAC地址 掩码设置不当导致广播/多播误匹配
type uint16_t EtherType(如0x0800 for IPv4) 未使用rte_cpu_to_be_16()转换
调试经验:当Pattern中同时包含Eth和VLAN时,Eth的type字段应设置为0x8100(VLAN标签),而不是直接写IP类型。否则硬件会因协议层不匹配而丢弃规则。
/* 正确示例:匹配目的MAC为 00:11:22:33:44:55 的以太网帧 */
struct rte_flow_item_eth eth_spec = {
    .dst = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
    .src = {0},
    .type = 0,
};
struct rte_flow_item_eth eth_mask = {
    .dst = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
    .src = {0},
    .type = 0,
};
以太网头匹配项结构 目的MAC 6字节 源MAC 6字节 EtherType 2字节 掩码控制 全FF=精确匹配 全00=忽略

3.2 VLAN匹配

VLAN匹配用于识别802.1Q标签。DPDK使用rte_flow_item_vlan结构体,包含tpid(标签协议标识,通常为0x8100)、tci(标签控制信息,含PCP、DEI、VID)。

  • VID范围:0-4095,掩码可精确到bit级别。
  • PCP优先级:3bit,常与QoS配合。
  • DEI标志:1bit,用于丢弃指示。
常见陷阱:很多网卡(如Intel XL710)要求VLAN匹配必须与以太网头的EtherType=0x8100配合使用。如果Pattern中写了VLAN Item但Eth的type未设置0x8100,驱动会返回rte_flow_error类型为RTE_FLOW_ERROR_TYPE_ITEM的错误。
/* 匹配VLAN ID = 100,PCP = 3 */
struct rte_flow_item_vlan vlan_spec = {
    .tpid = rte_cpu_to_be_16(0x8100),
    .tci = rte_cpu_to_be_16((3 << 13) | 100),  /* PCP=3, VID=100 */
};
struct rte_flow_item_vlan vlan_mask = {
    .tpid = rte_cpu_to_be_16(0xffff),
    .tci = rte_cpu_to_be_16(0xffff),  /* 精确匹配所有位 */
};
VLAN TCI 字段位分解 PCP 3bit DEI 1bit VID (VLAN ID) 12bit 掩码可精确到bit 如只匹配VID: 0xfff PCP/DEI掩码置0

3.3 IP头匹配

IP头匹配分为IPv4和IPv6,分别使用rte_flow_item_ipv4和rte_flow_item_ipv6。常用匹配字段包括源/目的IP地址、协议号(Protocol)、TOS/DSCP。

字段 IPv4 IPv6 注意事项
源地址 hdr.src_addr hdr.src_addr (128bit) IPv6需使用memcmp风格掩码
目的地址 hdr.dst_addr hdr.dst_addr 注意网络字节序
协议号 hdr.next_proto_id hdr.proto IPv4中该字段在hdr内
TOS/TC hdr.type_of_service hdr.vtc_flow (部分) IPv6 Traffic Class需移位提取
性能优化:如果只需要匹配目的IP前缀(如/24),不要使用全量掩码。设置mask.dst_addr为0xffffff00(网络序)可减少TCAM条目占用,提升转发性能。
/* 匹配目的IP 192.168.1.0/24,协议为TCP */
struct rte_flow_item_ipv4 ip_spec = {
    .hdr.dst_addr = rte_cpu_to_be_32(0xc0a80100),  /* 192.168.1.0 */
    .hdr.next_proto_id = 6,  /* TCP */
};
struct rte_flow_item_ipv4 ip_mask = {
    .hdr.dst_addr = rte_cpu_to_be_32(0xffffff00),  /* /24掩码 */
    .hdr.next_proto_id = 0xff,  /* 精确匹配协议 */
};
IPv4头匹配关键字段 源IP地址 32bit 目的IP地址 32bit 协议 8bit TOS 8bit 掩码示例:目的IP /24 → mask.dst_addr = 0xffffff00 (网络序) 协议号精确匹配 → mask.next_proto_id = 0xff

3.4 TCP/UDP端口匹配

L4端口匹配使用rte_flow_item_tcp或rte_flow_item_udp结构体。核心字段为源端口(hdr.src_port)和目的端口(hdr.dst_port)。

  • 端口范围匹配:DPDK原生不支持直接写端口范围(如1024-65535),需要通过掩码或使用RSS扩展实现。
  • 常见误区:TCP和UDP不能同时出现在同一个Pattern中。如果需要匹配任意L4协议,应使用rte_flow_item_any。
调试陷阱:某些硬件(如Mellanox CX5)要求L4端口匹配必须配合IP协议号匹配。如果Pattern中写了TCP端口但IP协议号未设置为6,规则可能被静默忽略。建议始终在IP Item中明确设置next_proto_id。
/* 匹配目的TCP端口 80 */
struct rte_flow_item_tcp tcp_spec = {
    .hdr.dst_port = rte_cpu_to_be_16(80),
};
struct rte_flow_item_tcp tcp_mask = {
    .hdr.dst_port = rte_cpu_to_be_16(0xffff),
    .hdr.src_port = 0,  /* 不匹配源端口 */
};
L4端口匹配流程 IP协议号检查 端口字段提取 掩码比较 注意:TCP/UDP端口必须与IP协议号联动,否则硬件可能误匹配 端口范围匹配需通过多规则或RSS扩展实现

3.5 完整Pattern示例与调试清单

以下是一个完整的四层匹配Pattern,用于匹配“来自VLAN 100、目的IP 10.0.0.0/24、目的TCP端口443”的流量:

struct rte_flow_item pattern[] = {
    /* 1. 以太网头 - 必须指定EtherType为VLAN */
    [0] = {
        .type = RTE_FLOW_ITEM_TYPE_ETH,
        .spec = &eth_spec,
        .mask = &eth_mask,
    },
    /* 2. VLAN头 */
    [1] = {
        .type = RTE_FLOW_ITEM_TYPE_VLAN,
        .spec = &vlan_spec,
        .mask = &vlan_mask,
    },
    /* 3. IPv4头 */
    [2] = {
        .type = RTE_FLOW_ITEM_TYPE_IPV4,
        .spec = &ip_spec,
        .mask = &ip_mask,
    },
    /* 4. TCP头 */
    [3] = {
        .type = RTE_FLOW_ITEM_TYPE_TCP,
        .spec = &tcp_spec,
        .mask = &tcp_mask,
    },
    /* 5. 结束标记 */
    [4] = {
        .type = RTE_FLOW_ITEM_TYPE_END,
    },
};
调试清单(逐项检查):
  1. 确认所有spec和mask指针非NULL。
  2. 确认Eth的type与后续Item匹配(有VLAN则设0x8100,无VLAN则设IP类型)。
  3. 确认所有多字节字段使用rte_cpu_to_be_16/32()转换。
  4. 确认Pattern数组以RTE_FLOW_ITEM_TYPE_END结尾。
  5. 确认硬件支持该Pattern组合(如某些网卡不支持VLAN+IPv6+UDP同时匹配)。
Pattern协议栈层级(L2→L4) L2: 以太网头 (Eth) L2.5: VLAN (可选) L3: IPv4 / IPv6 L4: TCP / UDP
专家建议:在开发阶段,使用rte_flow_validate()而不是rte_flow_create()来测试Pattern。验证失败时,检查返回的rte_flow_error中的type和message字段,它们会精确指示是哪个Item或字段出错。