IP:超時,重傳,控流,擁塞處理(一)

TCP/IP 通信過程經常碰見的幾個超時情況:

  • 建連接時SYN超時
  • SYN Flood攻擊
  • ISN的初始化
  • MSL 和 TIME_WAIT
  • TIME_WAIT數量太多以及解決方案
TCP/IP:超時,重傳,控流,擁塞處理(一)

1、建連接時 SYN 超時

假設 server 端接到了clien 發的 SYN 後回了 SYN-ACK 後 client 掉線了,server 端沒有收到 client 回來的 ACK,那麼,這個連接處於一箇中間狀態,即沒成功,也沒失敗。

於是,server 端如果在一定時間內沒有收到的 TCP 會重發 SYN-ACK。在 Linux 下,默認重試次數為5次,重試的間隔時間從1s開始每次都翻售,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s,

第5次發出後還要等32s 都知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才會把斷開這個連接

2、SYN Flood 攻擊

凡是有超時的地方就有 DDOS 攻擊的可能。一些惡意的人就為此製造了SYN Flood 攻擊——給服務器發了一個SYN 後,就下線了,於是服務器需要默認等63s才會斷開連接,這樣,攻擊者就可以把服務器的syn連接的隊列耗盡,讓正常的連接請求不能處理。

於是,Linux下給了一個叫 tcp_syncookies 的參數來應對這個事——當 SYN 隊列滿了後,TCP 會通過源地址端口、目標地址端口和時間戳打造出一個特別的 Sequence Number 發回去(又叫cookie),如果是攻擊者則不會有響應,如果是正常連接,則會把這個 SYN Cookie 發回來,然後服務端可以通過cookie 建連接(即使你不在 SYN 隊列中)。

注意,請先千萬別用 tcp_syncookies 來處理正常的大負載的連接的情況。因為,synccookies 是妥協版的 TCP 協議,並不嚴謹。對於正常的請求,你應該調整三個 TCP 參數可供你選擇,

  • 第一個是:tcp_synack_retries 可以用他來減少重試次數;
  • 第二個是:tcp_max_syn_backlog,可以增大SYN連接數;
  • 第三個是:tcp_abort_on_overflow 處理不過來乾脆就直接拒絕連接了

3、ISN 的初始化

ISN 是不能硬編碼,不然會出問題的——比如:如果連接建好後始終用1來做 ISN,如果 client 發了30個 segment 過去,但是網絡斷了,於是 client 重連,又用了1做 ISN,但是之前連接的那些包到了,於是就被當成了新連接的包,此時,client的Sequence Number 可能是3,而 Server 端認為 client 端的這個號是30了。全亂了。

RFC793 中說,ISN 會和一個假的時鐘綁在一起,這個時鐘會在每4微秒對 ISN 做加一操作,直到超過2^32,又從0開始。這樣,一個ISN的週期大約是4.55個小時。

因為,我們假設我們的 TCP Segment 在網絡上的存活時間不會超過 Maximum Segment Lifetime(縮寫為MSL),所以,只要 MSL 的值小於4.55小時,那麼,我們就不會重用到 ISN。

TCP/IP:超時,重傳,控流,擁塞處理(一)

4、MSL 和 TIME_WAIT

通過上面的 ISN 的描述,相信你也知道 MSL 是怎麼來的了。我們注意到,在 TCP 的狀態圖中,從 TIME_WAIT 狀態到 CLOSED 狀態,有一個超時設置,這個超時設置是 2*MSL(RFC793定義了MSL為2分鐘,Linux設置成了30s)。

為什麼要這有 TIME_WAIT,為什麼不直接給轉成 CLOSED 狀態。主要有兩個原因:

1)TIME_WAIT 確保有足夠的時間讓對端收到了 ACK,如果被動關閉的那方沒有收到 Ack,就會觸發被動端重發 Fin,一來一去正好2個MSL,

2)有足夠的時間讓這個連接不會跟後面的連接混在一起(你要知道,有些自做主張的路由器會緩存 IP數據包,如果連接被重用了,那麼這些延遲收到的包就有可能會跟新連接混在一起)

5TIME_WAIT數量太多以及解決方案

從上面的描述我們可以知道,TIME_WAIT 是個很重要的狀態,但是如果在大併發的短鏈接下,TIME_WAIT 就會太多,這也會消耗很多系統資源。

網上很多教程會教你設置兩個參數,一個叫 Tcp_tw_reuse,另一個叫 Tcp_tw_recycle 的參數,這兩個參數默認值都是被關閉的,後者 recyle 比前者 resue 更為激進,resue 要溫柔一些。

另外,如果使用 Tcp_tw_reuse,必需設置 Tcp_timestamps=1,否則無效。這裡,你一定要注意,打開這兩個參數會有比較大的坑——可能會讓 TCP 連接出一些詭異的問題(因為如上述一樣,如果不等待超時重用連接的話,新的連接可能會建不上。

tcp_tw_reuse:
官方文檔上說 Tcp_tw_reuse 加上Tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保證協議的角度上的安全,但是你需要 tcp_timestamps 在兩邊都被打開。

Tcp_tw_recycle:
如果是 tcp_tw_recycle 被打開了話,會假設對端開啟了 tcp_timestamps,然後會去比較時間戳,如果時間戳變大了,就可以重用。

但是,如果對端是一個 NAT 網絡的話(如:一個公司只用一個IP出公網)或是對端的 IP 被另一臺重用了,這個事就複雜了。建鏈接的 SYN 可能就被直接丟掉了可能會看到 connection time out 的錯誤。

Tcp_max_tw_buckets:


這個是控制併發的 TIME_WAIT 的數量,默認值是180000,如果超限,那麼,系統會把多的給 destory 掉,然後在日誌裡打一個警告(如:time wait bucket table overflow),官網文檔說這個參數是用來對抗 DDoS 攻擊的。也說的默認值180000並不小。這個還是需要根據實際情況考慮。

使用 Tcp_tw_reuse 和 Tcp_tw_recycle 來解決 TIME_WAIT 的問題是非常非常危險的,因為這兩個參數違反了 TCP 協議

其實,TIME_WAIT 表示的是你主動斷連接。如果讓對端斷連接,那麼這個破問題就是對方的了。另外,如果你的服務器是於 HTTP 服務器,那麼設置一個 HTTP 的 KeepAlive 有多重要(瀏覽器會重用一個 TCP 連接來處理多個 HTTP 請求),然後讓客戶端去斷鏈接。

由於篇幅過長,TCP/IP 重傳,控流,擁塞處理 將在下一篇文章,敬請關注~


分享到:


相關文章: