背景技术
现有技术中,网络路由器中的IP查找通常使用CPE方法。CPE方法是由Srinivasan提出的一种快速查找方法,论文名称为“Faster IPLookups using Controlled Prefix Expansion”(出自proc.ACMsigmetrics′98 conf.,madison,WI,第1-11页),该方法利用分段压缩的思想减少方法的时间复杂度,使得查找速度大大增加,根据不同的配置,可以在3~32步之间查找完成。下面简单介绍CPE方法。
自从1993年采用CIDR(无类域间路由)协议以来,IP路由就包含两个部分<IP前缀,前缀的长度>,前缀的长度范围在0~32之间。对于每一个输入包,IP查找逻辑需要查找出匹配的IP前缀集合,并从匹配的前缀集合中查找出最长匹配的前缀。最长匹配的前缀所指示的地址就是查找出来的下一跳的地址。关于CIDR的详细内容可见:RFC1519Classless Inter-Domain Routing(CIDR):an Address Assignment andAggregation Strategy.V.Fuller,T.Li,J.Yu,& K.Varadhan.September1993.以及RFC1817 CIDR and Classful Routing.Y.Rekhter.August1995.
路由表中的规则可以表示为前缀的形式,前缀的长度在8位和32位之间变化,IP查找方法即找出最长匹配的前缀,由于前缀长度的多样性,是造成查找方法时间复杂度高的一个重要因素。对于前缀最大长度为W,NetBSD(NetBSD是一种标准的unix操作系统,该操作系统实现了TCP/IP协议栈,其中包含IP查找的实现)中的二叉树的方法复杂度为O(W),如果前缀最大长度减少为L(L<W),则方法复杂度将减少为O(L)。显而易见,减少前缀的最大长度将提高方法的速度。控制前缀扩充方法的基本思想是通过减少前缀长度的多样性来增加方法的步长,从而减少了方法的复杂度,达到快速查找的目的。
任何前缀,可以扩充为指定长度的前缀集合,如对于前缀10*,可以扩充为长度为4的前缀集合{1000*,1001*,1010*,1011*),通过扩充前缀到指定的长度,可以归一化前缀的长度,从而减少了前缀长度的多样性。扩充前缀方法可以把扩充后归一化的前缀长度分为几级,每一级前缀的长度是一样的,通过数组索引就可以找到该级别的节点。
扩充前缀tries树把原来的1比特树的32级结果压缩为3级结构,查找次数缩短为3次,方法的时间复杂度为O(3)。从而达到快速查找的目的。在同一级别中,方法可以采用直接索引数组的方式找到需要的节点。由于采用的直接索引数组的方式,所以内存占用较大,有很多的空间没有元素,造成很大的浪费,方法需要进行优化。
典型的扩充前缀树的结构如图1所示,其中原始前缀为P1=10*、P2=111*、P3=11001*、P4=1*、P5=0*、P6=1000*、P7=100000*、P8=1000000*。前缀分为3级,级别长度分别为2、5、7。
前缀扩充方法可以在级别上进行优化,由于级别的个数就是最坏查找的次数。可以想到级别越小,查找的次数越少,内存的占用也越大,综合考虑查找次数和可实现的内存占用,可以使用的级别为3、4、5级。
每一级别中前缀的长度即每一级别的步长,也是可以优化的参数,可以清楚的看到,前缀长度的不同与内存的占用有很大的关系,优化的目标是在特定的前缀集合上选取最佳的步长,使得内存的占用最少。
可变步长也是一种优化方法,由于每一级别中前缀可能没有占满,很多空间可能是浪费的。通过可变步长可以节约大概5%~10%的内存,可变步长给前缀的插入和删除造成麻烦,需要谨慎考虑。
LeafPush是一种较好的优化内存的方法。扩充前缀tries树(多叉树)的每一个节点,包含前缀和指向下一级的指针。如果把处在中间部分的前缀推到叶子的位置,那么在树中的每一个节点要么是前缀,要么是指针。每一个节点可以用一个单元来表示。这样可以节约大概一半的内存。由于需要把前缀从中间部分推到叶子所以LeafPush(叶子下移)方法在树的插入和删除上要花费一些时间。
总之,现有技术中的CPE方法存在如下问题:
1.CPE方法在前缀分散的情况下,占用内存较大,并且其最大的问题是路由修改在最坏情况下,一次路由修改需要改动262,144个单元,需要2.5ms的时间。在高速路由转发的情况下是无法容忍的。
2.CPE方法路由修改操作非常复杂,路由修改时需要借助一个“1比特辅助树”,并且在修改路由时需要进行复杂的遍历和读写操作。而且路由修改都在I/O接口板中完成,对I/O接口板的性能要求更高。
3.CPE方法的路由修改操作复杂,用硬件实现非常困难,或者说不可能实现。
因此,本发明的一个目的就是为了在CPE方法中减少内存占用,降低路由修改次数及路由修改的复杂度,使路由修改操作便于用硬件实现,实现大容量路由项(前缀)的快速修改。
具体实施方式
图2是根据本发明的前缀分解示意图。本发明首先分析了网络路由器上的实际路由数据,发现路由前缀的长度在8~32之间,即路由前缀的最小长度为8,对应于A类地址。从路由协议中也可以得到这个结论,所以前缀的长度范围可以缩小。所以,假设将前缀分为4级,每级采用16、21、26、32的配置方式,最坏情况下,路由修改的次数可以减少为256次。根据扩充前缀方法,取决于实际中的具体需要,前缀的级数及各级的配置显然可以有多种方法。并且,根据“Faster IP Lookups usingControlled Prefix Expansion”的教导,可以动态地确定级数及每级长度。因为在该文献中已有详细描述,本说明书就不再重复。在此将该文献合并至此以作参考。
在具体描述前缀分解前,先介绍一下“前缀分解”的概念。为了解决路由修改操作复杂以及硬件实现困难的问题,“前缀分解”的基本思想就是把一个复杂的操作分解为多个简单的操作。具体到本发明,就是把一个路由修改操作分解为多个原子操作。
通过分析,发明人认为如果原子前缀具有以下特点,则显然能简化操作,并且便于用硬件实现:
1、原子前缀的插入和删除在一段连续的内存进行;
2、原子前缀下游没有其他的前缀存在,插入和删除可以直接进行,不用判断前缀的长度以及覆盖关系。
发明人仔细研究了前缀的结构,发现对于一个路由修改的前缀,根据前缀的覆盖关系可以把前缀分解为多个原子前缀。有了原子前缀的思想,前缀的修改可以分为如下2个步骤:
1、分解前缀为原子前缀;2、原子前缀的插入和删除操作。
根据原子前缀的特点,原子前缀的分解如图2所示。首先,前缀分解需要辅助1比特树。关于1比特树的生成和结构是本领域技术人员的常识,在此不再详细描述。在图2的辅助1比特树中,(1)~(5)表示1比特树中的前缀,具有前缀的节点用实心点表示,没有前缀的节点用空心点表示。1比特树的左节点表示0,右节点表示1,对于前缀(3),用1比特树表示其前缀为11001*。按照CPE的方法,可以将图2中的1比特树分为例如2级,其中级别1包含2个比特,级别2包含3个比特。
如图2所示,不失一般性,这里考虑LeafPush的影响。如果需要修改前缀(4),根据前缀修改的原理,将影响(4)的下游前缀,包括前缀(1)、(2)、(3)。由于前缀(1)、(2)、(3)的长度比前缀(4)长,所以前缀(4)的修改不能覆盖前缀(1)、(2)、(3),而只能修改附加的前缀P1、P2、P3。在常规CPE方法中,找到需要修改的位置是通过遍历来实现的,关于这一点,在CPE方法中有详细介绍,这里合并至此以作参考。但是根据上面描述的本发明的原子前缀的概念,可以得出这里P1、P2和P3即是需要修改的原子前缀,即P1、P2、P3不与另外的任何前缀有覆盖关系。因此,如果能用简单的方法分解出它们,则显然能加快前缀的修改。根据本发明的一种实施例,原子前缀的一种分解方法包括如下步骤:
1.从需要分解前缀开始遍历前缀节点的所有子树;
2.如果节点上有前缀,则停止该节点所有子树的遍历;
3.如果节点上没有前缀,并且节点至少有一个子树为空,则该节点可以分解为相应的原子前缀,如图2中的节点P1、P2、P3所对应的节点。
根据上述原子前缀的分解方法,我们来分解图2中的前缀(4)。首先从(4)所对应的节点开始,沿左分支进行到节点P1′,该节点P1′无前缀,且对应的1子树为空,所述该节点P1′可分解,分解出的原子前缀为P1。按着沿P1′为非空的子树0进行,到达(1),节点(1)为实心,所以有前缀,停止该节点子树的遍历。同样可以得到前缀(4)的其它两个原子前缀为P2和P3。这样,得到了前缀(4)的三个原子前缀P1,P2和P3。
原子前缀的分解是一个递归所有子树的操作,递归操作可以通过堆栈转化为非递归操作。
图3根据原子前缀的分解方法,示出了用递归方法分解的操作流程。
图3的流程从方框300开始,进行到方框301,取需要分解的前缀,例如前缀(4)。接着到达判定方框302,确定左子树是否不为空。如果判定结果为否,即左子树为空,则左子树为原子前缀,如方框303所示。接着进行到方框304,判断右子树是否不为空,如果判定结果为否,即右子树为空,则右子树为原子前缀,如方框305所示。接着,流程在310结束。回到方框302,若判定结果为是,即左子树不为空,则判断左子树是否有前缀,如方框306所示。若左子树有前缀,则流程从方框304开始继续。如果方框306的判定结果为否,即左子树无前缀,则到达方框307,对图3所示的整个流程进行递归调用。回到方框304,若判定结果为是,即右子树不空,则流程进行到方框308,确定右子树是否有前缀,如果判定结果为是,则流程跳到方框310结束。若判定结果为否,则如方框309所示,递归调用图3流程进行前缀分解。根据本发明的原理,显然可以对图3进行一些修改,如可以先判断右子树是否为空,然后再判断左子树是否为空。
从递归方法流程图中可以看出,前缀分解为原子前缀是根据前缀的上下覆盖关系来确定的,一个前缀在级别中的剩余长度决定分解原子前缀的最多个数。如对于16、21、26、32配置的方式,长度为24的前缀,其原子前缀在级别21~26中的剩余长度为26-24=2,所以其分解的原子前缀个数最多为2+1=3个。对于剩余长度为n的前缀,最多分解的原子前缀个数如下所示:
Max_Expansion_Number(n)=n*(n+1)/2
由于递归方法效率不高,也可以用堆栈来实现非递归方法,前缀分解的非递归方法流程图如图4所示:
图4的流程从方框400开始,进行到方框401,取出需要分解的前缀并将其压栈。接着,流程进行到方框402,取出出堆栈中的前缀,并在方框403判断左子树是否不为空。若判断结果为否,即左子树为空,则左子树为原子前缀。流程继续到方框405,判断右子树是否不为空,若判断结果为否,即右子树为空,则在方框406将右子树认定为是原子前缀。流程继续到方框407,判定堆栈是否为空,如果是,进入方框412,流程结束,若为否,则回到方框402,取出堆栈中的前缀。回到方框403,若判断结果为是,即左子树不为空,则流程前进到方框408,判断左子树是否有前缀,若判断结果为是,则说明不是原子前缀,流程到达方框405。若方框408的判断为否,则说明该前缀需要继续分解,所以在方框409将其压栈。现在再看方框405,若判定结果为是,则到方框410接着判断右子树是否有前缀,若判断结果为是,则表示右子树不需要分解,也不是原子前缀,所以流程到达方框407判断堆栈是否为空。若方框410的判断结果为否,则表示该前缀需要继续分解,所以在方框411将其压入堆栈,并到达方框407判断堆栈是否为空。在实际中,是先判断左子树还是右子树的顺序可以互换。另外,方框411执行后,堆栈肯定不会为空,所以执行方框411后流程可以直接到方框402。
我们知道,对于16、21、26、32的配置,最坏情况下前缀的剩余长度为8(因为最短的IP前缀为8,如前面所分析的),所以最坏情况下,原子前缀的个数为8*(8+1)/2=36个。即最坏情况下,一个需要修改的前缀最多可以分解为36个原子前缀。
因为以上实现的前缀分解的方法考虑了LeafPush的影响,所以前缀的分解需要把前缀从前缀所在的位置,分解到前缀节点以下的辅助1比特树的所有节点。如果不采用LeafPush算法,前缀分解算法可以进一步的简化,前缀的分解可以只需要分解到前缀所在节点以下,在前缀所在当前级别以内的所有节点。这样可以大大减少分解的原子前缀的个数。
在不用LeafPush的情况下,递归算法的流程图与图3的情况很相近,只是在图3的方框301的步骤后还需要加入一个判断步骤,用来判断需要分解的前缀是否在当前级别内。若需要分解的前缀不在当前级别内,则流程结束。其余处理过程与图3一样。
在不用LeafPush的情况下,非递归算法的流程图与图4的流程也很相近,只是在图4的方框408的判断步骤中判断条件改为“左子树是否有前缀且是否在当前级别内”以及在方框410的判断步骤中判断条件改为“右子树是否有前缀且是否在当前级别内”。
图5示出了本发明的硬件实现总体方案。从原子前缀的特点可以知道,原子前缀的修改可以转化为连续的内存写操作。所以对于16、21、26、32的配置,前缀的修改操作最坏情况下可以转化为36次连续内存的写操作,即可完成路由修改。在当今内存的burst模式下,完全可以满足高速路由转发的需求。
前缀分解为原子前缀后,特别适用于路由修改的硬件实现。我们知道硬件实现查表和遍历操作是非常复杂而且不现实。如果把复杂的路由修改操作分解为原子前缀的修改操作,路由修改可以简化为连续内存的写操作,前缀修改可以在同样一种硬件指令下完成,不需要其他额外的指令和逻辑,大大简化了硬件实现方案。
本发明依据“前缀分解”的思想,结合一种具体的路由器提出了一种硬件实现方案。如图5所示,这种硬件实现方案是为了解决高速路由器的路由查找操作而设计的,不失一般性,这里假设高速路由器包含路由处理板和转发接口板两个部分,其中路由处理板根据路由协议收集路由信息,并生成路由项(前缀)。路由处理板收集到的路由项下载到转发接口板上,最终形成路由查找结构。转发接口板则接收数据报文,根据IP查找方法以及路由查找结构,查找出需要的路由,并发送出去。
下面详细描述图5。根据图5的实施例,本发明的路由器由两部分组成,路由处理板和接口板,路由处理板对整个系统进行监控,并对路由协商控制报文、VPN报文、MP报文等全局信息进行管理。接口板提供快速报文转发的功能,路由信息发生变化,路由处理板把协商好的路由项,也就是前缀,传递到接口板上进行处理。各个I/O板的结构是一样的。
针对以上的路由器的体系结构,这里采用4级数据结构的方式。
由处理板上保存1比特树结构,以及数据结构的全局信息,接口板上则保存查找结构,如果要插入或删除一条路由,可以分为以下四个步骤:
1、插入或删除该前缀f到1比特树中;
2、根据数据结构的分段信息对前缀进行分解,分解为最多8*(8+1)/2=36个前缀的原子前缀组f[];
3、将原子前缀组f[]传递到各接口板上,原子前缀组中的原子前缀可以分次传递给各接口板;
4、插入或删除原子前缀到接口板上的数据结构中。
原子前缀组分别传递到各接口板上,对于每一个分解后的前缀,都不存在覆盖原有前缀的问题,所以插入和删除操作可以简单地包括下列步骤:查找出需要修改前缀的起始地址;修改一个或多个连续的内存单元。关于修改的具体过程将在下图参照图8描述。
由于把内存分为两部分保存,1比特树保存在路由处理板,对于MaeEast数据库,采用2字节作为指针,1比特树占用内存只有700K,tries树保存在接口板,对于MaeEast数据库,采用2字节作为指针,tries树占用内存只有600K。内存的消耗在路由处理板和接口板上都很小,完全可以放在cache中。达到更快的处理速度。
图6示出了图5中的接口板的硬件实现方案。如图6所示,接口板包括一个主CPU和一个IP处理引擎以及保存Tries树结构的SRAM。在本发明中,除了完成常规功能外,CPU完成的功能是:IP处理引擎的初始化和配置功能,初始化功能可以初始化内存的使用,配置功能可以配置IP处理引擎中内存大小,查找树的级别,以及每个级别中步长的大小;和提交原子前缀的修改功能,就是把路由处理板提交的原子前缀,通过命令发送到IP处理引擎中,IP处理引擎进行原子前缀的路由修改操作。虽然在本说明中将CPU和IP处理引擎分开表示,本领域的技术人员将能够理解,它们两个可以是同一个处理器。
IP处理引擎上要进行的操作包括:IP查找逻辑和修改逻辑。例如,将IP包输入通过查找逻辑从路由地址输出引发中断。IP查找逻辑可以是常规的,也可以是本说明书中下面将要描述的硬件实现的查找逻辑。
图6的工作过程是:路由处理板提交的需要修改的原子前缀,通过通讯模块提交给主CPU,再通过主CPU提供给IP处理引擎。
图7示出了工作数据结构下的IP查找逻辑的硬件设计示意图。首先要说明的是,本例中的IP查找逻辑结构与本发明的原子前缀分解是两个可以独立使用的发明。从图7可看出,IP查找逻辑所需要实现的元操作有:字的位与操作,根据两个字进行按位与操作,如方框701;字的右移位操作,可以根据一个字的长度对字进行右移位操作,如方框702;存储器寻址操作,根据基址和偏移量进行存储器寻址,如方框703;字的比较操作,比较两个字是否相等,如方框704;内存读写操作,可以进行连续的内存写操作,和普通的内存读操作。简单的内存管理操作,可以分配和释放内存,当然也可以在主CPU上进行内存管理。
从图7中可以看出,硬件设计逻辑比较简单,可以很容易的实现。其中mask1、mask2、shift1、shift2可以是常量,可以是存储在SRAM中的变量,也可以是主CPU输入的变量。如果是常量,则意味数据结构和步长是固定的,减少了数据结构的灵活性,但对路由的查找和修改等操作没有影响。
图7的处理从输入IP包开始,接着到达方框701和701’,通过与相应级别的MASK值相与取出IP包的前缀在相应级别中的值,在本实施例中是取出在第一级别中的值和在第二级别中的值。
mask的产生是通过级别来定义的,mask1表示第一级别的mask,mask2表示第二级别的mask,以此类推。通过mask可以把相应级别的IP地址的值取出来,例如,对于级别分别为2、5、7的前缀分解树来说,mask1=1100000,mask2=0011100,mask3=0000011。由此可见对于固定步长的前缀树来说,各个级别的mask是固定的。这一步的操作是为了取出相应级别的前缀值,因此,在可变级别的情况下,相应级别的MASK值也相应变化。
方框701和701’的输出结果送入框702和702’进行移位。shift主要用来把IP地址相应级别的值移位到最低位,便于形成查找的数组指针。shift1表示第一级别的shift,shift2表示第二级别的shift,以此类推。每一级别的shift等于上一级别的步长值。对于级别分别为2、5、7的前缀树来说。shift1=0,shift2=2,shift3=3。
方框702和702’的结果分别送到方框703和703’,与存储器寻址进行相加。在方框703,因为是在第一级,所以存储器的寻址基址也就是当前节点的首地址。对于方框703’,因为这是第二级,所以要通过上级基址+输入来获得输出。输入就是通过mask和shift操作得到的该节点数组的下标指针,这样通过存储器的寻址基址加上输入的下标指针,就可以查出需要的路由ID或者下一级别的指针。在方框704和704’分别对方框703和703’的结果进行判断,看其最高位是否为1。通过最高位是否为1来表示区分该值究竟是路由ID还是下一个级别的指针。如果是路由ID,则输出。如果是下一个级别的指针,则输入到下一个级别。如果是最后一个级别,但在方框704的判断还得出最高位不为1,则表示出错。
在本实施例中考虑的是已经采用了LeafPush的情况,如果不采用LeafPush的方法,在数组的一个表项中可以同时存储路由ID和指针,这种情况下,在方框704需要判断的是指针是否为空,如果为空,则表示查到需要的路由ID,否则,获得下一个级别的指针,继续查找。
在图7中虽然与操作和移位操作是串行表示的,但实际上它们可以并行处理,这样可以提高查找的性能,使得多级的数据结构下也具有较好的查找性能。数据结构分级较多,可以显著的减少内存的占用,可以在查找性能和内存占用下取得平衡。
图7中只示出了两级结构,如果级数更多,则只要增加相应的模块即可。图7的这种查找逻辑显然特别适合用硬件来实现。
图8示出了路由修改逻辑示意图。
路由修改逻辑包含两个步骤,第一步是对输入的原子前缀进行同样的查找操作,第二步是从查找到的基址开始进行连续的内存写操作。对原子前缀的查找可以用本说明书中公开的方法,也可以用常规的任何方法。下面主要介绍内存的写操作。
这里连续的内存写操作,需要判断内存写的长度,长度的判断是根据原子前缀的长度与原子前缀所在级别的长度决定的,假设原子前缀的长度为p,原子前缀所在级的长度为q,则需要连续修改的内存长度为2的(q-p)次幂。例如,对于图2中的前缀4,其分解出来的原子前缀为P1=101、P2=1101和P3=11001。当修改P1时,因为P1的长度是3,P1在第二级,而第二级的长度是5,所以要修改的内存个数为25-3=22=4。如果原子前缀与当前级别长度相同,则只需要修改1个表项。这里的连续内存写操作写的所有表项的路由ID是相同的,可以借用内存的Burst模式来完成。另外,这里的修改包括插入和删除及覆盖。本领域的技术人员将能理解需要修改的内存长度也能以其它方式获得。
以上对本发明的实施方式进行了详细说明。下面比较一下本发明相对CPE方法在大容量路由器情况下相对内存占用而言所取的结果。
对于大容量的路由器,CPE方法没有进行分析,CPE方法局限性也体现在内存占用上,从对CPE方法的分析中可以看出,当前缀数目增大时,内存占用越来越大,内存占用的极限为4G个比特,即4096M内存。所以在远小于4096M内存的占用上,内存的消耗与前缀个数之间是非收敛的关系,按正比例增长。
由于实际数据库在一些IP基址上是聚合的,所以实际数据库占用的内存远远小于随机数据库占用的内存,这主要是因为实际的前缀往往是针对一些子网设置的,许多前缀都集中在一定的区间中,而随机前缀则是分散的。实际数据库中前缀的聚合特性使得实际数据库生成的数据结构占用内存大打减少,为了得到实际数据库中内存于随机数据库内存占用之间的关系,这里采用实际数据库MaeEast与随机数据库之间比较的办法得到实际数据库于随机数据库之间内存占用的经验公式。通过经验公式对极限数据库进行分析,以便得到可信的结论。
通过分析可以得到的经验公式表明,实际数据库占用的内存是随机数据库占用内存的10%到30%之间。由于不可能得到大的实际数据库,这里采用随机数据库进行测试,并采用经验公式进行处理,得到一百万路由项占用内存在10M~40M之间。
采用本发明的结构,可以完美的解决扩充前缀方法中的占用内存较大,插入删除时间太长的问题,在实际高速路由器中完全可以进行应用。而且该方案完全可以采用硬件进行实现,原子前缀发送到接口板后,完全可以交给IP查找逻辑FPGA或者ASIC,由硬件进行查找和路由修改操作,这样可以达到极高速的查找和路由修改性能。具体的硬件实现方案在上面有详细的描述。
虽然参照具体实施方式详细描述了本发明,但显然在不脱离本发明实质的范围内,可对以上实施方式作很多修改,变化或等同替换。