网络子系统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 iphdr *iph, u32 user)
{
	//定位4元组
	__u16 id = iph->id;
	__u32 saddr = iph->saddr;
	__u32 daddr = iph->daddr;
	__u8 protocol = iph->protocol;
	//对4元组进行hash
	unsigned int hash = ipqhashfn(id, saddr, daddr, protocol);
	struct ipq *qp;

	read_lock(&ipfrag_lock);
	//选择正确的bucket
	for(qp = ipq_hash[hash]; qp; qp = qp->next) {
		if(qp->id == id		&&
		   qp->saddr == saddr	&&
		   qp->daddr == daddr	&&
		   qp->protocol == protocol &&
		   qp->user == user) {
			atomic_inc(&qp->refcnt);
			read_unlock(&ipfrag_lock);
			return qp;
		}
	}
	read_unlock(&ipfrag_lock);
	//该4元组的第一个分片,创建新的ipq
	return ip_frag_create(hash, iph, user);
}

//调用路径:ip_find->ip_frag_create
//	新ip分片到达时,根据5元组创建一个ipq
1.2 static struct ipq *ip_frag_create(unsigned hash, struct iphdr *iph, u32 user)
{
	struct ipq *qp;

	if ((qp = frag_alloc_queue()) == NULL)//SLAB缓存
		goto out_nomem;
	//5元组
	qp->protocol = iph->protocol;
	qp->last_in = 0;
	qp->id = iph->id;
	qp->saddr = iph->saddr;
	qp->daddr = iph->daddr;
	//重组的发起者
	qp->user = user;
	//新ipq还没有任何分片与之关联
	qp->len = 0;
	qp->meat = 0;
	qp->fragments = NULL;
	qp->iif = 0;//入口设备

	init_timer(&qp->timer);//定时器,当一定时间范围内,重组没有完成,则释放与之关联的内存
	qp->timer.data = (unsigned long) qp;
	qp->timer.function = ip_expire;
	spin_lock_init(&qp->lock);
	atomic_set(&qp->refcnt, 1);

	return ip_frag_intern(hash, qp);//将ipq插入到hash表中

out_nomem:
	NETDEBUG(if (net_ratelimit()) printk(KERN_ERR "ip_frag_create: no memory left !\n"));
	return NULL;
}

//	将ipq插入到hash表中
//	调用路径:ip_frag_create->ip_frag_intern
//	函数主要任务:
//		1.修改定时器的到期时间,在一段时间内没有接收到新的分片,则释放所有接收到的分片。
//		2.将ipq插入到hash表
//		3.将ipq插入到lru链表
1.3 static struct ipq *ip_frag_intern(unsigned int hash, struct ipq *qp_in)
{
	struct ipq *qp;

	write_lock(&ipfrag_lock);

	qp = qp_in;
	//sysctl_ipfrag_time = 30HZ
	if (!mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time))//ipq所有封包延迟定时器
		atomic_inc(&qp->refcnt);//增加引用计数,表示定时器对其的引用

	//表示hash表对其的引用
	atomic_inc(&qp->refcnt);
	if((qp->next = ipq_hash[hash]) != NULL)
		qp->next->pprev = &qp->next;
	ipq_hash[hash] = qp;//将ipq插入到hash表中
	qp->pprev = &ipq_hash[hash];
	//将新加入的ipq加入到lru尾
	INIT_LIST_HEAD(&qp->lru_list);
	list_add_tail(&qp->lru_list, &ipq_lru_list);
	ip_frag_nqueues++;
	write_unlock(&ipfrag_lock);
	return qp;
}

//ipq中所有分片的到期时间
//接收到的ip分片不能永久的存在内存中,如果在一定时间范围内,没有为其完成重组,则需要释放所有分片占用的内存
//	1.删除定时器
//	2.从hash表中unlink
//	3.使用分片的入口设备向发送方发送icmp消息,告诉对方过期
//	4.释放ipq中的所有分片,释放ipq结构
1.4 static void ip_expire(unsigned long arg)
{
	struct ipq *qp = (struct ipq *) arg;

	spin_lock(&qp->lock);

	if (qp->last_in & COMPLETE)
		goto out;
	//删除定时器,从ipq hash表中unlink
	ipq_kill(qp);

	if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) {
		struct sk_buff *head = qp->fragments;
		if ((head->dev = dev_get_by_index(qp->iif)) != NULL) {
			icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);//发送ICMP消息
			dev_put(head->dev);
		}
	}
out:
	spin_unlock(&qp->lock);
	ipq_put(qp, NULL);//释放与ipq关联的所有分片,释放ipq结构
}
时间: 2024-08-24 07:19:45

网络子系统54_ip协议分片重组_定位ipq的相关文章

网络子系统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如果之前已经接收到最后一个分片,则该分片在总有效载荷中的结尾位置需要等于之前接

网络子系统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,