- /* Add new segment to existing queue. */
-
static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
-
{
-
struct sk_buff *prev, *next;
-
struct net_device *dev;
-
int flags, offset;
-
int ihl, end;
-
int err = -ENOENT;
/* 分片队列已经完成或者即将被清除,都会置上INET_FRAG_COMPLETE */
-
if (qp->q.last_in & INET_FRAG_COMPLETE)
-
goto err;
/*
1. IPCB(skb)->flags只有在本机发送IPv4分片时被置位,那么这里的检查应该是预防收到本机自己发出的IP分 片。
2. 关于ip_frag_too_far:该函数主要保证了来自同一个peer(相同的源地址)不会占用过多的IP分片队列。 后面会有该函数的分析。我想,这个主要是为了防止攻击。不过如果中间设备有NAT的话,这个默认限制太小了。
不过有NAT设备的情况下,IPv4的分片也会有其它的问题。以后也许会谈谈这个问题。
3. 前面两个条件为真时,调用ip_frag_reinit,重新初始化该队列。出错,那么只好kill掉这个队列了。
*/
-
if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
-
unlikely(ip_frag_too_far(qp)) &&
-
unlikely(err = ip_frag_reinit(qp))) {
-
ipq_kill(qp);
-
goto err;
-
}
/* 得到分片的数据偏移量及分片标志 */
-
offset = ntohs(ip_hdr(skb)->frag_off);
-
flags = offset & ~IP_OFFSET;
-
offset &= IP_OFFSET;
-
offset <<= 3; /* offset is in 8-byte chunks */
-
ihl = ip_hdrlen(skb);
-
-
/* Determine the position of this fragment. */
- /* 得到IP报文总的长度或者说末端 */
-
end = offset+skb->len - ihl;
-
err = -EINVAL;
-
-
/* Is this the final fragment? */
-
if ((flags & IP_MF) == 0) {
- /* 这是最后一个分片 */
-
/* If we already have some bits beyond end
-
* or have different end, the segment is corrrupted.
-
*/
- /*
- 1. 末尾端要小于之前得到的总的长度,那么肯定出错了;
- 2. 之前已经收到了一个最后分片,且这次判断的末端不等于之前获得的值,那么肯定出错了。
- */
-
if (end < qp->q.len ||
-
((qp->q.last_in & INET_FRAG_LAST_IN) && end != qp->q.len))
-
goto err;
/* 置INET_FRAG_LAST_IN标志,表示收到了最后一个分片 */
-
qp->q.last_in |= INET_FRAG_LAST_IN;
- /* IP包总长度就等于end */
-
qp->q.len = end;
-
} else {
-
if (end&7) {
- /* 说明数据长度不是8的倍数。按照协议规定,除最后一个分片外,其余的IP分片的长度必须为8 的倍数*/
/* 将数据长度缩短为8的倍数的长度 */
-
end &= ~7;
-
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-
skb->ip_summed = CHECKSUM_NONE;
-
}
-
if (end > qp->q.len) {
- /* 这次判断的末尾超过了之前的获得长度 */
-
/* Some bits beyond end -> corruption. */
- /*
- 如果之前已经获得了最后一个分片,那么总的IP长度一定为正确的值。
- 结果与这次的判断不符,那么一定出错了。
- */
-
if (qp->q.last_in & INET_FRAG_LAST_IN)
-
goto err;
/* 更新IP总长度 */
-
qp->q.len = end;
-
}
-
}
/* 表示空的IP数据,出错 */
-
if (end == offset)
-
goto err;
-
-
err = -ENOMEM;
-
if (pskb_pull(skb, ihl) == NULL)
-
goto err;
-
-
err = pskb_trim_rcsum(skb, end - offset);
-
if (err)
-
goto err;
-
-
/* Find out which fragments are in front and at the back of us
-
* in the chain of fragments so far. We must know where to put
-
* this fragment, right?
-
*/
-
prev = qp->q.fragments_tail;
-
if (!prev || FRAG_CB(prev)->offset < offset) {
- /* 直接插入在末尾的情况 */
-
next = NULL;
-
goto found;
-
}
-
prev = NULL;
/* 插入到分片队列的中间 */
-
for (next = qp->q.fragments; next != NULL; next = next->next) {
-
if (FRAG_CB(next)->offset >= offset)
-
break; /* bingo! */
-
prev = next;
-
}
-
-
found:
-
/* We found where to put this one. Check for overlap with
-
* preceding fragment, and, if needed, align things so that
-
* any overlaps are eliminated.
- */
/* 现在已经找到了正确的插入位置,但是可能与已有的IP分片重叠,下面需要处理重叠问题 */
-
if (prev) {
-
int i = (FRAG_CB(prev)->offset+prev->len) - offset;
-
-
if (i > 0) {
- /* 与前一个分片有重叠,那么新的偏移量为上一个分片的末尾 */
-
offset = i;
-
err = -EINVAL;
- /* end如果小于了新的偏移,出错了,drop掉这个分片 */
-
if (end <= offset)
-
goto err;
-
err = -ENOMEM;
-
if (!pskb_pull(skb, i))
-
goto err;
/* 使checksum无效 */
-
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-
skb->ip_summed = CHECKSUM_NONE;
-
}
-
}
-
-
err = -ENOMEM;
-
- while (next && FRAG_CB(next)->offset < end) {
/* 与已有的后面的IP分片重叠,这里使用while循环,是因为可能与多个重叠 */
-
int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */
- /* 如注释所说,i为重叠了的字节数 */
-
-
if (i < next->len) {
- /* 与后面的IP分片头部重叠。那么就更新后面的IP分片的偏移即可。这时无需继续测试,可以跳出循 环 */
-
/* Eat head of the next overlapped fragment
-
* and leave the loop. The next ones cannot overlap.
-
*/
-
if (!pskb_pull(next, i))
-
goto err;
-
FRAG_CB(next)->offset = i;
-
qp->q.meat -= i;
-
if (next->ip_summed != CHECKSUM_UNNECESSARY)
-
next->ip_summed = CHECKSUM_NONE;
-
break;
-
} else {
- /* 重叠部分覆盖已有的IP分片,那么可以将已有的IP分片释放, 然后继续测试下一个分片 */
-
struct sk_buff *free_it = next;
-
-
/* Old fragment is completely overridden with
-
* new one drop it.
-
*/
-
next = next->next;
-
-
if (prev)
-
prev->next = next;
-
else
-
qp->q.fragments = next;
-
-
qp->q.meat -= free_it->len;
-
frag_kfree_skb(qp->q.net, free_it);
-
}
-
}
/* 已经处理完了重叠问题,offset为新的偏移量 */
-
FRAG_CB(skb)->offset = offset;
/* 下面的操作很简单,插入新的分片,更新分片队列的信息 */
-
/* Insert this fragment in the chain of fragments. */
-
skb->next = next;
-
if (!next)
-
qp->q.fragments_tail = skb;
-
if (prev)
-
prev->next = skb;
-
else
-
qp->q.fragments = skb;
-
-
dev = skb->dev;
-
if (dev) {
-
qp->iif = dev->ifindex;
-
skb->dev = NULL;
-
}
-
qp->q.stamp = skb->tstamp;
-
qp->q.meat = skb->len;
-
atomic_add(skb->truesize, &qp->q.net->mem);
/* 偏移为0,说明是第一个分片 */
-
if (offset == 0)
-
qp->q.last_in |= INET_FRAG_FIRST_IN;
-
/* - 如果已经收到了第一个分片和最后一个分片,且收到的IP分片的长度也等于了原始的IP长度。
- 那么说明一切就绪,可以重组IP分片了。
- */
-
if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
-
qp->q.meat == qp->q.len)
-
return ip_frag_reasm(qp, prev, dev);
/* IP分片还未全部收齐 */
-
write_lock(&ip4_frags.lock);
-
list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
-
write_unlock(&ip4_frags.lock);
-
return -EINPROGRESS;
-
-
err:
-
kfree_skb(skb);
-
return err;
-
}