1. 這些網(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)入騰訊的后端程序員!


        最近因?yàn)橛龅搅艘粋€(gè)和 MSS 有關(guān)的問(wèn)題,所以花了很多時(shí)間學(xué)習(xí)相關(guān)的東西。過(guò)程中又發(fā)現(xiàn)網(wǎng)上有很多相關(guān)的內(nèi)容是錯(cuò)誤的,或者介紹的東西其實(shí)現(xiàn)實(shí)世界已經(jīng)不用了。(我看到這些資料的時(shí)候有了無(wú)數(shù)的疑問(wèn),然后又花了很長(zhǎng)時(shí)間,這些疑問(wèn)現(xiàn)在基本上都有了一個(gè)合理的解釋。這篇文章就來(lái)總結(jié)一下和 MTU, MSS 有關(guān)的內(nèi)容。有可能也寫不完“一切”,但是預(yù)料到會(huì)寫的很長(zhǎng)。所以你現(xiàn)在去沖一杯咖啡,我們從最簡(jiǎn)單的地方開始。

        MTU

        MTU 指的是二層協(xié)議里面的最大傳輸單元。這是一個(gè)很簡(jiǎn)單的概念。但這是我最初的問(wèn)題來(lái)源,不知道你看到這個(gè)之后是否也會(huì)有以下相同的疑惑:
        • 為什么需要 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.

        那么為什么 MTU 都設(shè)置成 1500 呢?

        可以說(shuō)是歷史原因。維基百科有這么一句話:

        Larger MTU is associated with reduced overhead. Smaller MTU values can reduce network delay.

        第一句話很好理解,更大的MTU,header 占據(jù)整個(gè)包的比例就更小,那么鏈路上更多的資源就花在了數(shù)據(jù)的傳輸上,協(xié)議消耗的 overhead 就會(huì)很小。
        第二句話呢?其意思是,過(guò)大的MTU也會(huì)讓一個(gè)數(shù)據(jù)包占據(jù)鏈路的時(shí)間更長(zhǎng),所以總體上延遲就會(huì)變大。此外:
        • 更大的包意味著出錯(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:

        這里要提一下 Jumbo Frame,可以最大支持 9000 bytes,提高傳輸?shù)乃俾省2贿^(guò)現(xiàn)實(shí)中基本上見(jiàn)不到,Internet 上更見(jiàn)不到。因?yàn)?Ethernet 是 2 層協(xié)議,負(fù)責(zé)點(diǎn)對(duì)點(diǎn)的傳輸,如果因特網(wǎng)上一個(gè) Jombo Frame 要能從用戶傳到另一個(gè)用戶或服務(wù),這需要所有點(diǎn)對(duì)點(diǎn)設(shè)備都要支持才行。而現(xiàn)實(shí)的世界里,基本上網(wǎng)絡(luò)上所有的路由,交換設(shè)備,端設(shè)備,路由器,設(shè)置的 MTU 都是 1500.

        這就有了下一個(gè)問(wèn)題:那如果超過(guò)了這個(gè)大小呢?

        超過(guò) MTU 的 Frame 會(huì)發(fā)生什么?

        Drop. 這是最簡(jiǎn)單的處理方法。也是現(xiàn)實(shí)世界很多軟件,硬件的處理方式。
        但是顯然這取決于軟硬件的實(shí)現(xiàn)方式,比如 Cisco 的交換機(jī)就可以支持一個(gè) Baby Giant feature: 交換機(jī)可以轉(zhuǎn)發(fā)超過(guò) 1500 bytes ,但又不超過(guò)很多的 MTU。有些軟件和設(shè)備支持類似 feature,有些不支持,大部分都會(huì)直接 Drop。

        既然上文說(shuō)到基本上所有的設(shè)備設(shè)置的 MTU 都是 1500,那么為什么還會(huì)出現(xiàn)超過(guò) 1500 的 MTU 呢?

        什么時(shí)候發(fā)送的數(shù)據(jù)會(huì)超過(guò) MTU?

        最常見(jiàn)的是 VPN 和 overlay 網(wǎng)絡(luò)。這種網(wǎng)絡(luò)本質(zhì)上就是將二層包再包一層,在底層互聯(lián)網(wǎng)上建一個(gè)虛擬的二層網(wǎng)絡(luò)。比如說(shuō) VXLan,它會(huì)在原來(lái)的 Ethernet Frame 基礎(chǔ)上加一個(gè) VXLan header,然后變成 UDP 包發(fā)出去。

        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.

        要讓最終傳給 2 層協(xié)議的 Frame 數(shù)據(jù)大小不超過(guò) 1500 bytes,就要保證上層協(xié)議每一層都沒(méi)有超過(guò)這個(gè)大小。
        拿最常用的 4 層協(xié)議 TCP 來(lái)說(shuō),如果 MTU 是 1500,那么 IP 層就要保證 IP 層的 Packet 數(shù)據(jù)不超過(guò) 1480 bytes (1500 bytes – 20 bytes IP header), 對(duì)于 TCP 來(lái)說(shuō),它要保證每一個(gè) Segment 數(shù)據(jù)大小不超過(guò) 1460 bytes (1460 bytes – 20 TCP header).
        那么 TCP 層要怎么知道 2 層的 MTU 是多少呢?
        • 網(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ù)據(jù)大小,就叫做 MSS (Maximum segment size).
        對(duì)于 TCP 來(lái)說(shuō),我知道了自己這邊的 MSS,但是其實(shí)并沒(méi)有什么用,因?yàn)槲易鳛榻?/span>收端,收到的包大小取決于發(fā)送端,得讓發(fā)送端知道自己的 MSS 才行。

        所以 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.

        但是很多地方也說(shuō)兩邊的 MSS 會(huì)一樣。
        這個(gè)我自己測(cè)試了一下,手動(dòng)調(diào)整一端的 MTU,另一不調(diào)整,發(fā)現(xiàn)兩端發(fā)送數(shù)據(jù)都會(huì)比較小的值。
        MTU 一段設(shè)置為了 800,另一段是 1500,在 TCP 握手階段可以看到。

        從 10.130.0.6 發(fā)送給 10.130.0.5  最大的包是 800.

        從 10.130.0.5 到 10.130.0.6 也是 800.

        為啥雙方用一個(gè)共同的最小值,這個(gè)我沒(méi)找到確鑿的原因,我覺(jué)得理論上兩端分別用 MSS 是可以的,就像 TCP 的 rwnd 一樣。但是,在現(xiàn)實(shí)的網(wǎng)絡(luò)上,A 發(fā)送 B 有限制,那么 B 發(fā)送到 A 很大可能也有一樣的限制。所以兩邊會(huì)把這個(gè) MSS 作為鏈路上某一個(gè)點(diǎn)的瓶頸。畢竟,每一端都只知道自己這部分網(wǎng)絡(luò)的情況,最好是基于自己和對(duì)方綜合的信息來(lái)做決策。

        MSS 設(shè)置的方法

        如果已知有明確的網(wǎng)絡(luò)情況,可以調(diào)小自己的 MSS,設(shè)置的方法有 3 種:
        #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''')])
        當(dāng)然了,也可以直接調(diào)整網(wǎng)卡上的 MTU: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 抓包

        仔細(xì)觀察 TCP 建立連接的過(guò)程,可以發(fā)現(xiàn) Server 端抓包,發(fā)現(xiàn) Server 發(fā)送給 Client 的 MSS 值是 1460 bytes,但是 Client 收到的時(shí)候變成了 1190 bytes.
        這意味著,除了 TCP 的兩端,中間的路由設(shè)備也可以做 MSS Clamping,影響兩端選擇 MSS 的過(guò)程,以確保網(wǎng)絡(luò)中為其他協(xié)議的 overhead 預(yù)留出來(lái)了足夠的空間。
        以上說(shuō)的都是,協(xié)議的每一層,都確保了自己遞交給下一層協(xié)議的數(shù)據(jù)單元都沒(méi)有超過(guò)下一層協(xié)議的最大長(zhǎng)度。但是我們并不在一個(gè)完美的世界中,假設(shè)協(xié)議收到了超過(guò)最大數(shù)據(jù)單元的數(shù)據(jù),會(huì)怎么做呢?

        其實(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

        抓包如下:

        可以看到一個(gè) ping 一共有 4 個(gè) IP 包,2個(gè)完成 Echo 2 個(gè)完成 Reply. 其中 Echo request,第一個(gè) IP 包總大小是 1500 bytes,除了 IP 包的 20bytes header,還剩下 1480 bytes 是 ICMP 的數(shù)據(jù),第二個(gè) IP 包里面有 528bytes 是 ICMP 數(shù)據(jù),兩個(gè) IP 包帶的數(shù)據(jù)一共 2008 bytes,是符合我們的預(yù)期的,8bytes 是 ICMP 的 header。

        由此,可以發(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)題有:

        1. 同上面提到的 MTU 為什么是 1500 一樣的問(wèn)題:假設(shè)拆分成了 3 個(gè)包,丟了一個(gè)包就相當(dāng)于全丟了,丟包率直接變成(假設(shè)丟包率是 10%,那么3個(gè)包都不丟的概率就是 90%^3=72.9%)27%;
        2. 導(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ā)生;
        3. 產(chǎn)生一些比較難 debug 的問(wèn)題;
        4. 不是所有系統(tǒng)都能處理 IP Fragmentation,比如 Google GCE;

        此外,IP Fragmentation 本身就存在一些攻擊面(見(jiàn)文末),我猜這也是 GCE 關(guān)閉了 IP Fragmentation 的原因?

        所以,在現(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。

        Layer 3 的 IP Fragment 會(huì)導(dǎo)致這么多問(wèn)題,我們寧愿這個(gè)包被丟棄,也不要分成多個(gè)包發(fā)送。

        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 我們可以這么抓 ICMP 包:tcpdump -s0 -p -ni eth0 'icmp and icmp[0] == 3 and icmp[1] == 4'
        發(fā)送端收到這個(gè)錯(cuò)誤,就需要降低自己的 MSS 重新發(fā)送,重復(fù)這個(gè)過(guò)程,直到 MSS 滿足條件為止。這個(gè)過(guò)程就做 PMTUD, 中間路徑上的 MTU 探測(cè) RFC 4821.
        在 IPv6 中,行為基本上是一樣的,但是 IPv6 沒(méi)有這個(gè) DF flag,所有的 IPv6 包都禁止中間的路由設(shè)備進(jìn)行分片,也就是說(shuō)等同于 IPv6 包永遠(yuǎn)是 DF=1,遇到 MTU 太大丟包發(fā)回來(lái)的是 ICMPv6(Of course!)
        這里有一個(gè)問(wèn)題,就是如果一些中間設(shè)備,因?yàn)榘踩颍ㄏ挛臅?huì)解釋)禁用了 ICMP(ping), 這樣可能導(dǎo)致的問(wèn)題是,TCP 連接能建立成功,但是數(shù)據(jù)一直發(fā)不出去,造成黑洞連接 RFC 2923。解決這個(gè)問(wèn)題的核心,是要區(qū)分丟包到底是中間鏈路造成的,還是 MTU 太大造成的。TCP over IPv4 感覺(jué)沒(méi)有特別好的辦法,最好是允許 type 3 的 ICMP 包。RFC 4821 提出了一種不依賴 ICMP 的 PMTUD 方法,本質(zhì)是使用小包開始,逐漸增大大小直到達(dá)到 MTU 上線,和 TCP 擁塞控制有異曲同工之妙。
        至于這個(gè)“安全原因”,就比較有意思了。舉一個(gè)例子:如果攻擊者知道服務(wù)端的地址,即使攻擊者不在 Client – Server 的路由鏈路上,它也可以發(fā)送一個(gè) ICMP 包告訴 Clinet MTU too large, 讓這個(gè)連接的雙方降低 MSS 從而降低性能。
        還可能有另一個(gè)問(wèn)題,有些 DC 可能用了 ECMP 技術(shù),簡(jiǎn)單來(lái)說(shuō),一個(gè) IP 后面有多個(gè)服務(wù)器,ECMP 會(huì)根據(jù) TCP 端口,和 IP 來(lái)做 hash,這樣可以根據(jù) IP + Port 來(lái)保證路由到正確的 Server 上,即使 IP 一樣。但是對(duì)于 ICMP 包來(lái)說(shuō)就有問(wèn)題了,ICMP error 包可能被路由到了錯(cuò)誤的服務(wù)器上,導(dǎo)致 PMTUD 失敗。Cloudflare 就遇到過(guò)這個(gè)問(wèn)題。
        下面說(shuō)兩個(gè)有意思的問(wèn)題。

        道理我都懂,但是我的抓的包怎么大?

        正常情況下,jacson在序列化時(shí),json串中的字段名稱和類中屬性的名稱是一樣如果你通過(guò)抓包去看一下 MSS 是否是有效的,里面每一個(gè)包的大小是否最大是 1500 bytes,你會(huì)懷疑人生。

        明明協(xié)商的 MSS 1460,但是后面的數(shù)據(jù)居然有 1萬(wàn)多bytes的??在接收端抓包也一樣。

        這個(gè)叫 TSO,TCP Segment Offload.

        上面我們講為了不發(fā)生 IP Fragment, Kernel 協(xié)議棧要負(fù)責(zé)把 TCP 分成一個(gè)個(gè)不超過(guò) MSS 的小包發(fā)送,這部分工作是簡(jiǎn)單重復(fù)并且計(jì)算量比較大的,很顯然,適合網(wǎng)卡來(lái)做這個(gè)工作。
        所以 TSO,就是網(wǎng)卡 Driver 告訴 Kernel,這個(gè)工作可以交給我,做拆包我一直可以的,我可以一直拆包的,于是,Kernel 就發(fā)大包到網(wǎng)卡,網(wǎng)卡完成大包拆小包。

        但是對(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é)議棧。

        這減輕了 Kernel 的不少 CPU 負(fù)擔(dān),轉(zhuǎn)移到了硬件上完成。但是……分析 Sequence number 就是一個(gè) pain in the ass 了。

        第二個(gè)是工作中遇到的問(wèn)題,也是我看這些東西的起因。

        我們的 SDN 網(wǎng)絡(luò)有一種這樣的路由:

        一種奇怪的“三角路由”,其中 Router 會(huì)添加 50bytes 的額外 header,然后發(fā)現(xiàn) Router 這里發(fā)生了丟包。
        最后發(fā)現(xiàn),原因是我們對(duì) eth0 設(shè)置了 MTU = 1450,但是忘記設(shè)置 ip route,導(dǎo)致握手階段的包從 eth1 出去了,eth1 的默認(rèn) MTU 是 1500,PC 發(fā)送的 MTU(MSS actually) 也是 1500,就導(dǎo)致雙方一致認(rèn)為 MTU=1500,MSS=1460. 但是實(shí)際上到 Rrouter 這里加了 50bytes 的 overhead,就造成了丟包。

        前面提到 IP Fragment 有很多安全問(wèn)題,這里列舉了其中一些:

        1. IP fragment overlapped:攻擊者精心設(shè)計(jì)了很多 IP 分片,它們互相重疊,理論上這種包是無(wú)法在網(wǎng)絡(luò)上出現(xiàn)的。如果服務(wù)器收到這些分片,可能無(wú)法正確處理(IP 實(shí)現(xiàn)的 Bug),那么可能會(huì)崩潰;
        2. IP fragment overrun:攻擊者通過(guò) IP 分片的方式,發(fā)送的 IP 包組裝之后超過(guò)了 65535,可能造成服務(wù)器崩潰(溢出);
        3. IP fragmentation buffer full:攻擊者一直發(fā)送 IP 分片,more-fragments 一直設(shè)置為 true,導(dǎo)致服務(wù)器收到 IP 包的時(shí)候,只能存儲(chǔ)在 buffer 中試圖將它們組裝起來(lái),直到內(nèi)存耗盡 (DDoS);
        4. 其他構(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
          1. IP fragment too many packets

          2. IP fragment incomplete packet

          3. IP Fragment Too Small

        推薦閱讀:

        完全整理 | 365篇高質(zhì)技術(shù)文章目錄整理

        一張圖弄清楚緩存架構(gòu)設(shè)計(jì)中的經(jīng)典問(wèn)題及解決方案

        一張圖總結(jié)系統(tǒng)設(shè)計(jì)中的33個(gè)黃金法則

        主宰這個(gè)世界的10大算法

        徹底理解cookie、session、token

        專注服務(wù)器后臺(tái)技術(shù)棧知識(shí)總結(jié)分享

        歡迎關(guān)注交流共同進(jìn)步
        也可掃碼添加個(gè)人微信交流技術(shù),職場(chǎng)發(fā)展~
        添加時(shí)請(qǐng)注明公司名(或?qū)W校名)+方向??!


        碼農(nóng)有道 coding


        碼農(nóng)有道,和您聊技術(shù),和您聊職場(chǎng),和您聊互聯(lián)網(wǎng)那些事!

        瀏覽 157
        10點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        10點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. bytv跳转接口点击进入网页 | 插B视频网站 | 日韩精品欧美 | 美女露逼图片 | 欧美人与欧美做爰 |