Windows虚拟机UDP大包分片排障

问题

虚拟机发送大 UDP 数据报时,接收端无法正常处理。

结论

在 MTU 为 1500 的 IPv4 链路中,UDP 负载超过 1472 字节后通常会触发 IP 分片。分片并不一定表示宿主机或网卡故障,但部分应用、隧道、VPN、负载均衡设备和防火墙不会正确重组分片,因此发送端看似成功,接收端却可能收不到完整数据报。

网卡还可能启用 UDP Segmentation Offload(USO)。USO 会把较大的 UDP 数据交给网卡或虚拟化后端分段;如果虚拟网卡、TAP、网桥和物理网卡的卸载能力不一致,可能出现与另一台虚拟机不同的行为。排查时应先确认真实 MTU、卸载状态和抓包结果,再决定是否关闭 USO。

推荐处理顺序

  1. 优先让应用把 UDP 负载控制在路径 MTU 以内。
  2. 对比正常和异常虚拟机的网卡型号、MTU 和卸载状态。
  3. 只在确认 USO 参与问题后,关闭目标虚拟机的 IPv4/IPv6 USO。
  4. 用宿主机和实际接收端分别抓包验证。
  5. 记录 CPU 变化;大 UDP 发送关闭 USO 后可能增加 CPU 开销。

操作步骤

1. 确认宿主机网卡和链路 MTU

将下列占位符替换为实际值:

  • <目标虚拟机ID>:出现问题的虚拟机编号。
  • <对照虚拟机ID>:表现正常的虚拟机编号,可选。
  • <网桥名>:通常是 vmbr0,以当前配置为准。
  • <TAP接口>:通常形如 tap<目标虚拟机ID>i0

在 PVE 宿主机执行:

bash 复制代码
qm config <目标虚拟机ID> | grep '^net0'
qm config <对照虚拟机ID> | grep '^net0'
ip -d link show <网桥名>
ip -br link
ip link show <TAP接口>

重点记录:

  • 虚拟机、TAP、网桥和物理出口的 MTU 是否一致。
  • 网卡是否启用了 firewall=1、VLAN 或其他会改变路径的选项。

2. 在 Windows 中查看网卡卸载状态

确认已安装 QEMU Guest Agent 后,在目标 Windows 虚拟机的管理员 PowerShell 中执行:

powershell 复制代码
$nic = Get-NetAdapter |
  Where-Object InterfaceDescription -like 'Red Hat VirtIO Ethernet Adapter*' |
  Where-Object Status -eq 'Up' |
  Select-Object -First 1

if (-not $nic) {
  throw '没有找到正在使用的 VirtIO 网卡,请先确认网卡型号和驱动。'
}

Get-NetIPInterface -InterfaceIndex $nic.ifIndex -AddressFamily IPv4 |
  Select-Object InterfaceAlias,NlMtu,ConnectionState
Get-NetAdapterUso -Name $nic.Name
Get-NetAdapterLso -Name $nic.Name
Get-NetAdapterChecksumOffload -Name $nic.Name
Get-NetAdapterAdvancedProperty -Name $nic.Name |
  Where-Object DisplayName -match 'UDP|LSO|RSS|Checksum'

确认以下项目:

  • IPv4 和 IPv6 的 UDP Segmentation Offload 是否为 Enabled。
  • MTU 是否为 1500,或是否符合实际网络路径。
  • RSS、TCP LSO 和校验和卸载状态。
  • 目标虚拟机与正常虚拟机之间是否只有 USO 状态不同。

如果不是 VirtIO 网卡,以上 Red Hat VirtIO Ethernet Adapter 查询条件需要按实际驱动名称调整。

3. 只关闭 IPv4/IPv6 USO

确认 USO 是主要差异后,只关闭 USO,不要一次性关闭全部卸载功能:

powershell 复制代码
Disable-NetAdapterUso -Name $nic.Name -IPv4 -IPv6 -Confirm:$false

网卡可能短暂重置,RDP 或其他网络连接会断开数秒。重新连接后检查:

