VPP FIB路径链表环路检查

对于路径链表中的三种路径类型:

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
相关推荐
张维克1 年前
华为数通HCIP-MPLS
华为·mpls·fib·lsp·ip转发