linux iptables實現

Xtables提供的資源

iptables1.jpg 

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表的結構圖

iptables2.jpg 

  • 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。
Iptables利用Xtables構建的組織形式

iptables3.jpg


Iptable下發規則的執行流程圖

iptables4.jpg 

iptables內核層與應用層之間傳輸規則的組織形式

iptables5.jpg 

iptables將內核中獲取的規則轉換爲如下圖所示可管理的結構

iptables6.jpg


Iptables包含以下target
  • 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轉換爲樹形匹配,來加快其匹配效率,轉換後的結構圖如下圖所示:

iptables7.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章