powershell 复制代码
Get-NetAdapterUso -Name $nic.Name
Get-NetIPInterface -InterfaceIndex $nic.ifIndex -AddressFamily IPv4 |
  Select-Object InterfaceAlias,NlMtu,ConnectionState

预期结果:IPv4/IPv6 USO 为关闭,MTU 没有被改变。不要同时修改网卡型号、IP 地址、网桥和其他卸载选项,否则无法判断实际原因。

4. 验证应用层是否仍发送超大 UDP 数据报

在 MTU 1500 的 IPv4 网络中:

text 复制代码
最大 UDP 负载 ≈ 1500 - 20(IPv4 头)- 8(UDP 头)= 1472 字节

如果数据还要经过 VPN、隧道、VXLAN、容器网络或其他封装,实际安全值应更小。推荐应用层自行分片、重组,或把单个 UDP 负载控制在路径 MTU 以内。

5. 在宿主机抓包确认分片情况

<VM_IP><接收端IP><TAP接口> 替换为实际值:

bash 复制代码
tcpdump -ni <TAP接口> 'src host <VM_IP> and dst host <接收端IP> and ip' -vv -c 20

如果要检查物理出口,可在物理网卡上重复抓包:

bash 复制代码
tcpdump -ni <物理网卡> 'src host <VM_IP> and dst host <接收端IP> and ip' -vv -c 20

同时查看宿主机重组和 UDP 错误计数:

bash 复制代码
nstat -az | grep -Ei 'IpReasm(Reqds|OKs|Fails)|Udp(InErrors|RcvbufErrors|SndbufErrors)'

测试大于 MTU 的 UDP 数据报时,抓包中可能出现多个 IPv4 分片。正常情况下,重组成功计数会增加,IpReasmFails 不应持续增加,UDP 错误计数也不应异常增长。

注意:关闭 USO 不会消除超过 MTU 的 IP 分片,它只是避免 VirtIO USO 参与这条发送路径。如果实际接收端不支持 IPv4 分片,最终仍需从发送应用、隧道 MTU 或接收端处理能力上解决。

6. 重启后复查

Windows 重启后再次执行:

powershell 复制代码
Get-NetAdapterUso -Name $nic.Name

确认设置仍然保持。如果设置没有持久化,应检查 VirtIO 驱动版本、设备高级属性和 Windows 组策略,而不是继续重复修改 PVE 网卡配置。

7. 回滚

如果关闭 USO 后 CPU 占用明显增加、吞吐下降或业务出现新问题,可恢复原设置:

powershell 复制代码
Enable-NetAdapterUso -Name $nic.Name -IPv4 -IPv6 -Confirm:$false
Get-NetAdapterUso -Name $nic.Name
相关推荐
寒水馨2 小时前
Windows下载、安装scrcpy-v4.1(附安装包scrcpy-win64-v4.1.zip)
windows·scrcpy·投屏工具·安卓投屏·usb调试·android投屏·屏幕镜像
記億揺晃着的那天2 小时前
HTTPS 页面内网直连 NAS:解决 Mixed Content 与公网带宽瓶颈
网络协议·http·https·nas
神仙别闹3 小时前
基于QT(C++)实现Windows 自启动项查看和分析
c++·windows·qt
想学好C++的oMen5 小时前
socket编程TCP
linux·网络·网络协议·tcp/ip
love530love13 小时前
OpenClaw Windows Companion 桌面客户端 连接 LM Studio 完整配置指南
人工智能·windows·python·openclaw
光年像素1 天前
Windows Win+R 运行框常用快捷命令大全
windows
2401_873479401 天前
SOC告警日志中IP归属不明怎么办?部署IP离线库三步提升响应效率
网络·网络协议·tcp/ip
寒水馨1 天前
Windows下载、安装ollama-v0.32.1(附安装包OllamaSetup.exe)
windows·llm·大语言模型·llama·本地部署·ollama·模型运行
曾阿伦1 天前
Windows 下运行 Hadoop 并部署到 AWS EMR 指南
hadoop·windows·aws