准确追踪故障原因:掌握TCP时间戳的分析方法

上一期《TCP传输效率翻倍的秘密武器:SACK如何拯救你的网络性能?》我们学习了TCP的SACK选项相关知识,今天我们接着聊聊TCP时间戳的分析方法。

本期互动问题,欢迎大家评论区一起聊聊:

你曾经遇到过哪些和时间戳有关的问题?最后是如何解决的?

数据包涉及多少种时间戳?

时间戳,顾名思义,记录数据包的时间。

在数据包分析过程中,可能涉及多种时间戳:

1.数据包捕获时间戳:是抓包设备(CSNAS、Wireshark、科来回溯、科来全流量)捕获数据包时记录的时间。

2.IP时间戳:在数据包的三层IP协议头部中,IP选项可以支持时间戳功能,IP时间戳记录数据包经过的每个设备的时间戳和IP地址。

3.ICMP时间戳:ICMP协议支持发送时间戳请求,此类ICMP数据包中携带ICMP时间戳。

4.TCP时间戳:TCP启用了时间戳选项后,交互的数据包携带TCP时间戳。

5.应用层时间戳:例如HTTP数据包中的Date时间戳,NTP数据包中的对时时间戳。

由此可见,一个数据包中,可能同时存在多种时间戳,本文主要探讨的是TCP时间戳。在后续的讨论中,除非另有明确说明,文中提到的“时间戳”一词均默认指的是TCP时间戳。

TCP时间戳如何分析?

要分析时间戳,需要先了解时间戳的内容和格式。时间戳以TCP选项的形式进行传输,该选项的长度为10字节,格式如下:

类型(1字节):时间戳选项的类型,数值固定为08 

长度(1字节):时间戳选项的长度数值固定为0A,即10字节

时间戳数值(4字节):发送方在发送数据包时,将当前的时间戳填入此字段

时间戳回显(4字节):接收方在发送TCP包时,将上一个接收到的“时间戳数值”复制到此处,回复给发送方。在会话首包中,无时间戳可复制和回复,因此会话首包中的时间戳回显为全0。

图1    会话中的SYN包时间戳

如图1所示,红、绿、蓝、黄四部分部,分别为选项类型、长度、时间戳数值、时间戳回显的原始数据流和解码信息,该选项为SYN包的时间戳,即会话起始,时间戳数值为0xbe48897c;时间戳回显为空,填充为全0。

图2展示了该会话SYN/ACK包的时间戳,如下图所示,图2中的时间戳回显为0xbe48897c,与图1中的时间错正好相等。图2的时间戳为0x3c76d71c。

图2 该会话中SYN/ACK包的时间戳

了解了时间戳与回显应答的关系,我们再来看看时间戳是如何增长的。RFC 1323中这样描述时间戳的增长:

With the chosen range of timestamp clock requencies (1 sec to         1 ms), the time to wrap the sign bit will be between 24.8 days         and 24800 days.  A TCP connection that is idle for more than 24         days and then comes to life is exceedingly unusual.
关于时间戳时的钟频率范围(1秒到1ms),包裹符号位的时间将在24.8天至24800天之间。TCP连接空闲超过24天然后恢复正常是非常不寻常的。

因此,时间戳的递增单位一般为1ms,也有每10ms、100ms甚至1000ms递增的情况。

再来看看刚才这条会话三次握手ACK包的时间戳

图3 三次握手ACK包的相对时间和TCP时间戳

ACK包的时间戳为0xbe488985,SYN包时间戳为0xbe48897c,时间戳数值相差7;根据时序图中的相对时间(捕获时间戳)计算,ACK包的时间戳为会话第7ms,时间戳数值差和时间差显示该会话客户端时间戳在7ms内递增了7点,因此推测客户端的时间戳递增单位为1ms。

由于不同操作系统默认使用的时间精度和时间戳起始点不同,因此不同主机之间的时间戳数值看起来可能相差很多,如本例中的客户端时间戳0xbe48897c与服务器时间戳0x3c76d71c,数值就相差很远。

时间戳有什么作用?

时间戳的引入,能够为TCP协议带来三大作用:

一、便于双方精确计算RTT。

在一些传输窗口大、重传率高的会话中,精确测量RTT是困难的。引入时间戳后,得到了发送方真实发送数据的时间,能够实现对RTT的精确计算。

二、配合PAWS机制,区分序列号回绕前后的数据包,防止TCP会话由于序列号回绕导致的错误。

