路由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