tcp_tw_recycle和tcp_timestamps导致connect失败问题
2014-06-27 09:30:55   来源:我爱运维网   评论:0 点击:

近来线上陆续出现了一些connect失败的问题,经过分析试验,最终确认和proc参数tcp_tw_recycle/tcp_timestamps相关;1. 现象第一个现象:模...

近来线上陆续出现了一些connect失败的问题,经过分析试验,最终确认和proc参数tcp_tw_recycle/tcp_timestamps相关;
1. 现象
 第一个现象:模块A通过NAT网关访问服务S成功,而模块B通过NAT网关访问服务S经常性出现connect失败,抓包发现:服务S端已经收到了syn包,但没有回复synack;另外,模块A关闭了tcp timestamp,而模块B开启了tcp timestamp;
 第二个现象:不同主机上的模块C(开启timestamp),通过NAT网关(1个出口ip)访问同一服务S,主机C1 connect成功,而主机C2 connect失败;

2. 分析
 根据现象上述问题明显和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小的主机访问失败;

 参数:/proc/sys/net/ipv4/tcp_timestamps - 控制timestamp选项开启/关闭
 /proc/sys/net/ipv4/tcp_tw_recycle - 减少timewait socket释放的超时时间

3. 解决方法
 echo 0 > /proc/sys/net/ipv4/tcp_tw_recycle;
 tcp_tw_recycle默认是关闭的,有不少服务器,为了提高性能,开启了该选项;
 为了解决上述问题,个人建议关闭tcp_tw_recycle选项,而不是timestamp;因为 在tcp timestamp关闭的条件下,开启tcp_tw_recycle是不起作用的;而tcp timestamp可以独立开启并起作用。
 源码函数: tcp_time_wait()
 源码片段:
 if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
 recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);
 ......
 
 if (timeo < rto)
 timeo = rto;

 if (recycle_ok) {
 tw->tw_timeout = rto;
 } else {
 tw->tw_timeout = TCP_TIMEWAIT_LEN;
 if (state == TCP_TIME_WAIT)
 timeo = TCP_TIMEWAIT_LEN;
 }

 inet_twsk_schedule(tw, &tcp_death_row, timeo,
 TCP_TIMEWAIT_LEN);

 timestamp和tw_recycle同时开启的条件下,timewait状态socket释放的超时时间和rto相关;否则,超时时间为TCP_TIMEWAIT_LEN,即60s;

 内核说明文档 对该参数的介绍如下
 tcp_tw_recycle - BOOLEAN
 Enable fast recycling TIME-WAIT sockets. Default value is 0.
 It should not be changed without advice/request of technical
 experts.

 

 

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

 

这两天在处理一个新签客户的古怪问题,分支机构大部分用户都能访问,但少数用户时而能访问时而不行,通过缩小故障包围圈,目前初步探明是内核参数 net.ipv4.tcp_tw_recycle = 1的问题,设置为0后初步解除故障

 

通过此次故障,警示我们在进行日常程序,系统等变更,修改,重启等的操作上,需要我们严格按照流程仔细去进行测试,评估修改后的风险及出现问题回退和解决方法;特别是对内核参数的修改一定要理解透彻,不能盲目修改。然后进行逐步发布,避免故障影响全局。尽量让故障率降低。

 

相关热词搜索:timestamps tcp 内核参数

上一篇:你造吗-必须知道的16个Linux服务器监控命令
下一篇:最后一页

分享到: 收藏