由于TCP的序列号长度为32位,因此一轮序列号最多描述232字节数据,即4GB数据,在高速的TCP连接中(每秒传输1GB以上),TCP序列号每4秒即兜一圈回绕一次,在序列号回绕+丢包重传的组合场景下,时间戳能为服务器判断数据包的新旧提供非常好的依据,便于服务器直接忽略时间戳较旧的数据包,防止连接发生错误。

三、区分端口重用的新旧连接。

在服务器启用了tcp_tw_recycle和tcp_tw_reuse这样的快速端口重用场景下,时间戳可以用于区分数据包是来自新TCP连接或是来自旧TCP连接。

一次时间戳导致的故障分析实例

本系列之前文章《从时序图看TCP 06 时间等待与端口重用》曾提到,服务器的tcp_tw_recycle参数在NAT场景下可能引发新的问题。

故障的成因是由于不同客户端对TCP时间戳的计算精度、起点值不同,所以数值有很大差异(这一点从前文中的时间戳示例也能看出)。不同主机之间的时间戳数值有很大差距。而tcp_tw_recycle参数是基于源IP进行检查的,来自公网的不同客户端被SNAT后,对于服务器来说成为了一个客户端,这就会造成“一个客户端IP的时间戳忽快忽慢”,不巧的是tcp_tw_recycle参数又是丢弃时间戳较旧的数据包的,造成了“部分客户端无法建立TCP连接”的故障现象。

对于此类故障的排查,通常是极其耗时的,接下来看看如何从流量分析的角度发现此类问题:

图4 时而成功时而失败的TCP连接

如图所示,图中展示了一些会话,服务器192.168.1.171的443端口运行了HTTPS服务,两台客户端使用相同的SNAT地址访问服务器,一台成功,一台失败,影响了业务,需要分析原因。

观察连接建立失败的会话,都卡在了SYN_SENT状态,通过这里可以判断这些连接建立失败的会话连三次握手都没能成功建立,软件将这些会话识别为TCP协议。而那些状态为CLOSED的会话,根据数据包数量可以判断已经正常完成了交互,软件将该会话识别为HTTPS协议。

图5 连接建立失败会话的交易时序图

通过时序图也能看出,客户端发送的SYN包,服务器完全没有理会。

图6 几乎同时出现的2395端口会话和2396端口会话

端口2395和2396的会话是在同一秒开始的,一个成功,一个失败,说明该时段内服务器上的业务是正常的。

着重观察一下这两条会话的SYN包有什么区别,使用对比法,对比正常SYN包和异常SYN包的区别

图6 2395和2396端口会话SYN包和SYN/ACK包的情况

如图6所示,2395端口客户端发送SYN包后,服务器正常返回了SYN/ACK包。随后出现的2396端口会话,服务器似乎没有收到或者忽略了客户端的SYN包

当观察到时间戳时,发现了端倪

图7 两会话SYN包的时间戳异常

2395端口的客户端SYN包时间戳为be60ada1,服务器正常响应了SYN/ACK;2396端口的客户端SYN包时间戳为be498c98,服务器无响应。

对比这两个时间戳的数值,发现第二个客户端的时间戳数值比第一个客户端的时间戳数值小,形成了“后来的数据包反而较旧”的现象。联想到tcp_tw_recycle的PAWS序列号回绕机制,就不难理解服务器为何没有响应这些SYN包了。

分析结论为,时间戳的值随时间应为单调递增,但在分析中由于用户服务器是经过NAT后进行互联网的应用访问,而该NAT地址同时存在其他设备访问相同的应用,开启了tcp_tw_recycle选项后,当连接进入TIME_WAIT状态后,会记录对应远端主机最后到达的时间戳。如果同样的主机有新的分节到达,且时间戳小于之前记录的时间戳,即视为无效,相应的数据包会被丢弃

典型的故障实例

关于时间戳引发的故障,似乎总是和连接建立失败这一故障现象有关。另一个关于时间戳的故障,大家可以参考如下视频,得到更多启发:

视频链接:    https://www.bilibili.com/video/BV1Us4y1s79R

本文介绍了TCP的时间戳选项,深入探讨了时间戳选项的分析,和时间戳选项可能导致的故障原因和故障现象,包括了时间戳问题在CSNAS时序图和其它页面中的分析方法,通过深入理解和掌握时间戳的分析技巧,能够提升流量分析工程师在工作中快速分析解决此类故障的能力。