OpenVPN TAP adapter MTU in Windows
Recently I was in need of setting up some windows clients to connect to my OpenVPN server. This server running on Linux, uses a specific MTU value (let’s say 1400) to ensure maximum compatibility with different clients over different links.
In addition to the OpenVPN process itself, the kernel must also know about the correct MTU so packet size could be adjusted before reaching the tun/tap interface.
This is very easy to do in Linux. In fact you most likely do not need to do anything at all. OpenVPN will adjusted the MTU of the tun/tap interface while creating it. You can check the interfaces effective MTU by using ip link show
or ifconfig
command.
The same however can not be said about Windows. In a typical scenario, OpenVPN is not even directly responsible for creating the said interface. Instead, it requires the interface to be already in placed (which is achieved by calling tapinstall.exe
during the initial setup) and then it would connect to it.
So even though you have specified your MTU settings in the OpenVPN profile, at least at the time of writing, this does not reflect the MTU of the interface that Windows kernel would see.
The problem? The kernel could send more bytes than what’s been allowed in the OpenVPN config file and OpenVPN process while giving a scary error, ends up chocking on it:
read from TUN/TAP : More data is available. (code=234)
tun packet too large on write (tried=1500,max=1400)
Adjusting TAP MTU in Windows
Luckily (or so it seems so at first) windows TAP driver comes with a setting to easily adjust its MTU.
Here’s the required steps to do so:
- Open Start Menu, search for
Device Manager
and run it. - Under
Network adapters
double click onTAP-Windows Adapter V9
. - under the
Advanced
tab, theMTU
property can be adjusted.
Yay. Lets set the value to 1400, establish the tunnel and ensure that it’s up:
ping 192.168.30.1
Pinging 192.168.30.1 with 32 bytes of data:
Reply from 192.168.30.1: bytes=32 time=39ms TTL=58
Reply from 192.168.30.1: bytes=32 time=38ms TTL=58
Reply from 192.168.30.1: bytes=32 time=39ms TTL=58
Reply from 192.168.30.1: bytes=32 time=40ms TTL=58
Alright, Now lets send a packet larger than 1400 bytes and see what happens:
ping -l 2000 192.168.30.1
Pinging 192.168.30.1 with 2000 bytes of data:
General failure.
General failure.
General failure.
General failure.
Ooookay…
This error usually happens when there are no usable network interfaces or routes. It could also happen as the result of an internal network issue. In other words, this error happens before our packets even get a chance to go out.
Why this happens?
The reason behind it is not exactly clear to me and possibly requires reading the TAP driver source code to see how the set MTU is being handled.
My guess however is that this MTU setting simply tells the driver to reject the packets bigger than the specified value 1. When you change it to something lower than 1500 (which is the default for network adapters), windows network stack still has no idea the MTU of the interface is changed. The driver responsible for the TAP virtual interface however, rejects bigger packets right away. This results in an effect similar to a link with broken PMTUD.
Properly Adjusting TAP MTU in Windows
Windows provides us with a way to properly adjust the interfaces MTU. The correct way to so is via netsh
interface and works from windows vista upward.
-
Open
cmd
as administrator -
To see the effective interfaces MTU for IPv4 traffic:
netsh interface ipv4 show subinterface
MTU MediaSenseState Bytes In Bytes Out Interface
------ --------------- --------- --------- -------------
1500 1 541911 241226 Ethernet
4294967295 1 0 19314 Loopback...
1500 5 0 0 Ethernet 2
In my case, “Ethernet 2” is the TAP interface.
- Now to change its MTU:
netsh interface ipv4 set subinterface "Ethernet 2" mtu=1400
Ok.
- Checking the result:
netsh interface ipv4 show subinterface
MTU MediaSenseState Bytes In Bytes Out Interface
------ --------------- --------- --------- -------------
1500 1 545131 241226 Ethernet
4294967295 1 0 19314 Loopback...
1400 5 0 0 Ethernet 2
Alright, Lets re-establish our tunnel and try again:
ping -l 2000 192.168.30.1
Pinging 192.168.30.1 with 2000 bytes of data:
Reply from 192.168.30.1: bytes=2000 time=53ms TTL=58
Reply from 192.168.30.1: bytes=2000 time=56ms TTL=58
Reply from 192.168.30.1: bytes=2000 time=54ms TTL=58
Reply from 192.168.30.1: bytes=2000 time=52ms TTL=58
Inspecting the packets with Wireshark also confirms proper fragmentation of them.
If you also use IPv6, the same procedure for it is likely required (replace ipv4
with ipv6
in the above commands).
Kaspersky products and MTU
Update: Thanks to the Kaspersky, this now has gotten complex enough to earn its own dedicated post.
If you’re running Kaspersky products, some extra steps are needed which are provided in This Post.
-
Actually, the limit seems to be MTU+4. The extra 4 bytes might be reserved for VLAN ID ↩︎