注:本文是转载,但不是100%的转载,可能稍微有些出入,原文地址点击这里
基本概念
VPP作为思科开源项目,路由系统基本继承了Cisco快速转发(Cisco Express Forwarding,CEF)设计思路。作为理解VPP路由源码第一步,首先要掌握起理论基础。
Cisco Express Forwarding
内容摘自异步社区。
虽然基于缓存的交换机制相对进程交换而言提高了转发的性能,但是它们的性能是不确定的。进程交换和基于缓存的交换都是数据驱动的(data-driven),或者说是需求驱动的(demand-driven)。换句话说,只有当数据包进入路由器的时候交换组件才会被布置到位,一旦这些数据包不被路由器转发,交换组件就会被清除。如果存在大量的不可预知流量模式的数据包,交换性能就会显著地下降。显然,在Internet的级别上,这些交换路线是不可扩展的。
创建CEF是为了避免基于缓存交换机制所固有的问题。它的设计最好地适应了频繁变化的网络态势和流量特征,这些都是由于不断增长的较短持续时间的流所产生的。典型地,这些较短持续时间的流与基于Web的应用和交互式TCP会话相关联。
CEF具有以下几个优点:
- 可扩展性(Scalability)——CEF是拓扑驱动的(topology-driven),并与路由选择表紧密相关。当激活了分布式CEF(Distributed CEF)模式时,CEF在每一块线卡(line card)上也提供了全部的交换能力。CEF支持硬件辅助的(hardware-assisted)转发方式,这是在高容量的线卡上,提供线速交换(line rate switching)能力所必需的。
- 增强的性能——CEF的CPU密集程度比路由缓存机制的CPU密集程度低。更多的CPU处理能力可以专注于第3层服务,例如,处理BGP更新。
- 弹性(Resilience)——在大型动态网络中,CEF提供了更好的交换一致性和稳定性。在这样一些网络中,由于路由选择的变化,快速交换缓存表项会频繁地失效。这些变化导致路由器使用路由选择表进程交换流量,而不是使用路由缓存快速交换流量。由于CEF查找表包含了存在于路由选择表中所有已知的路由,因此它消除了路由缓存的维护需要,并使快速交换/进程交换转发不再适用。CEF能够比典型的按需缓存机制更有效地交换流量。
注意:
IP RIB中所有路由的表项不管是否被使用,它们都需要被维护,因而CEF可能比其他交换方式需要更多的内存。
CEF是拓扑驱动的交换机制,它的转发表和路由选择表是紧密关联的。无论何时路由选择表发生变化,CEF转发表也将被更新。在表项被创建的时候,数据包被交换到更慢的交换路线上。CEF把路由缓存的功能分割成两部分:
- 转发信息库(Forwarding Information Base,FIB)
- 邻接表(ajdancency table)
FIB
FIB包含了来自于路由选择表的所有IP前缀。如果不同的路由选择表被维护着,例如在MPLS VPN环境中,那么每个VPN都有自己的FIB。FIB不是数据驱动的。更确切地说,它是通过路由选择表来创建和更新的。FIB子系统负责确保所有的递归路由(是指那些没有与直接下一跳相联系的路由)被解析。
为了增强一致性和减少查找时间,FIB被组织成一个被称为mtrie的多路(multiway)数据结构。在mtrie数据结构中,树结构用来定位所要找的数据,但是数据本身存储在其他地方。相反,mtree数据结构在树结构本身中存储了实际的数据。例如,在最优交换的mtree缓存中,用来转发数据包的MAC头的数据就是实际存储于mtree中的。
Cisco路由器通常使用两种类型的mtrie结构:
- 8-8-8-8——这种格式也被称为256-way mtrie,因为4个八位组的IPv4地址被映射到4个8bit的结构中。因此,一条前缀的最大查找次数是4次。这种格式用在大多数Cisco路由器中。
- 16-8-8——这是一个3级的mtrie,它的根级有65536个表项。因此,一条前缀的最大查找次数是3次。换句话说,第一次查找解析了前面的两个八位组,接着最多再需要2次查找就可以解析最后的两个八位组。这种格式只用于Cisco 12000系列路由器。
mtrie的每一级都被称为一个节点(node)。最后的节点被称为叶节点(leaf)。叶节点指向邻接表(adjacency table),或者当到达相同目的地的多条路径存在时,指向另一个负载分担的结构。IP FIB的内容可以使用show ip cef命令来显示。
下面列举了一些FIB表项:
- 附接的(attached)——这种前缀被配置为可以通过接口直接到达,不需要由IP下一跳来创建邻接关系。这种前缀是指路由器本地接口所属的网络。
- 连接的(connected)——这种接口是由IP addressaddress mask配置命令来配置的。所有连接的FIB表项都是附接的,但不是所有附接的表项都是连接的。
- 收到(receive)——这种前缀是一个32位掩码的主机地址,它是路由器始终接收到的主机地址之一。每个接口通常有3种这样的地址:实际的接口地址、全0的子网和全1的广播地址。(博主理解为就是本机local地址)
- 递归的(recursive)——当前缀的输出接口不能通过路由选择协议或静态配置指定时,它就被标记为递归的。当找不到递归FIB表项的下一跳IP地址时,这个递归FIB表项也许就不能被解析。因此,递归标记实际上与下一跳地址,而不是与FIB表项相关联。
邻接表
邻接表被创建,以包含所有连接的下一跳地址。邻接节点(adjacency node)是指通过链路层一跳就可以到达的节点。一旦邻居变成了邻接的关系,用来到达那个邻居的链路层帧头——被称为MAC字符串或者MAC改写字符串——就会被创建,并且被保存在邻接表中。例如,在以太网段上,帧头信息就是目的地MAC地址、源MAC地址和以太类型(EtherType),并按以太网的定义来排列。
例2-6显示了以太网的MAC头。在这个例子中,00044EB31838是目的地MAC地址,0003E4BB2000是源MAC地址,0800是IP的以太类型。
一旦路由被解析,它就会指向一个邻接的下一跳地址。如果在邻接表中发现了一个邻接,那么与这个邻接相对应的一个指针就会被缓存到FIB单元中。如果存在到达相同目的地的多条路径(也就是说,存在多个下一跳或者邻接),那么每一个邻接的指针都会被增添到负载分担的结构中去。在CEF中,基于每数据包的负载分担在中断级别上是有用的。
除了上述情况,还存在几种异常邻接的类型。当前缀被增加到FIB表中时,需要异常处理(exception handling)的前缀被缓存为特殊的邻接。下面列举了一些特殊的邻接:
- Null——这种邻接针对那些指向Null 0接口的、要被丢弃的数据包。
- Glean——这种邻接针对那些通过广播网络附接的目的地,但是其广播网络又没有对应可用的MAC改写字符串。可以设想路由器直连到一个包含几台主机的子网的情形。路由器的FIB表维护着这个子网的前缀,而不是单个的主机前缀。这个子网前缀就指向一个glean邻接。当数据包需要被转发到一台特定的主机时,为这个特定的前缀就需要收集(glean)邻接数据库。这将导致额外的查找成本。(博主理解为,就是同一个网络中的主机,目前mac地址不知道,所以用glean表项标记其连接表项,然后再由arp协议来填充)
- Punt——如果数据包不被CEF支持,那么它们就会被转发给下一级慢速的交换路线来处理。
- Drop——是指要丢弃这种数据包,因为这些数据包不能被CEF交换,或者不能被踢给(punt)其他交换路线处理。
- Discard——类似于Drop邻接,但是只应用于Cisco 12000系列路由器。
下图显示了CEF的所有组件之间的关联关系。