作者: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. }
前段时间一直没有大块的时间,每天工作回家比较累,也就懒得看了。周末又陪老婆打球,休息了休息,也没看。今天总算给看完了。

tcp/ip源代码(17)——ip_fragment相关推荐

  1. TCP/IP协议端口大全

    TCP/IP协议端口大全 应用层网关服务     Internet 连接共享 (ICS)/Internet 连接防火墙 (ICF) 服务的这个子组件对允许网络协议通过防火墙并在 Internet 连接 ...

  2. tcp/ip 协议栈Linux源码分析四 IPv4分片 ip_fragment函数分析

    内核版本:3.4.39 很多项目涉及到IP分片的时候都是绕过去了,感觉分片挺难的.但是老这么做也不行啊,抽空分析了内核的分片处理函数ip_fragment,也不是特别复杂,感觉挺简单的,看来事情只有实 ...

  3. -1-7 java 网络编程基本知识点 计算机网络 TCP/IP协议栈 通信必备 tcp udp

    计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来, 在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统. 网络编程 ...

  4. linux下IPROTO_TCP,TCP/IP协议栈在Linux内核中的运行时序分析

    可选题目三:TCP/IP协议栈在Linux内核中的运行时序分析 在深入理解Linux内核任务调度(中断处理.softirg.tasklet.wq.内核线程等)机制的基础上,分析梳理send和recv过 ...

  5. TCP/IP详解--第十八章

     第18章 TCP连接的建立与终止 18.1    引言   TCP是一个面向连接的协议.无论哪一方向另一方发送数据之前,都必须先在双方之间 建立一条连接.本章将详细讨论一个TCP连接是如何建立的以及 ...

  6. TCP/IP详解--第八章

    第8章 Traceroute程序 8.1   引言   由Van Jacobson编写的 Traceroute程序是一个能更深入探索TCP/IP协议的方便可用的工具. 尽管不能保证从源端发往目的端的两 ...

  7. TCP/IP详解--第一章

    说明:专栏中的内容是<TCP/IP详解>这本书,博主分享在此. 第1章概     述 1.1   引言 很多不同的厂家生产各种型号的计算机,它们运行完全不同的操作系统,但  TCP/IP协 ...

  8. 《TCP/IP详解 卷1:协议》第4章 ARP:地址解析协议

    4.1 引言 本章我们要讨论的问题是只对TCP/IP协议簇有意义的IP地址.数据链路如以太网或令牌环网都有自己的寻址机制(常常为48 bit地址),这是使用数据链路的任何网络层都必须遵从的.一个网络如 ...

  9. C#使用TCP/IP与ModBus进行通讯

    C#使用TCP/IP与ModBus进行通讯 1. ModBus的 Client/Server模型 2. 数据包格式及MBAP header (MODBUS Application Protocol h ...

  10. 在Debian 4.0rc3上编译内核2.6.24时加入Layer7模块笔记[防火墙中在TCP/IP第七层Layer7应用层阻挡QQ,MSN等软件的应用]...

    作者:何祖彬[RobinHe] Mail:zubin.he@gmail.com 始于2008年8月3日 上午 版本号:KernelLayer7-V1.0-20080803,2008年8月3日首版 转载 ...

最新文章

  1. 因女朋友的一个建议,这位程序员创立仅 551 天公司就被 10 亿美元收购了
  2. 计算机系统结构教程卷子,计算机系统结构试卷试题.docx
  3. 【数据结构与算法】二项队列的Java实现
  4. Ruffer Investment共持有略高于3%的比特币敞口
  5. [转]VUE优秀UI组件库合集
  6. jq获取页面url后边带的参数
  7. openopc.opcerror: dispatch: 无效的类字符串_实战PyQt5: 064-MV框架中的Model类
  8. vos系统是什么?vos网络电话系统怎样搭建?
  9. ftp上传文件到服务器上,ftp上传文件到服务器上
  10. 服务器被攻击的常见手段以及解决方法
  11. EfficientNET_V1
  12. iOS 开发:『Runtime』详解(二)Method Swizzling
  13. 给自己一个拼搏的理由
  14. randon变换(拉东变换)
  15. 开通阿里云的对象存储服务OSS
  16. Python 文件 tell() 方法
  17. 调研分析:全球与中国多媒体投影仪镜头市场现状及未来发展趋势
  18. [ARM嵌入式系统开发]第一章之嵌入式系统的软硬件
  19. 求数列极差(贪心算法)
  20. linux 规避锐捷认证(版本三)

热门文章

  1. 如何用大数据开发套件周期调度机器学习算法
  2. F5 cookie值与IP地址(二):将IP地址转换成F5 cookie值
  3. VS2010下配置 OpenCV2.2
  4. preempt_disable宏
  5. php mysql占位符_php中的占位符
  6. Android7.1 Audio Debug相关方法
  7. Linux字符设备驱动实现
  8. Maven下载及安装配置
  9. opengl es3.0游戏开发学习笔记2--绘制地月星系
  10. redis缓存命中率计算