TCP-MSS, PMTU 詳解- MTU工具解析與常見問題彙總-下篇

家好

在《MTU工具解析與常見問題彙總-上篇》中,我們一起討論了MTU的基本定義,以及數據包分片的具體細節,同時也簡單列舉了MTU不匹配導致的問題和對網絡數據傳輸的危害。

 

既然MTU問題多多,那麼儘可能避免由MTU引發的網絡問題將顯得尤爲重要。通常而言,我們可以使用以下方法來檢測和避免MTU問題:

1.      手工測試發現MTU

2.      TCP-MSS

3.      PathMTU Discovery

 

今天我們就來聊聊以上工具的具體細節以及如何通過他們發現鏈路MTU的最佳值,同時奉上兩個典型的由MTU引發的問題案例。

 

什麼是最佳MTU值?

最佳MTU值,是指保證數據包不被分片的情況下,以字節爲基本單位的鏈路單個數據包尺寸的最大臨界值。如果數據包再多一個字節長度,數據包就會被分片。此值稱爲最佳MTU值。

 

MTU下篇:TCP-MSSPath MTU Discovery詳解以及常見問題分析

 

MTU工具測試環境介紹

 

爲了演示常見MTU檢測方法,特搭建網絡測試環境如下:

                            04c4ede08a3cdb7d8e478e20cd5c262e.png

 

上圖網絡拓撲中,存在兩個網絡站點ABA站點和B站點可以通過ISP運營商互聯互通。

A站點出口路由器爲SRX01,出口IP1.1.1.1

B站點出口路由器爲SRX02,出口IP2.2.2.2

 

 

 

站點A和站點B出口路由器的MTU值如下:

A站點的出口MTU值爲巨型MTU9000字節。

B站點的出口MTU 值爲2000字節。

 

A,B站點各有公網服務器一臺:

A站點Server 7 IP3.3.3.3

B站點Server 8 IP4.4.4.4

兩者可以通過ISPBGP路由互聯互通。

 

場景介紹:

此場景存在的問題爲:運營商內核心路由器之間的MTU值未知。但由於網絡通信是一個端到端的整體行爲,若不通過某種方法獲悉運營商之間的鏈路MTU值。那AB之間的通信可能存在數據包分片甚至丟包的行爲。

 

在開始研究如何發現端到端的鏈路最優MTU值之前,讓我們先做一個簡單的Ping聯通性測試以確保SRX01SRX02能夠正常通信。

 

lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 5
PING2.2.2.2 (2.2.2.2): 56 data bytes
!!!!!
--- 2.2.2.2ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-tripmin/avg/max/stddev = 21.491/32.152/37.884/6.149 ms
 
lab@SRX01>
 
lab@SRX01>traceroute 2.2.2.2 source 1.1.1.1
tracerouteto 2.2.2.2 (2.2.2.2) from 1.1.1.1, 30 hops max, 40 byte packets
 1 1.1.1.100 (1.1.1.100)  12.050ms  8.905 ms  14.453 ms
 2 35.0.0.5 (35.0.0.5)  41.774ms  21.306 ms  22.849 ms
 3 2.2.2.2 (2.2.2.2)  42.211 ms  29.103 ms 35.168 ms


Ping包成功,兩端鏈路通信正常。通過Traceroute可以看出穿越的運營商路由器IP地址。

注:上圖中,默認情況下,JuniperPing包負載尺寸爲56字節。

 

服務器連通性測試:

47be7ef26dfabf988d54217014c9b2ec.png

環境確認測試完成,讓我們來逐一介紹MTU的發現方式:

 

1.冷兵器時代:手工測試

最簡單也是最直觀的MTU檢測方式自然是手工測試。

手工測試提供了其他工具無法比擬的優勢:可視性,直觀性。

 

手工測試示例

讓我們來看看如何通過手工測試法發現鏈路的MTU值。

 

