• 网络子系统54_ip协议分片重组_定位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);//递增ipq的引用计数,当skb被添加到frags链表之后,会通过ipq_put递减该计数
    			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分片到达时,根据4元组创建一个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 !
    "));
    	return NULL;
    }
    
    //调用路径:ip_frag_create->ip_frag_intern
    // 将ipq插入到hash表中
    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))//对一个不活跃的定时器修改到期时间
    		atomic_inc(&qp->refcnt);//增加引用计数,表示定时器对其的引用
    
    	atomic_inc(&qp->refcnt);//增加引用计数,表示hash表对其的引用
    	if((qp->next = ipq_hash[hash]) != NULL)
    		qp->next->pprev = &qp->next;
    	ipq_hash[hash] = qp;//将ipq插入到hash表中
    	qp->pprev = &ipq_hash[hash];
    	INIT_LIST_HEAD(&qp->lru_list);//将新加入的ipq加入到lru尾
    	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结构
    }


  • 相关阅读:
    注解-案例
    注解(Annotation)
    适合新手看的超详细CentOS Linux 7 安装Tomcat8过程
    CentOS Linux 7 提示 lsof: 未找到命令
    解决MySql报错:1130
    Spring Boot 创建自定义的properties文件
    spring boot 使用Schedule创建轻量级定时任务
    4.Java数组模块
    3.IDEA开发工具
    2.java基础语法
  • 原文地址:https://www.cnblogs.com/riskyer/p/3366036.html
Copyright © 2020-2023  润新知