TCP系列08—鏈接管理—七、TCP 常見選項(option)

1、TCP選項概述linux

        在前面介紹TCP頭的時候,咱們說過tcp基本頭下面能夠帶有tcp選項,其中有些選項只能在鏈接過程當中隨着SYN包發送,有些能夠延後。下表彙總了一些tcp選項算法

其中我標記爲紅色的部分是常見的TCP選項,咱們僅針對這些紅色的TCP選項進行介紹(主要是非紅色的我也不太瞭解~~~),另外RFC1323已經被RFC7323取代,這裏給出的是TCP選項原始定義的RFC緩存

        按照RFC793規定,一個TCP選項只須要單字節對齊,可是在實現上通常是兩字節對齊或者會經過NOP選項實現四字節對齊,例如3bytes長的WSOPT選項,linux在添加這個tcp選項的時候,就會在這個選項前面加一個1byte的NOP選項湊成4bytes。服務器

       TCP選項的格式有兩種,一種是單字節長的TCP選項如EOL和NOP。另一種是包含1byte的kind,1byte的length,在加上選項的數據。除了EOL和NOP選項外,其餘的TCP選項都是後一種格式,且爲了兼容,協議要求後續若是擴展其餘tcp選項一樣須要採用後一種格式。cookie

       RFC1122協議規定TCP接收端必須可以處理任意TCP包中的選項,對於不能識別的TCP選項則採起忽略該選項的辦法。其中有一些選項如EOL、NOP、MSS等是協議規定必須支持的,同時協議要求未來新增的TCP選項都須要由length域,這樣TCP實現不能識別這個選項的時候就能夠跳過這個選項。網絡

2、EOL和NOPapp

EOL格式以下
tcp

        +--------+
        |00000000|
        +--------+
         Kind=0

      這個選項用來指示TCP的選項列表結束,這個選項是用在全部TCP選項的後面,並非每一個TCP選項後面都須要這個選項來指示選項結束,只有在TCP選項列表結束後沒有與TCP頭中的Header Length字段指定的頭長重合時候才須要使用EOL選項,另外這個選項並不必定放在TCP頭(包括擴展頭)的末尾。舉個例子,假如Header Length指定的TCP頭長爲40bytes,其中第29-38bytes爲TSOPT選項,則能夠在第39byte處添加一個EOL選項指示選項列表結束,能夠看到EOL並無位於TCP頭的結束位置的第40byte。對於最後一個byte RFC793協議規定須要以0來填充。注意這個EOL後面填充的0已經不屬於TCP選項的一部分了。佈局

NOP選項格式以下性能

        +--------+
        |00000001|
        +--------+
         Kind=1

       這個選項可使用在選項之間或者結尾處,好比,爲了使3bytes的WSOPT選項在四字節對齊的邊界處結束,能夠在WSOPT選項以前添加一個NOP選項,這樣整個選項長度爲4bytes,更容易對齊。可是按照RFC793協議規定,發送端並不保證會填充NOP選項來讓其餘選項達到對齊的目的,所以接收端也應該準備好接收非四字節對齊的WSOPT選項。也就是說一樣的幾個TCP選項能夠有不一樣的選項排列順序,即便是相同的排列順序也可能由於NOP和EOL等等而有不一樣的排列布局。

      最後從linux實現的角度來講,linux自己發送TCP數據包的時候並不會添加EOL選項,而是經過添加一個或者多個NOP選項來實現整個TCP頭長的四字節對齊(還記得咱們以前說過TCP頭中的Header Length字段的單位是32-bit word,所以TCP的頭長必定是4bytes的整數倍)。可是linux在接收數據包的時候支持解析EOL選項。另外協議雖然沒有限制TCP選項的排列順序,可是linux實現上會按照必定的順序排列TCP選項。緣由是雖然協議沒有限定options的順序,可是互聯網上有些設備對這個順序是比較敏感的,一些特定的options順序可能會引發問題。


3、MSS