lab@SRX01> ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 8000   
PING 2.2.2.2 (2.2.2.2): 8000 data bytes
36 bytes from 1.1.1.100: frag needed and DF set (MTU 1500)
Vr HL TOS  Len   ID Flg off TTL Pro  cks      Src     Dst
 4  5  001f5c c999   2 0000  40  014c02 1.1.1.1  2.2.2.2
.
--- 2.2.2.2 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss


 

針對上面的Ping測試,有幾點需要說明:

1.      Do-not-fragment關鍵字很重要,在《MTU共計解析與常見問題彙總-上篇》中曾經提到過,發送方可以自主確定數據包是否需要被分片。而決定的方法爲設置DF=1,告知下游路由器請不要對數據包進行分片。在此測試案例中,我指定了do-not-fragment。顧名思義,SRX01會根據我的要求把ICMP包的DF設置爲1

 

2.      Size 8000是告知SRX01請產生一個8000字節的載荷並封裝到ICMP中。爲什麼要用8000,是因爲SRX01的本地鏈路MTU最大值爲9000字節(包含IP包頭+ICMP包頭),所以我需要隨機選擇一個比9000稍小的數。

 

在上面測試中,當我用8k字節ping一個數據包的時候,ping測試失敗。從而證明中間鏈路的MTU值是小於8000字節的。

 

同時,在Ping 2.2.2.2 的過程中,SRX01收到一個錯誤信息。

36 bytes from 1.1.1.100: frag neededand DF set (MTU 1500)

有一條從1.1.1.100發過來的消息,內容大致如下:

你的數據包太大。要想往前走,數據包需要分片,可是DF=1,我沒法給你分。(順便悄悄告訴你,前方MTU值爲1500。)

 

難道ISP運營商內部還有咱的人,還給透露小道消息,在這裏先不管這高人是誰,後續聊到Path MTU Discovery時,將通過抓包揭曉謎底。

我們就只需要知道有高人指點,前方ISO網絡層數據包MTU大小最大爲1500字節。

 

抱着半信半疑的態度,我們再Ping測試一次。

但是在開始測試之前,先打一個小算盤:

網絡層1500字節=20字節IP+8字節ICMP+1472字節的載荷。

 

按照上面的推理,我猜如果Ping數據包載荷size=1472字節的話,就會成功。而如果稍稍多一點,例如1473字節Ping就會失敗。

 

通過實驗證明:

Ping Size=1472字節的包:

lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 1472  
PING2.2.2.2 (2.2.2.2): 1472 data bytes
!
--- 2.2.2.2ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-tripmin/avg/max/stddev = 28.471/28.471/28.471/0.000 ms


 

Ping Size=1473字節的包:

lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 1473   
PING2.2.2.2 (2.2.2.2): 1473 data bytes
36 bytesfrom 1.1.1.100: frag needed and DF set (MTU 1500)
Vr HLTOS  Len  ID Flg  off TTL Pro  cks     Src      Dst
 4 5  00 05dd d60b   2 0000 40  01 590f 1.1.1.1  2.2.2.2
.
--- 2.2.2.2ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss


 

驗證成功。我們現在可以100%確定,此神祕人物透露的1500字節的MTU值是絕對可信的。同時也獲悉端到端鏈路ISO第三層的最佳MTU值爲1500字節。

 

以上就是手工測試法,簡單明瞭。

 

但是有一個問題?

 

作爲管理員,我們通過測試明確了端到端MTU的實際值。可是我們如何也讓網絡中的應用程序也知曉這1500字節的MTU值呢?


讓我們繼續往下走。

 

2.小米加×××:TCP-MSS協商

TCP-MSS,全稱TCP maximum segment size。翻譯過來是TCP最大報文尺寸。它的值代表TCP傳輸層期望對端發送給自己單個TCP報文的最大尺寸

通俗來說,當TCP協議兩端在初始協商進行TCP三次握手協議的時候,主機兩端會把自己當前所在鏈路的MSS值告知對方。當一端主機收到另外一端的MSS值候,它會評估其MSS值並與自己的MSS值做對比,取最小的值來決定TCP發送的最大報文尺寸。

