概括来说有两个方面的原因
1.TCP两个队列满了(半链接和全链接队列),造成SYN报文被丢弃
2.开启了tcp_tw_recylce调优参数,并且在NAT环境下,造成了SYN报文丢弃
原因一分析
回忆一下三次握手:
1.服务端收到客户端发起的SYN请求后,内核会把该链接存储到半链接队列,并向客户端响应SYN+ACK。
2.接着客户端返回ACK, 服务端收到第三次握手的ACK后,内核会把链接从半链接队列移除
3.然后创建新的完全的链接,并将其添加到aceept队列(全链接队列),等待进程调用accept 函数时把链接取出来。
如果大量的syn数据包同时到达服务端,导致半连接队列瞬间被打满,多余的SYN就丢弃了(DDOS攻击),怎么解决?
(1)增大半链接队列
tcp_max_syn_backlog和backlog somaxconn,同时增大全链接队列
(2)开启tcp_syncookies
服务器更具当前状态计算出一个值, 放在服务端发送给客户端的SYN+ACK中发出,当客户端返回ACK报文时,取出该值验证,如果合法,认为该链接建立成功
0:关闭该功能;1:SYN半链接装不下开启;2:无脑开启
(3)减少SYN+ACK重传次数
当服务端收到SYN攻击时候,会有大量处于SYN_RECV状态的TCP链接,处于这个状态的TCP会重传SYN+ACK,当重传次数达到上限后,就会断开链接,那么针对SYN攻击的场景,我们可以减少SYN+ACK的重传次数,以加快SYN_RECV状态的TCP断开,默认为5,可以减少到1
在服务端并发处理大量请求时候,如果TCP accept队列太小,或者应用程序调用accept 不及时,就会造成accept队列满了,这时候或许的链接就会被丢弃,这样就会挤占半链接队列,这样就会丢弃syn包,如何解决?
(1)调大backlog以及somaxconn参数
(2)检查系统或者代码是不是调用accept不及时
原因二分析
为什么会有tcp_tw_recycle参数
1.TCP 第四次挥手中,主动断开方会有一个TIME_WAIT状态,这个状态会持续2MSL后才会转变为CLOSED状态
2.主断开链接方= 客户端 + 大量主动断开链接,导致大后面客户端没有端口可以用
tcp要解决,当真的没有端口使用的时候,客户端还要想建立连接该怎么办,所以诞生了tcp_tw_recycle
3.tcp_tw_recycle,如果开启,允许time_wait状态被快速回收;tcp_tw_refuse,如果开启,客户端在调用connect,如果内核选择的端口已经被相同四元组的连接占用的时候,会判断该连接是否处于time_wait状态,如果处于Time_wait并且持续时间超过1s,那么就会重用这个连接,然后就可以正常使用这个端口了
上述两个参数使用前提,必须打开时间戳即net.ipv4.tcp_timestamp = 1
4.开启了recycle和timestamps选项,就开启了PAWS机制(防止了TCP包中的序列号发生绕回)
什么是PAWS机制?
之前说过,开启了 recycle 和 timestamps 选项,就会开启一种叫 per-host 的 PAWS 机制。per-host 是对「对端 IP 做 PAWS 检查」,而非对「IP + 端口」四元组做 PAWS 检查。
正常来说每个 TCP 包都会有自己唯一的 SEQ,出现 TCP 数据包重传的时候会复用 SEQ 号,这样接收方能通过 SEQ 号来判断数据包的唯一性,也能在重复收到某个数据包的时候判断数据是不是重传的。但是 TCP 这个 SEQ 号是有限的,一共 32 bit,SEQ 开始是递增,溢出之后从 0 开始再次依次递增。
所以当 SEQ 号出现溢出后单纯通过 SEQ 号无法标识数据包的唯一性,某个数据包延迟或因重发而延迟时可能导致连接传递的数据被破坏,比如
上图 A 数据包出现了重传,并在 SEQ 号耗尽再次从 A 递增时,第一次发的 A 数据包延迟到达了 Server,这种情况下如果没有别的机制来保证,Server 会认为延迟到达的 A 数据包是正确的而接收,反而是将正常的第三次发的 SEQ 为 A 的数据包丢弃,造成数据传输错误。
PAWS 就是为了避免这个问题而产生的,在开启 tcp_timestamps 选项情况下,一台机器发的所有 TCP 包都会带上发送时的时间戳,PAWS 要求连接双方维护最近一次收到的数据包的时间戳(Recent TSval),每收到一个新数据包都会读取数据包中的时间戳值跟 Recent TSval 值做比较,如果发现收到的数据包中时间戳不是递增的,则表示该数据包是过期的,就会直接丢弃这个数据包。
对于上面图中的例子有了 PAWS 机制就能做到在收到 Delay 到达的 A 号数据包时,识别出它是个过期的数据包而将其丢掉。
最终原因 tcp_tw_recycle 在使用了 NAT 的网络下是不安全的!
1.经过同一个 NAT转换后的来自不同真实client数据流,在服务端看起来是和同一个Host打交道
a.客户端网络环境是用了NAT网关,那么客户端的每一台机器经过NAT网关之后,都会是相同的IP地址,服务器无法区别出来
2.虽然经过同一NAT转化,但不同的client会携带各自的timestamp值,因此无法保证经过NAT转化后的数据包携带的timestamp值严格递增
3.当服务器的per-host PAWS机制被触发后,会丢弃timestamp值不符合递增条件的数据包
举例:当客户端 A 通过 NAT 网关和服务器建立 TCP 连接,然后服务器主动关闭并且快速回收 TIME-WAIT 状态的连接后,客户端 B 也通过 NAT 网关和服务器建立 TCP 连接,注意客户端 A 和 客户端 B 因为经过相同的 NAT 网关,所以是用相同的 IP 地址与服务端建立 TCP 连接,如果客户端 B 的 timestamp 比 客户端 A 的 timestamp 小,那么由于服务端的 per-host 的 PAWS 机制的作用,服务端就会丢弃客户端主机 B 发来的 SYN 包。
因此,tcp_tw_recycle 在使用了 NAT 的网络下是存在问题的,如果它是对 TCP 四元组做 PAWS 检查,而不是对「相同的 IP 做 PAWS 检查」,那么就不会存在这个问题了。