网络子系统55_ip协议分片重组_加入ipq

//ip分片加入到正确的ipq结构
//调用路径:ip_defrag->ip_frag_queue

//	处理过程:
//		1.正在被释放的ipq,不处理新加入的分片(ipq正在被释放由last_in设置COMPLETE指出)
//		2.处理分片的合法性
//			2.1当该封包为最后一个分片时
//				2.1.1如果之前没有接收到最后一个分片,则该分片在总有效载荷中的结尾位置需要大于等于以推测出的最大长度
//				2.1.2如果之前已经接收到最后一个分片,则该分片在总有效载荷中的结尾位置需要等于之前接收到的最后一个分片给出的结尾位置
//			2.2结尾位置对齐到8字节边界,截去多余的字节,希望后续到达的分片补齐
//		3.更新该分片的skb->data移动到ip有效载荷,skb->tail到8字节
//		4.处理重叠
//		5.将分片插入到ipq的分片列表中
//		6.更新ipq的时间戳,移动ipq到rcu链表尾部

//	重叠的处理:
//		1.一个分片最多只会与一个前边的分片发生重叠,此时,截去该分片发生重叠的部分
//		2.一个分片可能会与多个后边的分片发生重叠,此时
//			2.1 如果后边的一个分片完全被重叠,则释放后边的这个分片
//			2.2 如果后边的这个分片只有部分被重叠,则从后边的这个分片中截去重叠的部分
//		3.使被截去缓存区的skb的校验和失效

//	插入ipq->fragments中:
//		sk_buff通过next域,插入到ipq->fragments中
1.1 static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
	struct sk_buff *prev, *next;
	int flags, offset;
	int ihl, end;
	//ipq正在被释放
	if (qp->last_in & COMPLETE)
		goto err;
	//此ip分片(ip有效载荷)在所有有效载荷中的偏移量
 	offset = ntohs(skb->nh.iph->frag_off);
	flags = offset & ~IP_OFFSET;//DF MF标志
	offset &= IP_OFFSET;////偏移量为13bit的字段,去掉高3bit的flag
	offset <<= 3;		//偏移量以8字节为单位
 	ihl = skb->nh.iph->ihl * 4;

 	end = offset + skb->len - ihl;//该ip分片的有效载荷相对于总有效载荷的结束位置

 	//接收到最后一个分片
	if ((flags & IP_MF) == 0) {
		//1.该分片指示的总有效载荷大小不足从已接收到的报文推断出的长度
		if (end < qp->len ||
		    ((qp->last_in & LAST_IN) && end != qp->len))//2.已经接收到最后一个分片,又接收到一个最后一个分片,但是长度不等
			goto err;
		qp->last_in |= LAST_IN;//LAST_IN表示已经接收到最后一个分片
		qp->len = end;//总有效载荷的长度
	} else {//非最后一个分片
		if (end&7) {//结尾位置没有对齐到8字节
			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)//长度为0分片的
		goto err;

	//pskb_pull 与 skb_pull的区别:
	//	1.skb_pull只简单移动 skb->data的指针
	//	2.pskb_pull考虑当skb->data到skb->tail之间的数据量如果不足够移动时,从frags或者frag_list中向前拷贝
	if (pskb_pull(skb, ihl) == NULL)//更新skb->data,使其指向ip有效载荷
		goto err;
	if (pskb_trim(skb, end-offset))//更新skb->tail指针,使skb->data与skb->tail之间的数据量为end-offset
		goto err;

	//在ipq->fragments中寻找该分片前边的分片
	//一个分片前边分片的满足条件:
	//	1.它的偏移量小于该分片的偏移量
	//	2.它后边分片的偏移量大于等于该分片的偏移量
	prev = NULL;
	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;//更新该分片的offset
			if (end <= offset)
				goto err;
			if (!pskb_pull(skb, i))//从该分片中删除重叠的部分
				goto err;
			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
				skb->ip_summed = CHECKSUM_NONE;//由于该分片长度被截取,因此校验和失效
		}
	}
	//接着之前的搜索,寻找该分片的后边的分片
	//该分片会与多个后边的分片重叠
	while (next && FRAG_CB(next)->offset < end) {
		int i = end - FRAG_CB(next)->offset;//与后边第一个分片重叠的字节数

		//重叠的字节数小于后边分片的长度,说明只与后边一个分片发生了重叠
		if (i < next->len) {
			//更新后边分片的data指针,截取重叠的部分
			if (!pskb_pull(next, i))
				goto err;
			//更新后边分片的偏移量
			FRAG_CB(next)->offset += i;
			//由于从后边分片截取了重叠的部分,从已接收到的数据量减去这部分字节
			qp->meat -= i;
			if (next->ip_summed != CHECKSUM_UNNECESSARY)
				next->ip_summed = CHECKSUM_NONE;//由于截取了后边的这个分片,使其校验和失效
			break;
		} else {//否则完全包含了后边的这个分片, 则直接释放掉被重叠的这个分片
			struct sk_buff *free_it = next;

			next = next->next;

			if (prev)
				prev->next = next;
			else
				qp->fragments = next;
			qp->meat -= free_it->len;
			frag_kfree_skb(free_it, NULL);
		}
	}
	//强制转换skb->cb为分片控制块
	//经过与前边,后边的分片比较,最终确定该分片的偏移量
	//设置该分片的偏移量
	FRAG_CB(skb)->offset = offset;
	//将分片添加到ipq的fragments链表中
	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->stamp;//更新ipq的时间戳为最新收到的这个skb的时间戳
	qp->meat += skb->len;
	//skb->truesize为skb中所有缓存区占用的总大小
	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);
}
时间: 2024-10-28 23:17:58

