当前位置: 首页 > news >正文

TCP Analysis Flags 之 TCP Window Full

前言

默认情况下,Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态,并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时,会对每个 TCP 数据包进行一次分析,数据包按照它们在数据包列表中出现的顺序进行处理。可以通过 “Analyze TCP sequence numbers” TCP 解析首选项启用或禁用此功能。

TCP 分析展示

在数据包文件中进行 TCP 分析时,关于 “TCP Window Full” 一般是如下显示的,包括:

  1. Packet List 窗口中的 Info 信息列,以 [TCP Window Full] 黑底红字进行标注;
  2. Packet Details 窗口中的 TCP 协议树下,在 [SEQ/ACK analysis] -> [TCP Analysis Flags] 中定义该 TCP 数据包的分析说明。

image.png

TCP Window Full 定义

实际在 TCP 分析中,关于 TCP Window Full 的定义非常简单,如下,为本端发送端所发的 TCP 段大小超过对端接收端的接收窗口大小,受到对端接收窗口大小的限制,需注意的是这个标记是在发送端标记,而不是在接收端标记

Set when the segment size is non-zero, we know the window size in the reverse direction, and our segment size exceeds the window size in the reverse direction.

具体的代码如下,总的来说这段代码的作用是检测 TCP 数据流中的“窗口已满”情况,即当前数据段刚好达到接收端宣告的窗口边界,检测到这种情况时,会设置相应的标志以供后续处理使用。

    /* WINDOW FULL* If we know the window scaling* and if this segment contains data and goes all the way to the* edge of the advertised window* then we mark it as WINDOW FULL* SYN/RST/FIN packets are never WINDOW FULL*/if( seglen>0&&  tcpd->rev->win_scale!=-1&&  (seq+seglen)==(tcpd->rev->tcp_analyze_seq_info->lastack+(tcpd->rev->window<<(tcpd->rev->is_first_ack?0:(tcpd->rev->win_scale==-2?0:tcpd->rev->win_scale))))&&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {if(!tcpd->ta) {tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);}tcpd->ta->flags|=TCP_A_WINDOW_FULL;}
  1. lastack,定义为 Last seen ack for the reverse flow。
  2. 关于 tcpd->rev->is_first_ack,在之后实例中会展开说明。

Packetdrill 示例

在上述 TCP Window Full 定义和代码可知,TCP 分析的逻辑很简单,因此通过 packetdrill 比较容易模拟出相关现象。

# cat tcp_window_full.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, ..., 500) = 500
+0 > P. 1:501(500) ack 1
+0.1 < . 1:1(0) ack 501 win 1000+.1 write(4, ..., 1000) = 1000
+0 > . 501:1001(500) ack 1
+0 > P. 1001:1501(500) ack 1
+0.1 < . 1:1(0) ack 1501 win 1000

通过 tcpdump 捕获数据包后,经 Wireshark 展示如下,可以看到由于客户端 No.5 宣告的 Win 为 1000,所以服务器仅能发送 1000 字节大小的数据分段,也就是分别发送了 No.6 和 No.7 两个 Len 为 500 字节的数据包,这样在 No.7 数据包中会判断为 [TCP Window Full],因为发送字节大小已经达到了对端也就是客户端宣告的 Win 大小。

image.png

BIF 大小 1000,专家信息标识说明为 TCP window specified by the receiver is now completely full接收端窗口满

image.png

实例

关于 TCP Window Full 的实例,正常来说考虑到接收端接收窗口的释放速度,再加上 Window Scale 的存在,TCP 窗口满现象相对来说并不是那么容易出现。而在不同的场景中,也会伴生着出现像是 TCP Window UpdateTCP ZeroWindowTCP ZeroWindowProbeTCP ZeroWindowProbeAck 等信息。

  1. 窗口满

可以看到客户端 No.277 宣告的 Win 为 8712,而服务器 No.278 标识为 [TCP Window Full],是由于在途字节数达到了 8712 字节。

image.png

image.png

而在之后的通讯过程中,由于客户端 Win 降为了 0,在 No.281 也就标识了 [TCP ZeroWindow],之后由于接收窗口释放变化,又发送了 No.282 Win 为 4356 的 ACK,之后服务器再次发送 No.283 数据分段时又一次标识了 [TCP Window Full],同样的原因是由于在途字节数达到了 4356 字节。

