shixudong@163.com
本人使用WSL2已有一段时间,近日在学习linux5.0新内核的UDP GRO功能时,偶尔发现WSL2在NAT模式下eth0的MTU值默认只有1420,顺便检查了一下Vmware和VirtualBox虚拟机,发现其NAT模式下以太网卡的MTU默认都是1500。感觉有点怪异,NAT理论上不涉及数据包的拆分合并,怎么会莫名其妙降低MTU呢,手工将其改为1500,似乎也没出现不良症状。好奇之下,网上搜了一下相关资料,WSL2(NAT)降低默认MTU,居然和所在Windows主机上的VPN网卡有关,如果所在主机没有VPN网卡,WSL2(NAT)的默认MTU一般还是1500。综合网上资料,最初WSL2(NAT)的MTU也是1500,后来发现在通过主机VPN网卡访问外部网络时不正常,便自动匹配Windows主机上的最小MTU,本人主机上使用了wireguard,其默认MTU为1420,所以WSL2(NAT)的MTU也顺理成章变成了1420。我试着将WSL2(NAT)的MTU手动改为1500,同时将Windows主机默认路由改为wg网卡(AllowedIPs = 0.0.0.0/1,128.0.0.0/1),此时确实出现了ssh不能连接的问题,进一步测试发现在启用wg的kill-switch功能后(AllowedIPs = 0.0.0.0/0),即使MTU=1420也必定会出现无法访问互联网现象。网上有位老外对WSL2(NAT)和主机VPN网卡之间的影响做了分析,网络访问异常主要由3个原因引起,分别是MTU、路由和DNS。本人同时对比了WSL2、Vmware以及VirtualBox下NAT和主机VPN网卡的互动,做了进一步的分析,有助于更深刻地了解虚拟机的NAT模式,特此记录。
一、MTU问题
对于ICMP和UDP数据包来说,MTU不匹配仅仅是个诱因,本质上还是WSL2(NAT)的网关(对应主机上的vEthernet (WSL)网卡)功能太弱鸡,完全不能处理IP碎片包(即使数据包携带“允许分段”标志)。测试发现,无论WSL2(NAT)的MTU自动设置为1420,或将其手动更改为1500,在WSL2(NAT)内ping外部网络,当包长度超过1392或1472(MTU-28)时,允许分段的ping包实际上已顺利发送出去,但由于网关无法处理IP碎片包导致ping不通。对于UDP数据包同样如此,使用iperf测试UDP包,因UDP包默认长度1470,已超过WSL2(NAT)的默认MTU(1420),WSL2在IP层对UDP包分片并顺利发送出去,但因网关不支持IP碎片包,分片后的UDP包到达网关后即被丢弃,导致无法进行UDP性能测试。要使iperf能顺利测试UDP,要么设置iperf的UDP包默认长度参数不超过MTU-28,要么将WSL2的MTU重新设置为1500。
TCP数据包能够自动协商MSS,故不存在上述IP碎片包问题。但当WSL2(NAT)的MTU手动更改为1500,和主机外出网卡的MTU不匹配时,首先依赖MSS clamping机制,并将PMTU发现机制作为备用手段,如两者都不起作用,就会引发TCP不通问题。
在NAT不能提供MSS clamping机制时,PMTU发现机制尤显重要,而且涉及本地和远端。本地PMTU发现机制一般没有问题,WSL2(NAT)的TCP大包外出时,主机能够第一时间发现WSL2(NAT)和主机外出网卡的MTU不匹配问题,并通过vEthernet (WSL)网卡向WSL2(NAT)发送通告Path MTU的ICMP包,以便WSL2(NAT)后续动态调整MSS。而对端网络如出于安全考虑,阻塞了ICMP包,将导致远端PMTU发现机制失效,对端TCP大包无法回来,具体表现为无法访问网页或者ssh操作挂起。另外由于远端高版本ssh服务器的认证信息数据量大,使用了TCP大包,导致认证信息到不了ssh客户端,所以有时也表现为ssh无法登录。鉴于对端网络的不可控因素可能影响TCP通信,干脆将WSL2(NAT)的默认MTU等同于主机上最小的MTU不失为一种简易且有效的方法。
Vmware和VirtualBox的NAT模式,在主机层面,其实质就是代理模式,主机上netstat能看到主机代理发起的相关TCP连接。虚拟机内TCP数据包通过主机路由出去时,代理模式发起的TCP连接将以主机外出网卡的MTU去协商TCP的MSS,此外如发现MTU不匹配,代理模式还自动将虚拟机发来的大包拆分成小包(非IP碎片包),使之适配外出网卡的MTU,以便顺利发送出去。代理过程对虚拟机完全透明,虚拟机认为自己处于NAT模式,也感觉不到两者MTU不匹配这一事实。
Vmware和VirtualBox的NAT对于UDP和ICMP数据包的处理,和TCP一样也采用了代理模式,不同之处在于MTU不匹配时,代理模式对虚拟机大包的拆分采用了IP碎片包方式。此外,当虚拟机MTU大于主机外出网卡MTU时,VirtualBox(NAT)存在一个小缺陷,当数据包长度落在两个MTU之间时,虚拟机发出的UDP和ICMP数据包默认带“不分段”标志,VirtualBox(NAT)在代理此类数据包时,并不对“不分段”标志进行必要转换,意味着数据包不允许分片,但由于数据包长度已超过外出网卡MTU,又必须进行拆包处理,该情形下两者相冲突,VirtualBox(NAT)对此无能为力,数据包最终无法通过主机转发。针对这一缺陷,可将虚拟机linux的ip_no_pmtu_disc设置为1,使得数据包默认都携带“允许分段”标志,允许VirtualBox(NAT)用IP碎片包方式进行拆包处理,顺利通过主机转发出去。
前面分析了Vmware和VirtualBox的NAT,最后还得提一下WSL2的本家Hyper-v(NAT),本自同根生,因此存在的问题和分析与WSL2(NAT)完全一致。至于前文所提的WSL2(NAT)网关功能太烂,不支持IP碎片包,实际上是Win11配套Hyper-v的NAT功能太烂,无法对IP碎片包进行NAT转换(事实上,IP碎片包的转换对基于端口的NAT也确实是个挑战),而不是网关对应的虚拟网卡弱鸡。经验证,只要不使用NAT转换功能,Hyper-v虚拟网卡对IP碎片包的处理完全没有问题。例如WSL2(NAT)和主机上的vEthernet (WSL)网卡(对应WSL2的网关IP)之间的通信无需使用NAT转换,所以两者之间互ping大包不受影响,能够正确处理IP碎片包。而WSL2(NAT)和主机其他物理网卡之间的通信因为需要经过NAT转换,即使两者MTU一致,也无法ping通大包。
二、路由和DNS问题
当Windows主机启用wg的kill-switch功能后(AllowedIPs = 0.0.0.0/0),即使WSL2(NAT)的MTU已自动设置为1420,依旧无法访问互联网,初步测试发现,是WSL2(NAT)网关作为DNS不能提供解析服务,如改用wg通道可达的DNS或者不使用域名直接访问外部IP,就没有问题。经深入研究,WSL2(NAT)网关不能解析DNS只是表象,本质上是由于kill-switch的数据包保护机制在发挥作用。wg启用kill-switch功能后,主机和外部网络之间的数据包,只允许来自或去往wg通道,本地局域网通信则不被允许。而WSL2(NAT)的网关就是主机上的vEthernet (WSL)网卡,WSL2(NAT)访问网关的DNS流量被视为本地局域网通信而丢弃,导致无法提供DNS解析。事实上,此时WSL2(NAT)和其网关之间压根无法互相ping通,但WSL2(NAT)通过网关转发出去的数据包因为需要通过wg通道,反而可以正常通信。
新版WSL2引入了dnsTunneling开关,启用后,WSL2(NAT)不再使用网关作为DNS,新的DNS地址127.0.0.42属于loopback地址,不受kill-switch机制影响。但如主机wg在启用kill-switch功能时没有同时为wg配置DNS的话,即使主机原DNS属于wg通道可达DNS,使用新的127.0.0.42仍然无法解析DNS。这是由于kill-switch的DNS保护机制在发挥作用,启用kill-switch后,主机DNS客户端只允许使用专门为wg配置的DNS,WSL2在启用dnsTunneling后,通过隧道调用主机DNS客户端,如没有专门为wg配置DNS,相当于没有设置DNS,所以无法解析。当然,该情形下依旧可以让WSL(NAT)改用wg通道可达的DNS而非127.0.0.42,此时将直接通过网络访问,绕过了主机DNS客户端,不受kill-switch的DNS保护机制影响。另一方面,主机上包括浏览器在内的大部分程序(同样包括下面提及的Vmware和VirtualBox)在获取域名时不通过主机DNS客户端,因此允许使用wg通道可达的任何DNS,同样不受kill-switch的DNS保护机制影响。
对比一下Vmware和VirtualBox的NAT模式在主机wg启用kill-switch功能后的表现,VirtualBox(NAT)在主机上没有对应的网卡,直接使用主机的DNS作为自己的DNS(通常为wg通道可达DNS),使用末位为2的同网段IP地址作为自己的网关。Vmware(NAT)表面上类似WSL2(NAT),在主机上生成了对应的网卡(IP末位地址为1),但该网卡IP并不充当虚拟机的网关和DNS,甚至可以设置不要在主机上生成该网卡,虚拟机则使用末位为2的同网段IP地址作为自己的网关和DNS。如同WSL2(NAT)一样,在主机wg启用kill-switch功能后,Vmware(NAT)和末位地址为1的IP之间也无法通信,但Vmware(NAT)和DNS之间的通信不涉及主机局域网,完全属于虚拟机内部通信,而VirtualBox(NAT)和DNS之间的通信因为需要通过wg通道,也不属于局域网通信,故两者都不受kill-switch机制影响。
显然Vmware和VirtualBox在NAT模式下的DNS解析完全符合kill-switch允许通信的要求,故没有类似WSL2(NAT)的DNS解析问题。另外一方面,也完美解释了Vmware(NAT)为什么使用.2而不是.1作为网关和DNS的疑惑。
Wg的kill-switch机制比较科学,对底层socket进行了截获,以判断其通信流量是否通过wg来控制放行与否,但有些VPN单纯采用了路由技巧实现kill-switch机制,如前面老外在路由问题中所提到的VPN,此类VPN通过新增InterfaceMetric为1的多条指向VPN的路由覆盖本地局域网路由(也包括虚拟机对应的局域网络),以引导流量完全指向VPN通道。由于纯粹使用路由机制实现kill-switch,导致外部网络回复WSL2(NAT)的数据包又重新路由到VPN通道,最终WSL2(NAT)彻底无法与外部网络通信。针对此类VPN,只有参照老外给出的方案,删除新增的针对虚拟机的局域网络,才能在启用kill-switch的同时解决WSL2(NAT)与外部网络的通信问题。至于Vmware和VirtualBox的NAT,虚拟机和外部网络通信时,由于采用代理模式,取消或绕过了虚拟机对应的主机网卡,同样不会受到此类VPN启用kill-switch功能的影响。
至于Hyper-v(NAT),主机wg启用kill-switch功能后,针对局域网通信,仅放开了主机作为DHCP客户端的通信,对于Hyper-v(NAT)来说,主机还要充当DHCP服务器,但是DHCP响应包却发不出去,导致Hyper-v(NAT)根本无法获取DHCP。而WSL(NAT)的DHCP不是通过网络分配(虚拟机里也无需跑DHCP客户端),所以不存在无法获取DHCP问题。
三、后话
WSL2(NAT)的底层网络机制和KVM(NAT)一致,不采用代理模式,主机上netstat看不到虚拟机和外部设备的TCP连接,NAT在主机层面既不修改或重新协商虚拟机发出TCP的MSS,也不对数据包进行拆分合并操作。理论上KVM(NAT)在虚拟机和主机外出网卡的MTU不匹配时,也会出现同样的TCP连接问题,模拟测试(不使用MSS clamping并禁用本地PMTU)证实了这一推理,但由于KVM(NAT)使用Linux主机iptables实现NAT功能,MSS clamping机制已成为标配,因而基本对用户无感,无需像WSL2(NAT)那样只能在虚拟机网卡层面进行MTU调优。
顺便提一下,最新版WSL2新增了mirrored模式,使用和主机完全相同的IP地址,外部网络访问WSL2(mirrored)不再需要另行配置NAT映射,相较bridged模式,主机也无需额外安装Hyper-v功能。主机VPN网卡在WSL2内部被视为另一块网卡,MTU直接继承自主机VPN,压根没有前面那些破事。但经初步使用,发现mirrored模式存在如下不足:
1、在WSL2(mirrored)上使用tcpdump捕获进来的UDP包时,如WSL2没有事先打开相应的UDP接收端口,主机根本不会把外部网络发来的UDP包镜像到虚拟机,你将会一无所获。同理,如果WSL2(mirrored)内部又嵌套了KVM虚拟机,从WSL2外部也无法通过iptables DNAT到KVM虚拟机,除非同时在WSL2上使用socat转发模式打开相应端口(此时从WSL2外部访问KVM虚拟机,实际起作用的仍是DNAT而非socat)。
2、对于TCP/UDP协议来说,WSL2(mirrored)内部的127.0.0.1地址始终指向Win11主机localhost,而非直接指向WSL2自身localhost。要从WSL2内部访问自身,使用localhost或127.0.0.1时需要经过Win11主机,建议使用127.0.0.2,可显著提高内部传输性能。
3、WSL2官方内核CONFIG_IP_MULTICAST is not set,不清楚WSL2(mirrored)为何号称支持multicast,而且由于WSL2镜像机制的特殊性,反而导致WSL2(mirrored)和Windows主机之间无法进行组播通信(详见《WSL2和multicast》)。
4、WSL2(mirrored)无法监控不经由系统调用的内核sockets创建,因此,内核态udp隧道如wireguard、vxlan等都无法工作(类似不足1,无法接收UDP包)。经过验证,可以借助用户态socket工具(如iperf -u -s -p port)重新bind一下内核态隧道已经打开的UDP接收端口,虽然会报地址已被使用的错误,但此后上述内核态udp隧道就能正常工作,也算是一种临时解决方案吧。

2365

被折叠的 条评论
为什么被折叠?



