什么是TCP半连接队列和全连接队列?
在TCP进行三次握手时,Liunx会为其维护两个队列:
- 半连接队列,也叫syn队列
- 全连接队列,也叫accept队列
在客户端发起第一次连接时,服务端会将其加入到syn队列中,并且响应客户端syn+ack报文,等到客户端发送ack应答报文时,服务端将该连接从半连接队列中取出,并新建一个新的连接,加入到accept队列当中。等待进程调用accept请求时,将该连接取出来
不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。
半连接队列(syn队列)
如何查看半连接队列的长度呢??
我们可以抓住半连接队列的特点,处于syn_recv状态的tcp连接,就是我们的半连接队列
于是,我们使用如下命令查看处于syn_recv状态的tcp连接
netstat -natp | grep SYN_RECV | wc -l
如何模拟tcp半连接队列溢出场景
我们只需要一直对服务端发送syn包,但是不回ack回应包,这样就会使得服务端有大量请求处于syn_recv状态,这就是所谓的syn洪泛,syn攻击,DDos攻击
如何抵御syn攻击
-
增大半连接队列
不能只增大tcp_max_syn_backlog,还需要一同增大somaconn和backlog,也就是增大全连接队列 -
开启tcp_syncookies功能
开启tcp_syncookies就可以在不使用syn半连接队列的情况下建立连接
syncookies在接收到客户端的syn报文时,计算出一个值,放到syn+ack报文中发出。当客户端返回ack报文时,取出该值验证,成功则建立连接,如下图:
-
减少ack+syn报文的重传次数
因为我们在收到syn攻击时,服务端会重传syn+ack报文到最大次数,才会断开连接。针对syn攻击的场景,我们可以减少ack+syn报文的重传次数,使处于syn_recv状态的它们更快断开连接
修改重传次数:/proc/sys/net/ipv4/tcp_synack_retries
全连接队列
如何知道TCP全连接队列的大小
可以使用ss命令,来查看TCP全连接队列的情况
全连接队列溢出
当服务端的全连接队列过小时,容易发生全连接队列溢出。发生全连接队列溢出,后续的请求就会别丢弃。
Linux有个参数可以指定TCP全连接队列满了,会使用什么策略来回应客户端
丢弃连接只是liunx的默认行为,我们还可以向客户端发送RST报文终止连接,告诉客户端连接失败
tcp_abort_on_overflow共有两个值分别是0和1
- 0:如果全连接队列满了,那么服务端丢弃ack报文
- 1:如果全连接队列满了,那么服务端会想客户端发送RST报文,终止这个握手连接
通常情况下设置为0更好,可以提高效率
如果设置为0的话,此时服务端全连接队列满了,客户端发送过来的ack报文,服务端丢弃。而此时客户端还会继续重传,如果此时服务端的全连接队列有空闲,那么就会接受重传的ack包,这样就能直接建立连接了。而设置为1的话,还需要重新连接
如何增大全连接队列呢?
当全连接队列溢出后,我们需要增大全连接队列的长度,以提高请求容量
TCP 全连接队列的最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)
,所以我们需要提高这两个参数的大小才能拿增大全连接队列
本文大都是直接给出结论知识,你也可以通过一些网上博文,尝试自我分析Linux源码来探究这些结论