WSL 2 and VPN - MTU size matters
So you got WSL networking working.. most of the time? Just some connections dropping out? NPM unable to fetch one or two packets? Docker image pull failing for some layers only? Apt-get stalling for some packages?
Congrats! That means you are not affect by or fixed two(VPN connections messing up the route from WSL 2 to the Windows host and WSL 2 configured to auto-resolve nameservers when used with a VPN) other issues I’ve already convered. With those two issues, no connections go through at all. Make sure you fix these first!
Lucky you, however! There is another issue lurking that causes only SOME connections to time out: mismatch of the MTU size on your WSL 2 network interface and the VPN virtual adapter on the Windows host.
This post is all about the MTU issue.
I see the problem on a Paloalto GlobalProtect solution, but other VPN clients have similar issues.
What’s up with MTU??
As you are probably aware, connecting to different online services involves sending packets. All the hops between you and the endpoint you’re trying to reach, have an opinion on the packet size. The size of each packet is not fixed, and there is a lot of smarts happening behind the scene to make sure your high-level operation is divided into packets of an acceptable size for everyone. You want the size as large as possible to maximize throughput. The maximum packet size acceptable for a route from a to be b is commonly referred to as the maximum transmission unit - MTU.
The auto-magic to calculate it is called Path MTU Discovery. In short, the process involves setting a special flag on a packet. Any part of the route that objects to the size notifies the originator with this flag set. The originator can then reduce the packet size until everyone finds it acceptable. In most scenarios, this just works(TM). And when it does not, there is a backup. If you can’t build agreement on the MTU size, individual steps on the route can simply fragment the packet into smaller packets before sending them on. Great!
Alright, what’s not to like? Surely all the VPN solutions work fine with this mechanism? Well.. wrong! That would be too easy.
Instead, some VPN virtual adapters work as a black hole when they receive packets too large. They don’t participate well in MTU Path Discovery, and they drop packets instead of fragmenting them. This is an issue with WSL 2 and such a VPN. The eth0 adapter in WSL 2 has a default MTU of 1500. You can check this quickly with ip addr | grep mtu
.
The packets route through the WSL virtual network adapter on the Windows host. In my case, it has an MTU of 1400.
With Path MTU discovery not working and fragmentation not happening, all the packets get dropped when they reach the VPN adapter on the Windows host.
We can see this happen with some simple tests. An IP packet has a header of 20 bytes, and the ICMP overhead in a ping packet adds another 8, a total of 28 bytes in headers. Let’s see what happens if we send a ping with a payload of 1372 bytes on the Windows host. This would make the total packet size 1400, which is the max of the VPN virtual adapter. I’m using ping 8.8.8.8 -f -l 1372
for this. -f
means not to fragment the packet, while l
sets the size.
Next, let’s do ping 8.8.8.8 -f -l 1373
. 1373 will make the total package size 1401, one more than the max MTU. This should fail. And it does, giving a helpful error message that the IP package needs to be fragmented.
Okay.. let’s fragment it with ping 8.8.8.8 -l 1373
(notice no -f
). It should work as the VPN adapter should just fragment the package and let us all move on with our day.
But.. it does not! The VPN simply drops all the requests. Now this is a real problem. The default WSL 2 MTU is 1500, which is guaranteed to result in packets larger than 1400 to the VPN adapter at some point.
Alright, what’s the fix?
Not too complicated: adjust the MTU of the eth0 adapter in WSL 2 to 1400 or below.
Like this:
sudo ifconfig eth0 mtu 1400 up
I’ve seen reports of people having to set it lower for specific VPN providers, so your mileage may vary. Please set it to something low like 1000 to be golden in all scenarios.
Make it stick
Unfortunately, the MTU will reset to 1500 at the restart of your Linux kernel. Making the change stick can be tricky.
How to change MTU size in Linux lists all the gory details for different distros. However, the familiar places to configure network settings or run startup commands do not always apply under WSL. Probably because of behind-the-scenes magic to make the distros work with WSL. Your mileage may vary depending on the Linux distro.
A solid solution, if you are running WSL on Windows 11 or Windows Server 2022, is to change the MTU in /etc/wsl.conf. It will set the correct MTU value whenever you start a new instance of a distro. Update /etc/wsl.conf with the snippet below. Do it for each distro; it is not shared between them. Use “the 8-second rule” to make sure WSL picks up your configuration changes.
[boot]
command = /sbin/ifconfig eth0 mtu 1400 #or some lower value here
Super hat tip to Michał Sacharewicz for the suggestion to use /etc/wsl.conf to make this stick!
Recap
WSL 2 and VPN are a frustrating combo. They don’t play nice with each other if their MTU size differs. Fix it by adjusting the MTU for the eth0 adapter inside WSL 2.