tcp_timestamps是在RFC 1323中定义的一个TCP选项。
这篇wiki介绍一下timestamps的设计目的和相关原理,尤其强调一些比较tricky的地方。
这是一篇介绍针对High-bandwidth, Long delay链路设计的一些TCP扩展选项的资料。强烈推荐阅读!
但这篇RFC其实已经被RFC7323所取代,不过RFC1323对于了解timestamp相关的基本概念来说还是足够了的。关于RFC7323也会在后续的wiki中详细的介绍。
High-bandiwidth, Long delay链路面临的性能问题
1 2 3 4 5 6 7 8 9 10 11
a. Window Size limit TCP头部仅16字节用于存放receive window,这在高BDP的链路中往往是不够用的 解决办法就是引入window scale选项,然后real_rcv_wnd = rcv_wnd_in_tcp_header * (2^win_scale) b. Recovery from Losses 当BDP很大时,意味着需要更大的cwnd来充分利用带宽。如果发生网络丢包,则对这类链路的影响是巨大的。 优化办法(真的不能说解决T_T)就是引入[SACK](https://tools.ietf.org/html/rfc2018)机制,来为发送方重传提供更加准确的信息。 c. Round-Trip Measurement TCP作为可靠的传输协议,一个重要的机制就是超时重传。因此如何计算一个准确(合适)的 RTO对于TCP性能有着重要的影响。而tcp_timestamp选项正是*主要*为此而设计的。
上一句话强调”主要”是因为tcp_timestamp还被用于PAWS机制,而这一重要用途却时常被忽略。
作为一个可靠的传输协议,TCP除了考虑如何应对性能问题,还需要考虑可靠性问题。
即使这些问题发生的概率较低,PAWS就是其中一个例子。
PAWS(Protect Against Wrapped Sequence numbers)一句话解释如下,后面会详细介绍
1 2
在高带宽下,TCP序列号可能在较短的时间内就被重复使用(recycle/wrapped) 就可能导致同一条TCP流在短时间内出现序号一样的两个合法的数据包及其确认包!
补充一句:什么用wrapped形容序列号被重复使用?因为压圈了呀 :)
tcp_timestamps 的设计tcp_timestamps的本质是记录数据包的发送时间。基本的步骤如下
1 2 3
1. 发送方在发送数据时,将一个timestamp(表示发送时间)放在包里面 2. 接收方在收到数据包后,在对应的ACK包中将收到的timestamp返回给发送方(echo back) 3. 发送发收到ACK包后,用当前时刻now - ACK包中的timestamp就能得到准确的RTT
当然实际运用中要考虑到RTT的波动,因此有了后续的(Round-Trip Time Measurement)RTTM机制
TCP Timestamps Option (TSopt)具体设计如下
1 2 3 4 5 6
Kind: 8 // 标记唯一的选项类型,比如window scale是3 Length: 10 bytes // 标记Timestamps选项的字节数 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | Kind=8 | Length=10 | TS Value (TSval) | TS ECho Reply (TSecr) | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 1 4 4
timestamps一个双向的选项,当一方不开启时,两方都将停用timestamps。
比如client端发送的SYN包中带有timestamp选项,但server端并没有开启该选项。
则回复的SYN-ACK将不带timestamp选项,同时client后续回复的ACK也不会带有timestamp选项。
当然,如果client发送的SYN包中就不带timestamp,双向都将停用timestamp。
如果没有timestamp,RTT的计算会怎样?
1 2
1. TCP层在发送出一个SKB时,使用skb->when记录发送出去的时间 2. TCP层在收到SKB数据包的确认时,使用now - skb->when来计算RTT
但上面的机制在丢包发生时会有问题,比如
1 2
1. TCP层第一次发送SKB的时间是send_time1, TCP层重传一个数据包的时间是send_time2 2. 当TCP层收到SKB的确认包的时间是recv_time
但是RTT应该是 (recv_time - send_time1)呢,还是(recv_time - send_time2)呢?