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,
};
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), /* 精确匹配所有位 */
};
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, /* 精确匹配协议 */
};
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, /* 不匹配源端口 */
};
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 = ð_spec,
.mask = ð_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,
},
};
调试清单(逐项检查):
- 确认所有
spec和mask指针非NULL。 - 确认Eth的
type与后续Item匹配(有VLAN则设0x8100,无VLAN则设IP类型)。 - 确认所有多字节字段使用
rte_cpu_to_be_16/32()转换。 - 确认Pattern数组以
RTE_FLOW_ITEM_TYPE_END结尾。 - 确认硬件支持该Pattern组合(如某些网卡不支持VLAN+IPv6+UDP同时匹配)。
专家建议:在开发阶段,使用
rte_flow_validate()而不是rte_flow_create()来测试Pattern。验证失败时,检查返回的rte_flow_error中的type和message字段,它们会精确指示是哪个Item或字段出错。