1. 架构深度剖析
理解 v2 (现有代码) 与 v1 (目标代码) 在协议栈层面的本质区别。
现状: ns3-rdma (v2)
基于 IP 路由
L4 Trans
UDP Header (Port 4791)
L3 Network
IP Header (ECN, DSCP)
L2 Link
Ethernet (Type 0x0800 IPv4)
问题点: RoCE v1 不应该有 UDP 和 IP 头部。现有代码依赖 IP 头中的 DSCP 做流控,依赖 ECN 做拥塞控制。
目标: RoCE v1 改造版
纯以太网
Custom
IB BTH Header
Custom
IB GRH Header
L2 Link
Ethernet (Type 0x8915)
改造核心: 移除 IP/UDP 层,手动构建 GRH/BTH,直接操作 MAC 层。
2. 改造实战指南 (大致方案,未校验)
从应用层到物理层的具体 C++ 代码修改步骤。
1
重构 Socket 通信机制
RoCE v1 不使用 IP 寻址,因此必须放弃 UdpSocketFactory,转而使用 ns-3 提供的 PacketSocketFactory 直接操作链路层。
原始代码 (v2 - udp-echo-client.cc)
// 典型的 UDP Socket
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
m_socket = Socket::CreateSocket (GetNode (), tid);
m_socket->Bind ();
// 使用 IP 地址连接
m_socket->Connect (InetSocketAddress (m_peerAddress, m_peerPort));
修改后 (v1 - roce-client.cc)
#include "ns3/packet-socket-factory.h"
// 1. 切换到 PacketSocket
TypeId tid = TypeId::LookupByName ("ns3::PacketSocketFactory");
m_socket = Socket::CreateSocket (GetNode (), tid);
// 2. 准备物理地址与协议类型
PacketSocketAddress socketAddr;
socketAddr.SetSingleDevice (m_netDevice->GetIfIndex());
socketAddr.SetPhysicalAddress (m_destMacAddress); // 必须预先获知 MAC
socketAddr.SetProtocol (0x8915); // 关键:RoCE v1 EtherType
// 3. 绑定
m_socket->Bind (socketAddr);
m_socket->Connect (socketAddr);
2
实现 RoCE 专用头部 (GRH & BTH)
由于移除了 UDP/IP,我们需要手动实现 ns3::Header 子类来构建物理线路上的字节流。
src/internet/model/roce-grh-header.cc
class RoceGrhHeader : public Header {
// ... 成员变量定义 ...
uint32_t m_flowLabel; // 20 bits
uint8_t m_tclass; // 8 bits (重要:用于 QoS/PFC 映射)
Ipv6Address m_sGid; // 128 bits GID
Ipv6Address m_dGid;
// 序列化逻辑:必须符合 Big Endian 网络序
void Serialize (Buffer::Iterator start) const override {
Buffer::Iterator i = start;
// 对应 IPv6 头部的第一个 32位字:Version(6) + TClass + FlowLabel
// 注意位操作拼接
uint32_t vtf = (6 << 28) | (m_tclass << 20) | (m_flowLabel & 0xFFFFF);
i.WriteHtonU32 (vtf);
i.WriteHtonU16 (m_payloadLen);
i.WriteU8 (m_nextHeader); // 通常指向 0x1B (BTH)
i.WriteU8 (m_hopLimit);
// 写入 128位 GID
WriteTo (i, m_sGid);
WriteTo (i, m_dGid);
}
};
3
修改交换机与流控逻辑
注意: v1 极其依赖 PFC (Priority Flow Control)。必须确保交换机不再查看 IP 头的 DSCP 字段,而是查看 GRH 中的 Traffic Class。
- 禁用 ECN: 在
broadcom-node.cc中,移除或注释掉所有涉及PeekHeader<Ipv4Header>的代码。v1 包里没有 IP 头,强行读取会导致错误。 - 流控映射: 修改
qbb-net-device.cc的入队逻辑。使用Packet::PeekHeader<RoceGrhHeader>读取 TClass,将其映射到硬件优先级队列,从而触发正确的 PAUSE 帧。
3. 协议内容与仿真代码映射表
速查表:RoCE v1 协议规范中的字段在 C++ 代码中是如何实现的。
| 协议层级 | 协议字段 (RoCE v1) | ns-3 代码实现 (C++) | 作用说明 |
|---|---|---|---|
| 链路层 (L2) | EtherType | SetProtocol(0x8915) | 核心标识。指示 NetDevice 封装帧时写入 0x8915。 |
| 链路层 (L2) | Dest MAC | SetPhysicalAddress(mac) | 替代 ARP,直接指定物理地址。 |
| 网络层 (GRH) | Traffic Class | RoceGrhHeader::m_tclass | 流控关键。决定进入交换机的哪个优先级队列,触发 PFC。 |
| 网络层 (GRH) | GID | Ipv6Address 类型 | 仅用于应用层连接验证,不用于路由。 |
| 传输层 (BTH) | Dest QP | RoceBthHeader::m_destQp | 接收端根据此 ID 将数据送入正确的内存区域。 |
| 流控机制 | PAUSE Frame | qbb-net-device::SendPfc() | 802.1Qbb 标准实现,发送 EtherType 0x8808 控制帧。 |