网络子系统55_ip协议分片重组_加入ipq的相关文章

网络子系统54_ip协议分片重组_定位ipq

// 为分片确定正确的ipq结构 // 通过5元组定位ipq,成功后,递增ipq引用计数,返回ipq // 定位5元组 // 1.<id, 源ip, 目的ip, l4协议> 可通过ip报文获取 // 2.user 通过ip_defrag给出,指出重组是由谁发起的,最常见的时IP_DEFRAG_LOCAL_DELIVER,当重组的入口分包要传递给本地时 // ipq中所有分片最迟完成重组的时间为30HZ 1.1 static inline struct ipq *ip_find(struct i

网络子系统56_ip协议分片重组_重组分片

//调用路径ip_defrag->ip_frag_reasm // 所有ip分片都被接收到时,重组ip数据包 // 判断ip所有分片都接收到的条件: // 1.FIRST_IN, LAST_IN // 2.meat = len,即 根据offset推断出的最大封包长度等于已接收到的封包长度 // 重组过程: // 1.将sk_buff链表从ipq->fragments取下 // 2.将第一个分片以后的分片挂在第一个分片的frag_list域 // 3.从分片子系统使用内存中减去该ip数据包的内

网络子系统52_ip协议报文重组

//分片子模块初始化函数 // 1.ipq hash 函数的随机因子 // 2.定时器,到期更新随机因子 // 3.ipq_hash 静态全局变量,静态分配64个buckets //调用路径:inet_init->ipfrag_init 1.1 void ipfrag_init(void) { //分片暂存的hash表 ipfrag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ (jiffies ^ (jiffies &

网络子系统51_ip协议报文分片

//ip分片 // 快速路径的条件: // 1.skb // 1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu // 2.skb的数据长度对齐到8字节的边界 // 3.skb没有被分片 // 4.skb没有被共享 // 2.skb->frag_list // 1.长度小于(mtu-ip报头-选项) // 2.除最后一个分片外,长度都需要对齐到8字节边界 // 3.head-data之间的空间,可以容纳ip报头 // 注:skb->frag_list的skb,没有填充ip头

网络子系统48_ip协议数据帧的发送

//ip协议与l4协议接口,l4通过此接口向下l3传递数据帧 //函数主要任务: // 1.通过路由子系统路由封包 // 2.填充l3报头 // 3.ip分片 // 4.计算校验和 // 5.衔接邻居子系统,向下层传送封包. 1.1 int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); struct ip_opt

网络子系统85_inet协议族-l3向上

// ip数据报向本地传递 // 调用路径:ip_rcv->dst_input->...->ip_local_deliver 1.1 int ip_local_deliver(struct sk_buff *skb) { //如果ip数据报被分片,则重组 if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } //通过hook点,向上传递 return NF_H

网络子系统42_ip协议数据帧的接收

//参考 深入理解linux网络技术内幕                                      // 注册l3协议 // ptype_all链表,链接所有ETH_P_ALL类型的l3协议 // ptype_base哈希表,非ETH_P_ALL类型的l3协议 // 注:l3协议可以使用相同的协议号 1.1 void dev_add_pack(struct packet_type *pt) { int hash; //ptype_all ptype_base共用一把锁 ptype

网络子系统36_BPDU协议格式

//参考 深入理解linux网络技术内幕 //通过以太网帧的目的地址,判断一个数据帧是否为bpdu数据帧 //1.BPDU封装格式:                                          //2.区分BPDU类型为CONFIG还是TCN,CONFIG类型的BPDU封包长38字节,TCN类型的BPDU封包长度为7字节 //由上图,越过以太网帧头6+6+2+3(llc头),之后为bpdu协议内容,偏移量为3的一个字节,指示封包为CONFIG或TCN            

网络子系统86_inet协议族-l4向下(一)

// l4数据向下l3传递 // 步骤: // 1.如果sock->sk_write_queue为空,初始化corking // 1.1 corking信息用于帮助ip层对数据进行分片 1.1 int ip_append_data(struct sock *sk, struct flowi4 *fl4, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from,