Chinaunix首页 | 论坛 | 博客
  • 博客访问: 67935
  • 博文数量: 24
  • 博客积分: 25
  • 博客等级: 民兵
  • 技术积分: 80
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-07 00:11
文章分类

全部博文(24)

文章存档

2014年(10)

2013年(7)

2012年(7)

我的朋友

分类: LINUX

2014-10-23 21:14:21

作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

今天学习上次剩下的最后一个UDP 发送数据调用中的最后一个函数,ip_fragment。这个函数用于当IP数据包过大时,对其进行分片发送。
  1. /*
  2.  *    This IP datagram is too large to be sent in one piece. Break it up into
  3.  *    smaller pieces (each of size equal to IP header plus
  4.  *    a block of the data of the original IP data part) that will yet fit in a
  5.  *    single device frame, and queue such a frame for sending.
  6.  */

  7. int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  8. {
  9.     struct iphdr *iph;
  10.     int ptr;
  11.     struct net_device *dev;
  12.     struct sk_buff *skb2;
  13.     unsigned int mtu, hlen, left, len, ll_rs;
  14.     int offset;
  15.     __be16 not_last_frag;
  16.     struct rtable *rt = skb_rtable(skb);
  17.     int err = 0;

  18.     dev = rt->dst.dev;

  19.     /*
  20.      *    Point into the IP datagram header.
  21.      */
  22.     /* 得到IP报文头的指针 */
  23.     iph = ip_hdr(skb);

  24.     if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
  25.         /* 禁止分片,增加错误计数 */
  26.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
  27.         icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
  28.              htonl(ip_skb_dst_mtu(skb)));
  29.         kfree_skb(skb);
  30.         return -EMSGSIZE;
  31.     }

  32.     /*
  33.      *    Setup starting values.
  34.      */
     /* 得到IP报文总长度 */
  1.     hlen = iph->ihl * 4;
  2.     /* 这里的mtu为真正的MTU-IP报文头,即允许的最大IP数据长度 */
  3.     mtu = dst_mtu(&rt->dst) - hlen;    /* Size of data space */
  4. #ifdef CONFIG_BRIDGE_NETFILTER
  5.     if (skb->nf_bridge)
  6.         mtu -= nf_bridge_mtu_reduction(skb);
  7. #endif
  8.     /* 为这个skb_buff置上分片完成的标志 */
  9.     IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;

  10.     /* When frag_list is given, use it. First, check its validity:
  11.      * some transformers could create wrong frag_list or break existing
  12.      * one, it is not prohibited. In this case fall back to copying.
  13.      *
  14.      * LATER: this step can be merged to real generation of fragments,
  15.      * we can switch to copy when see the first bad fragment.
  16.      */
  17.     /* 根据前面的学习,我们知道4层有可能会将数据包分片。这些分片存放在skb的frag_list中*/
  18.     if (skb_has_frags(skb)) {
  19.         /* skb_buffer已经有了一个frag list */
  20.         struct sk_buff *frag, *frag2;
  21.         /* 拿到数据包的长度 */
  22.         int first_len = skb_pagelen(skb);
         /*
         1.数据包的长度超过了MTU;
         2.数据包长度没有按8字节对齐;
         3.数据包设置了IP_MF或者IP_OFFSET位
         这样,进入slow_path
         */
  1.         if (first_len - hlen > mtu ||
  2.          ((first_len - hlen) & 7) ||
  3.          (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||
  4.          skb_cloned(skb))
  5.             goto slow_path; //跳到slow_path
         /* 遍历每一个分片 */
  1.         skb_walk_frags(skb, frag) {
  2.             /* 检查每个分片,如果有一个分片不符合要求,就只能使用slow path */
  3.             /* Correct geometry. */
  4.             if (frag->len > mtu ||
  5.              ((frag->len & 7) && frag->next) ||
  6.              skb_headroom(frag) < hlen)
  7.                 goto slow_path_clean;

  8.             /* Partially cloned skb? */
  9.             if (skb_shared(frag))
  10.                 goto slow_path_clean;

  11.             BUG_ON(frag->sk);
  12.             if (skb->sk) {
  13.                 frag->sk = skb->sk;
  14.                 frag->destructor = sock_wfree;
  15.             }
  16.             skb->truesize -= frag->truesize;
  17.         }

  18.         /* Everything is OK. Generate! */
  19. /* 现在可以进行fast path了*/
  20.         err = 0;
  21.         offset = 0;
  22.         /* 拿到frag list */
  23.         frag = skb_shinfo(skb)->frag_list;
  24.         /* 重置原来的frag list,相当于从skb_buff上取走了frag list */
  25.         skb_frag_list_init(skb);
  26.         /* 
  27.         得到实际的数据长度,置分片标志位和校验和
  28.         */
  29.         skb->data_len = first_len - skb_headlen(skb);
  30.         skb->len = first_len;
  31.         iph->tot_len = htons(first_len);
  32.         iph->frag_off = htons(IP_MF);
  33.         ip_send_check(iph);
         /* 分别处理每一个分片 */
  1.         for (;;) {
  2.             /* Prepare header of the next frame,
  3.              * before previous one went down. */
  4.             if (frag) {
  5.                 /* 表示checksm已经算好*/
  6.                 frag->ip_summed = CHECKSUM_NONE;
  7.                 /* 设置传输层*/
  8.                 skb_reset_transport_header(frag);
  9.                 __skb_push(frag, hlen);
  10.                 /* 设置网络层 */
  11.                 skb_reset_network_header(frag);
  12.                 memcpy(skb_network_header(frag), iph, hlen);
  13.                 iph = ip_hdr(frag);
  14.                 iph->tot_len = htons(frag->len);
  15.                 ip_copy_metadata(frag, skb);
  16.                 if (offset == 0)
  17.                     ip_options_fragment(frag);
  18.                 offset = skb->len - hlen;
  19.                 iph->frag_off = htons(offset>>3);
  20.                 if (frag->next != NULL)
  21.                     iph->frag_off |= htons(IP_MF);
  22.                 /* Ready, complete checksum */
  23. /* 计算分片的校验和 */
  24.                 ip_send_check(iph);
  25.             }
     /* 发送 */
  1.             err = output(skb);

  2.             if (!err)
  3.                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
  4.             if (err || !frag)
  5.                 break;

  6.             skb = frag;
  7.             frag = skb->next;
  8.             skb->next = NULL;
  9.         }

  10.         if (err == 0) {
  11.             IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
  12.             return 0;
  13.         }
         /* 出错是否内存 */
  1.         while (frag) {
  2.             skb = frag->next;
  3.             kfree_skb(frag);
  4.             frag = skb;
  5.         }
  6.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
  7.         return err;

  8. slow_path_clean:
  9.         /* 清除shared sk_buff */
  10.         skb_walk_frags(skb, frag2) {
  11.             if (frag2 == frag)
  12.                 break;
  13.             frag2->sk = NULL;
  14.             frag2->destructor = NULL;
  15.             skb->truesize = frag2->truesize;
  16.         }
  17.     }

  18. slow_path:
  19.     left = skb->len - hlen;        /* Space per frame */
  20.     ptr = hlen;        /* Where to start from */

  21.     /* for bridged IP traffic encapsulated inside f.e. a vlan header,
  22.      * we need to make room for the encapsulating header
  23.      */
  24.     ll_rs = LL_RESERVED_SPACE_EXTRA(rt->dst.dev, nf_bridge_pad(skb));

  25.     /*
  26.      *    Fragment the datagram.
  27.      */
     /* 得到偏移 */
  1.     offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
  2.    /* 通过IP_MF标志位,判断是否是最后一个分片 */
  3.     not_last_frag = iph->frag_off & htons(IP_MF);

  4.     /*
  5.      *    Keep copying data until we run out.
  6.      */

  7.     while (left > 0) {
  8.         /* 计算分片长度 */
  9.         len = left;
  10.         /* IF: it doesn't fit, use 'mtu' - the data space left */
  11.         if (len > mtu)
  12.             len = mtu;
  13.         /* IF: we are not sending upto and including the packet end
  14.          then align the next start on an eight byte boundary */
  15.         if (len < left)    {
  16.             len &= ~7;
  17.         }
  18.         /*
  19.          *    Allocate buffer.
  20.          */
          /* 为分片申请该分片申请一个sk_buff */
  1.         if ((skb2 = alloc_skb(len hlen ll_rs, GFP_ATOMIC)) == NULL) {
  2.             NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n");
  3.             err = -ENOMEM;
  4.             goto fail;
  5.         }

  6.         /*
  7.          *    Set up data on packet
  8.          */
         /* 复制数据,以及运输层 */
  1.         ip_copy_metadata(skb2, skb);
  2.         skb_reserve(skb2, ll_rs);
  3.         skb_put(skb2, len hlen);
  4.         skb_reset_network_header(skb2);
  5.         skb2->transport_header = skb2->network_header hlen;

  6.         /*
  7.          *    Charge the memory for the fragment to any owner
  8.          *    it might possess
  9.          */

  10.         if (skb->sk)
  11.             skb_set_owner_w(skb2, skb->sk);

  12.         /*
  13.          *    Copy the packet header into the new buffer.
  14.          */

  15.         skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);

  16.         /*
  17.          *    Copy a block of the IP datagram.
  18.          */
  19.         if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))
  20.             BUG();
  21.         left -= len;

  22.         /*
  23.          *    Fill in the new header fields.
  24.          */
  25.         /* 填充网络层 */
  26.         iph = ip_hdr(skb2);
  27.         iph->frag_off = htons((offset >> 3));

  28.         /* ANK: dirty, but effective trick. Upgrade options only if
  29.          * the segment to be fragmented was THE FIRST (otherwise,
  30.          * options are already fixed) and make it ONCE
  31.          * on the initial skb, so that all the following fragments
  32.          * will inherit fixed options.
  33.          */
  34.         /* 如果是第一个分片, 填充ip option */
  35.         if (offset == 0)
  36.             ip_options_fragment(skb);

  37.         /*
  38.          *    Added AC : If we are fragmenting a fragment that's not the
  39.          *         last fragment then keep MF on each bit
  40.          */
  41.         /* 设置IP_MF标志位 */
  42.         if (left > 0 || not_last_frag)
  43.             iph->frag_off |= htons(IP_MF);
  44.         ptr = len;
  45.         offset = len;

  46.         /*
  47.          *    Put this fragment into the sending queue.
  48.          */
  49.         iph->tot_len = htons(len hlen);
 /* 计算校验和 */
  1.         ip_send_check(iph);
 /* 发送该分片 */
  1.         err = output(skb2);
  2.         if (err)
  3.             goto fail;

  4.         IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
  5.     }
     /* 释放sk_buff */
  1.     kfree_skb(skb);
  2.     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
  3.     return err;

  4. fail:
  5.     kfree_skb(skb);
  6.     IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
  7.     return err;
  8. }
前段时间一直没有大块的时间,每天工作回家比较累,也就懒得看了。周末又陪老婆打球,休息了休息,也没看。今天总算给看完了。



阅读(918) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~