A word about AutoTuningLevel - TCP Receive Window Auto-Tuning Level explained

Hello Everyone!
This is Dante again, and today I have a new note for you that hope will help you keep understanding how Windows Networking works.
For today's note, we will talk a bit about how Windows handle the TCP Receive Window size since Windows Server 2008 / Windows Vista.
In previous releases, the TCP Receive Window size for every network communication in Windows had a statically assigned value for the TCP Receive Window size, se we could define this parameter to improve our communications in fast links (but that could cause severe isssues in not that fast links). That behavior is better explained in this article.
With newer versions, that is no more possible, since we introduced a new feature called TCP Receive Window Auto Tunning Level. That sounds cool, right?

But, how does it work?

In newer versions of Windows, the TCP Receive Window Auto Tuning Level (from now on "the autotuninglevel"), has the ability to negotiate a defined Receive Window size for every TCP communication, which is being done at the TCP Handshake.
This feature also makes full use of other features for improving network performance, such the rest of the TCP Options defined in RFC 1323. This enables the Windows machine to negotiate Window sizes which are "smaller" but scaled at a defined value, depending on the configuration, which makes it easier to be handled by several networking devices.

That sounds great, but how can we configure that?

The first thing I would recommend is to verify the current configuration to be familiar with this feature. To know how is it configured you should open an elevated command prompt and run the following command:

netsh interface tcp show global

Usually, after running that command, you will see something similar to this:

Querying active state...TCP Global Parameters ---------------------------------------------- Receive-Side Scaling State : enabled Chimney Offload State : disabled Receive Window Auto-Tuning Level : normal Add-On Congestion Control Provider : default ECN Capability : disabled RFC 1323 Timestamps : disabled Initial RTO : 3000 Receive Segment Coalescing State : enabled Non Sack Rtt Resiliency : disabled Max SYN Retransmissions : 2 Fast Open : enabled Fast Open Fallback : enabled Pacing Profile : off

As you can see, the default status is always normal. But that's not the only state it can have. If we look at the help for the command, we have the following available status:

autotuninglevel - One of the following values: disabled: Fix the receive window at its default value. highlyrestricted: Allow the receive window to grow beyond its default value, but do so very conservatively. restricted: Allow the receive window to grow beyond its default value, but limit such growth in some scenarios. normal: Allow the receive window to grow to accommodate almost all scenarios. experimental: Allow the receive window to grow to accommodate extreme scenarios.

These values can be setup using the following command:

netsh interface tcp set global autotuninglevel=<Insert value here>

Awesome, but how can we confirm that is actually working?

As usual, the best way to confirm something in Networking is actually working is by taking network captures. To begin with the network captures, we first need to understand how Windows determine the default TCP Receive Window size, which will be the base for our AutoTuningLevel negotiation.
As stated in the book Windows Server 2008 TCP/IP Protocols and Services by Joseph Davies (Amazing book for anyone that wants to understand better Windows TCP Stack, Thanks for the magic Joseph!), we know that TCP Receive Window size is determined by 2 factors:

  • How the application is defined for the TCP Receive Window size (some applications tell the stack the amount of data it should use)
  • If the above is not present, a default size determined by link speed.

In the case of the second factor, these are the values explained by Joe:

When Receive Window Auto-Tuning is disabled, the default advertised TCP receive window size is either specified by the application or is automatically determined based on the bit rate of the media as reported by the network adapter, according to the following rules: Below 1 megabits per second (Mbps): 8 kilobytes (KB)<br>1 Mbps to below 100 Mbps: 17 KB<br>100 Mbps to below 10 gigabits per second (Gbps): 64 KB<br>10 Gbps or higher: 128 KB

In my case, my Ethernet NIC is 1gbps, so it should match the third item: 100mbps to below 10gbps.

Being that said, this is the output for the first packet of the TCP Handshake, with each of the available configurations for the autotuninglevel. 192.169.0.5 is my machine, while 192.169.0.4 is my Linux based NAS I'm trying to connect to:

