當(dāng)前位置:首頁 > 公眾號精選 > 小林coding
[導(dǎo)讀]??大家好,我是小林。昨晚有位讀者問了我這么個問題:大概意思是,一個已經(jīng)建立的TCP連接,客戶端中途宕機了,而服務(wù)端此時也沒有數(shù)據(jù)要發(fā)送,一直處于establish狀態(tài),客戶端恢復(fù)后,向服務(wù)端建立連接,此時服務(wù)端會怎么處理?看過我的圖解網(wǎng)絡(luò)的讀者都知道,TCP連接是由「四元組」唯...

?

?大家好,我是小林。

昨晚有位讀者問了我這么個問題:大概意思是,一個已經(jīng)建立的 TCP 連接,客戶端中途宕機了,而服務(wù)端此時也沒有數(shù)據(jù)要發(fā)送,一直處于 establish 狀態(tài),客戶端恢復(fù)后,向服務(wù)端建立連接,此時服務(wù)端會怎么處理?看過我的圖解網(wǎng)絡(luò)的讀者都知道,TCP 連接是由「四元組」唯一確認的。然后這個場景中,客戶端的IP、服務(wù)端IP、目的端口并沒有變化,所以這個問題關(guān)鍵要看客戶端發(fā)送的 SYN 報文中的源端口是否和上一次連接的源端口相同。1. 客戶端的 SYN 報文里的端口號與歷史連接不相同如果客戶端恢復(fù)后發(fā)送的 SYN 報文中的源端口號跟上一次連接的源端口號不一樣,此時服務(wù)端會認為是新的連接要建立,于是就會通過三次握手來建立新的連接。那舊連接里處于 establish 狀態(tài)的服務(wù)端最后會怎么樣呢?
  • 如果服務(wù)端發(fā)送了數(shù)據(jù)包給客戶端,由于客戶端的連接已經(jīng)被關(guān)閉了,此時客戶的內(nèi)核就會回 RST 報文,服務(wù)端收到后就會釋放連接。

  • 如果服務(wù)端一直沒有發(fā)送數(shù)據(jù)包給客戶端,在超過一段時間后, TCP ?;顧C制就會啟動,檢測到客戶端沒有存活后,接著服務(wù)端就會釋放掉該連接。

2. 客戶端的 SYN 報文里的端口號與歷史連接相同如果客戶端恢復(fù)后,發(fā)送的 SYN 報文中的源端口號跟上一次連接的源端口號一樣,也就是處于 establish 狀態(tài)的服務(wù)端收到了這個 SYN 報文。大家覺得服務(wù)端此時會做什么處理呢?
  • 丟掉 SYN 報文?

  • 回復(fù) RST 報文?

  • 回復(fù) ACK 報文?

剛開始我看到這個問題的時候,也是沒有思路的,因為之前沒關(guān)注過,然后這個問題不能靠猜,所以我就看了 RFC 規(guī)范和看了 Linux 內(nèi)核源碼,最終知道了答案。我不賣關(guān)子,先直接說答案。
處于 establish 狀態(tài)的服務(wù)端如果收到了客戶端的 SYN 報文(注意此時的 SYN 報文其實是亂序的,因為 SYN 報文的初始化序列號其實是一個隨機數(shù)),會回復(fù)一個攜帶了正確序列號和確認號的 ACK 報文,這個 ACK 被稱之為 Challenge ACK。接著,客戶端收到這個 Challenge ACK,發(fā)現(xiàn)序列號并不是自己期望收到的,于是就會回 RST 報文,服務(wù)端收到后,就會釋放掉該連接。

RFC 文檔解釋

rfc793 文檔里的第 34 頁里,有說到這個例子。
原文的解釋我也貼出來給大家看看。
  • When the SYN arrives at line 3, TCP B, being in a synchronized state,
    and the incoming segment outside the window, responds with an
    acknowledgment indicating what sequence it next expects to hear (ACK
    100).

  • TCP A sees that this segment does not acknowledge anything it
    sent and, being unsynchronized, sends a reset (RST) because it has
    detected a half-open connection.

  • TCP B aborts at line 5. ?

  • TCP A willcontinue to try to establish the connection;

我就不瞎翻譯了,意思和我在前面用中文說的解釋差不多。

源碼分析

