TCP半連接隊列和全連接
概述
如上圖所示, 在TCP三次握手中,服務器維護一個半連接隊列(sync queue) 和一個全連接隊列(accept queue)。 當服務端接收到客戶端第一次SYN握手請求時,將創建的request_sock結構,存儲在半連接隊列中(向客戶端發送SYN+ACK,並期待客戶端響應ACK),此時的連接在服務器端出於SYN_RECV狀態。當服務端收到客戶端最後的ACK確認時,將半連接中的相應條目刪除,然後將相應的連接放入 全連接隊列中, 此時服務端連接狀態為ESTABLISHED。 進入全連接隊列中的連接等待accept()調用取用。既然是隊列,肯定就有大小,那麽當這兩個隊列滿了沒有空間了怎麽辦呢? 例如如果我們listen()後不去accept() ,那麽全連接隊列肯定會滿的。 我們下面分別對於這兩個隊列結合試驗進行描述。
試驗環境:
CentOS Linux release 7.5.1804 (Core)
Linux version 3.10.0-229.4.2.el7.x86_64
syns queue 半連接隊列
首先說一下 SYN flooding攻擊,為了應對SYN flooding(即客戶端只發送SYN包發起握手而不回應ACK完成連接建立,快速填滿server端的半連接隊列,讓它無法處理正常的握手請求),Linux實現了一種稱為SYNcookie的機制,通過net.ipv4.tcp_syncookies控制,設置為1表示開啟。簡單說SYNcookie就是將連接信息編碼在ISN(initialsequencenumber)中返回給客戶端,這時server不需要將半連接保存在隊列中,而是利用客戶端隨後發來的ACK帶回的ISN還原連接信息,以完成連接的建立,避免了半連接隊列被攻擊SYN包填滿。 也就是說,如果開啟了syncookies的話(通過 TCP參數 net.ipv4.tcp_syncookies配置 ),半連接隊列就相當於是無限大的了。在我的環境中就是默認開啟的。
如果我們將syncookies關閉的話,半連接隊列的長度將為 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog) ,,此時對半連接填滿時的處理策略是 server將 丟棄請求連接的SYN,不回復SYN+ACK,這樣就會造成client收不到握手響應,始終處在SYN_SENT狀態,經過幾次重傳後,客戶端 connect() 調用失敗。
accept queue 全連接隊列
全連接隊列的長度為 min(backlog, somaxconn),默認情況下,somaxconn 的值為 128(/proc/sys/net/core/somaxconn),表示最多有 129 的 ESTAB 的連接等待 accept(),而 backlog 的值則是由 int listen(int sockfd, int backlog) 中的第二個參數指定,listen 裏面的 backlog 可以有我們的應用程序去定義。 當全連接隊列滿了後的處理策略基於TCP參數net.ipv4.tcp_abort_on_overflow,在我的機器上默認為0。- tcp_abort_on_overflow 關閉時
當server收到最後一次ACK時,希望將連接從半連接隊列中取出放入全連接隊列,但是此時全連接隊列已滿,此時的策略是 將最後接收到的ACK丟棄,並且根據net.ipv4.tcp_synack_retries定義的次數重新向client發送SYN+ACK, client在接收到重傳的SYN+ACK後會認為之前的ACK丟失了進而重傳ACK,這樣在下次重新接收到ACK後,如果全連接隊列有空間了,連接就可以正確完成建立。 如果重傳了規定次數後全連接隊列中依舊沒有空間,那麽server會簡單終止這次連接(這裏簡單終止的意思是server並沒有像client發送RST表明連接無法建立,而是直接丟棄了,這樣就會導致在client中的連接處在ESTABLISHED狀態,並一直如此,後面的實驗會有涉及,我很困惑為什麽要這樣設計? 還是我沒有正確理解 !)。
- tcp_abort_on_overflow 開啟時
TCP半連接隊列和全連接