struct xt_af xt[]結構數組
該數組用於掛載各個協議的match和target資源。由於寫者(添加、刪除)和讀者(查找)都是在內核空間進程上下文執行,所以它們只需要用xt[n].mutex信號量進行互斥。讀者(查找)在將規則關聯上一個match或target時會增加它們所在模塊的引用計數,在它釋放這個引用計數之前該模塊是不會被卸載的,所以另外一個讀者(規則匹配)在軟中斷中可以放心的使用,不需加任何鎖。
-
xt_register_match(struct xt_match *match)與xt_unregister_match(struct xt_match *match)
用於在xt[]數組上掛載對應協議的match,由於它們都是在內核空間的進程上下文被使用,所以它們使用mutex_lock(&xt[af].mutex)信號量進行加鎖和解鎖。(寫者) -
xt_register_target(struct xt_target *target)與xt_unregister_target(struct xt_target *target)
用於在xt[]數組上掛載對應協議的target,由於它們都是在內核空間的進程上下文被使用,所以它們使用mutex_lock(&xt[af].mutex)信號量進行加鎖和解鎖。(寫者) -
struct xt_match *xt_find_match(u8 af, const char *name, u8 revision)與struct xt_target *xt_find_target(u8 af, const char *name, u8 revision)
用於在xt[]數組中查找對應協議的match或target與對應規則相關聯,並增加match和target所在模塊的引用計數。由於它們都是在內核空間的進程上下文被使用,所以它們使用mutex_lock(&xt[af].mutex)信號量進行加鎖和解鎖。同時內核在軟中斷中進行規則匹時配,它引用規則關聯的match和target是安全的,因爲match和target所在模塊由於引用計數是不會被釋放的。(讀者) -
由於有一個讀者是在軟中斷的中,並且有多個CPU同時使用,是否需要其它保護。答:不需要。因爲如果軟中斷中引用的規則使用了某個match或target,則擁有該match和target模塊的引用計數會被加1,該模塊將不會被卸載(這也就要求在調用xt_unregister_match()或xt_unregister_target()時必須先判斷它們所在模塊的引用計數,通常它們被放在模塊註銷函數中)。如果引用計數爲0,則說明沒有規則引用該match或target,則在軟中斷中也不會使用它。
net.xt.tables[]網絡命名空間協議鏈表
該命名空間協議鏈表用於將不同協議的table表掛到對應協議鏈表中。
寫者(添加、刪除)table表都在內核空間進程上下文執行,又由於它需要檢查該表與註冊的target、match名字不衝突,所以他們只需要用xt[n].mutex信號。
讀者在軟中斷中通過HOOK引用這些表,所以在寫者(添加、刪除)之前一定要保證沒有讀者在操作。添加表操作一定要先通過xt_register_table()添加一個表,然後再通過xt_hook_link()使HOOK能夠引用這些表;刪除表操作一定要先通過xt_hook_unlink()去掉HOOK對錶的引用,然後再通過xt_unregister_table()刪除一個表。
-
struct xt_table *xt_register_table(struct net *net, const struct xt_table *input_table, struct xt_table_info *bootstrap, struct xt_table_info *newinfo)
主要是複製input_table到table表,並將newinfo(由調用該函數模塊提供的私有數據xt_table_info)與該表的table->private指針相關聯,然後根據該表指定的協議掛入對應的net.xt.table[table->af]鏈表中。它使用xt[n].mutex信號進行加鎖(如上所述)。 -
void *xt_unregister_table(struct xt_table *table)
主要是將table從net.xt.table[table->af]鏈表中取下來,並返回table->private指針指向的xt_table_info數據。它使用xt[n].mutex信號進行加鎖(如上所述)。 -
struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)與void xt_hook_unlink(const struct xt_table *table, struct nf_hook_ops *ops)
主要是利用xt_table結構和鉤子函數構造出nf_hook_ops鉤子項,然後調用nf_register_hooks()或nf_unregisgter_hooks()函數來註冊或註銷ipv4協議對應點的鉤子函數,這兩個函數主要用在內核空間的進程上下文。由於nf_regisgter_hooks()已提供了保護,所以它們不需要任何形式的鎖保護。
Iptables利用Xtable初始化filter表的結構圖
-
struct xt_table *ipt_register_table(struct net *net, const struct xt_table *table, const struct ipt_replace *repl)(註冊並初始化一個表,然後調用xt_hook_link()引用該表)
該函數是iptables爲filter、nat、mangle模塊提供用於註冊相應表結構的接口。它根據當前表要被掛入的HOOK點來構建上圖所示的xt_table_info初始規則表,並調用xt_register_table()函數將filter表的xt_table和xt_table_info結構掛入net.xt.table[IPV4]鏈表中。(上圖是iptables_filter模塊調用該函數註冊的結構圖)
註冊完一個表後,就可以通過xt_hook_link()函數註冊一個HOOK點來使用這個表中的規則處理數據包。 -
void ipt_unregister_table(struct net *net, struct xt_table *table)(註銷一個表,要在xt_hook_unlink()之後使用)
該函數是iptables爲filter、nat、mangle模塊提供用於註銷相應表結構的接口。它調用xt_unregister_table()將xt_table從對應協議鏈表中取下並釋放,然後將返回的xt_table_info結構中的規則逐一釋放(同時也會釋放規則引用的match和target模塊的引用計數),最後釋放xt_table_info結構。
爲保證釋放table表時沒有其它讀者,所以在調用該函數之前要先調用xt_hook_unlink()函數註銷在HOOK點掛入的處理函數,保證沒有其它CPU會再引用到該表。 -
struct xt_info_lock xt_info_locks[CPU](用於保證讀取修改表中規則的鎖,每個CPU一個鎖)
struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, const char *name)
查找name指定的表,使用xt[af].mutex加鎖,保證只有一個寫者處理該表。並增加表所在模塊的引用計數,防止該表被錯誤釋放。
void xt_table_unlock(struct xt_table *table)
與xt_find_table_lock()配對使用,釋放xt[table->af].mutex鎖。
static inline void xt_info_rdlock_bh(void) 或 static inline void xt_info_rdunlock_bh(void)
獲取或釋放本CPU的xt_info_locks[cpu]鎖。這個鎖主要是用於防止正被使用的規則表(xt_table_info結構)被釋放。(它與get_counters()進行互斥)
static inline void xt_info_wrlock(unsigned int cpu) 或 static inline void xt_info_wrunlock(unsigned int cpu)
獲取指定CPU的xt_info_locks[cpu]鎖。它主要在get_counters()中被調用,用於獲取所有CPU的寫鎖,保證所有CPU都已完成了對規則表的引用。
static void get_counters(const struct xt_table_info *t, struct xt_counters counters[])
它可以保證其它CPU都已完成了一次對錶中所有規則的引用。因爲它要對所有其它CPU調用xt_info_wrlock(cpu)函數來獲取其它CPU的xt_info_lock,而其它CPU在讀取表中規則時,要通過xt_info_rdlock_bh獲取各自的xt_info_lock鎖,所有當它獲取完所有其它CPU的xt_info_lock鎖後,就表示其它CPU都已完成了對錶中規則的引用。這就說明了爲什麼在do_replace中調用完get_counters()後能夠安全的釋放舊的xt_table_info結構。 -
static int get_info(struct net *net, void __user *user, const int *len, int compat) (讀取表中信息)
該函數是用戶使用iptables命令操作表中規則時,用於獲取表中信息的接口。它使用xt_find_table_lock()和xt_table_unlock()保證沒有其它人操作該表。 -
static int get_entries(struct net *net, struct ipt_get_entries __user *uptr, const int *len)(讀取表中規則)
該函數是用戶使用iptables命令操作表中規則時,用於獲取表中規則的接口。它使用xt_find_table_lock()和xt_table_unlock()保證沒有其它人操作該表。 -
unsigned int ipt_do_table(struct sk_buff *skb, unsigned int hook, const struct net_device *in, const struct net_device *out, struct xt_table *table) (讀取表中規則)
該函數是iptables爲filter、nat、mangle模塊提供用於對數據包匹配各表中規則的接口。它根據表對應的xt_table_info結構中的信息,找到相應的規則,對數據包進行逐一匹配。爲保證所引用表中的規則(xt_table_info)不被其它寫者釋放,同時又不影響到其它讀者,使用xt_info_rdlock_bh()和xt_info_rdunlock_bh()來加鎖和解鎖。 -
static int do_replace(struct net *net, const void __user *user, unsigned int len) (修改表中規則)
該函數是iptables爲filter、nat、mangle模塊提供用於在對應表中下規則的接口。它根據用戶傳遞過來的規則,構建一個新的xt_table_info結構和規則,並將它們與對應表的xt_table->private相關聯。它通過xt_find_table_lock()和xt_table_unlock()保證當前只有一個寫者在操作該表。通過local_bh_disable()和local_bh_enable()保證更換table->private指向新的xt_table_info結構時不被打斷。通過get_counters()保證所有其它CPU都不再使用舊的xt_table_info結構,安全釋放舊的xt_table_info結構。
static int translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, const struct ipt_replace *repl)
根據ipt_replace結構構建一個xt_table_info結構,並做一些必要的檢查(鏈是否環路等),同時將表中的規則與相應的match和target相關聯。
struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters, struct xt_table_info *newinfo, int *error)
爲newinfo調用xt_jumpstack_alloc(struct xt_table_info *i)初始化stack相關數據,然後使table->private指向newinfo,並返回oldinfo。
Iptable下發規則的執行流程圖
iptables內核層與應用層之間傳輸規則的組織形式
iptables將內核中獲取的規則轉換爲如下圖所示可管理的結構
iptables內核層與應用層之間傳輸規則的組織形式
iptables將內核中獲取的規則轉換爲如下圖所示可管理的結構
Iptables包含以下target
Netfilter處理的返回值
NF_DROP
NF_ACCEPT
NF_STOLEN
NF_QUEUE
NF_REPEAT
NF_STOP
- IPT_ACCEPT:(struct ipt_standard_target *)target->verdict = -NF_ACCEPT-1 < 0 (ipt_do_table()碰到這個target直接返回NF_ACCEPT)
- IPT_DROP:(struct ipt_standard_target *)target->verdict = -NF_DROP-1 < 0 (ipt_do_table()碰到這個target直接返回NF_DROP)
- IPT_QUEUE:(struct ipt_standard_target *)target->verdict = -NF_QUEUE-1 < 0 (ipt_do_table()碰到這個target直接返回NF_QUEUE)
- IPT_RETURN:(struct ipt_standard_target *)target->verdict = -NF_REPATE-1 < 0 (ipt_do_table()碰到這個target特殊處理)
- 跳轉到子鏈target:(struct ipt_standard_target *)target->verdict = 要跳轉子鏈的偏移量 > 0 (ipt_do_table()碰到這個target會跳轉到該子鏈處理)
- IPT_CONTINUE: IPT_CONTINUE = XT_CONTINUE = 0xFFFFFFFF < 0 (ipt_do_table()碰到這個target會處理下一條規則,這個target被擴展target使用)(它不是一個標準target,但可被其它擴展TARGET用作返回值)
-
擴展target:它是struct xt_entry_target + date[] (ipt_do_table()碰到這個target會調用target->target()處理,並根據返回值做處理。擴展target可返回IPT_CONTINUE,或下面netfilter定義的值)
Netfilter處理的返回值
NF_DROP
NF_ACCEPT
NF_STOLEN
NF_QUEUE
NF_REPEAT
NF_STOP
衆所周知,iptables的一大缺點是線性匹配,這樣當>1000條規則時,匹配效率會明顯下降,爲此,我借鑑NF-hipac的算法,將iptables轉換爲樹形匹配,來加快其匹配效率,轉換後的結構圖如下圖所示: