• 路由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
  • 相关阅读:
    软件测试的方法
    常用的adb
    正则表达式
    Python学习笔记(九)————进程和线程
    CodeForces
    华为FusionSphere openstack安装
    华为FusionCompute单节点安装教程--VRM主机的安装
    华为FusionCompute单节点安装教程--CNA主机的安装
    华为FusionSphere--存储管理
    华为FusionSphere--架构介绍
  • 原文地址:https://www.cnblogs.com/codestack/p/15977419.html
Copyright © 2020-2023  润新知