TCP系列08—连接管理—7、TCP 常见选项(option)
一、TCP选项概述
在前面介绍TCP头的时候,我们说过tcp基本头下面可以带有tcp选项,其中有些选项只能在连接过程中随着SYN包发送,有些可以延后。下表汇总了一些tcp选项
其中我标记为红色的部分是常见的TCP选项,我们仅针对这些红色的TCP选项进行介绍(主要是非红色的我也不太了解~~~),另外RFC1323已经被RFC7323取代,这里给出的是TCP选项原始定义的RFC
按照RFC793规定,一个TCP选项只需要单字节对齐,但是在实现上一般是两字节对齐或者会通过NOP选项实现四字节对齐,例如3bytes长的WSOPT选项,linux在添加这个tcp选项的时候,就会在这个选项前面加一个1byte的NOP选项凑成4bytes。
TCP选项的格式有两种,一种是单字节长的TCP选项如EOL和NOP。另外一种是包含1byte的kind,1byte的length,在加上选项的数据。除了EOL和NOP选项外,其他的TCP选项都是后一种格式,且为了兼容,协议要求后续如果扩展其他tcp选项同样需要采用后一种格式。
RFC1122协议规定TCP接收端必须能够处理任意TCP包中的选项,对于不能识别的TCP选项则采取忽略该选项的办法。其中有一些选项如EOL、NOP、MSS等是协议规定必须支持的,同时协议要求将来新增的TCP选项都需要由length域,这样TCP实现不能识别这个选项的时候就可以跳过这个选项。
二、EOL和NOP
EOL格式如下
+--------+
|00000000|
+--------+
Kind=0
这个选项用来指示TCP的选项列表结束,这个选项是用在所有TCP选项的后面,并不是每个TCP选项后面都需要这个选项来指示选项结束,只有在TCP选项列表结束后没有与TCP头中的Header Length字段指定的头长重合时候才需要使用EOL选项,另外这个选项并不一定放在TCP头(包括扩展头)的末尾。举个例子,假如Header Length指定的TCP头长为40bytes,其中第29-38bytes为TSOPT选项,则可以在第39byte处添加一个EOL选项指示选项列表结束,可以看到EOL并没有位于TCP头的结束位置的第40byte。对于最后一个byte RFC793协议规定需要以0来填充。注意这个EOL后面填充的0已经不属于TCP选项的一部分了。
NOP选项格式如下
+--------+
|00000001|
+--------+
Kind=1
这个选项可以使用在选项之间或者结尾处,比如,为了使3bytes的WSOPT选项在四字节对齐的边界处结束,可以在WSOPT选项之前添加一个NOP选项,这样整个选项长度为4bytes,更容易对齐。但是按照RFC793协议规定,发送端并不保证会填充NOP选项来让其他选项达到对齐的目的,因此接收端也应该准备好接收非四字节对齐的WSOPT选项。也就是说同样的几个TCP选项可以有不同的选项排列顺序,即使是相同的排列顺序也可能因为NOP和EOL等等而有不同的排列布局。
最后从linux实现的角度来说,linux本身发送TCP数据包的时候并不会添加EOL选项,而是通过添加一个或者多个NOP选项来实现整个TCP头长的四字节对齐(还记得我们之前说过TCP头中的Header Length字段的单位是32-bit word,因此TCP的头长一定是4bytes的整数倍)。但是linux在接收数据包的时候支持解析EOL选项。另外协议虽然没有限制TCP选项的排列顺序,但是linux实现上会按照一定的顺序排列TCP选项。原因是虽然协议没有限定options的顺序,但是互联网上有些设备对这个顺序是比较敏感的,一些特定的options顺序可能会引起问题。
三、MSS
Maximum segment size(MSS)格式如下
+--------+--------+---------+--------+
|00000010|00000100| max seg size |
+--------+--------+---------+--------+
Kind=2 Length=4
Maximum segment size(MSS)是TCP期望从对端接收的最大的报文长度,自然也是对端在发送报文的时候的最大报文长度,注意MSS值仅指示TCP数据长度,并不包含关联的TCP头和IP头的长度。当连接在建立的时候,每个endpoint通常会在对应的SYN包中通过MSS option通告对方自己的MSS,按照RFC1122规定如果没有MSS选项提供则会使用默认的536bytes作为MSS(注意原始的RFC793协议是说没有提供MSS选项的时候可以发送任意大小的包,RFC1122修正了该说法)。还有一点需要注意由于目前网卡普遍支持TSO、GSO功能,在开启这些功能的前提下,协议栈中的TCP层可能会按照MSS的整数倍发包,然后再由网卡硬件来对TCP分段,这样减轻了CPU的处理压力。后面为了方便讨论窗口管理等特性,我们还是按照TCP层最大包不超过MSS来讨论。
在IPV6的jumbogram中(RFC2675),如果接收端接收到的MSS值为65535时候,标识真实的MSS需要根据PMTU值来确定。即MSS=PMTU-60。(jumbogram是IPV6中一种发送超大IP报文的协议特性,PMTU是接收端和发送端链路之间所有设备的最小MTU。)
RFC6691重新澄清了MSS选项的相关说明,并修正了之前几个RFC的错误说法。RFC6691明确规定在MSS选项中传递的MSS值为MTU减去IP基本头(ipv4为20bytes,IPV6为40bytes)和TCP基本头(20bytes)的值,不考虑扩展头。发送端负责发送数据前在这个MSS值的基础上扣除扩展头长度得出真实传输数据的长度。
四、WSOPT
WSOPT格式如下
+---------+---------+---------+
| Kind=3 |Length=3 |shift.cnt|
+---------+---------+---------+
RFC1323为长肥管道提供了两个高性能扩展,一个是WSOPT选项另外一个是TSOPT选项。长肥管道是指带宽时延积很大的网络。
我们在介绍TCP头结构的时候提到过Window Size字段,这个字段占16位,最大为2^16-1,在长肥管道中,当发送端TCP需要通告更大的接收窗口的时候,就需要通过WSOPT选项了。当使用WSOPT选项的时候,接收窗口的实际大小则为Window Size<<shift.cnt,其中shift.cnt按照协议最大只能为14,当接收端接收到的shift.cnt大于14的时候,则按照14来处理Window Size。
WSOPT选项只能在SYN包中发送,因此当TCP连接建立起来后,window scale就固定了。一般在TCP实现上会有一个最大接收缓存,进而决定了最大接收窗口和window scale。WSOPT选项将原有的16位Window Size扩展到近30位大小(大约1GB)可以有效提升TCP允许使用的接收缓存,进而提升长肥网络的性能。
如果要使能window scale,需要发送端在SYN包中发送WSOPT选项,接收端在SYN-ACK包中同样发送WSOPT。注意协商window scale过程中协议要求不能对SYN和SYN-ACK报文头中的window size应用WSopt选项。WSOPT中的shift.cnt可以为0,标识window scale factor为1(即2^0=1),即接收窗口的实际大小即为Window Size。如果发送端发送了WSOPT选项但是没有收到对端的WSOPT选项,则需要将自己的window scale factor设置为1。
发送端和接收端都各有一个接收窗口和一个发送窗口,因此总共四个窗口,共维护4个scale factor。假设发送端接收窗口的scale factor为R,发送窗口的scale factor为S,则对应的接收端的接收窗口scale factor为R,发送窗口scale factor为S。在协商好两端的scale factor后,之后接收到的数据包中的Window Size字段自动进行scale factor,发送出去的数据包中的这个字段则为实际接收窗口右移scale factor后的结果。
五、SACK-Permitted和SACK
SACK-Permitted格式
Kind: 4
+---------+---------+
| Kind=4 | Length=2|
+---------+---------+
SACK格式
Kind: 5
Length: Variable
+--------+--------+
| Kind=5 | Length |
+--------+--------+--------+--------+
| Left Edge of 1st Block |
+--------+--------+--------+--------+
| Right Edge of 1st Block |
+--------+--------+--------+--------+
| |
/ . . . /
| |
+--------+--------+--------+--------+
| Left Edge of nth Block |
+--------+--------+--------+--------+
| Right Edge of nth Block |
+--------+--------+--------+--------+
之前我们介绍过TCP的滑窗和ACK机制,我们再来简单的举个例子,假设接收端依序接收到系列号为2100的byte,序列号2100之前的byte都已经按序接收到了,接着因为乱序传输或者丢包的原因,接收端并没有接收到系列号为2101的TCP数据包,而是收到了系列号为2201的TCP报文并且长度为100byte。也就是说接收端缺少了2101-2200byte的数据,我们称接收端这种情况在滑窗上面形成了一个洞(hole)。如下图红色部分表示接收端已经接收到的数据。
此时接收端给发送端返回ACK报文的时候,TCP头中的ack number字段只能填写2101,还记得我们之前说过ack number表示接收端期望接收到的下一个byte的系列号吧,它是已经收到的连续报文中的最大序列号加1。注意是连续报文,因为2100和2201之间有洞,因此此时ack number只能是2101,发送端在接收到2101这个ack number后,并不能知道接收端实际上已经接收到了2201-2300byte,因而可能会在重传2101-2200byte的同时也会重传2201-2300byte的数据。那么有了SACK后,接收端就可以通过SACK来告诉发送端已经接收到了2201-2300byte的数据,这个就是一个SACK块(SACK block),同时结合ack number,发送端就可以仅仅只是重传2101-2200byte的数据,而不需要重传2201-2300byte的数据了。
一个endpoint如果在SYN包或者SYN-ACK包中解析处SACK-Permitted选项,那么就说明对端支持SACK扩展。那么本端就可以把收到的不连续报文信息发送给对端来帮助对端高效重传了。通常SACK-Permitted选项一般是在SYN包中发送,一旦收到对端SACK-Permitted选项后,SACK选项则可以在任意包中传输。Linux中可以通过/proc/sys/net/ipv4/tcp_sack控制是否使能SACK功能,设置为1时候使能,设置为0时候关闭SACK功能。因为SACK选项和TCP重传以及拥塞控制等等由比较大的关系,后面我们讲到这些的时候再来详细介绍。
六、TSOPT
TSOPT格式如下
Kind: 8
Length: 10 bytes
+-------+-------+---------------------+---------------------+
|Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)|
+-------+-------+---------------------+---------------------+
1 1 4 4
TSOPT选项也叫做timestamp选项,有时也会写为TSopt。如上面介绍WSOPT时候所说,TSOPT也是RFC1323为了改善长肥管道而提出的一个TCP扩展。当使用这个选项的时候,发送方在TSval处放置一个时间戳,接收方则会把这个时间通过TSecr返回来。因为接收端并不会处理这个TSval而只是直接从TSecr返回来,因此不需要双方时钟同步。这个时间戳一般是一个单调增的值,RFC1323建议这个时间戳每秒至少增加1。其中在初始SYN包中因为发送方没有对方时间戳的信息,因此TSecr会以0填充,TSval则填充自己的时间戳信息。
在RFC1323中,TSOPT主要有两个用途一个是RTTM(round-trip time measurement)即根据ACK报文中的这个选项测量往返时延,另外一个用途是PAWS(protect against wrapped sequence numbers),即防止同一个连接的系列号重叠。另外还有一些其他的用途,如SYN-cookie、 Eifel Detection Algorithm 等等。关于RTTM我们留到TCP重传部分进行介绍,此处我们简单介绍一下PAWS。
PAWS假设接收到的每个TCP包中的TSval都是随时间单调增的,基本思想就是如果接收到的一个TCP包中的TSval小于刚刚在这个连接上接收到的报文的TSval,则可以认为这个报文是一个旧的重复包而丢掉。实际上接收到的TCP报文的系列号如果落在接收窗口外面就可以丢弃,但是对于一些高速不稳定网络,可能会出现一种情况,就是系列号翻转后,之前某个无效的重传包系列号满足条件,落在了接收窗口内,这个时候仅仅依靠系列号就不足以鉴定这个TCP报文的有效性了,结合TSOPT则可以通过时间戳选项来进一步过滤旧的重复包。
类似PAWS,实际上时间戳作为了系列号的一个扩展,在同一个连接上单调增。RFC6191进一步利用这个特点,在同一个连接的不同实例间时间戳单调增的时候,可以利用这个时间戳区分同一个连接的不同实例的时候,即使在TIME-WAIT状态下也允许建立连接。
RFC7323明确在TCP头中的ACK标志位有效的时候TSecr字段才有效,如果ACK标志位没有置位的时候,发送端应该把TSecr置为0。当发送出去的数据包ACK标志位置位的时候,发送端必须在TSecr中回显一个最近接收到的TSval。当ACK标志位没有置位的时候,接收端必须忽视TSecr字段。
TCP可以在初始的SYN包中发送TSopt选项,但是接收端只有在接收到的初始SYN报文中解析到TSopt选项的时候才允许在SYN-ACK报文中发送TSopt选项。一旦TCP通信的两端通过SYN报文和SYN-ACK报文协商好TSopt选项后,在这个连接随后的非RST报文中,TSopt选项必须被发送。一旦接收到一个不带由TSopt选项的非RST报文的时候,TCP应该静默的丢弃这个报文(注意是应该should,不是必须must)。TCP不能(must not)因为缺少预期的TSopt选项而中止一个TCP连接。注意这里是协议的要求,实现上并不一定会静默的丢弃这个数据包,比如linux在协商好TSopt后,收到没有TSopt选项的数据也会正常接收,后面文章会有wireshark示例。
当在三次握手中没有协商TSopt选项而在随后的数据传输中接收到TSopt选项的时候,TCP必须忽视这个TSopt选项然后正常处理这个TCP报文。在TCP同开的时候如果一个SYN报文包含TSopt选项,另外一个SYN报文不包含TSopt选项,那么两端都可以在随后的SYN-ACK报文中发送TSopt选项。
另外TSopt选项还有两个重要作用,一个是RACK重传,另外一个是Eifel探测算法,后面的文章我们会专门进行实例介绍。Linux中/proc/sys/net/ipv4/tcp_timestamps可以设置是否启用TSopt选项,这个参数设置为0的时候TCP连接就不会使用TSopt选项。
七、FOC
FOC选项的格式如下
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Kind | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ Cookie ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Kind 1 byte: value = 34
Length 1 byte: range 6 to 18 (bytes); limited by
remaining space in the options field.
The number MUST be even.
Cookie 0, or 4 to 16 bytes (Length - 2)
Fast Open选项用来请求或者发送一个FOC(Fast Open Cookie),当cookie域为空的时候,client使用这个选项来从服务器请求一个FOC。当cookie域非空的时候,服务器可使用这个选项来把cookie传递给client,或者client可以使用这个选项来执行TFO。
最小的cookie大小是4byte,虽然图示中cookie是32位对齐的,但不是强制要求的,当数据包中不带SYN标志、Length值无效或者TFO功能没有打开的时候,需要忽略这个选项。
八、wireshark抓包示例
我们看一下之前FastOpen第一次正常连接SYN包中请求FOC时候对应的tcp选项截图如下,限于篇幅不再逐步讲解,现在我们讲解了TCP选项,建议下载wireshark文件,对照本节对TCP选项的讲解在看一遍wireshark中的TCP选项。
补充说明
1、linux支持的tcp选项可以参考TCPOPT_NOP宏定义附近定义的选项,写入tcp选项可以参考代码tcp_options_write,本文中wireshark中TCP选项的特定顺序也是在tcp_options_write写入的。
2、linux对于MSS的处理可以参考tcp_current_mss
3、第二版TCPIP详解P609中对于TSOPT的第二个字段描述为Timestamp Echo Retry,实际应该是Timestamp Echo Reply
TCP系列08—连接管理—7、TCP 常见选项(option)的更多相关文章
- TCP系列02—连接管理—1、三次握手与四次挥手
一.TCP连接管理概述 正如我们在之前所说TCP是一个面向连接的通信协议,因此在进行数据传输前一般需要先建立连接(TFO除外),因此我们首先来介绍TCP的连接管理. 通常一次完整的TCP数据传输一般包 ...
- TCP系列07—连接管理—6、TCP连接管理的状态机
经过前面对TCP连接管理的介绍,我们本小节通过TCP连接管理的状态机来总结一下看看TCP连接的状态变化 一.TCP状态机整体状态转换图(截取自第二版TCPIP详解) 二.TCP连接建立 ...
- TCP系列05—连接管理—4、TCP连接的ISN、连接建立超时及TCP的长短连接
一.TCP连接的ISN 之前我们说过初始建立TCP连接的时候的系列号(ISN)是随机选择的,那么这个系列号为什么不采用一个固定的值呢?主要有两方面的原因 防止同一个连接的不同实例(di ...
- TCP系列03—连接管理—2、TCP连接的同时打开和同时关闭
在前面的内容中我们介绍了TCP连接管理中最常见的三次握手方式和四次挥手的方式.但是有可能A和B两端同时执行主动打开并连接对方或者同时执行主动关闭连接(尽管发生这种情况的可能性比较低低),这个时候的流程 ...
- TCP系列04—连接管理—3、TCP连接的半打开和半关闭
在前面部分我们我们分别介绍了三次握手.四次挥手.同时打开和同时关闭,TCP连接还有两种场景分别是半打开(Half-Open)连接和半关闭(Half-Close)连接.TCP是一个全双工(Full-Du ...
- TCP系列09—连接管理—8、TCP Reset
我们在介绍TCP头的时候,提到过其中有个RST标志位.当一个TCP报文中这个标志位打开的时候,我们叫做reset包(严格的说应该叫做reset段,但是很多时候段包帧并不加以区分)或者简单称呼为rese ...
- TCP系列06—连接管理—5、TCP fastopen(TFO)
一.TFO背景 当前web和web-like应用中一般都是在三次握手后开始数据传输,相比于UDP,多了一个RTT的时延,即使当前很多应用使用长连接来处理这种情况,但是仍然由一定比例的短连接,这额外多出 ...
- TCP系列10—连接管理—9、syncookie、fastopen与backlog
这部分内容涉及较多linux实现,可以跳过. 一.listen系统调用对backlog的处理 当socket处于LISTEN或者CLOSED状态时,fastopen队列的长度可以通过TCP_FASTO ...
- TCP的运输连接管理
TCP的运输连接管理 TCP是面向连接的协议,有三个阶段:连接建立.数据传送 和 连接释放.运输连接的管理就是使运输连接的简历和释放都能正常地进行. 在TCP连接建立过程中要解决一下三个问题: 1. ...
随机推荐
- 【JVM】上帝视角看JVM内存模型,分而治之论各模块详情
1. 上帝视角 [树看JVM] [图看JVM] 2. 分而治之 2.1 堆区 构成:堆区由新生代和老年代组成,新生代中包含伊甸区(Eden).幸存者区(survivor from .survivor ...
- Java使用zxing生成解读QRcode二维码
1.maven的pom配置jar包,如果不实用maven请手动下载jar包 <dependency> <groupId>com.google.zxing</groupId ...
- python教程(三)·函数与模块
函数,这和数学中的函数有点关联,但又不是完全等价 概念 不说的这么官方,我就已自己的理解来表达 ^_^ 在数学中,把一个或多个值(输入x)进行一定的计算或者映射,得到一个值(输出y),这个计算或者映射 ...
- JZ2440开发板:用按键点亮LED灯(学习笔记)
本文是对韦东山嵌入式第一期学习的记录之一,如有您需要查找的信息,可以继续往下阅读. 想要用按键点亮LED灯,就需要知道按键和LED灯的相关信息,这样才可以进行之后的操作.阅读JZ2440的原理图,可以 ...
- 北京Uber优步司机奖励政策(11月9日~11月15日)
用户组:人民优步“关羽组”(适用于11月9日-11月15日)奖励政策: 滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月 ...
- JQuery.extend扩展实现同步post请求
有时需要在jQuery中实现同步post请求,而jquery自带的是异步,需要通过JQuery.extend扩展. 支持ie和firefox,方法转载而来.需要在submit前将form.append ...
- String、StringBuffer、StringBuilder的区别和解析
1.三个类之间的关系 他们都是通过字符数组来实现的,继承关系 String:字符串常量,不可变类 StringBuffer:字符串变量,可变类,线程安全 StringBuilder:字符串变量,可变类 ...
- 「日常训练」Regular Bridge(Codeforces Round 306 Div.2 D)
题意与分析 图论基础+思维题. 代码 #include <bits/stdc++.h> #define MP make_pair #define PB emplace_back #defi ...
- 【JAVA】关于java中 类.class.getResource("/").getPath()获取路径有空格的问题
写了一个web工程,在本地测试正确,但是部署到服务器上就出现错误.原因是读取不到配置文件. 后来从打印出来的文件路径中发现是用Java的class.getResource("/") ...
- 【springmvc+mybatis项目实战】杰信商贸-4.maven依赖+PO对+映射文件
上一篇我们附件的增删改查功能全部完成.但是我们的附件有一个字段叫做“类型”(ctype),这里我们要使用数据字典,所以对于这一块我们要进行修改. 首先介绍一下数据字典 数据字典它是一个通用结构,跟业务 ...