如何計算本地MSS值?

本地MSS=MTU-20字節的標準IP-20字節的標準TCP頭(換個角度看其實就是TCP負載)

 

TCP-MSS如何在避免數據包分片中,發揮它的作用呢?

 

讓我們來看一個實際示例:

 

還記得在測試環境中介紹的兩臺服務器Server 7 Server 8嗎?

3885991993470db1ec723b908cc2e132.png

 

現在兩臺服務器要搞事情了。

 

如上圖,服務器Server 7 要往Server 8 SCP的方式傳送一個40M的文件。Server7 MTU 9000字節,Server8 MTU 2000 字節。


TCP行爲理論分析:


TCP協商期間,Server 7Server 8之間會把自己當前的MSS值告知對方,通過對比本地MSS與對方的MSS值後,取兩者最小值,如下:

Server 7MSS值爲9000-40=8960字節。

Server 8 MSS值爲2000-40=1960字節。


很明顯,Server 7Server8 會達成協議,兩者中取Server 8MSS值,即1960字節。這個值將是他們的TCP數據傳輸過程中單個TCP數據包載荷的最大值。

 

讓我們通過實驗抓包來驗證理論分析:

77a1f38ef1e9f8b641e2a6487ce0626e.png

如上圖,正如理論分析。在Server 73.3.3.3)與Server84.4.4.4TCP三次握手期間,Server7發送了8960字節的MSS,而Server8發送了1960字節的MSS

 

爲了證明Server7 選擇了較低的1960字節的MSS,讓我們看看數據包傳輸過程中的TCP數據包大小。

af2cf1b2ee9380a6cddae4df0f3fe96a.png

因爲TCP存在窗口流控機制,TCP速率不會從一開始就達到最大值,而是一個循序漸進增長的過程,當然最後還是如上圖紅框所示達到了TCP的單包最大值2014字節,即TCP-MSS值。


這裏的2014爲整個數據包大小,分拆後如下:

2014字節=14字節二層幀頭+20字節IP+20字節標準TCP+12字節TCP可選項+1948的負載。


此處的TCP-MSS仍然爲1960字節,只是被拆分成爲了12字節的TCP可選項+1948的負載。


(記住MSS計算是以標準的20 字節TCP頭爲參考,在計算時拖掛的TCP選項被放在負載之內考慮)


通過抓包證明,Server7 的確使用了Server 8彙報的1960字節的TCP-MSS爲最大報文尺寸,而不是自身的8960字節。從而證明我們的理論分析完全正確。


但是,讓我們換個視角看看這些數據包在運營商網絡裏面是什麼樣:

aef3305e47482fa64c9091f18da28536.png

上圖爲運營商路由器SRX03 ge-0/0/0接口抓包,大家是否已經看見了每個數據包都帶了一個分片包的尾巴,以及大量刺眼的TCP Ack數據包。

 

一個完整的2014字節數據包就這樣被一分爲二:1514字節數據包+534字節數據包

 

 爲什麼數據包在運營商網絡會被分片?

大家是否還記得。在第一步中,我們通過人工方式知曉了運營商網絡的ISO第三層網絡層MTU值爲1500字節。所以很明顯,2014字節的數據包超過了15141500網絡層+14字節鏈路層二層幀頭)的大小,從而數據包被切割分片。

 

 

解決方案:攔截TCP-MSS

由於管理員通過手工測試獲悉了整條鏈路的TCP-MSS值。如果我們能夠配置站點ABSRX路由器,攔截並修改Server7 Server8之間的TCP-MSS協商爲手工測試的TCP-MSS值,變相告知兩臺服務器正確的端到端TCP-MSS。那豈不避免了服務器上的TCP應用不至於觸及到運營商網絡的MTU天花板,從而避免被分片的命運?


答案是Yes


SRX01以及SRX02作如下配置:

set security flow tcp-mss all-tcp mss 1460