image.png

该案例中,客户端并不支持 Window Scale ,也就是在 TCP SYN 中未携带该选项。

  1. 窗口满的特例

当 TCP 通讯速率上不去,疑似某端接收窗口满或有问题时,但又没看到明显的 [TCP Window Full] 标识时,请相信自己的判断,真有可能是发生了 TCP 窗口满问题。

看不到 [TCP Window Full] 标识,仅仅是有可能数据包文件或者说这条 TCP 流不完整,并没有抓到 TCP 三次握手的信息,因此后续的数据通讯过程中是无法得知这条流是否支持 Window Scale 以及它的具体数值,没有这些信息的情况下,是没有办法根据数据包中携带的 Win 字段值计算出实际的窗口值,所以也就没法判断什么时候发生了窗口满事件。

如下,就像只会知道接收窗口 250,在不知道 Window Scale 为 4 的情况下,如何能得出计算后的 Win 大小为 1000 ?

image.png
这种情况下,看到的现象就是一端通告的 Win 只是一个小值,但是另一端发送的在途数据又能超过这个值,此时的情况你就可以判断出是因为缺少了 TCP 三次握手。

注意,仅仅是 Wireshark 因为数据包文件不完整无法判断,真实的数据流如果双方均支持 Window Scale,实际的通讯流均是按照各自的缩放因子来计算且发送数据的。

案例也很好模拟出,在上述 packetdrill 示例中把 No.1-3 TCP 三次握手数据包忽略掉即可,如下,No.7 此时已经不再标识 [TCP Window Full], 虽然对端 No.5 所通告的 Win 为 1000 ,但是 Window size scaling factor: -1(unknown),从 [TCP Window Full] 的代码来说,tcpd->rev->win_scale =-1 已经没有后续判断的依据了,因此 [TCP Window Full] 不再出现。

注意区别 Window size scaling factor: -2 (no window scaling used),-2 代表着说数据包文件完整,也就是捕获到了 TCP 三次握手,但双方不支持 Window Scale。

image.png

如何知道?有条件的话,复现一次 TCP 连接,重新捕获到 TCP 三次握手即可,不管是 -2 不支持,或者其它的值。然而就算知道了,再通过手动指定 Window Scale 值的方式,也无法让 [TCP Window Full] 再出现 。

  1. 再谈窗口满的特例

这里就要提到之前代码中的 tcpd->rev->is_first_ack ,如下,如果 is_frist_ack 为真,对于 Window 的偏移量也就是 Window Scale ,使用值 0,而不是用 TCP 三次握手中的 Window Scale 值。

tcpd->rev->window<<(tcpd->rev->is_first_ack?0:(tcpd->rev->win_scale==-2?0:tcpd->rev->win_scale))

