3、RoCEv2协议栈解析:以太网、IP、UDP、BTH、RETH、原子操作等报文结构逐层拆解
RoCEv2(RDMA over Converged Ethernet version 2)是目前高性能计算与分布式存储网络中应用最广泛的RDMA承载协议。与RoCEv1直接在以太网二层(EtherType 0x8915)上承载InfiniBand报文不同,RoCEv2将IB报文封装进UDP/IPv4或UDP/IPv6报文之中,从而实现了跨三层路由的传输能力。这一设计使得RoCEv2能够适应大规模数据中心Spine-Leaf组网,但也带来了更复杂的报文解析需求。本章将从实际抓包出发,逐层拆解RoCEv2报文从以太网帧头到IB传输层(BTH)再到RDMA操作报文(RETH、原子操作等)的完整结构,帮助读者建立从字节流到语义的映射能力。
上图展示了RoCEv2报文在Wireshark中的协议栈分层视图。与普通TCP/IP报文相比,RoCEv2的关键差异在于:UDP目的端口固定为4791(IANA分配的RoCEv2端口),且UDP校验和必须为有效值(不可设置为0)。从抓包分析的角度,我们需要依次剥离以太网头、IP头、UDP头,然后进入IB协议栈的BTH(基础传输头)解析,最后根据OpCode字段判断后续是否跟随RETH(RDMA扩展传输头)、原子操作头或仅包含纯Payload。下面将逐层展开详细拆解。
3.1 以太网与IP层:识别RoCEv2流量的入口
核心原理:RoCEv2报文在以太网层的EtherType字段通常为0x0800(IPv4)或0x86DD(IPv6),这与普通UDP流量完全一致。因此,仅凭以太网头无法区分RoCEv2与普通UDP流量,必须深入解析UDP目的端口。在IP层,RoCEv2使用普通IP路由机制,支持DSCP(差分服务代码点)标记以实现优先级调度。值得注意的是,RoCEv2报文通常不允许IP分片——因为RDMA要求报文原子性,分片会导致接收端无法重组完整的RDMA消息。
判断方法:在Wireshark中,使用过滤表达式 udp.port == 4791 可以快速筛选出所有RoCEv2报文。若需要进一步区分读写操作,可结合 ib.bth.opcode 字段过滤。对于IPv6环境,过滤条件相同,但需注意IPv6扩展头可能影响报文偏移量计算。
| 字段 | 长度(字节) | 典型值/说明 | Wireshark显示 |
|---|---|---|---|
| 目的MAC | 6 | 接收方网卡MAC | eth.dst |
| 源MAC | 6 | 发送方网卡MAC | eth.src |
| EtherType | 2 | 0x0800 (IPv4) | eth.type |
| IP协议 | 1 | 17 (UDP) | ip.proto |
| DSCP | 1 | 通常为46 (EF) 或 26 (AF31) | ip.dsfield.dscp |
3.2 UDP层:RoCEv2的“伪装”与校验和强制要求
核心原理:RoCEv2选择UDP作为L4承载,主要目的是利用UDP端口号实现多租户隔离,并借助UDP校验和保证数据完整性。与普通UDP不同,RoCEv2要求UDP校验和必须计算且有效——这是RoCEv2规范(IBTA 1.2.1)的强制要求。若校验和错误,接收端网卡将直接丢弃报文,且不会产生ICMP错误消息。
操作流程:在Wireshark中,可通过 udp.checksum.status == 0 过滤校验和错误的报文。对于RoCEv2分析,建议在Wireshark首选项中启用“Validate UDP checksums if possible”,以便快速识别被网卡或交换机损坏的报文。
# 常用Wireshark过滤表达式
udp.port == 4791 # 所有RoCEv2报文
udp.port == 4791 && ip.dst == 192.168.1.10 # 发往特定IP的RoCEv2
udp.checksum.status == 0 # 校验和错误的报文(需开启校验验证)
ethtool -K eth0 rx on 关闭卸载以获取真实校验和。3.3 BTH(基础传输头):RDMA操作的核心语义
核心原理:BTH是每个RoCEv2报文必须包含的IB传输头,长度为12字节。它承载了OpCode(操作码)、目的QP号(Queue Pair)、PSN(包序列号)等关键信息。OpCode决定了后续报文类型:是发送(Send)、写(Write)、读(Read)还是原子操作。BTH中的PKey用于分区隔离,类似于VLAN在以太网中的作用。
关键字段拆解:
- OpCode(8位):高4位表示操作类型(0x00=Send,0x04=Write,0x08=Read,0x0C=Atomic),低4位表示是否首包/中间包/尾包。
- DestQP(24位):目的队列对编号,用于标识接收端的QP上下文。
- PSN(24位):包序列号,用于乱序检测和重传判断。
- PKey(16位):分区键,必须与接收端QP配置的PKey匹配。
| OpCode值 | 操作类型 | 后续头 | 典型场景 |
|---|---|---|---|
| 0x00 | Send First | RETH (可选) | SEND操作首包 |
| 0x04 | Write First | RETH | RDMA Write首包 |
| 0x08 | Read Request | RETH | RDMA Read请求 |
| 0x0C | Atomic Compare&Swap | 原子操作头 | 原子比较交换 |
| 0x11 | Atomic Fetch&Add | 原子操作头 | 原子取加 |
InfiniBand 协议树即可看到BTH各字段。重点关注 ib.bth.opcode 和 ib.bth.dest_qp。若OpCode显示为“Reserved”,说明报文被错误解析或存在非标准实现。3.4 RETH与原子操作头:RDMA操作的地址与长度信息
核心原理:RETH(RDMA Extended Transport Header)用于RDMA Read/Write操作,包含虚拟地址(VA)、RKey(远程内存访问密钥)和DMA长度。RKey是保护机制的核心——接收端网卡通过RKey验证发送方是否有权访问指定内存区域。原子操作头则包含VA、RKey以及操作数(Compare&Swap的旧值/新值,或Fetch&Add的加数)。
案例拆解:假设某分布式存储节点通过RDMA Write将4KB数据写入对端内存。抓包显示:
InfiniBand
BTH
OpCode: Write First (0x04)
DestQP: 0x001234
PSN: 0x000001
RETH
Virtual Address: 0x00000000_7fff0000
RKey: 0x12345678
DMA Length: 4096
分析过程:该报文是RDMA Write的首包,目标QP为0x001234,写入地址为0x7fff0000,长度4096字节。若后续还有中间包(OpCode=0x05)和尾包(OpCode=0x06),则总长度可能超过单包承载能力(通常为MTU-40字节)。
3.5 完整报文解析实战:从字节流到语义
操作流程:以下是一个完整的RoCEv2 Read Request报文解析步骤:
- 确认以太网EtherType=0x0800,IP协议=17,UDP目的端口=4791。
- 展开UDP头,记录源端口(动态分配)和UDP长度。
- 进入InfiniBand协议树,读取BTH中的OpCode=0x08(Read Request)。
- 解析RETH中的VA、RKey和DMA长度,确认请求的远程内存范围。
- 检查PSN连续性,判断是否存在丢包或乱序。
案例闭环:某AI训练集群出现训练速度下降,抓包发现大量重传。分析过程:
- 初始现象:训练任务吞吐量从120Gbps降至80Gbps,GPU利用率波动。
- 关键数据:Wireshark统计显示重传率3.2%,PSN跳变频繁。
- 分析判断:过滤
tcp.analysis.retransmission不适用(RoCEv2无TCP),改用ib.bth.psn排序发现PSN间隙。 - 实际操作:使用
ib.bth.psn作为Y轴绘制IO图,发现周期性PSN回退。 - 结果对比:定位为交换机ECMP哈希不均导致单链路拥塞,调整哈希算法后重传率降至0.1%。
- 复盘经验:RoCEv2排障必须关注PSN连续性,而非TCP序列号。
InfiniBand 解析器,并保存常用过滤表达式为按钮,提升日常分析效率。