内核中的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的这些代码看完,然后好好总结一下,才能明白




目录
相关文章
|
2月前
|
域名解析 存储 移动开发
TCP socket && UDP && TCP协议 && IP协议 && 以太网等
TCP socket && UDP && TCP协议 && IP协议 && 以太网等
20 0
|
2月前
|
安全 网络协议 Java
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
17 0
|
2月前
|
存储 网络协议 Java
网络编程:UDP socket
网络编程:UDP socket
11 0
|
3月前
|
网络协议 安全
基于TCP和UDP的Socket通信
TCP是面向连接的,安全的协议,它是一对一的关系 udp是面向无连接的,不安全,不可靠的,但是效率很高,支持一对一,一对多,多对多发送,udp传输的格式为数据报,要将其封装为数据报才能发送,
25 1
|
4月前
|
网络协议 Java 数据安全/隐私保护
使用Socket实现UDP版的回显服务器
使用Socket实现UDP版的回显服务器
|
4月前
|
网络协议 Linux C语言
linux下CC++网络编程基本:socket实现tcp和udp的例子
linux下CC++网络编程基本:socket实现tcp和udp的例子
105 0
|
6月前
|
缓存 网络协议 算法
【Python基础篇021】黏包现象丨udp的socket服务
【Python基础篇021】黏包现象丨udp的socket服务
10900 0
|
7月前
|
网络协议 Oracle 安全
java TCP/UDP、Socket、URL网络编程详解
java TCP/UDP、Socket、URL网络编程详解
370 0
|
8月前
|
缓存 网络协议
基于UDP协议的Socket通信
TCP和UDP最大的区别在于是否需要客户端与服务端建立连接后才能进行数据传输,如果你学习前面的TCP,传输前先开服务端,accept,等客户端接入,然后获得客户端socket然后进行IO操作,而UDP则不用,UDP以数据报作为数据的传输载体,在进行传输时首先要把传输的数据定义成数据报(Datagram),在数据报中指明数据要到达的Socket(主机地址和端口号),然后再将数据以数据报的形式发送出去,服务端收不收到我就不知道了,除非服务端收到后又给我回一段确认的数据报。
|
12月前
|
网络协议 Python
socket库:Python实现UDP客户和服务器通信
socket库:Python实现UDP客户和服务器通信
172 1
socket库:Python实现UDP客户和服务器通信