TCP Congestion Control with a Misbehaving Receiver
Clint Riley (clintr@)
Gavilan Galloway (gavilan@)
Much of the Internet was constructed with a benevolent world in mind. The goal was to connect computers together and share information, and as a result, relatively little thought was given to what would happen if there were deliberately malicious users actively causing problems in the network. TCP is no exception to this attitude.
TCP, particularly in its earlier implementations, relies on cooperation between the sender and receiver in order to perform correctly. Without this cooperation, numerous problems such as congestion collapse, unfair sharing of resources, etc. can arise. This paper illustrates three distinct avenues to achieve one result: a greedy receiver causing a sender to quickly, dramatically spike its rate of transmission. We attempt to reproduce the results from one such attack on a modern TCP server.
This attack takes advantage of fast retransmit and fast recovery. Recall that the fast retransmit specification indicates that upon receipt of a triple duplicate ack, the congestion window is set to the slow start threshold (ssthresh) plus 3 * SMSS, which increases the congestion window by the number of segments that have left the network. Further, the congestion window should be increased by SMSS for each additional duplicate ack (under the assumption that these duplicate ACKs represent actual segments leaving the network.
From Savage, et al.:
Upon receiving a data segment, the receiver sends a long stream of acknowledgments for the last sequence number received (at the start of a connection this would be for the SYN segment).
Since there is (currently) no way to determine if an ACK is legitimate or spoofed, the sender is forced to respond in compliance with the TCP specification. As a result, the receiver can essentially dictate the rate of the sender, depending on how many (forged) duplicate ACKs are sent.
The results show a dramatic increase in the arrival of higher sequence numbers, indicating a higher transfer rate (and a circumvention of TCP congestion control). Given that TCP congestion control was implemented to avoid congestion collapse where the Internet grinds to a near halt, the fact that it is possible to trick senders into ignoring it is a bit disturbing, particularly today.
One of the main reasons we chose the duplicate ack spoofing attack to reproduce is its simplicity. It is conceptually very straightforward to understand the attack and why it breaks the specified TCP congestion control. This makes it a very likely candidate for an attacker to choose. Additionally, this attack could arise from a programming error (e.g. not breaking out of a loop properly) that results in a number of bogus, duplicate acks to be sent.
We chose to implement our malicious host using a software TCP stack implemented on top of the Unix raw socket interface (with help from David Buchan’s blog). We needed only a small subset of features from the complete TCP specification, and we found implementing those features from scratch to be easier than configuring publicly available software TCP stacks or editing dense Linux kernel code.
For the target of the attack, we chose a nondescript webserver on the public Internet, owned by the authors.
Our malicious host establishes a TCP connection with the target server, sends a single HTTP request, then ACKs every packet received until the transmission completes and tears down the connection. The number of ACKs sent for each data packet received is configurable. We track the transmission rate of the target server for one, six, and twenty ACKs per data packet. We used Wireshark’s tshark tool to record the receipt of sequence numbers across time.
Results and Analysis
For the two dup-ack experiments, we saw the target server increase its transmit window at a moderately increased rate compared to our trials with the well-behaved host. The receiver did not achieve significantly better performance by duplicating acknowledgements. The moderate increase is likely due to the suggestion in RFC5681 to limit the amount of inflation in the congestion window during fast recovery.
As we hypothesized, the attacks were not as successful as those in the original implementation. The attacks detailed by the original paper have been public knowledge since 1999, giving kernel maintainers well over a decade to patch the vulnerabilities. Indeed, the kernel mentioned in the paper was version 2.2 and kernels that are common today are north of 3.0, several major releases later. The IETF released RFC 5681 in 2009, which proposes a solution to the vulnerability we targeted:
“[RFC 5681] recommends increasing the congestion window based on the number of bytes newly acknowledged in each arriving ACK rather than by a particular constant on each arriving ACK”
The server under attack did not appear to follow this more complete suggestion, but rather the aforementioned limited congestion window inflation during fast recovery. The RFC did not provide a suggestion to the extent of the limitation, so an area for future work would be to run the attack against different TCP implementations.
Similarly, we see RFC 3465 patches the ack-division vulnerability by specifying that the cwnd should be increased based on the number of bytes acknowledged, rather than by the number of packets acknowledged. RFC 3540 standardized the original paper’s suggestion that senders maintain a cumulative nonce used to verify ACKs and prevent optimistic ACKing.
Single ack (correct behavior)
6 acks (5 duplicate acks)
20 acks (19 duplicate acks)
While it turned out that implementing portions of TCP from the ground up using raw sockets was the easiest path we found, it was not a straightforward task. Although the blog post from David Buchan was quite useful, it only outlined rather basic functionality and didn’t have a complete implementation of even the TCP handshake or connection close. We were forced into a cycle where we would implement something, examine the captured packets in wireshark and attempt to debug.
Additionally, using raw sockets in this fashion to implement TCP does not necessarily interact well with the kernel. One major early issue occurred when we would attempt to establish a TCP connection with a web server, only to discover that our host was sending RST packets, thwarting our attempts. To resolve this, we had to install a rule using iptables to drop RST packets destined for the particular host we were communicating with.
To be sure, we gained a much deeper understanding of the finer details of TCP interaction at the lowest of levels.
- Stefan Savage, Neal Cardwell, David Wetherall, and Tom Anderson. 1999. TCP congestion control with a misbehaving receiver. SIGCOMM Comput. Commun. Rev. 29, 5 (October 1999), 71-78. DOI=10.1145/505696.505704 http://doi.acm.org/10.1145/505696.505704
- RFC 2581
- RFC 3390
- RFC 5681
- Lightweight IP
- Linux Kernel
- David Buchan’s blog
The source for our malicious host and instructions for reproducing our results may be obtained by contacting the authors via email.