Tcp tw recycle打开导致60s内不能两个用户同时登录

来自linux中国网wiki
跳到导航 跳到搜索

问题表现

用了一个qcloud 负载均衡器   公司内部在很短的时候内只能用一个手机一个帐号登录 
查看 real ip(后端的 web vm ) 的机器 /var/log/messages 有 kernel: TCP: kernel: TCP: time wait bucket table overflow (TCP time wait bucket表溢出了)

原因分析

TIME_WAIT是本地端主动关闭后一定会出现的状态。

用文字表达原因 就是  
根据现象上述问题明显和tcp timestmap有关;查看linux 2.6.32内核源码,发现tcp_tw_recycle/tcp_timestamps都开启的条件下,60s内同一源ip主机的socket connect请求中的timestamp必须是递增的。

源码函数:tcp_v4_conn_request(),该函数是tcp层三次握手syn包的处理函数(服务端);
    源码片段:
       if (tmp_opt.saw_tstamp &&
            tcp_death_row.sysctl_tw_recycle &&
            (dst = inet_csk_route_req(sk, req)) != NULL &&
            (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
            peer->v4daddr == saddr) {
            if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
                (s32)(peer->tcp_ts - req->ts_recent) >
                            TCP_PAWS_WINDOW) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
                goto drop_and_release;
            }
        }
tmp_opt.saw_tstamp:该socket支持tcp_timestamp
sysctl_tw_recycle:本机系统开启tcp_tw_recycle选项
TCP_PAWS_MSL:60s,该条件判断表示该源ip的上次tcp通讯发生在60s内
TCP_PAWS_WINDOW:1,该条件判断表示该源ip的上次tcp通讯的timestamp 大于 本次tcp

主机client1和client2通过NAT网关(1个ip地址)访问serverN,由于timestamp时间为系统启动到当前的时间,因此,client1和client2的timestamp不相同;根据上述syn包处理源码,在tcp_tw_recycle和tcp_timestamps同时开启的条件下,timestamp大的主机访问serverN成功,而timestmap小的主机访问失败;

用man的解析就是 
关于内核参数的详细介绍,可以参考官方文档 https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt 。我们这里简要说明一下tcp_tw_recycle参数。它用来快速回收TIME_WAIT连接,不过如果在NAT环境下会引发问题。

RFC1323中有如下一段描述:
An additional mechanism could be added to the TCP, a per-hostcache of the last timestamp received from any connection.This value could then be used in the PAWS mechanism to rejectold duplicate segments from earlier incarnations of theconnection, if the timestamp clock can be guaranteed to haveticked at least once since the old connection was open. Thiswould require that the TIME-WAIT delay plus the RTT togethermust be at least one tick of the sender’s timestamp clock.Such an extension is not part of the proposal of this RFC.

大概意思是说TCP有一种行为,可以缓存每个连接最新的时间戳,后续请求中如果时间戳小于缓存的时间戳,即视为无效,相应的数据包会被丢弃。
Linux是否启用这种行为取决于tcp_timestamps和tcp_tw_recycle,因为tcp_timestamps缺省就是开启的,所以当tcp_tw_recycle被开启后,实际上这种行为就被激活了。

现在很多公司都用LVS做负载均衡,通常是前面一台LVS,后面多台后端服务器,这其实就是NAT,当请求到达LVS后,它修改地址数据后便转发给后端服务器,但不会修改时间戳数据,对于后端服务器来说,请求的源地址就是LVS的地址,加上端口会复用,所以从后端服务器的角度看,原本不同客户端的请求经过LVS的转发,就可能会被认为是同一个连接,加之不同客户端的时间可能不一致,所以就会出现时间戳错乱的现象,于是后面的数据包就被丢弃了,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK,还可以通过下面命令来确认数据包不断被丢弃的现象:


1.现象 不同主机C1,C2上的相同模块(开启timestamp),通过NAT网关(1个出口ip)访问同一服务S,主机C1 connect成功,而主机C2 connect失败
2.分析 根据现象上述问题明显和tcp timestmap有关;查看linux 2.6.32内核源码,发现tcp_tw_recycle/tcp_timestamps都开启的条件下,60s(timewai时间)内同一源ip主机的socket connect请求中的timestamp必须是递增的。
3.验证
server端当tcp_tw_recycle和tcp_timestamps都是1的时候,会检查收到数据包TCP选项字段中的的timestamp(TS Value),当来自同一个IP地址(任意源端口号)后来的数据包中TCP选项字段如果有timestamp且比前面的数据包中的timestamp小,则server不做ACK响应
4.解决方法
1.服务器端不要将tcp_tw_recycle字段和tcp_timestamps字段同时设为1
2.客户端把tcp_timestamps字段设0,这样不会发送TCP选项字段中的timestamps选项
对于服务提供方1较适合

