協議森林4——我盡力:IP協議詳解

上兩篇「IP 接力」和「IP 地址」,讓我們瞭解了 IP 協議的基本工作方式。我們在這一篇中迴歸 IP 協議本身,看一看 IP 協議的具體細節和設計哲學。
IPv4 與 IPv6 頭部的對比
我們已經在 IP 接力中介紹過,IP 協議把數據包裹進一個個數據包。每個 IP 包分爲頭部(header)和數據(payload/data)兩部分。和其他網絡傳輸協議一樣,頭部攜帶的是實現通信必須的附加信息。數據部分纔是通信真正要傳送的信息。IP 協議規定了一個 IP 包頭部的格式。下圖是 IPv4 和 IPv6 的頭部,我們來研究一下兩者的異同。
在這裏插入圖片描述
黃色區域:相同區域
我們看到,有三個黃色區域同時存在於 IPv4 和 IPv6。版本號(Version)佔了 4 位的長度,用來表明 IP 協議版本,是 IPv4 還是 IPv6。對於 IPv4 來說, 它的四位版本號是 0100,即二進制的 4。IPv6 的四位版本號爲 0110。出發地址(Source Adrresss)和目的地址(Destination Address)分別爲發出地和目的地的 IP 地址。
藍色區域:名字更改區域
傳輸上限

在 IPv4 中有存活時間(Time to Live),對應 IPv6 中的最大中繼數(Hop Limit)。存活時間最初是表示一個 IP 包的最大存活時間:如果 IP 包在傳輸過程中超過存活時間,那麼 IP 包就作廢。後來,IPv4 的這個區域記錄一個整數。比如整數 30,就表示在 IP 包接力過程中最多經過 30 個路由接力,如果超過 30 個路由接力,那麼這個 IP 包就作廢。IP 包每經過一個路由器,路由器就給存活時間減一。當一個路由器發現存活時間爲 0 時,就不再發送該 IP 包。IPv6 中的最大中繼數記錄直接就是最大路由接力數。兩者不同名,但有相同功能,都避免了 IP 包在互聯網中無限接力。
服務/交通類型
IPv4 中的服務類型(Type of Service)最初是用來給 IP 包分優先級,比如語音通話需要實時性,所以它的 IP 包應該比 Web 服務的 IP 包有更高的優先級。然而,這個最初不錯的想法沒有被微軟採納。在 Windows 下生成的 IP 包都是相同的最高優先級,所以在當時造成 Linux 和 Windows 混合網絡中,Linux 的 IP 傳輸會慢於 Windows。說來荒唐的是,慢的原因僅僅是因爲 Linux 更加守規矩。後來,服務類型被實際分爲兩部分:服務區分(Differentiated Service Field, 即 DS,前 6 位)和外部阻塞通知(Explicit Congestion Notification,即 ECN,後 2 位),前者依然用來區分服務類型,而後者用於表明 IP 包途徑路由的交通狀況。IPv6 的交通類型(Traffic Class)與 IPv4 的服務類型相似,結構上也被如此分成兩部分。通過 IP 包提供不同服務的想法,並針對服務進行不同的優化的想法已經產生很久了,但具體做法並沒有形成公認的協議。比如 ECN 區域,它用來表示 IP 包經過路徑的交通狀況。如果接收者收到的 ECN 區域顯示路徑上的很擁擠,那麼接收者應該作出調整。但在實際上,許多接收者都會忽視 ECN 所包含的信息。交通狀況的控制往往由更高層的比如 TCP 協議實現。
在這裏插入圖片描述
碎片
高層協議
IPv4 中的協議,也就是 IPv6 中的內層格式(Next Header)。它們都用來說明 IP 包 Payload 部分所遵循的協議,也就是 IP 包的數據部分使用的更高層協議是什麼。它說明了 IP 包封裝的是一個怎樣的高層協議包,比如說 TCP 協議或者 UDP 協議。
長度信息
IPv4 中的總長(Total Length), 以及 IPv6 中數據長度(Payload Length)。它們都用來記錄 IP 包的大小。IPv4 中的總長是整個數據包的長度。而 IPv6 的數據長度只記錄了數據部分的大小。但 IPv6 的頭部是固定的長度 40 字節,所整個 IP 包的總長就是 40 字節加上數據長度。
紅色區域 (IPv6 中刪除的區域)
IPv4 包在頭部的最後,可以有多個選項(options)。每個選項有 32 位。選項不是必須的,所以一個 IPv4 頭部可以完全沒有選項。不考慮選項的話,整個 IPv4 頭部有 20 字節。但由於有選項的存在,整個頭部的總長度是浮動的。我們用頭部總長(IHL,Internet Header Length)來記錄頭部的總長度。通過頭部總長,我們能知道哪些部分是選項。IPv6 沒有選項。它的頭部是固定的長度 40 字節,所以 IPv6 中並不需要記錄頭部總長。
IPv4 中還有一個頭部校驗碼(Header Checksum)區域。這個校驗碼用於校驗 IP 包的頭部信息。我們在介紹以太網的「小喇叭」一文中也見過校驗碼,但 IP 校驗碼所使用的算法與以太網的 CRC 算法不同。IPv6 沒有校驗相關的區域。IPv6 包的校驗依賴高層的協議來完成,因此免去了執行校驗時間,減小了網絡延遲 。
身份(Identification),標識(flags)和碎片補償(fragment offset),這三個包都是爲碎片化(fragmentation)服務的。碎片化是指一個路由器將接收到的 IP 包分拆成多個 IP 包傳送,而接收這些「碎片」的路由器或者主機需要將「碎片」重新組合(reassembly)成一個 IP 包。不同的局域網所支持的最大傳輸單元(MTU, Maximum Transportation Unit)不同。如果一個 IP 包的大小超過了局域網支持的 MTU,就需要在進入該局域網時碎片化傳輸。這就好像方面面面餅太大了,必須掰碎才能放進碗裏。碎片化會給路由器和網絡帶來很大的負擔。最好在 IP 包發出之前探測整個路徑上的最小 MTU,IP 包的大小不超過該最小 MTU,就可以避免碎片化。IPv6 在設計上避免碎片化。每一個 IPv6 局域網的 MTU 都必須大於等於 1280 字節。IPv6 的默認發送 IP 包大小爲 1280 字節。
綠色區域 (IPv6 新增區域)
流標籤(Flow Label)是 IPv6 中新增的區域。它被用來提醒路由器來重複使用之前的接力路徑。這樣 IP 包可以自動保持出發時的順序。這對於流媒體之類的應用有幫助。流標籤的進一步應用尚在開發中。
「我盡力」
IP 協議在產生時是一個鬆散的網絡,這個網絡由各個大學的局域網相互連接成的,由一羣碰頭垢面的 Geek 維護。所以,IP 協議認爲自己所處的環境是不可靠的:諸如路由器壞掉、實驗室失火、某個 PhD 踢掉電纜之類的事情隨時會發生。
這樣的兇險環境下,IP 協議提供的傳送只能是「我盡力」(best effort)式的。所謂的「我盡力」,其潛臺詞是,如果事情出錯不要怪我,我只是答應了盡力,可沒保證什麼。所以,IP 包傳輸過程中可能出現各種各樣的錯誤,比如校驗碼對不上、交通太繁忙、比如超過最大存活時間,根據 IP 協議,你的 IP 包會直接被丟掉。就是這樣……被丟掉了……不會再有進一步的努力來修正錯誤。「我盡力」原則讓 IP 協議保持很簡單的形態。更多的質量控制交給高層協議處理,IP 協議只負責有效率的傳輸。
「效率優先」也體現在 IP 包的順序上。即使出發地和目的地保持不變,IP 協議也不保證 IP 包到達的先後順序。我們已經知道,IP 接力是根據路由表決定接力路線的。如果在連續的 IP 包發送過程中,路由表有時會更新,比如有一條新建的捷徑出現。這種時候,後發出的 IP 包選擇走不一樣的接力路線。如果新的路徑傳輸速度更快,那麼後發出的 IP 包有可能先到。這就好像是多車道的公路上,每輛車都在不停變換車道,最終所有的車道都塞滿汽車。這樣可以讓公路利用率達到最大。
IPv6 中的流標籤可以建議路由器將一些 IP 包保持一樣的接力路徑。但這只是「建議」,路由器可能會忽略該建議。
IPv4 的校驗算法
IPv4 會校驗頭部信息。校驗碼區域有 16 位。這個校驗碼是這樣獲得的。首先,從頭部取得除校驗碼之外的信息,例如下面是一個演示的頭部,寫成十六進制形式:

