TCP快速重傳觸發條件的一個細節
倒數第二個工作日,拒絕午飯!撰文以記之。
浙江溫州皮鞋溼,下雨進水不會胖!
前幾日和前同事聊天聊到一個Linux核心協議棧實現中關於TCP快速重傳觸發條件的一個細節,覺得比較有意思。
這個細節是這樣的。
且看tcp_ack中,如果我們發現該ACK所攜帶的資訊是 可疑的 , 那麼邏輯就會進入到進一步的篩選判斷中,以最終抉擇 是不是要讓該TCP連線的擁塞狀態機從Open切換到Disorder或者Recovery狀態 。
我們看一卡這個 可疑的 其判斷標準是什麼。
#define FLAG_NOT_DUP(FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) #define FLAG_CA_ALERT(FLAG_DATA_SACKED|FLAG_ECE) ... static inline bool tcp_ack_is_dubious(const struct sock *sk, const int flag) { return !(flag & FLAG_NOT_DUP) || (flag & FLAG_CA_ALERT) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open; }
FLAG_CA_ALERT這個倒是很明確,只要攜帶了SACK,或者顯式的擁塞標誌,那肯定是可疑的,需要進一步判斷。
這裡比較有意思的是:
為什麼只要攜帶FLAG_DATA標誌可以代表NOT_DUP?
比較明顯的疑點就是,在關閉SACK支援的情況下,如果一個雙向傳輸的TCP連線,對端每次都發送點資料過來,那麼本端豈不是永遠都不會被觸發快速重傳嗎?
簡單點說是這樣的,確實是只要對端傳送資料過來,就不會進入tcp_ack_is_dubious分支,進而不會觸發快速重傳。
編寫下面的packetdrill指令碼以驗證事實:
0socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0bind(3, ..., ...) = 0 +0listen(3, 1) = 0 +0< S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7> +0> S. 0:0(0) ack 1 <...> +0 < . 1:1(0) ack 1 win 257 +0accept(3, ..., ...) = 4 // Send 1 data segment and get an ACK, so cwnd is now 4. +0write(4, ..., 1000) = 1000 +0> P. 1:1001(1000) ack 1 +0 < . 1:1(0) ack 1001 win 257 // Write 4 data segments. +0write(4, ..., 4000) = 4000 +0> P. 1001:5001(4000) ack 1 // Get 3 SACKs. // +0 < . 1:11(10) ack 1001 win 257 <sack 2001:3001,nop,nop> +0< . 11:21(10) ack 1001 win 257 <sack 2001:4001,nop,nop> +0< . 21:31(10) ack 1001 win 257 <sack 2001:5001,nop,nop> // net.ipv4.tcp_sack=0時,這裡沒有快速重傳,這裡超時 // net.ipv4.tcp_sack=1時,這裡觸發了快速重傳,因為FLAG_CA_ALERT置位。 // Receiver ACKs all data. +10 < . 1:1(0) ack 6001 win 257
這是一個問題嗎?如果是問題為什麼多年以來Linux的TCP實現一直這麼寫。
仔細思考了一下這個問題,最後發現其實這不是問題。
TCP是全雙工通訊協議,它原本應該全雙工兩個方向獨立進行傳輸/確認,為了提高資源利用率,實施了 捎帶確認 的邏輯,也就是說,捎帶確認只是一個優化,而不是與生俱來的。
在開啟SACK支援時,捎帶確認中埋藏SACK告訴反方向的一端說可能有擁塞丟包,那這只是一種友情協助,並不是人家的義務。而關閉SACK支援時,無論如何捎帶確認也無法明確告知反方向資料包可能丟包或者亂序了…
換句話說, 只有3次重複ACK明確是由本端傳送的資料包所觸發的時候,才會去進一步判斷是否要快速重傳,否則在可疑判斷時將會直接放行!
顯然,攜帶資料的包是對端主動傳送的,而其攜帶的ACK屬於捎帶確認,不屬於本端觸發的確認,因此不會被認為是重複ACK。
如果我們不區分 捎帶確認 和 資料接收觸發的確認 會怎樣呢?下圖示之:

為了在 可疑異常 情況下可以儘快讓 資料接收觸發的確認 反饋到傳送端,RFC規定, 可疑異常情況下的資料接收觸發的確認,不允許捎帶!必須立刻傳送!
從小沒咋放過鞭炮,也沒咋吃過一家人一起的除夕團圓飯,所以不知年味兒,雖然如此,我還是祝福每一個人,每一個家庭都能團團圓圓,幸運美滿,明年獲得更多的幸福收穫!
浙江溫州皮鞋溼,下雨進水不會胖!