随着計算機網絡和Internet普及,計算機很久之前就開始遭受各種入侵了。是以為了阻止入侵,就産生了網絡防火牆以及網絡資料分析的需求。
而這個netfilter就是在linux系統中來實作防火牆功能。
netfilter是Linux 2.4.x引入的一個Linux核心架構,提供一整套的hook函數的管理機制。可以根據動态定義的條件來過濾和操作分組。從防火牆到網絡通信資料的詳細分析,到分組過濾器,都可以實作。
其在核心中的位置如下圖:

圖中也反映了我們常用的使用者層指令iptables和底層netfilter的關系。
iptables是Linux的應用層防火牆工具,使用時候主要關注的是chain和table,其關聯如下。
5個不同的chain其實就是5個過濾點,3個表根據功能和表的定義劃分。因為其是在應用層,是以我們關注的是他的編寫。
l  [-t 表名]:該規則所操作的哪個表,可以使用filter、nat等,如果沒有指定則預設為filter
l  -A:新增一條規則,到該規則鍊清單的最後一行
l  -I:插入一條規則,原本該位置上的規則會往後順序移動,沒有指定編号則為1
l  -D:從規則鍊中删除一條規則,要麼輸入完整的規則,或者指定規則編号加以删除
l  -R:替換某條規則,規則替換不會改變順序,而且必須指定編号。
l  -P:設定某條規則鍊的預設動作
l  -nL:-L、-n,檢視目前運作的防火牆規則清單
l  chain名:指定規則表的哪個鍊,如INPUT、OUPUT、FORWARD、PREROUTING等
l  [規則編号]:插入、删除、替換規則時用,--line-numbers顯示号碼
l  [-i|o 網卡名稱]:i是指定資料包從哪塊網卡進入,o是指定資料包從哪塊網卡輸出
l  [-p 協定類型]:可以指定規則應用的協定,包含tcp、udp和icmp等
l  [-s 源IP位址]:源主機的IP位址或子網位址
l  [--sport 源端口号]:資料包的IP的源端口号
l  [-d目标IP位址]:目标主機的IP位址或子網位址
l  [--dport目标端口号]:資料包的IP的目标端口号
l  -m:extend matches,這個選項用于提供更多的比對參數,如:
n  -m state --state ESTABLISHED,RELATED
n  -m tcp --dport 22
n  -m multiport --dports 80,8080
n  -m icmp --icmp-type 8
n  <-j 動作>:處理資料包的動作,包括ACCEPT、DROP、REJECT等。
例如禁止對外向某個IP發送資料包。
#iptables
-t filter -A INPUT -s 192.168.1.10 -j DROP
-表:規定使用的表(filter、nat、mangle,不同的表有不同的功能)
-鍊:規定過濾點
-比對屬性:規定比對資料包的特征
-比對後的動作
使用iptables -L檢視規則。
删除現有規則:iptable -F
netfilter鈎子能在6挂接點注冊回調函數,也是挂鈎編号:
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
定義在<b>include/uapi/linux/netfilter.h </b>
netfilter鈎子目的是在支援運作階段加載netfilter核心子產品。
我們知道netfilter的入口是在ip層的,是以其資料包入口其實就是ip包,不剛好是使用者層的指令iptables相對應麼?是不是感覺世界也變得清晰起來了。
那麼這些挂鈎函數是如何調用的呢?
以上圖中NF_INET_LOCAL_IN這個關鍵點為例,我們來看下其上遊函數ip_local_deliver()函數,如下:
/*
*
Deliver IP Packets to the higher protocol layers.
*/
int ip_local_deliver(struct sk_buff *skb)
{
/*
*
Reassemble IP fragments.
*/
struct net *net = dev_net(skb->dev);
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
}
return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
net, NULL, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
可以看到挂鈎函數通過NF_HOOK宏調用(其它過濾點同理),該宏是定義在include/linux/netfilter.h檔案中的。是netfilter鈎子,如果允許包傳遞則傳回1。如果傳回其他值說明這個包被hook給消耗掉了。
static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
struct sock *sk, struct sk_buff *skb,
struct net_device
*indev, struct net_device *outdev,
int (*okfn)(struct net *, struct sock *, struct sk_buff *))
其中pf表示挂鈎的協定族,
hook是挂鈎編号,
skb是處理的套接字緩沖區
indev和outdev是網絡裝置的net_device執行個體的指針。
okfn是函數指針,在netfilter挂鈎結束時候執行。
每個鈎子函數最後必須向Netfilter架構傳回下列幾個值其中之一,定義在include/uapi/linux/netfilter.h:
NF_DROP 丢棄該資料報,不再傳輸。
NF_ACCEPT 繼續正常傳輸資料報。這個傳回值告訴 Netfilter:到目前為止,該資料包還是被接受的并且該資料包應當被遞交到網絡協定棧的下一個階段。
NF_STOLEN 子產品接管該資料報,告訴Netfilter“忘掉”該資料報。該回調函數将從此開始對資料包的處理,并且Netfilter應當放棄對該資料包做任何的處理。但是,這并不意味着該資料包的資源已經被釋放。這個資料包以及它獨自的sk_buff資料結構仍然有效,隻是回調函數從Netfilter 擷取了該資料包的所有權。
NF_QUEUE 對該資料報進行排隊(通常用于将資料報給使用者空間的程序進行處理)
NF_REPEAT 再次調用該回調函數,應當謹慎使用這個值,以免造成死循環。
NF_STOP為了相容使用者層的NF_QUEUE。
在之前的文章中有提到抓包工具tcpdump使用的是AF_PACKET套接字,而不是Netfilter。因為有一下幾點:
l  資料包進入netfilter時已經做過一些處理,資料包可能發生了變化。另外,進入netfilter前需要重組分片,無法抓取到原始的封包分片。
l  另外如果是發送,封包離開netfilter時也未完全結束協定棧的處理,導緻封包不完整。
l  在Netfilter抓取的封包,實作較為複雜。
是以用AF_PACKET來進行封包抓取。
小結一下,總體邏輯過程其實非常簡單。
在linux網絡核心協定棧中,已預定義的關鍵點PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT和POST_ROUTING,當資料包經過這些關鍵點時候,會根據資料包的協定簇PF_INET去查找是否注冊有鈎子函數。
如果沒有,則繼續走協定棧;如果有則進入到netfilter架構中去進一步調用已注冊在該點下的鈎子函數,最後再根據其傳回值來确定是否繼續執行,因為資料包可能被幹掉了。
本片主要是一些前提的知識點和邏輯,最後祝大家玩得開心。
洞悉linux下的Netfilter&iptables:什麼是Netfilter?
《Linux Kernel Networking》