完成配置以後,在站點ASRX01面向運營商接口的ge-0/0/1抓包如下:

 

bc9df3156707247889cd5912d342ff40.png

如上圖,當Server 7Server8 在協商TCP-MSS時,無論是Server7 Server8發送的TCP-MSS均爲1460字節。

 

f0bedc966221bddd106bb5c741d1753f.png

 

同時,通過查看TCP數據流,我們可以很明顯的看出修改前後的區別,在上圖中TCP完成窗口初始化後,直接以1514字節的整包傳輸,而不是先嚐試2014字節。

 

TCP-MSS實驗小結:

·        通過修改TCP-MSS的確能夠有效的防止TCP流量觸及天花板,進而優化TCP的傳輸性能。

·        但是TCP-MSS有一個弊端,那就是需要人工先提前檢測出整個鏈路的MTU值,並在出口路由器上全局干預站點的TCP-MSS協商。

 

你說要是這MTU能夠自動發現,完全不需要人爲干預多好啊!

 

方法是有的,請接着看。

 

3.鳥槍換炮:Path MTU Discovery(鏈路MTU自動發現協議)

 

Path MTUDiscoveryMTU自動發現!

這工具一聽就覺得高大上,但是作爲資深網工,我們不光要知道 Path MTU Discovery(以下簡稱PMTU是什麼,更需要清楚PMTU是如何高大上,並自動發現MTU最優值的?

 

先上理論:

顧名思義,PMTU自動發現就是賦予應用程序權力讓其自主發現端到端鏈路的MTU值,並繞開數據包分片這個坑。

目前支持PMTU的協議爲TCP以及UDP

 

MTU發現機制非常巧妙和簡單,用一句話形容就是:不怕犯錯敢於嘗試,最後總會得出正確值。

 

方法如下:

當主機發送TCP/UDP數據包時,主機會在每一個發送的數據包上打上DF=1的標誌,當這些數據包經過下游鏈路到達目的地之前,由於DF1的原因數據包永遠不會被中間節點分片。


自然而然,數據包的結局只可能有兩種:被中間路由器丟棄或者順利到達目的地。

 

同時,主機在發送數據包給下游目的地時,它並不知曉整個網絡鏈路上的最小MTU值。介於此,主機在初期發送數據包時,數據包尺寸爲其所在鏈路的MTU值。

 

把上面兩個條件組合起來後,主機發送的數據包就會出現如下結局:

 

1.      如果主機本地鏈路的MTU大於端到端鏈路中某一點的MTU值,那麼這個數據包因爲有DF=1的原因,會被丟棄。

2.      如果路由器本地鏈路的MTU爲整個端到端鏈路中最小值時,數據包很幸運的被送達目的地。

 

對於結局2,皆大歡喜。


而我們需要重點說說結局1。在結局1中,當鏈路中路由器丟棄此數據包時,此路由器會返回一個ICMP Destination UnreachableFragment Needed(目標不可達,需要分片)的消息給源主機。


對源主機而言,相比知曉目標不可達而言。它更需要另外一個消息:

這個ICMP包中另有玄機,它同時也包含了此鏈路路由器的下一跳MTU值!


回到本文章第一步手工測試MTU那一段,我曾提到了一個運營商內部人士,他悄悄告知了我們ISP運營商正確的MTU值是多少。而這個高人正是丟包路由器的ICMP 目標不可達消息我們可以把這個ICMP數據包理解爲一個信使,通過告知源主機正確MTU值後,源主機可以調節字節的發送數據包尺寸,從而避免數據過大被丟棄。

 

演示案例:

還是以TCP-MSS中的Server7Server8通信爲例,服務器Server 7 要往Server 8 SCP的方式傳送一個40M的文件。Server7 MTU9000字節,Server8 MTU 2000 字節。

6a926b068e6c03aed98f5dba91f028db.png

不過與TCP-MSS情況不同,在此案例中我們在Server7Server8上開啓了PMTU

 

同時關閉站點AB出口路由器SRX01 SRX02TCP-MSS攔截:

lab@SRX01#delete security flow tcp-mss
 
[edit]
lab@SRX01#show | compare
[editsecurity]
-   flow {
-       tcp-mss {
-           all-tcp {
-               mss 1460;
-           }
-       }
-   }


 

d531d84bc4b528a480d1e61d84c14bab.png

通過上圖,Server7 Server8恢復到了8960字節和1960字節的TCP-MSS

 

重點在下圖:

大家可以看見,根據TCP-MSS協商原則,Server7最終選擇了較低的2014字節,但是這個值還是大於了運營商鏈路中的MTU=1514


正如之前所說,IP1.1.1.100ISP運營商SRX03)的路由器針對每一個丟棄的數據包回覆了一個ICMP不可達消息。而更有意思的是,在這個ICMP包裏面攜帶了下一跳MTU=1500的驚天大祕密。

