对 Linux TCP 的若干疑点和误会

整理了一下 Linux 的 TCP 相关的几个疑点和对以往错误认识的纠正。主要是系统出现大量 TIME_WAIT 和 SYN 请求的一些问题,以及一些 TCP 内核参数的意义。

提到的参数(选项)大都在 /proc/sys/net/ipv4/,如果需要永久生效,希望做到重启也不变,可以修改 /etc/sysctl.confnet.ipv4.tcp_*

1. 关于 TIME_WAIT

按照网络上很多文章的说法,当 Linux 服务器上出现大量 TIME_WAIT 状态时,可以通过对 TCP 相关的几个内核参数的修改,来减少 TIME_WAIT 状态。最常看到的是这两个

  • tcp_tw_reuse
  • tcp_tw_recycle

tcp_tw_reuse 设置为 1 时,就允许系统重用处于 TIME_WAIT 状态的 socket。如果 tcp_timestamp 没有设置为 1,只把 tcp_tw_reuse 设置为 1 是无效的。

tcp_tw_recycle 设置为 1 会开启系统对 TIME_WAIT 状态的 socket 的快速回收。开启这个功能,系统就会存下 TCP 连接的时间戳,当同一个 IP 地址过来的包的时间戳小于缓存的时间戳,系统就直接丢包,“回收”这个 socket。这个选项同样需要开启 tcp_timestamp 才生效。

开启这个功能是有很大风险的,如前面所说,会根据同一个 IP 来的包得时间戳来判断是否丢包,而时间戳是根据发包的客户端的系统时间得来的,如果服务端收到的包是同一出口 IP 而系统时间不一样的两个客户端的包,就有可能会丢包,可能出现的情况就是一个局域网内有的客户端能连接服务端,有的不能。

相对 tcp_tw_recycletcp_tw_reuse 是比较安全的一个选项,但是也可能会导致问题。按照官方文档的说法

It should not be changed without advice/request of technical experts.

还有一个与 TIME_WAIT密切相关的参数,tcp_max_tw_buckets 指定系统在同一时间最多能有多少 TIME_WAIT 状态,当超过这个值时,系统会直接干掉这个 TIME_WAIT 的 socket。不要为了减少 TIME_WAIT 就把这个值改小。

2. SYN 和 listen 函数的 backlog

先从 backlog 说起,根据 Linux 的文档,listen 函数的 backlog 等待应用程序 accept 的连接队列的长度,这个队列里是 ESTABLISHED 状态的连接,而不是还未完成三次握手的连接。如果要设置未完成连接的队列长度,可以设置 tcp_max_syn_backlog 这个参数。

未完成连接,也就是系统可能收到对端的 SYN,已经发送了 SYN/ACK,在等待对端的 ACK。DDOS 攻击中的 SYN 攻击,就是发送大量的 SYN,但是不发送 ACK,服务器大量的资源在维持这些未完成的连接,等待 ACK,这时 netstat 可以看到大量的 SYN_RECV 状态。

不少文章里提到为了应对这种现象,为了能让服务端尽量提供服务,就增大 tcp_max_syn_backlog 的值,以使服务器能够接受更多的连接请求,这样做是合情合理的。但是也有不少文章里建议开启 tcp_syncookies 来减少 SYN 攻击的影响。tcp_syncookies 可以说是一个无奈的参数,它使严重违反 TCP 协议的。开启这个选项,系统会根据缓存的未完成的连接的信息算一个值,也就是 cookie,返回给客户端,如果客户端是正常的连接请求,再往服务端发包时,会把这个 cookie 带回来,服务端就会查看这个 cookie,类似加了一个验证的过程。

如果服务器收到大量的正常请求,导致服务器负载非常高,不要为了应对这种情况开启 tcp_syncookies。文档里用了大写的 “MUST NOT” 来提醒我们,然后很良心地建议去修改 tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow

tcp_max_syn_backlog 已经提到过,设置更高的值,允许系统保持更多的“未完成连接”。按照在 listen 函数的文档所说,当开启了 tcp_syncookies 之后,tcp_max_syn_backlog 的值就失去了原本意义,该值被忽略

系统发回给客户端 SYN/ACK 后,如果没收到客户端的 ACK,会重发 SYN/ACK,tcp_synack_retries 用于指定重发 SYN/ACK 的次数,默认是 5 次,可以适当减小这个值。

tcp_abort_on_overflow 是个跟应用程序更相关的参数,当被设置为 1 时,如果应用程序处理速度比较慢,来不及接受新的连接,系统就直接丢弃这个连接,给对端发个 RST。这个选项也要谨慎开启。

有时候会看到 somaxconn (/proc/sys/net/core/somaxconn) 这个值跟 tcp_max_syn_backlog 一起出现,其实 somaxconnlistenbacklog 参数的意义是基本一样的,指定用于保存已完成三次握手等待 accept 的队列长度,somaxconnbacklog 可以设置的最大值。当在应用程序里设置 backlog 的值大于 somaxconn 参数时,系统也会默默地把 backlog 减小为 somaxconn 指定的值。

Subscribe to Atom

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe