本系列不是介紹How to
配置iptables
的文章。由於網絡上已經有不少這類型的教程了,其中一些還不錯(好比連接).數組
本系列也不是通常意義上的Netfilter
源碼分析文章。由於大段粘貼代碼也會讓人心生畏懼和厭煩!網絡
本系列文章的目標是,用盡可能少的文字和圖片講明白How Netfilter work
框架
Netfilter
是一套融入在Linux
內核網絡協議棧中的報文處理(過濾
或者修改
)框架。它在內核中報文的關鍵流動路徑上定義了5
個HOOK
點(下圖藍色方框),各個協議(如IPv4
、IPv6
、ARP
)能夠在這些HOOK
點安裝鉤子函數,報文流經此地,內核會按照優先級調用這些鉤子函數,這些鉤子函數最終會決定報文是被NF_ACCEPT
(放行)仍是NF_DROP
(丟棄)。函數
圖中紅色虛線表示內核最多見的報文流經的路徑:本機接收、轉發、本機發送。5
個HOOK
點分別是:路由前、本地上送、轉發、本地發送、路由後1
源碼分析
初次接觸iptables
的同窗可能會被四表五鏈
這個名字嚇到,特別是鏈
這個名字真的很容易使人困惑! 而當你瞭解了Netfilter
的實現細節後,纔會發現:噢,原來鏈
就是HOOK
點,HOOK
點就是鏈
,由於有5
個HOOK
點,因此有五鏈
!spa
那麼,爲何要叫鏈
呢?.net
由於一個HOOK
點能夠上能夠安裝多個鉤子, 內核用「鏈條」將這些鉤子串起來!設計
相比之下,四表(table)
就沒那麼神祕了: 起過濾做用的filter
表、起NAT
做用的nat
表,用於修改報文的mangle
表,用於取消鏈接跟蹤的raw
表。code
Netfilter
設計多個表的目的,一方面是方便分類管理,另外一方面,更重要的是爲了限定各個鉤子(或者說用戶規則)執行的順序!教程
以PREROUTING
這個HOOK
點爲例,用戶使用iptables
設置的NAT
規則和mangle
會分別掛到nat hook
和mangle hook
,NAT
表的優先級天生比mangle
表低,所以報文必定會先執行mangle
表的規則。
這就是四表五鏈
的概念。我我的認爲鏈
的比表
重要多了. 由於就算Netfilter
沒有表的概念,那麼經過當心翼翼地設置各個rule
的順序其實也能夠達到相同的效果。但鏈
(也就是HOOK
點)的做用是獨一無二的。換個角度,用戶在配置iptables
規則時,更多的精力也是放在 "應該在哪一個HOOK點進行操做",至於用的是filter
表、nat
表仍是其餘表,其實都是瓜熟蒂落的事情。
用戶經過iptables
配置的規則最終會記錄在HOOK
點。HOOK
點定義在struct net
結構中,即HOOK
點是各個net namespace
中獨立的。因此,在使用容器的場景中,每一個容器的防火牆規則是獨立的。
struct net { /* code omitted */ struct netns_nf nf; /* code omitted */ } struct netns_nf { /* code omitted */ struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; };
從上面的定義能夠看到,HOOK
點是一個二維數組,每一個元素都是一個鏈表頭。它的第一個維度是協議類型,其中最經常使用的NFPROTO_IPV4
,咱們使用的iptables
命令都是將這個鉤子安裝到這個協議的hook
,而使用ip6tables
就是將鉤子安裝到NFPROTO_IPV6
的hook
;第二個維度是鏈
,對於IPV4
來講,它的取值範圍以下:
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, }
hooks
的每一個元素都是鏈表頭,鏈表上掛的元素類型是struct nf_hook_ops
,這些元素有兩個來源,一類來自於Netfilter
初始化時各個表(如filter
)的初始化,另外一類來自於如鏈接跟蹤這樣的內部模塊。下圖展現了第一類來源的元素的掛接狀況,它們按優先級排列(數字越小優先級越高),而.hook
就是報文到達對應的路徑時會執行的鉤子函數。
附:相關內核函數的例子
iptable_filter_init |--xt_hook_link |-- nf_register_hooks |-- nf_register_hook
Netfilter
框架已經徹底融入內核協議棧了,因此在協議棧代碼中經常能夠看到NF_HOOK
宏的調用,這個宏的參數指定了HOOK
點。
以本機收到IPv4
報文爲例
int ip_rcv(struct sk_buff* skb,...) { // code omitted return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, skb, dev, NULL, ip_rcv_finish); // code omitted }
它指定要遍歷的鉤子函數是net namespace爲net
的hooks[NFPROTO_IPV4][NF_INET_PRE_ROUTING]
鏈表上的元素,也就是上面圖中的第一行的鏈表。若是三個鉤子函數執行的結果(verdict
)都是NF_ACCEPT
,那麼NF_HOOK
指定的ip_rcv_finish
就會被執行。