說明:本文沒有源碼分析的內容,然而我認爲能理解本質比能看懂源碼更有用,由於理解了本質以後,你也許就不用再看源碼了,你甚至均可以寫源碼了。這就是Linux內核和Cisco的網站中包含大量文檔的緣由。
引:路由是互聯網的一個核心概念,廣義的講,它使分組交換網的每一個節點彼此獨立,經過路由耦合在一塊兒,甚至在電路交換網中,虛電路的創建也依賴路由,路由就是網絡中數據通路的指向標。狹義的講,路由專指IP路由,它支撐着整個IP網絡。
因爲IP是數據報網絡,它是不創建鏈接的,所以IP分組是一跳一跳被轉發,通路是經過路由信息一跳一跳的被打通的,所以路由直接關係到整個基於IP的網絡的連通性。因爲IP協議沒有方向,甚至它都沒有會話的概念,所以路由必然要是雙向的,不然數據就有去無回了(有人提倡用NAT來解決反向路由問題,實際上NAT在公共核心網絡上口碑十分不咋地,它甚至破壞了IP協議的原則,記住,NAT通常只用於端點)。互聯網如此之大,每一個路由器上的路由信息會很是之多,路由器是怎麼在海量的路由信息中用最快的速度-顯然很重要-檢索出本身須要的呢?另外如此海量的路由信息又是怎麼生成的呢?本文着重回答第一個問題,關於第二個問題請參考《Internet路由結構(第二版)》(Cisco Press,想看就趕快買,不買就買不到了,Cisco有幾本書真的很火爆,老是很差買)
1.基本概念
路由的概念:路由是一種指向標,由於網絡是一跳一跳往前推動的,所以在每一跳都要有一系列的指向標。實際上不只僅是分組交換網須要路由,電路交換網在建立虛電路的時候也須要路由,更實際的例子,咱們平常生活中,路由無處不在。簡單的說,路由由三元素組成:目標地址,掩碼,下一跳。注意,路由項中其實沒有輸出端口-它是鏈路層概念,Linux操做系統將路由表和轉發表混爲一談,而實際上它們應該是分開的(分開的好處之一使得MPLS更容易實現)。
路由項經過兩種途徑加入內核,一種是經過用戶態路由協議進程或者用戶靜態配置配置加入,另外一種是主機自動發現的路由。所謂自動發現的路由其實是「發現了一個路由項和一個轉發表」,其含義在主機某一個網卡啓動的時候生效,好比eth0啓動,那麼系統生成下列路由表項/轉發項:往eth0同一IP網段的包經過eth0發出。
路由表:路由表包含了一系列的表項,包括上述的三元素。
路由框架的層次:路由大體分爲兩個要素,也能夠當作兩個層次。第一個層次是路由表項的生成;第二個層次是主機對路由表項的查找。
路由表項生成算法:生成路由表項的方式有兩種,第一種是管理員手工配置,第二種爲經過路由協議動態生成。
路由查找算法:本文着重於主機層面上對路由表項的查詢算法。畢竟這是一個純技術活兒...相反的,路由協議的實現和配置更講究人爲的策略,若是你人爲配置RIP或者OSPF只須要配幾條命令就OK了,那麼配一個BGP試試,它講究大量的策略,不是純技術能解決的。若是有時間,我會單獨寫一篇文章談路由協議的,可是今天,只談路由器/主機對路由表項的查找過程。
這個過程很重要,若是路由器的查找算法效率提升了,那麼很顯然,端到端的延遲就下降了,這是必定的。
2.Linux的哈希查找算法
這是Linux操做系統的經典的路由查找算法,直到如今仍是默認的路由查找算法。然而它很簡單。因爲它的簡單性,內核(kernel)開發組一直很推崇它,雖然它有這樣那樣的侷限性,但因爲Linux內核的哲學就是「夠用便可」,由於Linux幾乎歷來不被用於專業的核心網絡路由系統,所以哈希查找法一直都是默認的選擇。
2.1.查找過程
查找結構以下圖所示:node
查找順序以下圖所示:linux
爲了實現最長前綴匹配,從最長的掩碼開始匹配,每個掩碼都有一個哈希表,目的IP地址哈希到這些哈希表的特定的桶中,而後遍歷其衝突鏈表獲得最終結果。算法
注意,哈希查找算法是基於掩碼的遍從來實現嚴格的最長前綴匹配的,也就是說若是一條最終將要經過默認網關發出的數據報,它起碼要匹配32次才能獲得結果。這種方式十分相似於傳統的Netfilter的filter表的過濾方式-一個一個嘗試匹配,而不像HiPac的過濾方式,是基於查找的。接下來咱們會看到,高性能的路由器在查找路由的時候使用的都是基於查找型數據結構的方式,最經常使用的就是查找樹了。
2.2.侷限性
咱們知道,哈希算法的可擴展性一直都是一個問題,一個特定的哈希函數只適合必定數量的匹配項,幾乎很難找到一個通用的哈希函數,可以適應從幾個匹配項到幾千萬個匹配項的情形,通常而言,隨着匹配項的增長,哈希碰撞也會隨着增長,而且其時間複雜性不可控,這是一個很大的問題,這個問題阻止了哈希路由查找算法走向核心專用路由器,限制了Linux路由的規模,它根本不可能使用哈希來應對大型互聯網絡或者BGP之類的域間路由協議產生的大量路由信息。
核心路由器上,使用哈希算法無疑是不妥的,一定須要找到一種算法,使得其查找的時間複雜度限制在一個範圍(咱們不關心空間複雜度,這和端到端用戶的體驗沒有關係,只和他們花的錢多少有關,花10萬買的路由器有4G內存,花100萬買的路由器則支持64G內存...)。咱們知道,基於樹的查找算法能夠作到這一點,實際上,不少的路由器都是使用基於樹的查找算法來實現的。咱們先從Linux的trie樹開始。便於查閱代碼(雖然本文不分析代碼...)。
3.Linux的LC-Trie樹查找算法
trie算法分爲三大塊,第一塊是查找,第二塊是插入/刪除,第三塊是平衡。咱們首先先無論其名稱爲什麼這麼叫,也沒必要非要去深刻理解一下Trie樹的概念,直接實踐就是了。雖然不少的教科書都喜歡最後講查找型數據結構的插入,而我這裏卻要先說插入,由於一旦你明白了插入,查找就不言自明瞭,另外,講完插入以後,接下來我要說的是trie樹的平衡以及多路操做,由於這樣的話,最終的查找纔會變得高效。咱們權當高效的查找操做是一個必然結果吧。
3.1.基本理論
很很差意思,這裏沒什麼理論,一切都很簡單。咱們能夠經過電話號碼來認識trie樹,trie樹本質上是一棵檢索樹,和全球電話號碼簿同樣,咱們知道,電話號碼有三部分組成:國家碼+地區號+號碼,好比086+372+5912345,若是從美國撥出這個號碼,首先要決定送往哪一個國家,所要作的就是用肯定位數的國家碼和出×××換機的轉發表的國家碼部分進行匹配,發現086正好是中國,而後該號碼到達中國後,再匹配區號,發現要送往安陽市,最後到達安陽市,而後將請求發往5912345這個號碼。
如今的問題是,在每個環節如何使用最快的方式檢索到請求下一步要發往哪裏?我想最好的方式就是使用「桶算法」,舉個例子,在美國的電話請求出口處放置一張表,表項有X個,其中X表明全球全部國家和地區的總和,中國的國家碼是086,那麼它就是第86個表項,這樣直接取第86個表項,獲得相應的交換信息,電話請求經過信息中指示的鏈路發往中國...
另一個例子就是計算機的頁表,這個咱們在3.3節再談。
trie樹,其實和上述的結構差很少,只不過上述結構的檢索分段是固定的,好比電話號碼就是3位10進制數字等,且匹配檢測索引的位置也是固定的,好比電話號碼的地區號就是從第4位十進制數字開始等。對於trie樹而言,須要檢測的位置不是固定的,它用pos表示,而檢測索引的長度也不固定,它由bits表示,咱們把每個檢測點定爲一個CheckNode,它的結構體以下:
CheckNode{
int pos;
int bits;
Node children[1<<bits];
}
union Node{
Leaf entry;
CheckNode node;
}
圖解以下:編程
可見pos和bits是一個CheckNode的核心,pos指示從哪一位開始檢測,bits指示了孩子結點數組,直接取key[pos...pos+bits]便可直接取到孩子結點。windows
3.2.trie樹的插入
我覺得,研究一種樹型結構的時候,首先理解其插入算法無疑是最好的,然而不少的教科書都是從檢索開始,而後將插入操做一筆帶過,這是很不妥的。我認爲只要把插入操做理解深入了,接下來的查詢和刪除就很簡單了,畢竟插入是第一步!插入雖然重要,可是想學習的人不要認爲它很難,要知道,只要是人想出的東西,理解它們都不會很難,難的是什麼?難的是你不會首先想不出來!插入應該怎麼進行呢?:
第一步,若是一個CheckNode節點都沒有,則建立根CheckNode節點,而且建立一個葉子,結束。注意,每個路由項都是一片葉子。若是已經有了根CheckNode,則須要計算新節點插入的位置。
第二步,計算插入位置前的位置匹配。步驟以下:
根據已有CheckNode的pos/bits信息,從根開始執行一系列比較:
1).取出根CheckNode
2).設當前CheckNode爲PreCheckNode
3).判斷是否須要繼續匹配。
4).若是須要繼續匹配,則看看本身是其哪一個孩子或者該孩子的分支,而且取出該孩子Child-CheckNode爲當前CheckNode,回到2。
5).若是不須要繼續匹配,退出匹配過程
其中判斷CheckNode是否須要繼續匹配其Child-CheckNode的算法以下:數組
NewKey和CheckNode在上述的藍色虛線區域內只要有不一樣的bit,則沒必要再和Child-CheckNode繼續匹配了,能夠肯定,NewKey確定插入後做爲PreCheckNode的某個孩子了。若是須要繼續匹配,判斷是哪一個孩子的方式以下:緩存
第三步,肯定插入位置而且插入,步驟以下:網絡
0).若是沒有發生第二步中的和Child-CheckNode不匹配的情形,則直接將NewKey做爲葉子做爲PreCheckNode的第NewKey[PreCheckNode的pos...PreCheckNode的pos+PreCheckNode的bits]插入,結束。不然執行下面的步驟,處理和Child-CheckNode的衝突
1).建立一個CheckNode,而後看下圖:數據結構
假設上圖中的綠色圈起來的位是Child-CheckNode和NewKey首次不匹配的地方,記爲miss,那麼NewKey將建立一個新的CheckNode,記爲NewNode,其POS爲miss,其bits爲1,這樣原來的Child-CheckNode就成了NewNode的一個孩子,而待插入的NewKey建立一個新的葉子,做爲NewNode的另外一個孩子。NewNode代替Child-CheckNode做爲PreCheckNode的孩子插入其孩子數組中。框架
第四步,完畢
基本上,上述的過程已經很清楚了,然而給出一個例子會更好些,接下來我給出一個例子,依次插入3條路由項:
1:192.168.10.0/24
2:192.168.20.0/24
3:2.232.20.0/24
而後咱們看圖說話,首先看一下比特圖:
接下來看一下插入trie的情形:
3.3.trie平衡以及多路trie
若是僅僅看3.2節所論述的內容,咱們發現trie不過是一棵二叉查找樹而已,這又有何好說的呢?然而做爲路由表結構的trie卻遠不止這麼簡單。若是咱們如今還想不到做爲路由表的trie樹長什麼樣子,咱們能夠先考慮一下頁表,畢竟這是實現虛擬內存的關鍵,處理器設計者必定會選擇一種至關高效的方式來從虛擬地址查找物理地址的,頁表使用分段索引的方式來快速定位頁表項,也就是說將一個虛擬地址分爲N段,每一段定位一個索引,然而將這些索引層接起來就是最終的頁表項。這裏就再也不給出圖示了,關於頁表的資料不少。
若是把頁表結構從頁目錄展開來看的話,頁表結構就是一棵大分叉的樹,足有4096叉,然而卻不高,也就兩層到四層。咱們想一下它爲什麼如此高效,由於它比較矮小,索引能夠快速定位樹的分支,最終快速到達葉子。
可是,且慢,樹矮小的代價是什麼?時間複雜度小了,空間複雜度通常都會變大。它太耗內存了。所以最好的方案就是,樹不能過高,也不能過矮。多路的trie樹就是這樣設計的。極端狀況下,多路trie樹會退化成一個鏈表或者進化成一棵「2的32次方」叉的只有兩層的樹:
鏈表情形-bits=0
多叉樹情形-bits=32
而動態多路trie所要維護的就是讓這棵樹不這麼極端。
咱們首先看一眼普通多路trie樹的插入情形,注意,所謂多路trie樹插入是假的,在Linux的實現中,只有平衡操做才能讓trie成爲多路的,這裏給出的實例在Linux中是不會出現的,只有通過平衡操做的trie樹纔會是這個樣子,也就是說,不可能一插入就是這樣的,具體的CheckNode的bits在這裏是事先肯定好的,而在Linux的實現中倒是動態調整的。多路trie的本質在於其「多路」,而多路的本質在於CheckNode的bits字段。看一下上面講查入時的例子,此時咱們又多了一個路由項從而多了一個節點,首先看比特圖:
再看一下多路trie樹:
這就是多路trie樹。
所謂的平衡操做很簡單,每次插入新的節點都會平衡這棵樹,原則以下:
1).若是過高了,那麼就壓胖它。
使該CheckNode的pos不變,bits加1,使得其孩子的容量增大一倍,而後依次將其孩子從新加入新的CheckNode,加入過程當中遞歸執行平衡操做。
2).若是太胖了,那就拉高它。
使該CheckNode的pos不變,bits減1,使得其孩子的容量減小一倍,而後依次將其孩子從新加入新的CHeckNode,加入過程當中遞歸執行平衡操做。
總之,Linux實現的trie樹是動態變化的,這種動態變化的優勢是能夠根據系統當前的負載以及內存狀況動態對trie樹的形態作出調整,使得資源的整體利用率提升,然而也有缺點,那就是算法自己太複雜,不適合作擴展,最重要的是不適合用硬件實現。
3.4.trie樹的查找
終於到查找操做了。在咱們理解了上述的插入和平衡操做以後,查找就變得很簡單了,咱們不但能夠看得出其簡單-好的算法通常都簡單,而且因爲平衡操做算法還來得很高效,惟一的新東西就是回溯,不過這一節咱們只介紹通常回溯,下一節介紹關於回溯的優化。
查找其實很是簡單,簡單的讓我都不想寫算法流程了,我家小小又鬧了,加上又喝點酒...來個例子吧,好比來了一個數據包,目的地址是192.168.10.23,來看一下怎麼查找,將該地址寫成二進制:
根據trie樹根,得知pos=0/bits=3,所以知道應該去往根CheckNode的第7個孩子,因而到達CheckNode2,相似的,咱們檢查該ip地址的第19位後面的兩位,到達葉子節點1,因爲其掩碼爲24,經過,順利找到,在描述樹查找過程前,我先將添加默認網關的比特圖給出:
而後給出trie樹:
整個trie查找過程爲,紅線標示查找過程:
接下來咱們看看回溯,首先看看爲什麼要回溯。trie樹不像頁表,檢測範圍覆蓋整個32位虛擬地址,trie的檢查點覆蓋範圍之間會有空隙:
藍色虛線圈住的區域就是空隙-(見路徑壓縮),萬一在查找時,在這個區域發生不匹配,是不能直接檢測出來的,這樣好像查找過程就進入了一個死衚衕,注意,第一次匹配查找的過程是精確匹配,此次進入了死衚衕以後,立刻轉變查找策略,將從精確匹配轉爲「最長前綴匹配」,因爲越靠近葉子的節點的前綴(理解爲子網掩碼)越長-由於它比較精確,因此此次查找採用從葉子到根的方式,查找最長前綴的匹配,這就是回溯,舉例來說:
1).111100和111110不匹配
2).可是它卻和111000,110000,100000,000000都匹配
3).取最長的匹配,那就是111000
好比來了一個目的地址是192.169.20.32,按照上面的方式,將跳過第16位的不一樣,最終達到的葉子節點是4,然而最終的總體檢查失敗,進入最長前綴匹配,也就是回溯,首先回溯到哪呢?固然是CheckNode3,而後下一步呢?在介紹下一步以前,咱們看看回溯的原則。最長前綴匹配中,0是很重要的,只要某個匹配項除了後面的0,前面都匹配,那就算成功匹配,咱們須要作的是找到「最長」的匹配。哪一個是最長的匹配呢?咱們能夠經過一個算法獲得結果,這也是Linux內核中所使用的算法:
這樣的結果,咱們看一下整個過程:
最後,值得注意的是,每個CheckNode和Leaf都有一個前綴鏈表,好比:
192.168.10.0/24 via 1.2.3.4
192.168.10.0/27 via 4.3.2.1
兩個entry就共享一個Leaf,然而該Leaf卻有兩個掩碼,兩個掩碼連接成鏈。當發生匹配的時候,必須依次匹配每個鏈表上的掩碼。有兩個原則決定了最終的匹配結果的前綴是最長的,第一,從樹根到葉子的精確匹配;第二,每一個葉子節點的掩碼鏈表是按照從長到短的順序排列的。
3.5.回溯優化
回溯是很低效的,好比上面的例子,整個繞了兩圈,若是能提早發現那個不匹配的位,那就不用耗費那麼多的無用功了,實際上作到這一點很簡單,那就是在取下一個孩子的時候,判斷一下:在當前CheckNode的[pos+bits]和欲往的孩子節點的[pos+bits]之間有不一樣的比特嗎?若是有,看看CheckNode中不一樣的那位之後是否全0,若是是,則直接檢測該CheckNode的掩碼鏈表,不然直接回溯,這樣就沒必要作無用功了。這種「忽略的不匹配」現象以下所示:
檢查到了這種狀況以後,匹配過程立刻進入「最長前綴匹配」,將掩碼從32位(精確匹配)減小到和當前CheckNode的key[pos+bits]個孩子的第一個不匹配的位指示的那個位置:
檢索鍵和匹配項相差異的那一位,不是0就是1,只有在匹配項的那位是0(檢索鍵的那位是1)的時候,檢索才能繼續下去,不然,回溯!繼續檢索以後,按照常規的匹配來匹配,區別就是掩碼不一樣,精確匹配時是32位掩碼匹配,而最長掩碼匹配是N位掩碼匹配。
3.6.動態多路trie樹的本質-路徑壓縮
因爲多路trie樹的目的快速從根節點找到一個葉子節點,而後匹配,若是不匹配的話就回溯,所以表示路由表的trie樹就應該能快速一條惟一的從根到葉子的路徑,所以樹的高度不便過高,所以沒有必要對查找鍵每個比特位都進行檢測,trie樹中的CheckNode中的pos以及bits決定了在哪一個地方檢測已經檢測多少位,而trie樹此時是已經建好的,能夠把當前已插入路由項以外的比特信息檢測所有忽略掉,這就是路徑壓縮,見下圖:
檢索鍵的藍顏色圈起來的位在精確匹配過程當中暫時不須要進行匹配,等到最長前綴匹配時再考慮。路徑壓縮的好處在於匹配時計算的次數會減小,然而隨着更多的路由項的插入,不少的節點將會使下列的等式成立:
node.pos=Pnode.pos+Pnode.bits(Pnode爲node的Parent)
若是一個CheckNode有太多這樣的孩子,說明進入此分支的匹配所有都要「走很長的路」了,那麼爲了使匹配操做「路途更短」,該進行一次平衡操做了,所要作的就是將高樹壓低壓胖。
4.BSD/Cisco的Radix查找算法
4.1.基本理論
不少時候,仍是這個名稱形成了極大的困惑,radix樹?基樹?二叉樹?...停吧!
4.2.radix查找
複雜的多路trie樹查找咱們都已經會了,這個還難嗎?可能惟一的區別就是BSD的樹相對於Linux的而言比較固定,所以更易於用硬件來並行實現,華爲的VRP所以也受益良多!舉個例子來講明這一點,若是咱們將IP地址分爲相等的4個部分,每一部分就是8個位,那麼就很容易將4個索引並行處理,即便不併行處理,使用硬件交叉網絡來實現也是蠻快的,能夠看到,這和頁面的查詢很是相似了,只是頁表查找失敗會引起缺頁異常,而路由查找失敗將回溯。仍是那個問題,回溯到哪裏?基本算法和trie樹同樣,也是依賴每個CheckNode都存在一個掩碼鏈表...
5.BSD/Cisco的X叉樹查找算法
5.1.基本理論和查找
用空間換時間,這是一種不太瘋狂且很正當的舉動,由於時間相比空間要重要的多,人們對時間的敏感性也比對空間的敏感性更高,空間廣義的說能夠是無限的,而時間卻存在一個個的閥值。另外,並行也是空間換時間的一個直接益處,咱們知道並行是一個時間上的概念。
Linux的trie樹的回溯優化版本中,發現不匹配就回溯,回溯的過程當中包含了一個一個嘗試的步驟,無非就是從右到左依次將1變成0後再次嘗試前綴匹配,這種方式當然可行,然而若是能直接指出下個匹配哪一個節點,那就不須要回溯過程當中的嘗試行爲了。而這正是Cisco的實現,傳說中的256叉樹就是用固定的4個8位一組來定位索引的,和頁表項查找同樣,一旦出現不匹配的或者索引對應的孩子爲NULL的,直接根據節點結構中指示的「下一個節點」來直接到達下一個節點處繼續匹配。比特結構見下圖:
可見,這種256叉樹中間沒有空隙,也就是說每個比特都要參與索引定位,不會有遺漏的,另外在插入的時候,已經動態計算好了節點不存在時將要從哪裏繼續開始匹配,也就是說每個空節點都包含一個指針,指向「下一個可能匹配的節點」,另外在每個非空節點中,也包含一個指針,指向「下一個可能匹配的節點」(這個指針幾乎不用),所以回溯時再也不須要動態計算,回溯只須要在獲得「下一個可能匹配的節點」後,一路往下取全0的孩子便可,這就是「前綴匹配」。256叉樹能夠一步到位的進行查找,大大提高了效率。其查找樹以下:
查找過程很簡單,計算第一個8位爲P,第二個8位爲Q,第三個8位爲L,第四個8位爲N,所以匹配項在樹中每層(從第2層開始)的索引爲P,Q,L,N。這樣很容易定位到最終的節點,若是是一個空間點,表示沒有精確的匹配項,那麼就開始回溯,回溯過程走上圖中的紅色線路。
所以一次查找操做在有限次數內就能找到,樹很矮,時間效率很高,至關高,然而因爲全部的路徑都是在插入時肯定的,所以其插入操做比較複雜,不過即便再複雜無非也就是和多路trie樹查找時作的那樣,計算一下回溯路徑而已,而後將其添加到256叉樹的節點項中,最終路由查找的時候高效的直接使用,僅此!
5.2.評價
256叉樹的查找結構是一個通常性的路由表結構,實際上Cisco路由器的CEF的實現是上述256叉樹的某種優化-CEF使用的數據結構是一個256-way-mtrie,它本質上也是分爲4層,和上述的沒有什麼兩樣,只是再也不存在空節點,也沒有了紅色粗線表示的靜態回溯路徑,而是直接把那條紅線最終指向的節點的信息直接寫入到那個空節點中。看起來以下這個樣子:
實際上CEF使用的也是一棵多路trie樹,只不過這棵樹比較容易和硬件創建關聯,從而用硬件創建轉發表,而Linux的trie是動態的,純軟件的。
6.總的評價
總的評價不談哈希算法,由於哈希函數的可擴展性不好,我自己也不是很喜歡這個東西,雖然Linux內核中大量使用了哈希,可是正是這些哈希限制了Linux支持應用的規模,尋找好的哈希函數簡直太難了,若是這會兒你的西牆倒了,而且你此時並不在意東牆,那麼你就用哈希吧,拆了東牆補西牆!
樹算法是不錯的選擇,肯定性強,並且越是簡單的樹實際上效率越高,這是爲何呢?由於易於用硬件實現,專業級的硬件仍是要比單純使用cpu的軟件效率高几個級別的。是設計高效複雜的純軟件算法仍是用硬件實現一個簡單然而並不怎麼高效的算法,這是一個問題。基本上能夠肯定,通常而言,純硬件實現的遍歷要比純軟件實現的哈希好不少,硬件是信號,電流驅動的,而軟件依賴cpu指令,時鐘週期等...
本文基本就介紹了路由查找使用的兩種樹,第一種是二叉樹,以下圖(圖片來自google的結果):
第二種是256叉樹,以下圖(圖片來自google的結果):
另一種樹,多路動態的trie樹,其實是介於退化成鏈表的二叉樹和2的32次方叉樹之間的一種樹。
Internet路由-主機路由表和轉發表
2011-07-10 16:21 2443人閱讀 評論(2) 收藏 舉報
internetlinux內核路由器網絡ciscowindows
目錄(?)[+]
1.路由表
路由信息最終要存儲在用於路由器的主機或者專業路由器上,存放這些信息的地方稱爲路由表。其中包含三元素:目標地址,掩碼,下一跳。
1.1.查詢路由表的開銷
有人認爲查詢路由表是一件和交換機查詢MAC地址同樣的事,那就大錯特錯了,查詢MAC地址/端口對是一種固定的數據查找,MAC地址都是固定的48字節,而IP地址呢,自從CIDR出現之後,地址成了「無類」的了,所以任意位的掩碼都能成爲正確的掩碼,另外,聰明的管理員想出了路由匯聚,這些傢伙將負擔轉嫁給了系統的實現者,從而系統實現者必須實現「最長掩碼」匹配,既然掩碼是不固定的,所以查詢就是不固定的,這是很麻煩的事。
2.轉發表
數據包到達路由器的時候,要根據「指示」前往特定的端口,相似交換機的地址信息表,路由器上存放這個「指示」的地方叫作轉發表
3.兩個表的關係
3.1.聯繫
轉發表直接做用於數據包,而路由表是轉發表生成的依據,轉發表經過路由表生成。一個特定的進程能夠經過使用路由表中的信息,加上自身的主機方面的信息-好比網卡等信息-加以綜合,獲得一張轉發表。路由信息和主機是沒有任何關係的,它只描述網絡鏈路狀態和方向。
3.2.區別
兩表存儲的信息是不一樣的,路由表只存儲三元素-目標,掩碼,下一跳;而轉發表存儲更詳細的信息,好比輸出端口信息,好比標記信息等。轉發表描述了主機方面的信息,在主機內部將一個數據包從一個端口導向另外一端口,而路由表描述網絡信息,將數據包從一個機器導向另外一機器。
4.誤區
當前不少人都懂一個系統的實現,這些人不外乎3類,第一類精通linux內核,第二類精通Cisco設備的配置,第三類精通Windows網絡編程以及NDIS。三類人通常的交集不多,所以大部分熟悉Linux內核的人都不會去區分路由表和轉發表,由於Linux內核自己就不區分這兩個表,每到一個數據包都會查路由表(固然前面還有一個緩存...但這不是轉發表);所以熟悉Cisco的傢伙會認爲兩個表是必定要區分的,不然MPLS怎麼實現?熟悉Windows核心的人呢?可能並不關心這些,由於Windows幾乎不用於路由器。
5.區分二表的好處之實例
MPLS是區分路由表和轉發表的一個明顯的例子,首先聲明,MPLS是一個網絡,而不是運行特定協議的一個節點。在MPLS中,徹底經過標籤進行數據包轉發,在MPLS網絡可用於數據業務以前(中間也能夠經過標籤分發協議動態調整),MPLS轉發表就建好了,它的表項包含三元素:來源數據自帶標籤/轉發到的端口/轉發出去攜帶的標籤。這個表項是經過路由表創建的,咱們將之視爲轉發表。實現MPLS須要在入口路由器上爲數據包單獨打上一個標籤,這樣直到出去MPLS網絡,數據包就能夠快速被轉發了,省去了直接查詢路由表的開銷。
6.Linux中經過NAT原理配置轉發表
若是管理員能事先「打通一條路」,那麼就能夠在每個路由器的入口上配置DNAT,而後在其出口處配置SNAT,從而實現一個標準的點對點的IP鏈路,每個節點都覺得數據要發往「下一跳」,而不是越過下一跳的「遠端機器」。因爲NAT基於鏈接,它爲無方向的IP網絡增長了方向性,所以配置SNAT後就無需再配置反向路由了。
鑑於上述的方式,若是咱們能在DNAT信息中識別標籤信息而後直接肯定輸出端口,同時在輸出端口的SNAT中封裝上新的出口標籤,無需在進入路由模塊,這就是一個MPLS實現。然而目前的標準Linux內核的Netfilter中並無如此實現。
7.優點
除了能夠實現MPLS等特定的專用線路網絡以外,轉發表和路由表分開的最大好處在於其可使用硬件實現轉發表,從而實現高速鏈路層轉發,而路由表很難用硬件實現,緣由就是它不是精確匹配的,而是「最長前綴」匹配的,這裏面充滿了變數。
做者:文宇肅然
來源:CSDN
原文:https://blog.csdn.net/wenyusuran/article/details/40584233 版權聲明:本文爲博主原創文章,轉載請附上博文連接!