網絡子係統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