is_first_ack 具体是在 TCP 三次握手阶段即设置为 True,不管是 SYN 或是 SYN/ACK 数据包,均会设置本方向的 is_first_ack 值为 True 。

    if(tcph->th_flags & TH_SYN) {if(tcph->th_flags & TH_ACK) {expert_add_info_format(pinfo, tf_syn, &ei_tcp_connection_synack,"Connection establish acknowledge (SYN+ACK): server port %u", tcph->th_sport);/* Save the server port to help determine dissector used */tcpd->server_port = tcph->th_sport;}else {expert_add_info_format(pinfo, tf_syn, &ei_tcp_connection_syn,"Connection establish request (SYN): server port %u", tcph->th_dport);/* Save the server port to help determine dissector used */tcpd->server_port = tcph->th_dport;tcpd->ts_mru_syn = pinfo->abs_ts;}/* Remember where the next segment will start. */if (tcp_desegment && tcp_reassemble_out_of_order && tcpd && !PINFO_FD_VISITED(pinfo)) {if (tcpd->fwd->maxnextseq == 0) {tcpd->fwd->maxnextseq = tcph->th_seq + 1;}}/* Initiliaze the is_first_ack */tcpd->fwd->is_first_ack = TRUE;

在以下这个案例中,No.79 判定为 [TCP Window Full],光从表面现象来看会感觉很奇怪,因为 TCP 三次握手中明显双方均支持 Window Scale,且双方均为很大的一个值 WS 128,而客户端仅仅从 No.70-79 传了 10 个 MSS 1448 的分段后即出现了窗口满现象。No.79 显示为 BIF 14480 大小,和 SYN/ACK 通告的窗口 Win 14480 相等,但没有使用 Window Scale 来相乘计算,即对于 [TCP Window FUll] 的判断,使用的直接是 0 值,而不是 Window Scale 128。

image.png

image.png
这是因为对于客户端 No.79 来说,tcpd->rev->is_first_ack 实际就是服务器端 is_first_ack 的取值,而此时在 rev 方向仅有 No.68 SYN/ACK 数据包,此时 is_first_ack 的值为 True,因此对于 Window 的偏移量也就是 Window Scale ,使用值 0,所以最终判断为 [TCP Window Full] 。

  1. 继续谈窗口满的特例

再说一个案例,服务器端 No.5 判定为 [TCP Window Full],是因为客户端 No.3 通告的 Win 为 1000,也就是 Win 250 使用了 Window Scale 4 来相乘计算,因此服务器端能发送 No.4 和 No.5 两个 500 字节大小的数据分段,并在 No.5 上标记 [TCP Window Full] 。

image.png

也就是说服务器端在 No.5 数据包分析时,判断 rev 方向 Window Scale 的值是真实值 4 而不是 0 ,这也就意味着此时客户端的 is_first_ack 的值为 False 。而这是在分析客户端 No.3 ACK 数据包时,通过以下代码所实现:

    /** Remember if we have already seen at least one ACK,* then we can neutralize the Window Scale side-effect at the beginning (issue 14690)*/if(tcp_analyze_seq&& (tcph->th_flags & (TH_SYN|TH_ACK)) == TH_ACK) {if(tcpd->fwd->is_first_ack) {tcpd->fwd->is_first_ack = FALSE;}}

总结

总结来说,关于 TCP Window Scale ,有两句话说得很到位:

  1. RFC7323 2.2 The window field in a segment where the SYN bit is set (i.e., a or <SYN,ACK>) MUST NOT be scaled.
  2. The Window Scale option has to be honored and interpreted adequately.

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 解决 Vue 页面中地址栏参数变更不刷新的问题
  • react防抖和节流hooks封装
  • Hystrix 线程池策略时使用ThreadLocal
  • 【LeetCode】219.存在重复元素II
  • STM32卡死、跑飞如何调试确定问题
  • CMD运行指令
  • 鸿蒙系统开发【ASN.1密文转换】安全
  • 线程池工具类 Executors源代码详解
  • 基于Redis实现全局唯一id
  • 小试牛刀-Telebot区块链游戏机器人(TS升级)
  • 【Python】数据类型之详讲字符串(下)
  • 全球轻型汽车安全气囊面料市场规划预测:未来六年CAGR为4.3%
  • 1. 什么是操作系统
  • 24年税务师考试补报名即将开始啦
  • ComfyUI插件:ComfyUI layer style 节点(三)
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 2017年终总结、随想
  • 230. Kth Smallest Element in a BST
  • centos安装java运行环境jdk+tomcat
  • Django 博客开发教程 16 - 统计文章阅读量
  • gcc介绍及安装
  • java8 Stream Pipelines 浅析
  • JS基础之数据类型、对象、原型、原型链、继承
  • k8s如何管理Pod
  • mockjs让前端开发独立于后端
  • 产品三维模型在线预览
  • 从零搭建Koa2 Server
  • 官方解决所有 npm 全局安装权限问题
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 延迟脚本的方式
  • python最赚钱的4个方向,你最心动的是哪个?
  • #QT 笔记一
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (delphi11最新学习资料) Object Pascal 学习笔记---第14章泛型第2节(泛型类的类构造函数)
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (待修改)PyG安装步骤
  • (二)hibernate配置管理
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (力扣题库)跳跃游戏II(c++)
  • (转)程序员技术练级攻略
  • (转)视频码率,帧率和分辨率的联系与区别
  • .FileZilla的使用和主动模式被动模式介绍
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .Net CF下精确的计时器
  • .NET DataGridView数据绑定说明
  • .NET Micro Framework初体验
  • .Net Web项目创建比较不错的参考文章
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET程序员迈向卓越的必由之路
  • .net分布式压力测试工具(Beetle.DT)
  • .NET框架设计—常被忽视的C#设计技巧