- static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
-
struct net_device *dev)
-
{
-
struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
-
struct iphdr *iph;
-
struct sk_buff *fp, *head = qp->q.fragments;
-
int len;
-
int ihlen;
-
int err;
/* kill掉该IP队列 */
-
ipq_kill(qp);
-
/* Make the one we just received the head. */
- /* 当prev不为null时,head=prev->next,即当前收到的这个分片,也就是把刚收到的这个分片当作hea d。当prev为null时,qp->q.fragments就是刚收到的分片 */
-
if (prev) {
- /*
- 这里做的处理,就是将收到的该分片作为head。
- 为什么一定要将刚收到的分片作为head呢?
- */
- head = prev->next;
-
fp = skb_clone(head, GFP_ATOMIC);
-
if (!fp)
-
goto out_nomem;
-
-
fp->next = head->next;
-
if (!fp->next)
-
qp->q.fragments_tail = fp;
-
prev->next = fp;
-
-
skb_morph(head, qp->q.fragments);
-
head->next = qp->q.fragments->next;
-
-
kfree_skb(qp->q.fragments);
-
qp->q.fragments = head;
-
}
/*
ok,现在head肯定为刚刚收到的分片。
大胆猜测一下用意,因为刚收到的IP分片里面的IP信息是最新的,所有选择它作为head,用于生成重组后的IP包。
比如也许会有一些新的IP option等。
*/
-
WARN_ON(head == NULL);
-
WARN_ON(FRAG_CB(head)->offset != 0);
-
-
/* Allocate a new buffer for the datagram. */
-
ihlen = ip_hdrlen(head);
-
len = ihlen + qp->q.len;
-
-
err = -E2BIG;
-
if (len > 65535)
-
goto out_oversize;
-
-
/* Head of list must not be cloned. */
-
if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
-
goto out_nomem;
-
-
/* If the first fragment is fragmented itself, we split
-
* it to two chunks: the first with data and paged part
-
* and the second, holding only fragments. */
-
if (skb_has_frags(head)) {
- /* 这里的分片不同于IP分片。因为skb存储数据的时候有两种方式,一个是存储在skb自身的空间,另外一 种是申请一个page,数据存储在该page中。当使用后者的方式时,skb_has_frags为true */
/* 这段代码的目的如注释所说,在此就不重复了 */
-
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_frag_list_init(head);
-
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, &qp->q.net->mem);
-
}
/*
将后面的分片赋给head->frag_list。
这里所做的IP分片重组,并不是真的生成一个完整的独立的IP分片,而是将后面的分片挂载到head分片的frag_li st上。
*/
-
skb_shinfo(head)->frag_list = head->next;
-
skb_push(head, head->data - skb_network_header(head));
/* 更新checksum和头分片的真实大小 */
-
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(head->truesize, &qp->q.net->mem);
/*这里我有一个问题,head->dev = dev是否必要。在ip_frag_queue中,dev就是skb->dev,而head即为刚 收到skb。那么这个head->dev=dev不就没有必要了吗?*/
-
head->next = NULL;
-
head->dev = dev;
-
head->tstamp = qp->q.stamp;
/* 更新新的IP header信息,并将分片从队列qp中卸载 */
-
iph = ip_hdr(head);
-
iph->frag_off = 0;
-
iph->tot_len = htons(len);
-
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
-
qp->q.fragments = NULL;
-
qp->q.fragments_tail = NULL;
-
return 0;
-
-
out_nomem:
-
LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
-
"queue %p\n", qp);
-
err = -ENOMEM;
-
goto out_fail;
-
out_oversize:
-
if (net_ratelimit())
-
printk(KERN_INFO "Oversized IP packet from %pI4.\n",
-
&qp->saddr);
-
out_fail:
-
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
-
return err;
-
}