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
相关推荐
kakakahahahaha1 小时前
【Windows】C盘低空间反复复发的排查与扩容边界
c语言·windows·电脑·笔记本电脑·内容运营·软件需求
鲨鱼辣钊3 小时前
FastAPI进阶_Day21_WebSocket实时通讯实战
websocket·网络协议·fastapi
2301_800074213 小时前
map,list简单方法
windows·python·list
技术不好的崎鸣同学3 小时前
Windows 红队实战:前言
windows
吴声子夜歌4 小时前
Guava——基本工具(二)
windows·python·guava
猎嘤一号6 小时前
【2026 最新】Windows 11 右键菜单还原为 Windows 10 经典样式:一条命令、原理、回退与新版说明
windows·python
2501_915106326 小时前
安卓抓包软件2026,免证书抓包 应用层抓包 代理抓包全解析
网络协议·计算机网络·网络安全·ios·adb·https·udp
qq_369224336 小时前
ddraw.dll丢失是什么原因引起的?老游戏运行报错,多种可行修复手段汇总分享
windows·游戏·dll·dll修复·dll丢失·dll错误
Neighbor_OldY7 小时前
云上VPC流日志与网络流量安全分析实战:异常流量、横向移动、C2外连与恶意IP的发现与处置
网络协议·tcp/ip·安全
BR_D7 小时前
Windows 下 nvm 安装与完整使用教程
windows·nodejs·nvm