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 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
rockmelodies2 天前
# Windows Server2008 R2 Standard(SP1镜像U盘)重置本地管理员密码【完整详细步骤】
windows·密码破解
XUEYUAN52122 天前
ASN 自治系统号风控:平台如何通过 IP 所属自治域批量识别代理流量
python·网络协议·http·网络安全·socks5
CHENKONG_CK2 天前
破解制鞋打磨痛点:RFID赋能去毛刺工序自动化升级
网络·单片机·嵌入式硬件·网络协议·tcp/ip
z落落2 天前
C#UDP+串口服务端+UDP 客户端(含 CRC16 校验)
网络·网络协议·udp
kakakahahahaha2 天前
Windows C盘空间不足的安全清理与迁移流程
windows·电脑·笔记本电脑·软件需求·c盘清理
做咩啊~2 天前
远程连接提示:身份验证错误,要求的函数不受支持
windows
曼巴UE52 天前
UE5 客户端 需要的网络同步概念总结(3 )-事件同步 RPC 以及三种调用方式
网络·c++·网络协议·学习·rpc·ue5
百事牛科技2 天前
PPT只读怎么取消?四种情况分别处理
windows·powerpoint
开开心心就好2 天前
安卓手写文字生成工具,多种纸张一直免费
网络·网络协议·tcp/ip·leetcode·智能手机·电脑·模拟退火算法