35ddfb5473456376ad979063d19cf9ef.png

而作爲源的Server7也不簡單,當它收到這個小道消息後,馬上修改了自己的數據包發送尺寸。從2014立即改爲1514(底部紅色字體數據包串),並使用此尺寸來發送後續的TCP重傳數據包。

 

PMTU小結:

以上就是一個典型的PMTU行爲案例。從中可以看出,PMTU不需要人爲干預其具體MTU值,PMTU會通過試錯的方式來發現最佳MTU值。

當然鏈路中可能存在各式各樣的MTU值,而PMTU則以不變應萬變。只要把上述過程在不同鏈路中重複一次或多次就一定會最終發現一個端到端的最優MTU值。

 

PMTU有什麼缺陷?

你或許已經猜到了,它太依賴ICMP消息。

 

常見MTU典型故障總結以及案例分析

 

1.防火牆做的好事兒,ICMP被幹掉了。

 

Path MTU Discovery中,我們發現PMTU工作的核心機制在於主機能夠收到中間某個節點發過來的ICMP不可達數據包,幷包含了下一跳MTU值。

 

那假設我們在上述拓撲中,把站點ASRX01入站口放置ACL阻止ICMP從運營商進入站點A

雖然理論上給站點A帶來了額外的安全保障,但是副作用就是PMTU被幹掉了。

因爲缺失ICMP消息,PMTU不再生效。

 

解決方法:

日常工作中,大家可以考慮在邊界防火牆上放行ICMP協議,或者至少放行ICMP unreachableICMP Type 3 Code 4)消息。

 

2.夾心餅乾,三層設備之間夾雜二層設備。

 

在一般網絡拓撲環境中,往往在三層路由器設備之間會通過二層交換機匯聚互聯。

但是在某些情況下,因爲設計原因或者人爲失誤。導致二層交換機的MTU值小於三層路由器MTU值,從而導致網絡故障。

可能有人會問,那MTU小了,分片就可以了,也不至於產生網絡故障。

但是仔細考慮發現,數據包分片只存在於ISO第三層的網絡設備上,而二層節點只查看二層幀,其並沒有重寫三層IP頭的能力,更不知曉三層IP層的具體網絡細節。

當數據包過大以後,二層設備是不會也不能去分片數據包,因爲在配置的時候,沒有賦予它任何此二層VLANIP信息,導致其沒有寫IP數據包的能力去執行數據包分片。

 

讓我們來看一個詭異的示例:

eca2e94f436756cd2be51dec73e1764c.png

如上圖,SRX3SRX5之間配置了9000字節的MTU,而SRX03SRX05則通過SRX04的二層設備互聯。

在二層SRX04設備上,所有ISO二層幀MTU值爲1514

 

由於SRX03SRX05ISP運營商路由器,它們之間運行了BGP路由協議。但是令網絡工程師不解的是,SRX03SRX05BGP鄰居雖然處在UP狀態。但是SRX05沒有收到任何SRX03通告的路由?

 

SRX03 BGP鄰居信息:

