3、RoCE协议栈:以太网、IP、UDP、RoCE头部封装格式

3.1 核心原理:RoCE的“分层封装”哲学

RoCE(RDMA over Converged Ethernet)的本质,是将InfiniBand(IB)的RDMA语义移植到以太网物理链路上。理解RoCE协议栈的关键,在于认清它并非“另起炉灶”,而是对现有以太网/IP/UDP协议栈的“有选择复用”与“语义扩展”。从数据包封装视角看,RoCE v2的报文自外向内依次为:以太网头(L2)→ IP头(L3)→ UDP头(L4)→ RoCE头部 → IB传输层头部 → 有效载荷。每一层都承担着特定的寻址、路由或可靠性职责,而RoCE头部则是承载RDMA操作语义(如发送、读取、写入、原子操作)的核心。

与RoCE v1直接封装在以太网类型字段(0x8915)中不同,RoCE v2引入UDP封装(目的端口4791),使得报文能够穿越三层路由网络,从而突破了RoCE v1仅能在同一二层广播域内通信的限制。这种设计让RoCE v2可以复用现有IP网络的路由、ECMP(等价多路径)和QoS机制,但也带来了新的挑战——例如UDP端口在ECMP中的哈希不均、PFC(优先级流控)与ECN(显式拥塞通知)的协同配置等。

关键认知:RoCE v2的UDP封装并非为了“传输数据”,而是为了“借用端口号”实现多路径负载均衡与网络层路由。目的端口固定为4791,源端口则由网卡驱动根据流特征动态生成,用于ECMP哈希计算。
RoCE v2 协议栈封装结构(自外向内逐层解封装) L2 以太网头 目的MAC (6B) 源MAC (6B) VLAN Tag (4B, 可选) EtherType = 0x0800 (IPv4) 或 0x86DD (IPv6) L3 IP头 版本/首部长度 (1B) DSCP (1B, QoS) 总长度 (2B) 协议 = 17 (UDP) 源IP (4B) 目的IP (4B) L4 UDP头 源端口 (2B, 动态生成) 目的端口 = 4791 (2B) UDP长度 (2B) UDP校验和 (2B) RoCE 头部 (IB BTH) OpCode (1B, 操作码) SE (1b) / M (1b) / Pad (6b) P_Key (2B, 分区键) 目的QP号 (3B) A (1b) / 保留 (7b) IB 传输层头部 + 有效载荷 RETH (16B, RDMA读/写) 原子操作ETH (28B) 数据载荷 (0~4KB) I-CRC (4B, 不变CRC) FECN/BECN (拥塞标记) 封装方向 ↑ 发送端逐层添加 ↓ 接收端逐层剥离 RoCE v2 头部总长:14B(ETH) + 20B(IPv4) + 8B(UDP) + 12B(BTH) = 54B(不含VLAN与CRC)

3.2 方法流程:逐层封装与关键字段解析

在发送端,RoCE网卡驱动将上层RDMA操作(如ibv_post_send提交的WR)转换为IB传输层报文,随后依次添加BTH(Base Transport Header)、RoCE头部、UDP头、IP头和以太网头。接收端则按相反顺序解封装,最终将数据直接写入用户态内存(零拷贝)。整个流程中,UDP源端口的选择是影响网络负载均衡的关键——若所有流都使用相同源端口,ECMP哈希将把所有流量映射到同一条物理链路上,造成严重倾斜。

# 查看RoCE网卡支持的GID索引与UDP端口配置(Mellanox/BlueField)
rdma link show
# 输出示例:
# link mlx5_0/1 state ACTIVE physical_state LINK_UP netdev ens6f0
#     link_layer Ethernet

# 查看当前RoCE模式(v1/v2)
cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/0
# 输出:RoCE v2

# 使用tcpdump抓取RoCE v2报文(过滤UDP 4791端口)
tcpdump -i ens6f0 -s 0 -vv udp port 4791 -c 10

在抓包分析时,重点关注以下字段的对应关系:

