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电脑 trae对接lanhu-mcp
windows
蜡台7 小时前
本机 局域网 搭建本地 HTTPS 域名完整方案
网络协议·http·https·openssl
戒了,最后一次8 小时前
Windows 安装 FFmpeg 完整教程(附环境变量配置 + 常用命令示例)
windows·ffmpeg
吹什么轩10 小时前
linux网络:UDP套接字的业务实现:字典
linux·运维·udp
liulilittle10 小时前
DeepSeek Harness 自定义模型提供商配置
前端·javascript·人工智能·windows·llm·deepseek·harness
fruge11 小时前
Windows部署MiGPT GUI:小爱音箱接入大模型、TTS与自定义人设
windows
Alkaid207711 小时前
为什么查 IP 有两个结果?这个问题问倒过多少人
网络协议·tcp/ip
源码学社12 小时前
DeerFlow Windows 本地运行教程(uv)
windows·uv·沙箱·deerflow·超级智能体·deerflow安装
Sombra_Olivia12 小时前
复现Windows Server服务RPC请求缓冲区溢出漏洞(MS08067)
网络·windows·安全·web安全·网络安全·渗透测试·vulhub
-天道酬勤-12 小时前
查看Windows电脑开机时间
windows·电脑