首页 > 系统运维 > tcp_rw_recycle引起数据包丢弃
您的足迹
  • 你没有浏览过任何文章或者你没有开启cookies。

tcp_rw_recycle引起数据包丢弃

最近发现LB服务器tw非常高,8W+TCP连接有6W左右都是TIME_WAIT状态,我们知道单个IP使用端口最多是(65535-1024)个,所以想打开tcp_tw_recycle把TIME_WAIT状态快速回收,一个不起眼的参数却引起了系统故障。

问题现象:

南京IDC连北京接口有的机器是有时通,有时不通,无法telnet端口,另一条专线线路正常。

问题分析

分析从南京IDC到北京IDC间有两种途径:从防火墙NAT出去和专线。分析出问题线路一段一段分析,每段进行抓包分析,最后在服务器发现只有从防火墙过来TCP三次握手第一个数据包SYN,后续的包均被丢弃。如下图:

抓包图

 

查看tcp_recycle代码:

#define TCP_PAWS_MSL     60              /* Per-host timestamps are invalidated
                                         * after this time. It should be equal
                                         * (or greater than) TCP_TIMEWAIT_LEN
                                         * to provide reliability equal to one
                                         * provided by timewait state.
                                         */
#define TCP_PAWS_WINDOW 1               /* Replay window for per-host
                                         * timestamps. It must be less than
                                         * minimal timewait lifetime.
                /* VJ's idea. We save last timestamp seen
                 * from the destination in peer table, when entering
                 * state TIME-WAIT, and check against it before
                 * accepting new connection request.
                 *
                 * If "isn" is not zero, this request hit alive
                 * timewait bucket, so that all the necessary checks
                 * are made in the function processing timewait state.
                 */
                if (tmp_opt.saw_tstamp &&
                    tcp_death_row.sysctl_tw_recycle &&
                    (dst = inet_csk_route_req(sk, &fl4, req)) != NULL &&
                    fl4.daddr == saddr &&
                    (peer = rt_get_peer((struct rtable *)dst, fl4.daddr)) != NULL) {
                        inet_peer_refcheck(peer);
                        if ((u32)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;
                        }
                }

发现有7种情况会引起丢包:

1 tcp的option有 time stamp字段.
2 tcp_tw_recycle有设置。
3 在路由表中是否存在完全相同的流(如果打开了xfrm的话,还要比较端口,默认xfrm应该是打开的),如果存在则直接返回.
4 并且数据包的源地址和新请求的源地址相同.
5 根据路由表以及源地址能够查找到保存的peer(这个可以看我以前的blog,也就是保存了一些连接统计信息).
6 当前时间(接收到syn)比最后一次的时间(time stamp)小于60秒.
7 已经存在peer的最近一次时间戳要大于当前请求进来的时间戳.

结合我们情况分析,我们因服务器timewait过多对tcp_tw_recycle进行调整过。所以,如果客户端是NAT出来的,并且我们server端有打开tcp_tw_recycle ,并且time stamp也没有关闭,那么假设第一个连接进来,然后关闭,此时这个句柄处于time wait状态,然后很快(小于60秒)又一个客户端(相同的源地址)发一个syn包,此时linux内核就会认为这个数据包异常的,因此就会丢掉这个包。

找到问题就容易解决了。

问题解决

我们直接把lb上面的tcp_timestamps(默认打开)关了,这样问题也就解决了。值得注意的是,当tcp_timestamp关闭后,tcp_tw_reuse和tcp_tw_recycle均不起作用了。查阅相关文档后,我尝试把tcp_timestamps打开,并关闭tcp_tw_recycle和tcp_tw_reuse,但打开一会用netstat -s |grep timestamp就发现因timestamp拒绝掉的包有所上升,原因未知,所以目前解决方法还是把tcp_timestamp给关闭的。

这次参数调整没有仔细阅读相关说明,另外发现几乎所以中文文档针对time_wait过多情况都是调整tcp_tw_recycle和tcp_tw_reuse参数。但是,我后来查阅了一下国外大多数文档,都明确说明tcp_tw_recyle打开要慎重。英文原文是:It should not be changed without advice/request of technical experts.  有时候调优文档也真够坑爹的。另外这个问题在LVS的NAT环境也有同样的问题。

关于如何处理tcp_tw_wait过多情况,我会另开一篇说明。

 

 

参考文档:

http://ju.outofmemory.cn/entry/96

http://blog.sina.com.cn/s/blog_781b0c850100znjd.html

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

  1. Aceslup 2014/12/10 下午 2:39 | #1

    英语底子差的技术员,就只能在国内环境中人云亦云了。希望博主多推一些好文。

    • Moon 2015/01/17 下午 5:33 | #2

      谢谢。这个故障亲生遇到的,抓包解决的。

评论提交中, 请稍候...

留言

可以使用的标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackbacks & Pingbacks ( 0 )
  1. 还没有 trackbacks
Feed