處于 establish 狀態(tài)的服務(wù)端如果收到了客戶端的 SYN 報文時,內(nèi)核會調(diào)用這些函數(shù):tcp_v4_rcv
??->?tcp_v4_do_rcv
????->?tcp_rcv_established
??????->?tcp_validate_incoming
????????->?tcp_send_ack
我們只關(guān)注 tcp_validate_incoming 函數(shù)是怎么處理 SYN 報文的,精簡后的代碼如下:
從上面的代碼實現(xiàn)可以看到,處于 establish 狀態(tài)的服務(wù)端,在收到報文后,首先會判斷序列號是否在窗口內(nèi),如果不在,則看看 RST 標(biāo)記有沒有被設(shè)置,如果有就會丟掉。然后如果沒有 RST 標(biāo)志,就會判斷是否有 SYN 標(biāo)記,如果有 SYN 標(biāo)記就會跳轉(zhuǎn)到 syn_challenge 標(biāo)簽,然后執(zhí)行 tcp_send_challenge_ack 函數(shù)。tcp_send_challenge_ack 函數(shù)里就會調(diào)用 tcp_send_ack 函數(shù)來回復(fù)一個攜帶了正確序列號和確認號的 ACK 報文。

如何關(guān)閉一個 TCP 連接?

我這里問題大家這么一個問題,如何一個 TCP 連接?可能大家第一反應(yīng)是「殺掉進程」不就行了嗎?是的,這個是最粗暴的方式,殺掉客戶端進程和服務(wù)端進程影響的范圍會有所不同:
  • 在客戶端殺掉進程的話,就會發(fā)送 FIN 報文,來斷開這個客戶端進程與服務(wù)端建立的所有 TCP 連接,這種方式影響范圍只有這個客戶端進程所建立的連接,而其他客戶端或進程不會受影響。

  • 而在服務(wù)端殺掉進程影響就大了,此時所有的 TCP 連接都會被關(guān)閉,服務(wù)端無法繼續(xù)提供訪問服務(wù)。

所以,關(guān)閉進程的方式并不可取,最好的方式要精細到關(guān)閉某一條 TCP 連接。有的小伙伴可能會說,偽造一個四元組相同的 RST 報文不就行了?這個思路很好,但是不要忘了還有個序列號的問題,你偽造的 RST 報文的序列號一定能被對方接受嗎?如果 RST 報文的序列號不能落在對方的滑動窗口內(nèi),這個 RST 報文會被對方丟棄的,就達不到關(guān)閉的連接的效果。所以,要偽造一個能關(guān)閉 TCP 連接的 RST 報文,必須同時滿足「四元組相同」和「序列號正好落在對方的滑動窗口內(nèi)」這兩個條件。直接偽造符合預(yù)期的序列號是比較困難,因為如果一個正在傳輸數(shù)據(jù)的 TCP 連接,滑動窗口時刻都在變化,因此很難剛好偽造一個剛好落在對方滑動窗口內(nèi)的序列號的 RST 報文。辦法還是有的,我們可以偽造一個四元組相同的 SYN 報文,來拿到“合法”的序列號!正如我們最開始學(xué)到的,如果處于 establish 狀態(tài)的服務(wù)端,收到四元組相同的 SYN 報文后,會回復(fù)一個 Challenge ACK,這個 ACK 報文里的「確認號」,正好是服務(wù)端下一次想要接收的序列號,說白了,就是可以通過這一步拿到服務(wù)端下一次預(yù)期接收的序列號。然后用這個確認號作為 RST 報文的序列號,發(fā)送給服務(wù)端,此時服務(wù)端會認為這個 RST 報文里的序列號是合法的,于是就會釋放連接!在 Linux 上有個叫 killcx 的工具,就是基于上面這樣的方式實現(xiàn)的,它會主動發(fā)送 SYN 包獲取 SEQ/ACK 號,然后利用 SEQ/ACK 號偽造兩個 RST 報文分別發(fā)給客戶端和服務(wù)端,這樣雙方的 TCP 連接都會被釋放,這種方式活躍和非活躍的 TCP 連接都可以殺掉。使用方式也很簡單,只需指明客戶端的 IP 和端口號。./killcx?<IP地址>:<端口號>
killcx 工具的工作原理,如下圖。
它偽造客戶端發(fā)送 SYN 報文,服務(wù)端收到后就會回復(fù)一個攜帶了正確「序列號和確認號」的 ACK 報文(Challenge ACK),然后就可以利用這個 ACK 報文里面的信息,偽造兩個 RST 報文
  • 用 Challenge ACK 里的確認號偽造 RST 報文發(fā)送給服務(wù)端,服務(wù)端收到 RST 報文后就會釋放連接。

  • 用 Challenge ACK 里的序列號偽造 RST 報文發(fā)送給客戶端,客戶端收到 RST 也會釋放連接。

正是通過這樣的方式,成功將一個 TCP 連接關(guān)閉了!這里給大家貼一個使用 killcx 工具關(guān)閉連接的抓包圖,大家多看看序列號和確認號的變化。
所以,以后抓包中,如果莫名奇妙出現(xiàn)一個 SYN 包,有可能對方接下來想要對你發(fā)起的 RST 攻擊,直接將你的 TCP 連接斷開!怎么樣,很巧妙吧!當(dāng)時我了解到這種方式關(guān)閉 TCP 連接的時候,我也是相當(dāng)震驚的(末尾點題,手動狗頭)。??

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