其他分享
首页 > 其他分享> > 路由output 查找

路由output 查找

作者:互联网

看下以前的文章首先说明一下 Routing与 Neighboring subsystem的关联

1、在路由过程中,需要寻找或创建 struct dst_entry (另一种形式是 struct rtable)。 dst_entry 通过neighbour 域与 struct neighbour 关联。

每个 dst_entry 对应一个 neighbour,这样在路由之后,立刻能找到对应的 neighbour,此后,数据包通过 neighbour->output 送到链路层。

以 UDP 包的发送过程为例,这个过程如下

Udp_sendmsg() ==> ip_route_output() 
        ==> udp_push_pending_frames()==》udp_send_skb==》ip_send_skb==》ip_local_out==》dst_output==》skb->dst->output

Ip_route_output_slow() : 当查不到路由 cache 后(下一跳地址的cache),根据 route rule ,通过 dst_alloc() 创建一个 dst_entry 结构,这同时也是一个 rtable 结构,然后将 dst_entry 的 output 指向 ip_output();

此后,udp_sendmsg 继续调用 ip_send_skb() 来发包;

rth->u.dst.output=ip_output;
Udp_sendmsg() ==> udp_push_pending_frames ==> udp_send_skb==> ip_send_skb==>ip_local_out==》skb->dst->output()//这里的 output 就是 ip_output()
ip_output ==> __ip_finish_output() ==> ip_finish_output2() ==> dst_neigh_output()

  因此,最终数据包是通过dst_neigh_output  也就是 neighbour->output() 往下送的。

IPv4 代码实现:ip_route_output在路由 cache 中查不到路由结果后,查找__mkroute_output->rt_dst_alloc-> route rule ,如果没有合适的路由规则,则失败返回。否则,通过 dst_alloc() 创建一个 dst_entry 结构,这同时也是一个 rtable 结构,此 rtable 结构被挂入 hash 表中。这时候我们已经有了下一跳的 L3地址。

static struct dst_ops ipv4_dst_ops = {
    .family =        AF_INET,
    .protocol =        cpu_to_be16(ETH_P_IP),
    .check =        ipv4_dst_check,
    .default_advmss =    ipv4_default_advmss,
    .mtu =            ipv4_mtu,
    .cow_metrics =        ipv4_cow_metrics,
    .destroy =        ipv4_dst_destroy,
    .ifdown =        ipv4_dst_ifdown,
    .negative_advice =    ipv4_negative_advice,
    .link_failure =        ipv4_link_failure,
    .update_pmtu =        ip_rt_update_pmtu,
    .redirect =        ip_do_redirect,
    .local_out =        __ip_local_out,
    .neigh_lookup =        ipv4_neigh_lookup,//rtable 和 neigh_table绑定
};


static struct rtable *rt_dst_alloc(struct net_device *dev,
                   unsigned int flags, u16 type,
                   bool nopolicy, bool noxfrm, bool will_cache)
{
    struct rtable *rt;

    rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK,
               (will_cache ? 0 : (DST_HOST | DST_NOCACHE)) |
               (nopolicy ? DST_NOPOLICY : 0) |
               (noxfrm ? DST_NOXFRM : 0));

    if (rt) {
        rt->rt_genid = rt_genid_ipv4(dev_net(dev));
        rt->rt_flags = flags;
        rt->rt_type = type;
        rt->rt_is_input = 0;
        
        rt->rt_iif = 0;
        rt->rt_pmtu = 0;
        rt->rt_gateway = 0;
        rt->rt_uses_gateway = 0;
        rt->rt_table_id = 0;
        //初始化其rt_uncached链表指针
        INIT_LIST_HEAD(&rt->rt_uncached);

        rt->dst.output = ip_output;
        if (flags & RTCF_LOCAL)
            rt->dst.input = ip_local_deliver;
    }

    return rt;
}


void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
        int initial_ref, int initial_obsolete, unsigned short flags)
{
    struct dst_entry *dst;

    if (ops->gc && dst_entries_get_fast(ops) > ops->gc_thresh) {
        if (ops->gc(ops))
            return NULL;
    }
    dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC);
    if (!dst)
        return NULL;
    dst->child = NULL;
    dst->dev = dev;
    if (dev)
        dev_hold(dev);
    dst->ops = ops;// 赋值ops
    dst_init_metrics(dst, dst_default_metrics, true);
    dst->expires = 0UL;
    dst->path = dst;
#ifdef CONFIG_XFRM
    dst->xfrm = NULL;
#endif
    dst->input = dst_discard;
    dst->output = dst_discard//创建时
    dst->error = 0;
    dst->obsolete = initial_obsolete;
    dst->header_len = 0;
    dst->trailer_len = 0;
#ifdef CONFIG_IP_ROUTE_CLASSID
    dst->tclassid = 0;
#endif
    atomic_set(&dst->__refcnt, initial_ref);
    dst->__use = 0;
    dst->lastuse = jiffies;
    dst->flags = flags;
    dst->pending_confirm = 0;
    dst->next = NULL;
    if (!(flags & DST_NOCOUNT))
        dst_entries_add(ops, 1);
    return dst;
}

  新版本中都是缓存下一跳地址,所以路由表和neigh表分开,现在是找到下一跳IP, 直接对IP运行对用neigh 相关协议找到IP 对应MAC;

可以看到dst_neigh_output(dst, neigh, skb); 虽然传入的dst参数,但是其实际没有使用dst->ops函数去处理rtable 和neighbour的bind关系

复制代码
1.1 ip_finish_output2() 
1.2 nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr)
1.3 __ipv4_neigh_lookup_noref(dev, nexthop)
  if (!neigh)
    neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
1.4 if(neigh)
  dst_neigh_output(dst, neigh, skb);
复制代码
static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n,
                   struct sk_buff *skb)
{
    struct hh_cache *hh;

    if (unlikely(dst->pending_confirm)) {
        n->confirmed = jiffies;
        dst->pending_confirm = 0;
    }

    hh = &n->hh;
    if ((n->nud_state & NUD_CONNECTED) && hh->hh_len)
        return neigh_hh_output(hh, skb);
    else
        return n->output(n, skb);
}
/*neigh_alloc() 用于分配 neighbour 结构
neigh_create() 进一步设置此结构,对于 ARP 来说,它调用 arp_constructor() ,在这个函数里面,对 neighbour 的 ops 域和 output 域进行设置。
Ops 域,根据底层 driver 的类型进行不同的设置,
对于没有链路层地址的,指向arp_direct_ops
对于没有链路层 cache 的,指向arp_generic_ops
对于有链路层 cache 的, 指向arp_hh_ops

 

标签:rt,ops,ip,dst,neigh,查找,output,路由
来源: https://www.cnblogs.com/codestack/p/15977419.html