- /*
- 第一个分片(或者是正常的普通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;
-
}
- /* 未完待续 */