路由下一跳中的变数exception

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


路径MTU改变

内核接收到目的地址不可达(ICMP_DEST_UNREACH)类型的ICMP消息,判断其code为ICMP_FRAG_NEEDED,需要更新pmtu。此时,内核会创建一个下一跳exception(fnhe)保存新的mtu值:

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

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


路由重定向

内核接收到重定向(ICMP_REDIRECT)类型的ICMP消息,调用update_or_create_fnhe函数创建或者更新路由下一跳exception,设置新的网关地址new_gw。

static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4, bool kill_route)
{
	//ICMP头中给出了重定向的新网关IP地址。
    __be32 new_gw = icmp_hdr(skb)->un.gateway;
	//发送此ICMP消息的设备为旧网关,saddr为其IP地址
    __be32 old_gw = ip_hdr(skb)->saddr;
	struct neighbour *n;
	
    n = __ipv4_neigh_lookup(rt->dst.dev, new_gw);
    if (!n)
        n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev);
    if (!IS_ERR(n)) {
        if (fib_lookup(net, fl4, &res, 0) == 0) {
            struct fib_nh *nh = &FIB_RES_NH(res);

            update_or_create_fnhe(nh, fl4->daddr, new_gw,
                    0, jiffies + ip_rt_gc_timeout);
        }
    }
}


路由exception查询


查询操作会跳过下一跳(fib_nh->nh_gw下一跳中的nh_gw的值可能已不是最新),首先查询是否存在exception,如果存在,rtable使用exception保存的相关值,包括fnhe_pmtu、fnhe_gw等。

static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
{
    rt->rt_pmtu = fnhe->fnhe_pmtu;
    rt->dst.expires = fnhe->fnhe_expires;

    if (fnhe->fnhe_gw) {
        rt->rt_flags |= RTCF_REDIRECTED;
        rt->rt_gateway = fnhe->fnhe_gw;
        rt->rt_uses_gateway = 1;
    }
}

内核版本

linux-4.14.4


扫描二维码关注公众号,回复: 1719522 查看本文章

猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/80268419