從演算法到協定
在前三篇裡,你從頭打造了那套機制。你學到轉送是快速、本地的動作——用一張表把封包推出正確的介面;而路由則是較慢、分散式的工作——一開始去建出那張表。你把網路模型化成一張加權圖,去求最小成本路徑。接著你看到兩大類:鏈路狀態,每台路由器都學到整張地圖、在本地跑 Dijkstra 演算法;以及距離向量,路由器只跟鄰居交換距離表、永遠看不到整張地圖。你還沒看到的,是這兩個概念穿上真實的衣服。這一篇要補上這點。
RIP 與 OSPF 都屬於一個叫做內部閘道協定(IGP)的類別。「內部」這個詞很重要:這些協定運行在單一一個管理網域之內——某一個組織的網路,那裡人人互相信任、共享「找出好路徑」這個共同目標。這跟整個網際網路上、組織「之間」那種雜亂又互不信任的路由是完全不同的世界,那個世界需要一個完全不同的協定,並會在後面的篇章另有專文。眼下,請想像一個校園或一家公司的網路:數十台路由器,全都互相合作,全都由同一個團隊管理。
RIP:穿上工作服的距離向量
RIP,路由資訊協定,就是把距離向量路由做成具體實作,而且簡單得討喜。它的成本度量是想像得到最粗糙的東西:跳數,也就是封包必須跨越的路由器數目。每一條鏈路的成本剛好都是 1,不管它是條老舊遲緩的線、還是條飛快的光纖。所以 RIP 心目中的最佳路徑單純就是跳數最少的那條,這很誠實但也很天真:兩跳但走慢速鏈路的路徑,可能遠比三跳但走快速鏈路的差,而 RIP 分不出差別。那份簡單在 1980 年代正是重點所在,也是為什麼 RIP 至今仍出現在小型、簡單的網路中。
RIP 的運作方式跟你那篇距離向量所描述的一模一樣。每台路由器保存一張「目的地與到各目的地距離」的表,大約每 30 秒就把整張表廣播給直接相連的鄰居。一個聽到「我能以距離 4 抵達網路 X」的鄰居,會考慮把那台路由器當作前往 X 的下一跳、距離為 5。表一圈圈向外擴散,距離逐漸穩定下來,過一陣子之後,大家就對「到各處的最短跳數」達成共識。沒有任何一台路由器看得到整張地圖;每台都只是信任鄰居反覆告訴它的事,就像辦公室八卦最終會匯聚成一個穩定版本的真相。
RIP 的兩個細節純粹是你已經研究過的那些危險所導致的結果。第一,RIP 把無窮定義成 16。一條 16 跳的路徑會被當成不可達。那個小小的上限是對計數至無窮的刻意解藥:藉著為距離能爬多高設一個帽子,RIP 保證一條壞掉的路由會迅速瓦解,而不是永遠往上數。代價是對規模的殘酷誠實:RIP 根本無法描述一張寬度超過 15 跳的網路,這正是它只待在小型部署裡的一大原因。第二,RIP 倚賴你見過的那些解藥,尤其是水平分割(絕不把一條路由通告回你當初從它那裡學來的那個鄰居)與毒性反轉,從一開始就防止迴圈成形。
OSPF:工業規模的鏈路狀態
OSPF,開放最短路徑優先,是長大了、被派去做正經工作的鏈路狀態路由,也是大型實際網路真正在跑的東西。「開放」表示它是公開、不綁特定廠商的標準;「最短路徑優先」不過是 Dijkstra 演算法的別名。它的核心承諾正是你學過的:每台路由器最終都握有一張整個網域、完全相同的地圖,然後各自獨立地在那張地圖上跑 Dijkstra 演算法,算出自己到每個目的地的最短路徑樹。因為大家都從同一張地圖計算,他們的結果彼此一致,而一致的結果意味著沒有迴圈。
大家是怎麼拿到同一張地圖的?每台路由器把描述自己鏈路的小型通告——稱為鏈路狀態通告(LSA)——洪泛給網域中其他每一台路由器。「洪泛」就是你先前見過那種刻意、受控的擴散:當一台路由器收到一份它沒見過的 LSA,就把它複製、從其他每一個介面送出去,於是消息又快又可靠地傳遍整個網域。每台路由器把所有 LSA 收進一個鏈路狀態資料庫裡,那就是那張共享的地圖。關鍵在於:LSA 只在有東西變動時送出,外加一個緩慢的週期性更新,而不是 RIP 那種持續不斷的嘮叨。所以 OSPF 對一條失效鏈路的反應遠在一秒之內,而 RIP 可能慢吞吞地拖上數十秒、等它下一次廣播。
OSPF 也修補了 RIP 最粗糙的弱點:它的鏈路成本是可設定的,不是一律 1。依慣例成本會設成與頻寬成反比,於是一條 1 Gbps 的鏈路比一條 100 Mbps 的便宜,Dijkstra 自然會偏好那條較粗的管子,即使要多走一跳。這就是「數門」與「真正去量走廊」的差別。結果是一個能擴展到大型網路、收斂迅速、並依真實鏈路品質而非鈍重跳數來繞送的協定。代價是複雜度:OSPF 比 RIP 有更多運作零件、更多封包型別、更多設定,而這正是你從玩具走向主力馬時所做的取捨。
兩個協定,並排來看
把兩個協定在關鍵點上彼此對照會很有幫助。你會注意到幾乎每一項差異都能直接追溯到底下的演算法家族:RIP 繼承了距離向量的簡單與它的迴圈危險,而 OSPF 繼承了鏈路狀態較豐富的地圖與它較快、無迴圈的收斂。協定與其說發明了新行為,不如說是把演算法落實成一份真實的時程、一種真實的訊息格式,以及一套真實的故障政策。
RIP OSPF ---------------------------------------------------------------------- algorithm family distance-vector link-state cost metric hop count (every link=1) configurable (~1/bandwidth) what's exchanged whole distance table link-state ads (LSAs) to whom only direct neighbors flooded to all routers how often every ~30 s (periodic) on change + slow refresh sees full map? no yes (link-state database) path computation Bellman-Ford idea Dijkstra (shortest path) 'infinity' 16 hops (caps loops) n/a (loop-free by design) max network size ~15 hops (small) large convergence speed slow (tens of seconds) fast (sub-second)
一次故障真正的樣子
感受兩者差別最清楚的方式,是弄斷一條鏈路、看著每個協定如何應付。想像一台路由器原本是通往某個網路的唯一路徑,突然失去了那條鏈路。路由協定的全部意義就在於:偵測到中斷、把壞消息散播出去、重新計算,並穩定到一條新的可用路徑上。這所花的時間叫做收斂時間,在這段期間封包可能被丟棄、甚至短暫地繞圈。底下是 OSPF 處理它的方式。
- 偵測:路由器注意到鏈路斷了,通常在數毫秒之內,因為 OSPF 鄰居會互相交換極小的「Hello」封包,連續漏掉幾個就代表那個鄰居或鏈路已經死了。
- 通告:它產生一份新的 LSA,內容是「我這條鏈路沒了」,並從每個介面把它洪泛出去,於是消息在整個網域裡飛奔。
- 更新地圖:每台路由器都收到那份 LSA,並修改自己的鏈路狀態資料庫,把那條邊從共享的圖裡刪掉。現在大家手上都握有同一張、已修正的地圖。
- 重新計算:每台路由器各自獨立地在新地圖上重跑 Dijkstra,找出新的最短路徑。因為所有地圖一致,所有新路徑也彼此一致,於是不會形成迴圈。
- 收斂完成:轉送表被改寫,封包沿著新路徑流動,從鏈路失效那一刻算起通常遠在一秒之內。
把同一次故障丟給 RIP 跑一遍,對比就觸目驚心了。沒有壞消息的即時洪泛,只有路由器每 30 秒慢吞吞地廣播自己的表,於是變動一次一個鄰居地向外爬。更糟的是,這正是會冒出計數至無窮風險的情境:一個仍然相信那條死路的鄰居,可能把一條過時、略長的路由餵回來,距離就一點一點往上爬,直到終於撞上 RIP 那個 16 的天花板、路由才被宣告死亡。那些解藥——水平分割與毒性反轉——能限制這件事,卻無法根除它。這不是 RIP 寫得爛;這是「路由器永遠看不到整張地圖、只能靠鄰居八卦推理」這件事無可避免的代價。
這把你帶到哪裡
你現在把抽象的演算法「與」它們在現實中的化身握在同一隻手裡,而這正是這一級的重點。兩大家族不再只是黑板上的概念:距離向量就是 RIP,嘮叨地散播它的表、把距離封頂在 16;鏈路狀態就是 OSPF,洪泛 LSA、建出一張共享地圖、又快又無迴圈地跑 Dijkstra。每個協定所做的每一個選擇,都能追溯到它底下的演算法、以及你上一篇研究過的那些危險——尤其是計數至無窮。
在最後一篇之前,有一個誠實的限制要先標出來。RIP 與 OSPF 都是內部閘道協定:它們假設只有一個彼此合作的網域,那裡人人共享一張地圖、或信任彼此的八卦。這個假設在整個網際網路上根本不成立——那裡是一些可能互相競爭、互不信任、各自保守祕密的獨立公司,卻仍必須交換流量。網域「之間」的路由是一場規則不同的不同遊戲,並有它自己的協定。這一級的最後一篇會升上那個更大的尺度,去問:當網路不是一個校園、而是整個地球時,路由要如何保持可管理。