計算機網路那些事之 MTU 篇 pt.2

2023-06-29 21:00:31

哈嘍大家好,我是鹹魚

《計算機網路那些事之 MTU 篇 》中,鹹魚跟大家介紹了 MTU 是指資料鏈路層能夠傳輸的最巨量資料幀的大小

如果傳送的資料大於 MTU,則就會進行分片操作(Fragment);如果小於 MTU,就會在實際資料內容後面新增填充資料(Padding),使得封包總長度達到最小長度要求

TCP 因為是有連線的協定,連線在建立的時候就有 MSS(TCP 報文段中資料部分的最大長度) 的協商,以便在傳輸過程中進行分片

在網路傳輸的過程中,如果中間裝置(例如路由器)的 MTU 比較小,就會 MSS clamping

MSS clamping 是一種 TCP 優化技術,用於解決 TCP 封包在傳輸過程中可能發生的分片問題

它會檢查 TCP SYN 包中的 MSS 選項,並將其值與網路鏈路的 MTU 進行比較

如果 MSS 值大於 MTU,就將 MSS 值設定為 MTU 減去 IP 頭部和 TCP 頭部的長度,從而保證 TCP 封包的大小不會超過網路鏈路的 MTU,避免發生 IP 分片

在 TCP 連線建立後,雙方會根據 MSS 值來控制每個 TCP 封包的大小,從而保證 TCP 封包不會超過網路鏈路的 MTU,提高網路傳輸的效率

但是對於面向無連線的協定(例如 UDP ),這時候該怎麼辦呢?

我們知道乙太網規定 MTU 範圍上限為 1500 位元組,那麼理論上 UDP 能傳輸的封包大小上限為:

MTU(1500) - IP Header(20) - UDP Header(8) = 1472 位元組

  • 如果 UDP 封包小於等於1472 個位元組,那麼正常傳送不用分片
  • 如果 UDP 封包超過 1472 個位元組,那麼移交網路層進行分片並在接收方進行重組

但我們需要知道的是,UDP 包進行分片的時候不像 TCP 那樣每個分片裡面都複製一個 Header

而是第一個分片有 UDP Header,其餘的都沒有

所以接收方拿到 UDP 分片包之後在傳輸層就得先進行重組成一個完整的 UDP 包然後才能交給上一層

這樣就會導致一個問題:如果在傳輸過程中其中一個 UDP 分片包丟了,造成 UDP 包重組失敗,接收方會把整個包給丟掉

但是又因為 UDP 沒有重傳機制,就會導致 UDP 傳送方不知道接收方丟了這個包

然後傳送方就會一直髮包,接收方一直丟包

所以說最好不要發超過接收方 MTU 大小的 UDP 封包

但面向無連線的 UDP 是怎麼知道對方 MTU 的呢?(TCP 有 MSS 協商)

為此我在虛擬機器器上簡單做了一個實驗

  • 環境
    • CentOS 7
  • 伺服器 A
    • IP:192.168.149.130
    • MTU:800
  • 伺服器 B
    • IP:192.168.149.131
    • MTU:1500

然後用 B 伺服器去 ping 伺服器 A,傳送一個大小為 1500 bytes 的資料

# -M do 表示不分片
# -p 選項用於指定封包的內容
# -c 2 指定傳送封包的數量為2
# -s 指定封包的大小
ping -c 2 -s 1000 -M do -p $(echo -n abcdefghijklmnopqrstuvwxyz | xxd -p)   192.168.149.130

結果發現伺服器 A 全部接受了,按理說 A 的 MTU 是 800,又因為設定了不分片,應該丟棄這個包才對

然後我們用 B 伺服器去 ping 伺服器 A,傳送一個大小為 1600 bytes 的資料

發現超出伺服器 B 的 MTU (1500)了,又因為不允許分片,導致包不能發出

ping -c 2 -s 1600 -M do -p $(echo -n abcdefghijklmnopqrstuvwxyz | xxd -p)   192.168.149.130

跟我想象的不太一樣,伺服器 A 收到伺服器 B 傳送的超過自身 MTU 大小的包應該是丟棄,然後回覆一個:

Type=3 (Destination Unreachable) and code=4, packet too big and DF is set

表示傳送方封包無法到達目的地且無法分片

後面我查了一下,應該是本地環境即兩個虛擬機器器之間的網路比較簡單,導致網路卡能接受這種不合理的包,如果有懂的小夥伴可以私信我

但是這個實驗至少證明了一點:即伺服器不會去關心對方的 MTU,只會根據自己的 MTU 去進行分片

然後我又查閱了大量的資料,我看到一個比較貼切的答案就是:

我們來看下上面這段內容,可以得出

在 RFC 中,Internet (IPv4) 標準規定 MTU 最小是 576 bytes

所以對於 UDP 來講,能發的有效資料(Payload)只要不超過 508 bytes

576 bytes (MTU) - 60 bytes (IP header 20 bytes + IP option 0-40 bytes) - 8 bytes (UDP Header)= 508 bytes

我管你伺服器的 MTU 是多少,我只傳送全世界最小的二層包,總沒問題了吧?

很多情況下 IP header 是達不到 60 bytes的,所以一般 UDP 的 Payload 也會是是 512 bytes

總結一下:

  • 不同於 TCP ,UDP 這個簡單的協定並不關心對方的 MTU 這些問題;如果你要基於 UDP 實現一個協定,就要自己處理超過 MTU 的問題
  • 為了避免分片問題,UDP 只發不超過 IPV4 MTU 範圍下限(576 bytes)的包(即UDP 封包的最大 Payload 是 512 位元組
  • 上述討論針對的是 IPV4,而非 IPV6

Beej's Guide to Network Concepts

domain name system - Why DNS through UDP has a 512 bytes limit? - Server Fault

MTU 和 UDP (以及基於 UDP 的協定) | 卡瓦邦噶! (kawabangga.com)