协议层关键字段典型值/说明故障排查意义
以太网EtherType0x0800(IPv4)若为0x8915则为RoCE v1,需检查是否混用
IPDSCP通常为46(EF)或26(AF31)用于QoS队列映射,异常时检查PFC配置
UDP目的端口4791(固定)若被ACL或防火墙丢弃,通信直接失败
UDP源端口动态生成(0-65535)源端口固定会导致ECMP哈希不均
RoCE BTHOpCode0x00(SEND)、0x01(RDMA WRITE)等识别操作类型,判断是否与预期一致
RoCE BTHP_Key0xFFFF(默认)P_Key不匹配会导致“分区拒绝”错误
RoCE BTH目的QP号1-0xFFFFFEQP号错误会触发“无效请求”错误

3.3 案例场景:RoCE v2报文抓包深度解析

以下是在两台配置RoCE v2的服务器间执行rping测试时抓取的报文关键字段(已省略部分非关键字节):

# 发送端执行:rping -s -a 192.168.10.1 -v
# 接收端执行:rping -c -a 192.168.10.1 -v

# tcpdump抓包结果(关键行)
14:23:01.123456 IP 192.168.10.1.4791 > 192.168.10.2.4791: UDP, length 48
        0x0000:  000c 29ab 1234 000c 29cd 5678 0800 4510  # 以太网+IP头
        0x0010:  003c 1a2b 4000 3f11 5a6b c0a8 0a01 c0a8  # IP头(TTL=63, UDP协议)
        0x0020:  0a02 12b7 12b7 0028 0000 0000 0000 0000  # UDP头(源/目的端口4791)
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  # RoCE BTH(OpCode=0x00 SEND)
        0x0040:  0000 0000 0000 0000 0000 0000 0000 0000  # 有效载荷(此处为rping控制消息)

从抓包结果可以看到:UDP源端口和目的端口均为4791(rping工具默认使用相同端口),这在测试环境中可以工作,但在生产环境中强烈建议让驱动自动分配源端口。若所有QP都使用固定源端口,在接入层有4条ECMP等价链路时,哈希结果可能将所有流量都映射到同一条链路上,导致单链路拥塞而其他链路空闲。

实操建议:对于Mellanox网卡,可通过mlxconfig工具设置ROCE_MODE=ROCE_V2,并启用ROCE_TRAFFIC_CLASS(通常设为106对应DSCP 46)。同时建议开启UDP_SOURCE_PORT_HASH功能,使网卡基于流特征自动生成UDP源端口,确保ECMP哈希均匀。

3.4 误区与复盘:RoCE封装中的常见陷阱

误区一:认为RoCE v2的UDP校验和可以关闭。虽然UDP校验和在IPv4中是可选的(IPv6中强制要求),但RoCE规范要求UDP校验和必须启用。关闭校验和会导致接收端丢弃所有RoCE报文,且问题难以定位——因为普通UDP通信可能正常,只有RoCE流量异常。

误区二:混淆RoCE v1与v2的EtherType。RoCE v1使用以太网类型0x8915,而RoCE v2使用0x0800(IPv4)。若交换机配置了基于EtherType的ACL或策略,可能只放行了0x8915而阻断了RoCE v2流量。排查时应先确认两端网卡实际使用的RoCE版本。

误区三:忽略P_Key校验。P_Key相当于IB网络中的“VLAN ID”,通信双方的P_Key必须一致。默认值为0xFFFF,但某些虚拟化环境或容器场景可能修改了P_Key,导致“分区拒绝”错误。查看ibv_devinfo输出中的pkey_tbl_len和当前P_Key值可快速定位。

误区四:认为RoCE头部只有BTH。对于RDMA READ和ATOMIC操作,BTH之后还跟随RETH(RDMA Extended Transport Header)或原子操作ETH,这些扩展头部包含远程虚拟地址、R_Key、交换长度等关键信息。若抓包时只关注BTH而忽略扩展头,可能无法理解完整的操作语义。

复盘要点:当RoCE通信异常时,不要急于检查上层应用。先通过tcpdump确认报文是否到达目的端,再逐层检查:①以太网FCS是否正确(物理层);②IP地址与DSCP是否匹配预期;③UDP目的端口是否为4791且校验和正确;④BTH中的P_Key、QP号是否有效;⑤OpCode是否与上层操作一致。按照“物理→网络→传输→RDMA语义”的顺序排查,可快速缩小问题范围。