Virtio 上的 pfSense:关闭 offload 前 TCP 会卡住
虚拟化的 pfSense 能通过 ping,却在批量 TCP 上卡死。与其更换网卡,不如关闭 virtio 校验和卸载路径并测量代价。
客户报告:隧道连上了,ping 有回应,登录页也能加载,但传输在每次尝试时都冻结在同一点。这个现象已经说明很多:可达性正常,持续吞吐不正常。在打开防火墙之前,先弄清楚 VPN 究竟是否参与了故障。在同一条网关上不走隧道直接推大流量 TCP;如果它同样冻结,那 OpenVPN 从来就不是原因。
使用 virtio 网卡的 pfSense 虚拟机,有很充分的理由恰好以这种形态失败。
小包能过,大 TCP 卡死
握手、DNS 应答和 ICMP 回显都很小,通常能通过。持续传输会迫使虚拟机分段,而损坏的卸载路径正是在这里暴露出来。故障未必对称:出方向可能干净,入方向却丢包,或者相反,因此用户能登录,却在第一个大响应上卡住。由于同一个客户端换一个网关就能用,最新的组件便背了锅。
所以先在没有隧道的情况下测试网关。一次持续时间足够几秒的 LAN 到 WAN 下载就够,而且必须在改动任何设置之前完成,这样事后的对比才有意义。
卸载究竟承诺了什么
virtio 是半虚拟化的:宿主和客户机分担工作,宿主宣告校验和与分段能力,并期待客户机把开始的事做完。如果客户机的协议栈没做到,帧就会带着不完整的校验和离开,或者被宿主丢弃。模拟的 E1000 提供了更简单也更慢的模型,换成它问题就消失了,根本不用改 pfSense 的任何一行。能力协商是按设备进行的,这解释了为什么一台虚拟机跑几个月都没事,而重建的同款第一天就出问题。
协商出的能力集取决于 hypervisor 版本和所选的网卡型号,而不只是你导入的配置。
一个复选框就能留住 virtio
在 pfSense 界面里,系统、高级、网络这一路径下有一个名为“禁用硬件校验和卸载”的复选框。勾选后客户机会用软件计算校验和,这与 pfSense 背后的 FreeBSD 协议栈对半虚拟化设备的期待一致。同一页上还有两个相关选项,分别对应硬件 TCP 分段卸载和硬件大接收卸载。当握手能成而大流量传不动时,就该用它们。
做这项改动时,把网卡保持为 virtio。换成 E1000 会在虚拟机的整个生命周期里为每个包消耗 CPU,还把这个配置问题从接手主机的人眼前藏起来,这比稍慢但可预期的数据路径是更差的交易。
测量你刚刚接受的代价
软件计算校验和把工作移到客户机里,所以要事后测量吞吐,而不是假定它没有代价。在小虚拟机上惩罚是真实的,但它可见且稳定,不像静默丢包。比较改动前后的接口错误计数和客户机内核日志。过滤网桥的 hypervisor 是另一种证据、另一种故障,任何卸载标志都治不好它。
每次测试只改一样东西,并把菜单路径、勾选了哪个框、测到的数字写下来。下一个面对重建虚拟机的人都想要这三样。
其他基于 BSD 的防火墙在各自的接口设置里也有等效开关,所以即便菜单路径不同,这套推理依然成立。