C/C++教程

FIB nexthop Exception是什么 转载

本文主要是介绍FIB nexthop Exception是什么 转载,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

转载自:

理论

3.6版本内核移除了FIB查询前的路由缓存,取而代之的是下一跳缓存,这在路由缓存的前世今生 中已经说过了。本文要说的是在该版本中引入的另一个概念:FIB Nexthop Exception,用于记录下一跳的例外情形。

它有什么用呢?

  内核通过查询转发信息表(fib_lookup),得到下一跳(fib_nh),从而得到了关于此条路由的相关信息,其中重要的包括下一跳设备nh_dev,下一跳网关nh_gw等。这些表项基本是稳定的。然而,但内核实际发包时,关于此条路由可能存在两个变数exception,其一是这条路由相关的路径MTU(PMTU)发生改变;其二是收到了关于此条路由的ICMP重定向报文。由于此两种改变并不是永久的,内核将他们保存在下一跳fib_nh的exception

  • 收到过ICMP REDIRECT报文,表示之前发送的报文绕路了,之后的报文应该修改报文的下一跳。
  • 收到过ICMP FRAGNEEDED报文,表示之前的报文太大了,路径上的一些设备不接受,需要源端进行分片。

这两种情况是针对单一目的地址的,什么意思呢?已PMTU为例,在下面的网络拓扑中,我在主机A上配置了下面一条路由

topo

ip route add 4.4.0.0/16 via 4.4.4.4

意思是向所有目的地址在4.4.0.0/16的主机发包,下一跳都走192.168.99.1。

当A发送一个长度为1500的IP报文给C时 ,中间的一台网络设备B觉得这个报文太大了,因此它向A发送ICMP FRAGNEEDED报文,说我只能转发1300以下的报文,请将报文分片。A收到该报文后怎么办呢?总不能以后命中这条路由的报文全部按1300发送吧,因为并不是所有报文的路径都会包含B。

这时FIB Nexthop Exception就派上用场了,他可以记录下这个例外情况。当发送报文命中这条路由时,如果目的地址不是C,那么按1500进行分片,如果是C,则按1300进行分片。

实现

内核中使用fib_nh_exception表示这种例外表项

(include/net/ip_fib.h)

struct fib_nh_exception {
    struct fib_nh_exception __rcu    *fnhe_next;  /*  冲突链上的下个fib_nh_exception结构 */
    __be32                fnhe_daddr;              /*  例外的目标地址                     */
    u32                  fnhe_pmtu;                 /*  收到的ICMP FRAGNEEDED通告的PMTU    */
    __be32                fnhe_gw;                 /*  收到的ICMP REDIRECT通告的网关      */         
    unsigned long            fnhe_expires;        /*  该例外表项的过期时间                */
    struct rtable __rcu        *fnhe_rth;           /*  关联的路由缓存                     */
    unsigned long            fnhe_stamp;
};

 

每一个下一跳结构fib_nh上有一个指针指向fnhe_hash_bucket哈希桶的指针:

struct fib_nh {
   ------------------------------- 
    /* code omitted */
    struct fnhe_hash_bucket    *nh_exceptions;
};

  哈希桶在update_or_create_fnhe中创建,每个哈希桶包含2048条冲突链,每条冲突链可以存5个fib_nh_exception

以PMTU为例,在收到网络设备返回的ICMP FRAGNEEDED报文后,会调用下列函数将通告的pmtu值记录到fib_nh_exception上(也会记录到绑定的路由缓存rtable上)

static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
{
    /* */
    if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) {
        struct fib_nh *nh = &FIB_RES_NH(res);

        update_or_create_fnhe(nh, fl4->daddr, 0, mtu,
                      jiffies + ip_rt_mtu_expires);
    }
}

 

而在发包流程查询FIB之后,会首先看是否存在以目标地址为KEY的例外表项,如果有,就使用其绑定的路由缓存,如果没有就使用下一跳上的缓存

static struct rtable *__mkroute_output(const struct fib_result *res,
                       const struct flowi4 *fl4, int orig_oif,
                       struct net_device *dev_out,
                       unsigned int flags)
{
    /* code omitted */
    
    if (fi) {
        struct rtable __rcu prth;
        struct fib_nh *nh = &FIB_RES_NH(*res);

        fnhe = find_exception(nh, fl4->daddr);      //  查找 fl4->daddr 是否存在 fib_nh_exception
        if (fnhe)
            prth = &fnhe->fnhe_rth;                  // 如果有,直接使用其绑定的路由缓存
        else {
            if (unlikely(fl4->flowi4_flags &
                     FLOWI_FLAG_KNOWN_NH &&
                     !(nh->nh_gw &&
                       nh->nh_scope == RT_SCOPE_LINK))) {
                do_cache = false;
                goto add;
            }
            prth = __this_cpu_ptr(nh->nh_pcpu_rth_output);   // 如果没有,使用下一跳上缓存的路由缓存
        }
        rth = rcu_dereference(*prth);
        if (rt_cache_valid(rth)) {
            dst_hold(&rth->dst);
            return rth;
        }
    }
}

 

这篇关于FIB nexthop Exception是什么 转载的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!