先用一张图回顾sock连接全过程:
1、什么是全连接队列和半连接队列,数据结构分别是什么
半连接队列
- 也称为SYN队列,在三次握手中存储了接收了SYN报文的连接
- 数据结构是哈希表
使用哈希表的原因:
- 半连接队列里的连接都是不完整的,不同连接中的客户端发来ACK报文的时间是不一样的;
- 因此,如果收到了一个客户端发来的ACK报文,如果半连接队列是队列,就需要O(n)的时间复杂度遍历;
- 但是如果是哈希表,就仅需要O(1)的时间复杂度来遍历
全连接队列
- 也称为accept队列,存储在三次握手中接收了ACK报文,但是代码没有使用accept函数提取的连接
- 数据结构是链表
使用链表的原因:
- 全连接队列里的连接都是已经连接好的连接,不需要按照特定顺序逐个遍历,只需要accpet函数依次取即可
2、sock连接中connect函数和accept函数在三次握手中的坑
- 客户端调用connect函数后,就开启了TCP三次握手流程
- TCP三次握手完成后,服务器内核会返回连接成功,将该连接加入到全连接队列中,等待服务器代码调用accpet函数处理该连接
- 因此,connect函数会在完成三次握手后返回0,无需服务器代码调用accept函数
3、全连接队列和半连接队列的大小设置
全连接队列:
- 全连接队列的大小跟两个参数有关,一个是listen函数中的backlog,另一个是somaxconn
- 全连接队列的容量是这两个参数的最小值
- somaxconn在
/proc/sys/net/core/somaxconn里可以查看与设置
半连接队列:
- 半连接队列的大小跟两个参数有关,一个是全连接队列的大小,以及tcp_max_syn_backlog的大小
- 半连接队列的容量是这两个参数最小值的两倍
- 如果全连接队列更小,那么半连接队列的大小为min(backlog,somaxconn)*2
-
如果tcp_max_syn_backlog更小,那么半连接队列的大小为tcp_max_syn_backlog*2
-
tcp_max_syn_backlog在/proc/sys/net/ipv4/tcp_max_syn_backlog
里可以查看与设置
4、全连接队列满了怎么处理
全连接队列满了以后的操作与tcp_abort_on_overflow的值相关
tcp_abort_on_overflow 的值为0:
- 服务器会丢弃客户端发的ACK报文与SYN报文
- 当用户给服务器发请求时,只要服务器不 Response,客户端就会不断重试发送第三次握手的 ACK
- 只要在重试次数到达上限前,服务器调用了 accept 处理了全连接队列的一个连接,就可以处理该用户的请求了
tcp_abort_on_overflow 的值为1:
- 服务器会发RST报文给客户端,通知废弃整个连接
- 但是客户端不知道服务端响应的 RST 包到底是因为该端口没有进程监听,还是该端口有进程监听,只是它的队列满了
- 只有非常肯定 TCP 全连接队列会长期溢出时,才能设置为 1 以尽快通知客户端
5、半连接队列满了怎么处理
半连接队列满了以后的操作与tcp_syncookies的值相关,且SYN攻击就是将目标服务器的半连接队列打满
tcp_syncookies的值为0:
- 关闭tcp_syncookies
- 不接收客户端的syn报文
tcp_syncookies的值为1:
- 仅当半连接队列满了才开启
- 不接收客户端的syn报文
tcp_syncookies的值为2:
- 一直开启tcp_syncookies
- 在第一次握手服务器收到客户端的SYN报文后,会计算一个cookie值,放在SYN+ACK报文中发送
- 客户端会在返回ACK报文的时候带上这个cookie值,服务器会对这个cookie值进行验证,如果合法则加入到全连接队列里
注意:
- cookie值由于要保证唯一性,所以生成所使用的算法比较复杂,频繁的进行cookies验证和生成,计算量很大,非常耗费CPU
- 服务器在没有解析cookie之前根本不知道该cookie是不是合法的
- 编造很多带上不合法cookies值的第三次握手ACK让CPU解析,如果解析完发现不合法,等于做了很多无用功消耗掉CPU资源,这就是ACK攻击的一种方式