内核中的UDP socket流程(10)——ip_append_data

简介: 作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好继续ip_append_data,    /* hh_len是hardware header...
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好

继续ip_append_data,
  1.     /* hh_len是hardware header的长度,也就是2层帧头长度 */
  2.     hh_len = LL_RESERVED_SPACE(rt->dst.dev);
      
     /*fragheader是3层IP报文头长度(正常报文头+IP选项的长度)*/
  1.     fragheaderlen = sizeof(struct iphdr) (opt ? opt->optlen : 0);
  2.     /* maxfraglen为4层实际数据的最大长度(不含IP报文头),为MTU减去IP头长然后对齐 */
  3.     maxfraglen = ((mtu - fragheaderlen) & ~7) fragheaderlen;
     /* 这里对长度进行检查,因为最大的数据包长为65535,如果不满足条件,则出错返回 */
  1.     if (inet->cork.length+length > 0xFFFF - fragheaderlen) {
  2.         ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport,
  3.              mtu-exthdrlen);
  4.         return -EMSGSIZE;
  5.     }
现在还是不明白inet->cork的用法,以后一定得搞明白。
  1.     /*
  2.     第一个分片(或者是正常的普通IP数据包),长度小于MTU(即不会被分片),输出数据的网卡支持校验和,且没     有其它的报文头(exthdrlen),如IPSec头等,如果条件为真,则标识此报文由硬件做校验和。
  3.     */
  4.     /*
  5.      * transhdrlen > 0 means that this is the first fragment and we wish
  6.      * it won't be fragmented in the future.
  7.      */
  8.     if (transhdrlen &&
  9.      length + fragheaderlen = mtu &&
  10.      rt->dst.dev->features & NETIF_F_V4_CSUM &&
  11.      !exthdrlen)
  12.         csummode = CHECKSUM_PARTIAL
     /* 得到上一个数据buf */
  1.     skb = skb_peek_tail(&sk->sk_write_queue);
  1.     inet->cork.length += length;
  2.     /* 
  3.     如果数据长度超过MTU,且是UDP协议,网卡支持UDP分片,那么就进行UDP分片。
  4.     这里的skb_is_gso(skb)判断,不知道是为了什么,留待以后研究。
  5.     */
  6.     if (((length > mtu) || (skb && skb_is_gso(skb))) &&
  7.      (sk->sk_protocol == IPPROTO_UDP) &&
  8.      (rt->dst.dev->features & NETIF_F_UFO)) {
  9.         /* 进行UDP分片 */
  10.         err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
  11.                      fragheaderlen, transhdrlen, mtu,
  12.                      flags);
  13.         if (err)
  14.             goto error;
  15.         return 0;
  16.     }

  17.     /* So, what's going on in the loop below?
  18.      *
  19.      * We use calculated fragment length to generate chained skb,
  20.      * each of segments is IP fragment ready for sending to network after
  21.      * adding appropriate IP header.
  22.      */
  23.     /*
  24.     之前没有任何的数据buffer
  25.     */
  26.     if (!skb)
  27.         goto alloc_new_skb;

  28.     while (length > 0) {
  29.         /* Check if the remaining data fits into current packet. */
  30.         /* 得到剩余空间 */
  31.         copy = mtu - skb->len;
  32.         /* 当剩余空间无法容纳当前数据时 */
  33.         if (copy length)
  34.             copy = maxfraglen - skb->len;
  35.         if (copy = 0) {
  36.             /* 数据超过最大分片长度,需要分片 */
  37.             char *data;
  38.             unsigned int datalen;
  39.             unsigned int fraglen;
  40.             unsigned int fraggap;
  41.             unsigned int alloclen;
  42.             struct sk_buff *skb_prev;
  43. alloc_new_skb:
  44.             /* 检查上一个buffer中遗留的数据长度 */
  45.             skb_prev = skb;
  46.             if (skb_prev)
  47.                 fraggap = skb_prev->len - maxfraglen;
  48.             else
  49.                 fraggap = 0;

  50.             /*
  51.              * If remaining data exceeds the mtu,
  52.              * we know we need more fragment(s).
  53.              */
  54.             /* 要发送的数据长度等于本次数据长度加上上次遗留的分片数据长度 */
  55.             datalen = length + fraggap;
  56.             /* 如果超过了最大分片数据长度,那么要发送的数据长度就为最大分片数据长度-IP头长*/
  57.             if (datalen > mtu - fragheaderlen)
  58.                 datalen = maxfraglen - fragheaderlen;
  59.             /* 这里的datalen就是真正的3层IP数据, 那么fraglen就是2层数据帧的数据长度 */
  60.             fraglen = datalen + fragheaderlen;
     
             /* 
             计算要申请的内存长度:
             flags被置上MSG_MORE位,表示还有其它分片,且网卡不支持Scatter/gather IO,那么申请内存长              度就为MTU,如果条件为假,申请长度就是真实的2层数据帧的数据长度。
             
     Scatter/gather IO特性说明如下:表示IO设备可以对物理上不连续的内存进行操纵,就不用进行不必              要的拷贝使之成为连续的内存块了。            
             */
  1.             if ((flags & MSG_MORE) &&
  2.              !(rt->dst.dev->features&NETIF_F_SG))
  3.                 alloclen = mtu;
  4.             else
  5.                 alloclen = datalen + fragheaderlen;

  6.             /* The last fragment gets additional space at tail.
  7.              * Note, with MSG_MORE we overallocate on fragments,
  8.              * because we have no idea what fragment will be
  9.              * the last.
  10.              */
  11.             if (datalen == length + fraggap)
  12.                 alloclen += rt->dst.trailer_len;
             /* 申请sk_buff */
  1.             if (transhdrlen) {
  2.                 skb = sock_alloc_send_skb(sk,
  3.                         alloclen + hh_len + 15,
  4.                         (flags & MSG_DONTWAIT), &err);
  5.             } else {
  6.                 skb = NULL;
  7.                 if (atomic_read(&sk->sk_wmem_alloc) =
  8.                  2 * sk->sk_sndbuf)
  9.                     skb = sock_wmalloc(sk,
  10.                              alloclen + hh_len + 15, 1,
  11.                              sk->sk_allocation);
  12.                 if (unlikely(skb == NULL))
  13.                     err = -ENOBUFS;
  14.                 else
  15.                     /* only the initial fragment is
  16.                      time stamped */
  17.                     ipc->shtx.flags = 0;
  18.             }
  19.             if (skb == NULL)
  20.                 goto error;

  21.             /*
  22.              *    Fill in the control structures
  23.              */
  24.             skb->ip_summed = csummode;
  25.             skb->csum = 0;
  26.             skb_reserve(skb, hh_len);
  27.             *skb_tx(skb) = ipc->shtx;

  28.             /*
  29.              *    Find where to start putting bytes.
  30.              */
  31.             /* 得到可以填充数据的起始位置 */
  32.             data = skb_put(skb, fraglen);
  33.             /* 设置IP头的位置 */
  34.             skb_set_network_header(skb, exthdrlen);
  35.             /* 得到传输层报文头的位置 */
  36.             skb->transport_header = (skb->network_header +
  37.                          fragheaderlen);
  38.             data += fragheaderlen;

  39.             if (fraggap) {
  40.                 /* 将上一个分片的数据先复制到buffer中 */
  41.                 skb->csum = skb_copy_and_csum_bits(
  42.                     skb_prev, maxfraglen,
  43.                     data + transhdrlen, fraggap, 0);
  44.                 /* 调整上一个sk_buff的校验和 */
  45.                 skb_prev->csum = csum_sub(skb_prev->csum,
  46.                              skb->csum);
  47.                 data += fraggap;
  48.                 /* 这个函数不知道具体用于什么 */
  49.                 pskb_trim_unique(skb_prev, maxfraglen);
  50.             }
             /* 计算要拷贝的长度 */
  1.             copy = datalen - transhdrlen - fraggap;
  2.             /* 拷贝数据 */
  3.             if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) 0) {
  4.                 err = -EFAULT;
  5.                 kfree_skb(skb);
  6.                 goto error;
  7.             }

  8.             offset += copy;
  9.             length -= datalen - fraggap;
  10.             transhdrlen = 0;
  11.             exthdrlen = 0;
  12.             csummode = CHECKSUM_NONE;

  13.             /*
  14.              * Put the packet on the pending queue.
  15.              */
  16.             __skb_queue_tail(&sk->sk_write_queue, skb);
  17.             continue;
  18.         }
  19.         /* 未完待续 */
