IP:網路上的擊鼓傳花

林子er發表於2022-01-27

連結,而不是直達

在之前《聽說你很懂 DNS?》中我們分析過使用者在瀏覽器裡面輸入 www.baidu.com 後,瀏覽器如何通過 DNS 解析拿到 IP 地址,然後請求該 IP 地址獲取網站內容。

本文接著就講講瀏覽器端給目標主機(IP 地址)傳送的資料在網路上如何歷經千辛萬苦到達目的地的。

我們現在所說的網路一般就是指因特網(Internet)。因特網是一種網路架構模式,世界上除了因特網還有其它網路架構如 ATM、幀中繼等。現在地球上使用的網際網路就是基於因特網架構。

在因特網中,每臺聯網裝置都有一個全網唯一的身份標識叫 IP 地址(簡稱 IP),有了 IP 地址,兩臺主機(電腦、手機等聯網裝置)就能通過網路通訊了:

image-20220122103332527

上圖我們在兩臺電腦之間直接拉了一條線纜,兩端直通。如果土豆還想跟白菜通訊,得再跟白菜拉一條線纜——也就是你的主機要分別跟百度、新浪、騰訊、阿里......各自拉一條線纜來通訊,這顯然不現實。

所以現實中主機間通訊並不是直連的,而是通過很多箇中轉站來轉發的。這就好比你在淘寶上買了袋餅乾,人家快遞公司不可能開著飛機從商家直接飛到你家樓下,而是通過各種中轉站轉發的,如先從商家所在的街道網點發往城區中轉站,再從城區發往華北排程中心然後發往華中分發中心,進而分發給各區域網點,最後快遞小哥騎著摩的送到你家。

所以,我們的網路看起來是這樣:

image-20220122105859772

主機間並不是兩兩直連的,而是通過中轉裝置交換資料。這個中轉裝置我們叫它路由器(router)。

