网络子系统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