码迷,mamicode.com
首页 > 其他好文 > 详细

dst_output发包

时间:2019-05-11 17:56:06      阅读:342      评论:0      收藏:0      [点我收藏+]

标签:type   才有   rcu   组播   通过   ret   realloc   pac   nbsp   

不管是收到报文转发还是本机发送报文,最后都会调用dst_output

/* Output packet to network from transport.  */
static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
   /*
     * 如果是单播数据包,设置的是ip_output(),
     * 如果是组播数据包,设置的是ip_mc_output().dev_queue_xmit
     */
    return skb_dst(skb)->output(net, sk, skb);
}

 单播:

/*
 * 对于单播数据包,目的路由缓存项中的输出接口是ip_output().
 */
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct net_device *dev = skb_dst(skb)->dev;

	IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
	/*
	 * 设置数据包的输出网络设备和数据包网络
	 * 层协议类型。
	 */
	skb->dev = dev;
	skb->protocol = htons(ETH_P_IP);
/*
	 * 经netfilter处理后,调用ip_finish_output()继续IP数据包的输出
	 */
	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
			    net, sk, skb, NULL, dev,
			    ip_finish_output,
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
}

 

static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	unsigned int mtu;

#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
	/* Policy lookup after SNAT yielded a new policy */
	if (skb_dst(skb)->xfrm) {
		IPCB(skb)->flags |= IPSKB_REROUTED;
		return dst_output(net, sk, skb);
	}
#endif
/*
//如果不支持TSO或者GSO,tcp发送的时候是按照mss来组织skb的,
所以skb->len会等于mtu  所以TCP叫分段,和IP分片不一样,只有UDP才有IP分片
//SKB不是gso类型,并且skb->len大于mtu则需要分片  
对方接受后的分片重组在netfilter中的ipv4_conntrack_defrag
*/
	mtu = ip_skb_dst_mtu(sk, skb);
	if (skb_is_gso(skb))
		return ip_finish_output_gso(net, sk, skb, mtu);
 /* 如果数据包长度大于MTU,则调用ip_fragment()
	 * 对IP数据包进行分片。
	 */
	if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
		return ip_fragment(net, sk, skb, mtu, ip_finish_output2);

	return ip_finish_output2(net, sk, skb);
}

 

/* ip send the packet  by ip_finish_output2*/
static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	struct dst_entry *dst = skb_dst(skb);
	struct rtable *rt = (struct rtable *)dst;
	struct net_device *dev = dst->dev;
	unsigned int hh_len = LL_RESERVED_SPACE(dev);
	struct neighbour *neigh;
	u32 nexthop;

	if (rt->rt_type == RTN_MULTICAST) {
		IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
	} else if (rt->rt_type == RTN_BROADCAST)
		IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);

	/* Be paranoid, rather than too clever. */
	 /* skb头部空间不能存储链路头 */
	if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
		struct sk_buff *skb2;
		 /* 重新分配skb */
		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
		if (!skb2) {
			kfree_skb(skb);
			return -ENOMEM;
		}
		if (skb->sk)/* 关联控制块 */
			skb_set_owner_w(skb2, skb->sk);
		consume_skb(skb); /* 释放skb */
		skb = skb2; /* 指向新的skb */
	}

	rcu_read_lock_bh();
	/* 获取下一跳 */
	nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);// get the dst ip address (u32)
	neigh = __ipv4_neigh_lookup_noref(dev, nexthop); 
	//根据目的IP查找邻居项是否存在  
	//如果没有则创建邻居项,然后通过dst_neigh_output  发包
	if (unlikely(!neigh))
		neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
	if (!IS_ERR(neigh)) {/* 成功 */
		int res = dst_neigh_output(dst, neigh, skb); /* 通过邻居子系统输出 */

		rcu_read_unlock_bh();
		return res;
	}
	rcu_read_unlock_bh();

	net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
			    __func__);
	kfree_skb(skb);
	return -EINVAL;
}

 

技术图片

最后通过令邻居子系统,调用dev_queue_xmit 将数据报文发送给链路层驱动

 

dst_output发包

标签:type   才有   rcu   组播   通过   ret   realloc   pac   nbsp   

原文地址:https://www.cnblogs.com/codestack/p/9292122.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!