OneKVM 远程视频码率上不去:UDP MTU 问题

深夜排查 KVM 码率问题

现象

远程观看 OneKVM(NanoKVM Pro 刷的 OneKVM OS)时:

  • 码率上不去,卡在 300 kbps 左右,编码到收帧延迟明显
  • 日志持续刷
1
WARN failed resending nacked packet: io error: io error: Message too large (os error 90)
  • 局域网直连、以及走 IPv6 直连都正常,只有经 WireGuard 隧道回连时坏
  • 有时看着"重启下服务就好了",其实是 ICE 重新协商后换到了另一条路径(见下)

原因

OneKVM 按本机网卡的 MTU 打包,不做路径 PMTU 发现:

1
WebRTC ICE path selected ... interface="eth0" interface_mtu=1500 rtp_mtu=1444

rtp_mtu = interface_mtu - 56,所以发出的 IP 包正好 1500 字节。而隧道那条路的真实 MTU 只有 ~1400,于是:

  1. 超尺寸的 UDP 包发出去,QUIC 置了 DF(协议要求,靠 ICMP 拿 PMTU),路由器没有分片权,只能整包丢弃
  2. 丢弃触发 frag-needed 回程,内核学到更小的 pmtu,之后同尺寸的包在本地就被直接拒发(EMSGSIZE)
  3. 重传风暴 → 拥塞控制崩溃 → 码率被压到 300 kbps

顺带一提:网页用 https://192.168.233.15/(IPv4)打开,不代表媒体流也走 IPv4。ICE 选路是独立的,日志里选中的是:

1
2
(local) udp host 2408:8207:c3b:5f60:1852:f6ff:fe7d:867a:60252
<-> (remote) udp srflx 2001:da8:215:230b::83c8:58461

这条 IPv6 路径绕开了隧道,MTU 1500,所以一切正常;选中 IPv4 那条就进隧道、必炸。这也是"有时候好像又好了"的原因。

定位

在 kvm 上用二分法量路径 MTU,逐步加大小,回复消失的位置就是上限:

1
2
3
4
for s in 1400 1420 1440 1456 1472; do
printf "%s: " $s
ping -c 1 -W 2 -s $s 192.168.235.4 | grep -c "bytes from"
done

再看内核学到的 pmtu:

1
2
ip route get 192.168.235.4
# cache expires 599sec mtu 1412

解决

把 kvm 的 eth0 MTU 降到路径 MTU 以内,OneKVM 下次建会话时就会按新值打包(interface_mtu 跟着变):

1
ip link set eth0 mtu 1412

1412 是实测的路径上限,正好卡满、零余量;1400 也是实测可用,留 12 字节余量,路径 MTU 哪天降一点也不会复发,建议用 1400。

持久化。/run/systemd/network/20-onekvm.network 每次开机由 onekvm-network-config-prepare 重新生成,用 drop-in 叠加在上面:

1
2
mkdir -p /etc/systemd/network/20-onekvm.network.d
printf '[Link]\nMTUBytes=1400\n' > /etc/systemd/network/20-onekvm.network.d/10-mtu.conf

改完要重建会话才生效(已有会话仍按老 MTU 打包):

1
systemctl restart onekvm

效果:EMSGSIZE 归零,IPv4 路径码率从 300 kbps 回到 10+ Mbps。

试过但没用的

  • 调 MikroTik 的 WireGuard 接口 MTU:DF 的包路由器没有分片权,只能整包收或整包丢,隧道 MTU 必须 ≥ kvm 发的包大小才有意义。而且客户端那端用的是 WireGuard 图形客户端,隧道 MTU 写死 1420,配置文件里的 MTU = 字段只有 wg-quick 才认
  • MikroTik 的 clamp-to-pmtu:只钳 TCP 握手里的 MSS,对 UDP 报文不生效,实测无效
  • 清 kvm 的 pmtu 缓存:30 秒内就被重新填上

后续

已联系 OneKVM 作者,作者表示后续会加入 UDP MTU 探测,届时应该不需要再手动降 MTU。