If more than one Timestamps option is received before a reply segment is sent, the TCP must choose only one of the TSvals to echo, ignoring the others. To minimize the state kept in the receiver (i.e., the number of unprocessed TSvals), the receiver should be required to retain at most one timestamp in the connection control block.
There are three situations to consider:
Many TCP's acknowledge only every Kth segment out of a group of segments arriving within a short time interval; this policy is known generally as "delayed ACKs". The data-sender TCP must measure the effective RTT, including the additional time due to delayed ACKs, or else it will retransmit unnecessarily. Thus, when delayed ACKs are in use, the receiver should reply with the TSval field from the earliest unacknowledged segment.
The sender will continue sending until the window is filled, and the receiver may be generating ACKs as these out-of-order segments arrive (e.g., to aid "fast retransmit").
The lost segment is probably a sign of congestion, and in that situation the sender should be conservative about retransmission. Furthermore, it is better to overestimate than underestimate the RTT. An ACK for an out-of-order segment should therefore contain the timestamp from the most recent segment that advanced the window.
The same situation occurs if segments are re-ordered by the network.
The segment that fills the hole represents the most recent measurement of the network characteristics. On the other hand, an RTT computed from an earlier segment would probably include the sender's retransmit time-out, badly biasing the sender's average RTT estimate. Thus, the timestamp from the latest segment (which filled the hole) must be echoed.
An algorithm that covers all three cases is described in the following rules for Timestamps option processing on a synchronized connection:
SEG.SEQ <= Last.ACK.sent < SEG.SEQ + SEG.LEN
then the TSval from the segment is copied to TS.Recent; otherwise, the TSval is ignored.
The following examples illustrate these rules. Here A, B, C... represent data segments occupying successive blocks of sequence numbers, and ACK(A),... represent the corresponding acknowledgment segments. Note that ACK(A) has the same sequence number as B. We show only one direction of timestamp echoing, for clarity.
By Case (1), the timestamp from the oldest unacknowledged segment is echoed.
TS.Recent <A, TSval=1> -------------------> 1 <B, TSval=2> -------------------> 1 <C, TSval=3> -------------------> 1 <---- <ACK(C), TSecr=1> (etc)
By Case (2), the timestamp from the last segment that advanced the left window edge is echoed, until the missing segment arrives; it is echoed according to Case (3). The same sequence would occur if segments B and D were lost and retransmitted..
TS.Recent <A, TSval=1> -------------------> 1 <---- <ACK(A), TSecr=1> 1 <C, TSval=3> -------------------> 1 <---- <ACK(A), TSecr=1> 1 <B, TSval=2> -------------------> 2 <---- <ACK(C), TSecr=2> 2 <E, TSval=5> -------------------> 2 <---- <ACK(C), TSecr=2> 2 <D, TSval=4> -------------------> 4 <---- <ACK(E), TSecr=4> (etc)