linux内核ip分片函数ip_fragment解析(转贴)

  1. int  ip_fragment( struct  sk_buff *skb,  int  (*output)( struct  sk_buff *))  
  2. {  
  3.     struct  iphdr *iph;  
  4.     int  raw = 0;  
  5.     int  ptr;  
  6.     struct  net_device *dev;  
  7.     struct  sk_buff *skb2;  
  8.     unsigned int  mtu, hlen, left, len, ll_rs, pad;  
  9.     int  offset;  
  10.     __be16 not_last_frag;  
  11.     /* 取得路由表 */   
  12.     struct  rtable *rt = skb->rtable;  
  13.     int  err = 0;  
  14.     /* 网络设备 */   
  15.     dev = rt->u.dst.dev;  
  16.   
  17.     /*  
  18.      *  Point into the IP datagram header.  
  19.      */   
  20.     /* 取得ip头 */   
  21.     iph = ip_hdr(skb);  
  22.     /*  
  23.      * 判断DF位,知道如果df位被设置了话就表示不要被分片,  
  24.      * 这时ip_fragment将会发送一个icmp报文返回到源主机。这里  
  25.      * 主要是为forward数据所判断。   
  26.      */   
  27.     if  (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {  
  28.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
  29.         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,  
  30.               htonl(ip_skb_dst_mtu(skb)));  
  31.         kfree_skb(skb);  
  32.         return  -EMSGSIZE;  
  33.     }  
  34.   
  35.     /*  
  36.      *  Setup starting values.  
  37.      */   
  38.     /* 得到ip头的长度 */   
  39.     hlen = iph->ihl * 4;  
  40.     /*  
  41.      * 得到mtu的大小。这里要注意,他的大小减去了hlen,也就是ip头的大小  
  42.      */   
  43.     mtu = dst_mtu(&rt->u.dst) - hlen;    /* Size of data space */   
  44.     IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;  
  45.       
  46.       
  47.     /* When frag_list is given, use it. First, check its validity:  
  48.      * some transformers could create wrong frag_list or break existing  
  49.      * one, it is not prohibited. In this case fall back to copying.  
  50.      *  
  51.      * LATER: this step can be merged to real generation of fragments,  
  52.      * we can switch to copy when see the first bad fragment.  
  53.      */   
  54.     /*  
  55.     * 如果4层将数据包分片了,那么就会把这些数据包放到skb的frag_list链表中,  
  56.     * 因此这里首先先判断frag_list链表是否为空,为空的话将会进行slow 分片  
  57.     */   
  58.     if  (skb_shinfo(skb)->frag_list) {  
  59.         struct  sk_buff *frag;  
  60.         /*  
  61.          * 取得第一个数据报的len.当sk_write_queue队列被flush后,  
  62.          * 除了第一个切好包的另外的包都会加入到frag_list中,而这里  
  63.          * 需要得到的第一个包(也就是本身这个sk_buff)的长度。  
  64.          */   
  65.         int  first_len = skb_pagelen(skb);  
  66.         int  truesizes = 0;  
  67.         /*  
  68.          * 接下来的判断都是为了确定能进行fast分片。分片不能被共享,  
  69.          * 这是因为在fast path 中,需要加给每个分片不同的ip头(而并  
  70.          * 不会复制每个分片)。因此在fast path中是不可接受的。而在  
  71.          * slow path中,就算有共享也无所谓,因为他会复制每一个分片,  
  72.          * 使用一个新的buff。     
  73.          */   
  74.           
  75.         /*  
  76.          * 判断第一个包长度是否符合一些限制(包括mtu,mf位等一些限制).  
  77.          * 如果第一个数据报的len没有包含mtu的大小这里之所以要把第一个  
  78.          * 切好片的数据包单独拿出来检测,是因为一些域是第一个包所独有  
  79.          * 的(比如IP_MF要为1)。这里由于这个mtu是不包括hlen的mtu,因此  
  80.          * 需要减去一个hlen。    
  81.          */   
  82.         if  (first_len - hlen > mtu ||  
  83.             ((first_len - hlen) & 7) ||  
  84.             (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||  
  85.             skb_cloned(skb))  
  86.             goto  slow_path;  
  87.   
  88.         /* 遍历剩余的frag */   
  89.         for  (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {  
  90.             /* Correct geometry. */   
  91.   
  92.             /*  
  93.              * 判断每个帧的mtu,以及相关的东西,如果不符合条件则要  
  94.              * 进行slow path,基本和上面的第一个skb的判断类似  
  95.              */   
  96.             if  (frag->len > mtu ||  
  97.                 ((frag->len & 7) && frag->next) ||  
  98.                 skb_headroom(frag) < hlen)  
  99.                 goto  slow_path;  
  100.               
  101.             /* Partially cloned skb? */   
  102.             /* 判断是否共享 */   
  103.             if  (skb_shared(frag))  
  104.                 goto  slow_path;  
  105.   
  106.             BUG_ON(frag->sk);  
  107.             /* 进行socket的一些操作 */   
  108.             if  (skb->sk) {  
  109.                 sock_hold(skb->sk);  
  110.                 frag->sk = skb->sk;  
  111.                 frag->destructor = sock_wfree;  
  112.                 truesizes += frag->truesize;  
  113.             }  
  114.         }  
  115.   
  116.         /* Everything is OK. Generate! */   
  117.         /* 通过上面的检测,都通过了,因此可以进行fast path分片了 */   
  118.         err = 0;  
  119.         offset = 0;  
  120.         /* 取得frag_list列表  */   
  121.         frag = skb_shinfo(skb)->frag_list;  
  122.         skb_shinfo(skb)->frag_list = NULL;  
  123.         /* 得到数据(不包括头)的大小 */   
  124.         skb->data_len = first_len - skb_headlen(skb);  
  125.         skb->truesize -= truesizes;  
  126.         skb->len = first_len;  
  127.         iph->tot_len = htons(first_len);  
  128.         /* 设置mf位  */   
  129.         iph->frag_off = htons(IP_MF);  
  130.         ip_send_check(iph);  
  131.   
  132.         /* 开始进行发送 */   
  133.         for  (;;) {  
  134.             /* Prepare header of the next frame,  
  135.              * before previous one went down. */   
  136.             if  (frag) {  
  137.                 frag->ip_summed = CHECKSUM_NONE;  
  138.                 /* 设置相应的头部 */   
  139.                 skb_reset_transport_header(frag);  
  140.                 /* 预留ddos header 空间 */   
  141.                 // hlen += DDOS_HDR_LEN;   
  142.                 __skb_push(frag, hlen);  
  143.                 skb_reset_network_header(frag);  
  144.                 // hlen -= DDOS_HDR_LEN;   
  145.                   
  146.                 /* 复制ip头 */   
  147.                 memcpy(skb_network_header(frag), iph, hlen);  
  148.                 iph = ip_hdr(frag);  
  149.                 iph->tot_len = htons(frag->len);  
  150.                 /* 将当前skb的一些属性付给将要传递的分片好的帧 */   
  151.                 ip_copy_metadata(frag, skb);  
  152.                 /* 处理ip_option  */   
  153.                 if  (offset == 0)  
  154.                     ip_options_fragment(frag);  
  155.                 offset += skb->len - hlen;  
  156.                 /* 设置位移 */   
  157.                 iph->frag_off = htons(offset>>3);  
  158.                 if  (frag->next != NULL)  
  159.                     iph->frag_off |= htons(IP_MF);  
  160.                 /* Ready, complete checksum */   
  161.                 ip_send_check(iph);  
  162.             }  
  163.             /* 调用输出函数 */   
  164.             err = output(skb);  
  165.   
  166.             if  (!err)  
  167.                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);  
  168.             if  (err || !frag)  
  169.                 break ;  
  170.             /* 处理链表中下一个buf */   
  171.             skb = frag;  
  172.             frag = skb->next;  
  173.             skb->next = NULL;  
  174.         }  
  175.   
  176.         if  (err == 0) {  
  177.             IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);  
  178.             return  0;  
  179.         }  
  180.         /* 释放内存 */   
  181.         while  (frag) {  
  182.             skb = frag->next;  
  183.             kfree_skb(frag);  
  184.             frag = skb;  
  185.         }  
  186.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
  187.         return  err;  
  188.     }  
  189.   
  190. slow_path:  
  191.     /* 分片的数据剩余长度 */   
  192.     left = skb->len - hlen;      /* Space per frame */   
  193.     /* 而ptr就是分片开始的数据指针 */   
  194.     ptr = raw + hlen;       /* Where to start from */   
  195.       
  196.     /* for bridged IP traffic encapsulated inside f.e. a vlan header,  
  197.      * we need to make room for the encapsulating header  
  198.      */   
  199.     /* 处理桥接、VLAN、PPPOE相关MTU */   
  200.     pad = nf_bridge_pad(skb);  
  201.     ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, pad);  
  202.     mtu -= pad;  
  203.   
  204.     /*  
  205.      *  Fragment the datagram.  
  206.      */   
  207.     /* 取出ip offset域 */   
  208.     offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;  
  209.     /* not_last_frag表明这个帧是否是最后一个分片 */   
  210.     not_last_frag = iph->frag_off & htons(IP_MF);  
  211.   
  212.     /*  
  213.      *  Keep copying data until we run out.  
  214.      */   
  215.     /* 开始为循环处理,每一个分片创建一个skb buffer */   
  216.     while  (left > 0) {  
  217.         len = left;  
  218.         /* IF: it doesn't fit, use 'mtu' - the data space left */   
  219.         /* 如果len大于mtu,设置当前的将要分片的数据大小为mtu */   
  220.         if  (len > mtu)  
  221.             len = mtu;  
  222.         /* IF: we are not sending upto and including the packet end  
  223.            then align the next start on an eight byte boundary */   
  224.         /* 长度对齐 */   
  225.         if  (len < left)  {  
  226.             len &= ~7;  
  227.         }  
  228.   
  229.         /*  
  230.          *  Allocate buffer.  
  231.          */   
  232.         /* malloc一个新的buff,它的大小包括ip payload,ip head,以及L2 head */   
  233.         if  ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {  
  234.             NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!/n" );  
  235.             err = -ENOMEM;  
  236.             goto  fail;  
  237.         }  
  238.   
  239.         /*  
  240.          *  Set up data on packet  
  241.          */   
  242.         /* 调用ip_copy_metadata复制一些相同的值的域 */   
  243.         ip_copy_metadata(skb2, skb);  
  244.         /* 保留L2 header空间 */   
  245.         skb_reserve(skb2, ll_rs);  
  246.         /* 设置ip header & ddos header & ip paylod 空间 */   
  247.         skb_put(skb2, len + hlen);  
  248.         skb_reset_network_header(skb2);  
  249.         /* L4 header指针为ip header + ddos header数据偏移位置,用于复制原始payload */   
  250.         skb2->transport_header = skb2->network_header + hlen;  
  251.   
  252.         /*  
  253.          *  Charge the memory for the fragment to any owner  
  254.          *  it might possess  
  255.          */   
  256.         /* 将每一个分片的ip包都关联到源包的socket */   
  257.         if  (skb->sk)  
  258.             skb_set_owner_w(skb2, skb->sk);  
  259.           
  260.         /*  
  261.          *  Copy the packet header into the new buffer.  
  262.          */   
  263.   
  264.         /* 拷贝ip header */   
  265.         skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);  
  266.   
  267.         /*  
  268.          *  Copy a block of the IP datagram.  
  269.          */   
  270.         /* 拷贝ip payload数据 */   
  271.         if  (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))  
  272.             BUG();  
  273.               
  274.         /* 分片的数据剩余长度 */   
  275.         left -= len;  
  276.           
  277.         /*  
  278.          *  Fill in the new header fields.  
  279.          */   
  280.         /* 填充相应的ip头 */   
  281.         iph = ip_hdr(skb2);  
  282.         iph->frag_off = htons((offset >> 3));  
  283.   
  284.         /* ANK: dirty, but effective trick. Upgrade options only if  
  285.          * the segment to be fragmented was THE FIRST (otherwise,  
  286.          * options are already fixed) and make it ONCE  
  287.          * on the initial skb, so that all the following fragments  
  288.          * will inherit fixed options.  
  289.          */   
  290.         /* 第一个包,因此进行ip_option处理 */   
  291.         if  (offset == 0)  
  292.             ip_options_fragment(skb);  
  293.   
  294.         /*  
  295.          *  Added AC : If we are fragmenting a fragment that's not the  
  296.          *         last fragment then keep MF on each bit  
  297.          */   
  298.         /* 不是最后一个包,因此设置mf位 */   
  299.         if  (left > 0 || not_last_frag)  
  300.             iph->frag_off |= htons(IP_MF);  
  301.               
  302.         /* 移动数据指针以及更改数据偏移 */   
  303.         ptr += len;       
  304.         offset += len;  
  305.   
  306.         /*  
  307.          *  Put this fragment into the sending queue.  
  308.          */   
  309.         /* 增加ddos header 长度 */   
  310.         // hlen += DDOS_HDR_LEN;   
  311.           
  312.         /* 更新包头的数据长度 */   
  313.         iph->tot_len = htons(len + hlen);  
  314.         /* 重新计算校验 */   
  315.         ip_send_check(iph);  
  316.   
  317.         /* 复位ip header大小 */   
  318.         // hlen -= DDOS_HDR_LEN;   
  319.           
  320.         err = output(skb2);  
  321.         if  (err)  
  322.             goto  fail;  
  323.   
  324.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);  
  325.     }  
  326.     kfree_skb(skb);  
  327.     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);  
  328.     return  err;  
  329.   
  330. fail:  
  331.     kfree_skb(skb);  
  332.     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);  
  333.     return  err;  
  334. }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值