免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1588 | 回复: 0
打印 上一主题 下一主题

inux内核ip分片函数ip_fragment解析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-10-29 16:50 |只看该作者 |倒序浏览

               
               
                 int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))  
{  
     struct iphdr *iph;  
     int raw = 0;  
     int ptr;  
     struct net_device *dev;  
     struct sk_buff *skb2;  
     unsigned int mtu, hlen, left, len, ll_rs, pad;  
     int offset;  
     __be16 not_last_frag;  
     /* 取得路由表 */  
     struct rtable *rt = skb->rtable;  
     int err = 0;  
     /* 网络设备 */  
     dev = rt->u.dst.dev;  
   
     /*
      *  Point into the IP datagram header.
      */  
     /* 取得ip头 */  
     iph = ip_hdr(skb);  
     /*
      * 判断DF位,知道如果df位被设置了话就表示不要被分片,
      * 这时ip_fragment将会发送一个icmp报文返回到源主机。这里
      * 主要是为forward数据所判断。  
      */  
     if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {  
         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,  
               htonl(ip_skb_dst_mtu(skb)));  
         kfree_skb(skb);  
         return -EMSGSIZE;  
     }  
   
     /*
      *  Setup starting values.
      */  
     /* 得到ip头的长度 */  
     hlen = iph->ihl * 4;  
     /*
      * 得到mtu的大小。这里要注意,他的大小减去了hlen,也就是ip头的大小
      */  
     mtu = dst_mtu(&rt->u.dst) - hlen;    /* Size of data space */  
     IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;  
      
      
     /* When frag_list is given, use it. First, check its validity:
      * some transformers could create wrong frag_list or break existing
      * one, it is not prohibited. In this case fall back to copying.
      *
      * LATER: this step can be merged to real generation of fragments,
      * we can switch to copy when see the first bad fragment.
      */  
     /*
     * 如果4层将数据包分片了,那么就会把这些数据包放到skb的frag_list链表中,
     * 因此这里首先先判断frag_list链表是否为空,为空的话将会进行slow 分片
     */  
     if (skb_shinfo(skb)->frag_list) {  
         struct sk_buff *frag;  
         /*
          * 取得第一个数据报的len.当sk_write_queue队列被flush后,
          * 除了第一个切好包的另外的包都会加入到frag_list中,而这里
          * 需要得到的第一个包(也就是本身这个sk_buff)的长度。
          */  
         int first_len = skb_pagelen(skb);  
         int truesizes = 0;  
         /*
          * 接下来的判断都是为了确定能进行fast分片。分片不能被共享,
          * 这是因为在fast path 中,需要加给每个分片不同的ip头(而并
          * 不会复制每个分片)。因此在fast path中是不可接受的。而在
          * slow path中,就算有共享也无所谓,因为他会复制每一个分片,
          * 使用一个新的buff。   
          */  
           
         /*
          * 判断第一个包长度是否符合一些限制(包括mtu,mf位等一些限制).
          * 如果第一个数据报的len没有包含mtu的大小这里之所以要把第一个
          * 切好片的数据包单独拿出来检测,是因为一些域是第一个包所独有
          * 的(比如IP_MF要为1)。这里由于这个mtu是不包括hlen的mtu,因此
          * 需要减去一个hlen。   
          */  
         if (first_len - hlen > mtu ||  
             ((first_len - hlen) & 7) ||  
             (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||  
             skb_cloned(skb))  
             goto slow_path;  
   
         /* 遍历剩余的frag */  
         for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {  
             /* Correct geometry. */  
   
             /*
              * 判断每个帧的mtu,以及相关的东西,如果不符合条件则要
              * 进行slow path,基本和上面的第一个skb的判断类似
              */  
             if (frag->len > mtu ||  
                 ((frag->len & 7) && frag->next) ||  
                 skb_headroom(frag)  hlen)  
                 goto slow_path;  
               
             /* Partially cloned skb? */  
             /* 判断是否共享 */  
             if (skb_shared(frag))  
                 goto slow_path;  
   
             BUG_ON(frag->sk);  
             /* 进行socket的一些操作 */  
             if (skb->sk) {  
                 sock_hold(skb->sk);  
                 frag->sk = skb->sk;  
                 frag->destructor = sock_wfree;  
                 truesizes += frag->truesize;  
             }  
         }  
   
         /* Everything is OK. Generate! */  
         /* 通过上面的检测,都通过了,因此可以进行fast path分片了 */  
         err = 0;  
         offset = 0;  
         /* 取得frag_list列表  */  
         frag = skb_shinfo(skb)->frag_list;  
         skb_shinfo(skb)->frag_list = NULL;  
         /* 得到数据(不包括头)的大小 */  
         skb->data_len = first_len - skb_headlen(skb);  
         skb->truesize -= truesizes;  
         skb->len = first_len;  
         iph->tot_len = htons(first_len);  
         /* 设置mf位  */  
         iph->frag_off = htons(IP_MF);  
         ip_send_check(iph);  
   
         /* 开始进行发送 */  
         for (;;) {  
             /* Prepare header of the next frame,
              * before previous one went down. */  
             if (frag) {  
                 frag->ip_summed = CHECKSUM_NONE;  
                 /* 设置相应的头部 */  
                 skb_reset_transport_header(frag);  
                 /* 预留ddos header 空间 */  
                 // hlen += DDOS_HDR_LEN;  
                 __skb_push(frag, hlen);  
                 skb_reset_network_header(frag);  
                 // hlen -= DDOS_HDR_LEN;  
                  
                 /* 复制ip头 */  
                 memcpy(skb_network_header(frag), iph, hlen);  
                 iph = ip_hdr(frag);  
                 iph->tot_len = htons(frag->len);  
                 /* 将当前skb的一些属性付给将要传递的分片好的帧 */  
                 ip_copy_metadata(frag, skb);  
                 /* 处理ip_option  */  
                 if (offset == 0)  
                     ip_options_fragment(frag);  
                 offset += skb->len - hlen;  
                 /* 设置位移 */  
                 iph->frag_off = htons(offset>>3);  
                 if (frag->next != NULL)  
                     iph->frag_off |= htons(IP_MF);  
                 /* Ready, complete checksum */  
                 ip_send_check(iph);  
             }  
             /* 调用输出函数 */  
             err = output(skb);  
   
             if (!err)  
                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);  
             if (err || !frag)  
                 break;  
             /* 处理链表中下一个buf */  
             skb = frag;  
             frag = skb->next;  
             skb->next = NULL;  
         }  
   
         if (err == 0) {  
             IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);  
             return 0;  
         }  
         /* 释放内存 */  
         while (frag) {  
             skb = frag->next;  
             kfree_skb(frag);  
             frag = skb;  
         }  
         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
         return err;  
     }  
   
