对于路径链表中的三种路径类型:
FIB_PATH_TYPE_RECURSIVE
FIB_PATH_TYPE_ATTACHED_NEXT_HOP
FIB_PATH_TYPE_ATTACHED
在添加新的路径时需要进行路径环路检查。函数fib_path_list_recursive_loop_detect会被反复调用。
static void
fib_entry_recursive_loop_detect_i (fib_node_index_t path_list_index)
{
fib_node_index_t *entries = NULL;
fib_path_list_recursive_loop_detect(path_list_index, &entries);
vec_free(entries);
}
FIB路径链表环路检查
函数fib_path_list_recursive_loop_detect根据path_list_index获取FIB路径链表,遍历路径向量(fpl_paths)中的每个路径。
int
fib_path_list_recursive_loop_detect (fib_node_index_t path_list_index, fib_node_index_t **entry_indicies)
{
fib_node_index_t *path_index;
int is_looped, list_looped;
fib_path_list_t *path_list;
list_looped = 0;
path_list = fib_path_list_get(path_list_index);
函数fib_path_recursive_loop_detect对路径(path_index)进行检查,是否和copy_ptr中的路径有环路。对于第一个路径,copy_ptr为空,无环路。函数被再次调用的时候,entry_indicies不为空。
copy_ptr向量保存已经进行过环路检查的FIB表项(参见FIB表项环路检查一节)。list_looped表示FIB路径列表中的环路数量。
vec_foreach (path_index, path_list->fpl_paths)
{
fib_node_index_t *copy, **copy_ptr;
/* we need a copy of the nodes visited so that when we add entries
* we explore on the nth path and a looped is detected, those entries
* are not again searched for n+1 path and so finding a loop that does not exist.
*/
copy = vec_dup(*entry_indicies);
copy_ptr = ©
is_looped = fib_path_recursive_loop_detect(*path_index, copy_ptr);
list_looped += is_looped;
vec_free(copy);
}
根据list_looped的值,路径链表设置环路标志(FIB_PATH_LIST_FLAG_LOOPED)。
FIB_PATH_LIST_DBG(path_list, "loop-detect: eval:%d", list_looped);
if (list_looped) {
path_list->fpl_flags |= FIB_PATH_LIST_FLAG_LOOPED;
} else {
path_list->fpl_flags &= ~FIB_PATH_LIST_FLAG_LOOPED;
}
return (list_looped);
FIB路径环路检查
以上为FIB路径链表的环路检查函数。以下为FIB路径的环路检查。首先排除DROP动作的路径,其不需要解析(resolve),也不会有环路。
int
fib_path_recursive_loop_detect (fib_node_index_t path_index, fib_node_index_t **entry_indicies)
{
fib_path_t *path = fib_path_get(path_index);
/* the forced drop path is never looped, cos it is never resolved. */
if (fib_path_is_permanent_drop(path))
return (0);
对于RECURSIVE递归类型的FIB路径,遍历参数entry_indicies中所有的表项索引,如果存在和要检查的递归路径所依赖的FIB(fp_via_fib)相同的情况,表明存在环路。
此FIB递归路径本身是RECURSIVE类型,并且要通过FIB路径链表中的某个FIB路径去解析(RESOVLE),添加到路径链表将导致环路。
switch (path->fp_type)
{
case FIB_PATH_TYPE_RECURSIVE:
{
fib_node_index_t *entry_index, *entries = *entry_indicies;
int looped = 0;
vec_foreach(entry_index, entries) {
if (*entry_index == path->fp_via_fib) {
/* the entry that is about to link to this path-list (or one of this path-list's children)
* is the same entry that this recursive path resolves through. this is a cycle. abort the walk. */
looped = 1;
break;
}
}
如果发现环路,将FIB路径的DPO设置为DPO_DROP类型的DPO,避免转发流量,设置路径的运行标志FIB_PATH_OPER_FLAG_RECURSIVE_LOOP。
if (looped) {
FIB_PATH_DBG(path, "recursive loop formed");
path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RECURSIVE_LOOP;
dpo_copy(&path->fp_dpo, drop_dpo_get(path->fp_nh_proto));
如果以上没有发现环路,递归检查FIB路径依赖的FIB表项。fp_via_fib是否和路径链表path-list中的已有表项(entry_indicies)有环路。根据结果为FIB路径设置或者清除标志位FIB_PATH_OPER_FLAG_RECURSIVE_LOOP。
} else {
/* no loop here yet. keep forward walking the graph. */
if (fib_entry_recursive_loop_detect(path->fp_via_fib, entry_indicies)) {
FIB_PATH_DBG(path, "recursive loop formed");
path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RECURSIVE_LOOP;
} else {
FIB_PATH_DBG(path, "recursive loop cleared");
path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RECURSIVE_LOOP;
}
}
break;
}
对于直连类型的FIB路径,如果DPO是邻居类型,检查邻居是否有环路,设置相应的标志位。
case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
case FIB_PATH_TYPE_ATTACHED:
if (dpo_is_adj(&path->fp_dpo) && adj_recursive_loop_detect(path->fp_dpo.dpoi_index, entry_indicies)) {
FIB_PATH_DBG(path, "recursive loop formed");
path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RECURSIVE_LOOP;
} else {
FIB_PATH_DBG(path, "recursive loop cleared");
path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RECURSIVE_LOOP;
}
break;
以下这些FIB路径类型不可能形成环路。
case FIB_PATH_TYPE_SPECIAL:
case FIB_PATH_TYPE_DEAG:
case FIB_PATH_TYPE_DVR:
case FIB_PATH_TYPE_RECEIVE:
case FIB_PATH_TYPE_INTF_RX:
case FIB_PATH_TYPE_UDP_ENCAP:
case FIB_PATH_TYPE_EXCLUSIVE:
case FIB_PATH_TYPE_BIER_FMASK:
case FIB_PATH_TYPE_BIER_TABLE:
case FIB_PATH_TYPE_BIER_IMP:
/* these path types cannot be part of a loop, since they are the leaves of the graph. */
break;
}
return (fib_path_is_looped(path_index));
FIB表项环路检查
如果FIB表项没有链接在任何FIB路径链表(fe_parent有效)中,不需要进行环路检查。
int
fib_entry_recursive_loop_detect (fib_node_index_t entry_index, fib_node_index_t **entry_indicies)
{
fib_entry_t *fib_entry;
int was_looped, is_looped;
fib_entry = fib_entry_get(entry_index);
这里更新entry_indicies。再次调用开始时的fib_path_list_recursive_loop_detect函数,检查路径链表fe_parent中的路径是否与entries有环路。
if (FIB_NODE_INDEX_INVALID != fib_entry->fe_parent)
{
fib_node_index_t *entries = *entry_indicies;
vec_add1(entries, entry_index);
was_looped = fib_path_list_is_looped(fib_entry->fe_parent);
is_looped = fib_path_list_recursive_loop_detect(fib_entry->fe_parent, &entries);
*entry_indicies = entries;
如果FIB路径链表fe_parent的环路状态发生变化,重新评估所有表项的转发属性,设置load-balance。
if (!!was_looped != !!is_looped) {
/* re-evaluate all the entry's forwarding. NOTE: this is an inplace modify */
fib_entry_delegate_type_t fdt;
fib_entry_delegate_t *fed;
FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
{
fib_entry_src_mk_lb(fib_entry, fib_entry_get_best_source(entry_index),
fib_entry_delegate_type_to_chain_type(fdt), &fed->fd_dpo);
});
}
FIB表项没有链接到FIB路径链表。
} else {
/* the entry is currently not linked to a path-list. this happens
* when it is this entry that is re-linking path-lists and has thus broken the loop
*/
is_looped = 0;
}
return (is_looped);
DROP路径
如果路径fib_path_t,配置了FIB_PATH_CFG_FLAG_DROP标志,应该类似于Linux中的黑洞路由。或者设置了运行标志FIB_PATH_OPER_FLAG_DROP,返回真,认为此路径为permanent_drop。
static int
fib_path_is_permanent_drop (fib_path_t *path)
{
return ((path->fp_cfg_flags & FIB_PATH_CFG_FLAG_DROP) ||
(path->fp_oper_flags & FIB_PATH_OPER_FLAG_DROP));
}
FIB路径环路标识
检查路径结构的操作标志(fp_oper_flags),如果设置了FIB_PATH_OPER_FLAG_RECURSIVE_LOOP,表明路径有环路。
int
fib_path_is_looped (fib_node_index_t path_index)
{
fib_path_t *path;
path = fib_path_get(path_index);
return (path->fp_oper_flags & FIB_PATH_OPER_FLAG_RECURSIVE_LOOP);
}
邻居环路
以下类型的DPO都属于邻居类型。
int dpo_is_adj (const dpo_id_t *dpo)
{
return ((dpo->dpoi_type == DPO_ADJACENCY) ||
(dpo->dpoi_type == DPO_ADJACENCY_INCOMPLETE) ||
(dpo->dpoi_type == DPO_ADJACENCY_GLEAN) ||
(dpo->dpoi_type == DPO_ADJACENCY_MCAST) ||
(dpo->dpoi_type == DPO_ADJACENCY_MCAST_MIDCHAIN) ||
(dpo->dpoi_type == DPO_ADJACENCY_MIDCHAIN));
}
对于除去MIDCHAIN和MCAST_MIDCHAIN之外,其它的IP_LOOKUP_NEXT_*节点都是终止图节点,没有成环的可能性。MIDCHAIN类型通常为隧道接口使用,添加外层IP头部数据使用,存在环路的可能性。
int adj_recursive_loop_detect (adj_index_t ai, fib_node_index_t **entry_indicies)
{
ip_adjacency_t * adj = adj_get(ai);
switch (adj->lookup_next_index) {
case IP_LOOKUP_NEXT_REWRITE:
case IP_LOOKUP_NEXT_ARP:
case IP_LOOKUP_NEXT_GLEAN:
case IP_LOOKUP_NEXT_MCAST:
case IP_LOOKUP_NEXT_BCAST:
case IP_LOOKUP_NEXT_DROP:
case IP_LOOKUP_NEXT_PUNT:
case IP_LOOKUP_NEXT_LOCAL:
case IP_LOOKUP_NEXT_ICMP_ERROR:
case IP_LOOKUP_N_NEXT:
/* these adjacency types are terminal graph nodes, so there's no
* possibility of a loop down here. */
break;
对于MIDCHAIN类型,进行检查。
case IP_LOOKUP_NEXT_MIDCHAIN:
case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
return (adj_ndr_midchain_recursive_loop_detect(ai, entry_indicies));
}
return (0);
如果MIDCHAIN邻居adj解析所需要的FIB表项(fei索引),与entry_indicies向量中的某个表项索引相等,表明存在路由环路,设置ADJ_FLAG_MIDCHAIN_LOOPED标志。
int adj_ndr_midchain_recursive_loop_detect (adj_index_t ai, fib_node_index_t **entry_indicies)
{
fib_node_index_t *entry_index, *entries;
ip_adjacency_t * adj = adj_get(ai);
entries = *entry_indicies;
vec_foreach(entry_index, entries) {
if (*entry_index == adj->sub_type.midchain.fei) {
/* The entry this midchain links to is already in the set of visited entries, this is a loop */
adj->ia_flags |= ADJ_FLAG_MIDCHAIN_LOOPED;
return (1);
}
}
adj->ia_flags &= ~ADJ_FLAG_MIDCHAIN_LOOPED;
return (0);
路由环路示例
如下三条recursive类型的路由形成环路。
ip route add 5.5.5.5/32 via 6.6.6.6
ip route add 6.6.6.6/32 via 7.7.7.7
ip route add 7.7.7.7/32 via 5.5.5.5
对应以下三个FIB表项。
vpp# show fib entry
FIB Entries:
7@5.5.5.5/32
unicast-ip4-chain
[@0]: dpo-load-balance: [proto:ip4 index:10 buckets:1 uRPF:8 to:[0:0]]
[0] [@0]: dpo-drop ip4
8@6.6.6.6/32
unicast-ip4-chain
[@0]: dpo-load-balance: [proto:ip4 index:9 buckets:1 uRPF:10 to:[0:0]]
[0] [@0]: dpo-drop ip4
9@7.7.7.7/32
unicast-ip4-chain
[@0]: dpo-load-balance: [proto:ip4 index:11 buckets:1 uRPF:9 to:[0:0]]
[0] [@0]: dpo-drop ip4
以下路由链表:13,14,15都设置了loop标志。并且,其中的路径都设置了recursive-loop标志,对应于fib_path_t结构中成员fp_cfg_flags中标志位FIB_PATH_OPER_ATTRIBUTE_RECURSIVE_LOOP。
vpp# show fib path-lists
FIB Path Lists
path-list:[13] locks:2 flags:shared,looped, uPRF-list:8 len:0 itfs:[]
path:[13] pl-index:13 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 6.6.6.6 in fib:0 via-fib:8 via-dpo:[dpo-drop:0]
path-list:[14] locks:2 flags:shared,looped, uPRF-list:10 len:0 itfs:[]
path:[14] pl-index:14 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 7.7.7.7 in fib:0 via-fib:9 via-dpo:[dpo-drop:0]
path-list:[15] locks:2 flags:shared,looped, uPRF-list:9 len:0 itfs:[]
path:[15] pl-index:15 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 5.5.5.5 in fib:0 via-fib:7 via-dpo:[dpo-drop:0]
命令show logging查看在加入最后一条路由之后的日志。在函数fib_path_recursive_loop_detect中打印了形成环路的FIB路径(path)的详细信息,在函数fib_path_list_recursive_loop_detect中打印了形成环路的路径链表(path-list)的详细信息。
日志"loop-detect: eval:1"表明环路的数量为1.
fib/path [path:[15] pl-index:15 ip4 weight=1 pref=0 recursive: via 5.5.5.5 in fib:0 via-fib:7 via-dpo:[dpo-load-balance:10]]: recursive loop formed
fib/path-list [path-list:[15] locks:2 flags:shared, uPRF-list:7 len:0 itfs:[]
path:[15] pl-index:15 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 5.5.5.5 in fib:0 via-fib:7 via-dpo:[dpo-drop:0]
]:loop-detect: eval:1
fib/path [path:[14] pl-index:14 ip4 weight=1 pref=0 recursive: via 7.7.7.7 in fib:0 via-fib:9 via-dpo:[dpo-load-balance:11]]: recursive loop formed
fib/path-list [path-list:[14] locks:2 flags:shared, uPRF-list:8 len:0 itfs:[]
path:[14] pl-index:14 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 7.7.7.7 in fib:0 via-fib:9 via-dpo:[dpo-load-balance:11]
]:loop-detect: eval:1
fib/path [path:[13] pl-index:13 ip4 weight=1 pref=0 recursive: via 6.6.6.6 in fib:0 via-fib:8 via-dpo:[dpo-load-balance:9]]: recursive loop formed
fib/path-list [path-list:[13] locks:2 flags:shared, uPRF-list:9 len:0 itfs:[]
path:[13] pl-index:13 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 6.6.6.6 in fib:0 via-fib:8 via-dpo:[dpo-load-balance:9]
]:loop-detect: eval:1
fib/path [path:[15] pl-index:15 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,via 5.5.5.5 in fib:0 via-fib:7 via-dpo:[dpo-drop:0]]: recursive loop formed
fib/path-list [path-list:[15] locks:2 flags:shared,looped, uPRF-list:7 len:0 itfs:[]
path:[15] pl-index:15 ip4 weight=1 pref=0 recursive: oper-flags:recursive-loop,
via 5.5.5.5 in fib:0 via-fib:7 via-dpo:[dpo-drop:0]
]:loop-detect: eval:1