如果服务器身处NAT环境,安全起见,通常要禁止tcp_tw_recycle,至于TIME_WAIT连接过多的问题,可以通过激活tcp_tw_reuse来缓解。

进一步学习

在一些高并发的 WebServer上,为了端口能够快速回收,打开了net.ipv4.tcp_tw_recycle,而在关闭 net.ipv4.tcp_tw_recycle的时候,kernal 是不会检查对端机器的包的时间戳的;打开了 tcp_tw_reccycle 了,就会检查时间戳,很不幸移动的cmwap发来的包的时间戳是乱跳的,所以服务器就把带了“倒退”的时间戳的包当作是“recycle的tw连接的重传数据,不是新的请求”,于是丢掉不回包,造成大量丢包。

net.ipv4.tcp_tw_recycle

This enables fast recycling of TIME_WAIT sockets. 
The default value is 0 (disabled). Should be used with caution with loadbalancers.

快速收回处于TIME_WAIT状态的套接字(Sockets)
默认是0(无效)
加上上面的代码可知在使用负载均衡器的环境,需谨慎修改该参数


net.ipv4.tcp_max_tw_buckets

Linux系统内核参数net.ipv4.tcp_max_tw_buckets是控制系统允许处于TIME_WAIT状态的套接字(Sockets)的最大数, 这个限制是为了防止DOS(denial-of-service)攻击而存在。默认值是NR_FILE*2,并且会根据系统的内存容量被调整。 如果超过net.ipv4.tcp_max_tw_buckets上限,该连接被关闭,并且输出该信息到syslog(/var/log/messages)。

所以出现上面的提示,就是处于TIME_WAIT状态的套接字达到了net.ipv4.tcp_max_tw_buckets的数量,此时调大net.ipv4.tcp_max_tw_buckets。 如果net.ipv4.tcp_max_tw_buckets已经很大了,尝试配置另外2个TIME WAIT相关的内核参数。

net.ipv4.tcp_tw_recycle

This enables fast recycling of TIME_WAIT sockets. 
The default value is 0 (disabled). Should be used with caution with loadbalancers.

快速收回处于TIME_WAIT状态的套接字(Sockets)
默认是0(无效)
在使用负载均衡器的环境,需谨慎修改该参数

net.ipv4.tcp_tw_reuse

This allows reusing sockets in TIME_WAIT state for new connections when it is safe from protocol viewpoint.
Default value is 0 (disabled). It is generally a safer alternative to tcp_tw_recycle.
Note: The tcp_tw_reuse setting is particularly useful in environments where numerous short connections are
open and left in TIME_WAIT state, such as web servers and loadbalancers. Reusing the sockets can be very effective
in reducing server load. Starting in Linux 2.6.7 (and back-ported to 2.4.27), linux includes alternative congestion
control algorithms beside the traditional ‘reno’ algorithm. These are designed to recover quickly from packet loss
on high-speed WANs.

从协议安全性,允许将处于TIME_WAIT状态的套接字(Sockets)用于新的TCP连接
默认是0(无效)
一般来讲这是比修改tcp_tw_recycle安全

shell> netstat -s | grep timestamp ... packets rejects in established connections because of timestamp
如果服务器身处NAT环境,安全起见,通常要禁止tcp_tw_recycle,至于TIME_WAIT连接过多的问题,可以通过激活tcp_tw_reuse来缓解。

进一步思考,既然必须同时激活tcp_timestamps和tcp_tw_recycle才会触发这种现象,那只要禁止tcp_timestamps,同时激活tcp_tw_recycle,就可以既避免NAT丢包问题,又降低TIME_WAIT连接数量。如果服务器并不依赖于RFC1323,那么这种方法应该也是可行的,不过最好多做测试,以防有其他的副作用。 这个在我们的生产环境中不适合,我觉得

shell> sysctl net.ipv4.tcp_timestamps=0 
shell> sysctl net.ipv4.tcp_tw_recycle=1


解决方案

关闭net.ipv4.tcp_tw_recycle 

vim /etc/sysctl.conf
net.ipv4.tcp_tw_recycle = 1 --->net.ipv4.tcp_tw_recycle = 0
#再执行以下命令,让修改结果立即生效:
/sbin/sysctl -p


