Chinaunix首页 | 论坛 | 博客
  • 博客访问: 185615
  • 博文数量: 71
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 48
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-27 14:13
个人简介

坐着,坐着,就胖了。。。

文章分类

全部博文(71)

文章存档

2014年(15)

2013年(56)

我的朋友

分类: LINUX

2013-11-08 19:16:52

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
前面的博文学习了ip_frag_queue——将收到的IP分片加入对应的IP分片的队列中——kernel保存IP分片的缓存,当所有的IP分片收到后,将调用ip_frag_reasm来重组IP分片。

  1. static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
  2.              struct net_device *dev)
  3. {
  4.     struct net *net = container_of(qp->q.net, struct net, ipv4.frags);
  5.     struct iphdr *iph;
  6.     struct sk_buff *fp, *head = qp->q.fragments;
  7.     int len;
  8.     int ihlen;
  9.     int err;
      
     /* kill掉该IP队列 */
  1.     ipq_kill(qp);

  1.     /* Make the one we just received the head. */            
  2.     /* 当prev不为null时,head=prev->next,即当前收到的这个分片,也就是把刚收到的这个分片当作hea         d。当prev为null时,qp->q.fragments就是刚收到的分片 */
  3.     if (prev) {
  4.         /* 
  5.         这里做的处理,就是将收到的该分片作为head。
  6.         为什么一定要将刚收到的分片作为head呢?
  7.         */
  8.         head = prev->next;
  9.         fp = skb_clone(head, GFP_ATOMIC);
  10.         if (!fp)
  11.             goto out_nomem;

  12.         fp->next = head->next;
  13.         if (!fp->next)
  14.             qp->q.fragments_tail = fp;
  15.         prev->next = fp;

  16.         skb_morph(head, qp->q.fragments);
  17.         head->next = qp->q.fragments->next;

  18.         kfree_skb(qp->q.fragments);
  19.         qp->q.fragments = head;
  20.     }
   
     /* 
     ok,现在head肯定为刚刚收到的分片。
     大胆猜测一下用意,因为刚收到的IP分片里面的IP信息是最新的,所有选择它作为head,用于生成重组后的IP包。
     比如也许会有一些新的IP option等。
     */

  1.     WARN_ON(head == NULL);
  2.     WARN_ON(FRAG_CB(head)->offset != 0);

  3.     /* Allocate a new buffer for the datagram. */
  4.     ihlen = ip_hdrlen(head);
  5.     len = ihlen + qp->q.len;

  6.     err = -E2BIG;
  7.     if (len > 65535)
  8.         goto out_oversize;

  9.     /* Head of list must not be cloned. */
  10.     if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
  11.         goto out_nomem;

  12.     /* If the first fragment is fragmented itself, we split
  13.      * it to two chunks: the first with data and paged part
  14.      * and the second, holding only fragments. */
  15.     if (skb_has_frags(head)) {
  16.         /* 这里的分片不同于IP分片。因为skb存储数据的时候有两种方式,一个是存储在skb自身的空间,另外一         种是申请一个page,数据存储在该page中。当使用后者的方式时,skb_has_frags为true */
         /* 这段代码的目的如注释所说,在此就不重复了 */
  1.         struct sk_buff *clone;
  2.         int i, plen = 0;

  3.         if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
  4.             goto out_nomem;
  5.         clone->next = head->next;
  6.         head->next = clone;
  7.         skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
  8.         skb_frag_list_init(head);
  9.         for (i=0; i<skb_shinfo(head)->nr_frags; i++)
  10.             plen += skb_shinfo(head)->frags[i].size;
  11.         clone->len = clone->data_len = head->data_len - plen;
  12.         head->data_len -= clone->len;
  13.         head->len -= clone->len;
  14.         clone->csum = 0;
  15.         clone->ip_summed = head->ip_summed;
  16.         atomic_add(clone->truesize, &qp->q.net->mem);
  17.     }

     /* 
     将后面的分片赋给head->frag_list。
     这里所做的IP分片重组,并不是真的生成一个完整的独立的IP分片,而是将后面的分片挂载到head分片的frag_li      st上。
     */
  1.     skb_shinfo(head)->frag_list = head->next;
  2.     skb_push(head, head->data - skb_network_header(head));

     /* 更新checksum和头分片的真实大小 */
  1.     for (fp=head->next; fp; fp = fp->next) {
  2.         head->data_len += fp->len;
  3.         head->len += fp->len;
  4.         if (head->ip_summed != fp->ip_summed)
  5.             head->ip_summed = CHECKSUM_NONE;
  6.         else if (head->ip_summed == CHECKSUM_COMPLETE)
  7.             head->csum = csum_add(head->csum, fp->csum);
  8.         head->truesize += fp->truesize;
  9.     }
  10.     atomic_sub(head->truesize, &qp->q.net->mem);

     /*这里我有一个问题,head->dev = dev是否必要。在ip_frag_queue中,dev就是skb->dev,而head即为刚      收到skb。那么这个head->dev=dev不就没有必要了吗?*/
  1.     head->next = NULL;
  2.     head->dev = dev;
  3.     head->tstamp = qp->q.stamp;

     /* 更新新的IP header信息,并将分片从队列qp中卸载 */
  1.     iph = ip_hdr(head);
  2.     iph->frag_off = 0;
  3.     iph->tot_len = htons(len);
  4.     IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
  5.     qp->q.fragments = NULL;
  6.     qp->q.fragments_tail = NULL;
  7.     return 0;

  8. out_nomem:
  9.     LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
  10.              "queue %p\n", qp);
  11.     err = -ENOMEM;
  12.     goto out_fail;
  13. out_oversize:
  14.     if (net_ratelimit())
  15.         printk(KERN_INFO "Oversized IP packet from %pI4.\n",
  16.             &qp->saddr);
  17. out_fail:
  18.     IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
  19.     return err;
  20. }

未完待续。。。

阅读(1016) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~