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:

Don’t do this until you read the rest of this article

  1. Open Start Menu, search for Device Manager and run it.
  2. Under Network adapters double click on TAP-Windows Adapter V9.
  3. under the Advanced tab, the MTU property can be adjusted.

TAP-Windows Adapter V9 MTU

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:

    This setting is persistence across reboot

    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).

Leave the MTU parameter of the “TAP-Windows Adapter V9” which we discussed earlier, to the default of 1500 bytes

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.


  1. Actually, the limit seems to be MTU+4. The extra 4 bytes might be reserved for VLAN ID ^

Related

comments powered by Disqus