本文檔的Copyleft歸yfydz所有,使用GPL發布,可以自由拷貝、轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。 msn: yfydz_no1@hotmail.com來源:http://yfydz.cublog.cn 1. iptables規則中的state匹配 在2.4/2.6核心的Linux中的防火牆代碼netfilter中實現了狀態檢測(stateful inspection)檢測技術,在命令列介面的iptables命令是通過匹配“-m state”來實現,“-m state”匹配中定義了四種狀態:NEW,表示新串連;ESTABLISHED,表示已經建立的串連;RELATED,表示相關的子串連;INVALID,表示非法狀態。所以在制定iptables規則時,只需要定義單向的規則,再加上如下兩條規則,就可以保證串連回應包能正確通過防火牆而不必加反向規則:iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -I FORWARD -m state --state INVALID -j DROP (如果是保護本機,將FORWARD改為INPUT) 2. 核心中的state匹配的實現 (以下的核心代碼基於2.4.26核心) 和匹配state對應的核心部分實現是ipt_state.c(net/ipv4/netfilter),匹配部分的代碼非常簡單,就是根據函數ip_conntrack_get()的傳回值來判斷資料包的狀態類型: if (!ip_conntrack_get((struct sk_buff *)skb, &ctinfo)) statebit = IPT_STATE_INVALID; else statebit = IPT_STATE_BIT(ctinfo);3. skb與ip_conntrack的關聯 再跟蹤到ip_conntrack_get()函數(net/ipv4/netfilter_ipv4 /ip_conntrack_core.c),該函數有兩個參數,一個是指向sk_buff資料包的指標skb,另一個是枚舉類型enum ip_conntrack_info的指標,實際上是一個返回參數,將返回資料包的狀態類型;該函數的傳回值是串連結構struct ip_conntrack的指標,表示該包屬於哪個串連,如果串連不存在,返回空,該包也將被認為是非法包: struct ip_conntrack * ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo) { if (skb->nfct) return __ip_conntrack_get(skb->nfct, ctinfo); return NULL; } ip_conntrack_get()函數本身也很簡單,實際上是__ip_conntrack_get()函數(net/ipv4 /netfilter_ipv4/ip_conntrack_core.c)的包裹函數,判斷skb的nfct是否非空,非空則將其和枚舉指標 ctinfo傳給__ip_conntrack_get()函數,nfct是sk_buff中用於描述netfilter conntrack資訊的struct nf_ct_info指標,在核心配置了CONFIG_NETFILTER後有效。struct nf_ct_info結構在include/linux/sk_buff.h中定義:struct nf_conntrack { atomic_t use; void (*destroy)(struct nf_conntrack *); }; struct nf_ct_info { struct nf_conntrack *master; };在__ip_conntrack_get()函數中,通過nfct->master擷取串連指標ct,而nfct相對於ct成員infos的位移就是串連狀態: static inline struct ip_conntrack * __ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo) { struct ip_conntrack *ct = (struct ip_conntrack *)nfct->master; /* ctinfo is the index of the nfct inside the conntrack */ *ctinfo = nfct - ct->infos; IP_NF_ASSERT(*ctinfo >= 0 && *ctinfo < IP_CT_NUMBER); return ct; } 由此可見,netfilter的狀態檢測過程很簡潔,如果skb包中的nfct指標設定了的話,可以很快地確定該skb包屬於哪個串連,如果不屬於任何串連則是非法包。現在的焦點是skb中的nfct值是如何設定的?4. netfilter如何?中skb與ip_conntrack串連的關聯 nfct值是在resolve_normal_ct()函數(net/ipv4/netfilter/ip_conntrack_core.c)中定義的,該函數被ip_conntrack_in()函數(net/ipv4/netfilter/ip_conntrack_core.c)調用,在 ip_conntrack_local()函數(net/ipv4/netfilter/ip_conntrack_standalone.c)中調用了ip_conntrack_in()函數,而這兩個函數分別是PREROUTING鏈和OUTPUT鏈的起始函數,由下面兩個結構定義(net/ipv4/netfilter/ip_conntrack_standalone.c): static struct nf_hook_ops ip_conntrack_in_ops = { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_CONNTRACK }; static struct nf_hook_ops ip_conntrack_local_out_ops = { { NULL, NULL }, ip_conntrack_local, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_CONNTRACK };這說明netfilter是在串連的第一個包時就建立串連了,這個串連可能是由外部發起的,也可能是由本機發起的,後續包尋找串連HASH表找到相應的串連,然後賦值給skb結構中的nfct,這就是resolve_normal_ct()函數的工作: /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ static inline struct ip_conntrack * resolve_normal_ct(struct sk_buff *skb, // 資料包 struct ip_conntrack_protocol *proto, // 連線協定跟蹤,該結構對應協議特殊處理過程,如TCP的狀態轉換等 int *set_reply, // 傳回值,表示該包是否是回應程式的包 unsigned int hooknum, enum ip_conntrack_info *ctinfo) { struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple_hash *h; IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);// 確定該包對應的tuple,對TCP/UDP協議來說就是地址協議連接埠的5元組 if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto)) return NULL; /* look for tuple match */ // 根據tuple值尋找串連HASH h = ip_conntrack_find_get(&tuple, NULL); if (!h) { // 在當前串連表中沒找到,說明是新串連的包,初始化新串連 h = init_conntrack(&tuple, proto, skb); if (!h) return NULL; // 該包非法返回NULL,其他錯誤會返回錯誤號碼的負值 if (IS_ERR(h)) return (void *)h; } /* It exists; we have (non-exclusive) reference. */ 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 { // 串連原始方向的包 /* Once we've had two way comms, always ESTABLISHED. */ if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) { DEBUGP("ip_conntrack_in: normal packet for %p/n", h->ctrack); // 如果不是第一個包,設定ESTABLISHED屬性,這個位標誌表示發現了回應程式發的包, // 表示串連可以建立,該標誌在ip_conntrack_in()函數中設定 *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) { DEBUGP("ip_conntrack_in: related packet for %p/n", h->ctrack); // 該標誌表示是子串連的包,如FTP的資料連線 // 該標誌在init_conntrack()函數中設定 *ctinfo = IP_CT_RELATED; } else { DEBUGP("ip_conntrack_in: new packet for %p/n", h->ctrack); // 沒有任何標誌,是串連的第一個包,設定為NEW屬性 *ctinfo = IP_CT_NEW; } // 這個包是發起方的包 *set_reply = 0; } // infos是數量為IP_CT_NUMBER(=5)的數組,分別對應串連發起方向的 // NEW(2)/ESTABLISHED(0)/RELATED(1) // 和串連回應程式向的ESTABLISHED(3)/RELATED(4) // 每個skb包都會劃歸為其中一類, 也就是ctinfo的值 // 每個skb包根據其類型,將其nfct值設為相應infos項的地址 skb->nfct = &h->ctrack->infos[*ctinfo]; return h->ctrack; } 在init_conntrack()函數中將串連的infos值初始化為: ... for (i=0; i < IP_CT_NUMBER; i++) conntrack->infos[i].master = &conntrack->ct_general; ...也就是infos各項的值都是相同的,沒有發現在其他地方重新賦值,所以把所有項實際都是相同的,指向同一個串連。設計之初可能是考慮要區分, 但實現時還是只用到一個就行了,在其他地方基本只用到infos[0]進行處理,infos的用處目前只用來判斷狀態,也就是每一項相對於 infos[0]的位移值。 最後討論一下為什麼在__ip_conntrack_get()函數中nfct->master值就是串連的地址: __ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo) { struct ip_conntrack *ct = (struct ip_conntrack *)nfct->master; ...skb的nfct值是infos數組中某一項的地址,而這些項中的master成員都初始化為串連中ct_general的地址,而ct_general參數是串連結構的第一項參數,所以其地址和串連結構的地址是相同,在Linux核心中,類似用法很多。 5. 結論 在每個skb資料包進入netfilter架構時,netfilter就會給其建立串連,通過簡單方法就能找到串連,能區分出發起方、回應程式以及第一個包還是後續包,並能識別子串連,從而實現狀態檢測。 串連結構中的infos數組就目前而言似乎不是很必要,單值就可以,然後增加一個串連狀態參數,而不是通過infos的位移來判斷。 |
|
|
網友: 本站網友 |
時間:2007-10-24 18:01:01 IP地址:221.221.155.★ |
|
|
|
|
|
網友: 本站網友 |
時間:2008-08-29 15:37:07 IP地址:218.247.216.★ |
|
|
|
在2.6.15核心已經改變很大了,比如:
static inline struct ip_conntrack * ip_conntrack_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
{
*ctinfo = skb->nfctinfo;
return (struct ip_conntrack *)skb->nfct;
}
也就是已經沒有__ip_conntrack_get,更簡單了,同時在struct sk_buff中,有變數:
__u8 local_df:1
cloned:1
ip_summed:2
nfctinfo:3
#ifdef CONFIG_NETFILTER
__u32 nfmark;
struct nf_conntrack *nfct; |
|
|
|
網友: 本站網友 |
時間:2008-08-29 15:58:18 IP地址:218.247.216.★ |
|
|
|
新的 resolve_normal_ct,(linux2.6.15)
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
static inline struct ip_conntrack *
resolve_normal_ct(struct sk_buff *skb,
struct ip_conntrack_protocol *proto,
int *set_reply,
unsigned int hooknum,
enum ip_conntrack_info *ctinfo)
{
struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple_hash *h;
struct ip_conntrack *ct;
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
&tuple,proto))
return NULL;
/* look for tuple match */
h = ip_conntrack_find_get(&tuple, NULL);
if (!h) {
h = init_conntrack(&tuple, proto, skb);
if (!h)
return NULL;
if (IS_ERR(h))
return (void *)h;
}
ct = tuplehash_to_ctrack(h);
/* It exists; we have (non-exclusive) reference. */
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 {
/* 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;
}
skb->nfct = &ct->ct_general;
skb->nfctinfo = *ctinfo;
return ct;
}
|
|
|