[转帖]TCP timestamp 选项那点事

tcp,timestamp,选项 · 浏览次数 : 0

小编点评

**TCP 序号丢失或乱序处理算法** 1. **本地窗口处理** - 接收端维护两个 32-bit 的变量 `TS.Recent` 和 `Last.ACK.sent`。 - `TS.recent` 保存下一个填入 TSecr 的时间戳。 - `Last.ACK.sent` 保存最近收到的时间戳。 - 当 `TS.recent` 比 `Last.ACK.sent` 更近时,认为序号已发送并丢弃该报文。 2. **重新发送机制** - 当收到确认时,更新 `TS.Recent` 和 `Last.ACK.sent`。 - 如果 `TS.recent` 比 `Last.ACK.sent` 更近时,认为是旧的报文。 - 接收端将该报文重新发送并更新 `TS.Recent`。 3. **保护 against wrapped sequences (PAWS)** - 32-bit 的 TCP 序号可能很快就能用一个轮回,因此使用时间戳来甄别报文的序号是否是属于上一个序号周期。 - 当接收端收到一个报文时,将其时间戳与 `TS.Recent` 进行比较。 - 如果 `TS.recent` 比 `Last.ACK.sent` 更近时,认为是老旧的报文,将其丢弃。 4. **恢复机制** - 当接收端在 RTT 估计过程中收到多个确认时,它会使用 TCP Timestamp 来恢复丢失的序号。 - 这些序号可以通过时间戳与 `TS.recent` 进行比较。 - 如果时间戳与 `TS.recent` 相同,认为是有效的确认,接收端将将其从窗口中删除。

正文

https://switch-router.gitee.io/blog/tcp-timestamp/

 

TCP 最早在 RFC1323 [] 中引入了 timestamp 选项, 并在后来的 RFC7323 中进行了更新。引入 timestamp 最初有两个目的:1.更精确地估算报文往返时间(round-trip-time, RTT) 2. 防止陈旧的报文干扰正常的连接.

本文将以 RFC7323 为基础,介绍 timestamp 选项的应用场景和当前业界对其的一些讨论。

介绍

Timestamp 是作为一个 TCP 选项存在于 TCP 首部。如下图所示,一个 timestamp 选项需要占据 首部中的 10 个字节。

 Kind: 8
 Length: 10 bytes
 
          +-------+-------+---------------------+---------------------+
          |Kind=8 |  10   |   TS Value (TSval)  |TS Echo Reply (TSecr)|
          +-------+-------+---------------------+---------------------+
             1       1              4                     4

选项的核心数据是两个 32-bit 的时间戳字段.TSval 表示发送端发出该报文时的本地时间戳, 而 TSecr 则负责回放(Echo) 最近一次收到的对端报文中的 TSval 的值。

TSval 和 TSecr 在数值上并没有绝对的大小关系。TSval 是以本地的时钟为基准的, 而 TSecr 则是以对端的时钟为基准的。

以下是一个组典型的时间戳交互过程

             TCP  A                                     TCP B

                             <A,TSval=1,TSecr=120> ----->

                  <---- <ACK(A),TSval=127,TSecr=1>

                             <B,TSval=5,TSecr=127> ----->

                  <---- <ACK(B),TSval=131,TSecr=5>

启用 Timestamp 选项需要经过双方的协商,协商在三次握手时完成,如果协商成功,则在后续的报文中, 除了 RST 之外的所有报文均必须包含 Timestamp 选项。

虚拟时钟

前面提到过,TSval 的值是以本地时钟为基准的,更准确地说是来自一个虚拟的时间戳时钟,这个时钟的流逝必须 与真实时间成比例,但不需要一定与真实时间相同,同时它既不能走得太慢,也不能走得太快

不能走得太慢是为了能更准确地测量报文的 RTT。假设这个时钟 10s 才 tick 一下,那么对于 往返时间为 1s 的 TCP 连接,一端发送报文之后,很有可能会发现收到对端的 ACK 报文中的 TSecr 和当前时钟 的值是一样的,这说明 RTT 为 0 ! 显然,这是十分荒谬的。

