Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1638082
  • 博文数量: 511
  • 博客积分: 967
  • 博客等级: 准尉
  • 技术积分: 2560
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-06 14:19
文章分类

全部博文(511)

文章存档

2016年(11)

2015年(61)

2014年(257)

2013年(63)

2012年(119)

分类: LINUX

2014-05-17 22:57:47

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================

继续前面的分片重组代码。在前面的文章中,已经将如何找到或者创建一个新的IPv4分片队列的代码学习了一遍。下面的函数ip_frag_queue,将新的IPv4分片加入到分片的队列中。且如果已经获得了所有的分片后,就开始重组分片。

  1. /* Add new segment to existing queue. */
  2. static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
  3. {
  4.     struct sk_buff *prev, *next;
  5.     struct net_device *dev;
  6.     int flags, offset;
  7.     int ihl, end;
  8.     int err = -ENOENT;
     
     /* 分片队列已经完成或者即将被清除,都会置上INET_FRAG_COMPLETE */
  1.     if (qp->q.last_in & INET_FRAG_COMPLETE)
  2.         goto err;

     /*
     1. IPCB(skb)->flags只有在本机发送IPv4分片时被置位,那么这里的检查应该是预防收到本机自己发出的IP分      片。
     2. 关于ip_frag_too_far:该函数主要保证了来自同一个peer(相同的源地址)不会占用过多的IP分片队列。      后面会有该函数的分析。我想,这个主要是为了防止攻击。不过如果中间设备有NAT的话,这个默认限制太小了。
     不过有NAT设备的情况下,IPv4的分片也会有其它的问题。以后也许会谈谈这个问题。
     3. 前面两个条件为真时,调用ip_frag_reinit,重新初始化该队列。出错,那么只好kill掉这个队列了。
     */
  1.     if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
  2.      unlikely(ip_frag_too_far(qp)) &&
  3.      unlikely(err = ip_frag_reinit(qp))) {
  4.         ipq_kill(qp);
  5.         goto err;
  6.     }
     
     /* 得到分片的数据偏移量及分片标志 */
  1.     offset = ntohs(ip_hdr(skb)->frag_off);
  2.     flags = offset & ~IP_OFFSET;
  3.     offset &= IP_OFFSET;
  4.     offset <<= 3;        /* offset is in 8-byte chunks */
  5.     ihl = ip_hdrlen(skb);

  6.     /* Determine the position of this fragment. */
  7.     /* 得到IP报文总的长度或者说末端 */
  8.     end = offset+skb->len - ihl;
  9.     err = -EINVAL;

  10.     /* Is this the final fragment? */
  11.     if ((flags & IP_MF) == 0) {
  12.          /* 这是最后一个分片 */
  13.         /* If we already have some bits beyond end
  14.          * or have different end, the segment is corrrupted.
  15.          */
  16.         /*
  17.         1. 末尾端要小于之前得到的总的长度,那么肯定出错了;
  18.         2. 之前已经收到了一个最后分片,且这次判断的末端不等于之前获得的值,那么肯定出错了。
  19.         */
  20.         if (end < qp->q.len ||
  21.          ((qp->q.last_in & INET_FRAG_LAST_IN) && end != qp->q.len))
  22.             goto err;
         /* 置INET_FRAG_LAST_IN标志,表示收到了最后一个分片 */
  1.         qp->q.last_in |= INET_FRAG_LAST_IN;
  2.         /* IP包总长度就等于end */
  3.         qp->q.len = end;
  4.     } else {
  5.         if (end&7) {
  6.             /* 说明数据长度不是8的倍数。按照协议规定,除最后一个分片外,其余的IP分片的长度必须为8                  的倍数*/
             /* 将数据长度缩短为8的倍数的长度 */
  1.             end &= ~7;
  2.             if (skb->ip_summed != CHECKSUM_UNNECESSARY)
  3.                 skb->ip_summed = CHECKSUM_NONE;
  4.         }

  1.         if (end > qp->q.len) {
  2.             /* 这次判断的末尾超过了之前的获得长度 */

  3.             /* Some bits beyond end -> corruption. */
  4.             /* 
  5.             如果之前已经获得了最后一个分片,那么总的IP长度一定为正确的值。
  6.             结果与这次的判断不符,那么一定出错了。
  7.             */
  8.             if (qp->q.last_in & INET_FRAG_LAST_IN)
  9.                 goto err;
             /* 更新IP总长度 */
  1.             qp->q.len = end;
  2.         }
  3.     }
     
     /* 表示空的IP数据,出错 */
  1.     if (end == offset)
  2.         goto err;

  3.     err = -ENOMEM;
  4.     if (pskb_pull(skb, ihl) == NULL)
  5.         goto err;

  6.     err = pskb_trim_rcsum(skb, end - offset);
  7.     if (err)
  8.         goto err;

  9.     /* Find out which fragments are in front and at the back of us
  10.      * in the chain of fragments so far. We must know where to put
  11.      * this fragment, right?
  12.      */
  13.     prev = qp->q.fragments_tail;
  14.     if (!prev || FRAG_CB(prev)->offset < offset) {
  15.         /* 直接插入在末尾的情况 */
  16.         next = NULL;
  17.         goto found;
  18.     }
  19.     prev = NULL;
     /* 插入到分片队列的中间 */
  1.     for (next = qp->q.fragments; next != NULL; next = next->next) {
  2.         if (FRAG_CB(next)->offset >= offset)
  3.             break;    /* bingo! */
  4.         prev = next;
  5.     }

  6. found:
  1.     /* We found where to put this one. Check for overlap with
  2.      * preceding fragment, and, if needed, align things so that
  3.      * any overlaps are eliminated.
  4.      */
     /* 现在已经找到了正确的插入位置,但是可能与已有的IP分片重叠,下面需要处理重叠问题 */     

  1.     if (prev) {
  2.         int i = (FRAG_CB(prev)->offset+prev->len) - offset;

  3.         if (i > 0) {
  4.             /* 与前一个分片有重叠,那么新的偏移量为上一个分片的末尾 */
  5.             offset = i;
  6.             err = -EINVAL;
  7.             /* end如果小于了新的偏移,出错了,drop掉这个分片 */
  8.             if (end <= offset)
  9.                 goto err;
  10.             err = -ENOMEM;
  11.             if (!pskb_pull(skb, i))
  12.                 goto err;
             /* 使checksum无效 */
  1.             if (skb->ip_summed != CHECKSUM_UNNECESSARY)
  2.                 skb->ip_summed = CHECKSUM_NONE;
  3.         }
  4.     }

  5.     err = -ENOMEM;

  6.     while (next && FRAG_CB(next)->offset < end) {
         /* 与已有的后面的IP分片重叠,这里使用while循环,是因为可能与多个重叠 */
  1.         int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
  2.         /* 如注释所说,i为重叠了的字节数 */

  3.         if (i < next->len) {
  4.             /* 与后面的IP分片头部重叠。那么就更新后面的IP分片的偏移即可。这时无需继续测试,可以跳出循             环 */
  5.             /* Eat head of the next overlapped fragment
  6.              * and leave the loop. The next ones cannot overlap.
  7.              */
  8.             if (!pskb_pull(next, i))
  9.                 goto err;
  10.             FRAG_CB(next)->offset = i;
  11.             qp->q.meat -= i;
  12.             if (next->ip_summed != CHECKSUM_UNNECESSARY)
  13.                 next->ip_summed = CHECKSUM_NONE;
  14.             break;
  15.         } else {
  16.             /* 重叠部分覆盖已有的IP分片,那么可以将已有的IP分片释放, 然后继续测试下一个分片 */
  17.             struct sk_buff *free_it = next;

  18.             /* Old fragment is completely overridden with
  19.              * new one drop it.
  20.              */
  21.             next = next->next;

  22.             if (prev)
  23.                 prev->next = next;
  24.             else
  25.                 qp->q.fragments = next;

  26.             qp->q.meat -= free_it->len;
  27.             frag_kfree_skb(qp->q.net, free_it);
  28.         }
  29.     }
     
     /* 已经处理完了重叠问题,offset为新的偏移量 */
  1.     FRAG_CB(skb)->offset = offset;

     /* 下面的操作很简单,插入新的分片,更新分片队列的信息 */
  1.     /* Insert this fragment in the chain of fragments. */
  2.     skb->next = next;
  3.     if (!next)
  4.         qp->q.fragments_tail = skb;
  5.     if (prev)
  6.         prev->next = skb;
  7.     else
  8.         qp->q.fragments = skb;

  9.     dev = skb->dev;
  10.     if (dev) {
  11.         qp->iif = dev->ifindex;
  12.         skb->dev = NULL;
  13.     }
  14.     qp->q.stamp = skb->tstamp;
  15.     qp->q.meat = skb->len;
  16.     atomic_add(skb->truesize, &qp->q.net->mem);
     /* 偏移为0,说明是第一个分片 */
  1.     if (offset == 0)
  2.         qp->q.last_in |= INET_FRAG_FIRST_IN;

  3.     /* 
  4.     如果已经收到了第一个分片和最后一个分片,且收到的IP分片的长度也等于了原始的IP长度。
  5.     那么说明一切就绪,可以重组IP分片了。
  6.     */
  7.     if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
  8.      qp->q.meat == qp->q.len)
  9.         return ip_frag_reasm(qp, prev, dev);

     /* IP分片还未全部收齐 */
  1.     write_lock(&ip4_frags.lock);
  2.     list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
  3.     write_unlock(&ip4_frags.lock);
  4.     return -EINPROGRESS;

  5. err:
  6.     kfree_skb(skb);
  7.     return err;
  8. }
未完待续。。。
阅读(425) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~