我们都知道数据链路层有mtu的限制,如果我们上层发的包太大,那就要分片,那么对端就需要重组分片,组装好再通知上层。我们看一下分片重组的过程。我们看一下分片重组中用到的数据结构。ipq结构体是代表一个完整的传输层包,他被ip层分成了多个分片。ipfrag结构体是代表一个ip分片。他是传输层包的一个部分。
再看一下ip报文的格式。
我们开始分析组片之前,先看一下一些基础函数。 1 创建一个用于重组传输层数据包的结构体ipq。这个是第一个ip分配到达时调用的。他维护了属于同一个分片组(同一个传输层数据包)的多个分片。
// 创建一个队列用于重组分片,iphdr 即ip报文头
static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
{
struct ipq *qp;
int maclen;
int ihlen;
// 申请一个新ipq的表示分片队列
qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
// 初始化内存
memset(qp, 0, sizeof(struct ipq));
// skb->data指向mac头首地址,mac头长度等于ip头减去mac头首地址
maclen = ((unsigned long) iph) - ((unsigned long) skb->data);
// 分配内存保存mac头
qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
// ip头长度由ip头字段*4得出,多分配8个字节给icmp
ihlen = (iph->ihl * sizeof(unsigned long));
qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
// 把mac头内容复制到mac字段
memcpy(qp->mac, skb->data, maclen);
// 把ip头和传输层的8个字节复制到iph字段,8个字段的内容用于发送icmp报文时
memcpy(qp->iph, iph, ihlen + 8);
// 未分片的ip报文的总长度,未知,收到所有分片后重新赋值
qp->len = 0;
// 当前分片的ip头和mac头长度
qp->ihlen = ihlen;
qp->maclen = maclen;
qp->fragments = NULL;
// 关联的设备
qp->dev = dev;
// 开始计时,一定时间内还没收到所有分片则重组失败,发送icmp报文
qp->timer.expires = IP_FRAG_TIME;
qp->timer.data = (unsigned long) qp;
qp->timer.function = ip_expire;
// 开始计时,如果超时没有收到新的ip分片,则重组失败,如果收到一个新的,重置计时器
add_timer(&qp->timer);
qp->prev = NULL;
cli();
// 头插法插入分片重组的队列
qp->next = ipqueue;
/*
如果当前新增的节点不是第一个节点则把当前第一个
节点的prev指针指向新增的节点
*/
if (qp->next != NULL)
qp->next->prev = qp;
//更新ipqueue指向新增的节点,新增节点是首节点
ipqueue = qp;
sti();
return(qp);
}
根据一开始的结构体,上面的代码不难理解。主要是申请一个ipq结构体。然后初始化各个字段,最后插入ipq队列。并且开始计算分片重组的超时时间和超时回调。
2 通过ip头查找对应的ipq队列。
// 根据ip头找到分片队列的头指针
static struct ipq *ip_find