slow_path:  
     /* 分片的数据剩余长度 */  
     left = skb->len - hlen;      /* Space per frame */  
     /* 而ptr就是分片开始的数据指针 */  
     ptr = raw + hlen;       /* Where to start from */  
      
     /* for bridged IP traffic encapsulated inside f.e. a vlan header,
      * we need to make room for the encapsulating header
      */  
     /* 处理桥接、VLAN、PPPOE相关MTU */  
     pad = nf_bridge_pad(skb);  
     ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, pad);  
     mtu -= pad;  
   
     /*
      *  Fragment the datagram.
      */  
     /* 取出ip offset域 */  
     offset = (ntohs(iph->frag_off) & IP_OFFSET)  3;  
     /* not_last_frag表明这个帧是否是最后一个分片 */  
     not_last_frag = iph->frag_off & htons(IP_MF);  
   
     /*
      *  Keep copying data until we run out.
      */  
     /* 开始为循环处理,每一个分片创建一个skb buffer */  
     while (left > 0) {  
         len = left;  
         /* IF: it doesn't fit, use 'mtu' - the data space left */  
         /* 如果len大于mtu,设置当前的将要分片的数据大小为mtu */  
         if (len > mtu)  
             len = mtu;  
         /* IF: we are not sending upto and including the packet end
            then align the next start on an eight byte boundary */  
         /* 长度对齐 */  
         if (len  left)  {  
             len &= ~7;  
         }  
   
         /*
          *  Allocate buffer.
          */  
         /* malloc一个新的buff,它的大小包括ip payload,ip head,以及L2 head */  
         if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {  
             NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n");  
             err = -ENOMEM;  
             goto fail;  
         }  
   
         /*
          *  Set up data on packet
          */  
         /* 调用ip_copy_metadata复制一些相同的值的域 */  
         ip_copy_metadata(skb2, skb);  
         /* 保留L2 header空间 */  
         skb_reserve(skb2, ll_rs);  
         /* 设置ip header & ddos header & ip paylod 空间 */  
         skb_put(skb2, len + hlen);  
         skb_reset_network_header(skb2);  
         /* L4 header指针为ip header + ddos header数据偏移位置,用于复制原始payload */  
         skb2->transport_header = skb2->network_header + hlen;  
   
         /*
          *  Charge the memory for the fragment to any owner
          *  it might possess
          */  
         /* 将每一个分片的ip包都关联到源包的socket */  
         if (skb->sk)  
             skb_set_owner_w(skb2, skb->sk);  
           
         /*
          *  Copy the packet header into the new buffer.
          */  
   
         /* 拷贝ip header */  
         skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);  
   
         /*
          *  Copy a block of the IP datagram.
          */  
         /* 拷贝ip payload数据 */  
         if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))  
             BUG();  
               
         /* 分片的数据剩余长度 */  
         left -= len;  
           
         /*
          *  Fill in the new header fields.
          */  
         /* 填充相应的ip头 */  
         iph = ip_hdr(skb2);  
         iph->frag_off = htons((offset >> 3));  
   
         /* ANK: dirty, but effective trick. Upgrade options only if
          * the segment to be fragmented was THE FIRST (otherwise,
          * options are already fixed) and make it ONCE
          * on the initial skb, so that all the following fragments
          * will inherit fixed options.
          */  
         /* 第一个包,因此进行ip_option处理 */  
         if (offset == 0)  
             ip_options_fragment(skb);  
   
         /*
          *  Added AC : If we are fragmenting a fragment that's not the
          *         last fragment then keep MF on each bit
          */  
         /* 不是最后一个包,因此设置mf位 */  
         if (left > 0 || not_last_frag)  
             iph->frag_off |= htons(IP_MF);  
               
         /* 移动数据指针以及更改数据偏移 */  
         ptr += len;      
         offset += len;  
   
         /*
          *  Put this fragment into the sending queue.
          */  
         /* 增加ddos header 长度 */  
         // hlen += DDOS_HDR_LEN;  
           
         /* 更新包头的数据长度 */  
         iph->tot_len = htons(len + hlen);  
         /* 重新计算校验 */  
         ip_send_check(iph);  
   
         /* 复位ip header大小 */  
         // hlen -= DDOS_HDR_LEN;  
           
         err = output(skb2);  
         if (err)  
             goto fail;  
   
         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);  
     }  
     kfree_skb(skb);  
     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);  
     return err;  
   
fail:  
     kfree_skb(skb);  
     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
     return err;  
}  


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/88926/showart_2082264.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP