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

现象
远程观看 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,于是:
- 超尺寸的 UDP 包发出去,QUIC 置了 DF(协议要求,靠 ICMP 拿 PMTU),路由器没有分片权,只能整包丢弃
- 丢弃触发 frag-needed 回程,内核学到更小的 pmtu,之后同尺寸的包在本地就被直接拒发(EMSGSIZE)
- 重传风暴 → 拥塞控制崩溃 → 码率被压到 300 kbps
顺带一提:网页用 https://192.168.233.15/(IPv4)打开,不代表媒体流也走 IPv4。ICE 选路是独立的,日志里选中的是:
1 | (local) udp host 2408:8207:c3b:5f60:1852:f6ff:fe7d:867a:60252 |
这条 IPv6 路径绕开了隧道,MTU 1500,所以一切正常;选中 IPv4 那条就进隧道、必炸。这也是"有时候好像又好了"的原因。
定位
在 kvm 上用二分法量路径 MTU,逐步加大小,回复消失的位置就是上限:
1 | for s in 1400 1420 1440 1456 1472; do |
再看内核学到的 pmtu:
1 | ip route get 192.168.235.4 |
解决
把 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 | mkdir -p /etc/systemd/network/20-onekvm.network.d |
改完要重建会话才生效(已有会话仍按老 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。