不能走的太快是为了防止时间戳回绕的干扰。TCP 协议规定的最大 MSL 为 255s, 显然一次时钟的循环必须大于这个值, 换算下来,32-bit 的时钟 tick 必须大于 59ns。否则,就无法区分两个时间戳之差是否经历了时间戳的回绕。

RFC 规定虚拟时钟的频率为每 1ms 到 1s 一个 tick。 按照 1ms 计算,32-bit 的时间戳回绕一次需要 24.8 天。

TSecr 的选择

通常情况下,为报文填入 TSecr 几乎是无脑的,因为只需要填入对端上一个报文的 TSval 就行了,但有几种特殊场景需要注意

1.Delay ACK

Delay ACK 是为了减少网络中的 pure ACK, 设计了一种接收端延时应答机制,当接收端收到一个数据报文时,不立即进行应答, 而是等待一段时间(通常为 40ms), 如果在这段时间内本端有数据也正好需要发送,则可以减少一个 pure ACK。 如果这段时间内又收到了后续其他报文,则接收端会累积起来,时间结束后一齐应答,这样也可以减少 pure ACK。

那么如果启用了 Delay ACK, 并且接收端收到了多个报文,这些报文的 TSval 不同,那么应该 Echo 哪一个报文的 TSval 呢?

答案是:需要 Echo 最早收到的那个报文的 TSval , 因为只有这样,发送端测量的 RTT 才更加准确.

2.报文丢失造成了接收端序号空洞

发送端发送了多个报文,但中间有报文出现了丢失或者乱序,这会使得接收端的窗口产生空洞(即在未收到序号较小的报文时,先收到序号较大的报文)。这种情况可能预示着链路发生了拥塞,因此,这种时候最好能让接收方 Echo 稍早时候的 TSval ,而不是序号最大报文的 TSval , 这样使得发送端估算的 RTT 能偏大,也就是发送报文更保守(conservative),有利于减小拥塞。

3.接收端序号空洞被填上

接收端序号空洞被填上意味着 1. 乱序的报文姗姗来迟; 或者 2.收到了发送端重传报文;

无论哪种情况,这都是反映了网络的真实情况,因此这个时候接收端应该 Echo 这个报文的 TSval。

算法

RFC 中设计了一个算法可以处理以上这些特殊场景,并且该算法也兼容正常应答的场景。

1.本地为每条 TCP 连接维护了两个 32-bit 的变量 TS.Recent 和 Last.ACK.sent。TS.recent 用来保存下一个填入 TSecr 的时间戳,当需要发送报文时,报文的 TSecr 始终从当前 TS.Recent 获得。 Last.ACK.sent 用来保存上一个应答的报文序号。在不启用 delay-ACK 时,它始终会与 RCV.NXT 保持同步;如果启用 delay-ACK,则每收到一个有序报文,RCV.NXT 会立即向后推进,而 Last.ACK.sent 不会,Last.ACK.sent 只会在真正 ACK 报文发送过后才更新。

2.如果收到的报文的时间戳大于 TS.Recent 并且报文没有造成空洞,则立即更新 TS.Recent

 if SEG.TSval >= TS.Recent and SEG.SEQ <= Last.ACK.sent
 then
    TS.Recent = SEG.TSval  

来看看这个算法对上面提到的特殊情况的处理

  • case 1, 接收端启用了 Delay ACK, 报文 A-B-C 按序到达:
                                                    TS.Recent   Last.ACK.sent  RCV.NXT
                  <A, TSval=1> ------------------->                             
                                                        1            A          A->B
                  <B, TSval=2> ------------------->
                                                        1            A          B->C
                  <C, TSval=3> -------------------> 
                                                        1            A          C->D
                           <---- <ACK(C), TSecr=1>                   
                  (etc.)                                1           A->D         D
    

