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