路由表查找算法概述-哈希/LC-Trie树/256-way-mtrie树

说明:本文没有源码分析的内容,然而我认为能理解本质比能看懂源码更有用,由于理解了本质以后,你也许就不用再看源码了,你甚至均可以写源码了。这就是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 版权声明:本文为博主原创文章,转载请附上博文连接!

相关文章
相关标签/搜索