建议关闭tcp_tw_recycle选项,而不是timestamp;因为 在tcp timestamp关闭的条件下,开启tcp_tw_recycle是不起作用的;而tcp timestamp可以独立开启并起作用。 
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
原因是因为 此时是nat环境  

tcp_timestamps 在 kernel 2.2 之后就是默认打开了的
而 tcp_tw_recycle  不建议在 NAT 下开启 clb 和 cvm之间也是 例如tgw那样的 NAT环境

参考来自 man 7 tcp   
tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)
              Enable  fast recycling of TIME_WAIT sockets.  Enabling this option is not recommended for devices communicating with the general Internet or using NAT (Network Address Translation).  Since some NAT gate‐ways  pass  through IP timestamp values, one IP can appear to have non-increasing timestamps.  See RFC 1323 (PAWS), RFC 6191.   
              
tcp_timestamps (Boolean; default: enabled; since Linux 2.2)
              Enable RFC 1323 TCP timestamps.

 tcp_tw_recycle默认是关闭的,有不少服务器,为了提高性能,开启了该选项;
    为了解决上述问题,个人建议关闭tcp_tw_recycle选项,而不是timestamp;因为 在tcp timestamp关闭的条件下,开启tcp_tw_recycle是不起作用的;而tcp timestamp可以独立开启并起作用。



当远程端主机HOST处于NAT网络中时,时间戳在一分钟之内(MSL时间间隔)将禁止了NAT网络后面,除了这台主机以外的其他任何主机连接,因为他们都有各自CPU CLOCK,各自的时间戳。这会导致很多疑难杂症,很难去排查,建议你禁用这个选项。

因为2.6内核中tcp_timestamps默认是打开的,所以当打开 tcp_tw_recycle时会导致部分通过NAT上网client无法正确连接服务器,故障表现为client发出SYN后无法收到server返回 的SYN+ACK,推荐的解决方法是关闭tcp_tw_recycle,打开tcp_tw_reuse解决TIME-WAIT过多的问题。


NAT,下面不能打开tcp快速回收
qcloud  所有的内网和外网都是nat的环境

[[email protected] evan]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 14423
FIN_WAIT1 18
ESTABLISHED 44
SYN_RECV 3
CLOSING 7
LAST_ACK 3


常用参数解说

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

说明:
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

再执行以下命令,让修改结果立即生效:
/sbin/sysctl -p

  用以下语句看了一下服务器的TCP状态:
  netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  返回结果如下:
  ESTABLISHED 1423
  FIN_WAIT1 1
  FIN_WAIT2 262
  SYN_SENT 1
  TIME_WAIT 962

  效果:处于TIME_WAIT状态的sockets从原来的10000多减少到1000左右。处于SYN_RECV等待处理状态的sockets为0,原来的为50~300。  


参考

http://jaminzhang.github.io/network/server-prompt-time-wait-bucket-table-overflow

https://www.awspack.com/os/linux/time-wait-table/

http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html

应该 要 先看tcp后 才能看明白这个

good 源码 加文字分析 爽 tcp_tw_recycle和tcp_timestamp的问题 http://hustcat.github.io/tcp_tw_recycle-and-tcp_timestamp/

不错的教程 还结合了源码 http://blog.sina.com.cn/s/blog_781b0c850100znjd.html

tcp_tw_recycle和tcp_timestamps的文章汇总 其实是参考了上面的sina blog http://blog.csdn.net/caianye/article/details/38540867

https://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux

https://huoding.com/2012/01/19/142

Haproxy作为MySQL中间层如何避免TCP端口耗尽 http://noops.me/?p=252

修改Linux内核参数,减少TCP连接中的TIME-WAIT sockets[原创] http://zyan.cc/post/271/

tcp协议timestamp字段导致问题分析 http://noops.me/?p=269

good tcp_tw_recycle和tcp_timestamp的问题 http://hustcat.github.io/tcp_tw_recycle-and-tcp_timestamp/

tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项 http://www.cnblogs.com/lulu/p/4149312.html

http://www.ttlsa.com/yun_wei_an_li/tcp_-time-wait-bucket-table-overflow-solution/

TCP: time wait bucket table overflow http://itnihao.blog.51cto.com/1741976/758353

服务器提示kernel TCP time wait bucket table overflow http://jaminzhang.github.io/network/server-prompt-time-wait-bucket-table-overflow/

不错 很多TCP 相关的

系统大量TIME_WAIT、load高处理