分类: LINUX
2014-12-15 15:19:54
这个函数将收到的skb插到ipq结构的fragments碎片包链表中
static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
struct sk_buff *prev, *next;
int flags, offset;
int ihl, end;
//不考虑碎片重组已经完成的情况
if (qp->last_in & COMPLETE)
goto err;
//检查skb中的分片标志
if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
//检查碎片是否间隔过远,如果中间间隔超过64个报文无法完成重组,协议
//栈就不再支持
unlikely(ip_frag_too_far(qp)) && unlikely(ip_frag_reinit(qp))) {
ipq_kill(qp);
goto err;
}
//计算碎片包的偏移量
offset = ntohs(ip_hdr(skb)->frag_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
//偏移量是8字节的倍数,所以左移3
offset <<= 3;
//取得IP头的长度
ihl = ip_hdrlen(skb);
//本数据包插入后报文长度等于本数据包的偏移量加上数据包长度再减去IP头长度
end = offset + skb->len - ihl;
//如果是最后一个碎片包
if ((flags & IP_MF) == 0) {
//如果完成数据包插入以后整个数据包长度小于偏移数据总长
if (end < qp->len ||
((qp->last_in & LAST_IN) && end != qp->len))
goto err;
//设置ipq标志并得出数据偏移总长
qp->last_in |= LAST_IN;
qp->len = end;
} else {
//检查报文除去IP头之外的数据部分长度是否8字节对齐
if (end&7) {
end &= ~7;
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;
}
if (end > qp->len) {
if (qp->last_in & LAST_IN)
goto err;
qp->len = end;
}
}
if (end == offset)
goto err;
//将skb data指针挪到ip首部以后
if (pskb_pull(skb, ihl) == NULL)
goto err;
if (pskb_trim_rcsum(skb, end-offset))
goto err;
prev = NULL;
//遍历ipq中的fragments链表,每个skb的cb成员记录着当前skb进行ip重组的
//时候所需要的偏移,fragments中的skb都是按照offset升序排好的,所以,找到
//第一项offset大于当前IP包偏移的数据包就可以了
for (next = qp->fragments; next != NULL; next = next->next) {
if (FRAG_CB(next)->offset >= offset)
break;
prev = next;
}
//如果发生了重叠的情况,则以后数据包的偏移量都要减小,累加值也需要减小
if (prev) {
int i = (FRAG_CB(prev)->offset + prev->len) - offset;
if (i > 0) {
offset += i;
if (end <= offset)
goto err;
if (!pskb_pull(skb, i))
goto err;
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;
}
}
×××××××××××××××××××××××××××××××××××××
FRAG_CB(skb)->offset = offset;
//将当前skb插入ipq fragments链表,上面省去部分代码,实际上除了第一个报文
//以外,所有的碎片包都要使用skb_pull剥离掉ip头以后将剩余部分插入
skb->next = next;
if (prev)
prev->next = skb;
else
qp->fragments = skb;
if (skb->dev)
qp->iif = skb->dev->ifindex;
skb->dev = NULL;
qp->stamp = skb->tstamp;
qp->meat += skb->len;
atomic_add(skb->truesize, &ip_frag_mem);
if (offset == 0)
qp->last_in |= FIRST_IN;
write_lock(&ipfrag_lock);
list_move_tail(&qp->lru_list, &ipq_lru_list);
write_unlock(&ipfrag_lock);
return;
err:
kfree_skb(skb);
}