TCP/IP 筆記 - 超時和重傳


TCP協議為了提供可靠的數據傳輸服務,會啟動數據重傳來解決下層網絡層(IP)可能出現的數據包丟失。

超時重傳介紹

TCP重傳由兩套獨立機制來完成重傳,基於時間的超時重傳(RTO,TCP發送數據時會設置一個計時器,若至計時器超時仍未收到數據確認信息,則會引發相應的超時和計時器重傳操作),基於確認信息的構成(通常在沒發生延時的情況下,若TCP累積確認無法返回新的ACK,或者當ACK包含的選擇確認信息(SACK)表明出現失序報文段時,快速重傳會推斷出現丟包,這時候發送端認為接收端可能出現數據丟失時,需要決定發送新數據還是重傳)。

對於超時和重傳,之前有了一定的認識,如:

  在ICMP目的不可達的時,采用UDP的TFTP客戶端使用簡單且低效的超時和重傳策略:設置足夠大的超時間隔,每5秒進行一次重傳;

  第13章提到TCP所使用指數回退行為:在目的主機不存在的場景中,TCP在嘗試建立連接過程中,每次重傳時采用比上次更大的延時間隔。

TCP能不斷"學習"發送端與接收端之間的鏈路特征,以記錄一些狀態變量。早些的TCP中,當連接關閉,這些學習而得的狀態便會丟失;在較新的TCP中實現了維護這些度量值,即使連接斷開后,也能保存之前存在的路由或轉發表項或其他一些系統數據結構。當創立一個新的連接時,首先查看數據結構中是否存在與該目的端的先前通信信息,如果存在則使用信息來初始化連接的一些變量值。如在linux中,變量值更新為現存值中的最大值和最近測量的數據,可通過iproute2[IPR2]的相關工具來查看變量值。這也稱為TCP連接的目的度量。

當TCP超時重傳時,它並不需要完全重傳相同的報文段,TCP允許執行重新組包,發送一個更大的報文段來提高性能(但不能大於接收端通告的MSS和路徑MTU)。因為TCP是通過字節號來識別發送和接收的數據的。

RTO值的計算

由於TCP需要適應不同環境進行操作,若TCP先於RTT開始重傳,可能會在網絡中引入不必要的重復數據;若延遲遠大於RTT的間隔發送重傳數據,整體網絡利用率(以及單個連接吞吐量)會隨之下降。因此,RTO需要TCP動態設置,且RTO的設置是TCP性能的關鍵。

TCP在收到數據后會返回確認信息,因此可在該信息中攜帶一個字節的數據來測量傳輸該確認信息所需的時間,每個此類的測量結果被稱為RTT樣本,TCP則根據這些樣本來給出RTO估值。每個TCP連接的RTT均獨立估算,並且重傳計時器會對任何占用序列號的在傳數據計時。

RTO計算的經典方法:

    # 采用如下公式得到平滑的RTT估計值(稱為SRTT):
    SRTT ← α(SRTT)+(1-α)RTTs (s為下標)
    # SRTT是基於現存值和新的樣本值RTTs得到的更新結果,常量α是平滑因子,推薦值為0.8~0.9,這個方法被稱為指數加權移動平均或者低通過濾器。
    
    # 采用以下公式設置RTO:
    RTO = min(ubound,max(1bound,(SRTT)β))
    # β為時延離散因子,推薦值為1.3~2.0,ubound為RTO的上邊界(可設定建議值,如1分鍾),lbound為RTO的下邊界(可設定建議值,如1秒)。它使得RTO的值設置為1秒或約2倍的SRTT。相對於文檔的RTT來說,這種方法能取得不錯的性能,然而若TCP運行於RTT變化較大的網絡中,則無法獲得期望的效果。

RTO計算的標准方法(結合平均值和平均偏差來進行估算):

    srtt ← (1 - g)(srtt) + (g)M
    rttvar ← (1 - h)(rttvar) + (h)(|M - srtt|)
    RTO = srtt + 4(rttvar)
    # srtt代替了之前的SRTT,且rttvar為平均偏差的EWMA,M代替了之前的RTTs。

