共计 2087 个字符,预计需要花费 6 分钟才能阅读完成。
SYN 是 TCP 三次握手的一部分,开发网络应用时通常不会关注,但它与申请中偶发
的长时延 (latency spike) 密切相关,是服务器保护环节中不可漠视的重要局部。如果
SYN 在发送过程中丢包了,通常客户端会在 1s, 3s, 7s, 15s, 31s 后重,这就是长提早
的起源之一。备受游戏公司困扰的 SYN Flood 攻打也是利用了 SYN 的特点。
查看原文
Linux 是如何解决 SYN 的
如上面 TCP 状态图 的上局部 所示,当利用 listen() 之后,就能够接管 SYN 并发送
SYN+ACK,进入 SYN RECEIVED 状态;期待收到 ACK 之后,进入 ESTABLISHED
状态。之后就能够被利用 accept()。
这意味着一个 TCP 连贯的建设过程里,有两个期待:1 是期待 ACK,2 是期待利用
accept()。Linux 应用 2 个队列实现这两次期待,队列中保留
struct inet\_request\_sock 构造。咱们不思考非凡状况,如 TCP\_SAVED\_SYN,
TCP\_DEFER\_ACCEPT, TCP\_FAST\_OPEN。
第一个队列是 SYN queue (也叫未实现队列),当收到 SYN 并返回 SYN+ACK 之后,
连贯会保留在这个队列中,并期待 ACK,连贯进入 SYN RECEIVED 状态。期待 ACK
超时的话会重传 net.ipv4.tcp_synack_retries
次。因为 SYN Cookie 的存在,
这个队列的长度并不非常重要。
第二个队列是 Accept queue (也叫实现队列),当收到 ACK 当前,连贯从 SYN queue
移除,保留到 Accept queue 中,进入 ESTABLISHED 状态,期待利用调用 accept()
。
如果队列满了会发送 RST。
SYN Queue 的长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog
管制。Accept queue
的长度由 listen()
的第二个参数 backlog
限度,并且不能大于 /proc/sys/net/core/somaxconn
。somaxconn
在 Linux 5.4 之前默认值是 128,5.4
之后改成了 4096。
最佳 Accept Queue 长度是什么
这个问题常常被提出:backlog 应该设置为多少?
答案是看状况。大多数状况这个参数不重要,Golang 1.11 之前甚至不能扭转这个参数。
如果新建连贯十分频繁,或者设置了 TCP\_DEFER\_ACCEPT,能够设置得大一些。
但设置过大也是不对的,1 个 inet_requst_sock
占用 256 字节呢。
有些系统管理员会倡议设置一个十分大的 backlog 而且让 net.core.somaxconn
也十分大。这实际上是覆盖了应用程序的问题,如果应用程序始终来不及生产,队列
大也没有多少意义,反而会早场客户端的长时延。不如设置一个短的队列,用 RST
尽早通知客户端出了某些问题,可能须要重连,如果设置了适合的负载均衡器,重连时
可能就是好的。
我个别设置 listen(fd, -1)
,而后依据状况批改 somaxconn
的值。
应用上面的命令能够查看某个端口的 SYN Queue 元素数量:
ss -n state syn-recv sport = :80 | wc -l
应用上面的命令能够查看某个端口的 Accept Queue 元素数量,其中 Recv-Q 就是
Accept Queue 元素数量,而 Send-Q 是 backlog 参数。
ss -plnt sport = :80
有几个变量能够查看队列满的次数:
- TcpExtListenOverflows 是 Accept Queue 满而无奈服务的次数
-
TcpExtTCPReqQFullDoCookies 是 SYN Queue 满而转为 Syn Sookie 的次数。
应用下列命令查看这些变量:
nstat -az TcpExtListenOverflows
依据理论须要调整 somaxconn 即可。
SYN Flood 和 SYN Cookie
1996 年之前,不停地发送 SYN 能够另服务器 SYN Queue 满从而进行服务,直到
SYN Cookie 呈现。这种攻打就是 SYN Flodd,只须要大量的带宽,发送不算太多的
报文,就能够让 SYN Queue 满,简略无效。
- syn-flood.c 我不是喊你去攻打人家啊
因为 ACK 的 ack 值就是 SYN+ACK 的 seq 值,能够 SYN 的哈希值保留到 seq 里,
收到 ACK 的时候再验证。这样就不再须要 SYN Queue 了:
+----------+--------+-------------------+
| 6 bits | 2 bits | 24 bits |
| t mod 32 | MSS | hash(ip, port, t) |
+----------+--------+-------------------+
开启 syn cookie (net.ipv4.tcp_timestamps=1
,默认开启) 之后,SYN Queue
满时会启用这个性能,用以防备 SYN Flood。比拟麻烦的问题就是,这会向网络中发送
大量没用的 SYN+ACK。
Linux 4.4 之前,SYN 的解决是很慢的,而后他们把一个内核锁去掉了,当初根本不须要
适度放心 SYN Flood 的问题。