288
技術社區[雲棲]
網絡子係統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結構 }
最後更新:2017-04-03 14:53:41