這組等式也可以寫成另一種形式:

    Err = M - srtt
    srtt ← srtt + g(Err)
    rttvar ← rttvar + h(|Err| - rttvar)
    RTO = srtt + 4(rttvar)
    # srtt為均值的EWMA,rttcar為絕對誤差|Err|的EWMA,Err為測量值M與當前RTT估計值srtt之間的偏差。

增量g為新RTT樣本M占srtt估計值的權重,取1/8,增量h為新平均偏差樣本(新樣本M與當前平均值srtt之間的絕對誤差)占偏差估計值rttvar的權重,取1/4。這種方法是迄今為止許多TCP實現計算RTO的方法。

在測量RTT的過程中,TCP始終始終處於運轉狀態,對初始序列號來說,TCP時鍾通常為某個變量,隨着系統時鍾而做出更新。TCP時鍾一個"滴答"的時間長度稱為粒度,通常該值相對較大(約500ms),但近期實現的時鍾使用更細的粒度(如linux的1ms)。

粒度會影響RTT的測量及RTO的設置,在[RFC6298]中,粒度用於優化RTO的更新情況,並給RTO設置了一個下界:

    RTO = max(srtt + max(G,4(rttvar)),1000)
    # G為計時器粒度,1000ms為整個RTO的下界值([RFC6298]的規則建議值)。因此,RTO至少為1s,同時也提供可選上界值,假設為60s。

對於RTO的初始值,根據[RFC6298]描述,為1s,而初始SYN報文段采用的超時間隔為3s,當接收到收個RTT測量結果M,TCP按如下方式進行初始化:

    srtt ← M
    rttvar ← M/2

在測量RTT樣本過程中,可能會出現重傳的二義性,如假設一個包的傳輸出現超時,該數據包被重傳,接着收到一個確認信息,那么這個信息是對第一次還是第二次傳輸的確認就存在二義性。當出現超時重傳時,接收到重傳數據的確認信息時不能更新RTT估計值,這是Karn算法的第一部分,通過排除二義性數據來解決RTT估算中出現的二義性問題。

Karn算法的第二部分則在計算RTO過程中采用一個退避系數,每當重傳計時器出現超時,退避系數加倍,該過程一直持續至接收到非重傳數據,此時退避系數重新設置為1(二進制指數退避取消),重傳計時器返回正常值。

TCP時間戳選項(TSOPT)可用作RTT測量[RFC1323]。時間戳值(TSV)攜帶於初始化SYN的TSOPT中,並在SYN+ACK的TSOPT的TSER部分返回,以此設定srtt、rttvar與RTO的初始值。初始SYN也可看作數據,應測量其RTT值。

因為TCP並非對其接收到的每個報文段都返回ACK,如傳輸大批量數據時,TCP通常采取每兩個報文段返回一個ACK的方法,或者當數據出現丟失、失序或重傳成功時,TCP累積確認機制表明報文段與其ACK之間並非嚴格的一一對應,為解決這些問題,使用時間戳選項的TCP采用如下算法來測量RTT樣本值:、

  1. TCP發送端在其發送的每個報文段的TSOPT的TSV部分攜帶一個32比特的時間戳值。該值包含數據發送時刻的TCP時鍾值。

  2. 接收端記錄接收到的TSV值(名為TsRecent的變量)並在對應的ACK中返回,並且記錄其上一個發送的ACK號(名為LastACK的變量)。

  3. 當一個新的報文段到達(接收端)時,如果其序列號與LastACK的值吻合(即為下一個期望接收的報文段),則將其TSV值存入TsRecent。

  4. 接收端發送的任何一個ACK都包含TSOPT,TsRecent變量包含的時間戳值被寫入其TSER部分。

  5. 發送端接收到ACK后,將當前TCP時鍾減去TSER值,得到的差即為新的RTT樣本估計值。

若在連接初始化過程中,TCP的通信一方啟用時間戳,則另一端將啟用時間戳。

