前言
默认情况下,Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态,并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时,会对每个 TCP 数据包进行一次分析,数据包按照它们在数据包列表中出现的顺序进行处理。可以通过 "Analyze TCP sequence numbers" TCP 解析首选项启用或禁用此功能。
TCP 分析展示
在 TCP 分析中和 TCP 零窗口相关的实际上有三种信息,分别是:TCP ZeroWindow
、TCP ZeroWindowProbe
、TCP ZeroWindowProbeAck
。实际运行环境中,有时是单独出现的,譬如 TCP ZeroWindow
,有时是一起出现的,也就是出现了零窗口,之后就会出现需要为恢复窗口而进行的零窗口探测和零窗口探测确认。
在数据包文件中进行 TCP 分析时,关于 "TCP ZeroWindow" 一般是如下显示的,包括:
- Packet List 窗口中的 Info 信息列,以 [TCP ZeroWindow] 黑底红字进行标注;
- Packet Details 窗口中的 TCP 协议树下,在 [SEQ/ACK analysis] -> [TCP Analysis Flags] 中定义该 TCP 数据包的分析说明。
TCP ZeroWindow 定义
实际在 TCP 分析中,关于 TCP ZeroWindow 相关的定义也相对简单,当接收窗口大小为 0 且 SYN、FIN、RST 均未设置时设置, 是接收方发送,用以通知发送方暂停发送数据。
plain
Set when the receive window size is zero and none of SYN, FIN, or RST are set.
The window field in each TCP header advertises the amount of data a receiver can accept. If the receiver can't accept any more data it will set the window value to zero, which tells the sender to pause its transmission. In some specific cases this is normal --- for example, a printer might use a zero window to pause the transmission of a print job while it loads or reverses a sheet of paper. However, in most cases this indicates a performance or capacity problem on the receiving end. It might take a long time (sometimes several minutes) to resume a paused connection, even if the underlying condition that caused the zero window clears up quickly.
具体的代码如下,总的来说这段代码是 Wireshark 分析 TCP 流量时处理零窗口通告情况的一个关键部分,有助于正确解析和显示 TCP 连接的状态和控制信息。如果当接收窗口大小为 0 和 SYN、FIN、RST 均未设置这两个条件同时满足时,则认为该数据包是一个零窗口通告,零窗口通告表示接收方暂时没有可用缓冲区来接收数据,因此通知发送方暂停发送数据。
plain
/* ZERO WINDOW
* a zero window packet has window == 0 but none of the SYN/FIN/RST set
*/
if( window==0
&& (flags&(TH_RST|TH_FIN|TH_SYN))==0 ) {
if(!tcpd->ta) {
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW;
}
Packetdrill 示例
根据上述 TCP ZeroWindow
定义和代码说明,TCP 分析的逻辑很简单,因此通过 packetdrill 比较容易模拟出相关现象。
plain
# cat tcp_zero_window.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 1000 <mss 1460>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 1000
+0 accept(3, ..., ...) = 4
+.1 write(4, ..., 1000) = 1000
+0 > P. 1:1001(1000) ack 1
+0.1 < . 1:1(0) ack 1001 win 0
#
经 Wireshark 展示如下,No.6 因为 Win 为 0 且未设置 SYN、FIN、RST 的情况下,标识为 [TCP ZeroWindow]
。
实例
关于 TCP ZeroWindow
的实例,实际日常抓包中并不是经常会看到,但如果看到了,则代表着本端接收窗口为 0 ,意味着本端因性能或容量等问题而无法接收数据,因此通知发送方暂停发送数据,如果窗口恢复时间较长,TCP 传输速率自然就下降了,再反复出现的话,应用传输就会明显感觉慢。在不同的场景下,也会伴生着出现像是 TCP Window Update
、TCP ZeroWindowProbe
、TCP ZeroWindowProbeAck
等信息。
- TCP Window Full + TCP ZeroWindow + TCP Window Update
一种比较常见的零窗口情景,接收端短暂的出现 Win 为 0 的情形,紧接着就会释放窗口,发出窗口更新的消息。
首先服务器端发送数据,发现客户端接收窗口满了,则在 No.278 上标识 [TCP Window Full]
,此时客户端 No.281 因为 Win 为 0 且未设置 SYN、FIN、RST 的情况下,标识为 [TCP ZeroWindow] ,之后 12ms 紧接着就发送了 [TCP Window Update]
窗口更新消息,Win 恢复成 4536,该交互过程并没有出现 [TCP ZeroWindowProbe]
等数据包。
- TCP Window Full + TCP ZeroWindow + TCP ZeroWindowProbe + TCP Window Update
另外一种零窗口情景,接收端出现 Win 为 0 的情形,发送 TCP ZeroWindow
通知,发送端在经过一段时间后发出 TCP ZeroWindowProbe
数据包,但接收端收到探测后,由于已经打开窗口,因此直接回复 TCP Window Update
数据包。
首先服务器端 No.10 Win 为 0 且未设置 SYN、FIN、RST 的情况下,标识为 [TCP ZeroWindow] ,之后 286ms 客户端发送了 No.11 [TCP ZeroWindowProbe]
用于确认服务器端接收窗口是否恢复,服务器紧接着回复确认 No.12,表示窗口已恢复 Win 1420,标识为 [TCP Window Update]
数据包。
- TCP Window Full + TCP ZeroWindow + TCP ZeroWindowProbe + TCP ZeroWindowProbeAck + TCP Window Update
🤣 大满贯场景,覆盖了 5 种 TCP 分析标志。首先客户端发送数据,发现服务器接收窗口满了,则在 No.4 和 No.6 上标识 [TCP Window Full]
,此时服务器端 No.7 因为 Win 为 0 且未设置 SYN、FIN、RST 的情况下,标识为 [TCP ZeroWindow] ,之后陷入等待,大概 2 秒+后,客户端发送了 No.8 [TCP ZeroWindowProbe]
用于确认服务器端接收窗口是否恢复,服务器紧接着回复确认 No.9,表示仍处于零窗口未恢复,标识为 [TCP ZeroWindowProbeAck]
+ [TCP ZeroWindow]
,又再过了 300ms 后,服务器端发送 No.10 Win 此时更新为 14600,表示接收窗口已恢复,标识成 [TCP Window Update]
,至此完成一次完整的零窗口出现、探测及恢复过程。
类似的场景同样如下
总结
👓 不知道总结能不能这样说,正常情况下,有出现 TCP Window Full
,不一定会有 TCP ZeroWindow
,但有 TCP ZeroWindow
, 则之前必有 TCP Window Full
。💯