启用了 Delay ACK => Last.ACK.sent 直到发出 ACK 才更新 => TS.Recent 一直未更新 => ACK(C)的 TSecr=1

  • case 2, 未启用 Delay ACK,但有报文乱序到达了:
                                                    TS.Recent   Last.ACK.sent  RCV.NXT
                  <A, TSval=1> ------------------->
                                                        1           A           A->B 
                           <---- <ACK(A), TSecr=1>
                                                        1          A->B          B
                  <C, TSval=3> ------------------->
                                                        1           B            B                         
                           <---- <ACK(A), TSecr=1>
                                                        1           B            B
                  <B, TSval=2> ------------------->
                                                       1->2         B           B->D           
                           <---- <ACK(C), TSecr=2>
                                                        2          B->D          D
                  <E, TSval=5> ------------------->
                                                        2            D           D
                           <---- <ACK(C), TSecr=2>
                                                        2            D           D
                  <D, TSval=4> ------------------->
                                                       2->4          D          D->F       
                           <---- <ACK(E), TSecr=4>
                                                        4           D->F         F
                  (etc.)
    

    报文 C 到达, 由于造成了接收端序号空洞,因此不会更新 TS.Recent, 报文 B 到达, 填上了空洞,因此 TS.Recent 更新.

PAWS

PAWS 的全称是 Protection Against Wrapped Sequences,即防止序号回绕。它的本质实际上是利用 TCP Timestamp 选项的单调递增特性来识别老旧的报文,防止这些老旧报文的干扰。

我们知道 TSval 是发送端发送该报文的时间戳,越晚发出的报文显然时间戳应该越大, 因此如果 SEG.TSval < TS.Recent, 则说明这个这个报文实际上是一个老旧的报文,接收应该将其丢弃。

假设发送端发送报文 SEG1 阻塞在网络中,然后重传了这个报文 SEG2。 显然 SEG1.TSval < SEG2.TSval。 接收端先收到 SEG2,于是更新 TS.Recent = SEG2.TSval,而后又收到了 SEG1。这个时候由于 SEG1.TSval < TS.Recent, 因此接收端需要丢弃 SEG1。

那这如何跟防止序号回绕产生联系呢?

答案是:在高速 TCP 连接的场景中,32-bit 的 TCP 序号可能很快就能用一个轮回,而 Timestamp 可以用来甄别报文的序号是否是属于上一个序号周期。

举个例子,假设发送端的序号从 1 开始计数,此时已经发送了 2^32 + 4999 字节的数据,当前接收端的 RCV.NXT = 5000, TS.Recent = 100, 如果收到一个报文 SEG.SEQ = 5000, SEG.LEN = 1000, TSval=70 如果不看时间戳,那么接收端会认为这个报文正好是预期的报文,但是这个报文实际上却是第 5000~5999 字节的报文。

TCP Timestamp 实际上是扩充了 32-bit 的 TCP 报文序号。

这里还有一个非常罕见的例外情况:TCP Timestamp 自身发生了回绕。 前面提到过,按照 1ms 的时间戳时钟计算,32-bit 的时间戳回绕一次的周期是 24.8 天, 因此如果一条 TCP 连接在 24.8 天之内都没有收到对端的数据,这个时候 TS.Recent 应该被视作失效状态,此时收到的第一个报文便不能使用 PAWS 进行检验了,从第二个开始才能重新使用。

TCP Timestamp 的另一面

TCP Timestamp 选项虽然能带来好处, 但并不是所有的 TCP 连接都会使用该选项,比如 Windows 系统就是默认不不启用该选项的,而 Linux 系统则是默认启用了该选项。 据 tcpm 的统计,在全球范围内,使用了 TCP Timestamp 的连接比例大概为 60%~70% 。

不支持 TCP Timestamp 的理由是该选项占用的报文长度太多了,它会占用 TCP 报文首部的 10 个字节,而且是每个报文都会有这种损耗。

Yoshifumi Nishida 曾在 2018 年提出过 PAWS 的替代方案

而站在 Linux 内核的角度,Yuchung Cheng 则列举了 TCP Timestamp 在内核中的应用场景。

1. RTT estimation on retransmission - this is crucial when a TCP is in
constant recovery mode such as encountering a traffic policer. Note
that the standard ACK time approach no longer works if any sequence
acked has been transmitted, thus RTO may stale during extended
recovery period.

