# 利用 Dummy 网卡与 USB4 P2P 链路构建三节点无交换机全互联集群 > **环境**: Ubuntu Server 22.04+,systemd-networkd / netplan > **硬件**: 三台主机,每台配备 2x USB4/雷电4 接口 --- ## 一、设计原理 ### 1.1 拓扑 三台主机各有两个 USB4 接口,天然构成三角形 Full-Mesh:每对节点之间独占一条 40Gbps P2P 链路,无需交换机。 ``` ┌────────────┐ │ node-a │ dum0: 10.40.40.1/32 │ 10.40.40.1 │ └─────┬──────┘ ┌─────────┘ └──────────┐ tbt0│ │tbt1 │ │ ┌────┴────┐ ┌───┴─────┐ │ node-b │ │ node-c │ │10.40.40.2│ │10.40.40.3│ └──────────┘ └──────────┘ ``` ### 1.2 核心设计 - **Dummy 网卡 (`dum0`)**: 承载 `/32` 集群身份地址,接口永不下线,与物理链路状态解耦。集群服务(Ceph、K8s、Corosync)将此 IP 作为稳定的节点标识。 - **USB4 接口 (`tbt0`/`tbt1`)**: 零 IP,纯链路层承载。`thunderbolt_net` 驱动通过 Thunderbolt DMA 将以太网帧直接送达对端内存,绕过传统 PCIe 网卡数据通路。 - **路由**: 对端 dummy 地址的 `/32` 主机路由绑定对应 USB4 出接口,`scope: link`,**无网关、无 via、无接口 IP**。 ### 1.3 为什么接口不需要 IP 地址 USB4 P2P Networking 不是传统以太网。链路上只有唯一对端,驱动工作在点对点模式。当内核通过 `scope: link` 路由将报文送出时,`thunderbolt_net` 直接通过 DMA 写入对端内存区域,不存在"下一跳 IP"的概念。ARP 在首次通信时自动解析对端 dummy 接口的 MAC 地址。 ### 1.4 报文转发流程 以 node-a ping node-b 为例: ``` ping 10.40.40.2 │ ▼ 路由 lookup 匹配 10.40.40.2/32 → dev tbt0 (scope link) │ ▼ ARP (首次) "谁有 10.40.40.2?" → tbt0 → node-b node-b 回复 (dum0 响应 ARP) │ ▼ 封装 src=dum0_mac, dst=node_b_tbt0_mac, IP(10.40.40.1→10.40.40.2) │ ▼ thunderbolt_net DMA 直达 node-b 内存,绕过 PCIe 网卡 │ ▼ node-b 目标 IP 匹配本机 dum0 → 上交协议栈 ``` ### 1.5 为什么不需要策略路由 `/32` 主机路由的掩码长度(32)大于默认路由的 `/0` 或网段路由的 `/24`,在路由查找中**天然优先**。两条精确路由分别绑定两个物理出接口——目标地址即决定转发路径,无需 `ip rule` 介入,也无需额外路由表。 ### 1.6 Thunderbolt 安全模型与 P2P 网络 Thunderbolt 接口原生支持 DMA(直接内存访问),允许外设绕过 CPU 直接读写系统内存。为防止恶意设备(如伪装成合法设备的硬件攻击工具)通过 DMA 窃取数据或注入攻击载荷,Intel 在 Thunderbolt 规范中引入了四级安全策略: | 级别 | 名称 | 行为 | |------|------|------| | `none` | 无保护 | 所有设备自动连接并授权,无需任何交互 | | `user` | 用户授权 | 新设备需用户手动运行 `boltctl enroll` 授权(默认级别) | | `secure` | 安全认证 | 设备需持有有效密钥证书,且用户确认 | | `dponly` | 仅 DP | 仅允许 DisplayPort 设备,拒绝所有数据通道 | **为什么 P2P 网络必须设为 `none`?** 在 `user` 模式下,当一条 USB4 线缆插入两个主机之间时,Thunderbolt 控制器识别到对端是一个新"设备",将其状态标记为 **未授权(unauthorized)**。此时: - `thunderbolt_net` 驱动无法在未授权链路上创建网络接口 - 接口不会出现,或出现但处于 DOWN 状态 - 必须手动执行 `boltctl enroll ` 才能建立连接 三节点集群共 6 条链路(每台 2 条),且 Ubuntu Server 通常为无头(headless)部署,逐一手动授权不现实。设为 `none` 后,控制器自动授权所有 P2P 连接,`thunderbolt_net` 在检测到对端主机后立即创建接口并尝试链路协商。 **风险与替代方案** `none` 的风险在于任何插入的 Thunderbolt 设备都自动获得完整 DMA 访问权限。但在本场景中: - 主机位于受控环境(机房/机柜),物理接触受限制 - 线缆另一端是已知的主机,不存在不可信外设 - USB4 接口不对外暴露(无公共接入点) 如需保留安全控制,可在初始部署完成、链路稳定后,改用 `bolt` 工具显式注册对端主机: ```bash # 查看已连接设备 boltctl list # 授权特定设备(替换为实际 UUID) sudo boltctl enroll 0038c554-fd8b-3e01-ffff-ffffffffffff ``` 然后将安全级别恢复为 `user`,并配合 udev 规则在已授权设备重新连接时自动通过。 --- ## 二、地址与路由规划 | 节点 | dum0 | tbt0 路由 | tbt1 路由 | |------|------|-----------|-----------| | node-a | `10.40.40.1/32` | `10.40.40.2/32 dev tbt0 scope link` | `10.40.40.3/32 dev tbt1 scope link` | | node-b | `10.40.40.2/32` | `10.40.40.1/32 dev tbt0 scope link` | `10.40.40.3/32 dev tbt1 scope link` | | node-c | `10.40.40.3/32` | `10.40.40.2/32 dev tbt0 scope link` | `10.40.40.1/32 dev tbt1 scope link` | 每节点**一个 IP 地址**(dum0 上的 /32),两条**无网关主机路由**。 --- ## 三、前置准备 ### 3.1 物理连线 ``` node-a:tbt0 ──USB4 线── node-b:tbt0 node-b:tbt1 ──USB4 线── node-c:tbt0 node-c:tbt1 ──USB4 线── node-a:tbt1 ``` 使用认证 USB4/雷电4 线缆(40Gbps),普通 USB-C 充电线无法协商网络通道。 ### 3.2 内核模块 ```bash modinfo thunderbolt_net # 确认模块存在 sudo modprobe thunderbolt_net # 加载 echo 'thunderbolt_net' | sudo tee /etc/modules-load.d/usb4-net.conf # 开机自动加载 lspci | grep -i thunderbolt # 确认控制器识别 ``` ### 3.3 关闭 Thunderbolt 安全认证 ```bash echo 'none' | sudo tee /sys/bus/thunderbolt/devices/domain0/security echo 'ACTION=="add", SUBSYSTEM=="thunderbolt", ATTR{security}="none"' | \ sudo tee /etc/udev/rules.d/50-tb-auto.rules sudo udevadm control --reload-rules ``` ### 3.4 USB Power Delivery 与主机对主机供电问题 USB4/雷电接口内置 USB Power Delivery(PD)功能,最高支持 100W(甚至 240W EPR)供电。当用于**主机对主机**P2P 网络时,需要理解 PD 的角色协商机制。 **USB-C PD 角色协商原理** USB-C 通过 CC(Configuration Channel)引脚上的电阻确定供电角色: | 角色 | CC 引脚 | 行为 | |------|---------|------| | **Source** | 上拉电阻 Rp(56k/22k/10k) | 向 VBUS 输出电力 | | **Sink** | 下拉电阻 Rd(5.1k) | 从 VBUS 接收电力 | | **DRP** | 可切换 Rp/Rd | 双角色,根据策略选择 | 当两个 **Source(如台式服务器)** 通过 USB-C 连接时,双方 CC 引脚均为上拉电阻,电压超出识别范围,VBUS **保持 0V**(Cold Socket 状态)。此时: - **不会互相供电**——VBUS 无电压,链路 purely 用于数据传输 - **无额外功耗**——PD 控制器检测到无效 CC 电平,不启动供电 当 **DRP 设备(如笔记本)** 参与连接时,角色取决于 Try.SRC/Try.SNK 策略。如果笔记本**从 AC 适配器供电**,通常表现为 Source,与服务器直连无供电冲突;但如果笔记本**仅靠电池运行**,可能尝试成为 Sink,从对端服务器请求供电。 **实际部署建议** | 场景 | 供电行为 | 处理建议 | |------|----------|----------| | 三台台式机/服务器(均 AC 供电) | 双方均为 Source,VBUS=0V,无供电 | **无需处理** | | 包含笔记本(AC 供电) | 笔记本通常为 Source,无冲突 | **无需处理** | | 包含笔记本(电池运行) | 笔记本可能尝试 Sink,请求对端供电 | 建议接入 AC 适配器;或确保对端端口供电能力充足 | **如何判断是否存在 PD 问题?** ```bash # 1. 检查 VBUS 电压(如系统暴露该接口) cat /sys/class/power_supply/*/voltage_now 2>/dev/null # 2. 观察链路稳定性——PD 协商失败常见症状 # - thunderbolt 接口间歇性 UP/DOWN # - dmesg 中出现 "tb: port reset" 或 "link training failed" dmesg | grep -iE "thunderbolt|tb:|usb4" | tail -20 # 3. 检查 boltctl 中的设备状态(如使用 user/secure 模式) boltctl list # 关注 power 列:如果显示对端在尝试供电,会有标注 ``` **如果确实需要隔离供电(极少见)** Linux 下没有标准软件接口直接禁用 USB4 端口的 PD 功能——PD 协商由 Type-C 端口控制器(TCPC)硬件管理,独立于操作系统。如确需隔离: 1. **BIOS 设置**:部分主板 BIOS 提供 "USB-C Power Delivery" 开关(罕见,多见于笔记本) 2. **硬件方案**:使用 USB-C PD 隔离器(只保留 SBU/DP 数据引脚,切断 VBUS 供电引脚),或自行用绝缘胶带覆盖线缆/接口的 VBUS(A4/B4/A9/B9)引脚 3. **内核参数**:某些系统的 TCPC 驱动支持通过设备树(DT)或 ACPI 覆盖 PD 策略,但非通用方案 对于典型的三节点服务器集群,**上述问题极少出现**——只要所有节点都有稳定的 AC 供电,USB4 端口默认表现为 Source 角色,VBUS 保持 Cold Socket 状态,链路 purely 用于 40Gbps 数据传输。 ### 3.5 接口重命名 通过 PCI 路径固定命名,避免重启后 thunderbolt0/1 顺序翻转: ```bash udevadm info -q path -p /sys/class/net/thunderbolt0 # 输出: /devices/pci0000:00/0000:00:08.3/0000:ca:00.5/... ``` `/etc/systemd/network/10-tbt0.link`: ```ini [Match] Path=pci-0000:ca:00.5 Driver=thunderbolt-net [Link] Name=tbt0 MACAddressPolicy=none ``` `/etc/systemd/network/10-tbt1.link`: ```ini [Match] Path=pci-0000:ca:00.6 Driver=thunderbolt-net [Link] Name=tbt1 MACAddressPolicy=none ``` ```bash sudo systemd-hwdb update sudo reboot # 重启生效 ``` --- ## 四、netplan 配置 ### node-a ```yaml network: version: 2 renderer: networkd dummy-devices: dum0: addresses: - 10.40.40.1/32 ethernets: eth0: dhcp4: true tbt0: routes: - to: 10.40.40.2/32 scope: link tbt1: routes: - to: 10.40.40.3/32 scope: link ``` ### node-b ```yaml network: version: 2 renderer: networkd dummy-devices: dum0: addresses: - 10.40.40.2/32 ethernets: eth0: dhcp4: true tbt0: routes: - to: 10.40.40.1/32 scope: link tbt1: routes: - to: 10.40.40.3/32 scope: link ``` ### node-c ```yaml network: version: 2 renderer: networkd dummy-devices: dum0: addresses: - 10.40.40.3/32 ethernets: eth0: dhcp4: true tbt0: routes: - to: 10.40.40.2/32 scope: link tbt1: routes: - to: 10.40.40.1/32 scope: link ``` ### 应用 ```bash sudo netplan generate sudo netplan apply ``` --- ## 五、验证与测试 ### 5.1 接口状态 ```bash $ ip -br addr dum0 UNKNOWN 10.40.40.1/32 tbt0 UP tbt1 UP eth0 UP 192.168.1.10/24 ``` dum0 有 IP,tbt0/tbt1 无 IP——正确。 ### 5.2 路由表 ```bash $ ip route 10.40.40.1 dev dum0 proto kernel scope link src 10.40.40.1 10.40.40.2 dev tbt0 scope link 10.40.40.3 dev tbt1 scope link ``` ### 5.3 路径确认 ```bash $ ip route get 10.40.40.2 10.40.40.2 dev tbt0 scope link src 10.40.40.1 uid 0 cache $ ip route get 10.40.40.3 10.40.40.3 dev tbt1 scope link src 10.40.40.1 uid 0 cache ``` ### 5.4 连通性 ```bash ping -c3 10.40.40.2 # A→B via tbt0 ping -c3 10.40.40.3 # A→C via tbt1 ``` ### 5.5 带宽 ```bash sudo apt install -y iperf3 iperf3 -s -D # 所有节点 iperf3 -c 10.40.40.2 -t 30 -P 4 # A→B iperf3 -c 10.40.40.3 -t 30 -P 4 # A→C ``` 预期 TCP 多流 25~35 Gbps。 --- ## 六、生产优化 ### 6.1 巨型帧 ```yaml tbt0: mtu: 9000 routes: - to: 10.40.40.2/32 scope: link ``` ### 6.2 内核调优 `/etc/sysctl.d/99-usb4.conf`: ```ini net.core.rmem_max = 134217728 net.core.wmem_max = 134217728 net.ipv4.tcp_rmem = 4096 87380 134217728 net.ipv4.tcp_wmem = 4096 65536 134217728 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_slow_start_after_idle = 0 net.core.netdev_max_backlog = 65536 ``` ### 6.3 服务集成 Ceph、Kubernetes、Corosync 等服务直接使用 `10.40.40.X` 作为节点地址: ```ini [global] mon_host = 10.40.40.1,10.40.40.2,10.40.40.3 public_network = 10.40.40.0/24 ``` --- ## 七、故障排查 | 现象 | 诊断 | |------|------| | tbt 接口不出现 | `dmesg \| grep thunderbolt` 查热插拔事件;确认线缆为 40Gbps 认证 USB4 线 | | `Network unreachable` | `ip route` 确认 /32 路由存在;`ip link` 确认 tbt 接口 UP | | ping 无响应 | `ip neigh show dev tbt0` 查 ARP;对端 dum0 是否配置正确 | | 带宽低 | 加 `-P 4` 多流测试;确认 `mtu: 9000` 生效 | | 重启丢失 | `sudo netplan generate` 看 YAML 语法;检查 renderer 为 networkd | --- ## 八、一键脚本 ### node-a ```bash #!/bin/bash set -e # 模块 sudo tee /etc/modules-load.d/usb4-net.conf <<< "thunderbolt_net" sudo modprobe thunderbolt_net 2>/dev/null || true # 安全策略 echo 'none' | sudo tee /sys/bus/thunderbolt/devices/domain0/security echo 'ACTION=="add", SUBSYSTEM=="thunderbolt", ATTR{security}="none"' | \ sudo tee /etc/udev/rules.d/50-tb-auto.rules sudo udevadm control --reload-rules # netplan sudo tee /etc/netplan/60-usb4-cluster.yaml << 'EOF' network: version: 2 renderer: networkd dummy-devices: dum0: addresses: - 10.40.40.1/32 ethernets: eth0: dhcp4: true tbt0: routes: - to: 10.40.40.2/32 scope: link tbt1: routes: - to: 10.40.40.3/32 scope: link EOF sudo netplan generate && sudo netplan apply echo "[OK] node-a configured. Test: ping -c3 10.40.40.2 && ping -c3 10.40.40.3" ``` node-b: dum0 `10.40.40.2/32`,路由目标 `.1` 和 `.3`。 node-c: dum0 `10.40.40.3/32`,路由目标 `.2` 和 `.1`。 --- ## 参考 - [pmx-cluster-tb](https://github.com/pieter-v-n/pmx-cluster-tb) - [Linux Thunderbolt 子系统文档](https://www.kernel.org/doc/html/latest/driver-api/thunderbolt.html)
没有评论