Maximum segment size(MSS)格式以下


        +--------+--------+---------+--------+
        |00000010|00000100|   max seg size   |
        +--------+--------+---------+--------+
         Kind=2   Length=4


      Maximum segment size(MSS)是TCP指望從對端接收的最大的報文長度,天然也是對端在發送報文的時候的最大報文長度,注意MSS值僅指示TCP數據長度,並不包含關聯的TCP頭和IP頭的長度。當鏈接在創建的時候,每一個endpoint一般會在對應的SYN包中經過MSS option通告對方本身的MSS,按照RFC1122規定若是沒有MSS選項提供則會使用默認的536bytes做爲MSS(注意原始的RFC793協議是說沒有提供MSS選項的時候能夠發送任意大小的包,RFC1122修正了該說法)。還有一點須要注意因爲目前網卡廣泛支持TSO、GSO功能,在開啓這些功能的前提下,協議棧中的TCP層可能會按照MSS的整數倍發包,而後再由網卡硬件來對TCP分段,這樣減輕了CPU的處理壓力。後面爲了方便討論窗口管理等特性,咱們仍是按照TCP層最大包不超過MSS來討論。

       在IPV6的jumbogram中(RFC2675),若是接收端接收到的MSS值爲65535時候,標識真實的MSS須要根據PMTU值來肯定。即MSS=PMTU-60。(jumbogram是IPV6中一種發送超大IP報文的協議特性,PMTU是接收端和發送端鏈路之間全部設備的最小MTU。)

        RFC6691從新澄清了MSS選項的相關說明,並修正了以前幾個RFC的錯誤說法。RFC6691明確規定在MSS選項中傳遞的MSS值爲MTU減去IP基本頭(ipv4爲20bytes,IPV6爲40bytes)和TCP基本頭(20bytes)的值,不考慮擴展頭。發送端負責發送數據前在這個MSS值的基礎上扣除擴展頭長度得出真實傳輸數據的長度。

4、WSOPT

WSOPT格式以下

            +---------+---------+---------+
            | Kind=3  |Length=3 |shift.cnt|
            +---------+---------+---------+

        RFC1323爲長肥管道提供了兩個高性能擴展,一個是WSOPT選項另一個是TSOPT選項。長肥管道是指帶寬時延積很大的網絡。

        咱們在介紹TCP頭結構的時候提到過Window Size字段,這個字段佔16位,最大爲2^16-1,在長肥管道中,當發送端TCP須要通告更大的接收窗口的時候,就須要經過WSOPT選項了。當使用WSOPT選項的時候,接收窗口的實際大小則爲Window Size<<shift.cnt,其中shift.cnt按照協議最大隻能爲14,當接收端接收到的shift.cnt大於14的時候,則按照14來處理Window Size

        WSOPT選項只能在SYN包中發送,所以當TCP鏈接創建起來後,window scale就固定了。通常在TCP實現上會有一個最大接收緩存,進而決定了最大接收窗口和window scale。WSOPT選項將原有的16位Window Size擴展到近30位大小(大約1GB)能夠有效提高TCP容許使用的接收緩存,進而提高長肥網絡的性能。

        若是要使能window scale,須要發送端在SYN包中發送WSOPT選項,接收端在SYN-ACK包中一樣發送WSOPT。注意協商window scale過程當中協議要求不能對SYN和SYN-ACK報文頭中的window size應用WSopt選項。WSOPT中的shift.cnt能夠爲0,標識window scale factor爲1(即2^0=1),即接收窗口的實際大小即爲Window Size。若是發送端發送了WSOPT選項可是沒有收到對端的WSOPT選項,則須要將本身的window scale factor設置爲1。

        發送端和接收端都各有一個接收窗口和一個發送窗口,所以總共四個窗口,共維護4個scale factor。假設發送端接收窗口的scale factor爲R,發送窗口的scale factor爲S,則對應的接收端的接收窗口scale factor爲R,發送窗口scale factor爲S。在協商好兩端的scale factor後,以後接收到的數據包中的Window Size字段自動進行scale factor,發送出去的數據包中的這個字段則爲實際接收窗口右移scale factor後的結果。


5、SACK-Permitted和SACK

SACK-Permitted格式

       Kind: 4
       +---------+---------+
       | Kind=4  | Length=2|
       +---------+---------+

SACK格式

       Kind: 5
       Length: Variable
                         +--------+--------+
                         | Kind=5 | Length |
       +--------+--------+--------+--------+
       |      Left Edge of 1st Block       |
       +--------+--------+--------+--------+
       |      Right Edge of 1st Block      |
       +--------+--------+--------+--------+
       |                                   |
       /            . . .                  /
       |                                   |
       +--------+--------+--------+--------+
       |      Left Edge of nth Block       |
       +--------+--------+--------+--------+
       |      Right Edge of nth Block      |
       +--------+--------+--------+--------+