root@SRX3> show bgp summary   
Groups: 2 Peers: 2 Down peers: 0
Table          TotPaths  Act Paths Suppressed    History Damp State    Pending
inet.0               10          7          0          0         0          0
Peer                    AS      InPkt     OutPkt   OutQ   Flaps Last Up/DwnState|#Active/Received/Accepted/Damped...
1.1.1.1                101         13         19       0      3        3:53 3/4/4/0              0/0/0/0
35.0.0.5                200          5         34   6904       7          11 4/6/6/0              0/0/0/0


 

#上述輸出中,SRX0535.0.0.5SRX03的鄰居列表中,並且SRX03收到了SRX05宣告的6條路由。如下:

root@SRX3> show route receive-protocol bgp 35.0.0.5
 
inet.0: 10009 destinations, 10013 routes (10009 active, 0holddown, 0 hidden)
  Prefix                  Nexthop              MED     Lclpref   AS path
* 2.2.2.0/24             35.0.0.5                               200 I
* 4.4.4.0/24             35.0.0.5                               200 201 I
* 10.69.109.0/24         35.0.0.5                               200 I
  10.221.22.0/24          35.0.0.5                                200 201 I
  35.0.0.0/24             35.0.0.5                                200 I
* 172.16.1.0/24          35.0.0.5                               200 201 I
 
root@SRX3>


 

但是在SRX05中,卻沒有收到任何SRX03的路由條目:


root@SRX5> show bgp summary   
Groups: 2 Peers: 2 Down peers: 0
Table          TotPaths  Act Paths Suppressed    History Damp State    Pending
inet.0                4          3          0          0          0          0
Peer                    AS      InPkt     OutPkt   OutQ   Flaps Last Up/DwnState|#Active/Received/Accepted/Damped...
2.2.2.2                201      36075      35936       0      3      1w4d6h 3/4/4/0              0/0/0/0
35.0.0.3               100          2          5       0      8           4 0/0/0/0              0/0/0/0


 

更詭異的是,兩者的BGP鄰居總是過一段時間後就閃斷一次。

 

讓我們來通過抓包來揭示謎底:

在二層設備SRX04上抓包如下:

db0af1413e370fdd4963f7f56df4165b.png

很明顯,當SRX0335.0.0.3)與SRX0535.0.0.5)通過TCP建立BGP鄰居時一切正常。數據包大小均不大於1500字節。


但是當BGP開始通過Update消息傳輸路由條目(NLRI)的時候,開始以單包8000字節大小以上數據包傳輸。而中間的SRX04 ISO二層MTU1514字節(三層MTU1500字節),這8000字節的數據包將會被無情的丟棄。而因爲SRX04是二層設備,其將不會返回任何ICMP信息給SRX03。造成SRX03TCP數據包不斷超時並重傳。最終在到達TCP最大重傳次數後,BGP鄰居被刪除。

 

解決方法:

請務必保證路由器之間的二層設備MTU值與三層路由器設備MTU相同。

 

總結:

 

在這片文章中,我們一起分析了常見MTU最優值的發現方法,並簡單列舉了兩個常見MTU問題,知識回顧如下:

1.      手工MTU發現:通過手工ICMPPing測試發現最優MTU值。

2.      TCP-MSSTCP協議的最大傳輸報文尺寸,通過在中間防火牆或者路由器干預端到端的TCP-MSS協商從而達到避免鏈路數據包分片的目的。

3.      Path MTU Discovery:用於TCP/UDP數據包,通過設置初始數據包的DF=1不斷試探最佳MTU值,最終發現端到端的最佳MTU值。

 

當然,在日常工作中,MTU的問題會比我們想象的更爲複雜。

例如嵌套了IPsec ***GRE隧道的端到端流量等。但是萬變不離其中,只要大家掌握了MTU和相關工具的工作原理和細節。我們就可以從容分析各種場景並使用對應的工具來解決實際問題。

 

謝謝!

 

 

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章