ip_push_pending_frames函数

当调用ip_append_data和ip_append_page把传输层数据包复制到内核缓冲区,接下来调用ip_push_pending_frames函数发送数据包。struct sock *sk是ip_push_pending_frames函数唯一的输入参数,sk中包含了指向缓冲区队列sk_write_queue数据结构的指针。

1、取发送缓冲区

ip_push_pending_frames首先从sk_write_queue队列中取除缓冲区的数据,缓冲区队列中第一个数据包一定是sk_buff,保存在局部变量struct sk_buff *skb中,队列中的其他缓冲区数据保存在skb的frag_list链表上,再更新skb的len和data_len数据域指向所有缓冲区的总长度,一旦缓冲区连接到skb就从sk_write_queue队列中清除。

int ip_push_pending_frames(struct sock *sk)
{
	struct sk_buff *skb, *tmp_skb;
	struct sk_buff **tail_skb;
	struct inet_sock *inet = inet_sk(sk);
	struct net *net = sock_net(sk);
	struct ip_options *opt = NULL;
	struct rtable *rt = (struct rtable *)inet->cork.dst;
	struct iphdr *iph;
	__be16 df = 0;
	__u8 ttl;
	int err = 0;
	//从sk_write_queue队列中取缓冲区的数据
	//第一个缓冲区一定是一个sk_buff
	if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
		goto out;
	//取存放在缓冲区frag_list链上的数据包
	tail_skb = &(skb_shinfo(skb)->frag_list);

	/* move skb->data to ip header from ext header */
	if (skb->data < skb_network_header(skb))
		__skb_pull(skb, skb_network_offset(skb));
	//释放sk_write_queue缓冲区队列
	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
		__skb_pull(tmp_skb, skb_network_header_len(skb));
		*tail_skb = tmp_skb;
		tail_skb = &(tmp_skb->next);
		//更新数据包的长度
		skb->len += tmp_skb->len;
		//更新缓冲区总长度
		skb->data_len += tmp_skb->len;
		skb->truesize += tmp_skb->truesize;
		tmp_skb->destructor = NULL;
		tmp_skb->sk = NULL;
	}

...

去sk_write_queue队列中第一个skb包

/* Internal */
#define skb_shinfo(SKB)	((struct skb_shared_info *)(skb_end_pointer(SKB)))

缓冲区列表所在结构体

struct skb_shared_info {
	unsigned short	nr_frags;
	unsigned short	gso_size;
	/* Warning: this field is not always filled in (UFO)! */
	unsigned short	gso_segs;
	unsigned short  gso_type;
	__be32          ip6_frag_id;
	union skb_shared_tx tx_flags;
	//数据包缓冲区链表
	struct sk_buff	*frag_list;
	struct skb_shared_hwtstamps hwtstamps;

	/*
	 * Warning : all fields before dataref are cleared in __alloc_skb()
	 */
	atomic_t	dataref;

	skb_frag_t	frags[MAX_SKB_FRAGS];
	/* Intermediate layers must ensure that destructor_arg
	 * remains valid until skb destructor */
	void *		destructor_arg;
};

2、处理IP协议头

接下来向数据包中填充IP协议头,如果套接字控制信息配置了不分割数据包的标志IP_DF,且该标志作用于所有数据包:IP_PMTUDISC_DO,即使数据包长度大于路由最大传输单元PMTU也不分割数据包,就将IP_DF标志设置在IP协议头相应的数据域。然后就是给IP协议头各数据域赋值:版本、协议头长度、如果有IP选项就要调用ip_options_build构建IP选项。

	...

     //不分割数据包的标志IP_DF
	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
	    (skb->len <= dst_mtu(&rt->u.dst) &&
	     ip_dont_fragment(sk, &rt->u.dst)))
		df = htons(IP_DF);

	//初始化ip选项
	if (inet->cork.flags & IPCORK_OPT)
		opt = inet->cork.opt;
	//组发送
	if (rt->rt_type == RTN_MULTICAST)
		ttl = inet->mc_ttl;
	else
		ttl = ip_select_ttl(inet, &rt->u.dst);
	
	//IP协议头初始化
	iph = (struct iphdr *)skb->data;
	iph->version = 4;
	iph->ihl = 5;
	if (opt) {
		iph->ihl += opt->optlen>>2;
		//构建IP选项
		ip_options_build(skb, opt, inet->cork.addr, rt, 0);
	}
	
	iph->tos = inet->tos;
	iph->frag_off = df;
	ip_select_ident(iph, &rt->u.dst, sk);
	iph->ttl = ttl;
	iph->protocol = sk->sk_protocol;
	iph->saddr = rt->rt_src;
	iph->daddr = rt->rt_dst;

	skb->priority = sk->sk_priority;
	skb->mark = sk->sk_mark;
	/*
	 * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
	 * on dst refcount
	 */
	inet->cork.dst = NULL;
	//设置路由
	skb_dst_set(skb, &rt->u.dst);

