這些網(wǎng)絡(luò)知識(shí)你清楚嗎?一切都要從MTU 和 MSS 說(shuō)起
共 11063字,需瀏覽 23分鐘
·
2024-04-18 08:33
推薦一個(gè)原創(chuàng)技術(shù)號(hào)-非科班大廠碼農(nóng),號(hào)主是機(jī)械專業(yè)轉(zhuǎn)行進(jìn)入騰訊的后端程序員!
MTU
-
為什么需要 MTU? -
為什么是我看到過(guò)的 MTU 都是 1500? -
如果傳輸?shù)臄?shù)據(jù)(在二層我們叫做 Frame)超過(guò)了 MTU 會(huì)發(fā)生什么? -
那什么時(shí)候發(fā)送的數(shù)據(jù)會(huì)超過(guò) MTU?
下面就以上幾個(gè)疑惑一一進(jìn)行解釋,我們從最簡(jiǎn)單的說(shuō)起……
為什么需要 MTU,MTU大小為什么是1500
MTU 的存在很合理,F(xiàn)rame 不可能無(wú)限大,發(fā)送小的數(shù)據(jù)是可以的。所以就設(shè)定了一個(gè)最大值。我們?cè)诰W(wǎng)卡上看到的 MTU 一般都是 1500bytes,要注意這個(gè)值指的是 Frame 內(nèi)容的最大值,并不包括 Ethernet Frame 的 header 和 FCS。一個(gè) Ether Frame 最大是 MTU + Header 14bytes + FCS 4 bytes = 1518 bytes.
Larger MTU is associated with reduced overhead. Smaller MTU values can reduce network delay.
更大的包意味著出錯(cuò)的概率更大,所以會(huì)增加重傳的比例;
重傳的代價(jià)也更大,一大段數(shù)據(jù)里面如果有一個(gè) bit 出錯(cuò)了,這一大段就會(huì)整個(gè)重傳;
-
以太網(wǎng)是分組交換網(wǎng)絡(luò),即存儲(chǔ),轉(zhuǎn)發(fā),在轉(zhuǎn)發(fā)給下一跳之前,路由設(shè)備或者交換機(jī)要存儲(chǔ)還沒(méi)發(fā)完的數(shù)據(jù),更大的 MTU 就對(duì)設(shè)備的性能有更高的要求,意味著更高的成本;
綜上,MTU既不能設(shè)置的過(guò)大,也不能設(shè)置的過(guò)小,因此1500 其實(shí)是一個(gè) Trade Off,這就是為什么一般 MTU 都是 1500。其實(shí),不同的 2 層協(xié)議有不同的 MTU:
這就有了下一個(gè)問(wèn)題:那如果超過(guò)了這個(gè)大小呢?
超過(guò) MTU 的 Frame 會(huì)發(fā)生什么?
既然上文說(shuō)到基本上所有的設(shè)備設(shè)置的 MTU 都是 1500,那么為什么還會(huì)出現(xiàn)超過(guò) 1500 的 MTU 呢?
什么時(shí)候發(fā)送的數(shù)據(jù)會(huì)超過(guò) MTU?
VXLan 包結(jié)構(gòu)
這樣,假設(shè)我們?cè)瓉?lái)的 Ethernet Frame 里面的數(shù)據(jù)是 1500 bytes,經(jīng)過(guò) VXLan 包裝之后,就變成了:1500 + 14(原來(lái)的 Ethernet Frame header) + 8(VXLan header) + 8(UDP Header) + 20 (IP Header) = 1550 bytes, 超過(guò)了 50 bytes. (原來(lái)的 Frame 里的 FCS 不在里面,因?yàn)榫W(wǎng)絡(luò)處理過(guò)了。)
它是原來(lái)的 Ether II frame 變成了 UDP 的數(shù)據(jù),被包起來(lái)了,又封裝成 IP,Ether 發(fā)出去。
超過(guò) MTU 的包大部分網(wǎng)絡(luò)設(shè)備都會(huì)直接丟掉,所以我們就需要保證發(fā)送的數(shù)據(jù)不超過(guò) MTU (上圖是一個(gè)反例)。
如何保證發(fā)送的數(shù)據(jù)不超過(guò) MTU?
很顯然,我們需要分成多份發(fā)送。如果我們要讓 2 層網(wǎng)絡(luò)發(fā)送(意思就是包括 IP header 在內(nèi)一共) 4000 bytes 的數(shù)據(jù),那么就要分成 3 個(gè) Etherframe 來(lái)發(fā)送:第一次發(fā)送 1500 bytes,第二次 1500 bytes,第三次 1000 bytes.
-
網(wǎng)卡驅(qū)動(dòng)知道 2 層的 MTU 是多少; -
3 層協(xié)議棧 IP 會(huì)問(wèn)網(wǎng)卡驅(qū)動(dòng) MTU 是多少; -
4 層協(xié)議 TCP 會(huì)問(wèn) IP Max Datagram Data Size (MDDS) 是多少;
所以 TCP 在握手的時(shí)候,會(huì)把自己的 MSS 告知給對(duì)方。
MSS
MSS通告
在 TCP 的握手階段, SYN 包里面的 TCP option 字段中,會(huì)帶有 MSS,如果不帶的話,default 是 536. 對(duì)方也會(huì)把 MSS 發(fā)送過(guò)來(lái),這時(shí)候兩端會(huì)比較 MSS 值,然后選擇一個(gè)最小的值作為傳輸?shù)?MSS.
實(shí)際應(yīng)用場(chǎng)景是什么?拿上文我們提到的 VXLan 封裝舉例,VXLan 封裝的這一端知道自己需要 50bytes 的 overhead 來(lái)封裝 VXLan,那么它就可以告訴對(duì)方,自己能接受的最大的 MSS 是 1410bytes (1500bytes MTU – 20 IP headers – 20 UDP headers – 50 bytes VLan),對(duì)方發(fā)過(guò)來(lái)的 MSS 是 1460 bytes(1500 bytes – 20 bytes – 20 bytes). 然后兩端都會(huì)用 1410 bytes 作為 TCP MSS 值,即保證發(fā)送的 4層 segment 都不會(huì)超過(guò) 1410 bytes.
這里就有一個(gè)疑問(wèn):為什么 MSS 兩端都使用一個(gè)共同的值,而不是 A -> B 1410 bytes; B -> A 1460 bytes, 這樣不是可以更高效嗎?
這個(gè)問(wèn)題的答案我找了好久,感覺(jué)很多地方說(shuō)法不一,比如這里就說(shuō):
TCP MSS is an option in the TCP header that is used by the two ends of the connection independently to determine the maximum segment size that can be accepted by each host on this connection.
從 10.130.0.6 發(fā)送給 10.130.0.5 最大的包是 800.
從 10.130.0.5 到 10.130.0.6 也是 800.
MSS 設(shè)置的方法
#1、iptables
iptables -I OUTPUT -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 48
#2、ip route
ip route change 192.168.11.0/24 dev ens33 proto kernel scope link src 192.168.11.111 metric 100 advmss 48
#3、程序可以自己設(shè)置,本質(zhì)上是自己往 TCP option 里寫 MSS:
from scapy.all import *
ip = IP(dst="192.168.11.112")
tcp = TCP(dport=80, flags="S",options=[('MSS',48),('SAckOK', '')])
ifconfig eth0 mtu 800 up. 這樣 Kernel 的 TCP 棧在建立連接的時(shí)候會(huì)自動(dòng)計(jì)算 MSS。
我們這里說(shuō)的都是 TCP 兩端的設(shè)備如果清楚自己的網(wǎng)絡(luò)情況的話,可以進(jìn)行的一些設(shè)置。還有一些情況,比如說(shuō)一些 VPN 和 overlay,兩端對(duì)此并不知曉,完全是中間的路由設(shè)備做的。中間設(shè)備需要預(yù)留 50bytes,有什么方法可以讓兩邊都知道,發(fā)送的數(shù)據(jù)包要預(yù)留 50bytes 呢?
MSS Clamping
我們?cè)谝粋€(gè) VPN 環(huán)境中測(cè)試一下,網(wǎng)絡(luò)結(jié)構(gòu)可以簡(jiǎn)單地理解為 [Client -> VPN] -> Server.
Client 端對(duì) TCP SYN 抓包
Server 端對(duì) TCP SYN 抓包
其實(shí),每一層協(xié)議自己都會(huì)有機(jī)制,讓自己發(fā)送的內(nèi)容不超過(guò)下層協(xié)議能承載的最大內(nèi)容(最大 PDU)。但是畢竟我們不處于一個(gè)完美的世界……
不完美的網(wǎng)絡(luò)環(huán)境
我們從下往上講起。
Layer 2
二層協(xié)議一般都很簡(jiǎn)單,如果收到了超過(guò) MTU 的包,一般會(huì)簡(jiǎn)單地 drop 掉,要依靠上層協(xié)議來(lái)保證發(fā)送的數(shù)據(jù)不超過(guò) MTU。
但是也有協(xié)議可以支持拆分(Fragment),比如二層的 MLPPP
Layer 3
IP 層的處理就比較經(jīng)典了,自己收到的是上層協(xié)議發(fā)給它的內(nèi)容,然后要負(fù)責(zé)通過(guò) 2 層來(lái)發(fā)送出去,上層的內(nèi)容是無(wú)法控制的,但是要控制自己發(fā)送到下層的內(nèi)容。
所以 IP 支持一個(gè) feature 叫做 IP Fragmentation.
如果 IP Packet 超過(guò)了 1500bytes,IP 協(xié)議會(huì)將這個(gè) packet 的 data 段拆分成多個(gè),每一個(gè)都加上 IP header,以及 fragment header 標(biāo)志這是拆分成的第幾段。接收端等收到所有的 IP 分片之后,再組裝成完整的數(shù)據(jù)。
我們可以通過(guò) ping 來(lái)發(fā)送一個(gè)超過(guò) MTU 1500 bytes 的數(shù)據(jù)。ping -s 2000 -I 172.16.42.21 172.16.42.22
抓包如下:
由此,可以發(fā)現(xiàn) IP Fragmentation 其實(shí)是把上層的數(shù)據(jù)拆分到多個(gè) IP 包里面,不管上層的數(shù)據(jù)是什么。說(shuō)白了,第一個(gè) frame 有 ICMP 的 header,第二個(gè) ICMP 包沒(méi)有。如果把承載 ICMP 協(xié)議換成 TCP 協(xié)議,我們就可以發(fā)現(xiàn)問(wèn)題了:收到了 IP framented frame,是無(wú)法處理的,因?yàn)檫@個(gè) IP 包的數(shù)據(jù)對(duì)于上層協(xié)議來(lái)說(shuō)是不完整的,假設(shè)一個(gè) IP 包被 fragment 成了 3 個(gè) IP 包,我們就必須等到 3 個(gè) IP 包全部到齊才可以處理。
所以說(shuō):IP Fragmentation is generally a BAD thing.
可能導(dǎo)致的問(wèn)題有:
-
同上面提到的 MTU 為什么是 1500 一樣的問(wèn)題:假設(shè)拆分成了 3 個(gè)包,丟了一個(gè)包就相當(dāng)于全丟了,丟包率直接變成(假設(shè)丟包率是 10%,那么3個(gè)包都不丟的概率就是 90%^3=72.9%)27%; -
導(dǎo)致 TCP 亂序:現(xiàn)在網(wǎng)絡(luò)很多設(shè)備都是針對(duì) TCP 做優(yōu)化的,比如,根據(jù) TCP 的 port number 去 hash 到同一條路由上去,減少 TCP reorder 的概率。但是如果 IP fragmentation 發(fā)生的話,后續(xù)的 IP 包在路由器看來(lái)并不是 TCP 包,因?yàn)?TCP header 只在第一個(gè) fragment 上才有,所以會(huì)導(dǎo)致 hash 失效,從而更容易發(fā)生 TCP 亂序;另外,對(duì)段會(huì)等齊所有的 fragment 到達(dá)才會(huì)交給上層,這也導(dǎo)致了延遲增加和亂序的發(fā)生; -
產(chǎn)生一些比較難 debug 的問(wèn)題; 不是所有系統(tǒng)都能處理 IP Fragmentation,比如 Google GCE;
所以,在現(xiàn)實(shí)的世界中,我們幾乎看不到 IP Fragmentation 的,要依靠上層協(xié)議保證傳給 IP 層的數(shù)據(jù)大小不需要 fragment.
Layer 4
上文已經(jīng)提到了 MSS。但是我們平時(shí)寫應(yīng)用程序的時(shí)候,從沒(méi)有自己分過(guò) Segment,這是因?yàn)?TCP 是面向數(shù)據(jù)流的,你有一個(gè) socket 之后,盡管向里面寫就可以了,Kernel 的協(xié)議棧會(huì)負(fù)責(zé)給你將數(shù)據(jù)拆成正好能放到 IP 包里的大小發(fā)出去。注意這里是拆成多個(gè) TCP segment 發(fā)送,在 IP 層并沒(méi)有拆開,每一個(gè) IP 包里面都有 TCP 的 header。
DF(Don’t fragment bit)
IP 協(xié)議的 header 中有一位 bit 叫做 DF,如果這個(gè) bit 設(shè)置了,就是告訴中間的路由設(shè)備不要分片發(fā)送這個(gè)包,如果大于最大傳輸單元的大小,直接丟棄即可。丟棄這個(gè)包的設(shè)備會(huì)發(fā)回一個(gè) ICMP 包,其中,type=3 Destination Unreachable, Code=4 Fragmentation required, and DF flag set. RFC 1191
tcpdump -s0 -p -ni eth0 'icmp and icmp[0] == 3 and icmp[1] == 4'
DF=1,遇到 MTU 太大丟包發(fā)回來(lái)的是 ICMPv6(Of course!)
道理我都懂,但是我的抓的包怎么大?
正常情況下,jacson在序列化時(shí),json串中的字段名稱和類中屬性的名稱是一樣如果你通過(guò)抓包去看一下 MSS 是否是有效的,里面每一個(gè)包的大小是否最大是 1500 bytes,你會(huì)懷疑人生。
明明協(xié)商的 MSS 1460,但是后面的數(shù)據(jù)居然有 1萬(wàn)多bytes的??在接收端抓包也一樣。
這個(gè)叫 TSO,TCP Segment Offload.
但是對(duì)于抓包來(lái)說(shuō),我們看到的就是 Kernel 發(fā)送了大包,因?yàn)樽グ^(guò)程是看不到后面網(wǎng)卡具體做了什么的。
如果我們關(guān)閉 TSO 功能: ethtool -K eth0 tx off。然后再抓包,你就會(huì)發(fā)現(xiàn)抓到的每一個(gè)發(fā)送的包都是 1500 bytes 了。
但是即使你按照 1500bytes 發(fā)送,然后這時(shí)候去接收端抓包,會(huì)發(fā)現(xiàn)還是有大包。發(fā)送端發(fā)送的都是小包,為啥到接收端就成了大包呢?顯然網(wǎng)卡可以對(duì)發(fā)送做 offload,也可以對(duì)接收做 offload,網(wǎng)卡會(huì)攢一些 TCP 包,然后合起來(lái)發(fā)送給 Kernel 的協(xié)議棧。
第二個(gè)是工作中遇到的問(wèn)題,也是我看這些東西的起因。
我們的 SDN 網(wǎng)絡(luò)有一種這樣的路由:
前面提到 IP Fragment 有很多安全問(wèn)題,這里列舉了其中一些:
-
IP fragment overlapped:攻擊者精心設(shè)計(jì)了很多 IP 分片,它們互相重疊,理論上這種包是無(wú)法在網(wǎng)絡(luò)上出現(xiàn)的。如果服務(wù)器收到這些分片,可能無(wú)法正確處理(IP 實(shí)現(xiàn)的 Bug),那么可能會(huì)崩潰; -
IP fragment overrun:攻擊者通過(guò) IP 分片的方式,發(fā)送的 IP 包組裝之后超過(guò)了 65535,可能造成服務(wù)器崩潰(溢出); -
IP fragmentation buffer full:攻擊者一直發(fā)送 IP 分片,more-fragments 一直設(shè)置為 true,導(dǎo)致服務(wù)器收到 IP 包的時(shí)候,只能存儲(chǔ)在 buffer 中試圖將它們組裝起來(lái),直到內(nèi)存耗盡 (DDoS); -
其他構(gòu)造的無(wú)法正確組裝的 IP 包??赡軐?dǎo)致 DDoS,或者可能導(dǎo)致 IDS(入侵檢測(cè)系統(tǒng))無(wú)法正確組裝并識(shí)別這些包,導(dǎo)致這些包繞過(guò)安全系統(tǒng)進(jìn)入了服務(wù)器,最終構(gòu)造出攻擊。參考 Rose Fragmentation Attack IP fragment too many packets
IP fragment incomplete packet
IP Fragment Too Small
推薦閱讀:
完全整理 | 365篇高質(zhì)技術(shù)文章目錄整理
一張圖弄清楚緩存架構(gòu)設(shè)計(jì)中的經(jīng)典問(wèn)題及解決方案
一張圖總結(jié)系統(tǒng)設(shè)計(jì)中的33個(gè)黃金法則
專注服務(wù)器后臺(tái)技術(shù)棧知識(shí)總結(jié)分享
碼農(nóng)有道,和您聊技術(shù),和您聊職場(chǎng),和您聊互聯(lián)網(wǎng)那些事!
