Chinaunix首页 | 论坛 | 博客
  • 博客访问: 825568
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2014-02-12 15:48:41

转:http://blog.csdn.net/nerdx/article/details/12624407
  1. //ip分片  
  2. //  快速路径的条件:  
  3. //      1.skb  
  4. //          1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu  
  5. //          2.skb的数据长度对齐到8字节的边界  
  6. //          3.skb没有被分片  
  7. //          4.skb没有被共享  
  8. //      2.skb->frag_list  
  9. //          1.长度小于(mtu-ip报头-选项)  
  10. //          2.除最后一个分片外,长度都需要对齐到8字节边界  
  11. //          3.head-data之间的空间,可以容纳ip报头  
  12. //  注:skb->frag_list的skb,没有填充ip头,skb填充有ip头  
  13. //  
  14. //  慢速路径条件:  
  15. //      只要不满足快速路径其中的一条,使用慢速路径  
  16.   
  17. //  快速路径处理过程:  
  18. //      1.第一个分片使用完整的ip选项  
  19. //      2.其余分片使用部分ip选项  
  20. //      3.除最后一个分片外,设置MF标志  
  21. //      4.设置offset  
  22. //      5.使用相同的路由信息,向下层传递  
  23.   
  24. //  慢速路径处理过程:  
  25. //      1.分配新的skb,长度为mtu,或者剩余数据量,对齐到8字节边界  
  26. //      2.预留l2帧头空间  
  27. //      3.拷贝l3报头,以及数据到新skb中  
  28. //      4.除第一个分片使用完整的ip选项,其余分片使用部分ip选项  
  29. //      5.设置offset  
  30. //      5.使用相同的路由信息,向下层传递  
  31.   
  32.   
  33. //  对比快速路径与慢速路径:  
  34. //      1.慢速路径的慢主要表现在分配新的缓存区,从旧缓存区中拷贝数据  
  35.   
  36. //  注:ip报头的offset字段,只针对有效载荷(ip头,ip选项不包括在内)  
  37.   
  38. //调用路径ip_output/ip_mc_output->ip_fragment  
  39. 1.1 int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))  
  40. {  
  41.     struct iphdr *iph;  
  42.     int raw = 0;  
  43.     int ptr;  
  44.     struct net_device *dev;  
  45.     struct sk_buff *skb2;  
  46.     unsigned int mtu, hlen, left, len, ll_rs;  
  47.     int offset;  
  48.     int not_last_frag;  
  49.     struct rtable *rt = (struct rtable*)skb->dst;  
  50.     int err = 0;  
  51.     //出口设备  
  52.     dev = rt->u.dst.dev;  
  53.     //ip头  
  54.     iph = skb->nh.iph;  
  55.     //ip报头设置有DF标志,禁止分片  
  56.     ////skb->local_df如果被设置,则在需要分片,但是设置DF标志,不向发送方传送ICMP消息  
  57.     if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {  
  58.         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,//需要分片,但是设置DF标志,通知发送方不可达,原因是需要分片,并告知对方mtu  
  59.               htonl(dst_pmtu(&rt->u.dst)));  
  60.         kfree_skb(skb);  
  61.         return -EMSGSIZE;  
  62.     }  
  63.   
  64.     //可以快速分片的条件:  
  65.     //  skb  
  66.     //      1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu  
  67.     //      2.skb的数据长度对齐到8字节的边界  
  68.     //      3.skb没有被分片  
  69.     //      4.skb没有被共享  
  70.     //  skb->frag_list  
  71.     //      1.长度小于(mtu-ip报头)  
  72.     //      2.除最后一个分片外,长度都需要对齐到8字节边界  
  73.     //      3.head-data之间的空间,可以容纳ip报头  
  74.     //  注:skb->frag_list的skb,没有填充ip头,skb填充有ip头  
  75.     hlen = iph->ihl * 4;//ip头长度  
  76.     mtu = dst_pmtu(&rt->u.dst) - hlen;   //数据空间的大小  
  77.   
  78.     if (skb_shinfo(skb)->frag_list) {//frag_list存在skb  
  79.         struct sk_buff *frag;  
  80.         int first_len = skb_pagelen(skb);//skb主缓存区,frags片段中的数据长度,不包括frag_list中的skb  
  81.   
  82.         if (first_len - hlen > mtu ||//超过允许的最大数据量  
  83.             ((first_len - hlen) & 7) ||//数据长度没有对齐到8字节  
  84.             (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||//此skb是一个分片  
  85.             skb_cloned(skb))//克隆一份skb,慢速分片  
  86.             goto slow_path;  
  87.         //检查frag_list中的skb是否可以快速分片  
  88.         for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {  
  89.             //frag_list中的skb没有ip头  
  90.             if (frag->len > mtu ||  
  91.                 ((frag->len & 7) && frag->next) ||//除最后一个分片外,其他分片的长度必须对其到8字节  
  92.                 skb_headroom(frag) < hlen)//头空间不够容纳ip报头(ip头+选项)  
  93.                 goto slow_path;  
  94.   
  95.             if (skb_shared(frag))//skb被共享  
  96.                 goto slow_path;  
  97.         }  
  98.     //快速路径:  
  99.         err = 0;  
  100.         offset = 0;  
  101.         frag = skb_shinfo(skb)->frag_list;  
  102.         skb_shinfo(skb)->frag_list = NULL;  
  103.         //更新skb->data_len为skb->frags中数据的大小,原始skb->data_len包括frags,frag_list中所有数据的长度  
  104.         skb->data_len = first_len - skb_headlen(skb);  
  105.         skb->len = first_len;//更新总长度为主缓存区,frags中数据的大小,原始skb->len包括主缓存区,frags,frag_list中所有数据的长度  
  106.         iph->tot_len = htons(first_len);//ip报头的数据包长度  
  107.         iph->frag_off |= htons(IP_MF);//表示有更多的分片,第一个分片,offset=0  
  108.         ip_send_check(iph);//计算ip校验和  
  109.   
  110.         for (;;) {  
  111.             if (frag) {//处理skb->frag_list中的skb,为其准备分片的ip报头  
  112.                 frag->ip_summed = CHECKSUM_NONE;//表示校验和没有计算  
  113.                 frag->h.raw = frag->data;  
  114.                 frag->nh.raw = __skb_push(frag, hlen);//移动skb->data指针,填充ip报头和选项  
  115.                 memcpy(frag->nh.raw, iph, hlen);  
  116.                 iph = frag->nh.iph;  
  117.                 iph->tot_len = htons(frag->len);//总长度  
  118.                 ip_copy_metadata(frag, skb);//使分片skb与头skb有一样的出口设备,路由信息  
  119.                 if (offset == 0)//第一个分片具有完整的选项,其他分片将所有非copied的选项,均设置为NOOP  
  120.                     ip_options_fragment(frag);  
  121.                 offset += skb->len - hlen;//计算本分片的偏移量  
  122.                 iph->frag_off = htons(offset>>3);//偏移量对齐在8字节  
  123.                 if (frag->next != NULL)  
  124.                     iph->frag_off |= htons(IP_MF);//设置还有更多skb  
  125.                 ip_send_check(iph);//计算ip校验和  
  126.             }  
  127.   
  128.             err = output(skb);//向下传递前一个skb,调用ip_finish_output  
  129.   
  130.             if (err || !frag)  
  131.                 break;  
  132.   
  133.             skb = frag;//保留指向前一个skb的指针  
  134.             frag = skb->next;//frag为下一个待处理的skb  
  135.             skb->next = NULL;//  
  136.         }  
  137.   
  138.         if (err == 0) {  
  139.             IP_INC_STATS(IPSTATS_MIB_FRAGOKS);  
  140.             return 0;  
  141.         }  
  142.         //快速路径分片出现错误,释放所有skb  
  143.         while (frag) {  
  144.             skb = frag->next;  
  145.             kfree_skb(frag);  
  146.             frag = skb;  
  147.         }  
  148.         IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);  
  149.         return err;  
  150.     }  
  151.   
  152.     //慢速路径  
  153. slow_path:  
  154.     left = skb->len - hlen;  //(主缓存区,frags,frag_list)数据的总大小(不包括ip头,选项)     
  155.     ptr = raw + hlen;       //新分片的数据在原skb中的起始位置  
  156.   
  157.     offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;//当前分片的偏移量  
  158.     not_last_frag = iph->frag_off & htons(IP_MF);//判断是否为最后一个分片  
  159.   
  160.     //开始进行分片  
  161.     while(left > 0)  {  
  162.         len = left;  
  163.         if (len > mtu)//使用mtu(此处的mtu去掉ip报头和选项长度)  
  164.             len = mtu;  
  165.           
  166.         if (len < left)  {  
  167.             len &= ~7;//长度对齐到8字节边界  
  168.         }  
  169.         //分配新的skb,长度包括l2帧头,l3报头,l3有效载荷  
  170.         if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {  
  171.             NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));  
  172.             err = -ENOMEM;  
  173.             goto fail;  
  174.         }  
  175.         //使所有分片都使用相同的路由信息,出口设备  
  176.         ip_copy_metadata(skb2, skb);  
  177.         skb_reserve(skb2, ll_rs);//预留l2帧头  
  178.         skb_put(skb2, len + hlen);//data-tail之间空间大小为(ip报头+ip选项+ip有效载荷)  
  179.         skb2->nh.raw = skb2->data;//设置l3报头起始地址  
  180.         skb2->h.raw = skb2->data + hlen;//l3有效载荷  
  181.         //设置新创建的skb所属的sock  
  182.         if (skb->sk)  
  183.             skb_set_owner_w(skb2, skb->sk);  
  184.         //拷贝ip头,选项  
  185.         memcpy(skb2->nh.raw, skb->data, hlen);  
  186.         //将skb起始地址为ptr的len个字节拷贝到skb2中  
  187.         //由skb_copy_bits处理frag,frag_list  
  188.         if (skb_copy_bits(skb, ptr, skb2->h.raw, len))  
  189.             BUG();  
  190.         left -= len;//更新剩余待分片的数据量  
  191.   
  192.         iph = skb2->nh.iph;  
  193.         iph->frag_off = htons((offset >> 3));//偏移量  
  194.   
  195.         //1.只有第一个分片需要完整的选项,其他分片将非copied的选项设置为NOOP  
  196.         //2.由于第一个分片已经拷贝完整的ip报头以及选项到其分片中  
  197.         //3.优化效率,在第一个分片拷贝完整的选项后,更新选项,非第一个分片都使用相同的ip选项  
  198.         if (offset == 0)  
  199.             ip_options_fragment(skb);  
  200.   
  201.         if (left > 0 || not_last_frag)  
  202.             iph->frag_off |= htons(IP_MF);//还有跟多的分片  
  203.         ptr += len;//下一个分片的数据在原skb中的起始位置  
  204.         offset += len;//偏移量  
  205.   
  206.         IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);  
  207.   
  208.         iph->tot_len = htons(len + hlen);  
  209.   
  210.         ip_send_check(iph);  
  211.   
  212.         err = output(skb2);  
  213.         if (err)  
  214.             goto fail;  
  215.     }  
  216.     kfree_skb(skb);  
  217.     IP_INC_STATS(IPSTATS_MIB_FRAGOKS);  
  218.     return err;  
  219.   
  220. fail:  
  221.     kfree_skb(skb);   
  222.     IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);  
  223.     return err;  
  224. }  
  225.   
  226. //调用路径 ip_fragment->ip_options_fragment  
  227. //  修改ip_option(skb->cb),将非copied类型的选项,均设置为NOOP类型  
  228. 2.1 void ip_options_fragment(struct sk_buff * skb)   
  229. {  
  230.     unsigned char * optptr = skb->nh.raw;  
  231.     struct ip_options * opt = &(IPCB(skb)->opt);  
  232.     int  l = opt->optlen;  
  233.     int  optlen;  
  234.   
  235.     while (l > 0) {  
  236.         switch (*optptr) {  
  237.         case IPOPT_END:  
  238.             return;  
  239.         case IPOPT_NOOP:  
  240.             l--;  
  241.             optptr++;  
  242.             continue;  
  243.         }  
  244.         optlen = optptr[1];  
  245.         if (optlen<2 || optlen>l)  
  246.           return;  
  247.         if (!IPOPT_COPIED(*optptr))//在选项type的第8个比特,指出该选项是否应该复制到ip分片中  
  248.             memset(optptr, IPOPT_NOOP, optlen);//对不需要拷贝到除第一个分片外的选项,设置为NOOP  
  249.         l -= optlen;  
  250.         optptr += optlen;  
  251.     }  
  252.     opt->ts = 0;//均设置为0,表明不需要time stamp,record route  
  253.     opt->rr = 0;  
  254.     opt->rr_needaddr = 0;  
  255.     opt->ts_needaddr = 0;  
  256.     opt->ts_needtime = 0;  
  257.     return;  
  258. }  
阅读(647) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~