網絡子係統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