libnids中TCP/IP栈实现细节分析(下)——IP分片重组

转载:http://blog.csdn.net/msda/article/details/8494575

在此之前,如果不懂IP分片技术的话,请参照这里。IP分片技术比较简单暴力,没有TCP那样复杂复杂的窗口协议。基本上只是暴力的拆分和重组,代码基本在ip_defragment.c中。

先从总体上说说。首先,每个IP(主机)都会有IP分片包(注意是IP,不是IP对)。所以,每个IP都有一个如下的结构体来维护上面的所以IP分片:

  1. struct hostfrags {  
  2.   struct ipq *ipqueue;//这里维护IP碎片队列  
  3.   int ip_frag_mem;  
  4.   u_int ip;//主机对应的IP地址  
  5.   //很明显,下面三行告诉我们,这是哈希表的一个元素  
  6.   int hash_index;  
  7.   struct hostfrags *prev;  
  8.   struct hostfrags *next;  
  9. };  
  10. //下面这个就是维护所有IP的哈希表了。  
  11. static struct hostfrags **fragtable;  

每个IP下面又有很多的被分片的IP包——IP碎片队列,IP碎片队列的定义在这:
  1. /* Describe an entry in the "incomplete datagrams" queue. */  
  2. struct ipq {  
  3.   unsigned char *mac;        /* pointer to MAC header                */  
  4.   struct ip *iph;        /* pointer to IP header                 */  
  5.   int len;            /* total length of original datagram    */  
  6.   short ihlen;            /* length of the IP header              */  
  7.   short maclen;            /* length of the MAC header             */  
  8.   struct timer_list timer;    /* when will this queue expire?         */  
  9.   struct ipfrag *fragments;    /* linked list of received fragments    */  
  10.   struct hostfrags *hf;  
  11.   struct ipq *next;        /* linked list pointers                 */  
  12.   struct ipq *prev;  
  13.   // struct device *dev;    /* Device - for icmp replies */  
  14. };  

最终的IP碎片的定义在这:
  1. /* Describe an IP fragment. */  
  2. struct ipfrag   
  3. {  
  4.   int offset;            /* offset of fragment in IP datagram    */  
  5.   int end;            /* last byte of data in datagram        */  
  6.   int len;            /* length of this fragment              */  
  7.   struct sk_buff *skb;        /* complete received fragment           */  
  8.   unsigned char *ptr;        /* pointer into real fragment data      */  
  9.   struct ipfrag *next;        /* linked list pointers                 */  
  10.   struct ipfrag *prev;  
  11. };  

由于libnids中的分片重组代码是从内核中拿出来修改的,所以保留了内核的注释。这里就不多做解释了。

好了步入处理逻辑,照例,先看初始化:

  1. void ip_frag_init(int n)  
  2. {  
  3.   struct timeval tv;  
  4.    
  5.   gettimeofday(&tv, 0);  
  6.   time0 = tv.tv_sec;  
  7.   fragtable = (struct hostfrags **) calloc(n, sizeof(struct hostfrags *));  
  8.   if (!fragtable)  
  9.     nids_params.no_mem("ip_frag_init");  
  10.   hash_size = n;  
  11. }  
