近日,在分析某項業務故障時,抓取到,TCP客戶端發送SYN包,對端沒有收到,然而客戶端也沒有進行SYN包重傳的現象。具體情況如下圖:
可以看到,經過過濾,本次抓包抓取到的tcp連接情況,只有客戶端主動發起了TCP連接,發送了建立連接的syn包,之後再無關於該tcp連接的任何數據包傳遞發生。由此可以推測,該syn包沒有被服務器端收到,或者服務器端收到syn包後沒有響應。於是,根據tcp源端口,在服務器側抓取到的數據包中進行過濾。
可見,服務器側沒有收到該SYN包。所以服務器側,關於該TCP連接沒有任何響應是正常的。
然而,我們知道tcp連接,在客戶端發送了SYN包後,若沒有收到服務器端返回的SYN-ACK,客戶端會進行SYN包的重傳,然而通過TCP客戶端側的抓包看,在發送了一次SYN包後,沒有收到響應後,卻沒有進行SYN包的重傳,導致該TCP連接,在只嘗試了一次後,就沒有再嘗試建立連接。
經過進一步分析TCP-SYN包重傳的機制,客戶端發送syn包,在沒有收到SYN-ACK的情況下,等待RTO時間超時,之後會重新發送SYN包,再有等待了2個RTO時間沒有收到響應後,會再一次進行SYN報的重傳,直到連接建立或達到重傳次數限制。RTO時間和重傳次數在linux上均有定義,同時應用程序也可以修改重傳次數。
首先來講重傳次數
該參數在vi /etc/sysctl.con中修改
net.ipv4.tcp_syn_retries=1
net.ipv4.tcp_synack_retries=1
可以看到,可以修改操作系統作爲客戶端或服務器端,發送syn包或SYN-ACK包的數量。
這個參數同樣可以在/proc/sys/net/ipv4/tcp_syn_retries進行設置。經查看,本次客戶端設置/proc/sys/net/ipv4/tcp_syn_retries爲5。同樣,也可以通過在該機器上telnet 不存在的IP地址和端口的方式,驗證操作系統的行爲。
除此之外,應用程序也可以通過也可以通過TCP_SYNCNT的socket選項單獨設置某一個TCP連接SYN重傳次數。對這個不是很懂,不詳細說了。
經查看操作系統配置並諮詢開發人員,關於SYN重傳的參數設置5,按道理應該是SYN包會重傳5次,可見不是TCP重傳參數的設置問題導致SYN包沒有重傳。
下面我們來講SYN包超時的問題:
在研究重傳次數的問題時,我們發現,網絡上存在着該參數設置的值與實際重傳發生的次數不匹配的一些案例,這些案例是由於linux內核的一個bug導致的,連接如下:
Suse網站上也有相關描述:https://www.suse.com/support/kb/doc/?id=7010211
簡單來說,文章中描述了,在bug未修復前,超時時間是由TCP_RTO_MIN這個參數指定,然後帶入計算的,該參數在內核代碼/include/net/tcp.h中定義
#define TCP_RTO_MIN ((unsigned(HZ/5))
這個bug修復後,超時時間由TCP_TIMEOUT_INIT定義
#define TCP_TIMEOUT_INIT((unsigned(1*HZ)
/*RFC6298 2.1 initial RTO value */
由於上述差異導致計算上的錯誤,導致重傳的次數沒有按照設置的TCP_SYN_RETRIES發生。
這個值在RFC6298中,定義爲1秒。在RFC 1122中,該值爲3秒。也就是說若3秒沒有收到SYN包的回覆,會進行SYN包的重發。若仍沒有響應,TCP連接中RTO的時間,採用指數退避的方式增長,每一次的RTO是上一個RTO值得兩倍,也就是說SYN包會在6秒後進行重發,若仍沒有響應,會再等待12秒以後,在此重傳,直到達到前文提到地SYN_Retry次數的限制。
爲什麼沒有重傳發生?
在知道了以上知識以後,是什麼導致了我們在TCP連接客戶端一側的抓包沒有抓取到TCP SYN重傳的發生呢,經過與應用系統管理員的溝通,該應用程序等待返回超時的時間設置爲2秒。而我們的操作系統版本爲Linux Suse 11.3,查看/include/net/tcp.h文件中的定義,設置的TCP_TIMEOUT_INIT爲3秒鐘。在客戶端應用程序調用TCP Socket發出建立連接的SYN包以後,在等待了2秒仍然沒有返回時,應用程序便將這個TCP連接主動關閉了,導致了沒有重傳的發生。
總結
至此,我們瞭解了關於Linux操作系統中,影響TCP連接中SYN包重傳次數的參數設置;SYN包的RTO時間是如何得出的以及這個時間如何增長的;最後我們分析了爲什麼在文章開頭一開始講到的案例中,沒有重傳的發生。
我們應在之後的工作中進一步瞭解操作系統之上運行的應用系統的關於時間的要求,以進一步優化操作系統,提高應用系統運行的連續性和可靠性。
最後附上tcp.h源碼的連接,感興趣的讀者可以通過查看源碼查看其中各項參數的設置
https://github.com/torvalds/linux/blob/master/include/net/tcp.h