linux的RTT測量過程與標准方法有所區別,使用更細的時鍾粒度和更頻繁的RTT測量。linux記錄兩個新的變量,mdev和mdev_mas,mdev為采用標准方法的瞬時平均偏差估計值,即之前的rttvar;mdev_max則記錄在測量RTT樣本過程中的最大mdev,其最小值不小於50ms。rttvar需定期更新以保證其不小於mdev_max,因此,RTO不會小於200ms。

TCP時間戳選項攜帶了發送端TCP時鍾的副本。接着ACK將該值返回至接收端,通過計算兩者之差(當前時鍾減去返回的時間戳)來更新其srtt與rttvar估計值。如下圖:

初始化RTO估算計算過程:

    srtt = 16ms
    mdev = (16/2)ms = 8ms
    rttvar = mdev_max = max(mdev, TCP_RTO_MIN) = max(8,50) = 50ms
    RTO = srtt + 4(rttvar) = 16 + 4(50) = 216ms

第二次RTO估算計算過程:

    m = 223 - 127 = 96
    mdev = mdev(3/4) + |m - srtt|(1/4) = 8(3/4) + |80|(1/4) = 26ms
    mdev_max = max(mdev_max,mdev) = max(50,26) = 50ms
    srtt = srtt(7/8) + m(1/8) = 16(7/8) + 96(1/8) = 14+12 = 26ms
    rttvar = mdev_max = 50ms
    RTO = srtt + 4(rttvar) = 26 + 4(50) = 226ms

針對RTT減小的情況,若新樣本值小於RTT估計范圍的下界(srtt - mdev),則減小新樣本的權重:

    if(m < (srtt - mdev))
        mdev = (31/32) * mdev + (1/32) * |srtt - m|
    else
        mdev = (3/4) * mdev + (1/4) * |srtt - m|

該結果可以避免RTT減小導致的RTO增大問題。

重傳的實現

基於計時器的重傳 

一旦TCP發送端得到基於時間變化的RTT測量值,就能據此設置RTO,發送報文段時應確保重傳計時器設置合理。在設定計時器前,需記錄被計時的報文段序列號,若及時收到了該報文段的ACK,則計時器被取消。之后發送端發送一個新的數據包時,需設定一個新的計時器並記錄新的序列號。因此,每一個TCP連接的發送端不斷地設定和取消一個重傳計時器;如果沒有數據丟失,則不會出現計時器超時。

對TCP而言,計時器需要有效的實現被設置、重新設置、取消的功能,若TCP正常工作,則計時器不會出現超時的情況。

若在連接設定的RTO內,TCP沒有收到計時報文段的ACK,將會觸發超時重傳。TCP將超時重傳視為相當重要的事件,當發生這種情況時,它通過降低當前數據發送率來對此進行快速響應。實現有兩種方法:基於擁塞控制機制減小發送窗口大小;每當一個重傳報文段被再次重傳時,則增大RTO的退避因子。

RTO值乘上值γ來形成新的超時退避值:

    RTO = γRTO

通常情況下γ為1,隨着多次重傳,γ呈加倍增長:2,4,8...。通常γ不能超過最大退避因子(linux確保其RTO設置不超過TCP_RTO_MAX,其默認值為120s)。一旦受到相應ACK,γ會重置為1。

快速重傳

快速重傳機制[RFC5681]基於接收端的反饋信息來引發重傳,而非重傳計時器的超時,快速重傳能更加及時有效的修復丟包情況。

快速重傳概況如下:TCP發送端在觀測到至少dupthresh(重復ACK閾值,一定數目的重復ACK,默認通常為3),即重傳可能丟失的數據分組,而不必等到重傳計時器超時。

流程如圖:

帶選擇確認的重傳

隨着選擇確認選項的標准化[RFC2018],TCP接收端可提供SACK功能,通過TCP頭部的累積ACK號字段來描述其接收到的數據。采用SACK選項時,一個ACK可包含三四個告知失序數據的SACK信息。每個SACK信息包含32位的序列號,代表接收端存儲的失序數據的起始至最后一個序列號(加1)。

