為什麼平面的圖會崩潰
在這個段位前面幾篇裡,我們把網路當成一張平面的加權圖,讓每一台路由器都算出通往每一個目的地的最低成本路徑——在連結狀態繞送裡靠氾洪一張完整地圖再跑 Dijkstra,或在距離向量繞送裡用 Bellman-Ford 的想法和鄰居交換距離向量。這對一座校園或一家公司運作得很完美。但它對整個網際網路行不通,原因純粹是算術。外面有數十萬個不同的目的網路。一個平面的方案會逼每一台路由器都去認識其中的每一個。
三種代價會同時爆炸。第一是記憶體:每一台路由器都需要一張對地球上每一個網路都有一筆條目的繞送表,而由它導出的轉送表也會一樣龐大。第二是計算量:Dijkstra 的執行時間隨圖的大小成長,所以每當有一條連結閃爍一下,就要在一張行星大小的地圖上重建整棵樹,根本毫無希望。第三是擾動:在單一一個平面網域裡,世界某個角落一條跳動的纜線,會把一輪新的更新漣漪般地推送給各地每一台路由器。如果它從不靜止下來,這東西就根本無法收斂。
階層:把世界劃分成區域
第一個工具是階層式繞送。我們不用一張平面圖,而是把路由器分組成區域,然後分兩階段繞送:在自己的區域內走細節,在區域之間走摘要。想想你怎麼給人指路。在自己的社區裡你認識每一條街;要去另一座城市,你根本不會去背它的街道——你只知道怎麼上高速公路,並相信每座城市會在你抵達後處理自己的道路。路由器也一樣:附近是完整細節,其他地方是粗略摘要。
在真實的網際網路上,這個區域單位是自治系統(AS):一個由單一管理單位掌管的網路,像一所大學、一家公司或一家網路供應商,自行決定它內部的繞送。在一個 AS 內部,你跑一個內部閘道協定——也就是你上一篇認識的 OSPF 或 RIP——把自己這個區域的每一個細節都學起來。在自治系統之間,則由一個完全不同的協定(BGP,下一段位的主題)只攜帶摘要:不是一個網路裡的每一條街,而是「這一整塊位址住在那邊」這個事實。這種兩層的切分,正是為什麼東京的一台路由器不會、也不需要去知道巴西某個網路內部是怎麼接線的。
OSPF 其實還把階層往更深推一層,落在單一網域內部。一個大型的 OSPF 網路被切成多個區域(area),全都掛在一個中央的骨幹區域上。路由器只在自己的區域內氾洪它們詳細的連結狀態地圖,而區域邊界路由器只把摘要注入到邊界另一側。所以即使在同一家公司裡,某個區域出問題也不會逼全公司每一台路由器都重算。階層是碎形的:同一個「分割再摘要」的想法,被以更小的尺度再套用一次。
彙整:許多位址,一條路由
階層組織路由器;路由彙整則縮小每一台路由器必須記住的東西。這個把戲完全建立在 IP 段位的 CIDR 定址上。因為位址是以連續的前綴發放的,一台路由器可以把許多具體的網路摺疊成一個摘要前綴,然後只通告那一個。假設一家 ISP 擁有 200.23.16.0/20,並把它切成許多更小的客戶網路,像 200.23.16.0/23、200.23.18.0/23 等等。對世界其他地方,這家 ISP 不會把每一小塊都通告出去。它只通告一行:「把任何符合 200.23.16.0/20 的封包送來給我」,再在內部把細節分清楚。
Without aggregation: 8 routes leak to the world 200.23.16.0/23 -> ISP 200.23.18.0/23 -> ISP 200.23.20.0/23 -> ISP 200.23.22.0/23 -> ISP ... 4 more /23s ... With aggregation: 1 summary route covers them all 200.23.16.0/20 -> ISP (a /20 spans 16 of these /23 blocks) The outside world stores ONE entry instead of eight. The ISP keeps the detailed /23 routes only inside itself.
彙整只有在位址以階層方式配發時才行得通——也就是當一家供應商的各個區塊真的是連續的、能被摘要成一個。這正是為什麼供應商要從自己的範圍裡撥位址給客戶:它讓整個網際網路的繞送表不至於爆掉。誠實的限制是:真實世界很雜亂。一個換了供應商卻保留舊位址區塊的客戶,會在某人乾淨的摘要上戳出一個洞,於是那個更具體的前綴又必須被單獨通告一次。彙整是定址系統所鼓勵的一種傾向,而不是它能保證的一條定律。
兩個前綴都吻合——最長的那個贏
摘要製造出一個明顯的難題。如果一台路由器同時握有一條通往 200.23.16.0/20 的概括路由,以及一條住在它裡面、通往 200.23.18.0/23 的具體路由,那麼一個前往 200.23.18.5 的封包就同時吻合這兩條。哪一筆會贏?這條規則你在 IP 段位第一次見過,叫做最長前綴吻合:路由器永遠挑出吻合位元最多的那一筆——最具體的那條路由。概括的 /20 是安全網;精確的 /23 則是只要它適用、就會凌駕在上面的那條繞道。
最長前綴吻合正是讓階層與彙整能安全地結合在一起的東西。你可以通告一條概括的摘要,同時又用更細的路由在本地凌駕它,而轉送永遠做最具體的那件事。它也說明了一個微妙的重點:彙整從不會讓轉送變錯,只會變粗。一條被摘要過的路由,也許會把封包送上一條比完整地圖會挑的稍微不那麼理想的路徑,但它仍然會抵達一台知道後半段怎麼走的路由器。我們拿一點路徑最佳性,換取表格大小的巨幅縮減,而在行星尺度的網路上,這是一筆划算的交易。
預設路由:拿不定主意時,就往上送
彙整有一個輝煌的極限情況。最粗的摘要會是什麼?一個長度為零的前綴——0.0.0.0/0——它吻合存在的每一個位址。這就是預設路由,那唯一一筆寫著「對於任何我沒有更具體路由可走的封包,往這個方向送」的條目。依照最長前綴吻合,預設路由是每一場較量的輸家:任何真實的前綴都比它更具體,所以預設路由只有在沒有別的吻合時才會啟動。它是表格最底部那條包山包海的條目。
預設路由是「有紀律的無知」的終極表現,也是你家路由器之所以還能活下來的原因。你的路由器並不儲存一張網際網路的地圖。它只握有寥寥幾條本地路由,加上一條指向你供應商的預設路由,而那唯一一行就涵蓋了世界其餘的全部。這個模式沿著階層往上重複:一間小辦公室把它的預設路由指向它的 ISP;那家 ISP 把它的預設路由指向一家更大的 ISP;如此往上,直到你抵達網際網路的最頂端——核心裡那些自治系統,它們不帶任何預設路由,因為它們真的必須直接知道每一個目的地。
- 一個前往某個公開網站的封包抵達你筆電的路由器。路由器在它的表裡尋找與目的地吻合的最長前綴。
- 它找不到具體的路由——這個目的地不在你的家用網路上,也不在路由器知道細節的任何區塊裡。唯一吻合的條目是 0.0.0.0/0,也就是預設路由。
- 依最長前綴吻合,預設路由因為是唯一的吻合而勝出,所以路由器把封包轉送到它的下一跳:你 ISP 的路由器。
- 你的 ISP 用它自己(更大)的表重複同樣的邏輯,也許用它自己往更大供應商的預設路由,於是封包就沿著階層往上爬,直到抵達一台握有通往該目的地之具體路由的路由器。
完整的圖像:小小的表,行星尺度的觸及
退一步,兩個想法就扣合在一起。階層把網路分割開來,讓每一台路由器完整地知道自己的區域,其他地方則只知道摘要。建立在 CIDR 上的彙整,讓那些摘要可以小到極點——一個短短的前綴代表數千個位址——而最長前綴吻合則保證新增一條摘要永遠不會凌駕一條更具體的本地路由。預設路由則是把彙整推到它邏輯上的盡頭:用一行表達「其餘的一切,往那邊」。
這是離開網域內繞送的自然橋樑。這個段位裡的一切——把網路建模成圖、連結狀態與距離向量演算法、RIP 與 OSPF——都活在單一一個自治系統內,那裡有一位管理者想要最便宜的路徑。一旦繞送跨越到自治系統之間,目標就不再是「最短」,而變成「我的商業政策與合約所允許的任何東西」。那個世界有它自己的協定,BGP,而那正是下一段位開始的地方。