精华内容
下载资源
问答
  • 有类及无类路由查找方式
    2020-07-31 08:33:02

    有类及无类路由查找方式
    CISCO 路由器在路由的全局查找上有两种方式:有类(Classful)查找方式及无类(Classless)查找方式。
    当路由器执行无类别路由查找时(默认,ip classless),它不会注意目的地址的类别,它会在目的地址和所有
    己知的路由之间逐位(bit by bit)执行最长匹配;
    而如果是有类路由查找(no ip classless 且关闭 ip cef),那么收到一个数据包时,路由器先查找目的地址所
    属主类,如果路由表中有主类路由,则再去找子网,如果有子网路由,则查询被限定在这些子网中,并进一步查
    找,如果最终查找失败(没有任何子网匹配这条路由),则丢弃数据包,即使有默认路由存在;如果本地没有该主
    类路由,则看是否有默认路由,如果有,则按默认路由转发,如果无,则丢弃数据包。
    在 R2 上做如下配置并做测试(均在 no ip classless 且关闭 ip cef 环境下做的测试):
    在这里插入图片描述

     【实验 1】有主类路由;有默认路由,走主类路由
    ip route 0.0.0.0 0.0.0.0 192.168.23.3
    ip route 172.16.0.0 255.255.0.0 192.168.12.1 !! 走主类路由
     【实验 2】有子网路由(匹配);有默认路由,走子网路由
    ip route 0.0.0.0 0.0.0.0 192.168.23.3
    ip route 172.16.1.0 255.255.255.0 192.168.12.1 !! 走子网路由
     【实验 3】无主类网络;有默认路由,走默认路由
    ip route 0.0.0.0 0.0.0.0 192.168.23.3 !! 走默认路由
    ip route 172.17.0.0 255.255.255.0 192.168.12.1
     【实验 4】有主类网络;有子网路由(匹配),按最长匹配
    ip route 172.16.1.0 255.255.255.0 192.168.12.1
    ip route 172.16.0.0 255.255.0.0 192.168.23.3
     【实验 5】有子网路由,子网路由前缀长度不一样(但都匹配),按最长匹配
    ip route 172.16.1.0 255.255.255.0 192.168.12.1
    ip route 172.16.1.0 255.255.255.224 192.168.23.3 !! 按最长匹配,走这条
     【实验 6】有子网路由,子网路由前缀长度不一样(匹配及不匹配均有),走匹配路由
    ip route 172.16.1.0 255.255.255.224 192.168.12.1 !! 匹配,走这条
    ip route 172.16.1.32 255.255.255.224 192.168.23.3
     【实验 7】有子网路由(不匹配);有默认路由
    ip route 0.0.0.0 0.0.0.0 192.168.12.1
    ip route 172.16.1.32 255.255.255.224 192.168.23.3
    无法 ping 通,因为去往 172.16.1.1 查表后发现有子网路由,因此查找被限定在子网路由中,然后却发现这条路由不匹配,因此直接丢弃报文,而不会走默认路由。
    PS:要注意将有类、无类路由查找方式,与有类、无类路由选择协议区分开来。

    更多相关内容
  • 分析基于前缀长度的二分路由查找算法和基于Trie的路由查找算法的优缺点,在此基础上提出一个改进的路由查找算法,并给出其在IPv6下的实现方案,由于基于前缀长度的二分路由查找算法扩展性好、查找速度快,而基于Trie...
  • 根据路由表前缀扩展特性,采用...与传统TCAM路由查找相比,可以节省约40%的功耗。此外,该算法在查找性能、路由更新和存储空间方面也有很大优势,能够达到最少访问一次存储器,最多需要访问3次实现处理一个IP数据包。
  • 为了解决路由器报文转发中路由查找速度慢的瓶颈问题,在分析了路由器中广泛使用的各种典型IP路由算法的基础上,提出一种基于多分枝trie树的改进路由查找算法。在多分枝trie树中取消前缀查找,组成一个大的中间结点。...
  • 为此,将哈希表和多比特树相结合,提出一种新的路由查找算法。根据路由前缀的长度将路由表项分层存储在固定的三层Tree中,采用哈希表存储路由下一跳的信息,根据目的IP地址在二层Tree结构中按最长前缀匹配的原则进行...
  • 路由查找算法

    2019-03-10 17:18:26
    自己做的路由查找算法ppt,上课用。主要从四个方面总结,1.Internet地址结构的发展2. 路由查找算法3. 路由查找算法的评价4. 相关进展
  • 提出一种FIB多级映射的并行多流水路由查找架构,建立了FIB多级映射路由查找架构的功耗模型,基于FIB多级映射路由查找架构提出了路由表动态映射算法.设计了一种绿色路由查找步长优化算法.真实网络路由表实验结果表明,...
  • 对IPv6相关的通用型与特定型路由算法进行了分析,重点研究了以BSR为基础的IPv6路由算法在查找和更新时的不平衡问题,提出了基于前缀区间集合的IPv6路由算法。通过对路由前缀(N)进行范围(K)、集合(M)划分以及...
  • 分析了互联网路由表和路由更新的特征,提出了一种基于叶子节点进行路由表分区的并行IP路由查找方法Leaf-TCAM,分区子表按照流量特征在K个TCAM芯片中进行均衡分布。分析表明,该路由查找方法在引入0.1*(K-1)冗余的...
  • Linux IP路由查找算法研究.pdf
  • 高端路由器路由查找算法分析与实现.pdf
  • 路由器与路由查找算法 路由器基本概念 路由器就是在网络层根据数据包目的地址进行IP数据包转发并完成其他网络功能的设备。 1)为什么要分为域间与域内路由? 满足不同域之间管理的需要,减少路由表大小。 2)为...

    2019年秋季学期 计算机网络 单章节复习 路由器和路由查找算法

    路由器基本概念以及功能简介

    路由器是什么?
    一种设备,根据到达数据包的IP目的地址进行数据包转发的网络设备,同时可以完成多种网络功能。
    相关问题:
    1)为什么要分为域间与域内路由?
    减少路由表大小,减少网络整体的负载,区分两者可以很好的将网络中的功能区分开来,网络内部主机可以不用存储外部路由信息,直接将数据包发送给边缘路由器或者用于外部通信的路由器即可。而边缘路由器由运营商负责,可以很好的优化负载并完成各种转发任务。
    2)为什么要最长前缀匹配实现路由查找,而非精确匹配?
    如果实现了精确匹配,那么路由表将会十分冗余。对于一个子网掩码为255.255.255.000的子网,其内部可能需要2的8次方(256)个项,这样查找起来费时费力,无法实际使用。
    3)IP地址同时标识位置与身份有什么缺点?
    由于两者紧耦合,导致其移动性较差,同时很难满足一些特定要求的设计。
    4)路由表RIB/转发表FIB如何产生?
    具体可以参考这个博客:路由表 (RIB) 与转发表 (FIB)
    5)路由器需要完成哪些基本功能?
    ⚫路由计算−路由协议获取拓扑结构,建立并维护路由表
    ⚫查找转发−IP包检验(如版本号、头长度、头校验等)、目的IP地址解析及查表、包头修改(如TTL域修改、头校验生成)等
    ⚫特殊服务−NAT(网络地址转化)、QoS(服务质量保证)、ACL(访问控制列表)等

    路由器结构设计

    基本流程和组件如 下所示,每一代路由器在设计时根据硬件设备配置略有不同。
    路由器基本功能
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    ⚫控制平面:主控卡
    –路由计算(路由表/转发表)–快速收敛、最优路径、流量工程
    ⚫管理平面:主控卡
    –监测、配置
    ⚫数据平面:线卡
    进行数据包地址查找以及数据包转发,检查并修改数据包中的一部分项目(TTL,checksum等)控制数据包转发速度、更新速度。维护数据包队列并根据网络转狂保证服务质量(QoS)。

    路由查找算法

    数据平面中需要根据数据包本身的目的地址对数据包进行转发。
    转发依据为最长前缀匹配算法。
    实际设计时应该使用最长前缀匹配算法来保证其可以在内存大小、匹配速度以及更新速度中得到平衡?

    方法一:顺序查找
    时间复杂度O(n)
    空间复杂度O(n)
    插入复杂度O(1)
    删除/更新复杂度: O(n)
    对于大型的路由设备并不现实。

    方法二:Trie查找
    Trie tree 3个基本性质:
    根节点不包含字符,除根节点外每一个节点都只包含一个字符;
    从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
    每个节点的所有子节点包含的字符都不相同。

    在网络中,我们将其路由表中一整项视作一个字符串,并将转发端口作为节点,将中间字符串视作边。可以使用多种方式将全部IP地址映射到树中。
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
    查找和构建的时间复杂度:
    假设所有字符串长度之和为n,构建字典树的时间复杂度为O(n)
    假设要查找的字符串长度为k,查找的时间复杂度为O(k)

    构建trie树的空间复杂度:
    字典树每个节点都需要用一个数组来存储子节点的指针,即便实际只有两三个子节点,但依然需要一个完整大小的数组。所以,字典树比较耗内存,空间复杂度较高。

    方法三:Hash查找
    根据前缀长度哈希到不同的存储列表中,再从列表中进行查找。
    查找性能:≤W ; 存储空间:~2(w+1)
    Hash最大问题是时间开销与存储开销之间的冲突
    更新:1
    可以使用并行化提升查找性能

    目前问题:一个查找算法如何可以在空间复杂性、更新时间复杂性、匹配时间复杂性中得到平衡?
    在这里插入图片描述

    一种方法:
    在组成原理中我们知道,CPU的存储架构存在层次化,即是说离核越近,速度越快,容量越小。
    因此在改进算法时要更合理、高效的使用缓存,通过算法和结构设计利用甚至制造局部性。需要根据缓存结构和调度原理来针对路由算法设计数据结构。
    这里有一种基于硬件的查找方法,在地址空间中按照一定顺序排列前缀项目。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    减少匹配的TCAM块数,降低能耗,同时使用块选择技术如前缀子串分割,Tried分割等改进时间复杂度。
    可以使用流水线操作,将树的每一层映射到不同的SRAM中来提高查找速度以及更新速度。
    硬件查找的优缺点
    ⚫三态内容地址查找存储器ternary content(addressable memory,TCAM)
    确定性高速查找,一时钟周期但是更新开销大
    SRAM–将trie树每级节点映射到一个流水级,但是片内SRAM流水线存储空间不足

    将整个路由表分为两个部分,一部分是重叠的tire树,另一部分是不相交的前缀集。
    在这里插入图片描述
    两者组成混合IP查找架构。
    在这里插入图片描述

    针对虚拟路由器的查找算法
    由于虚拟路由器是建立在资源虚拟化后的路由器上的,因此面临众多新的挑战,
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    基于TCAM的虚拟路由路由器查找。
    将不同FIB直接存储在TCAM中,使用VID避免冲突

    在这里插入图片描述在这里插入图片描述

    之后合并前缀,构建新的TCAM表。但是其中的一些项目可能会被错误填充,需要将所有无效指针换为有效的下一跳指针。
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 关于Linux路由查找的方式,多个内核已经不知反复了多少回合,其中最具有里程碑意义的变革有两个: Linux 3.5内核之后取消了基于hash表的路由缓存。 Linux 4.18内核以后开始支持路由查找的Flow offload。 基于Flow ...

    关于Linux路由查找的方式,多个内核已经不知反复了多少回合,其中最具有里程碑意义的变革有两个:

    • Linux 3.5内核之后取消了基于hash表的路由缓存。
    • Linux 4.18内核以后开始支持路由查找的Flow offload。

    基于Flow offload这个框架,进一步的优化就是 Flow HW offload

    可以参考我之前写的文章:
    https://blog.csdn.net/dog250/article/details/50809816
    https://blog.csdn.net/dog250/article/details/103422860


    无论是在FIB表之外的离线hash cache,还是绕过FIB查找的各种offload短路措施,都只是在Linux内核的 算法范畴 的,无非就是在查找路由的过程中节省几个步骤。 它们都没有利用数据包本身的特征。

    我们知道, 数据特征是可以影响算法结构的。 最典型的例子就是 “分级存储机制” CPU cache内存数据,内存cache磁盘数据… 这一切完全是基于数据的局部性特征设计的。如果数据没有局部性特征,那么分级存储设计将毫无意义。

    回到路由查找,其实也是可以利用数据包的特征的,幸运的是,数据包也具有局部性特性:

    • 时间局部性:同一个数据流的数据包会连续到来。
    • 空间局部性:到达同一个域名的数据包会连续到来。

    简单解释一下。

    数据包以数据流的形式编排是数据包时间局部性的根源,考虑到当前的网络已经非常高速,中间节点无论在进行包分类还是在进行DPI时,一般都是按照数据流来汇总的,而这也是TCP等流式协议得以进行拥塞控制的依据。

    数据包的时间局部性也为运营商提供了详细的数据流在统计意义上的特征数据。

    数据包的空间局部性这里不多说,它比较复杂,比时间局部性更难以分析和利用,后面有空再谈。

    说了这么多,数据包的时间局部性能为路由查找提供什么便利呢?

    早在2015年,我为了优化nf_conntrack的查找,我为其增加了一个查找cache,详见:
    https://blog.csdn.net/dog250/article/details/47193113
    这篇文章所述的优化背后的依据就是数据包的时间局部性和空间局部性,不过在当时并没有利用这个局部性特征去优化路由查找,因为我把路由项已经藏在nf_conntrack里面啦,查找nf_conntrack就等于查到了一切,路由,socket,等等,什么都有…

    Linux 5.5内核在路由查找算法上,终于有了针对数据包时间局部性的优化:

    When doing RX batch packet processing, Linux always repeated the route lookup for each ingress packet. When no custom rules are in place, and there aren’t routes depending on source addresses, we know that packets with the same destination address will use the same dst. This change tries to avoid per packet route lookup caching the destination address of the latest successful lookup.

    请看下面的patchset:
    ipv6: add fib6_has_custom_rules() helper
    ipv6: keep track of routes using src
    ipv6: introduce and uses route look hints for list input.
    ipv4: move fib4_has_custom_rules() helper to public header
    ipv4: use dst hint for ipv4 list receive

    看明白代码就不用听我扯这些形而上学的东西了,不过我这些可能会帮你进行一些DIY的优化。


    一般而言,路由器不对数据包之间的关联做任何假设,它认为数据包是独立到达的,并且报文之间毫无关联,这是IP逐跳寻址的基础。

    所以,路由器针对每一个数据包都要进行 同样的FIB路由表查询动作-最长前缀匹配算法。

    后来,人们发现没有必要去用 最长前缀 算法查询FIB表,于是转发表从FIB表中分离了出来,无论是高端的Cisco的CEF,还是Linux 3.6内核以前的路由cache,都是这个路子,甚至OVS流表的背后,也是这个思想。

    无论是查FIB还是查cache,无论这cache是软件的,还是offload到硬件的,终究都还是要查,终究还是不能直接拿来就用。

    我的意思是,设想有一个全局变量,它保存着一个 当前流的下一跳路由项 , “流”通过目标地址来标识,也就是说, 只要目标地址是一样的,就认为它们通过同一个下一跳发出。

    嗯,大致的思路就是下面一张图所示的:
    在这里插入图片描述
    太简洁了,一张图解释一切。

    解释下为什么不能作用于Policy Routing。

    标准的IP路由是基于目标地址来路由的,它就像当前大多数导航一样,趋向于去往同一个目的地的流量使用同一条路经,但是这会造成很多问题,比如拥塞。此外,显式的负载均衡也是很多设备商的卖点…

    于是,Policy Routing本身就是用来打破标准IP路由的枯燥乏味的,它引入像源地址,源入口之类的健用来更加灵活地为数据包找到一个下一跳。因此,如果系统中存在Policy Routing配置,便无法使用本文所述的优化方案了。


    最后,回应题目,什么是Flowlet?Flowlet和路由查找有什么关系?请看:

    非常有意思的Flowlet

    Flowlet可是大有作为,运营商早就看出来网络上跑的都是Flowlet了,虽然大家都在说pacing,然而在运营商看来,终于还是bursting…


    浙江温州皮鞋湿,下雨进水不会胖。

    展开全文
  • 分析了IPV6路由结构的特点,介绍了IPV6各种路由查找技术,提出了基于ABV的快速路由查找算法,并对算法的性能进行了分析比较。
  • Trie树路由查找算法在网络处理器中的实现.pdf
  • 徐恪大神的文章,欢迎学习
  • Linux系统策略性路由查找算法的分析与改进.pdf
  • 基于网络处理器IXP1200的路由查找功能的微码实现.pdf
  • 分组转发的重要一步就是查找路由表,因此快速的路由查找算法是实现高速分组转发的关键.路由查找需要实现最长前缀匹配.近年来,研究人员提出了多种路由查找算法,以提高查找性能.分析了路由查找问题及其难点,全面综述了...
  • 电信设备-AD+HOC+无线通信系统中的最小代价路由查找.zip
  • 一种改良的IPv6路由查找算法,陈鹏翰,徐明伟,随着IPv4地址的耗竭,IPv6的推广速度得到了非常大的提升。具体的表现为,IPv6地址分配量增多,骨干网路由器FIB(Forwarding Information Table��
  • 路由查找

    2013-09-26 13:48:35
    引:路由是互联网的一个核心概念,广义的讲,它使分组交换网的每个节点彼此独立,通过路由耦合在一起,甚至在电路交换网中,虚电路的建立也依赖路由路由就是网络中数据通路的指向标。狭义的讲,路由专指IP路由
    说明: 本文没有源码分析的内容,然而我认为能理解本质比能看懂源码更有用,因为理解了本质之后,你也许就不用再看源码了,你甚至都可以写源码了。这就是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.查找过程

    查找结构如下图所示:


    查找顺序如下图所示:




    为了实现最长前缀匹配,从最长的掩码开始匹配,每一个掩码都有一个哈希表,目的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]即可直接取到孩子结点。

    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次方叉树之间的一种树。
    展开全文
  • 一种基于计数器Bloom滤波器和Trie树的IP路由查找算法,王舒荷,袁东明,IP地址路由查找是网络转发技术的核心内容,路由表的组织和快速的路由查找算法是实现高速转发分组的关键。大量的研究表明在现有的以
  • linux路由表,策略路由,路由查找

    千次阅读 2016-05-16 12:35:49
    路由表内核中路由表有2种:l 一个是缓存路由(fib),是自动学习生成自动管理的,用户没必要去干预,但是内核还是提供了方法让用户可以去清空它。但是用户不能设置它的项,但是可以根据这个缓存更新的原理从外部...
  • Inter LPM算法 实现路由查找,对算法感兴趣的同学可以研究研究。
  • 什么时候发生路由查找? 首先认识路由树: 具体说来: 根据ip地址来区分参考“IPv6地址类型:单播/组播/任播地址”: (1)If the destination address matchesFE80:://数据包是发给本机,执行一般的收发,...
  •  随着网络流量的不断增加和路由表容量的不断增大,路由查找已经成为制约因特网的主要瓶颈。尽管采用CIDR技术能产生聚集路由,但路由器的路由表项还是很大,使得路由查找成为高,速路由器的瓶颈。因此,提高路由查找...
  • 路由查找   // 这个函数负责从 fib_table 中查找 fn_key 相匹配的 fib_node ,然后找到相应的 fib_info, // 将结果存放在 fib_result 结构中   // 传入参数 fz_divisor 是 linux kernel 2.6 的变化...
  • 详细介绍了TCP/IP 网络层的相关知识,包括IP 地址分类,路由寻址以及其他相关知识
  • IPv6路由查找算法探究

    2011-04-05 14:54:13
    有关ipv6的所有的查找算法,软件方法硬件方法等等

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 134,615
精华内容 53,846
关键字:

路由查找