以前咱們介紹過TCP的滑窗和ACK機制,咱們再來簡單的舉個例子,假設接收端依序接收到系列號爲2100的byte,序列號2100以前的byte都已經按序接收到了,接着由於亂序傳輸或者丟包的緣由,接收端並無接收到系列號爲2101的TCP數據包,而是收到了系列號爲2201的TCP報文而且長度爲100byte。也就是說接收端缺乏了2101-2200byte的數據,咱們稱接收端這種狀況在滑窗上面造成了一個洞(hole)。以下圖紅色部分表示接收端已經接收到的數據。

此時接收端給發送端返回ACK報文的時候,TCP頭中的ack number字段只能填寫2101,還記得咱們以前說過ack number表示接收端指望接收到的下一個byte的系列號吧,它是已經收到的連續報文中的最大序列號加1。注意是連續報文,由於2100和2201之間有洞,所以此時ack number只能是2101,發送端在接收到2101這個ack number後,並不能知道接收端實際上已經接收到了2201-2300byte,於是可能會在重傳2101-2200byte的同時也會重傳2201-2300byte的數據。那麼有了SACK後,接收端就能夠經過SACK來告訴發送端已經接收到了2201-2300byte的數據,這個就是一個SACK塊(SACK block),同時結合ack number,發送端就能夠僅僅只是重傳2101-2200byte的數據,而不須要重傳2201-2300byte的數據了。

        一個endpoint若是在SYN包或者SYN-ACK包中解析處SACK-Permitted選項,那麼就說明對端支持SACK擴展。那麼本端就能夠把收到的不連續報文信息發送給對端來幫助對端高效重傳了。一般SACK-Permitted選項通常是在SYN包中發送,一旦收到對端SACK-Permitted選項後,SACK選項則能夠在任意包中傳輸。Linux中能夠經過/proc/sys/net/ipv4/tcp_sack控制是否使能SACK功能,設置爲1時候使能,設置爲0時候關閉SACK功能。由於SACK選項和TCP重傳以及擁塞控制等等由比較大的關係,後面咱們講到這些的時候再來詳細介紹。

6、TSOPT