SACK選項指定n個塊的長度為8n+2字節,因此40字節可包含最多4個塊。通常SACK會與TSOPT一同使用,因此需要額外的10個字節(外加2字節的填充數據),所以每個ACK只能包含3個塊。

SACK接收端行為:接收端在TCP連接建立期間受到SACK許可選項即可生成SACK。通常來說,每當緩存中存在失序數據時,接收端就可生成SACK。第一個SACK塊內包含的是最近接收到的報文段序列號范圍,其余的SACK塊包含的內容按照接收的先后依次排列。也就是說,最新一個塊中包含的內容除了包含最近接收的序列號信息,還需重復之前的SACK塊。

SACK發送端行為:在發送端也應提供SACK功能,並且合理地利用接收到的SACK塊來進行丟失重傳,該過程也稱選擇性重傳或選擇性重發。SACK發送端記錄接收到的累積ACK信息,還需記錄接收到的SACK信息,並利用該信息來避免重傳正確接收的數據。一種方法是當接收到相應序列號范圍的ACK時,則在其重傳緩存中標記該報文段的選擇重傳成功。

偽超時和重傳

在一些情況下,即使沒有出現數據丟失也可能引發重傳,這種不必要的重傳稱為偽重傳,其主要造成原因是偽超時,即過早判定超時,其他因素如包失序、包重復,或者ACK丟失也可能導致該現象。為處理偽超時問題提出許多方法,這些方法通常包含檢測算法和響應算法。檢測算法用於判斷某個超時或基於計時器的重傳是否真實,一旦認定出現偽超時則執行響應算法,用於撤銷或減輕該超時帶來的影響。

下面介紹一些處理方法:

重復SACK(DSACK)擴展:基本的SACK機制對接收端收到重復數據段時怎樣運作沒有規定,這些重復數據可能是偽重傳、網絡中的重復或其他原因造成的。在接收端采用DSACK(重復SACK),並結合通常的SACK發送端,可在第一個SACK塊中告知接收端收到的重復報文段序列號。DSACK的主要目的是判斷何時的重傳是不必要的,並了解網絡中的其他事項,因此發送端至少可以推斷是否發生了包失序、ACK丟失、包重復或偽重傳。

Eifel檢測算法:實驗性的Eifel檢測算法[RFC3522]利用了TCP的TSOPT來檢測偽重傳。在發生超時重傳后,Eifel算法等待接收下一個ACK,若為針對第一次傳輸的確認,則判定該重傳是偽重傳。利用Eifel檢測算法能比僅用采用DSACK更早檢測到偽重傳行為,因為它判斷偽重傳的ACK是在啟動丟失恢復之前生成的。

前移RTO恢復:前移RTO恢復[RFC5682]是檢測偽重傳的標准算法,但只檢測由重傳計時器超時引發的偽重傳。F-RTO會修改TCP的行為,在超時重傳后收到第一個ACK時,TCP會發送新的數據,之后再響應后一個到達的ACK。如果其中有一個為重復ACK,則認為此次重傳沒問題,如果兩個都不是重復ACK,則表示該重傳是偽重傳。如果新數據的傳輸得到了相應的ACK,就使得接收端窗口前移。如果新數據的發送導致了重復ACK,那么接收端至少有一個或更多的空缺。

Eifel響應算法:一旦判斷出現偽重傳,則會引發一套標准操作,即Eifel響應算法[RFC4015]。由於響應算法邏輯上與Eifel檢測算法分離,所以它可與前面的任一種檢測方法結合使用。原則上超時重傳和快速重傳都可使用Eifel響應算法,但目前只針對超時重傳做了相關規定。Eifel響應算法根據是否能盡早或較遲檢測出偽超時的不同而有所區別。前綴稱為偽超時,通過檢查ACK或原始傳輸來實現;后者稱為遲偽超時,基於有超時而引發的重傳所返回的ACK來判定,響應算法只針對第一種重傳事件。