Restricted: Frame: Number = 3, Captured Frame Length = 66, MediaType = ETHERNET + Ethernet: Etype = Internet IP (IPv4),DestinationAddress:[D8-FE-E3-65-F3-FD],SourceAddress:[C8-5B-76-7D-FA-7F] + Ipv4: Src = 192.169.0.5, Dest = 192.169.0.4, Next Protocol = TCP, Packet ID = 2319, Total IP Length = 52 - Tcp: [Bad CheckSum]Flags=......S., SrcPort=60890, DstPort=Microsoft-DS(445), PayloadLen=0, Seq=1966088568, Ack=0, Win=64240 ( Negotiating scale factor 0x4 ) = 64240 SrcPort: 60890 DstPort: Microsoft-DS(445) SequenceNumber: 1966088568 (0x75302178) AcknowledgementNumber: 0 (0x0) + DataOffset: 128 (0x80) + Flags: ......S. ---------------------------------------------------------> SYN Flag set Window: 64240 ( Negotiating scale factor 0x4 ) = 64240 ---------> TCP Receive Window set as 64K as per my Link bitrate. Note it shows the 0x4 Scale Factor. Checksum: 0x8182, Bad UrgentPointer: 0 (0x0) - TCPOptions: + MaxSegmentSize: 1 + NoOption: + WindowsScaleFactor: ShiftCount: 4 -------------------------------> Scale factor, defined by AutoTuningLevel. + NoOption: + NoOption: + SACKPermitted:

Highly Restricted: Frame: Number = 115, Captured Frame Length = 66, MediaType = ETHERNET + Ethernet: Etype = Internet IP (IPv4),DestinationAddress:[D8-FE-E3-65-F3-FD],SourceAddress:[C8-5B-76-7D-FA-7F] + Ipv4: Src = 192.169.0.5, Dest = 192.169.0.4, Next Protocol = TCP, Packet ID = 2388, Total IP Length = 52 - Tcp: [Bad CheckSum]Flags=......S., SrcPort=60903, DstPort=Microsoft-DS(445), PayloadLen=0, Seq=1463725706, Ack=0, Win=64240 ( Negotiating scale factor 0x2 ) = 64240 SrcPort: 60903 DstPort: Microsoft-DS(445) SequenceNumber: 1463725706 (0x573EAE8A) AcknowledgementNumber: 0 (0x0) + DataOffset: 128 (0x80) + Flags: ......S. ---------------------------------------------------------> SYN Flag set Window: 64240 ( Negotiating scale factor 0x2 ) = 64240 ---------> TCP Receive Window set as 64K as per my Link bitrate. Note it shows the 0x2 Scale Factor. Checksum: 0x8182, Bad UrgentPointer: 0 (0x0) - TCPOptions: + MaxSegmentSize: 1 + NoOption: + WindowsScaleFactor: ShiftCount: 2 ------------------------------> Scale factor, defined by AutoTuningLevel + NoOption: + NoOption: + SACKPermitted:

Experimental Frame: Number = 238, Captured Frame Length = 66, MediaType = ETHERNET + Ethernet: Etype = Internet IP (IPv4),DestinationAddress:[D8-FE-E3-65-F3-FD],SourceAddress:[C8-5B-76-7D-FA-7F] + Ipv4: Src = 192.169.0.5, Dest = 192.169.0.4, Next Protocol = TCP, Packet ID = 2490, Total IP Length = 52 - Tcp: [Bad CheckSum]Flags=......S., SrcPort=60933, DstPort=Microsoft-DS(445), PayloadLen=0, Seq=2095111365, Ack=0, Win=64240 ( Negotiating scale factor 0xe ) = 64240 SrcPort: 60933 DstPort: Microsoft-DS(445) SequenceNumber: 2095111365 (0x7CE0DCC5) AcknowledgementNumber: 0 (0x0) + DataOffset: 128 (0x80) + Flags: ......S. ---------------------------------------------------------> SYN Flag set Window: 64240 ( Negotiating scale factor 0xe ) = 64240 ---------> TCP Receive Window set as 64K as per my Link bitrate. Note it shows the 0xe Scale Factor. Checksum: 0x8182, Bad UrgentPointer: 0 (0x0) - TCPOptions: + MaxSegmentSize: 1 + NoOption: + WindowsScaleFactor: ShiftCount: 14 -----------------------------> Scale factor, defined by AutoTuningLevel + NoOption: + NoOption: + SACKPermitted:

