作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好
- /* hh_len是hardware header的长度,也就是2层帧头长度 */
- hh_len = LL_RESERVED_SPACE(rt->dst.dev);
/*fragheader是3层IP报文头长度(正常报文头+IP选项的长度)*/
- fragheaderlen = sizeof(struct iphdr) (opt ? opt->optlen : 0);
- /* maxfraglen为4层实际数据的最大长度(不含IP报文头),为MTU减去IP头长然后对齐 */
- maxfraglen = ((mtu - fragheaderlen) & ~7) fragheaderlen;
/* 这里对长度进行检查,因为最大的数据包长为65535,如果不满足条件,则出错返回 */
- if (inet->cork.length+length > 0xFFFF - fragheaderlen) {
- ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport,
- mtu-exthdrlen);
- return -EMSGSIZE;
- }
现在还是不明白inet->cork的用法,以后一定得搞明白。
- /*
- 第一个分片(或者是正常的普通IP数据包),长度小于MTU(即不会被分片),输出数据的网卡支持校验和,且没 有其它的报文头(exthdrlen),如IPSec头等,如果条件为真,则标识此报文由硬件做校验和。
- */
- /*
- * transhdrlen > 0 means that this is the first fragment and we wish
- * it won't be fragmented in the future.
- */
- if (transhdrlen &&
- length + fragheaderlen = mtu &&
- rt->dst.dev->features & NETIF_F_V4_CSUM &&
- !exthdrlen)
- csummode = CHECKSUM_PARTIAL;
/* 得到上一个数据buf */
- skb = skb_peek_tail(&sk->sk_write_queue);、
- inet->cork.length += length;
- /*
- 如果数据长度超过MTU,且是UDP协议,网卡支持UDP分片,那么就进行UDP分片。
- 这里的skb_is_gso(skb)判断,不知道是为了什么,留待以后研究。
- */
- if (((length > mtu) || (skb && skb_is_gso(skb))) &&
- (sk->sk_protocol == IPPROTO_UDP) &&
- (rt->dst.dev->features & NETIF_F_UFO)) {
- /* 进行UDP分片 */
- err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
- fragheaderlen, transhdrlen, mtu,
- flags);
- if (err)
- goto error;
- return 0;
- }
-
- /* So, what's going on in the loop below?
- *
- * We use calculated fragment length to generate chained skb,
- * each of segments is IP fragment ready for sending to network after
- * adding appropriate IP header.
- */
- /*
- 之前没有任何的数据buffer
- */
- if (!skb)
- goto alloc_new_skb;
-
- while (length > 0) {
- /* Check if the remaining data fits into current packet. */
- /* 得到剩余空间 */
- copy = mtu - skb->len;
- /* 当剩余空间无法容纳当前数据时 */
- if (copy length)
- copy = maxfraglen - skb->len;
- if (copy = 0) {
- /* 数据超过最大分片长度,需要分片 */
- char *data;
- unsigned int datalen;
- unsigned int fraglen;
- unsigned int fraggap;
- unsigned int alloclen;
- struct sk_buff *skb_prev;
- alloc_new_skb:
- /* 检查上一个buffer中遗留的数据长度 */
- skb_prev = skb;
- if (skb_prev)
- fraggap = skb_prev->len - maxfraglen;
- else
- fraggap = 0;
-
- /*
- * If remaining data exceeds the mtu,
- * we know we need more fragment(s).
- */
- /* 要发送的数据长度等于本次数据长度加上上次遗留的分片数据长度 */
- datalen = length + fraggap;
- /* 如果超过了最大分片数据长度,那么要发送的数据长度就为最大分片数据长度-IP头长*/
- if (datalen > mtu - fragheaderlen)
- datalen = maxfraglen - fragheaderlen;
- /* 这里的datalen就是真正的3层IP数据, 那么fraglen就是2层数据帧的数据长度 */
- fraglen = datalen + fragheaderlen;
/*
计算要申请的内存长度:
flags被置上MSG_MORE位,表示还有其它分片,且网卡不支持Scatter/gather IO,那么申请内存长 度就为MTU,如果条件为假,申请长度就是真实的2层数据帧的数据长度。
Scatter/gather IO特性说明如下:表示IO设备可以对物理上不连续的内存进行操纵,就不用进行不必 要的拷贝使之成为连续的内存块了。
*/
- if ((flags & MSG_MORE) &&
- !(rt->dst.dev->features&NETIF_F_SG))
- alloclen = mtu;
- else
- alloclen = datalen + fragheaderlen;
-
- /* The last fragment gets additional space at tail.
- * Note, with MSG_MORE we overallocate on fragments,
- * because we have no idea what fragment will be
- * the last.
- */
- if (datalen == length + fraggap)
- alloclen += rt->dst.trailer_len;
/* 申请sk_buff */
- if (transhdrlen) {
- skb = sock_alloc_send_skb(sk,
- alloclen + hh_len + 15,
- (flags & MSG_DONTWAIT), &err);
- } else {
- skb = NULL;
- if (atomic_read(&sk->sk_wmem_alloc) =
- 2 * sk->sk_sndbuf)
- skb = sock_wmalloc(sk,
- alloclen + hh_len + 15, 1,
- sk->sk_allocation);
- if (unlikely(skb == NULL))
- err = -ENOBUFS;
- else
- /* only the initial fragment is
- time stamped */
- ipc->shtx.flags = 0;
- }
- if (skb == NULL)
- goto error;
-
- /*
- * Fill in the control structures
- */
- skb->ip_summed = csummode;
- skb->csum = 0;
- skb_reserve(skb, hh_len);
- *skb_tx(skb) = ipc->shtx;
-
- /*
- * Find where to start putting bytes.
- */
- /* 得到可以填充数据的起始位置 */
- data = skb_put(skb, fraglen);
- /* 设置IP头的位置 */
- skb_set_network_header(skb, exthdrlen);
- /* 得到传输层报文头的位置 */
- skb->transport_header = (skb->network_header +
- fragheaderlen);
- data += fragheaderlen;
-
- if (fraggap) {
- /* 将上一个分片的数据先复制到buffer中 */
- skb->csum = skb_copy_and_csum_bits(
- skb_prev, maxfraglen,
- data + transhdrlen, fraggap, 0);
- /* 调整上一个sk_buff的校验和 */
- skb_prev->csum = csum_sub(skb_prev->csum,
- skb->csum);
- data += fraggap;
- /* 这个函数不知道具体用于什么 */
- pskb_trim_unique(skb_prev, maxfraglen);
- }
/* 计算要拷贝的长度 */
- copy = datalen - transhdrlen - fraggap;
- /* 拷贝数据 */
- if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) 0) {
- err = -EFAULT;
- kfree_skb(skb);
- goto error;
- }
-
- offset += copy;
- length -= datalen - fraggap;
- transhdrlen = 0;
- exthdrlen = 0;
- csummode = CHECKSUM_NONE;
-
- /*
- * Put the packet on the pending queue.
- */
- __skb_queue_tail(&sk->sk_write_queue, skb);
- continue;
- }
- /* 未完待续 */
发现到了这个阶段,代码不太容易看懂了,因为有一些机制和概念仍然没有搞清楚,比如cork,还有buffer的组织。这估计要等到把UDP的这些代码看完,然后好好总结一下,才能明白