8 min read
Our series exploring TCP continues with a look at TCP Timestamps and their use within high bandwidth networks.
As always, we've gathered up the captures mentioned in this article into this collection over on CloudShark
Did you know you can make collections just like this with CloudShark too? Learn more...
You’ve probably seen the TCP Timestamp fields before, even without looking for them. In captures taken on a modern OS, the “Info” column will usually include information from the fields TSval and TSecr. Even though that second one looks like it might have something to do with “secrets”, it really refers to “echo reply” and is just straightforward timestamp information sent back and forth for both hosts.
The TCP Timestamp option originally came from RFC 1323 titled “TCP Extensions for High Performance” - this is the same RFC that defined Window Scale. This was replaced in 2014 by RFC 7323. As you may have guessed from the RFC’s name, this doc defines options that help TCP function more reliably in high bandwidth networks today.
The Timestamp option can be used to measure the round-trip time (RTT) of every packet that is acknowledged. This is done by including a Timestamp Value TSval in every segment that is sent. These TSval values are echoed by the opposite side of the connection in the Timestamp Echo Reply TSecr field. So, when a segment is ACKed, the sender of that segment can simply subtract their current timestamp from the TSecr value to compute an accurate Round Trip Time (RTT) measurement.
An accurate calculation of the RTT is important because it helps inform what the Retransmission Timeout (RTO) value should be. The RTO is responsible for retransmitting packets that have been unacknowledged after a certain amount of time.
Here’s the ASCII art option definition from the RFC:
+-------+-------+---------------------+---------------------+ |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)| +-------+-------+---------------------+---------------------+ 1 1 4 4
It’s much easier to look at it in an actual packet however:
https://www.cloudshark.org/captures/7e751e01085a?filter=frame.number==1
Expand the TCP Options in the above packet and check out the Timestamp values. This option is implemented in an option field that carries two four-byte timestamp fields: TSVal TSval and TSEchoReply TSecr.
The two endpoints put their own individual timestamp value in the TSVal field, and then echo back and forth the timestamp that it received from the other one.
If you’re interested in reading more about specific implementation details, like when internal timers are updated and how to handle retransmissions, the RFC does a good job explaining these.
The TCP Timestamp Option also helps Protect Against Wrapped Sequence numbers which can happen while transferring very large streams of data. The sequence number field in TCP is only 32-bits, so after 2^32 bytes have been transmitted, the sequence number wraps around back to the beginning.
With large enough windows, it is possible for a duplicate packet to arrive after the sequence numbers have wrapped around. In this case it is hard for TCP to know which segment in the overall stream the particular sequence number represents. Since the Timestamp fields are monotonic-increasing, they can be used to determine if a received packet is in the right place and reject old duplicate segments that might corrupt an open TCP connection.
The RFC goes into more detail.
Because of the bandwidth required, we haven’t been able to get a capture of this one in our lab, but we will keep trying! If you have an example of sequence number wrapping, upload it to CloudShark and send the link our way.
Let’s take a look at some packets in CloudShark with the Timestamp Option enabled. Here is the SYN from a handshake proposing the option:
https://www.cloudshark.org/captures/7e751e01085a?filter=frame.number==1
Just a few packets into the stream, we can take a look at the values of TSval and TSecr. Here is packet #5 transmitting some data:
https://www.cloudshark.org/captures/7e751e01085a?filter=frame.number==5
Now let’s look at packet #8 which contains the ACK for Frame 5:
https://www.cloudshark.org/captures/7e751e01085a?filter=frame.number==8
Expand the TSval and TSecr timestamps for both packets and let’s compare them. You see in frame 5 the TSval is 532122417. In Frame 8, this value is echoed back in the TSecr field.
Now let’s jump ahead and compare this to a packet later in this stream:
https://www.cloudshark.org/captures/7e751e01085a?filter=frame.number==428
This packet was captured 262 milliseconds after packet 5 shown above. Comparing the TCP timestamp values we see that this packet’s timestamp is 249 ticks later. These values are close, but they don’t exactly match the time delta seen in the capture. The reason is because the TSval gets set by the transmitter of the packet, and we are comparing it to the time we captured it off the wire. Also consider that different TCP implementations increment their internal clocks at different rates - some by 100 every second, and others by 1000. (If you want dive more into it, you can read part of the RFC about the Timestamp Clock)
The transmitter will keep incrementing this internal clock, and when the timestamp gets echoed back it can compare the two to get an estimate of the RTT. There are some cases to keep in mind though. What if neither side of the TCP session had anything to send for 60 seconds? It certainly shouldn’t look at the next timestamp echoed back and include that when it updates the RTT estimate! Thats why RFC 7323 states that only packets that acknowledge new data should be used for this.
As you can see, it’s a pretty simple mechanism, but effective at allowing each endpoint to keep tabs on the Round Trip Time of the connection.
Above are two simple examples of how Timestamp values can help a TCP connection, but it can also hurt if not done correctly! Here’s one unusual situation:
Here the client initiates a TCP conversation by sending its initial SYN packet. The server responds with the expected SYNACK. But then the client resets the TCP conversation! Ouch! That server is probably wondering why the client threw a reset in its face when it started the conversation in the first place.
If you look closer at what the server sent in it’s SYNACK though you’ll see in the TCP Timestamp option what it put in the reply field is not what the client sent. This causes the initiator to reset the connection!
It used to be that TCP Timestamps could be used to guess how long a system had been up for and the popular network profiling tool Nmap added an uptime guess to it’s OS-fingerprinting option. Imagine the situation where the TCP clock starts at zero on reboot. When a client connects to the host, it sees the value sent in TSval. A subsequent connection would make it possible to extrapolate the clock frequency, and from there, some simple math to determine when the timer started! The clock frequency may also help with OS fingerprinting.
Today however, the TCP timer is typically initialized to a value other than zero at boot time which reduces the effectiveness of this potential fingerprinting technique.
We’ve curated this collection of 3 captures to illustrate this point.
While researching this article, we came up with some additional scenarios that we want to dig into a little more before talking about them. We plan on publishing more about this in the future. Sign up for our newsletter to be notified when we publish a new article.
Digging a little bit deeper we see that there is a lot going on at the transport layer! The TCP Timestamp option is another feature that is keeping TCP going strong. With improved RTT calculation and protection against wrapped sequence numbers in big windows, TCP continues to evolve and improve.
Upload a file to CloudShark yourself and take a look at the TCP Options that are sent in the Handshake. Chances are they’ll be in there.
Got a TCP Option or behavior you want us to cover? Contact us and let us know!
Want articles like this delivered right to your inbox?
No spam, just good networking