Disabled Frame: Number = 353, Captured Frame Length = 62, MediaType = ETHERNET + Ethernet: Etype = Internet IP (IPv4),DestinationAddress:[D8-FE-E3-65-F3-FD],SourceAddress:[C8-5B-76-7D-FA-7F] + Ipv4: Src = 192.169.0.5, Dest = 192.169.0.4, Next Protocol = TCP, Packet ID = 2576, Total IP Length = 48 - Tcp: [Bad CheckSum]Flags=......S., SrcPort=60956, DstPort=Microsoft-DS(445), PayloadLen=0, Seq=2315885330, Ack=0, Win=64240 ( ) = 64240 SrcPort: 60956 DstPort: Microsoft-DS(445) SequenceNumber: 2315885330 (0x8A099B12) AcknowledgementNumber: 0 (0x0) + DataOffset: 112 (0x70) + Flags: ......S. ---------------------------------------------------------> SYN Flag set Window: 64240 ( ) = 64240 ----------------------------------------> TCP Receive Window set as 64K as per my Link bitrate. Note there is no Scale Factor defined. In this case, Scale factor is not being sent as a TCP Option, so it will not be used by Windows. Checksum: 0x817E, Bad UrgentPointer: 0 (0x0) - TCPOptions: + MaxSegmentSize: 1 + NoOption: + NoOption: + SACKPermitted:

Normal (Default state) Frame: Number = 492, Captured Frame Length = 66, MediaType = ETHERNET + Ethernet: Etype = Internet IP (IPv4),DestinationAddress:[D8-FE-E3-65-F3-FD],SourceAddress:[C8-5B-76-7D-FA-7F] + Ipv4: Src = 192.169.0.5, Dest = 192.169.0.4, Next Protocol = TCP, Packet ID = 2667, Total IP Length = 52 - Tcp: [Bad CheckSum]Flags=......S., SrcPort=60975, DstPort=Microsoft-DS(445), PayloadLen=0, Seq=4075590425, Ack=0, Win=64240 ( Negotiating scale factor 0x8 ) = 64240 SrcPort: 60975 DstPort: Microsoft-DS(445) SequenceNumber: 4075590425 (0xF2EC9319) AcknowledgementNumber: 0 (0x0) + DataOffset: 128 (0x80) + Flags: ......S. ---------------------------------------------------------> SYN Flag set Window: 64240 ( Negotiating scale factor 0x8 ) = 64240 ---------> TCP Receive Window set as 64K as per my Link bitrate. Note it shows the 0x8 Scale Factor. Checksum: 0x8182, Bad UrgentPointer: 0 (0x0) - TCPOptions: + MaxSegmentSize: 1 + NoOption: + WindowsScaleFactor: ShiftCount: 8 -----------------------------> Scale factor, defined by AutoTuningLevel + NoOption: + NoOption: + SACKPermitted:

So, as summary, the different states for AutoTuningLevel are as follows:

Normal (Default): 0x8 (Scale Factor of 8) Restricted: 0x4 (Scale Factor of 4) Highly Restricted: 0x2 (Scale Factor of 2) Disable: No scale factor available Experimental: 0xE (Scale Factor of 14)

Thanks a lot for this explanation, but I still have issues with the communication over my WAN link, what can I do?

In that case, it's possible you are behind a networking device that is not compliant with TCP Window Scale Option defined on RFC 1323, and thus it doesn't support the Scale Factor.
In such cases, we encourage you to check the following KB or contact the support team of your networking device's vendor.

That's all for today, hope you enjoyed the note and helped you get a better understanding on how Windows works!

If you still have further questions about this, please leave your comment and I will get back to you as soon as I can.

Have a great day everyone!

Dante