之前系统学习过TCP协议,但很多地方并不是特别清晰,今天结合抓包情况,再拾一下拾一下>.<
首先看一下TCP数据包的标志位:
SYN:简写为S,同步标志位,用于建立会话连接,同步序列号;
ACK: 简写为.,确认标志位,对已接收的数据包进行确认;
FIN: 简写为F,完成标志位,表示我已经没有数据要发送了,即将关闭连接;
PSH:简写为P,推送标志位,表示该数据包被对方接收后应立即交给上层应用,而不在缓冲区排队;
RST:简写为R,重置标志位,用于连接复位、拒绝错误和非法的数据包;
URG:简写为U,紧急标志位,表示数据包的紧急指针域有效,用来保证连接不被阻断,并督促中间设备尽快处理;
ps:(赋值粘贴自:https://juejin.cn/post/6844904070000410631,写的很详细)
三次握手
第一次握手(SYN):客户端向服务器发送一个 SYN(Synchronize)报文段,用来请求建立连接。该报文段包含客户端的初始序列号 ISN©。
客户端 --> 服务器: SYN=1, Seq=ISN©=80830526
192.168.1.7.36268 > 192.168.1.9.22: Flags [S], cksum 0x01dc (correct), seq 80830526, win 62440, options [mss 4460,sackOK,TS val 3708280593 ecr 0,nop,wscale 7], length 0
第二次握手(SYN-ACK):服务器收到 SYN 报文后,返回一个 SYN-ACK 报文,表示同意建立连接,并向客户端发送自己的初始序列号 ISN(s)。ACK 字段确认了客户端的 ISN。
服务器 --> 客户端: SYN=1, ACK=1, Seq=ISN(s)=1265140446, Ack=ISN©+1=80830527
192.168.1.9.22 > 192.168.1.7.36268: Flags [S.], cksum 0x97c6 (incorrect -> 0x6c95), seq 1265140446, ack 80830527, win 62970, options [mss 4210,sackOK,TS val 591110811 ecr 3708280593,nop,wscale 7], length 0
第三次握手(ACK):客户端收到服务器的 SYN-ACK 报文后,发送一个确认报文段 ACK,确认服务器的 ISN,表示连接建立成功。
客户端 --> 服务器: ACK=1, Seq=ISN©+1, Ack=ISN(s)+1,二者均被tcpdump转换为了相对序列号:1
192.168.1.7.36268 > 192.168.1.9.22: Flags [.], cksum 0x9a32 (correct), seq 1, ack 1, win 488, options [nop,nop,TS val 3708280593 ecr 591110811], length 0
完整的三次握手过程见:
13:21:19.951917 00:00:00:00:01:01 > 00:00:00:00:01:02, ethertype IPv4 (0x0800), length 74: (tos 0x10, ttl 63, id 13722, offset 0, flags [DF], proto TCP (6), length 60)192.168.1.7.36268 > 192.168.1.9.22: Flags [S], cksum 0x01dc (correct), seq 80830526, win 62440, options [mss 4460,sackOK,TS val 3708280593 ecr 0,nop,wscale 7], length 0
13:21:19.951973 00:00:00:00:01:02 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)192.168.1.9.22 > 192.168.1.7.36268: Flags [S.], cksum 0x97c6 (incorrect -> 0x6c95), seq 1265140446, ack 80830527, win 62970, options [mss 4210,sackOK,TS val 591110811 ecr 3708280593,nop,wscale 7], length 0
13:21:19.952050 00:00:00:00:01:01 > 00:00:00:00:01:02, ethertype IPv4 (0x0800), length 66: (tos 0x10, ttl 63, id 13723, offset 0, flags [DF], proto TCP (6), length 52)192.168.1.7.36268 > 192.168.1.9.22: Flags [.], cksum 0x9a32 (correct), seq 1, ack 1, win 488, options [nop,nop,TS val 3708280593 ecr 591110811], length 0
三次握手的协商:在三次握手过程中,双方会协商一些关键参数,例如初始序列号(ISN),窗口大小(Window Size),以及一些 TCP 选项(如最大报文段长度 MSS)。这些参数的协商确保了后续的数据传输可以顺利进行。我们仔细看下刚才三次握手过程中,客服端服务端协商的信息:
-
窗口大小 窗口大小(Window Size),表示接收方愿意接收的字节数,通常用于流量控制。
-
mss 4210: 最大报文段大小(Maximum Segment Size),表示单个 TCP 段允许的最大数据量,这里是 4210 字节。
-
sackOK: 选择性确认(Selective Acknowledgment)允许,表示接收方支持 SACK 选项,允许更有效地处理丢失的数据包。
-
TS val 591110811: 时间戳(Timestamp)值,用于 RTT 测量和防止序列号重用攻击。
-
ecr 3708280593: 时间戳回显回复(Echo Reply),表示时间戳的回复值,用于对方确认 RTT。
-
nop: No Operation,用于对齐 TCP 选项字段。
-
wscale 7: 窗口扩大因子(Window Scale),是一个用于扩大窗口大小的因子。这里的因子是 7,即窗口大小可以乘以 2^7。
传输过程
拓扑:192.168.1.7 ·············> 192.168.1.9
传输过程中,比较简单,通信双方分别确认对方的数据报文序列号
15:43:11.911729 IP 192.168.1.7.60704 > 192.168.1.9.ssh: Flags [P.], seq 33:1545, ack 43, win 502, options [nop,nop,TS val 531487780 ecr 3452696654], length 1512
15:43:11.911774 IP 192.168.1.9.ssh > 192.168.1.7.60704: Flags [P.], seq 43:1155, ack 1545, win 501, options [nop,nop,TS val 3452696655 ecr 531487780], length 1112
15:43:11.911839 IP 192.168.1.7.60704 > 192.168.1.9.ssh: Flags [.], ack 1155, win 501, options [nop,nop,TS val 531487780 ecr 3452696655], length 0
15:43:11.917012 IP 192.168.1.7.60704 > 192.168.1.9.ssh: Flags [P.], seq 1545:1593, ack 1155, win 501, options [nop,nop,TS val 531487785 ecr 3452696655], length 48
15:43:11.919997 IP 192.168.1.9.ssh > 192.168.1.7.60704: Flags [P.], seq 1155:1751, ack 1593, win 501, options [nop,nop,TS val 3452696663 ecr 531487785], length 596
15:43:11.920055 IP 192.168.1.7.60704 > 192.168.1.9.ssh: Flags [.], ack 1751, win 501, options [nop,nop,TS val 531487788 ecr 3452696663], length 0
四次挥手
截取了四次挥手的过程,如下:
16:00:35.625181 IP 192.168.1.7.60706 > 192.168.1.9.ssh: Flags [F.], seq 3361, ack 4015, win 501, options [nop,nop,TS val 532531491 ecr 3453740368], length 0
16:00:35.626116 IP 192.168.1.9.ssh > 192.168.1.7.60706: Flags [F.], seq 4015, ack 3362, win 501, options [nop,nop,TS val 3453740370 ecr 532531491], length 0
16:00:35.626173 IP 192.168.1.7.60706 > 192.168.1.9.ssh: Flags [.], ack 4016, win 501, options [nop,nop,TS val 532531492 ecr 3453740370], length 0
客户端 192.168.1.7 通过发送 FIN 数据包请求关闭连接。
服务器 192.168.1.9 确认了客户端的请求,并发送了自己的 FIN 请求,表示它也准备关闭连接。
客户端最后发送一个 ACK 数据包,确认服务器的 FIN 请求,至此连接关闭。
虽然这个过程可以用三个报文来表示,但它仍然遵循四次挥手的逻辑。特别是在服务器发送的第二个报文中,FIN 和 ACK 是同时发送的,所以看起来像是三次通信,但实际上包含了四次挥手的全部内容。
RST强制中断
发送 RST 报文是强制关闭 TCP 连接的方式,直接中断连接而不进行四次挥手的正常关闭过程。这种方法用于快速和直接地终止连接,通常在出现错误或异常情况下使用。比如连接超时、程序异常终止等。其行为如下:
-
立即终止连接:RST 报文会立即终止连接,无论连接的状态如何。发送 RST 报文的一方(可以是客户端也可以是服务端)将直接通知对方,表示连接不能再继续进行。
-
不进行四次挥手:与正常关闭连接时的四次挥手不同,RST 报文不需要等待确认,也不会进行正常的连接关闭过程。RST 报文会立即中断数据传输,并且不进行数据的有序释放。
-
丢失未发送数据:由于 RST 报文强制关闭连接,任何在连接中尚未发送的数据都会被丢失。
尝试使用如下命令构造了RST报文的场景
apt install dsniff # 安装tcpkill
# 如果有从eth0 到192.168.1.9的连接就中断掉
tcpkill -i eth0 host 192.168.1.9
# 尝试和192.168.1.9建立连接
telnet 192.168.1.9 22
抓包来看
16:42:32.176289 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [S], seq 1907790919, win 64240, options [mss 1460,sackOK,TS val 535048037 ecr 0,nop,wscale 7], length 0
16:42:32.176301 IP 192.168.1.9.ssh > 192.168.1.7.60722: Flags [S.], seq 3539316215, ack 1907790920, win 65160, options [mss 1460,sackOK,TS val 3456256920 ecr 535048037,nop,wscale 7], length 0
16:42:32.176402 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [.], ack 1, win 502, options [nop,nop,TS val 535048037 ecr 3456256920], length 0
16:42:32.182200 IP 192.168.1.9.ssh > 192.168.1.7.60722: Flags [P.], seq 1:43, ack 1, win 510, options [nop,nop,TS val 3456256926 ecr 535048037], length 42: SSH: SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
16:42:32.182267 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [.], ack 43, win 502, options [nop,nop,TS val 535048043 ecr 3456256926], length 0
16:42:32.348727 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [.], ack 43, win 502, options [nop,nop,TS val 535048210 ecr 3456256926], length 0
16:42:32.348836 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [R], seq 1907790920, win 0, length 0
16:42:32.348836 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [R], seq 1907791430, win 0, length 0
16:42:32.348836 IP 192.168.1.7.60722 > 192.168.1.9.ssh: Flags [R], seq 1907792450, win 0, length 0
telnet尝试和192.168.1.9建立连接,但是连接建立完成后,客户端直接发送RST报文,强制终止了连接,期间并发生四次挥手。