9194 8073 0000 4000 4011 C0A8 0001 C0A8 00C7

信息按照 16 位分割,也就是每 4 位 16 進制數分爲一組。上面的序列已經分割好了。將分割後的各個 4 位 16 進制數累積相加。如果有超過 16 位的進位出現,則將進位加到後 16 位結果的最後一位。比如說先將最開始的兩組 16 進制數求和,獲得:

9194 + 8073 = 1 1207

結果是 11207,從 4 位變成了 5 位。那麼將進位的 1 加到 1207 上:

1207 + 1 = 1208

上面的求得 1208 的過程就叫做叫做一補數求和(one’s complement sum)。用 1208 繼續累加,直到獲得所有組的累積和,將會是 1433。
然後,將十六進制的 1433 寫成二進制序列,把二進制的每一位取,即 0 變成 1、 1 變成 0, 就得到校驗碼:EBCC。把校驗碼 EBCC 插回到頭部,我們整個頭部就是:

9194 8073 0000 4000 4011 EBCC C0A8 0001 C0A8 00C7

IP 包的接收方在接收到 IP 包之後,對整個頭部進行一次一補數累積和運算,應該得到 FFFF。如果不是 FFFF,那麼傳輸過程中,頭部信息發生變動,造成校驗碼與頭部信息對不上。根據「我盡力」原則,整個 IP 包會被丟棄。
總結 每個網絡協議的形成都有其歷史原因。比如 IP 協議是爲了將各個分散的實驗室網絡連接起來。由於當時的網絡很小,所以 IPv4 的地址總量爲 40 億。儘管當時被認爲是很大的數字,但數字浪潮很快帶來了地址耗盡危機。IPv6 的主要目的是增加 IPv4 的地址容量,但同時根據 IPv4 的經驗和新時代的技術進步進行改進,比如避免碎片化以及取消驗證碼。網絡協議技術上並不複雜,更多的考量是政策性的。 IP 協議是「我盡力」式的,IP 傳輸是不可靠的。但這樣的設計成就了 IP 協議的效率。