...

3、和网络过滤子系统的交互

最后调用进入netfiler框架OUT链上的钩子处理函数,钩子处理函数结束就调用dst_output继续进入下一个阶段。

//最后进入Netfiler的OUT链上钩子函数
	//钩子函数处理结束后调用dst_output继续处理
	err = ip_local_out(skb);
	if (err) {
		if (err > 0)
			err = net_xmit_errno(err);
		if (err)
			goto error;
	}

ip_local_out函数:

扫描二维码关注公众号,回复: 4337288 查看本文章
int ip_local_out(struct sk_buff *skb)
{
	int err;

	err = __ip_local_out(skb);
	if (likely(err == 1))
		err = dst_output(skb);

	return err;
}

__ip_local_out函数:

int __ip_local_out(struct sk_buff *skb)
{
	struct iphdr *iph = ip_hdr(skb);

	iph->tot_len = htons(skb->len);
	//ip校验和
	ip_send_check(iph);
	//调用OUT链上的钩子处理函数
	return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
		       skb_dst(skb)->dev, dst_output);
}

3、发送数据包的完整过程

以UPD协议微粒子简述传输层到网络层的整个过程,UPD协议通过udp_sendmsg函数调用ip_append_data和ip_push_pend_frams实现数据包的发送,udp_sendmsg首先调用ip_append_data函数将数据包发送到缓冲区,然后判断corkreq标志中有没有设置MSG_MORE,如果没有设置立即调用udp_push_pending_frames将刚缓冲的数据包发送出去,如果ip_append_data缓冲数据包失败就将数据包从sk_write_queue队列中释放:

...

getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
	//缓冲数据包
	err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
			sizeof(struct udphdr), &ipc, &rt,
			corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
	//缓存数据包失败删除把skb从sk_write_queue队列中释放
	if (err)
		udp_flush_pending_frames(sk);
	//corkreq标志没有设置MSG_MORE立即调用udp_push_pending_frames发送
	//发送刚缓存的数据包
	else if (!corkreq)
		err = udp_push_pending_frames(sk);
	else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
		up->pending = 0;
	//释放sk
	release_sock(sk);

...

udp_push_pending_frames是ip_push_pending_frames的封装,udp_push_pending_frames首先初始化UDP协议头,然后判断是否禁止传输层校验和,如果禁止就调转调用ip_push_pending_frames,如果没有禁止就初始化UDP检验和。

static int udp_push_pending_frames(struct sock *sk)
{
	struct udp_sock  *up = udp_sk(sk);
	struct inet_sock *inet = inet_sk(sk);
	struct flowi *fl = &inet->cork.fl;
	struct sk_buff *skb;
	struct udphdr *uh;
	int err = 0;
	int is_udplite = IS_UDPLITE(sk);
	__wsum csum = 0;

	/* Grab the skbuff where UDP header space exists. */
	if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
		goto out;

	/*
	 * Create a UDP header
	 */
	 //创建udp协议头
	uh = udp_hdr(skb);
	uh->source = fl->fl_ip_sport;
	uh->dest = fl->fl_ip_dport;
	uh->len = htons(up->len);
	uh->check = 0;

	if (is_udplite)  				 /*     UDP-Lite      */
		csum  = udplite_csum_outgoing(sk, skb);

	else if (sk->sk_no_check == UDP_CSUM_NOXMIT) {   /* UDP csum disabled */
		//禁止传输层校验和,直接调到send
		skb->ip_summed = CHECKSUM_NONE;
		goto send;

	} else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
		//udp校验和
		udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len);
		goto send;

	} else						 /*   `normal' UDP    */
		csum = udp_csum_outgoing(sk, skb);

	/* add protocol-dependent pseudo-header */
	uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len,
				      sk->sk_protocol, csum);
	if (uh->check == 0)
		uh->check = CSUM_MANGLED_0;

send:
	//发送数据包
	err = ip_push_pending_frames(sk);
	if (err) {
		if (err == -ENOBUFS && !inet->recverr) {
			UDP_INC_STATS_USER(sock_net(sk),
					   UDP_MIB_SNDBUFERRORS, is_udplite);
			err = 0;
		}
	} else
		UDP_INC_STATS_USER(sock_net(sk),
				   UDP_MIB_OUTDATAGRAMS, is_udplite);
out:
	up->len = 0;
	up->pending = 0;
	return err;
}

猜你喜欢

转载自blog.csdn.net/City_of_skey/article/details/84206779
今日推荐