Wednesday, 23 April 2008

Adjusting MTU to improve ADSL performance

So here I am in China, being driven mad by the number of dropped packets I'm seeing. Mostly it's not too bad until I make a VPN connection and then I'm seeing somewhere in the neighbourhood of 10% packet loss on a ping.

Well, after much, much debugging and internet searching I think I've finally found my problem: the MTU setting. MTU is the Maximum Transmission Unit or maximum size of an individual frame being sent on a network. Ethernet has an MTU of 1500, which is usually set as the default value for connections (at least in MS Windows). But other network equipment along your route may not be using Ethernet and may have a lower value. This is ok in theory; the equipment will break up larger frames into smaller ones and combine smaller frames into larger ones as the data is transferred between different networks.

The problem usually comes when your ISP's equipment is using an MTU slightly lower than your value. Apparently PPPoE connections always have a lower MTU than Ethernet (usually 1492) but Windows XP already accounts for that by reducing the MTU for PPPoE connections to 1480. In my case, though, China Telecom seems to be using equipment with an MTU of 1398.

What happens is that I send out 1480 bytes of data and China Telecom sends the first 1398 bytes and then holds onto the remaining 82 bytes until it gets some more data from me. Let's say I send another 1480 bytes. Now the first 1316 bytes of that frame are added to the 82 bytes from my first frame and sent and the remaining 164 bytes are held onto. This is slightly inefficient and can make the connection seem "bursty" because data is being held up before being sent. It also tends to get a lot worse if your connection is flaky (which mine is); I think this is because a lost frame along the way may hold part of the data for 2 or more of my original frames, all of which now need to be retransmitted. VPNs (because of the way TCP packets are being embedded into TCP packets) apparently amplify these effects further.

So how do we solve it?

Finding the Correct MTU

First, you need to find out what MTU setting your ISP is using. This MS KnowledgeBase article describes how to do this. Basically, you pick a host as close as possible to you on your routing path and run the following command:

ping -f -l size

The -f marks the packet as non-fragmentable and the -l size parameter specifies the number of bytes to include in the ping packet.

Start with a value of 1472 for size. If the ping is successful, your ISP's MTU is 1500. If you get the error Packet needs to be fragmented but DF set then decrease the value of size and try again. Once you find the highest value that allows a successful ping, that value plus 28 (20 bytes for the IP header and 8 for the ICMP header) is the correct MTU value.


Adjusting the MTU Value in Windows XP

Follow this KnowledgeBase article to set the MTU value for PPPoE (also seems to affect all VPN connections - not sure if there's a way to avoid that). Basically you want to set the following registry keys (all values are DWORD type):

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NdisWan\Parameters\Protocols\0]
ProtocolType = 0x00000800
PPPProtocolType = 0x00000021
ProtocolMTU = 1480

Obviously you would adjust the MTU value as desired and make sure to choose "decimal" when inputting the value. You probably have to reboot.

You may alternatively be ale to specify the MTU for the PPPoE interface here:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{Adapter-id/GUID}]
MTU = 1500

but I haven't tested it yet (again, adjust the MTU as desired and choose "decimal" when entering the value).

UPDATE: I tested setting the MTU for just the one PPPoE interface as above and it seems to work fine. I set the MTU value for NdisWan back to 1480 and will probably test removing that section from the registry altogether to see if it still works.

Other possibly helpful links:
http://www.speedguide.net/read_articles.php?id=157
http://www.speedguide.net/analyzer.php