发现到了这个阶段,代码不太容易看懂了,因为有一些机制和概念仍然没有搞清楚,比如cork,还有buffer的组织。这估计要等到把UDP的这些代码看完,然后好好总结一下,才能明白




目录
相关文章
|
1月前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
52 2
|
1月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
108 1
|
6月前
|
网络协议 Java
Java的Socket编程:TCP/IP与UDP深入探索
Java的Socket编程:TCP/IP与UDP深入探索
104 0
|
2月前
|
C语言
C语言 网络编程(七)UDP通信创建流程
本文档详细介绍了使用 UDP 协议进行通信的过程,包括创建套接字、发送与接收消息等关键步骤。首先,通过 `socket()` 函数创建套接字,并设置相应的参数。接着,使用 `sendto()` 函数向指定地址发送数据。为了绑定地址,需要调用 `bind()` 函数。接收端则通过 `recvfrom()` 函数接收数据并获取发送方的地址信息。文档还提供了完整的代码示例,展示了如何实现 UDP 的发送端和服务端功能。
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【9月更文挑战第6天】
|
4月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
86 0
|
5月前
|
存储 网络协议 数据处理
【Socket】解决UDP丢包问题
UDP(用户数据报协议)是一种无连接的传输层协议,因其不保证数据包的顺序到达和不具备内置重传机制,导致在网络拥塞、接收缓冲区溢出或发送频率过快等情况下容易出现丢包现象。为应对这些问题,可以在应用层实现重传机制、使用前向纠错码等方法。这些方法在一定程度上可以缓解UDP通信中的丢包问题,提高数据传输的可靠性和效率。
|
6月前
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
462 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
5月前
|
网络协议 应用服务中间件 网络性能优化
解析TCP /UDP协议的 socket 调用的过程
【6月更文挑战第2天】该文介绍了传输层的两种主要协议TCP和UDP的区别。TCP是面向连接、可靠的,提供顺序无错的数据传输,而UDP则是无连接、不可靠的,不保证数据顺序或不丢失。
|
1月前
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程