使用tcpdump观察TCP头部信息和三次握手四次挥手
前言
本篇文章为笔者的读书笔记,未经允许请勿转载。如果对你有帮助记得点个赞(●’◡’●)
本次实验是目的在于弄清tcp头部信息和三次握手四次挥手的细节,并且补充一些tcp协议的常用知识。读本文需要一些前置知识,具体请参照《linux高性能服务器编程》p32-p37。
实验测试机是阿里云ECS服务器和本地虚拟机。不同于书本上,它的测试机是在同一局域网内。
实验开始
-
在服务器上将echo服务打开,具体请看笔者写的tcpdump观察ARP通信过程,这里不多赘述。
-
在本地虚拟机上开启两个终端
-
监听终端
root@ubuntu:/# tcpdump -S -i ens33 -ntx '(src 192.168.1.7 and dst 120.79.72.214) or (src 120.79.72.214 and dst 192.168.1.7)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
-S 序号和确认号以绝对值形式显示(如果是相对值显示,则从第三个报文开始,seq和ack相对ISN的偏移), -i 监听指定网络接口ens33,-n 指定将每个监听到数据包中的域名转换成IP地址后显示,-t 在输出的每一行不打印时间戳, -x 把协议头和包内容都原原本本的显示出来(tcpdump会以16进制和ASCII的形式显示)。后面src表示源ip,dst表示目的ip,这样写可以起到一个滤包的作用。
- 测试终端
root@ubuntu:/home/marvel# telnet 120.79.72.214 7
Trying 120.79.72.214...
Connected to 120.79.72.214.
Escape character is '^]'.
1
1
^]
telnet> q
Connection closed.
root@ubuntu:/home/marvel#
- 监听终端
root@ubuntu:/home/marvel# tcpdump -S -i ens33 -ntx '(src 192.168.1.7 and dst 120.79.72.214) or (src 120.79.72.214 and dst 192.168.1.7)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
1)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [S], seq 3843050562, win 64240, options [mss 1460,sackOK,TS val 2363801315 ecr 0,nop,wscale 7], length 0
0x0000: 4510 003c 3496 4000 4006 8341 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c42 0000 0000
0x0020: a002 faf0 8303 0000 0204 05b4 0402 080a
0x0030: 8ce4 bee3 0000 0000 0103 0307#60字节(16位,两字节,一组,一共30组),同步报文
2)IP 120.79.72.214.7 > 192.168.1.7.39804: Flags [S.], seq 3051969109, ack 3843050563, win 65160, options [mss 1400,sackOK,TS val 743316774 ecr 2363801315,nop,wscale 7], length 0
0x0000: 4514 003c 0000 4000 3406 c3d3 784f 48d6
0x0010: c0a8 0107 0007 9b7c b5e9 5a55 e510 4c43
0x0020: a012 fe88 547b 0000 0204 0578 0402 080a
0x0030: 2c4e 1d26 8ce4 bee3 0103 0307#60字节(16位,两字节,一组,一共30组),同步确认报文
3)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [.], ack 3051969110, win 502, options [nop,nop,TS val 2363801340 ecr 743316774], length 0
0x0000: 4510 0034 3497 4000 4006 8348 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c43 b5e9 5a56
0x0020: 8010 01f6 82fb 0000 0101 080a 8ce4 befc
0x0030: 2c4e 1d26#52字节(16位,两字节,一组,一共26组),确认报文
4)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [P.], seq 3843050563:3843050566, ack 3051969110, win 502, options [nop,nop,TS val 2363810557 ecr 743316774], length 3
0x0000: 4510 0037 3498 4000 4006 8344 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c43 b5e9 5a56
0x0020: 8018 01f6 82fe 0000 0101 080a 8ce4 e2fd
0x0030: 2c4e 1d26 310d 0a#55字节,length(数据长度)=55-52,
5)IP 120.79.72.214.7 > 192.168.1.7.39804: Flags [.], ack 3843050566, win 510, options [nop,nop,TS val 743326016 ecr 2363810557], length 0
0x0000: 4514 0034 d00d 4000 3406 f3cd 784f 48d6
0x0010: c0a8 0107 0007 9b7c b5e9 5a56 e510 4c46
0x0020: 8010 01fe 375f 0000 0101 080a 2c4e 4140
0x0030: 8ce4 e2fd#52字节,确认报文
6)IP 120.79.72.214.7 > 192.168.1.7.39804: Flags [P.], seq 3051969110:3051969113, ack 3843050566, win 510, options [nop,nop,TS val 743326016 ecr 2363810557], length 3
0x0000: 4514 0037 d00e 4000 3406 f3c9 784f 48d6
0x0010: c0a8 0107 0007 9b7c b5e9 5a56 e510 4c46
0x0020: 8018 01fe fc46 0000 0101 080a 2c4e 4140
0x0030: 8ce4 e2fd 310d 0a#55字节,length(数据长度)=55-52。
7)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [.], ack 3051969113, win 502, options [nop,nop,TS val 2363810582 ecr 743326016], length 0
0x0000: 4510 0034 3499 4000 4006 8346 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c46 b5e9 5a59
0x0020: 8010 01f6 82fb 0000 0101 080a 8ce4 e316
0x0030: 2c4e 4140#52字节
8)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [F.], seq 3843050566, ack 3051969113, win 502, options [nop,nop,TS val 2363813862 ecr 743326016], length 0
0x0000: 4510 0034 349a 4000 4006 8345 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c46 b5e9 5a59
0x0020: 8011 01f6 82fb 0000 0101 080a 8ce4 efe6
0x0030: 2c4e 4140#52字节,
9)IP 120.79.72.214.7 > 192.168.1.7.39804: Flags [F.], seq 3051969113, ack 3843050567, win 510, options [nop,nop,TS val 743329323 ecr 2363813862], length 0
0x0000: 4514 0034 d00f 4000 3406 f3cb 784f 48d6
0x0010: c0a8 0107 0007 9b7c b5e9 5a59 e510 4c47
0x0020: 8011 01fe 1d86 0000 0101 080a 2c4e 4e2b
0x0030: 8ce4 efe6#52字节
10)IP 192.168.1.7.39804 > 120.79.72.214.7: Flags [.], ack 3051969114, win 502, options [nop,nop,TS val 2363813889 ecr 743329323], length 0
0x0000: 4510 0034 349b 4000 4006 8344 c0a8 0107
0x0010: 784f 48d6 9b7c 0007 e510 4c47 b5e9 5a5a
0x0020: 8010 01f6 82fb 0000 0101 080a 8ce4 f001
0x0030: 2c4e 4e2b#52字节
^C
10 packets captured
10 packets received by filter
0 packets dropped by kernel
root@ubuntu:/home/marvel#
这里面信息量巨大,待我们来慢慢分析里面的细节!
1. 延迟确认
1)-3)是3次握手,8)-10)是4次挥手,但是这里只有三段报文段。原因是,延迟确认,即服务器不会马上确认收到的数据,而是在一段延迟时间后查看本端是否有数据要发送 ,如果有,则和确认信息一起发出。延迟确认可以减少发送tcp报文段的数量。这里的四次挥手中服务器的确认报文和结束报文一次性发送给客户端了。(书上是在局域网内进行的,没有延迟确认)
2. 序号(seq)和确认号(ack)之间的关系
序号的相对值是相对于自身发出第一个tcp报文段中的seq
确认号的相对值是相对于对方发出第一个tcp报文段中的seq(以绝对值显示的就不用管啦)
序号的值除了两次开头的同步报文是由系统初始化为某个随机ISN。其他序号为上一条对方发送报文段的确认号,由图中的红色线条为代表(一来一回)。如果是一方连续发送报文段(连续去没回),则序列号为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移(这一点本文未涉及,读者在观察其他复杂通信过程中肯定会碰到)。
确认号的值,如果是在三次握手1)-3)和四次挥手8)-10)中,则ack=对方发送报文段的seq+1;如果是在数据传输过程中4)-7),ack=上一条对方发送报文段的seq+数据长度。例如本例中报文段5)的确认号(3843050566)=上一条对方发送报文段的seq(3843050563)+它的数据字节(3),得到的也就是报文段4)seq 3843050563:3843050566,冒号后面那个值。冒号后面的值是由tcpdump自行添加的,其值刚好为接收端ack的值。
总结:序列号的相对值是对字节流的标识,根据序列号的差值可以确定两个数据报之间数据的大小。3.TS val和ecr的关系
这两者的关系很少有人提及,咱们直接看维基百科的解释
There are two timestamp fields:
TS val:a 4-byte sender timestamp value (my timestamp)
ecr:a 4-byte echo reply timestamp value (the most recent timestamp received from you).
TCP timestamps are used in an algorithm known as Protection Against Wrapped Sequence numbers, or PAWS (see RFC 1323 for details). PAWS is used when the receive window crosses the sequence number wraparound boundary. In the case where a packet was potentially retransmitted it answers the question: “Is this sequence number in the first 4 GB or the second?” And the timestamp is used to break the tie.
TCP时间戳选项有助于在传输非常大的数据流时保护包装序列。TCP中的序列号字段只有32位,所以序列号大于2^32-1之后,序列号会绕回开始。
另一个作用计算RTT值。
The Timestamp option can be used to measure the round-trip time (RTT) of every packet that is acknowledged. This is done by including a Timestamp Value TSval in every segment that is sent. These TSval values are echoed by the opposite side of the connection in the Timestamp Echo Reply TSecr field. So, when a segment is ACKed, the sender of that segment can simply subtract their current timestamp from the TSecr value to compute an accurate Round Trip Time (RTT) measurement.
RTT = 当前时间 - 数据包中Timestamp选项的回显时间(这个回显时间是该数据包发出去的时间)4. TCP状态转移(书上p41-p42原话)
重点掌握ESTABLISHED,FIN_WAIT_2<–>CLOSE_WAIT,TIME_WAIT(2MSL)
首先讨论服务器的典型状态转移过程(虚线路径),此时说的连接状态都是指该连接的服务器端的状态。
服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接,因此执行的是所谓的被动打开。服务器一旦监听到某个连接请求(收到同步报文段),就将该连接放入内核等待队列中,并向客户端发送带SYN标志的确认报文段。此时该连接处于SYN_RCVD状态。如果服务器成功地接收到客户端发送回的确认报文段,则该连接转移到ESTABLISHED。ESTABLISHED状态是连接双方能够进行双向数据传输的状态。
当客户端主动关闭连接时,服务器通过返回确认报文段使连接进入CLOSE_WAIT状态。这个状态的含义很明确:等待服务器应用程序关闭连接。通常,服务器检测到客户端关闭连接后,也会立即给客户端发送一个结束报文段来关闭连接。这将使连接转移到LAST_ACK状态,以等待客户端对结束报文段的最后一次确认。一旦确认完成,连接彻底关闭了。
其次讨论客户端(粗黑线),客户端通过connect系统调用(见第5章)主动与服务器建立连接。connect系统调用首先给服务器发送一个同步报文段,使连接转移到SYN_SENT状态。connect调用失败将使连接立即返回到初始的CLOSED状态。如果客户端成功收到服务器的同步报文段和确认,则connect调用成功返回,连接转移至ESTABLISHED状态。
当客户端执行主动关闭时,它将向服务器发送一个结束报文段,同时连接进入FIN_WAIT_1状态(到达这个状态时,会发送FIN告诉对方我要关闭连接)。若此时客户端收到服务器专门用于确认目的的确认报文段(比如图3-6中的TCP报文段5),则连接转移至FIN_WAIT_2状态(这个状态也称为半关闭状态,即客户端将自己的写buffer关闭了,但是读buffer还在)。当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这一对状态是可能发生半关闭的状态。此时如果服务器也关闭连接(发送结束报文段),则客户端将给予确认并进入TIME_WAIT状态,在这个状态客户端要等待2MSL(报文最大生存时间),才能完全关闭。5. FIN_WAIT_2状态(半关闭状态)
处于FIN_WAIT_2状态的客户端需要等待服务器发送结束报文段,才能转移至TIME_WAIT 状态,否则它将一直停留在这个状态。如果不是为了在半关闭状态下继续接收数据,连接长时间地停留在FIN_WAIT_2状态并无益处。连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:l/proc/sys/net/ipv4/tcp_max_orphans 和 /proc/syslnet/ipv4/tcp_fin_timeout。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中生存的时间。
6. TIME_WAIT状态
TIME_WAIT状态存在的原因有两
一:可靠地终止TCP连接。
TIME_WAIT状态为2MSL可以保证最糟糕情况也可以收到超时重传的FIN报文(确认报文半路嗝屁,最大存活时间MSL,重传的FIN报文到客户端花费的最大时间MSL,即收到重传的FIN报文小于等于2MSL)。
二:保证让迟来的TCP报文段有足够的时间被识别并丢弃。
我们反过来思考,如果没有TIME_WAIT状态,则应用程序能够立即创立一个和刚关闭的连接相似的连接(具有相同IP,和端口号)。这个新的,和原来相似的连接被称为原来连接的化身。新的化身可能接收到属于原来的连接的,携带应用程序数据的TCP报文段(迟到的报文段),这显然不应该发生的。7. 细说4)TCP报文段的详细信息
1.该报文段是一个数据报文段. Flags[P. ]:提示接收端应用程序应该立即从TCP接收缓冲区中读
走数据,为后续数据腾出空间; seq是序列号,“: ”后面的数值是下条接收方确认报文的确
认号; ack是确认号; win是窗大小,是TCP流量控制的-一个手段,如果是一个RWND (接收通
告窗口),它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样可以控制发送数据
的速度; options后面再说; length是指数据长度,没包括头部长度,即整个报文长度减去头
部长度。
2.十六进制数据具体分析。(16位一 组,一组两字节)
由于IP的选项很少用到,所以基本上都是20字节。
十六进制 | 十进制 | 二进制 | IP头部信息 |
---|---|---|---|
0x4 | 4 | IP版本号 | |
0x5 | 5 | 头部长度为5个32位(20字节) | |
0x10 | 16 | 0001 0000 | TOS选项中最小延时服务被开启 |
0x0037 | 55 | 数据报总长度,55字节 | |
0x3498 | 数据报标识 | ||
0x4 | 4 | 0100(前三位有用) | 设置了禁止分片标志 |
0x000 | 0 | 分片偏移 | |
0x40 | 64 | TTL被设为64 | |
0x06 | 6 | 协议字段为6,表示上层协议是TCP协议 | |
0x8344 | IP头部校验和 | ||
0xc0a8 0107 | 11000000.10101000.00000001.00000111 | 32位源端IP地址 192.168.1.7 | |
0x784f 48d6 | 01111000.01001111.01001000.11010110 | 32位目的端IP地址 120.79.72.214 |
十六进制 | 十进制 | 二进制 | TCP头部信息 |
---|---|---|---|
0x9b7c | 39804 | 源端口号 | |
0x0007 | 7 | 目的端口号 | |
0xe510 4c43 | 序列号 | ||
0xb5e9 5a56 | 确认号 | ||
0x8 | 8 | TCP头部长度为8个32位(32字节) | |
0x018 | 00011000(后面6位有效) | ACK和PSH标志位开启 | |
0x01f6 | 502 | 窗口大小 | |
0x82fe | 16位校验和 | ||
0x0000 | 没设置URG标志,紧急指针此处无意义 | ||
0x0101 | 两次kind的值均为1,两次nop操作,一般用于将TCP选项的总长度填充为4字节的整数倍 | ||
0x080a | kind=8,length=10(10字节),时间戳选项,较为准确的计算通信双方之间的RTT(回路时间) | ||
0x8ce4 e2fd | 2363810557 | 时间戳 | |
0x2c4e 1d26 | 743316774 | 回显应答时间戳 |
十六进制 | 十进制 | 二进制 | 数据段信息 |
---|---|---|---|
0x31 | 49 | 字符‘1’ | |
0x0d | 13 | 回车符 | |
0x0a | 10 | 换行符 |
一共三个字节。
8.Flags[p.],Flags[F.],Flags[.]中的点的含义
[.]这个点的含义通常是表示ACK(这个ACK和确认号不同,这是标志位),还可以表示URG
Flags[F.]、Flags[p.]和Flags[.]这里的点表示的就是ACK标志位开启。9.MSS(最大报文段长度)
TCP模块通常将MSS设置为【MTU(最大传输单元)-40】字节,40字节是TCP和IP的头部总和。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP和IP头部不包含选项字段),从而避免本机发生IP分片。以太网的MSS值是1460(1500-40)字节。
复位报文段
携带RST标志的报文段,即复位报文段。
产生复位报文段的四种情况
1.当客户端访问一个不存在的端口时。
2.客户端程序向服务器的某个端口发起连接,而该端口仍被处于TIME_WAIT状态的连接所占用时。
3.异常终止连接,TCP提供了异常终止一个连接的方法,即给对方发送一个复位报文段,一旦发送了复位报文段,发送端所有排队等待发送的数据都将被丢弃。
4.服务器(或客户端)关闭或者异常终止了连接,而对方没接收到结束报文段(比如网络故障),此时,客户端(或服务器)还维持着原来的连接。这种状态称为半打开状态,处于这种状态的连接称为半打开连接。如果客户端(或服务器)往处于半打开状态的连接写入数据,则对方将回应一个复位报文段。
TCP超时重传
TCP模块为每个TCP报文段都维护一个重传定时器,该定时器在TCP报文段第一次被发送时启动。如果超时时间内未收到接收方的应答,TCP模块将重传TCP报文段并重置定时器。至于下次重传的超时时间如何选择,以及最多执行多少次重传,就是TCP的重传策略。其实跟TCP超时重连的策略相似,在5次重传均失败的情况下,底层的IP和ARP开始接管,直到客户端放弃连接为止。