分类: LINUX
2014-12-15 15:16:30
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);
BUG_TRAP(head != NULL);
BUG_TRAP(FRAG_CB(head)->offset == 0);
//取得ipq中fragments头节点的ip头长度
ihlen = ip_hdrlen(head);
//如果把头节点的IP首部长度加上ipq结构中的碎片总长度相加,就得到了重组之
//后报文的长度
len = ihlen + qp->len;
if (len > 65535)
goto out_oversize;
//头节点必须没有被克隆过
if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
goto out_nomem;
//碎片中第一个节点如果是分片的,需要特殊处理,这里的”分片”并不是指IP包的
//碎片,而是指skb存储结构离散分布,并不在一个连续的内存空间内
if (skb_shinfo(head)->frag_list) {
struct sk_buff *clone;
int i, plen = 0;
//如果头节点是分片的,那么需要重新申请一个skb,并且把这个新的skb放到
//第一个skb end指针之后skb_shared_info结构的frag_list链表上
if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
goto out_nomem;
clone->next = head->next;
head->next = clone;
//把head原来的分片放在新申请的skb的frag_list里面
skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
skb_shinfo(head)->frag_list = NULL;
//计算head中总的分片长度
for (i=0; i
plen += skb_shinfo(head)->frags[i].size;
//实际上最后生成了一个自身数据为0.不包含任何数据,但是
//这个新的的frag_list中却包含了所有的分片
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);
}
//把head以后所有的碎片都当作是head frag_list里面的分片来处理
skb_shinfo(head)->frag_list = head->next;
skb_push(head, head->data - skb_network_header(head));
atomic_sub(head->truesize, &ip_frag_mem);
//协议栈的处理会通过skb_linearize()函数将head报文的frag_list链表里面的数据包
//都合并成一个报文,所以将链表里面所有skb的len和data_len,以及true_size都
//和head中相应的值相加,最后得到了合并后数据包的长度
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_COMPLETE)
head->csum = csum_add(head->csum, fp->csum);
head->truesize += fp->truesize;
atomic_sub(fp->truesize, &ip_frag_mem);
}
head->next = NULL;
head->dev = dev;
head->tstamp = qp->stamp;
iph = ip_hdr(head);
iph->frag_off = 0;
iph->tot_len = htons(len);
IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
qp->fragments = NULL;
return head;
//省去部分代码
}