天天看點

linux核心netfilter的實作以及ipset

netfilter的實作機制基于四個層次的比對,資料包在每個層次都要經過一個過濾連結清單,第一個層次就是hook,衆所周知linux核心中一共擁有5個hooks,當然你也可以自己修改核心在任何地方添加hook;第二個層次就是每個hook下面的tables,每一個hook都過挂載零個或者若幹個tables,資料包要一個一個經過這些tables;第三個層次就是rule,每個table下面擁有零個或者若幹個rule,資料包要依次經過這些rules,隻要有一個rule對資料包進行了裁決,那麼将不再經過該hook的該table的對應rule的後面rules;第四個層次是matchs,和rules的周遊正好相反,資料包隻有在對應rule下面的所有的matchs都比對後才算比對。

     在核心中,netfilter是由下面4個核心結構體支撐起來的:

struct xt_table_info

{

    unsigned int size;

    unsigned int number;

    unsigned int initial_entries;

    unsigned int hook_entry[NF_IP_NUMHOOKS];

    unsigned int underflow[NF_IP_NUMHOOKS];

    char *entries[NR_CPUS];

};

entries[cpu]指向了一個ipt_entry數組,該數組的記憶體組織形式是平坦的,通過ipt_entry的next_offset字段進行周遊:

struct ipt_entry

    struct ipt_ip ip;

    unsigned int nfcache;

    u_int16_t target_offset;

    u_int16_t next_offset;

    unsigned int comefrom;

    struct xt_counters counters;

    unsigned char elems[0];

elems指向了一塊平坦記憶體模式的,包含了若幹個matchs和一個target,target通過target_offset來定位,各個matchs正如上面所說,通過next_offset來進行周遊的:

struct xt_entry_match

    union {

        struct {

            u_int16_t match_size;

            char name[XT_FUNCTION_MAXNAMELEN-1];

            u_int8_t revision;

        } user;

            struct xt_match *match;

        } kernel;

        u_int16_t match_size;

    } u;

    unsigned char data[0];

聯合體u在資料從使用者空間進入核心空間的時候職能改變,xt_entry_match完全從使用者空間拷貝而入,進入核心後,netfilter會根據u.user.name來查找已經注冊的match,然後初始化u.kernel的資料。target也是一樣的,和match不同的是,target對于每條rule隻有一個:

struct xt_entry_target

            u_int16_t target_size;

            struct xt_target *target;

        u_int16_t target_size;

如果抛開任何其它的邏輯不談,netfilter對資料包的過濾過程可以簡化為下面的嵌套循環:

hook{

    table tables[num];

}

table{

    rule rules[num];

rule{

    match matchs[num];

for-each hook in hooks

    for-each table in hooks[i]

        for-each rule in tables[j]

            for-each match in rules[i]

如果寫成c語言的形式的話就要嵌套四層循環,當然hook點是分布的,而不是在一個循環中的,可僅從邏輯上展開核心代碼看的話,卻是是嵌套了四層循環,可見netfilter是比較耗時的,即使netfilter在實作的過程中使用了一些小技巧來優化性能。

     對應于使用者空間的iptables指令簡述一下,每條指令都會增加很多層的很多次周遊,當然固定的INPUT,OUTPUT,FORWARD等内置的最外層周遊是不會增加的,-A/D/I後面的參數訓示對應的hook,設為h,而-t後面的參數訓示h下的table,設為t,然後整個指令就是一條rule,接下來-s,-d,-p等等每個-x以及後面的參數都是一個match,一條iptables指令影響一個hook,一個tables,在一個table追加/删除/插入一條rule,并且為該rule設定若幹match,最後指出一旦比對則執行什麼動作,也就是target。

     正是因為netfilter的周遊很耗時,政策上應該盡可能的減少需要周遊的次數,是以ipset就有了用武之地,ipset用可以将任意的ip位址加入到一個集合,然後将這個集合看作一個整體,這樣就省去了很多的rule以及其下match的周遊,ipset用可插拔可替換的多種算法對ip位址進行增删查改等等,這些算法包括哈希,連結清單,map等等,有了ipset,如果需要比對多個不連續的ip位址,不再需要書寫多個規則了,隻需要一條規則就可以了,務必保證有一條match指向ipset注冊的match,剩下的工作ipset可以高效的完成,另外ipset可以在使用者空間動态配置,這樣就可以輕量級高效率的完成動态ip過濾,必要時還可以找資料庫來幫忙。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271912