TCP 三次握手(TCP Three-way Handshake)
TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。
![TCP Three-way Handshake](assets/TCP Three-way Handshake.png)
第一次握手 - 建立连接
客户端发送连接请求报文段,将Control Flag中的SYN位置为1,Sequence Number(发送序号)为x;然后,客户端进入SYN_SEND状态,等待服务器的确认。这个客户端发送连接请求报文段,是一个SYN报文段。
SYN报文段是指,Control Flag中的SYN位为1的TCP报文段。
第二次握手 - 服务器收到SYN报文段
服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number(确认序号)为x+1(即Sequence Number+1);同时,自己还要发送SYN请求信息(即将SYN位置为1),Sequence Number(发送序号)为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态。
类似地,ACK报文段是指Control Flag中的ACK位为1的TCP报文段。而SYN+ACK报文段是指Control Flag中的SYN位和ACK位均为1的TCP报文段。
第三次握手 - 客户端收到服务器的SYN+ACK报文段
客户端收到服务器发送的SYN+ACK报文段后,将Acknowledgment Number(确认序号)设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端进入ESTABLISHED状态。当服务器端接收到这个ACK报文段后,服务器端进入ESTABLISHED状态,完成TCP三次握手。
而如果,客户端向服务器发送ACK报文段没有达到服务器端(或者延迟了),服务器会重新发送一个第二次握手阶段中的SYN+ACK报文段。
以下为动画演示三次握手的交互过程:
SYN攻击
在三次握手过程中,Server发送SYN+ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。
SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
netstat -nap | grep SYN_RECV
如何防御 SYN 攻击?
SYN攻击不能完全被阻止,除非将TCP协议重新设计。我们所做的是尽可能的减轻SYN攻击的危害,常见的防御 SYN 攻击的方法有如下几种:
- 缩短超时(SYN Timeout)时间
- 增加最大半连接数
- 过滤网关防护
- SYN cookies技术
三次握手时可能发生的丢包问题
TCP规定:对有数据的TCP segment 在接收方收到数据后,必须向发送方发送一个ACK数据包以表示确认。
以主机A尝试与B进行连接为例,我们来分别讨论一下,在进行三次握手时,不同握手阶段的握手数据包丢失时的情况。
第一次握手时丢包
第一个包,即A发给B的SYN 数据报丢包了,没有到达B。此后,A会周期性超时重传,直到收到B的确认(ACK+SYN数据报)。
第二次握手时丢包
第二个包,即B发给A的SYN +ACK 数据报丢包了,没有到达A。此后,B会周期性超时重传,直到收到A的确认(ACK数据报)。
这就需要一个超时时间让B将这个连接断开,否则这个连接就会一直占用A的SYN连接队列中的一个位置,大量这样的连接就会将B的SYN连接队列耗尽,让正常的连接无法得到处理。
目前,Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了.所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接。
由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称 SYN flood 攻击),用于耗尽Server的SYN队列。对于应对SYN 过多的问题,linux提供了几个TCP参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。
第三次握手时丢包
第三个包,即A发给B的ACK 数据报丢包了,没有到达B。
A发完ACK,单方面认为TCP为 Established状态,而B显然认为TCP为Active状态。
因此,具体存在以下三种情况:
a. 假定在此后一段时间双方都没有数据发送,B会周期性超时重传,直到收到A的确认。且收到之后,B的TCP 连接也为 Established状态,因而可以双向发包。
b. 假定此时A有数据发送,B收到A的 Data + ACK后,自然会切换为Established 状态,并接受A的Data。
c. 假定B有数据发送,但是因为数据发送不了(由于B未接收到A发来的ACK数据报),因此会一直会周期性超时重传SYN + ACK,直到收到A的确认后,才可以发送数据。
Reference
- 关于 TCP/IP,必知必会的十个问题 - https://juejin.im/post/598ba1d06fb9a03c4d6464ab
- 通俗大白话来理解TCP协议的三次握手和四次分手 - https://github.com/jawil/blog/issues/14
- TCP 为什么是三次握手,而不是两次或四次? - https://www.zhihu.com/question/24853633
- 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手 -http://www.52im.net/thread-1729-1-1.html
- 不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇) - http://www.52im.net/thread-1003-1-1.html