TCP specification notes
This page provides some guidance on how to interpret the TCP specifications for our project.
In general, we will refer you to various IETF RFCs (mostly RFC9293), which provide the de-facto standard for TCP real implementations.
Why we do this? We want you to gain practice reading real network specifications so that you can take on specifications for other protocols outside this class. As a developer, this is a useful skill to have!
Opening the RFC right now may seem overwhelming at first–it is normal to feel this way. Take a deep breath. You have all the tools you need to help learn what everything means. This guide is here to help you navigate it, and to tell you what parts are required for the project.
Moreover, you don’t need to implement everything in the RFC. To reduce the complexity and operate in our virtual (localhost-only) network, we have made a few modifications to the standard and removed some components: as a result, your TCP will be a minimal version of a real TCP standard, with a few modifications to simplify certain cases. In particular, we remove a lot of error handling cases that aren’t required in our environment, and add little learning value on top of what you are already building.
So let’s break it down: the major components you will need are:
- Connection setup and state management (ie, implementing the TCP state machine)
- Building and sending TCP packets
- The sliding window protocol, which governs how to send and receive data
- Retransmissions for segments that time out, governed by an estimate for the round-trip-time to the destination (SRTT)
- Connection termination
The remainder of this page will describe these major components and outline how to think about them for the project. For the specification details, you’ll need to refer to the RFCs.
Note: We’re still trying to figure out the best way to organize this content, and we may update the docs as the project is out. To make sure you’re on the latest version, please refresh this page for updates!
Also, if you have questions, please feel free to ask! We are here to help as you learn how to read the standards.
Thanks for your patience and understanding as we work to provide resources for you.
Background: Socket API
The specifications here define what should happen inside your TCP stack.
In our project, you will create and interact with sockets using your Socket API. The specifications here will help you determine what your socket API should do, and how the internals of your TCP stack decide how to send/receive packets. If you’re just getting started, it may help to start thinking about how you’ll implement the initial parts of the Socket API, and then referring to the RFCs as you need them for the implementation details.
TCP State machine
You will implement a state machine that keeps track of the fundamental states of each connection. Here is one representation of the state diagram (courtesy of Wikipedia):
What do the labels on the arrows mean?
State transitions are labeled as
Event/Action
, whereEvent
is what triggers the state transition, andAction
indicates a packet that gets sent in response. For example, in theSYN_SENT
state, the transitionSYN+ACK/ACK
indicates “When aSYN+ACK
is received, send anACK
and go to theESTABLISHED
state.”
Events
in bold indicate an operation performed by the user (ie, the socket API) using the event terms listed in the RFC (see Section 3.9.1). For example, in theSYN_RECEIVED
state, the transitionCLOSE/FIN
means, “when the user performed aCLOSE
, send aFIN
and go to theFIN_WAIT_1
state”.
This state machine diagram might look like a lot right now—but don’t worry: the most important functionality isn’t as complicated as it may seem at first. To begin, we recommend that you start coding by just using the diagram and getting connections to set up and close under ideal conditions.
For the purposes of the assignment, we are asking you to implement a minimal form of TCP that implements most of the features that the RFC considers, with a few exceptions to help reduce the complexity.
In general, you are expected to follow the state machine and features described by RFC9293, except for the parts of referring to:
- Simultaneous OPEN
- Silly window syndrome (SWS) avoidance
PSH
flags- Urgent data (
URG
flags) - Any TCP options
- Precedence
- Congestion control (except capstone students)
- Security considerations
- Simultaneous CLOSE
- Edge cases involving
RST
packets. For a normal-type socket, receiving anRST
packetSHOULD
immediately terminate the connection. All otherRST
-related cases can be ignored.
Handling edge cases
There are several places in the RFCs that leave room for flexibility in implementation. We extend the same flexibility to your projects, as long as you can justify your design decisions (in a README
). In general, you can ignore any features you don’t support (eg. URG
or PSH
flags), but you shouldn’t crash if you encounter a packet that uses these flags.
Once you have the rest of your implementation working, there are some edge cases to consider: for example, what happens when, after a call to connect
, you’ve sent a SYN, but you receive a packet that has an incorrect ACK in it? Once your basic state diagram is working, we recommend that you look at the RFC for answers to questions such as these.
In particular, Section 3.10 of RFC9293 contains info on exactly what you should do in such scenarios for each state.
Not sure if something is required? Ask on Edstem! We are making an effort to expand documentation here, so questions are encouraged!
Building and sending TCP packets
Relevant RFCs: RFC9293 Sec 3,1, RFC1122 Sec 4.2.3.1
You MUST
use the standard TCP header for all TCP packets. However, as with the IP project, you don’t need to build/parse the TCP header yourself: you are welcome to use a library serialize/deserialize the TCP header for you. Specifically:
- The TCP-in-IP example provides a demo of how to parse the TCP header and compute the checksum using Google’s netstack library, similar to what we used for the IP checksum. We highly recommend looking here for more info!
- In C, you should use the header found in
netinet/tcp.h
(demo here).
Additional notes
- Selecting initial sequence numbers: RFC 9293 Sec 3.4.1 suggests a timer-based procedure for selecting an initial sequence number (ISN). For our project, you can just select a random 32-bit integer.
- TCP checksum: TCP uses a “pseudo-header” in its checksum calculation–see the TCP-in-IP example for details on how to complete the checksum. Section 3.1 of RFC9293 or this link may be helpful.
- As in the IP assignment, never send packets greater than the MTU. For our link layer, the maximum MTU is 1400 bytes: any TCP segments you send must be no larger than the MTU–therefore, the maximum TCP payload size is:
1400 bytes - (size of IP header) - (size of TCP header)
- You don’t have to handle any TCP options. You should ignore any options that you see in incoming packets, but your program shouldn’t crash if you encounter them.
Sliding Window Protocol
Relevant RFCs: RFC 9293 Sec 3.3, 3.4, 3.10.7; RFC1122 Sec 4.2.2.16,17,20,21
Your sliding window protocol controls how you send and receive data—this is the “heart” of your TCP stack. Some things to keep in mind:
- You
MAY
use external libraries that provide data structures to help with your send and receive buffers (eg. an implementation for a circular buffer/ring buffer). However, you may prefer to build this data structure yourself (or implement something quite different) depending on how you want to handle sequence numbers. - On the receiving side, your implementation must accept out of order packets. For example. your receiver should be able to accept segments that fit within the window but aren’t the next expected segment. The easiest way to handle such packets is to place them on a queue of potentially valid packets, and then deal with them once the window has caught up to the beginning of that segment’s sequence number.
- On the sending side, you
MUST
adhere to the flow control window as specified in the RFC, meaning youMUST NOT
send packets outside of the receiver’s window. Concretely, this means:- Your sender
MUST
keep track of how much data is “in-flight” (ie, sent but not ACKed), and not send more than the receiver’s advertised window - If no data is “in-flight” and the sender’s advertised window reaches zero, your sender
MUST
perform zero-window probing until space is available to continue sending normally
- Your sender
- As you implement the rest of TCP, keep in mind how your sliding window implementation will interact with the rest of your implementation. For example, closing a connection with
VClose
must only send aFIN
after all other data has been sent (in other words, theFIN
MUST
be the last sequence number used in the connection).
Important implementation note: Your sliding window protocol
MUST
be event-driven. In other words, youMUST NOT
use arbitrary sleeps or busy-waiting to handle sending/receiving packets. For example, you might have a thread which takes care of sending out packets for a particular socket. YouMUST NOT
have this thread check whether there is something to be sent every, say, 1 ms–this may unnecessarily wake up the thread and waste a lot of CPU cycles! Instead, your thread should send whenever data is available, or or when a retransmission timer expires. Channels, mutexes, and condition variables are your friends.For details on what this means, see Gearup II.
Retransmissions
Relevant RFCs: RFC9293 Sec 3.8.1 (and the resources it links)
As part of your sliding window protocol, your TCP implementation MUST
retransmit segments using the retransmission timeout (RTO). You should compute RTO by measuring the smoothed round-trip-time SRTT between when you send packets and when you receive an ACK.
The RFC states that a lower bound for your RTO should be 1 second. For us, this is way too long–instead, use 1 millisecond as the lower bound. By a similar principle, you do not need to be overzealous in precisely measuring RTT; it is reasonable to tolerate small processing delays (1-20ms).
About the RFCs
The original standard for TCP (RFC793) was released in 1981, and has been amended and extended over the past four decades by many others. Accordingly, in the past, this project has required sifting through a lot of RFCs.
In August 2022, a new RFC was forged that consolidates four decades of updates into a new document: RFC9293. This should be a definitive source for most information about most parts of your implementation.
Otherwise, this list contains the most important TCP-related RFCs, which are referenced throughout this page:
- ✨✨ RFC9293 ✨✨ is the most definitive source
- RFC 793
- RFC 1122
- RFC 5681
- Beej’s Guide to Network Programming (not an RFC, but a really good reference)