简单到不能再简单——分片了一个主机的哈希表。分完手工。好吧,看重组逻辑:
  1. //先是判断是否为分片的函数  
  2. int ip_defrag_stub(struct ip *iph, struct ip **defrag)  
  3. {  
  4.     int offset, flags, tot_len;  
  5.     struct sk_buff *skb;  
  6.   
  7.     numpack++;  
  8.     //先处理超时事件  
  9.     timenow = 0;//刷新时间  
  10.     while (timer_head && timer_head->expires < jiffies())  
  11.     {  
  12.         this_host = ((struct ipq *) (timer_head->data))->hf;  
  13.         timer_head->function(timer_head->data);  
  14.     }  
  15.   
  16.     //然后计算分片的偏移  
  17.     offset = ntohs(iph->ip_off);  
  18.     flags = offset & ~IP_OFFSET;  
  19.     offset &= IP_OFFSET;  
  20.   
  21.     //此包不是分片  
  22.     if (((flags & IP_MF) == 0) && (offset == 0))  
  23.     {  
  24.         ip_defrag(iph, 0);  
  25.         return IPF_NOTF;  
  26.     }  
  27.   
  28.     //此包是分片,先申请一个sk_buff把分片的数据保存起来,然后交给defrag函数  
  29.     tot_len = ntohs(iph->ip_len);  
  30.     skb = (struct sk_buff *) malloc(tot_len + sizeof(struct sk_buff));  
  31.     if (!skb)  
  32.         nids_params.no_mem("ip_defrag_stub");  
  33.     skb->data = (char *) (skb + 1);  
  34.     memcpy(skb->data, iph, tot_len);  
  35.     skb->truesize = tot_len + 16 + nids_params.dev_addon;  
  36.     skb->truesize = (skb->truesize + 15) & ~15;  
  37.     skb->truesize += nids_params.sk_buff_size;  
  38.   
  39.     //如果集齐了一个ip包的所有分片ip_defrag将返回合并后的ip包,此时返回IPF_NEW,进行下一步的ip包处理  
  40.     //否则,返回IPF_ISF,跳过ip包处理  
  41.     if ((*defrag = (struct ip *)ip_defrag((struct ip *) (skb->data), skb)))  
  42.         return IPF_NEW;  
  43.   
  44.     return IPF_ISF;  
  45. }  
  46.   
  47.   
  48. /* Process an incoming IP datagram fragment. */  
  49. //这里就是分片重组的主要逻辑了  
  50. static char *ip_defrag(struct ip *iph, struct sk_buff *skb)  
  51. {  
  52.     struct ipfrag *prev, *next, *tmp;  
  53.     struct ipfrag *tfp;  
  54.     struct ipq *qp;  
  55.     char *skb2;  
  56.     unsigned char *ptr;  
  57.     int flags, offset;  
  58.     int i, ihl, end;  
  59.   
  60.     //如果是分片,而且host哈希表里还没有对应的host项的话,果断新建一个  
  61.     //此处还负责将this_host变量设为当前ip对应的host  
  62.     if (!hostfrag_find(iph) && skb)  
  63.         hostfrag_create(iph);  
  64.   
  65.     /* Start by cleaning up the memory. */  
  66.     //内存用太多了,panic之,然后释放当前host分片所用的内存  
  67.     if (this_host)  
  68.         if (this_host->ip_frag_mem > IPFRAG_HIGH_THRESH)  
  69.             ip_evictor();  
  70.   
  71.     /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */  
  72.     //这里,找到这个ip包对应的ip分片链表  
  73.     if (this_host)  
  74.         qp = ip_find(iph);  
  75.     else  
  76.         qp = 0;  
  77.   
  78.     /* Is this a non-fragmented datagram? */  
  79.     offset = ntohs(iph->ip_off);  
  80.     flags = offset & ~IP_OFFSET;  
  81.     offset &= IP_OFFSET;  
  82.     if (((flags & IP_MF) == 0) && (offset == 0))  
  83.     {  
  84.         if (qp != NULL)  
  85.             ip_free(qp);      /* Fragmented frame replaced by full 
  86.                    unfragmented copy */  
  87.         return 0;  
  88.     }  
  89.   
  90.     /* ip_evictor() could have removed all queues for the current host */  
  91.     if (!this_host)  
  92.         hostfrag_create(iph);  
  93.   
  94.     offset <<= 3;           /* offset is in 8-byte chunks */  
  95.     ihl = iph->ip_hl * 4;  
  96.   
  97.     /* 
  98.       If the queue already existed, keep restarting its timer as long as 
  99.       we still are receiving fragments.  Otherwise, create a fresh queue 
  100.       entry. 
  101.     */  
  102.     //如果当前host下来过此包的碎片  
  103.     if (qp != NULL)  
  104.     {  
  105.         /* ANK. If the first fragment is received, we should remember the correct 
  106.            IP header (with options) */  
  107.         if (offset == 0)  
  108.         {  
  109.             qp->ihlen = ihl;  
  110.             memcpy(qp->iph, iph, ihl + 8);  
  111.         }  
  112.         del_timer(&qp->timer);  
  113.         qp->timer.expires = jiffies() + IP_FRAG_TIME;    /* about 30 seconds */  
  114.         qp->timer.data = (unsigned long) qp; /* pointer to queue */  
  115.         qp->timer.function = ip_expire;  /* expire function */  
  116.         add_timer(&qp->timer);  
  117.     }  
  118.     //否则新建一个碎片队列  
  119.     else  
  120.     {  
  121.         /* If we failed to create it, then discard the frame. */  
  122.         if ((qp = ip_create(iph)) == NULL)  
  123.         {  
  124.             kfree_skb(skb, FREE_READ);  
  125.             return NULL;  
  126.         }  
  127.     }  
  128.     /* Attempt to construct an oversize packet. */  
  129.     //再大的ip包也不能大过65535啊,一经发现,直接放弃  
  130.     if (ntohs(iph->ip_len) + (int) offset > 65535)  
  131.     {  
  132.         // NETDEBUG(printk("Oversized packet received from %s\n", int_ntoa(iph->ip_src.s_addr)));  
  133.         nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERSIZED, iph, 0);  
  134.         kfree_skb(skb, FREE_READ);  
  135.         return NULL;  
  136.     }  
  137.   
  138.     //下面就开始在碎片队列里面找位置了,同时处理好重叠  
  139.     //如果有重叠,把重叠的旧的部分去掉  
  140.     /* Determine the position of this fragment. */  
  141.     end = offset + ntohs(iph->ip_len) - ihl;  
  142.   
  143.     /* Point into the IP datagram 'data' part. */  
  144.     ptr = (unsigned char *)(skb->data + ihl);  
  145.   
  146.     /* Is this the final fragment? */  
  147.     if ((flags & IP_MF) == 0)  
  148.         qp->len = end;  
  149.   
  150.     /* 
  151.       Find out which fragments are in front and at the back of us in the 
  152.       chain of fragments so far.  We must know where to put this 
  153.       fragment, right? 
  154.     */  
  155.     prev = NULL;  
  156.     for (next = qp->fragments; next != NULL; next = next->next)  
  157.     {  
  158.         if (next->offset >= offset)  
  159.             break;            /* bingo! */  
  160.         prev = next;  
  161.     }  
  162.     /* 
  163.       We found where to put this one.  Check for overlap with preceding 
  164.       fragment, and, if needed, align things so that any overlaps are 
  165.       eliminated. 
  166.     */  
  167.     if (prev != NULL && offset < prev->end)  
  168.     {  
  169.         nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0);  
  170.         i = prev->end - offset;  
  171.         offset += i;        /* ptr into datagram */  
  172.         ptr += i;           /* ptr into fragment data */  
  173.     }  
  174.     /* 
  175.       Look for overlap with succeeding segments. 
  176.       If we can merge fragments, do it. 
  177.     */  
  178.     for (tmp = next; tmp != NULL; tmp = tfp)  
  179.     {  
  180.         tfp = tmp->next;  
  181.         if (tmp->offset >= end)  
  182.             break;            /* no overlaps at all */  
  183.         nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0);  
  184.   
  185.         i = end - next->offset;  /* overlap is 'i' bytes */  
  186.         tmp->len -= i;       /* so reduce size of    */  
  187.         tmp->offset += i;        /* next fragment        */  
  188.         tmp->ptr += i;  
  189.         /* 
  190.           If we get a frag size of <= 0, remove it and the packet that it 
  191.           goes with. We never throw the new frag away, so the frag being 
  192.           dumped has always been charged for. 
  193.         */  
  194.         if (tmp->len <= 0)  
  195.         {  
  196.             if (tmp->prev != NULL)  
  197.                 tmp->prev->next = tmp->next;  
  198.             else  
  199.                 qp->fragments = tmp->next;  
  200.   
  201.             if (tmp->next != NULL)  
  202.                 tmp->next->prev = tmp->prev;  
  203.   
  204.             next = tfp;       /* We have killed the original next frame */  
  205.   
  206.             frag_kfree_skb(tmp->skb, FREE_READ);  
  207.             frag_kfree_s(tmp, sizeof(struct ipfrag));  
  208.         }  
  209.     }  
  210.     //下面往队列中插入当前碎片  
  211.     /* Insert this fragment in the chain of fragments. */  
  212.     tfp = NULL;  
  213.     tfp = ip_frag_create(offset, end, skb, ptr);  
  214.   
  215.     /* 
  216.       No memory to save the fragment - so throw the lot. If we failed 
  217.       the frag_create we haven't charged the queue. 
  218.     */  
  219.     if (!tfp)  
  220.     {  
  221.         nids_params.no_mem("ip_defrag");  
  222.         kfree_skb(skb, FREE_READ);  
  223.         return NULL;  
  224.     }  
  225.     /* From now on our buffer is charged to the queues. */  
  226.     tfp->prev = prev;  
  227.     tfp->next = next;  
  228.     if (prev != NULL)  
  229.         prev->next = tfp;  
  230.     else  
  231.         qp->fragments = tfp;  
  232.   
  233.     if (next != NULL)  
  234.         next->prev = tfp;  
  235.   
  236.     /* 
  237.       OK, so we inserted this new fragment into the chain.  Check if we 
  238.       now have a full IP datagram which we can bump up to the IP 
  239.       layer... 
  240.     */  
  241.     //查看是不是碎片都搜集齐了,如果齐了,组合成一个大ip包返回  
  242.     if (ip_done(qp))  
  243.     {  
  244.         skb2 = ip_glue(qp);     /* glue together the fragments */  
  245.         return (skb2);  
  246.     }  
  247.     return (NULL);  

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值