IPv6路由节点查找

  路由表查找函数首先调用fib6_node_lookup查找路由节点,由指定路由表的根节点开始,根据数据流的目的和源地址进行

static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
                       struct lookup_args *args)
{
    struct fib6_node *fn;
    __be32 dir;

    if (unlikely(args->offset == 0))
        return NULL;

    /*
     *    Descend on a tree

     以下遍历由根节点开始,如果遍历节点的前缀长度,在所查找地址中对应的位为1,向右子树继续查找,
     如果为零,向左子树继续查找。一直到节点为空为止。
     */

    fn = root;

    for (;;) {
        struct fib6_node *next;

        dir = addr_bit_set(args->addr, fn->fn_bit);

        next = dir ? fn->right : fn->left;

        if (next) {
            fn = next;
            continue;
        }
        break;
    }
/*
    如果以上找到了匹配的节点,检查此节点是否具有合法的路由信息(RTN_RTINFO),并且其叶子节点存在,按照叶子节点中保存的地址和前缀长度,
    与待查找地址进行比较(ipv6_prefix_equal),如果相等,即为要找的路由节点。
    */
    while (fn) {
        if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
            struct rt6key *key;

            key = (struct rt6key *) ((u8 *) fn->leaf +
                         args->offset);

            if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
#ifdef CONFIG_IPV6_SUBTREES
/* 否则,检查节点树的父节点,向树根方向(backtrack),此方向节点的前缀长度(fn_bit)在减小。此函数遵循LPM最长匹配算法,
如果存在最长匹配的路由节点,优先选择。
    */            if (fn->subtree) {
                    struct fib6_node *sfn;
                    sfn = fib6_lookup_1(fn->subtree,
                                args + 1);
                    if (!sfn)
                        goto backtrack;
                    fn = sfn;
                }
#endif
                if (fn->fn_flags & RTN_RTINFO)
                    return fn;
            }
        }
#ifdef CONFIG_IPV6_SUBTREES
backtrack:
#endif
        if (fn->fn_flags & RTN_ROOT)
            break;

        fn = fn->parent;
    }

    return NULL;
}
static __be32 addr_bit_set(const void *token, int fn_bit)
{//t确定地址中的第fn_bit位的值

    const __be32 *addr = token;
    /*
     * Here,
     *    1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
     * is optimized version of
     *    htonl(1 << ((~fn_bit)&0x1F))
     * See include/asm-generic/bitops/le.h.
     首先将IPv6地址分成4个32位的数组,由运算[fn_bit>>5]确定数组的索引值;
     其次,确定fn_bit在此32位中的具体位置,此值不能大于32(由0x1f与操作保证),
     由于fn_bit是由高位向低位表示的,需要取反操作将此顺序反过来。
     */
    return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
           addr[fn_bit >> 5];
}

 

posted @ 2022-03-29 14:17  codestacklinuxer  阅读(74)  评论(0)    收藏  举报