最近對netfilter進行了研究,已經搞明白了其框架結構、運行流程、以及與iptables的交互流程包括規則設置,慢慢總結一下理解的知識,記錄下來,加深理解,以防忘記。
另外,雖然我只是記錄我的學習過程,以免忘記,而且不成體系,但是字段說明增加了結構體的重要字段在程序裏的關鍵點的使用時機,這個是網上資料所沒有的,網上資料只說明字段含義,從不說什麼時候用,什麼時候賦值,在哪裏賦值,而這些信息是理解功能和源碼必須要明白的。
1. netfilter規則組成
netfilter的每一條規則由結構體struct ipt_entry定義,規則包含三部分:
ip頭信息及控制信息 struct ipt_ip,屬於標準匹配信息
match匹配相關信息 struct xt_entry_match,屬於擴展match信息,可以沒有
target匹配成功時執行的動作相關信息 struct xt_entry_target,爲擴展target信息可以沒有,但是netfilter初始化時會設置默認策略,包含了標準target,即target必須只少有一個,以決定默認數據包走向。默認策略的設置代碼還需要再看看。
2. 規則匹配結構體
頭文件 linux/netfilter_ipv4/ip_tables.h
struct ipt_entry 定義如下:
/* This structure defines each of the firewall rules. Consists of 3
parts which are 1) general IP header stuff 2) match specific
stuff 3) the target to perform if the rule matches */
struct ipt_entry {
struct ipt_ip ip; //標準匹配:ip頭信息,源目的IP地址,掩碼,網絡接口等
//在匹配規則時先進行次標準匹配,作爲參數傳遞到ip_packet_match
//函數進行匹配
/* Mark with fields that we care about. */
unsigned int nfcache; //自己理解:匹配的字段,每一位表示一個字段
//查看iptables源碼時,發現設置規則時會把命令行的字段也就是規則需
//要的字段,將其宏定義值安照位或運算設置到該變量
/* Size of ipt_entry + matches */
u_int16_t target_offset; //target的偏移位置,獲取和遍歷target都根據該變量進行
/* Size of ipt_entry + matches + target */
u_int16_t next_offset; //一個完整的規則包含ipt_entry + matches + target三部分
//所以這三部分後就屬於下一條規則ipt_entry的開始位置
//也就是說該變量表示下一個規則的偏移
/* Back pointer */
unsigned int comefrom; //也是在遍歷規則時用,沒仔細看細節
/* Packet and byte counters. */
struct xt_counters counters; //計數器統計字節與包數,後續再細看
/* The matches (if any), then the target. */
unsigned char elems[0]; //這個變量長度是0,只是爲方便操作後續連續內存的match和target
//match和target以此放在該變量開始的連續內存中
//所以通過上述兩個偏移量字段可以獲取match和target,以及遍歷
//下一條規則結構體ipt_entry
//該字段開始連續的內存存放了擴展match和標準擴展target,在netfilter初始化或者iptables命令行設置
//規則時,netfilter調用do_replace函數將ipt_entry結構體及該字段開始的連續內存存放的match和
//target複製到netfilter的規則表(raw,netfiler。。。)中,ipt_entry表示的規則,每個cpu存儲一
//份,更新時對所有cpu的規則更新。
};
規則結構體ipt_entry中的match和target設置時機:netfilter初始化時,iptables命令行設置規則時。
再看match和target結構體:
3. match結構體,分爲標準match與非標準match
標準match, 根據規則結構體ipt_entry的ipt_ip ip字段的值進行匹配,主要是ip地址,網卡接口,網絡協議;
擴展match,即爲自定義的匹配函數,存儲在規則結構體末尾的連續內存中,以結構體xt_entry_match表示,其中嵌入的結構體xt_match存儲實際的匹配函數。
一個match表示一條規則如何匹配當前數據包(可以由多個match,依次存儲),match還包括匹配時的數據信息也稱爲參數,用於匹配操作。
頭文件 linux/netfilter_ipv4/ip_tables.h
#define ipt_entry_match xt_entry_match
上述對match結構體的宏定義,兩個通用
每一個規則匹配動作都由該結構體表示,該結構體的聯合字段u由用戶與內核空間共享,表示不同的數據和用途
struct xt_entry_match {
union {
struct {
__u16 match_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN]; //match名稱,如,tcp,udp,或者自定義pktsize
__u8 revision;
} user;
struct {
__u16 match_size;
/* Used inside the kernel */
struct xt_match *match; //netfilter匹配規則時調用該結構體的match函數
//該字段是在設置規則或者替換規則時,由netfilter的
//do_replace函數,根據匹配名稱及name字段的值,在保存match
//模塊的全局變量中查找,然後將查找到的指針賦值給該結構體,
//才能在數據包到達時,遍歷規則,進而調用規則的match->match函數。
} kernel;
/* Total length */
__u16 match_size;
} u;
unsigned char data[0]; //存儲匹配數據,程序裏以參數傳遞給match->match函數進行匹配,
//該字段表示匹配函數用到的匹配數據,比如iptables設置規則時通過命令
//行設置的參數或其它數據,這個數據一般是定義爲與匹配相關的結構體
//包含的字段及其含義,根據自己實現的match功能自定義即可。
//在iptables設置規則後,iptables會調用xtables_option_mpcall進行
//命令行解析,一般在這裏對命令行的數據解析後,設置字段data,可根據
//情況自行設置。
};
上述結構體成員struct xt_match *match說明
該結構體實際表示一個擴展匹配, 嵌入在xt_entry_match 中,xt_entry_match 的data成員是匹配數據,將作爲參數傳遞給xt_match中的match函數,具體類型含義由xt_match定義解釋,一般爲自定義匹配時用到的數據。
該結構體的成員在netfilter設置規則時,通過調用do_replace函數(間接調用find_match),
根據match模塊名稱即成員name查找全局match鏈表中match,然後對該結構體成員賦值,
其中的函數指針的邏輯有match實現者自己實現,只有實現者自己才知道自己實現的功能邏輯。
struct xt_match {
struct list_head list; //雙向鏈表,從此看出,match可以有多個,串起來存儲。
//match名稱或稱爲match模塊名稱,因爲以模塊的形式實現,如tcp,udp(定義在netfilter的
//net/netfilter/xt_tcpudp.c中,是netfilter定義的match用於tcp,udp),
//在match模塊定義文件中設置賦值,用於netfiler查找註冊的match模塊。
const char name[XT_EXTENSION_MAXNAMELEN];
u_int8_t revision;
/* Return true or false: return FALSE and set *hotdrop = 1 to
force immediate packet drop. */
/* Arguments changed since 2.6.9, as this must now handle
non-linear skb, using skb_header_pointer and
skb_ip_make_writable. */
//定義在match模塊定義文件中,如netfilter自帶的tcpudp匹配模塊文件xt_tcpudp.c
//netfilter加載規則時(調用do_replace函數間接調用find_match),根據match模塊名稱查找
//全局match鏈表中match,找到後成員match指針賦值,之後,數據包達到時,遍歷規則,調用該函數指針。
bool (*match)(const struct sk_buff *skb,
struct xt_action_param *);
//定義在match模塊定義文件中,如netfilter自帶的tcpudp匹配模塊文件xt_tcpudp.c
//用於對match的data表示的match數據進行check就是檢查合法性等
/* Called when user tries to insert an entry of this type. */
int (*checkentry)(const struct xt_mtchk_param *);
//從明字也能看出來是一個析構函數,在出錯時,退出時,總之就是不需要的時候調用
//如:在根據name成員查找match時,如果check失敗,則調用函數cleanup_match
//cleanup_match又調用destroy進行資源清理等收尾操作
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_mtdtor_param *);
//以下宏定義尚未研究
#ifdef CONFIG_COMPAT
/* Called when userspace align differs from kernel space one */
void (*compat_from_user)(void *dst, const void *src);
int (*compat_to_user)(void __user *dst, const void *src);
#endif
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me; //match是以模塊形式實現,所以在模塊實現文件中設置即可
const char *table; //所屬表名稱,如:filter,raw,mangle,forward
unsigned int matchsize; //匹配數據的大小,一般爲match成員函數指針所指函數的參數大小
#ifdef CONFIG_COMPAT
unsigned int compatsize;
#endif
unsigned int hooks; //match所屬hook點,即註冊在哪個hook點處
unsigned short proto; //match所關注的協議,tcp:IPPROTO_TCP等
unsigned short family; //match的協議族,ipv4天下PF_NET
};
4. target結構體,與match結構體很像:
target分爲標準與自定義target:
標準target的結構體xt_entry_target的字段target爲NULL,此時根據verdict的值決定數據包走向;
擴展target的結構體xt_entry_target的字段target不爲NULL,次時候根據target->target函數的返回值決定數據包走向
一個target表示規則匹配(執行上述結構體match->match(...)函數)成功後要執行的動作,比如丟棄數據包NF_DROP,放行NF_ACCEPT等。
struct xt_entry_target {
union {
struct {
__u16 target_size;
/* Used by userspace */
char name[XT_EXTENSION_MAXNAMELEN]; //target名稱,名稱爲空則爲標準target
//非標準target,由用戶自定義
__u8 revision;
} user;
struct {
__u16 target_size;
/* Used inside the kernel */
struct xt_target *target; //標準target時,該字段爲null,即執行標準target
/NF_ACCEPT,NF_DROP等。
//該字段值也是在規則替換時,由netfilter調用do_replace最後調用check_target設置
} kernel;
/* Total length */
__u16 target_size;
} u;
unsigned char data[0]; //與match中的data一個情形,表示target執行函數的數據或者稱爲參數
//由iptables設置規則時,解析命令行後調用target模塊的parse命令行選項
//解析函數解析後進行設置(一般是這樣),這個數據是自定義的,所以自己定
//義的target模塊文件裏要由parse解析函數,明字可以是abc-parse等等,
//這個設置需要了解自定義一個target所需要的步驟,與自定義一個match是一個套路。
};
xt_target結構體與xt_match類似,省略。
說明一點:
a。match(xt_match)與target(xt_target)都以模塊形式實現,可參考netfilter自帶tcpudp的match模塊
net/netfilter/xt_tcpudp.c
在netfilter初始化規則時,間接調用do_replace函數完成規則初始化,iptables設置規則時,使用setsocketot函數將規則設置到內
核空間,其實是setsocketopt調用鏈的最後一個函數do_ipt_set_ctl調用do_replace,將規則複製到內核的規則變量中,此時的規則,其xt_match結構體的match成員是NULL,所以在do_replace中,調用translate_table函數,其中有一步驟是遍歷剛剛複製到內核的規則,根據match的名稱,在全局變量af[NFPROTO_IPV4].mach中查找已註冊的match模塊,找到後將其賦值到剛剛複製到內核的規則結構體xt_entry_match的xt_match *match變量,這樣就可以匹配了;target也是這個過程。
match函數及target函數的數據參數即xt_entry_match/target的data成員是在iptables應用層設置後,在複製規則時一起復制到內核的,不是遍歷賦值的。對於xt_entry_match/target的data成員,如果是自定義match/target,則這個data指向的是自定義數據類型,一般是結構體。這個data指向的數據也是由iptbales寫入規則後,複製到內核中,netflter調用時作爲參數傳遞給匹配函數進行比較。
b。match與target都以模塊形式實現,那麼他們的結構體xt_match,xt_target 都會註冊到系統中,通過模塊註冊函數
xt_register_match註冊到全局變量struct xt_af af的match與target鏈表中,該結構體變量是一個全局數組,每個元組表示一種支持
的協議。
struct xt_af結構體:
struct xt_af {
struct mutex mutex;
struct list_head match; //match模塊鏈表,規則匹配用到的match都先註冊到這裏,規則加載時從這裏查詢再賦值到規則的xt_entry_match及其xt_match中
struct list_head target; //target模塊鏈表,規則匹配成功後執行動作用到的target都先註冊到這裏,規則加載時從這裏查詢再賦值到規則的xt_entry_target及其xt_target中
#ifdef CONFIG_COMPAT
struct mutex compat_mutex;
struct compat_delta *compat_offsets;
#endif
};
支持的協議枚舉:
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};
內存分配,可以看出是一個包含NFPROTO_NUMPROTO個元素的數組,每個元素對應一個協議,通過協議號進行索引。
xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL);
5. 以上規則結構體說明後,再看規則的內存佈局
netfilter規則是在本質上存儲在一段連續的內存中,三個結構體依次排列,其中match可以爲0個,ipt_entry ipt_entry_match ipt_entry_target。
圖畫的不好,記錄下來吧,以後湊合看。