tcp_tw_recycle和tcp_timestamps導致connect失敗問題

近來線上陸續出現了一些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.

轉載地址:http://blog.sina.com.cn/s/blog_781b0c850100znjd.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章