PREROUTING:ip_conntrack_defrag à ip_conntrack_in
1,ip_conntrack_defrag:
通常當(dāng)IP報文被送至L4層處理時,如果該報文是分片報文,那么報文就會先被保存起來,直到所有分片到達后重組成一個完整報文后,再被分發(fā)到L4層。當(dāng)沒有啟動conntrack時,netfilter各HOOK點對報文操作時,并不檢查該報文是否分片;但是如果啟動conntrack功能,則必須保證進入netfilter HOOK點的報文是一個完整的報文,因此ip_conntrack_defrag一般處于最前端的HOOK,負責(zé)將分片報文重組。
/* 若報文分片,則 調(diào)用ip_ct_gather_frags 組裝報文,如果所有分片均已到達,pskb指向新生成的報文,繼續(xù)沿著HOOK鏈進行下一步處理;否則為空,報文被緩存等待下一分片到來*/
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb,
hooknum == NF_IP_PRE_ROUTING ?
IP_DEFRAG_CONNTRACK_IN :
IP_DEFRAG_CONNTRACK_OUT);
if (!*pskb)
return NF_STOLEN;
}
return NF_ACCEPT;
ip_ct_gather_frags直接調(diào)用ip_defrag處理IP分片報文。對于分片報文的重組主要在ip_fragment.c中完成。
2,ip_conntrack_in:
當(dāng)報文到達ip_conntrack_in時,首先判斷系統(tǒng)連接跟蹤表中是否已經(jīng)存在該報文相應(yīng)的連接狀態(tài)ip_conntrack,若沒有,則建立相應(yīng)的數(shù)據(jù)結(jié)構(gòu),并初始化。獲取相應(yīng)的ip_conntrack后,用報文所攜帶的數(shù)據(jù),修改連接狀態(tài)。
/*1,檢查報文是否已經(jīng)經(jīng)過狀態(tài)檢測,nfct是指向相應(yīng)連接狀態(tài)數(shù)據(jù)結(jié)構(gòu)的指針*/
/* Previously seen (loopback or untracked)? Ignore. */
if ((*pskb)->nfct) {
CONNTRACK_STAT_INC(ignore);
return NF_ACCEPT;
}
/*2,檢查報文是否分片,從前面的分析可以看出,這種情況不可能發(fā)生;net_ratelimit用于保護內(nèi)核網(wǎng)絡(luò)調(diào)試信息的打印,當(dāng)它返回(TRUE)時則可以打印調(diào)試信息,返回零則禁止信息打印。*/
/* Never happen */
if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) {
if (net_ratelimit()) {
printk(KERN_ERR "ip_conntrack_in: Frag of proto %u (hook=%u)\n",
(*pskb)->nh.iph->protocol, hooknum);
}
return NF_DROP;
}
/*3,根據(jù)ip報文所攜帶數(shù)據(jù)的協(xié)議號,獲取相應(yīng)的協(xié)議封裝,該數(shù)據(jù)結(jié)構(gòu)封裝了對協(xié)議私有數(shù)據(jù)處理的函數(shù)和屬性;調(diào)用該協(xié)議相應(yīng)的error處理函數(shù),檢查報文是否正確*/
proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
/* It may be an special packet, error, unclean...
* inverse of the return code tells to the netfilter
* core what to do with the packet. */
if (proto->error != NULL && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0)
{
CONNTRACK_STAT_INC(error);
CONNTRACK_STAT_INC(invalid);
return -ret;
}
/*4,在全局連接表中,查找與該報文相應(yīng)的連接狀態(tài),返回的是ip_conntrack的指針,用于描述和記錄連接的狀態(tài);若該連接尚不存在,則創(chuàng)建相應(yīng)的結(jié)構(gòu),并進行初始化*/
if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
/* Not valid part of a connection */
CONNTRACK_STAT_INC(invalid);
return NF_ACCEPT;
}
…
/*5,調(diào)用相應(yīng)協(xié)議的packet處理函數(shù),判斷報文是否屬于有效連接,并更新連接狀態(tài);返回值若不為NF_ACCEPT,則報文不合法 */
ret = proto->packet(ct, *pskb, ctinfo);
if (ret < 0) {
/* Invalid: inverse of the return code tells
* the netfilter core what to do*/
nf_conntrack_put((*pskb)->nfct);
(*pskb)->nfct = NULL;
CONNTRACK_STAT_INC(invalid);
return -ret;
}
/*6,根據(jù)4的結(jié)果,設(shè)置應(yīng)答位;ip_conntrack_event_cache,用戶要求對連接跟蹤進行更詳細的控制(數(shù)據(jù)包是REPLY),使用??event_cache機制??*/
if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
ip_conntrack_event_cache(IPCT_STATUS, *pskb);
3, resolve_normal_ct:
resolve_normal_ct在全局連接表中,查找與該報文相應(yīng)的連接狀態(tài),返回的是ip_conntrack的指針,用于描述和記錄連接的狀態(tài);若該連接尚不存在,則創(chuàng)建相應(yīng)的結(jié)構(gòu),并進行初始化,設(shè)置連接狀態(tài)。
/*1,將數(shù)據(jù)包的內(nèi)容轉(zhuǎn)化成相應(yīng)的tuple,對于和協(xié)議相關(guān)的部分,如端口,則調(diào)用相關(guān)協(xié)議的處理函數(shù)pkt_to_tuple*/
if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
&tuple,proto))
return NULL;
/*2,在全局連接表中查找和tuple相同的hash項,全局連接表以tuple計算出相應(yīng)的hash值,每一個hash項所保存的元素也是相應(yīng)的tuple*/
h = ip_conntrack_find_get(&tuple, NULL);
/*3,若在全局連接表中無法查到tuple所對應(yīng)的hash項,即相應(yīng)的連接狀態(tài)不存在,系統(tǒng)調(diào)用init_conntrack創(chuàng)建并初始化ip_conntrack,并返回其相應(yīng)的tuple結(jié)構(gòu)指針*/
if (!h) {
h = init_conntrack(&tuple, proto, skb);
if (!h)
return NULL;
if (IS_ERR(h))
return (void *)h;
}
/*4,根據(jù)全局連接表所獲得tuple,獲取其對應(yīng)的ip_conntrack結(jié)構(gòu)*/
ct = tuplehash_to_ctrack(h);
/*5,判斷連接方向,若是reply方向,設(shè)置相應(yīng)的應(yīng)答標識和數(shù)據(jù)包狀態(tài)標識*/
if (DIRECTION(h) == IP_CT_DIR_REPLY) {
*ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
/* Please set reply bit if this packet OK */
*set_reply = 1;
} else {
/*6, 若是origin方向,根據(jù)ip_conntrack中的status,設(shè)置相應(yīng)的應(yīng)答標識和數(shù)據(jù)包狀態(tài)標識*/
/* Once we've had two way comms, always ESTABLISHED. */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: normal packet for %p\n",
ct);
*ctinfo = IP_CT_ESTABLISHED;
} else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
DEBUGP("ip_conntrack_in: related packet for %p\n",
ct);
*ctinfo = IP_CT_RELATED;
} else {
DEBUGP("ip_conntrack_in: new packet for %p\n",
ct);
*ctinfo = IP_CT_NEW;
}
*set_reply = 0;
}
/*7, 設(shè)置skb的對應(yīng)成員,數(shù)據(jù)包對應(yīng)的連接狀態(tài)結(jié)構(gòu)和數(shù)據(jù)包連接狀態(tài)標記*/
skb->nfct = &ct->ct_general;
skb->nfctinfo = *ctinfo;