记一次网络迁移的经历

背景

长久以来,我们战队的服务器通过PVE Host连接BUPT-Mobile联网,并且使用nftables做IPv4 NAT+端口转发的方式为网桥上的VM提供网络。

image1.drawio

然而,我们遇到了两个需求

  1. 希望为内网的VM提供公网IPv6
  2. 考虑后认为还是需要一个专职网关

我们的IPv6是通过SLAAC拿到一个/64的地址,因此需求1可以通过RA Relay和NDP Relay完成,不过折腾很久未果后,还是决定更换一个更好配置的网关:OpenWRT

迁移

因为配置工作比较繁琐+平时比较忙,因此我们首要面临的挑战就是:怎么在尽可能避免服务中断的情况下,进行OpenWRT的配置。好在我们有两张无线网卡,我们将闲置的无线网卡直通进OpenWRT (VM100),可以在配置好OpenWRT后,再将其作为其他VM在vmbr0上的网关。

image2.drawio

这样有以下几个优点:

  1. OpenWRT安装时可以通过PVE Host联网,从而安装必须的无线网卡驱动等软件包
  2. 保持了原先的IPv4网络拓扑,发生配置错误也不会影响其他VM
  3. 使VM连接了IPv6网络,可以通过OpenWRT连接外网IPv6测试配置是否正确
  4. 可以通过将VM的网关设为10.0.0.100的方式测试OpenWRT的IPv4配置

同时,在两个无线接口没有同时产生大流量时,也不会有明显的干扰现象。

待OpenWRT配置完毕后,我们更改OpenWRT的IP为10.0.0.1,PVE Host的IP为10.0.0.2,并屏蔽PVE Host的无线接口,从而使OpenWRT无缝替代原先的网关发挥作用。同时,通过DDNS的方式使得域名指向正确的IP。

image3.drawio

问题解决

然而,在测试过程中,我们发现在使用OpenWRT nftables的IPv6端口转发功能时,出现了明显的掉速现象,并且同时影响了上传和下载。通过使用Tcpdump在OpenWRT的WAN接口(phy0-sta0)抓包,我们发现了大规模的TCP重传,并且几乎每次重传一定对应一个长度大于MTU (1500)的帧

image4

我们首先怀疑是链路上MTU设置不当造成的,我们复查了链路上每个节点的MTU,发现均设置无误;同时,我们打开了OpenWRT的MSS Clamping功能,但没有效果。经过查询,我们发现大于MTU的帧被称为Jumbo Frame,是提升网络性能的特性,属于正常现象。并且我们对比了正常的IPv6传输流量(不经过端口转发),发现也有很大的Jumbo Frame,因此排除MTU设置不当的问题。

接下来,我们仔细分析数据包,发现了一点端倪

image5

注意到WAN口首先接收到了帧长度为8246bytes的TCP包,TCP Payload长度为8172,Seq为130753;然后接收到了帧长度为1436bytes的TCP包,Seq为138925;接下来系统却返回了Dup ACK请求重传,Ack编号为130753+1362=132115,同时SLE=138925表明后一个包被正常接收。由此我们可以判断,在WAN口接收到包并端口转发到LAN口上的VM时,链路上有节点拆了帧,将8172bytes的TCP Payload拆成了以一个1362bytes payload包开头的若干个包,其中第一份payload被正常接收,而后面的payload被丢包或拒绝

查询资料后,我们首先怀疑是虚拟化网卡的bug,并使用ethtool禁用掉了虚拟网卡的gso, tso, lro等offload,然后在LAN口抓包,发现了匪夷所思的一幕

image6

LAN口分明接收到了长度正常的帧,Seq为43488,却还是请求了43488的重传,这意味着这个数据包可能本身有问题。这时我们才想起打开WireShark的TCP Checksum Validation,结果让我们茅塞顿开

image-20250426020136251

每次重传一定对应一个checksum错误的包,但是我们分明已经关掉虚拟网卡上几乎所有的offload了呀。这时,我们将目光转移到了硬件网卡上。对刚才WAN口抓的包启用TCP Checksum Validation,发现网卡接收到的包同样存在checksum错误的问题。因此,我们关闭了无线网卡上可以关掉的offload,发现关闭GRO (Generic Receive Offload)后,问题得到了解决。