在重傳計時器超時后,它會查看srtt和rttvar的值,並按如下方式記錄新的變量srtt_prev和rttvar_prev:

    srtt_prev = srtt + 2(G)
    rttvar_prev = rttvar

在任何一次計時器超時后,都會指定這兩個變量,但只要在判定出現偽超時才會使用它們,用於設定新的RTO。在上式中,G代表TCP時鍾粒度。srtt_prev設為rtt加上兩倍的時鍾粒度是由於srtt的值過小,可能會出現偽超時。

完成srtt_prev和rttvar_prev的存儲后,就要觸發某些檢測算法。運行檢測算法后可得到一個特殊的值,稱為偽恢復。如果檢測到一次偽超時,則將偽恢復置為SPUR_TO。如果檢測到遲偽超時,則將其置為LATE_SPUR_TO。

若偽恢復為SPUR_TO,TCP可在恢復階段完成之前進行操作,通過將下一個要發送報文段(稱為SND.NXT)的序列號修改為最新的未發送過的報文段(稱為SND.MAX)。這樣就可在首次重傳后避免不必要的"回退N"行為。如果檢測到一次遲偽超時,此時已生成對首次重傳的ACK,則SND.NXT不變。在以上兩種情況下,都要重新設置擁塞控制狀態(第16章介紹)。一旦接收到重傳計時器超時后發送的報文段的ACK,就按如下方式風險srtt、rttvar、RTO:

    srtt ← max(srtt_prev,m)
    rttvar ← max(rttvar_prev,m/2)
    RTO ← srtt + max(G,4(rttvar))

m是一個RTT樣本值,它是基於超時后收個發送數據收到的ACK計算得到的。

包失序和包重復

包失序 

在IP網絡中出現包失序的原因有:在於IP層不能保證包傳輸是有序進行的;硬件方面一些高性能設備(路由器)采用多個並行數據鏈路;不同的處理延時;等其他情況...

如果失序發生在反向(ACK)鏈路,就會使得TCP發送端窗口快速前移,接着又可能收到一些顯然重復而應被丟棄的ACK。由於TCP的擁塞控制行為,這種情況可能導致發送端出現不必要的流量突發行為,影響可用網絡帶寬。

如果失序發生在正向鏈路,TCP可能無法識別失序和丟包。數據的丟失和失序都會導致接收端收到無序的包,造成數據之間的空缺。當失序比較嚴重時(數據包的字節值差距大),TCP會誤認為數據已經丟失,從而觸發快速重傳,導致偽重傳。

互聯網中嚴重的失序並不常見,且通過將dupthresh設為相對較小值(如3)就能處理大部分情況,或者一些方法得以動態調整dupthresh值。

參考圖如下:

包重復 

包重復的情況比較少,當IP協議也可能出現將單個包傳輸多次的情況。如當鏈路層網絡協議執行一次重傳並生成同一個包的兩個副本(這種估計屬於誤操作了...)。這時候,TCP可能出現混淆。針對包重復現象,利用SACK(特別是DSACK)就可以簡單的忽略這個問題。

相關攻擊

有一類DoS攻擊稱為低速率DoS攻擊[KK03],在這類攻擊中,攻擊者向網關或主機發送大量數據,使得受害系統持續處於重傳超時的狀態。由於攻擊者可預知受害TCP何時啟動重傳,並在每次重傳時生成並發送大量數據。因此,受害TCP總能感知到擁塞的存在,根據Karn算法不斷減小發送速率並退避發送,導致無法正常使用網絡帶寬。針對此類攻擊的預防方法是隨機選擇RTO,使得攻擊者無法預知確切的重傳時間。

與DoS相關但不同的一種攻擊為減慢受害TCP的發送,使得RTT估計值過大,使得受害者在丟包后不會立即重傳。相反的攻擊也是有可能的:攻擊者在數據發送完成但還未到達接收端時偽造ACK。這樣攻擊者就能使受害TCP認為連接的RTT遠小於實際值,導致過分發送而造成大量的無效傳輸。

 

參考:

《TCP IP 詳解卷1:協議》

RFC官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM