SKB中缓存的路由类型

skb结构体中的成员_skb_refdst用于暂时缓存路由,避免在skb生存期内的重复路由查找。不同于sock结构体中有两个成员缓存路由:sk_rx_dst缓存入口路由,sk_dst_cache缓存出口路由。skb结构体中的_skb_refdst在特定时刻仅缓存一种路由。


路由缓存引用计数

如下设置缓存的两个函数,skb_dst_set需要在调用前增加引用计数(dst_clone);而skb_dst_set_noref不需要,其通过标志SKB_DST_NOREF用来标识此缓存没有引用计数,并且在skb_dst_drop函数释放路由缓存时,不进行释放操作。

static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
{
    skb->_skb_refdst = (unsigned long)dst;
}

static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst)
{
    skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF;
}


出口路由缓存

对于本地发出的数据包(本地创建分配的skb),其缓存的为出口路由。例如,作为TCP服务端,在回复客户端SYN+ACK时,新建一个skb结构体,根据路由查询结果(inet_csk_route_req查询出口路由),设置skb路由缓存,此时缓存的为出口路由,之后在发送过程中就不需要再次查找路由了。

struct sk_buff *tcp_make_synack(...)
{
	skb = sock_wmalloc(sk, MAX_TCP_HEADER, 1, GFP_ATOMIC);
    skb_dst_set(skb, dst);
}

static int tcp_v4_send_synack(...)
{
    if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
        return -1;

    skb = tcp_make_synack(sk, dst, req, foc);
}


入口路由缓存


对于接收到的数据包,一种情况是通过early_demux获取缓存路由,例如,在函数tcp_v4_early_demux中,通过sock结构体成员sk_rx_dst中的路由缓存初始化skb的dst,顾名思义,此时缓存的为入口路由。使用设置函数skb_dst_set_noref,不增加dst的引用计数。使用关联的sock成员sk_rx_dst的引用计数,可保障在sock存续期间,skb的dst可安全释放;当sock释放时,关联的skb会一并释放。另一种情况直接查询入口路由(ip_route_input_noref),缓存到skb中。

static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
{
    if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
        ipprot->early_demux(skb);
    }

    if (!skb_valid_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                           iph->tos, skb->dev);
    }
}


转发路由缓存


转发路由缓存


与入口路由缓存查找方法相同,同是ip_rcv_finish函数中获得转发路由缓存。此时,FIB(fib_lookup)查询出的路由类型不是之前的RTN_LOCAL,路由dst的input函数指针设置为ip_forward; output函数指针设置为ip_output。在转发过程中避免重复查找路由。

static int __mkroute_input(...)
{
    rth = rt_dst_alloc(out_dev->dev,
               IN_DEV_CONF_GET(in_dev, NOPOLICY),
               IN_DEV_CONF_GET(out_dev, NOXFRM), do_cache);

    rth->dst.input = ip_forward;
    rth->dst.output = ip_output;
}

static int ip_route_input_slow(...)
{
    if (!IN_DEV_FORWARD(in_dev))
        goto no_route;

	// 最终调用__mkroute_input。
    err = ip_mkroute_input(skb, &res, &fl4, in_dev, daddr, saddr, tos);
out:    return err;
}


内核版本

linux-3.10.0



猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/80241113
今日推荐