(路由:指路徑,指從某地到某地、經過的意思,路由指經過某路徑到達某地,該路徑所經過的中轉站叫路由器

主機 A 要發資料給主機 B,需要通過路由器 A 中轉;如果發給主機 D,則需要路由器 A 和路由器 B 做兩次中轉——這裡需要幾次中轉,在計算機裡面有個術語叫(hop,英文指單足蹦跳之意,從路由器這邊”跳“到那邊)。主機 A 到主機 B 需要 1 跳,到主機 D 需要 2 跳。

我們再看下主機 A 到主機 D 的路徑,這條路徑是通過路由器 A 和 路由器 B 連結起來的,整個路徑就好像一根鏈子,所以稱該路徑上兩兩節點之間的線路叫鏈路,上面的路徑由主機 A — 路由器 A、路由器 A — 路由器 B、路由器 B — 主機 D 三條鏈路構成。

在繼續深入細節之前,我們有必要先了解下網路的分層架構設計。

網路分層架構

一個現實的例子:

一般第一次坐飛機的人多少有點摸不著頭腦(別問我是怎麼知道的!),比如拉著手推車拖著一百來斤的行李橫衝直撞,隨便找個像門的地方(比如國內到達口?)溜進去,如入無人之地——然後很自然地就被安保摁倒在地。

像航線這種高逼格而又複雜的系統,比起公交有多得多的流程,你的一次航行流程大致是這樣:

image-20220126193803077

你不是在一個地方搞定所有的事情(票務、行李、登機等),而是在不同的辦理點各搞定一部分,而且是按照上圖箭頭順序依次執行,在入和出兩個方向的辦事點是相對應的(入口處最先做的事情在出口處是最後做的,入口處最後做的對應的是出口處最先做的),各個部分串在一起共同構成了你的這次航行。

網際網路也是這樣一個複雜系統,應用程式之間需要互動各種格式的資料,還要保證資料能夠正確、及時、安全地傳輸到目的地,這過程中涉及到非常複雜的、異構的技術問題,也要採用這種分工的方式實現,具體的做法是在邏輯上分成幾個層次,每層只處理特定的事情。

每層做事情的章法我們叫它協議(行李託運、登機、起飛等都有各自的規章制度),每層都有自己的協議集(可能不止一個協議,如不同的航站可能登機程式不同),所有層的協議在一起我們叫協議棧——之所以叫棧,你看下上面飛機航行的流程,兩邊各流程的對應關係正是後進先出,也就是整個操作滿足棧的特性(比如上面左邊出發流程的最後一道程式是跑到起飛,而右邊到達流程的第一道程式是跑道著陸)。

OSI 模型和 TCP/IP 協議棧:

你在大學肯定學過著名的 OSI 七層模型:

image-20220126194311382

OSI 七層網路模型

然後你用 wireshark 等工具研究了半天,找不到會話層和表示層到底在哪裡。

要知道的是這個 OSI 模型在設計的時候是針對所有(也就是抽象的)網路的,而不是針對因特網(其實在設計之初並沒有考慮因特網)——所以這玩意對於我們來說理論意義大於實踐意義。

因特網用的分層模型叫 TCP/IP 協議棧,它是個五層模型:

image-20220126194652045

TCP/IP 協議棧的五層模型

我們分析下這五層具體是幹嘛的:

  • 首先溝通的雙方就具體的應用場景需要傳遞特定格式的資料,該資料格式是由應用層協議定的;
  • 這些資料需要通過物理實體(光纖、銅線、網路卡等)一步一步從源傳給目的地。物理層關注的是這些位元的物理傳輸問題;
  • 前面說過,源和目的之間並不是通過線纜直連的,而是通過一個個路由器轉發的,那麼兩個節點之間(鏈路)如何傳遞資料則是鏈路層要處理的事情;
  • 當資料通過鏈路層傳遞到中轉站路由器時,路由器需要通過某種規則(協議)決定下一步發往哪條鏈路(走哪個出口),並完成轉發——由網路層負責“尋路”的工作(具體的辦事章程就是IP 協議路由選擇協議);
  • 由前面的物理層(實際物理傳輸媒介)、鏈路層(兩個節點間傳輸規則)和網路層(尋路)一起完成了資料的傳輸。然而,因特網的這三層雖然能傳輸資料,但並不是可靠的(其它網路模型如 ATM 的 CBR 模型能夠在網路層保證資料的可靠傳輸,但因特網的設計原則是讓網路本身更加簡單,所以未在這三層提供可靠性保證),不能保證資料能準確、及時、安全地傳遞到目的地——比如路由器佇列滿了就會丟棄後續資料(而且不會給予通知)、TTL 跑完了(路由選擇出問題了,資料在網路中迷路了)也會被丟棄。因特網的原則是保持網路本身的簡單,將這種複雜的可靠性保證放在端系統(源和目的地主機)上實現。傳輸層就是用來做資料可靠性保證的(確保傳遞、流量控制)——當然如果應用本身為了效能考慮並不需要這層可靠性保證,那也可以不做。因特網傳輸層用到的協議是 TCP 和 UDP 協議。

可以看到,五層中的底下四層都是為了第五層資料的可靠傳輸設計的。下一層是在上一層的基礎上工作的;底下三層工作在整個網路的每個節點(主機和路由器)和鏈路上,而傳輸層和應用層是工作在端系統(源和目的主機)上。

可能有人會問,因特網的五層協議沒有 OSI 中的會話層和表示層,難道這兩層不重要嗎?

我們發現因特網的五層協議中,底下四層都是為資料傳輸服務的,所以整個模型我們可以歸納成兩個大層:應用層與傳輸層(包含底下四層),然而在這兩層之間其實還有很多事情要做:傳輸層很可能會對應用層資料做切分(資料過大無法一次性傳輸),所以在應用層接收到資料後不得不重新組裝資料;應用層的資料在傳輸前可能需要壓縮以降低網路流量佔用,所以接收後需要解壓;為了安全,應用層資料在網路上傳輸之前可能需要加密,所以接收後需要先解密;由於溝通雙方系統的異構性,溝通雙方需要就資料的某些方面做些約定,如多位元組字元的編碼、數值的大小端表示等。

因特網的回答是:這些雜碎的事情都讓應用層自己去處理吧!

所以應用層協議一般會兼具會話和表示的功能,比如 HTTP 協議中的各種頭部資訊(裡面包括字元編碼、文字格式、是否壓縮以及壓縮策略、安全策略等),使得兩邊的 HTTP 客戶端和伺服器應用能夠很好地理解資料的含義。

傳輸單元:

快遞的運輸單元叫包裹(packet),網路借鑑了這種說法。不過中文翻譯過來不叫“包裹”,而叫分組(packet)(是不是瞬間逼格提升 n 倍)。

每一層的分組都有它自己的特定名字(否則每層都叫分組就很容易混淆了):

image-20220124090244287

物理層嚴格意義上不能叫“分組”了

所以以後聽到說“TCP 分組”和“TCP 報文段”不要再糾結它倆到底有啥區別了——壓根就沒區別。

網路分組和快遞包裹有點不同的是,快遞運輸中往往是我們提供一個個小件包裹,然後快遞公司裝箱成大包裹——網路恰恰相反,網路上是應用層提供個大包裹(比如 HTTP 報文,可能有幾十兆),傳輸層一次傳不動,就會將它切分成一個個小的分組,這些分組在傳給網路層、鏈路層後可能還會被切分成更小的分組。

一個分組由首部(Header)和有效載荷(payload)兩部分構成。首部放一些後設資料,比如協議的版本、加密演算法、校驗和、實際資料長度等;有效載荷也就是實際的資料:

image-20220126194935343

整個網路分層體現在分組結構上就是俄羅斯套娃:下層(如網路層)拿到上層(如傳輸層)的分組(裡面包含了上層的首部和有效載荷)後,在上面加上自己的首部,形成新的分組並交給自己的下層:

image-20220126195119626

這樣講還是比較抽象,抓個包看看就明白了。

網路分層的運作過程:

我們抓一個 HTTP 包看看實際的資料包結構:

ip-05

那麼這樣的資料包是如何生成和使用的呢?其中涉及到封包和解包的過程:

image-20220126195236976

在傳送端(源主機上),應用層將報文(前圖橙色部分)傳給傳輸層;傳輸層收到報文後附上傳輸層的首部(前圖綠色部分,包括埠、序列號等資訊),該首部和應用層傳過來的報文一併構成傳輸層的報文段,繼續傳給網路層;網路層收到報文段後加上自己的首部(前圖藍色部分,包括 IP 地址等資訊),構成網路層的資料包,傳給鏈路層;鏈路層拿到傳過來的資料後繼續加上自己的首部(前圖紅色部分,包括 mac 地址等資訊)構成鏈路層,最後交由物理層介質傳輸。

上面的過程是傳送端的封包過程,當接收端(路由器或目標主機)收到資料後,需要執行逆向的解包。

根據裝置工作的層不同,需要解包到的層次也不同。鏈路層裝置(如鏈路層交換機)只需要從來源分組中取出鏈路層資訊(mac 地址),根據此資訊做分組轉發——鏈路層以上的資訊對它來說屬於黑盒,無需關心;網路層裝置(如路由器)需要取出網路層首部(IP 地址等資訊),據此作出相應行為(如根據目標 IP 做轉發)。目的主機需要對所有層解包,最終得到應用層報文,根據應用層協議做相應的處理。

注意,除了目標主機,其它的接收端(如路由器)同時也是傳送端,因而這些裝置除了在接收到分組後進行解包以獲取必要的資訊外,在轉發分組前還要封包。比如路由器,它接收到分組後首先進行鏈路層解包,得到 mac 資訊,發現其中的目標 mac 地址是自己後,再進行網路層解包(因為它是工作在網路層嘛)得到 IP 資訊,根據其中的目標 IP 地址結合自己的路由轉發表,決定要將分組轉發到某個出網口。在轉發前,它首先要修改該分組的網路層首部,將 TTL 的值減一(相應地校驗和也要改變。如果它是一臺 NAT 路由器,要做 NAT 轉發的話,還要修改源 IP 和埠,這個後面再說);然後它還要修改鏈路層首部,更新 mac 地址(源 mac 改成自己,目標 mac 改成下一跳目標裝置的)。

整個資料傳輸中的封包解包可參照下圖:

image-20220124104153005

圖片來自《計算機網路:自頂向下方法》

在結束網路分層架構的討論之前,你可能還有個疑惑:在解包過程中,下層如何知道應該將分組交給上層哪個處理器處理呢?比如網路層如何知道應該將分組交給 TCP 處理器還是 UDP 處理器呢?

因而在各層協議的首部,都包含了指向上層協議的“指標”,如圖:

image-20220124112444652

講完網路分層架構後,我們將目光聚焦到網路層上。

路由器

我們開頭講過,網際網路上的主機間並不是直連的,而是通過很多路由器形成鏈狀路徑(也因此兩節點之間的線路叫鏈路,負責兩個節點間通訊細節的叫鏈路層)。這裡的核心裝置是路由器

路由器屬於網路層分組交換機。所謂分組交換機,是指根據一定的路由規則對分組(如果現在你還對“分組”這個名字感到不適,那你就想象成快遞包裹)執行轉發的裝置,讓分組從一條鏈路走向另一條鏈路,最終到達目的地。交換機要執行轉發,必定要依據來源分組的某些資訊,並查詢自己的轉發表,以決定要將分組發往哪條出路——網路層分組交換機是指這個轉發表(轉發規則)是依據網路層資訊制定的,具體來說就是 IP(請記住我們討論的是因特網,其他網路模型就不一定是 IP 了)。

路由器做的核心的事情就是:根據來源分組的目標 IP 將其轉發到合適的出鏈路。

路由器原理大致如下:

ip-06

圖片來自《計算機網路:自頂向下方法》

路由器主要由四部分構成:輸入埠、輸出埠、交換結構、路由選擇處理器。

分組首先從輸入埠進入,輸入埠從中取出網路層首部,獲取目的 IP,根據該 IP 查詢自己的路由轉發表,決定要將該分組交給哪個輸出埠,然後將該分組放到交換結構的相應位置,由交換結構實際將分組傳送給對應的輸出埠

上面提到的轉發表是由路由選擇處理器(一個普通的 CPU 加相關軟體)計算的,該處理器根據相關路由選擇演算法計算本路由器的轉發表並拷貝給各輸入埠,輸入埠便根據該表做分組轉發。

一份轉發表本質上就是 IP 到網口的對映,大致像這樣(linux 用 route -n 檢視,Mac 用 netstat -nr):

image-20220124144515581

上面是我本機的路由轉發表,有兩個網路卡(Netif 欄位):en0(物理網路卡)和tap0(虛擬網路卡,用於跟公司做 VPN 連線)。你會發現,轉發表的 IP 列(第一列)大部分不是完整的 IP,而是字首,轉發表的匹配策略是最長字首匹配。上面定義了兩個預設策略(所有 IP 字首都匹配不上的時候走該策略),兩個中優先走 tap0(也就是 VPN的)。按照上面的配置,在我訪問絕大部分外網服務時都是走得 VPN 網路。上面有一條 14 的規則,說明以 14 開頭的 IP 走的是 en0。

需要注意的是,路由器的處理單位是分組(資料包),而物理層資料是按位元傳輸的,一個資料包的所有位元一般不是同時到達路由器輸入口的,此時路由器需要等待該分組的所有位元到達後才能開始處理該分組(而不是在第一個位元到來後就處理),這裡就存在一個時延。另外路由器拆包、查詢表、轉發同樣需要時間(雖然這些時間一般以納秒計),所以資料每經過一個路由器便不可避免地產生網路時延——如何優化路由選擇演算法以減少資料包經過的路由器數量,以及優化路由器本身的處理效能是網路層需要處理的核心問題。

另外,如果路由器的處理效能跟不上,分組到來的速度大於路由器轉發的速度,就會導致分組堆積到待處理佇列中,佇列堆滿後,後面來的分組就會被丟棄,也就是我們常說的丟包。由於因特網的網路層並不提供可靠性保障,路由器會直接將分組丟棄而不會通知傳送方,所以為了獲得傳輸上的可靠性,必須在上層(如傳輸層)實現相關保障機制。

IP

IP 協議:

IPv4 資料包大致長這個樣子:

image-20220126195558018

雖然只有20個位元組,不過瞅瞅內容還是挺複雜的。

其中有 4 個位元記錄首部長度——也就是說 IPv4 首部長度不是固定的!問題出在“服務型別”上,因為有些服務型別會有額外的 4 個位元組擴充套件資訊。

其中有三個部分跟分片有關——當出鏈路的 MTU(最大傳輸單元)不足以容納整個分組的時候,會將分組分成多個小分組傳送,那麼因為最終還要把這些切分的分組整合成原來的大分組,所以要在這些分組裡面記錄額外資訊(標識、分組偏移等)。

TTL 是該 IP 資料包的存活週期,該值在傳送端設定,然後每經過一臺路由器將其減 1,當值為 0 時直接丟棄——假如沒有這玩意,如果一個分組在網路上迷路了,就變成孤魂野鬼整日在網路上游蕩了(無限地轉發)。

上層協議前面說過,表示該資料包到達目的主機後應交給上層哪個協議處理器處理,這裡是要交給 TCP 處理。

首部校驗和是為了防止這些首部資訊被篡改而根據某種演算法生成的值。由於每個路由器都要修改 TTL 的值,因而也必須重新計算校驗和。

IPv4 的設計因其歷史原因存在兩大問題:

  1. 對路由器不友好。首先首部長度不是固定的,這就使得路由器無法就這塊做效能優化,它必須從首部先獲取長度,然後才能取出完整首部;然後裡面有個校驗和,而 TTL 是每經過一個路由器都會變化的,這就要求每個路由器得重新計算首部校驗和。像路由器這種對效能要求極其苛刻的裝置(不是說你家裡那臺僅服務兩三個人的路由器,而是諸如骨幹網上的那些路由器),做這些事情並不合適。
  2. IP 地址數量嚴重不足。IPv4 只有4 個位元組,也就 40 多億的地址,對於網際網路爆炸的今天來說,少得可憐。

當然,我們不能對在上世紀 80 年代初那種網路流量按位元組計、網路僅僅用於軍事和科研的情景下設計出來的東西做事後諸葛亮般的評論,那時候的設計者或許壓根沒想到因特網會全球範圍地民用化,相較於效能,他們考慮更多的可能是頻寬(所以 IP 地址只有 4 位元組,首部也是變長的,分組因受限於不同鏈路的 MTU 承載能力而不得不進一步切分)。

IPv6 針對這些問題做了優化,其設計原則是效能高於一切

IPv6 採用 40 位元組定長首部,這樣路由器便可以做針對性地優化。

IPv6 也不支援分片,如果某段鏈路不支援這麼大的分組,則直接給傳送端返回錯誤,讓傳送更小的分組。

IPv6 也不計算校驗和,資料正確性交由上層去保證。

這樣在 IPv6 的 40 位元組定長首部中,有 32 位元組是源和目標 IP,僅有 8 位元組是記錄其他資訊的。

分類編址與 CIDR:

IPv4 地址長度是 4 個位元組,用十進位制表示出來其範圍是 0.0.0.0 到 255.255.255.255。IP 地址由網路地址+主機地址構成。在起初的設計中,IP 地址分為 A、B、C 等幾大類,其中 A、B、C 三大類表示的是地址中哪些部分是網路地址,哪些部分是主機地址。 A 類地址規定 IP 的第一部分是網路地址,後面三個部分構成主機地址,而且第一部分的最高位必須是 0;B 類地址的前兩部分是網路地址,後兩部分是主機地址,而且最高兩位必須是 10;C 類地址的前三部分是網路地址,後一部分是主機地址,而且最高三位必須是 110。如圖:

image-20220126195750769

將 IP 地址分成網路部分和主機部分是個不錯的想法,它引入了子網的概念,使得路由選擇具有層次化,路由器在計算轉發策略時,只需要將分組轉發到整個子網(具體是轉發給子網的閘道器路由器),而不需要關心目的主機在子網的具體哪個地方;分組到達子網後,再由子網內部的路由器做近一步轉發。

這就好比你想到深圳南山區大學城,你是先從北京坐飛機到深圳,然後再到南山區,最後才在南山區裡面找大學城的位置,而不是一開始就鎖定具體的位置。

然而,這種 IP 分類方式帶來很大的問題,它會造成大量 IP 浪費。我們看看 A 類地址,它的網路部分一共有 2^7 - 2 = 126 個(之所以減 2 是因為全 0 和全 1的屬於特殊地址),主機部分是 2^24 - 2 = 16,777,214 個。什麼意思呢,A 類地址一共只能分配給 126 個機構,而每個機構可以擁有 1600 多萬臺主機!一般教科書會說“A 類地址一般用於擁有大量主機的大型網路”之類的廢話,問題在於,這僅有的 126 個地址到底該分配給誰才合適?這不是一個技術問題,更可能是個政治問題。

我們再來看看 C 類,該類地址一共可有 2^21 - 2 = 2,097,150 個網路地址(200 多萬),每個網路地址(網段)下只能有 2^8 - 2 = 254 個主機!你去大街上閉著眼睛指認一家公司很可能就超過這個數。當然,你可能認為 C 類的地址少沒關係啊,玩內網撒,NAT 不香嗎?問題是憑什麼分配到 A 類的機構不能玩 NAT 呢?

所以用現在的眼光看,IP 地址的這種分類是極不合理的,A、B 類會造成大量浪費而 C 類則遠遠不夠用。

所以,這種分類編址法早早地就被送進博物館了,現在世界上使用的是叫做無類別域間路由選擇(Classless Interdomain Routing,CIDR)的分配策略。這個名字繞的很,著重強調了它是無類別分配法。簡單地理解就是它仍然承襲了分類編制法的思想,即 IP 地址分成網路段和主機段兩部分;不過,它不再限制只能根據點(.)來劃分網路段和主機段,可以使用任意個位元表示網路段,而且主機段並不是只能掛主機,還可以進一步劃分子網。

子網

看看下面這張圖:

image-20220126195924570

該圖有兩個路由器,將 8 臺主機連線起來,而且路由器之間也有連線。

這張圖一共有 4 個子網,它們使用適當的 IP 字首作為子網標識。

最左邊的三臺主機都以 220.1.1 開頭,我們記作 220.1.1.0/24,這裡的 24 叫做子網掩碼,它表示 IP 中的高位 24 位元表示該子網地址(網路段),子網中的所有主機的 IP 都是以該子網地址開頭。同樣,右邊的子網地址是 220.1.2,下面兩臺的是 220.1.3。

上面那個路由器有三個網口,其中兩個分別連到兩個子網,第三個連到另一臺路由器,而且這兩個路由器也構成一個單獨的子網 220.1.8.0/24。

現在我們看看主機 220.1.1.2(以下稱主機 A) 如何給主機 220.1.3.20(以下稱主機 B) 發訊息。

從上面圖可知,兩臺主機處於不同的子網中,而且中間經過了兩臺路由器(兩跳)。首先主機 A 查詢自己的路由表(其路由表中並不需要為網段 220.1.3.0/24 配單獨的轉發記錄,直接走的預設閘道器路由器),發現要將分組發給自己的閘道器路由器 220.1.1.1(以下稱為路由器 A)。

路由器 A 的轉發表中會有一條類似下面的轉發規則(假設路由器 A 通過網口 if1 和下面的路由器 B 相連):

image-20220125104548573

路由器 A 拿到資料包首部中的目標 IP 資訊並查詢轉發表後,將分組轉發給網口 if1(IP 地址是 220.1.8.1),經該網口傳給下方的路由器 B(具體是傳給其網口 if2)。

路由器 B 拿到分組後,查詢自己的轉發表,發現需要轉發到網口 if3(假設該路由器下方那個 220.1.3.1 對應的網口叫 if3),這樣分組就進入了目標子網,然後再通過子網內部通訊機制最終傳送給目標主機 220.1.3.20。

現在有個問題,路由器 A 怎麼知道 220.1.3.0/24 的分組需要通過網口 if1 轉發給路由器 B 呢(即路由器 A 中的那條轉發表記錄是怎麼寫進去的)?

可以由網管手動寫入,也可以由路由選擇協議自動計算出來(還記得路由器裡面有個路由選擇處理器嗎)。路由器 B 分管著子網 220.1.3.0/24,但別的路由器不知道這件事啊,所以路由器 B(通過網口 if2)得大聲對外宣告:“所有 220.1.3.0/24 的資料包請發給我!”

子網分層與地址聚合:

你看上面的子網 220.1.3.0/24 是不是感覺它是個 C 類地址?

那如果是 120.1.3.0/24 呢?注意在分類編址中,120 屬於 A 類地址啊(A 類的範圍是 1 - 126),既然是 A 類地址,為何用 24 位元作為網路段呢?

前面說過,分類編址法早就躺在博物館了,現在世界上用的是 CIDR,它是個無類別編址法,所以不要再去想什麼 ABC 了。

另一個問題是,120.1.3.0/24 中的右 8 位元是不是一定只能用於主機呢?

答案是否定的。

子網是可以分層的。子網下面可以再劃分子網。實際上 120.1.3.0/24 並不是一級網路,它歸屬於更大的子網如 120.1.0.0/16(具體怎麼劃分取決於 ISP 自己)。整個網路是如下的樹形結構:

image-20220126200115856

比如我們給 120.1.3.0/24 繼續劃分子網,將最後的 8 位元中前 4 位元作為子網地址,後 4 位元作為主機地址,這樣的子網能容納 2^4 - 2 = 14 臺主機:

image-20220126200227737

注意上面的 128(二進位制:10000000)、192(二進位制:11000000)和 224(二進位制:11100000)都屬於網路段。當然這些子網也可以通過多個路由器形成更復雜的網路拓撲。

這種子網分層使得一個路由器的出口不一定是指向某臺主機,而是指向一個網段(該子網的閘道器路由器),這個網段(子網)是對一批 IP 地址的聚合。

自治系統(Autonomous System,AS)

有上百萬上圖那種子網分佈在世界各地,要想讓這些子網(其內部的主機)之間無障礙地通訊,子網閘道器(路由器)需要進行復雜的路由計算,它必須記錄到任何一個子網應該怎麼走,這不但要求全世界所有的路由器都必須使用同樣的路由選擇演算法,而且會導致路由器的轉發表非常龐大。

就像公司會通過增加部門等組織結構來應對人員規模,因特網也通過對這些路由器本身進行組織化來應對規模複雜性。前面說同一個網段的主機可形成一個子網,這些主機之間可以直接通訊,但無法直接跟子網外部通訊,跟外部通訊是通過閘道器路由器來實現的。類似地,我們將一群路由器(這些路由器一般屬於同一個 ISP 或公司)組織在一起形成一個自治系統,這些路由器之間可直接通訊,而且都知道自己到任何其他路由器的最短路徑,但這些路由器不能跟自治系統外部的路由器通訊,他們必須通過指定的一臺或幾臺閘道器路由器和外面的路由器通訊。同樣,外面的那些路由器也是被組織進其它的自治系統中,所以這種通訊實際上是自治系統之間的通訊。

image-20220126200517239

如圖有 3 個自治系統,其中 AS1 有兩個閘道器路由器 1d 和 1c,AS2 的閘道器路由器是 2c,AS3 的是 3c。自治系統內部的路由器之間直接或間接相連,都知道彼此通訊的路徑。現在假如 1b 子網的主機要和 3b子網的主機通訊,則 1b 查詢自己的路由表,發現目標在本 AS 外面,需要將分組交給閘道器路由器 1c,1c 查詢自己的轉發表後將分組發給 3c,最後由 3c 轉給 3b。

這樣 1b 就不用關心 3b 到底位於 AS3 中的哪個位置,直接把分組甩給閘道器,閘道器再甩給對面的閘道器就行了。

同樣,如果 AS2 中的 2b 要和 AS3 中的 3b 通訊,則需要通過 AS1 轉發。

這裡有個問題,AS1 有兩個閘道器出口,上面說的 1b 怎麼知道應該把分組轉給閘道器 1c 而不是 1d 呢?

這就涉及到自治系統之間的路由選擇。全世界所有自治系統間都執行同樣的路由選擇演算法 BGP(Broder Gateway Protocol,邊界閘道器協議), 通過該演算法,全世界的自治系統之間就能知道如何到達任何一個其他自治系統了,BGP 演算法本身很複雜,大致意思是這樣的:

  1. AS3 告訴鄰居 AS1:通過我可以到 220.1.0.0/16(這個地址段就是 AS3 管理的);

  2. AS1 的閘道器路由器收到訊息後,廣播給本 AS 內部所有路由器:通過我(AS1 的閘道器 1c)可以到 220.1.0.0/16;

  3. 於是這些路由器便在自己的轉發表裡面記錄一條轉發策略,我們拿 1b 為例:假設 1b 到 1c 的最短路徑是 1b -> 1a -> 1c(這是通過 AS 內部路由選擇演算法算出來的),其下一跳是 1a,假設 1b 通過網口 if1 和 1a 相連,則 1b 會在其轉發表中記錄:

    220.1.0.0/16 -> if1

  4. AS1 的另一個閘道器路由器 1d 當然也收到了這條訊息,它在自己的轉發表中記錄一條轉發策略後,還會將這條訊息告訴其鄰居 AS2:通過我(AS1)可以到達 220.1.0.0/16;

  5. 於是 AS2 內部也重複上面的步驟;

當然實際演算法會複雜很多,比如如何選擇 AS3 間的最短路徑等。

一個 ISP(如中國移動) 或公司(如阿里雲)往往會有多個自治系統,每個自治系統需要向相關注冊機構(中國是 CNNIC)申請自治號(ASN),在 AS 間路由選擇演算法中需要用到自治號。

可以從https://tools.ipip.net/as.php檢視某個 IP 所屬的自治系統資訊,比如我們看看 36.152.44.96:

image-20220125164246244

這個 IP 屬於中國移動江蘇分公司的自治系統 AS56046 管理,該自治系統對外宣告的 IP 段是 36.152.44.0/22。注意看右側的 AS Path,它表示自治系統的“36.152.44.0/22都發給我”這條訊息向外傳給了哪些其它自治系統(從右往左看)。我們看看第一條的詳情:

image-20220125165149338

該訊息從中移江蘇的自治系統傳播到中移總部的 AS9808,然後傳播到中移香港,再到英國,最後到美國。

我們再通過 https://whois.ipip.net/AS56046 看看這個 AS 詳情:

ip-07

該自治系統是在 APNIC(亞太網際網路資訊中心,屬於區域序號產生器構,CNNIC 是其下屬的國家序號產生器構。中國移動的 IP 也是由 APNIC 分配的)註冊的,雖然這個 AS 是用於江蘇地區,但註冊公司是中國移動(而不是江蘇分公司)。該 AS 一共持有 1484 個 IPv4 字首,一共有 300 多萬個 IP 地址(注意其持有的 IPv6 有多達 860 多億!)。途中藍色圈圈中是這些 IPv4 地址的使用分佈情況。

這些是 1484 個 IPv4 字首中的一小部分:

image-20220125171433781

需要注意的是,自治系統內部以及自治系統之間的路由選擇策略是不同的。自治系統內部的路徑選擇一般著重考慮效能,而自治系統之間的策略可能更多考慮的是商業和政治等經濟、社會因素。比如聯通使用者訪問行動網路中的伺服器,只會經過聯通和移動的自治系統,不可能經過電信的自治系統中轉,哪怕從技術上來說這樣路徑更短。

IP 地址的分配

《聽說你很懂 DNS?》一文中我們已經提過,全世界的 IP 地址都是由 ICANN(Internet Corporation for Assigned Names and Numbers,網際網路名稱與數字地址分配機構)管理和分配的(具體是其下設機構 IANA),ICANN 將地址分配給各區域序號產生器構(比如亞太的 APNIC),區域序號產生器構再分配給國家/地區序號產生器構(如中國的 CNNIC),區域序號產生器構也可以直接分配給一些大型 ISP(如中國移動)。

目前 IANA 在全球設立五大區域序號產生器構:

image-20220125172512028

其中 APNIC 分管亞太地區。

以下是 IANA 的地址分配情況(部分):

image-20220125172738692

我們發現,IANA 的分配都是以 8 作為子網掩碼,屬於粗粒度分配,各區域拿到這些地址段後,會更細粒度地分配給國家/地區的註冊中心。比如上面將 014/8 分配給了亞太,亞太可以再將 14.192/10 分配給中國的 CNNIC,CNNIC 再將 14.240/12 分配給阿里雲等公司。

上面有個 012/8 是分配給美國貝爾實驗室了,017/8 分配給蘋果了,019/8 給福特了。前面說過,歷史上 IP 地址曾採用分類編址法,這裡面這些 8 位掩碼的都屬於 A 類地址(1-126 的都屬於 A 類),按照當時的想法,這類地址是分配給一些大型公司的(當然也都是美國的),比如 IBM、AT&T、蘋果、福特、通用等,後面很快發現這種搞法行不通,所以廢棄了分類編址法,去官網看下會發現目前絕大部分的“A 類”地址還是分配給了區域序號產生器構。

當然所謂的“分配”並不是免費的,比如阿里雲從 CNNIC 申請了 1000 多萬的 IP 資源,每年需要交一筆不菲的年費給 CNNIC。跟域名一樣,這也是一塊肥水。

另外我們特別看下 47.106.193.19 這個 IP 地址,看其 47 開頭,查詢 IANA 官網發現它歸屬北美的 ARIN,但查一下該 IP 的 whois 資訊發現該 IP 是阿里巴巴(阿里雲)的——難道阿里雲不是從 CNNIC 申請地址,跑到美國找 ARIN 了?

我們看下 whois:

$ whois 47.106.193.19
......

refer:        whois.arin.net

inetnum:      47.0.0.0 - 47.255.255.255
organisation: Administered by ARIN
status:       LEGACY

whois:        whois.arin.net

changed:      1991-01
source:       IANA

# whois.arin.net

NetRange:       47.98.0.0 - 47.112.255.255
CIDR:           47.104.0.0/13, 47.112.0.0/16, 47.100.0.0/14, 47.98.0.0/15
NetName:        APNIC
......
Organization:   Asia Pacific Network Information Centre (APNIC)
RegDate:        2015-04-01
......

% Abuse contact for '47.104.0.0 - 47.111.255.255' is 'ipas@cnnic.cn'

inetnum:        47.104.0.0 - 47.111.255.255
netname:        ALISOFT
descr:          Aliyun Computing Co., LTD
......
source:         APNIC

irt:            IRT-CNNIC-CN
......

% Information related to '47.104.0.0/13AS37963'

route:          47.104.0.0/13
descr:          Hangzhou Alibaba Advertising Co.,Ltd.
country:        CN
origin:         AS37963
......

資訊很長,做了擷取。可以看出,在 1991 年 IANA 將 47/8 分配給了 ARIN,ARIN 在 2015 年將其中的網段 47.104.0.0/13, 47.112.0.0/16, 47.100.0.0/14, 47.98.0.0/15 轉給了 APNIC,APNIC 將其中的某些網段分配給了 CNNIC,阿里雲從 CNNIC 申請了 47.104.0.0/13 這段地址。

內網與 NAT 技術

IPv4 面臨的一個嚴峻問題是地址資源枯竭,雖然上世紀 90 年代 IETF 就開始研發 IPv6,但由於網路層設施涉及到整個網路的所有結點(而不僅僅是端系統),其升級替換的工程量浩大,至今仍未普及。

一個替代方案是採用可複用的私有 IP。和公有 IP 全球唯一性不同,私有 IP 僅在某個範圍(區域網)內是唯一的,出了這個範圍,不能保證其唯一性。

ICANN 為A、B、C 三類地址各保留了一個私有 IP 段,分別是:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16。

注意到 172/8 號段在 1993 年分配給 ARIN(美國網際網路號碼註冊中心) 了,而 172.16 私網是在 1996 年的RFC1918中定義的,也就是說這段號碼是後面從 ARIN 中收回的。

我們現在無論是在家庭還是公司中基本都是用的私有 IP,大致是這樣:

image-20220126201257700

一個問題是,既然內網的 IP 地址不是全球唯一的,無法在公網上使用,那當我通過瀏覽器在內網訪問百度伺服器,百度伺服器的資料是怎麼找到我的內網這臺電腦的呢?

內網和公網的通訊需要用到叫做 NAT(Network Address Translation,網路地址轉換)的技術。NAT 的原理是,NAT 路由器的兩塊網路卡,一塊連線內網,使用內網 IP,一塊連線外網,使用外網 IP。路由器接收到內網主機傳送的分組後,取出分組首部的 IP 資訊,將其中的源 IP(內網 IP,上面的 192.168.1.10)替換成自己的外網 IP(138.20.11.10),並且生成一個該路由器尚未使用的新埠號(上面的 4590)替換掉源埠號(3000),用這個新的 IP 和埠號替換掉分組中的源 IP 和埠,然後轉發出去。同時,路由器將內網的 IP+埠號與新的外網 IP+埠號的對映關係記錄到 NAT 轉發表中。

目標伺服器(如百度)接收到分組後,取出首部一看,該分組是 138.20.11.10 發來的,即它認為是那個路由器發的,而不知道其實是路由器後面的區域網主機發的。伺服器返回的分組的目標 IP 和埠自然是 138.20.11.10 和 4590,該分組自然會被上面那個 NAT 路由器收到。NAT 路由器從外網收到分組後,拿到目標 IP 和埠號,去 NAT 轉換表查,發現對應的內網 IP 和埠是 192.168.1.10 和 3000,於是修改分組的首部,最後通過區域網傳送給目標主機。

image-20220126182245652

NAT 轉換表

看到這樣的騷操作你是不是不由得豎起大拇指?人類的大腦終究還是聰明的,自己挖的坑自己"完美"地填平了。

別急,還有更騷的操作。

我家中使用無線路由上網,分配的區域網 IP 是 192.168.1.6,閘道器是 192.168.1.1。閘道器路由器的出口接到中國移動的路由器,我們看看那邊分配的 IP:

image-20220126094631508

說我的“外網” IP 是 100.64.148.126。處於好奇,我用 ip138 查了下這個 IP 的詳情:

image-20220126095021496

納尼?這也是個區域網 IP?在三個私有 IP 段沒見過 100 開頭的啊?

於是趕緊跑到 IANA 官網查一下,在底下腳註看到這麼句話:

100.64.0.0/10 reserved for Shared Address Space [RFC6598].

說是在 RFC6598 中將其定義為共享地址空間,翻開該 RFC,有這麼一段話:

This document requests the allocation of an IPv4 /10 address block to be used as Shared Address Space to accommodate the needs of Carrier Grade NAT (CGN) devices. It is anticipated that Service Providers will use this Shared Address Space to number the interfaces that connect CGN devices to Customer Premises Equipment (CPE).

Shared Address Space is distinct from RFC 1918 private address space because it is intended for use on Service Provider networks.However, it may be used in a manner similar to RFC 1918 private address space on routing equipment that is able to do address translation across router interfaces when the addresses are identical on two different interfaces. Details are provided in the text of this document.

如果你跟我一樣英文不好,就不要去鑽研上面這麼一大段話了,大致意思是 100.64.0.0/10 這個網段是專門給網路服務提供商(ISP)用的私有 IP。因為 ISP 的客戶(家庭、公司)已經在使用 RFC1918 定義的三個私有 IP 段了(就是10、172.16、192.168),所以 ISP 就不能使用這三個網段做私有 IP 了——否則當你家的路由器遇到目標 IP 是 192.168.1.10 它到底是要轉給內部呢還是轉給 ISP 呢?

ISP 為啥也要玩區域網呢?還不是因為 IP 不夠用嘛!你想中國移動、中國電信有多少客戶(家庭、公司),得要多少公網 IP 才能玩得轉?所以他們在區域網外再套一層區域網,於是我們和外網的通訊就變成這樣子:

image-20220126103214803

然而,NAT 並不是一項完美的技術。

從上面圖就能看出來,NAT 路由器要去修改分組首部,並維護 NAT 轉換表,這多少會帶來些許的網路時延,特別是在兩層 NAT “加持”下。

路由器本來屬於網路層裝置,而埠號是放在傳輸層的——意味著這個網路層裝置需要取出並修改傳輸層首部,這在設計上違背了分層原則。

區域網主機用的私有 IP,這導致它無法直接對外提供服務(想想外面的主機怎樣去定址 192.168.1.10?),因而無法直接實現 P2P。要想在這種情況下實現 P2P,需使用 NAT 穿越技術(此處不詳細討論這種技術,感興趣的可自行百度)。另外 NAT 也限制了物聯網的發展,比如你家有一臺美的的電暖氣,該電暖氣能夠聯網控制,但由於電暖氣位於區域網,用的是私有 IP,它無法直接和你的手機相連,必須通過美的的中央伺服器中繼。

可能更大的影響是,NAT 技術限制了 IPv6 的普及速度。試想如果沒有 NAT 技術,每臺主機想要聯網就必須持有外網 IP,這種情況下全世界的 IPv4 地址早已枯竭,逼迫各大運營商和公司去實施 IPv6。而現在有了 NAT,公司可以玩內網,運營商也可以玩 NAT,錢照賺不誤的情況下,誰會有動力花大財力去升級裝置?公司(ISP 當然屬於公司)是以盈利導向的,在當前大有盈利的情況下,傻子才去做那種短期看不到回報的大手筆投資。所以世界上 IPv6 的推進非常緩慢,即便中國在很早(上世紀 90 年代末)就開始關注 IPv6 了,但至今仍未普及。

現實中的網路

想想你家裡是怎麼接入網際網路的。

首先你要打電話找一家電信運營商(接入 ISP,比如中國移動),他們會就近網點派人上門給你拉光纖,裝個一體化路由器(光貓和路由器一體化裝置),設定或者不設定一個 IP(一般是通過 ISP 的 DHCP 伺服器自動獲取),然後你交了錢,就能上網了。

假如你現在訪問 www.baidu.com,電腦通過 DNS 查詢獲取 IP 後,請求是如何在網路上一路狂奔走到百度的某個機房的呢?

這取決於 DNS 返回的 IP 歸屬情況。目前大公司的權威 DNS 都有智慧解析的能力,假如你是在武漢,用的行動網路,如果百度在武漢有資料中心,而且還跟移動有合作,那它多半會返回武漢移動的 IP 給你,此時你的請求不需要出城,也不需要跨運營商網路,請求進入接入 ISP (就是給你辦寬頻的那家)網路後,經過武漢移動的某個自治系統可能就直接到達目的地了。

假如百度在武漢沒有移動資料中心,那他可能會返回一個其它城市的移動 IP 給你,比如江蘇南京的移動 IP。此時你的請求進入接入 ISP 網路後,很可能通過都會網路而後交換進入骨幹網,然後跑到南京。具體怎麼走取決於中國移動的網路基礎設施的架構。

但如果返回的是百度在武漢的聯通資料中心 IP 呢?此時資料包能直接從武漢行動網路進入武漢聯通網路嗎?很可能不能。移動和聯通屬於兩家 ISP,如果他倆在武漢沒有實現互聯互通的話,資料就必須出城跑到能互聯互通的那個城市(假如是廣州),然後在那個城市進入聯通網路,然後再跑回武漢百度的資料中心。這就是為啥 CDN 會盡量選擇和客戶在同一家網路的結點返回——不同家的網路,即使客戶和伺服器面對面站著,資料包也有可能要跑遍大半個中國。

假如我們要訪問美國的某個網站呢(雖然大部分訪問不了)?這就比較複雜了。首先你的資料包進入接入 ISP 的自治系統後,幾番路由,進入中國移動的骨幹網,然後通過某個核心節點(每家骨幹網一般會在一些省會城市建設核心節點,其中一些核心節點能夠通往國外)經由海底光纜傳輸到美國的某家運營商的核心節點(具體取決於目標 IP 歸屬),而後一層一層逐步路由到目的地。

整個網路從邏輯上大致是下面的等級關係:

ip-08

最接近使用者的是接入 ISP 網路(一般就是給你辦寬頻的那家),接入 ISP 會連入區域 ISP 網路,區域 ISP 網路進一步接入到第一層 ISP。區域 ISP 之間、第一層 ISP 之間一般會互聯。區域 ISP 一般也會接入到多家第一層 ISP。

不過這個理論圖你瞭解下就行了,實際中關係和概念可能都不同,每家運營商網路架構可能都不同,比如有些運營商會採用都會網路-省網-骨幹網的層次架構,有些則直接採用都會網路-骨幹網架構。

這裡面有個 IXP,中文叫網際網路交換中心(Internet Exchange Point),目的是讓多家運營商(網路接入商 ISP、內容提供商 ICP)在這裡交換資料,而不用為了從一個服務商網路進入另一個服務商網路就要繞地球大半圈——因為現實中不同運營商之間一般都是隻在頂層(骨幹網)互聯互通。IXP 是本地化交換中心,什麼意思呢,剛才說假如移動和聯通在武漢沒有實現互聯互通,那麼移動使用者想要訪問同一個城市的聯通網路上的伺服器,就得跑到遙遠的地方(意味著要經過很多個路由,而每個路由轉發都會帶來額外時延)跨網後再跑回來,大部分時間都是在浪費生命,而採用 IXP 方案的話就是在武漢建立一個本地交換中心,各運營商之間都可以在這裡直接互通。IXP 在歐美很火,但在中國一直髮展不起來。和歐美 ISP 百花齊放不同,中國主要就三座大山(後面廣電也進入了),各有各的利益考慮。打個比方,假如移動的網路設施比電信少很多(實際上就是少),大部分移動使用者訪問的網路服務都要經過電信的網路,這時候移動是要給電信錢的。搞了 IXP 後,雖然能同城跨網訪問了,但 IXP 國際慣例是雙方免費互通,電信願意?所以這種事情就跟中國的攜號轉網一樣,往往是剃頭的挑子一頭熱,甚至兩頭都不熱。中國曾經在政府主導下搞了北上廣三個國家級交換中心,但沒搞起來。這些大佬基本是通過頂層直連的方式互通。

相關文章