2026-03-25-202521011013-卢敦航-网络技术前沿三

实验报告

一、实验设备与环境

  • 操作系统:Ubuntu 24.04 (真实的物理机环境,非虚拟机)
Pasted image 20260325112944
  • 网络硬件:物理无线网卡 wlp0s20f3 (IP: 192.168.51.106)
Pasted image 20260325113027 Pasted image 20260325113100 * **RDMA 驱动/软件栈**:内核自带 SoftRoCE (RXE) 驱动,`rdma-core`,`libibverbs-dev`,`perftest` 测试工具包 * **抓包与分析工具**:Wireshark, tcpdump

二、实验目标

  1. 在 Ubuntu 物理机上通过 SoftRoCE 搭建虚拟 RDMA 实验环境,配置虚拟 RTE 网卡。
  2. 基于 libibverbs 库编写 C 语言程序,尝试完成一次单边通信的 RDMA-WRITE 操作。
  3. 使用 Wireshark 抓取 RoCEv2 数据包,并对以太网/UDP/BTH/RETH 头部协议进行简单分析。

三、实验过程

1、环境配置与虚拟网卡挂载:

首先在系统安装了构建环境和 RDMA 工具链。通过 sudo modprobe rdma_rxe 加载内核模块后,使用指令 sudo rdma link add rxe0 type rxe netdev wlp0s20f3 成功将虚拟 RDMA 设备 rxe0 绑定到了物理机的 WiFi 网卡上。通过 ibv_devices 命令验证了设备已成功激活。

sudo apt install -y build-essential cmake libudev-dev libnl-3-dev libnl-route-3-dev \ ninja-build pkg-config valgrind python3-dev cython3 python3-setuptools \ rdma-core perftest ibverbs-utils rdmacm-utils wireshark
Pasted image 20260325113207

2、编写 RDMA-WRITE 测试代码:

编写了 rdma_simple_write.c。代码逻辑中包含了打开设备、分配保护域 (PD)、注册内存区域 (MR) 并赋予读写权限、创建完成队列 (CQ) 和队列对 (QP)。

Pasted image 20260325113307

这里需要记录的ID是1,使用编号为1的wifi无线网络绑定,代码编写如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <infiniband/verbs.h>

int main() {
    struct ibv_device **dev_list = ibv_get_device_list(NULL);
    struct ibv_context *ctx = ibv_open_device(dev_list[0]);
    struct ibv_pd *pd = ibv_alloc_pd(ctx);

    char *src = aligned_alloc(4096, 4096);
    char *dst = aligned_alloc(4096, 4096);
    strcpy(src, "SUCCESS: RDMA Write Packet Captured!");
    struct ibv_mr *mr_src = ibv_reg_mr(pd, src, 4096, IBV_ACCESS_LOCAL_WRITE);
    struct ibv_mr *mr_dst = ibv_reg_mr(pd, dst, 4096, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);

    struct ibv_cq *cq = ibv_create_cq(ctx, 10, NULL, NULL, 0);
    struct ibv_qp_init_attr init_attr = {
        .send_cq = cq, .recv_cq = cq,
        .cap = { .max_send_wr = 10, .max_send_sge = 1 },
        .qp_type = IBV_QPT_RC
    };
    struct ibv_qp *qp = ibv_create_qp(pd, &init_attr);

    // --- 状态转换 ---
    struct ibv_qp_attr attr = { .qp_state = IBV_QPS_INIT, .port_num = 1, .qp_access_flags = IBV_ACCESS_REMOTE_WRITE };
    ibv_modify_qp(qp, &attr, IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS);

    attr.qp_state = IBV_QPS_RTR;
    attr.path_mtu = IBV_MTU_1024;
    attr.dest_qp_num = qp->qp_num; 
    attr.rq_psn = 0;
    attr.max_dest_rd_atomic = 1;
    attr.min_rnr_timer = 12;
    attr.ah_attr.is_global = 1;
    attr.ah_attr.port_num = 1;
    
    // 【重要:修改此处的 Index】 
    // ibv_devinfo -v 中看到的 RoCE v2 索引
    ibv_query_gid(ctx, 1, 1, &attr.ah_attr.grh.dgid); 
    
    ibv_modify_qp(qp, &attr, IBV_QP_STATE | IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_MIN_RNR_TIMER);

    attr.qp_state = IBV_QPS_RTS;
    attr.sq_psn = 0;
    attr.timeout = 14;
    attr.retry_cnt = 7;
    attr.rnr_retry = 7;
    attr.max_rd_atomic = 1;
    ibv_modify_qp(qp, &attr, IBV_QP_STATE | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_SQ_PSN | IBV_QP_MAX_QP_RD_ATOMIC);

    // --- 循环发送,确保 Wireshark 能抓到 ---
    struct ibv_sge sge = { .addr = (uintptr_t)src, .length = 64, .lkey = mr_src->lkey };
    struct ibv_send_wr wr = {
        .wr_id = 1, .opcode = IBV_WR_RDMA_WRITE, .send_flags = IBV_SEND_SIGNALED,
        .sg_list = &sge, .num_sge = 1,
        .wr.rdma.remote_addr = (uintptr_t)dst, .wr.rdma.rkey = mr_dst->rkey
    };
    struct ibv_send_wr *bad_wr;

    printf("Starting 10 RDMA Writes. Check Wireshark now!\n");
    for(int i=0; i<10; i++) {
        ibv_post_send(qp, &wr, &bad_wr);
        struct ibv_wc wc;
        while(ibv_poll_cq(cq, 1, &wc) < 1);
        printf("Packet %d sent.\n", i+1);
        sleep(1);
    }

    return 0;
}

