一条路由可以通过不止实体或者源的方式加入到FIB中。所谓的源包括: API, CLI命令行接口),LISP(Locator/Identifier Separation Protocol RFC6830),MAP等(所有的源可在fib_entry.h中查看到)。每种源提供了路由需要的确定FI(转发信息)。由于每种源使用不同的最佳路径和防止环路算法来确定FI,因而合并由多个源确定的FI将带来错误。FIB必须选择使用其中一个源提供的FI。这个选择是基于一个分配的静态优先级。例如:
如果是通过接口配置添加一个IP前缀(prefix):
set interfacc address 192.168.1.1/24 GigE0
然后也通过命令行接口添加一个前缀
ip route 192.168.1.1/32 via 2.2.2.2/32
那么“接口”源会获胜,因而产生一条”本地”路由(命令行接口优先级低,它配置的路由竞争失败)。
FIB中总是安装竞争中获胜的源产生的FI,并且维护竞争失败的源产生的FI,以便在获胜的源失效被删除时可以使用竞争失败源的FI。
adj-fib维护
当ARP或ND在链路上发现一个邻居时,会产生一个表示邻居地址的临接(adjacency)信息。此时也需要在合适的FIB表中插入一条路由,这条路由关联了链路的VRF,是表示一个邻居的条目。该条目被成为adj-fib。Adj-fibs有一个专用的源:“ADJ”。
ADJ源比其他很多源的优先级都低。看以下的配置操作:
set interface address 192.168.1.1/32 GigE0
ip arp 192.168.1.2 GigE0 dead.dead.dead
ip route add192.168.1.2 via 10.10.10.10 GigE1
这个操作将指向192.168.1.2的流量经过GigE1转发。因为通过控制平面添加的路由比通过ARP发现的临接更受青睐(优先级高)。控制平面有相关的认证机制,被认为是权威的源。
为了应对极端情况下临接注入,而导致极端情况的adj-fib添加到FIB中,FIB必须保证只有那些模糊覆盖前缀(less specific covering prefix,covering覆盖可以理解成掩码长度)是连通着的adj-fibs才可以加入到FIB中。这样就需要实现“覆盖跟踪”(cover tracking),也就是一条路由需要维护和它的模糊覆盖的依赖关系。当这个覆盖发生变化(如有了新的覆盖[掩码]路由)或者这个覆盖对应的路由信息发生变化,覆盖路由都要被通知到。
由于不支持重叠子网,因而adj-fib不可能有多个路径。
控制平面需要在接口改变VRF之前,删除接口的前缀配置。所以当下边的配置被应用时:
setinterface address 192.168.1.1/32 GigE0
ip arp 192.168.1.2 GigE0 dead.dead.dead
setinterface ip table GigE0 2(改变VRF)
这将得不到想要的结果。
附加: 导出操作
对以上adj-fib的进一步说明,考虑你以下的配置:
set interface address 192.168.1.1/24 GigE0
ip route addtable2192.168.1.0/24 GigE0
指向192.168.1.2的流量在表(table)2中将会从GigE0上产生一个ARP请求。然而由于GigE0在表(table)0中,所有adj-fibs都会加入到FIB 0当中。所以,表2中子网的所有主机将不可达。为了解决这个问题,所有的adj-fib和本地前缀(prefixes)从表 0(称作导出表)中被导出(拷贝),导入到表 2(称作导入表)中。一个导出表可以导出到多个导入表中。
递归路由解析
一项递归路由有以下的形式:
1.1.1.1/32 via 10.10.10.10
也就是没有指定路由的出接口。为了将流量转发到1.1.1.1/32,FIB 必须首先确定如何将流量转发到10.10.10.10/32。这就是递归解析。
像常规解析一样,递归解析使用最长前缀匹配寻找中转地址10.10.10.10。注意,路由项的中转地址(via后边的地址)只能是一个地址(32位掩码或IPV6的128位掩码),不允许是有更短掩码的前缀(这种情况没有意义)。
FIB中用于解析递归路由的条目被称为中转条目(via-entry)。由于递归解析使用最长前缀匹配,当FIB中有其他路由项加入时,中转条目可能会发生变化。考虑上边递归路由,以及下边的非递归路由:
10.10.10.0/24 via 192.168.16.1 GigE0
条目10.10.10.0/24将会被选中为由于解析的中转条目。如果该条目被修改,也就是说:
10.10.10.0/24 via 192.16.1.3 GigE0
那么指向1.1.1.1/32的报文,必须被送往新的下一跳。
现在考虑新增一条配置;
10.10.10.0/28 via 192.168.16.2 GigE0
更为精确的/28位掩码,是更好的最长前缀匹配,因而它就变成的中转条目。删除/28的条目,意味着/24位的条目将重新变为中转条目。这种变化的跟踪在FIB递归解析中是必须的。当中转条目的转发信息发生变化时,使用回退操作来更新递归路由的依赖。当新的路由项加入到路由表时,覆盖跟踪(cover tracking)特性会为中转条目路由提供必要的通知。
为1.1.1.1/32生成的邻接是一个递归邻接,它的下一个邻接将由中转条目贡献。维护递归邻接的有效性也是FIB必须要求的。
避免递归产生的环
考虑一下这组路由:
1.1.1.1/32 via 2.2.2.2
2.2.2.2/32 via 3.3.3.3
3.3.3.3/32 via 1.1.1.1
以上被称作一个递归环——所有的递归路由条目都没有被解析,因为他们没有一个解析出了邻接;然而所有每一条路由都被解析了,因为他的中转条目是确定的。非常重要的一点是,我们必须清楚的意识到控制平面对象和数据平面对象的区别(更多细节参考实现部分)。控制平面对象必须允许环的形成(也就是图变成了循环的),但是数据平面必须不能允许环的形成,否则报文无限循环,永远也不可能从设备中发送出去了——这将引起灾难。控制平面必须允许环形成,因为当环被打破时,所有的环中的成员需要被更新。形成环可以使依赖关系正确的建立。
VPP没有对递归的深度做限制,所以:
9.9.9.100/32 via 9.9.9.999.9.9.99/32 via 9.9.9.989.9.9.98/32 via 9.9.9.97... turtles, turtles, turtles ...9.9.9.1/32 via 10.10.10.10 Gig0
尽管有许多“接力条目”(turtles),但这样的配置是支持的。然后,当回走这个图时(本例中:从9.9.9.1/32回走到9.9.9.100/32,[9.9.9.1/32 变化是其他都要更新]),FIB需要区分这是一个很深的递归还是一个环。一个VPP使用的简单的办法是,限制回走的步数。VPP FIB限制是16。典型的BGP场景下,不会超过3(BGP Inter-AS option C)。
考虑下边的网络拓扑:
C
/
X -- A --- B - Y
| |
D F
/
E
所有的链路都有相同的开销,流量从X到Y。最佳路径是X-A-B-Y。还有两条可选的路径。一条通过C,一条通过E。如果其它两条可选路径上的路由器不会将流量转发的发送者,那个这两条路径是无环的。考虑路由器C,它到Y的最佳路径是通过B,所以如果A转发目的到Y的流量给C,那么C也会将流量转发给B——因而这是一个无环路径。相反地,考虑路由器D,D到Y的最短路径是通过A,如果A将目的为Y的流量转发给D,那么D将会把流量回送给A。这不是一个无环的路径。有几点是需要注意的:
Y/16-CE1--PE1---|P1---|PE3--CE3-X/16|-P2---/Y/16-CE2--PE2---/
流量从CE3到Y/16。在PE1上配置的路由为:
Y/16 (and hundreds of thousands of others like it)
via CE1 (primary首选)
via PE2 (backup备份)
and
CE1 (this is an adj-fib)
via 11.0.0.1 Link0 (this is CE1) << this is an adj-fib
PE2 (PE2's loopback address PE2的回环地址)
via 10.0.5.5 Link1 (this is link PE1-PE2)
在eBGP PIC-edge的场景下,首选和备份路径是BGP计算出来的。每个对等端配置成总是将它的最好外部路径通告给它的iBGP对等端。备选路径将流量从备份PE发送到可替代的PE的核心。一个PE可能有多外部路径,也就是有多个直连CE;它也可能有多个备份PE,但是这两种备份没有关系。因而,不像LFA-FRR,这里的收敛模型是N-M;N个首先路径并有M个备份路径。只有当所有的首选路径失效时,才会切换到备份路径。注意,PE2必须有合适的配置来转发它从PE1接收到的外部路径上的流量。VPP FIB不支持external-internal-BGP (eiBGP 外部到内部BGP)的负载均衡。
就LFA-FRR来说,首选和备份路径的使用现在还没有被支持。但是递归多路径邻接以及一个合适受约束的,用来从首选和备份路径集中做出选择的哈希算法,会提供所需的共享对象,以及进一步的不依赖前缀规模的路径切换。
聪明的读者应该意识到,两种EGBP PIC场景只适用于无BGP核心(BGP free core)。
根据上边大纲的要求,不是所有FIB知道的路由的都被真正由于转发的(比如adj-fibs)。然而,万一情况发生变化,那些之前没有用上的路由就被用上了。这需要FIB维护两张表,每张都有VRF,AF(这张表用前缀索引)——转发表和非转发表[应该是这个意思]。
VPP为了DP(数据平面)速度,就希望在转发表中的查询结果直接就是ADJ。所以这两张表,一个包含所有的路由(一次查找产生一个fib_entry_t结构数据),另一个只包含转发路由(一次查找产生一个ip_adjacency_t结构数据)。后者被用在DP。
这使用内存换取了转发性能。在VPP操作环境中是一个不错的交易。
注意这些表都是只是有前缀来作为键值的。邻接的键值是元组{next-hop, address (and its AF), interface, link/ether-type}。
考虑以下较少见但是合法的配置:
set int ip addr 10.0.0.1/24 Gig0
set ip arp Gig0 10.0.0.2 dead.dead.dead
# 这个子网中的主机使用更好下一跳来路由 (它避免了一个大的二层的域)
ip route add 10.0.0.2 Gig1 192.168.1.1# 这个递归的应该使用 Gig1
ip route add 1.1.1.1/32 via 10.0.0.2# 这个非递归的应该使用 Gig0
ip route add 2.2.2.2/32 via Gig0 10.0.0.2
对于最后一条路由,使用{Gig0, 10,0,0,2}在前缀表了查找路径不会产生正确的结果。为了修正这个问题我们需要一个单独的邻接表。