閱讀988 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統56_ip協議分片重組_重組分片

//調用路徑ip_defrag->ip_frag_reasm
//	所有ip分片都被接收到時,重組ip數據包
//	判斷ip所有分片都接收到的條件:
//		1.FIRST_IN, LAST_IN
//		2.meat = len,即 根據offset推斷出的最大封包長度等於已接收到的封包長度

//	重組過程:
//		1.將sk_buff鏈表從ipq->fragments取下
//		2.將第一個分片以後的分片掛在第一個分片的frag_list域
//		3.從分片子係統使用內存中減去該ip數據包的內存
//		4.返回第一個分片
1.1 static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
{
	struct iphdr *iph;
	struct sk_buff *fp, *head = qp->fragments;
	int len;
	int ihlen;
	//
	ipq_kill(qp);


	//ip報頭+ip選項大小
	ihlen = head->nh.iph->ihl*4;
	//ipq->len隻包括ip有效載荷的長度
	len = ihlen + qp->len;//ip分包總長度
	//ip報文最大長度為64k
	if(len > 65535)
		goto out_oversize;
	//由於第一個skb是通過skb_cloned構造,則需要重新拷貝一份skb緩存區
	//skb_cloned與skb_shared區別:
	//	1.skb_cloned 表示該sk_buff與另一個sk_buff共享同一個緩存區
	//	2.skb_shared 表示該sk_buff引用計數大於1
	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
		goto out_nomem;

	//保證第一個ip分片沒有frag_list
	//	解決辦法:
	//		1.創建一個新的sk_buff
	//		2.將該分片的frag_list添加到新sk_buff的frag_list上
	//		3.更新該skb,新skb的長度
	if (skb_shinfo(head)->frag_list) {
		struct sk_buff *clone;
		int i, plen = 0;

		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
			goto out_nomem;
		clone->next = head->next;
		head->next = clone;
		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
		skb_shinfo(head)->frag_list = NULL;
		for (i=0; i<skb_shinfo(head)->nr_frags; i++)
			plen += skb_shinfo(head)->frags[i].size;
		clone->len = clone->data_len = head->data_len - plen;
		head->data_len -= clone->len;
		head->len -= clone->len;
		clone->csum = 0;
		clone->ip_summed = head->ip_summed;
		atomic_add(clone->truesize, &ip_frag_mem);
	}

	//將ipq中,屬於同一ip數據包的所有sk_buff,鏈接到第一個ip分片的frag_list上
	//	即: first->frag_list = first->next
	skb_shinfo(head)->frag_list = head->next;
	skb_push(head, head->data - head->nh.raw);
	atomic_sub(head->truesize, &ip_frag_mem);//減去ip分片子係統使用的內存

	//通過第一個skb,統計該ip數據包的總長度
	for (fp=head->next; fp; fp = fp->next) {
		head->data_len += fp->len;
		head->len += fp->len;
		if (head->ip_summed != fp->ip_summed)//隻要有其中一個分片的校驗和失效,則標記第一個分片的校驗和失效
			head->ip_summed = CHECKSUM_NONE;
		else if (head->ip_summed == CHECKSUM_HW)//已經由硬件計算了校驗和
			head->csum = csum_add(head->csum, fp->csum);//l4校驗和保存在sk_buff->csum
		head->truesize += fp->truesize;
		atomic_sub(fp->truesize, &ip_frag_mem);
	}

	head->next = NULL;
	head->dev = dev;
	head->stamp = qp->stamp;//skb的時間戳為最後一個分片到達的時間戳

	iph = head->nh.iph;
	iph->frag_off = 0;
	iph->tot_len = htons(len);
	IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
	qp->fragments = NULL;
	return head;

out_nomem:
 	NETDEBUG(if (net_ratelimit())
	         printk(KERN_ERR 
			"IP: queue_glue: no memory for gluing queue %p\n",
			qp));
	goto out_fail;
out_oversize:
	if (net_ratelimit())
		printk(KERN_INFO
			"Oversized IP packet from %d.%d.%d.%d.\n",
			NIPQUAD(qp->saddr));
out_fail:
	IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
	return NULL;
}

最後更新:2017-04-03 14:53:43

  上一篇:go CORDIC 算法理論與實踐
  下一篇:go 飛蛾投火式的瞬間燦爛