服務器處於SYN_RECV狀態,客戶端連接不上的問題

原文鏈接:https://blog.csdn.net/weixin_34309435/article/details/91550273

一,背景:

今天下午發現線上的一臺機器從辦公網登錄不上且所有tcp端口都telnet不通,但是通過同機房的其它機器卻可以正常訪問到出問題的機器。於是就立即在這臺出問題的server端抓包分析,發現問題如下:
server端收到了本地pc發的SYN包,但是沒有回syn+ack包,所以確認是server端系統問題。tcpdump抓包如下:

wKioL1mKiFzyAHTYAAZzy5AQiS4317.jpg

 

二,排查

1,發現系統沒有任何負載

2,網卡也沒有丟包

3,iptables策略也都沒問題

4,系統的SYN_RECV連接很少,也沒超限

5,系統的文件描述符等資源也都沒問題

6,messages和dmesg中沒有任何提示或者錯誤信息

7,通過netstat命令查看系統上協議統計信息,發現很多請求由於時間戳的問題被rejected


 
  1. # netstat -s |grep reject

  2.     2181 passive connections rejected because of time stamp

  3.     34 packets rejects in established connections because of timestamp

 

三,通過google來協助

發現有同樣的人遇見這個問題:

是通過調整sysctl -w net.ipv4.tcp_timestamps=0或者sysctl -w net.ipv4.tcp_tw_recycle=0來解決這個問題,於是我就順藤摸瓜繼續查。

 

而在查詢這兩個參數的過程中,發現問題原因如下:

發現是 Linux tcp_tw_recycle/tcp_timestamps設置導致的問題。 因爲在linux kernel源碼中發現tcp_tw_recycle/tcp_timestamps都開啓的條件下,60s內同一源ip主機的socket connect請求中的timestamp必須是遞增的。經過測試,我這邊centos6系統(kernel 2.6.32)和centos7系統(kernel 3.10.0)都有這問題。


 
  1.     源碼函數:kernel 2.6.32 tcp_v4_conn_request(),該函數是tcp層三次握手syn包的處理函數(服務端);

  2.     源碼片段:

  3.        if (tmp_opt.saw_tstamp &&

  4.             tcp_death_row.sysctl_tw_recycle &&

  5.             (dst = inet_csk_route_req(sk, req)) != NULL &&

  6.             (peer = rt_get_peer((struct rtable *)dst)) != NULL &&

  7.             peer->v4daddr == saddr) {

  8.             if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&

  9.                 (s32)(peer->tcp_ts - req->ts_recent) >

  10.                             TCP_PAWS_WINDOW) {

  11.                 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);

  12.                 goto drop_and_release;

  13.             }

  14.         }

  15.         

  16.         tmp_opt.saw_tstamp:該socket支持tcp_timestamp

  17.         sysctl_tw_recycle:本機系統開啓tcp_tw_recycle選項

  18.         TCP_PAWS_MSL:60s,該條件判斷表示該源ip的上次tcp通訊發生在60s內

  19.         TCP_PAWS_WINDOW:1,該條件判斷表示該源ip的上次tcp通訊的timestamp 大於 本次tcp

 

總結:

我這邊和其它同事通過公司出口(NAT網關只有1個ip地址)訪問問題server,由於timestamp時間爲系統啓動到當前的時間,故我和其它同事的timestamp肯定不相同;根據上述SYN包處理源碼,在tcp_tw_recycle和tcp_timestamps同時開啓的條件下,timestamp大的主機訪問serverN成功,而timestmap小的主機訪問失敗。並且,我在辦公網找了兩臺機器可100%重現這個問題。

 

解決:

# echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle

 

四,擴展

1,net.ipv4.tcp_timestamps

tcp_timestamps的本質是記錄數據包的發送時間。基本的步驟如下:

  1. 發送方在發送數據時,將一個timestamp(表示發送時間)放在包裏面

  2. 接收方在收到數據包後,在對應的ACK包中將收到的timestamp返回給發送方(echo back)

  3. 發送發收到ACK包後,用當前時刻now - ACK包中的timestamp就能得到準確的RTT

 

當然實際運用中要考慮到RTT的波動,因此有了後續的(Round-Trip Time Measurement)RTTM機制。

TCP Timestamps Option (TSopt)具體設計如下


 
  1. Kind: 8             // 標記唯一的選項類型,比如window scale是3

  2. Length: 10 bytes    // 標記Timestamps選項的字節數

  3. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  4. | Kind=8 | Length=10 | TS Value (TSval) | TS ECho Reply (TSecr) |

  5. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 

  6.    1          1             4                       4

timestamps一個雙向的選項,當一方不開啓時,兩方都將停用timestamps。比如client端發送的SYN包中帶有timestamp選項,但server端並沒有開啓該選項。則回覆的SYN-ACK將不帶timestamp選項,同時client後續回覆的ACK也不會帶有timestamp選項。當然,如果client發送的SYN包中就不帶timestamp,雙向都將停用timestamp。

tcp數據包中timestamps的value是系統開機時間到現在時間的(毫秒級)時間戳。

 

參數:

0:停用

1:啓用(系統默認值)

 

2,net.ipv4.tcp_tw_recycle

TCP規範中規定的處於TIME_WAIT的TCP連接必須等待2MSL時間。但在linux中,如果開啓了tcp_tw_recycle,TIME_WAIT的TCP連接就不會等待2MSL時間(而是rto或者60s),從而達到快速重用(回收)處於TIME_WAIT狀態的tcp連接的目的。這就可能導致連接收到之前連接的數據。爲此,linux在打開tcp_tw_recycle的情況下,會記錄下TIME_WAIT連接的對端(peer)信息,包括IP地址、時間戳等。這樣,當內核收到同一個IP的SYN包時,就會去比較時間戳,檢查SYN包的時間戳是否滯後,如果滯後,就將其丟掉(認爲是舊連接的數據)。這在絕大部分情況下是沒有問題的,但是對於我們實際的client-server的服務,訪問我們服務的用戶一般都位於NAT之後,如果NAT之後有多個用戶訪問同一個服務,就有可能因爲時間戳滯後的連接被丟掉。

 

參數:

0:停用(系統默認值)

1:啓用

 

參考:

https://serverfault.com/questions/235965/why-would-a-server-not-send-a-syn-ack-packet-in-response-to-a-syn-packet

http://hustcat.github.io/tcp_tw_recycle-and-tcp_timestamp/

轉載於:https://blog.51cto.com/leejia/1954628

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