//为分片确定正确的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结构 }