TSOPT格式以下

         Kind: 8
         Length: 10 bytes
          +-------+-------+---------------------+---------------------+
          |Kind=8 |  10   |   TS Value (TSval)  |TS Echo Reply (TSecr)|
          +-------+-------+---------------------+---------------------+
              1       1              4                     4

        TSOPT選項也叫作timestamp選項,有時也會寫爲TSopt。如上面介紹WSOPT時候所說,TSOPT也是RFC1323爲了改善長肥管道而提出的一個TCP擴展。當使用這個選項的時候,發送方在TSval處放置一個時間戳,接收方則會把這個時間經過TSecr返回來。由於接收端並不會處理這個TSval而只是直接從TSecr返回來,所以不須要雙方時鐘同步。這個時間戳通常是一個單調增的值,RFC1323建議這個時間戳每秒至少增長1。其中在初始SYN包中由於發送方沒有對方時間戳的信息,所以TSecr會以0填充,TSval則填充本身的時間戳信息。

        在RFC1323中,TSOPT主要有兩個用途一個是RTTM(round-trip time measurement)即根據ACK報文中的這個選項測量往返時延,另一個用途是PAWS(protect against wrapped sequence numbers),即防止同一個鏈接的系列號重疊。另外還有一些其餘的用途,如SYN-cookie、 Eifel Detection Algorithm 等等。關於RTTM咱們留到TCP重傳部分進行介紹,此處咱們簡單介紹一下PAWS。

        PAWS假設接收到的每一個TCP包中的TSval都是隨時間單調增的,基本思想就是若是接收到的一個TCP包中的TSval小於剛剛在這個鏈接上接收到的報文的TSval,則能夠認爲這個報文是一箇舊的重複包而丟掉。實際上接收到的TCP報文的系列號若是落在接收窗口外面就能夠丟棄,可是對於一些高速不穩定網絡,可能會出現一種狀況,就是系列號翻轉後,以前某個無效的重傳包系列號知足條件,落在了接收窗口內,這個時候僅僅依靠系列號就不足以鑑定這個TCP報文的有效性了,結合TSOPT則能夠經過時間戳選項來進一步過濾舊的重複包。

       相似PAWS,實際上時間戳做爲了系列號的一個擴展,在同一個鏈接上單調增。RFC6191進一步利用這個特色,在同一個鏈接的不一樣實例間時間戳單調增的時候,能夠利用這個時間戳區分同一個鏈接的不一樣實例的時候,即便在TIME-WAIT狀態下也容許創建鏈接。

        RFC7323明確在TCP頭中的ACK標誌位有效的時候TSecr字段纔有效,若是ACK標誌位沒有置位的時候,發送端應該把TSecr置爲0。當發送出去的數據包ACK標誌位置位的時候,發送端必須在TSecr中回顯一個最近接收到的TSval。當ACK標誌位沒有置位的時候,接收端必須忽視TSecr字段。

        TCP能夠在初始的SYN包中發送TSopt選項,可是接收端只有在接收到的初始SYN報文中解析到TSopt選項的時候才容許在SYN-ACK報文中發送TSopt選項。一旦TCP通訊的兩端經過SYN報文和SYN-ACK報文協商好TSopt選項後,在這個鏈接隨後的非RST報文中,TSopt選項必須被髮送。一旦接收到一個不帶由TSopt選項的非RST報文的時候,TCP應該靜默的丟棄這個報文(注意是應該should,不是必須must)。TCP不能(must not)由於缺乏預期的TSopt選項而停止一個TCP鏈接。注意這裏是協議的要求,實現上並不必定會靜默的丟棄這個數據包,好比linux在協商好TSopt後,收到沒有TSopt選項的數據也會正常接收,後面文章會有wireshark示例。

        當在三次握手中沒有協商TSopt選項而在隨後的數據傳輸中接收到TSopt選項的時候,TCP必須忽視這個TSopt選項而後正常處理這個TCP報文。在TCP同開的時候若是一個SYN報文包含TSopt選項,另一個SYN報文不包含TSopt選項,那麼兩端均可以在隨後的SYN-ACK報文中發送TSopt選項。

       另外TSopt選項還有兩個重要做用,一個是RACK重傳,另一個是Eifel探測算法,後面的文章咱們會專門進行實例介紹。Linux中/proc/sys/net/ipv4/tcp_timestamps能夠設置是否啓用TSopt選項,這個參數設置爲0的時候TCP鏈接就不會使用TSopt選項。

7、FOC

FOC選項的格式以下

                                   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                   |      Kind     |    Length     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   ~                            Cookie                             ~
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   Kind            1 byte: value = 34
   Length          1 byte: range 6 to 18 (bytes); limited by
                           remaining space in the options field.
                           The number MUST be even.
   Cookie          0, or 4 to 16 bytes (Length - 2)

        Fast Open選項用來請求或者發送一個FOC(Fast Open Cookie),當cookie域爲空的時候,client使用這個選項來從服務器請求一個FOC。當cookie域非空的時候,服務器可以使用這個選項來把cookie傳遞給client,或者client可使用這個選項來執行TFO。

        最小的cookie大小是4byte,雖然圖示中cookie是32位對齊的,但不是強制要求的,當數據包中不帶SYN標誌、Length值無效或者TFO功能沒有打開的時候,須要忽略這個選項。

8、wireshark抓包示例

咱們看一下以前FastOpen第一次正常鏈接SYN包中請求FOC時候對應的tcp選項截圖以下,限於篇幅再也不逐步講解,如今咱們講解了TCP選項,建議下載wireshark文件,對照本節對TCP選項的講解在看一遍wireshark中的TCP選項。


補充說明

一、linux支持的tcp選項能夠參考TCPOPT_NOP宏定義附近定義的選項,寫入tcp選項能夠參考代碼tcp_options_write,本文中wireshark中TCP選項的特定順序也是在tcp_options_write寫入的。

二、linux對於MSS的處理能夠參考tcp_current_mss

三、第二版TCPIP詳解P609中對於TSOPT的第二個字段描述爲Timestamp Echo Retry,實際應該是Timestamp Echo Reply







相關文章
相關標籤/搜索