2. Undo operations (i.e. TCP Eifel RFC4015) requires TCP timestamps -
being able to detect false reactions to losses is particularly key for
"loss-based" congestion control. Such false positives are common in
wireless or even wired networks due to all sorts of dark queues and L2
optimizations. While DSACK and F-RTO can also be used to detect false
recoveries - both of them take at least one more round-trip and/or
require new application data, while timestamp can detect false
retransmission instantly on the original ACK

3. Receiver buffer auto-tuning uses TCP ACKs' TS value to echo to
measure RTT from the receiver to tune the buffer size.

4. Potentially TCP timestamps being generated with known units, can be
used to gauge the data arrival rate. The measurement can be valuable
for congestion control b/c it's not subject to hectic ACK delays in
modern networks. I know folks at NetFlix is doing some deep research
into this aspect.

与[转帖]TCP timestamp 选项那点事相似的内容:

[转帖]TCP timestamp 选项那点事

https://switch-router.gitee.io/blog/tcp-timestamp/ TCP 最早在 RFC1323 [] 中引入了 timestamp 选项, 并在后来的 RFC7323 中进行了更新。引入 timestamp 最初有两个目的:1.更精确地估算报文往返时间(roun

[转帖]tcp_tw_reuse、tcp_tw_recycle和tcp_timestamps

https://www.cnblogs.com/zh-dream/p/12702522.html TCP报文格式 1、TCP报文:由TCP首部和TCP数据组成 2、TCP首部:由 20字节的固定长度 + 变长字段(选项)+ 填充 组成成 3、MSS(Maximum Segment Size):占4字

[转帖]内核参数优化

net.ipv4.tcp_timestamps= 1 #服务器时间截,默念为1 net.ipv4.tcp_tw_reuse= 1 #服务器作为客户端时起作用,开启后time_wait在一秒内回收,(两端都要开启tw_timestamps=1时才有效) net.ipv4.tcp_tw_recycle=

[转帖]TCP的半关闭、半连接、半打开

参考:《UNIX 网络编程 · 卷1 : 套接字联网API》 TCP 半关闭 如果将客户端与服务器之间的网络作为全双工管道来考虑,请求是从客户端向服务器发送,应答是从服务器向客户端发送,其如下图所示: 上图假设 RTT 为 8,且服务器没有处理时间且请求大小与应答大小相同。既然从管道发出到管道的另一

[转帖]tcp连接测试工具_六款最佳、免费的网络延迟测试工具

作为网络管理员或网络工程师,时刻关注网络的交付速度至关重要。不仅需要确保自己有良好的响应时间,还需要确保网络的速度足以满足用户通信所需的每一条路径。而手动测试每个路径将占用你所有的时间。所以需要获得一个测试工具,以确保延迟不会影响网络的性能。 什么是延迟 延迟是网络流量的速度指标。可接受的传输时间根

[转帖]tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项

linux TIME_WAIT 相关参数: net.ipv4.tcp_tw_reuse = 0 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭net.ipv4.tcp_tw_recycle = 0 表示开启TCP连接中TIME-WAIT socket

[转帖]TCP的blacklog之全连接队列与半连接队列的深入研究

文章目录 Linux内核探测工具systemtap的安装与使用backlog、半连接队列、全连接队列是什么半连接队列、全连接队列基本概念 linux 内核是如何计算半连接队列、全连接队列的半连接队列的大小的计算模拟半连接队列占满全连接队列(Accept Queue) SYN+ACK重传次数全连接队列

[转帖]TCP半连接队列和全连接队列

TCP半连接队列和全连接队列 文章很长,建议收藏起来慢慢读! 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备免费赠送 经典图书:《Java高并发核心编程(卷1)》 面试必备 + 大

[转帖]TCP的全连接与半连接队列

TCP的全连接与半连接队列 总结 TCP 全连接队列的最大值: 取决于 somaxconn 和 backlog 之间的最小值, 也就是 min(somaxconn, backlog) TCP 半连接队列的最大值: min(min(somaxconn,backlog),tcp_max_syn_back

[转帖]tcp 粘包 和 TCP_NODELAY 学习

https://www.cnblogs.com/zhangkele/p/9494280.html TCP通信粘包问题分析和解决 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将