编译运行:

gcc rdma_simple_write.c -libverbs -o rdma_simple_write
  1. 使用 perftest 验证拓扑
    为了验证底层 RDMA 链路是否连通,开启了两个终端,使用官方工具 ib_write_bw 进行了本地回环测试:
    • 服务端:ib_write_bw -d rxe0 -x 1
    • 客户端:ib_write_bw -d rxe0 -x 1 127.0.0.1
      测试结果显示成功打通,并跑出了约 7.28 Gb/sec 的平均带宽,证明底层的 SoftRoCE 链路是完全正常工作的。
Pasted image 20260325113813 Pasted image 20260325113833
  1. 抓包与头部协议分析(理论验证阶段)
    针对 RoCEv2 协议进行了分析。明确了 RoCEv2 是封装在 UDP 之上的,标准目的端口为 4791。其核心数据包结构包含:
    • UDP 头部:标识 4791 端口。
    • BTH (基础传输头):包含操作码(Opcode = 0x0a 代表 RDMA Write Only)、目标 QP 号和 PSN(包序号)。
    • RETH (扩展传输头):包含 Virtual Address(目标内存地址)、Remote Key(内存访问权限密钥)以及 DMA Length。
Pasted image 20260325113917 Pasted image 20260325113946

四、实验遇到的问题及尝试解决的过程

在整个实验过程中,遇到了几个非常经典的底层网络与驱动机制问题,排查过程如下:

问题一:C 程序编译时找不到头文件

  • 现象:执行 gcc rdma_simple_write.c -libverbs 时报错 fatal error: infiniband/verbs.h: No such file or directory
  • 解决过程:发现系统只安装了运行时环境,缺少开发用的头文件。通过执行 sudo apt install libibverbs-dev 安装了对应的开发包后,编译顺利通过。

问题二:运行自己编写的 C 代码后,不仅没有效果,Wireshark 也抓不到任何包

  • 现象:代码能正常打印出分配的 Local Addr 和 RKey,程序瞬间执行完毕,但在网卡上没有任何流量产生。
  • 解决过程:经过分析 RDMA 的状态机机制发现,RDMA 编程与传统 Socket 不同,创建 QP 后它默认处于 RESET 状态。我的代码仅仅将 QP 转换到了 INIT 状态。而要让网卡真正发包,必须实现复杂的握手逻辑(交换远端的 QPN、IP、RKey 和虚拟地址),并依次将 QP 状态切换至 RTR (Ready to Receive)RTS (Ready to Send)。因为缺少了这部分状态机切换代码,网卡处于静默状态,所以没有任何物理包发出。
Pasted image 20260325114005

问题三:使用官方 ib_write_bw 跑出 7Gbps 带宽,但 Wireshark 依然抓不到 UDP 4791 端口的包

  • 现象:在 Wireshark 中无论监听 wlp0s20f3lo 还是 any 接口,使用过滤器 udp.port == 4791 始终抓不到数据包。
  • 解决过程与排查步骤
    1. 排查 GID 索引:起初怀疑流量走的是 RoCEv1(无 UDP 封装)。在 Ubuntu 24.04 下没有 show_gids 命令,于是通过 ibv_devinfo -v rxe0 | grep -A 4 "GID\[" 查看网卡属性,明确查到了 GID[ 1] 对应的是当前 WiFi 的 IP 且标注了 RoCE v2。因此在运行 ib_write_bw 时加上了 -x 1 参数强制走 RoCEv2。
    2. 排查 Wireshark 显示问题:发现 Wireshark 默认列表不显示端口号且不自动解析 4791 端口。尝试了通过“Decode As”强制将 4791 端口解析为 RoCEv2 协议。
    3. 发现底层闭环机制:在确认上述配置全部正确,且客户端明确输出 7.28 Gb/sec 带宽的情况下,抓包工具依然没有任何输出。最终确认这是由于 Linux 内核与 SoftRoCE 驱动的底层优化所致。在物理机上进行同一网卡内的单机回环测试时,SoftRoCE 的数据流直接在内核驱动的底层缓存中完成了闭环,直接绕过了标准 Linux 网络协议栈(sk_buff)。而 Wireshark 和 tcpdump 依赖于挂载在网络层接口上的 pcap 抓包机制,因此这种被底层驱动短路的流量彻底处于“隐形”状态,导致无法通过常规手段在物理机的单机回环中抓到包。

尝试了三种方法,均找不到端口4791的包,实验至此无法继续深入。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 kipleyarch@gmail.com
Obsidian