寫在前面
本系列不是介紹How to
配置iptables
的文章。因為網路上已經有很多這型別的教程了,其中一些還不錯(比如連結).
本系列也不是一般意義上的Netfilter
原始碼分析文章。因為大段貼上程式碼也會讓人心生畏懼和厭煩!
本系列文章的目標是,用盡量少的文字和圖片講明白How Netfilter work
Netfilter 的基本概念
Netfilter
是一套融入在Linux
核心網路協議棧中的報文處理(過濾
或者修改
)框架。它在核心中報文的關鍵流動路徑上定義了5
個HOOK
點(下圖藍色方框),各個協議(如IPv4
、IPv6
、ARP
)可以在這些HOOK
點安裝鉤子函式,報文流經此地,核心會按照優先順序呼叫這些鉤子函式,這些鉤子函式最終會決定報文是被NF_ACCEPT
(放行)還是NF_DROP
(丟棄)。
圖中紅色虛線表示核心最常見的報文流經的路徑:本機接收、轉發、本機傳送。5
個HOOK
點分別是:路由前、本地上送、轉發、本地傳送、路由後1
鏈(chain) & 表(table)
初次接觸iptables
的同學可能會被四表五鏈
這個名字嚇到,特別是鏈
這個名字真的很容易令人困惑! 而當你瞭解了Netfilter
的實現細節後,才會發現:噢,原來鏈
就是HOOK
點,HOOK
點就是鏈
,因為有5
個HOOK
點,所以有五鏈
!
那麼,為什麼要叫鏈
呢?
因為一個HOOK
點可以上可以安裝多個鉤子, 核心用“鏈條”將這些鉤子串起來!
相比之下,四表(table)
就沒那麼神祕了: 起過濾作用的filter
表、起NAT
作用的nat
表,用於修改報文的mangle
表,用於取消連線跟蹤的raw
表。
Netfilter
設計多個表的目的,一方面是方便分類管理,另一方面,更重要的是為了限定各個鉤子(或者說使用者規則)執行的順序!
以PREROUTING
這個HOOK
點為例,使用者使用iptables
設定的NAT
規則和mangle
會分別掛到nat hook
和mangle hook
,NAT
表的優先順序天生比mangle
表低,因此報文一定會先執行mangle
表的規則。
這就是四表五鏈
的概念。我個人認為鏈
的比表
重要多了. 因為就算Netfilter
沒有表的概念,那麼通過小心翼翼地設定各個rule
的順序其實也可以達到相同的效果。但鏈
(也就是HOOK
點)的作用是獨一無二的。換個角度,使用者在配置iptables
規則時,更多的精力也是放在"應該在哪個HOOK點進行操作",至於用的是filter
表、nat
表還是其他表,其實都是順理成章的事情。
Hook
HOOK 點的位置
使用者通過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,
}
HOOK 點的元素
hooks
的每個元素都是連結串列頭,連結串列上掛的元素型別是struct nf_hook_ops
,這些元素有兩個來源,一類來自於Netfilter
初始化時各個表(如filter
)的初始化,另一類來自於如連線跟蹤這樣的內部模組。下圖展示了第一類來源的元素的掛接情況,它們按優先順序排列(數字越小優先順序越高),而.hook
就是報文到達對應的路徑時會執行的鉤子函式。
附:相關核心函式的例子
iptable_filter_init
|--xt_hook_link
|-- nf_register_hooks
|-- nf_register_hook
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
就會被執行。