队列及参数
server 端的半连接队列(syn 队列)
在三次握手协议中, 服务器维护一个半连接队列, 该队列为每个客户端的 SYN 包开设一个条目(服务端在接收到 SYN 包的时候, 就已经创建了 request_sock 结构, 存储在半连接队列中), 该条目表明服务器已收到 SYN 包, 并向客户发出确认, 正在等待客户的确认包(会进行第二次握手发送 SYN+ACK 的包加以确认). 这些条目所标识的连接在服务器处于 Syn_RECV 状态, 当服务器收到客户的确认包时, 删除该条目, 服务器进入 ESTABLISHED 状态.
该队列为 SYN 队列, 长度为 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog) , 在机器的 tcp_max_syn_backlog 值在 / proc/sys/net/ipv4/tcp_max_syn_backlog 下配置.
server 端的完全连接队列(accpet 队列)
当第三次握手时, 当 server 接收到 ACK 报之后, 会进入一个新的叫 accept 的队列, 该队列的长度为 min(backlog, somaxconn), 默认情况下, somaxconn 的值为 128, 表示最多有 129 的 ESTAB 的连接等待 accept(), 而 backlog 的值则应该是由 int listen(int sockfd, int backlog) 中的第二个参数指定, listen 里面的 backlog 可以有我们的应用程序去定义的.
当 Client 发送 SYN 包之后挂了(syn flood 攻击)
Client 发送 SYN 包给 Server 后挂了, Server 回给 Client 的 SYN-ACK 一直没收到 Client 的 ACK 确认, 这个时候这个连接既没建立起来, 也不能算失败. 这就需要一个超时时间让 Server 将这个连接断开, 否则这个连接就会一直占用 Server 的 SYN 连接队列中的一个位置, 大量这样的连接就会将 Server 的 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 来调整应对.
- net.ipv4.tcp_synack_retries #内核放弃连接之前发送 SYN+ACK 包的数量
- net.ipv4.tcp_syn_retries #内核放弃建立连接之前发送 SYN 包的数量
为了应对 SYNflooding(即客户端只发送 SYN 包发起握手而不回应 ACK 完成连接建立, 填满 server 端的半连接队列, 让它无法处理正常的握手请求),Linux 实现了一种称为 SYNcookie 的机制, 通过 net.ipv4.tcp_syncookies 控制, 设置为 1 表示开启. 简单说 SYNcookie 就是将连接信息编码在 ISN(initialsequencenumber)中返回给客户端, 这时 server 不需要将半连接保存在队列中, 而是利用客户端随后发来的 ACK 带回的 ISN 还原连接信息, 以完成连接的建立, 避免了半连接队列被攻击 SYN 包填满.
当 syn 队列满的情况(
- tcp_abort_on_overflow
- )
对于 SYN 半连接队列的大小是由 (/proc/sys/net/ipv4/tcp_max_syn_backlog) 这个内核参数控制的, 有些内核似乎也受 listen 的 backlog 参数影响, 取得是两个值的最小值. 当这个队列满了, 不开启 syncookies 的时候, Server 会丢弃新来的 SYN 包, 而 Client 端在多次重发 SYN 包得不到响应而返回 (connection time out) 错误. 但是, 当 Server 端开启了 syncookies=1, 那么 SYN 半连接队列就没有逻辑上的最大值了, 并且 / proc/sys/net/ipv4/tcp_max_syn_backlog 设置的值也会被忽略.
Client 端在多次重发 SYN 包得不到响应而返回 connection time out 错误
查看
- netstat -s | grep LISTEN
- 4375 SYNs to LISTEN sockets dropped
当 accept 队列满的情况
当 accept 队列满了之后, 即使 client 继续向 server 发送 ACK 的包, 也会不被响应, 此时 ListenOverflows+1, 同时 server 通过 / proc/sys/net/ipv4/tcp_abort_on_overflow 来决定如何返回, 0 表示直接丢弃该 ACK,1 表示发送 RST 通知 client; 相应的, client 则会分别返回 read timeout 或者 connection reset by peer.
client 则会分别返回 read timeout 或者 connection reset by peer
查看
- root@b5dbe93bcb04:/opt# netstat -s | grep listen
- 22438 times the listen queue of a socket overflowed
accept 队列满了, 对 syn 队列也有影响, 在代码 net/ipv4/tcp_ipv4.c :
- int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
- {
- /*tcp_syncookies 为 2 进行 syn cookie
- tcp_syncookies 为 1 且 request 队列满了 进行 syn cookie 处理
- tcp_syncookies 为 0 且 request 队列满了 将该 syn 报文 drop 掉
- */
- if ((sysctl_tcp_syncookies == 2 ||
- inet_csk_reqsk_queue_is_full(sk)) && !isn) {
- want_cookie = tcp_syn_flood_action(sk, skb, "TCP");
- if (!want_cookie)
- goto drop;
- }
- /* Accept backlog is full. If we have already queued enough
- * of warm entries in syn queue, drop request.
- */
- if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk)> 1) {
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
- goto drop;
- }
accept 队列大多数情况下会比较小, 所以会出现 SYN 队列没有满, 而 ACCEPT 队列满了的情况, 此时会按照 tcp_aborton_overflow 来决定直接丢弃, 还是返回拒绝 RST. 而如果启用了 syncookies, 那么 syncookies 会开启, 限制 SYN 包进入的速度.
当系统丢弃最后的 ACK, 而系统中还有一个 net.ipv4.tcp_synack_retries 设置时, Linux 会重新发送 SYN ACK 包. 而客户端收到多个 SYN ACK 包, 则会认为之前的 ACK 丢包了. 于是促使客户端再次发送 ACK , 在 accept 队列有空闲的时候最终完成连接. 若 accept 队列始终满员, 则最终客户端收到 RST 包.
- doc
- Linux SYN Backlog and somaxconn http://www.aikaiyuan.com/7432.html
TCP/IP 协议中 backlog 参数 http://www.cnblogs.com/Orgliny/p/5780796.html
TCP SOCKET 中 backlog 参数的用途是什么?
了解 tcp 底层知识 http://ju.outofmemory.cn/entry/234236
再理解 tcp backlog
Linux TCP 队列相关参数的总结 https://yq.aliyun.com/articles/4252
TCP 全连接队列和半连接队列已满之后的连接建立过程抓包分析 http://www.cnblogs.com/menghuanbiao/p/5212131.html
linux,mysql,nginx,tomcat 环境下压力测试的主要调试参数 https://segmentfault.com/a/1190000002700923
从 TCP 三次握手说起 - 浅析 TCP 协议中的疑难杂症(1)
从 TCP 三次握手说起 - 浅析 TCP 协议中的疑难杂症(2)
tcp 的半连接与完全连接队列(二)
来源: http://www.bubuko.com/infodetail-2768505.html