Netfilter 是如何工作的(一):HOOK點

187J3X1發表於2019-06-11

寫在前面

本系列不是介紹How to配置iptables的文章。因為網路上已經有很多這型別的教程了,其中一些還不錯(比如連結).

本系列也不是一般意義上的Netfilter原始碼分析文章。因為大段貼上程式碼也會讓人心生畏懼和厭煩!

本系列文章的目標是,用盡量少的文字和圖片講明白How Netfilter work

Netfilter 的基本概念

Netfilter是一套融入Linux核心網路協議棧中的報文處理(過濾或者修改)框架。它在核心中報文的關鍵流動路徑上定義了5HOOK點(下圖藍色方框),各個協議(如IPv4IPv6ARP)可以在這些HOOK點安裝鉤子函式,報文流經此地,核心會按照優先順序呼叫這些鉤子函式,這些鉤子函式最終會決定報文是被NF_ACCEPT(放行)還是NF_DROP(丟棄)。

forward-model

圖中紅色虛線表示核心最常見的報文流經的路徑:本機接收轉發本機傳送
5HOOK點分別是:路由前本地上送轉發本地傳送路由後1

鏈(chain) & 表(table)

初次接觸iptables的同學可能會被四表五鏈這個名字嚇到,特別是這個名字真的很容易令人困惑! 而當你瞭解了Netfilter的實現細節後,才會發現:噢,原來就是HOOK點,HOOK點就是,因為有5HOOK點,所以有五鏈

那麼,為什麼要叫呢?

因為一個HOOK點可以上可以安裝多個鉤子, 核心用“鏈條”將這些鉤子串起來!

圖片描述
相比之下,四表(table)就沒那麼神祕了: 起過濾作用的filter表、起NAT作用的nat表,用於修改報文的mangle表,用於取消連線跟蹤的raw表。

Netfilter設計多個表的目的,一方面是方便分類管理,另一方面,更重要的是為了限定各個鉤子(或者說使用者規則)執行的順序!

PREROUTING這個HOOK點為例,使用者使用iptables設定的NAT規則和mangle會分別掛到nat hookmangle hookNAT表的優先順序天生比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_IPV6hook;第二個維度是,對於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 namespacenethooks[NFPROTO_IPV4][NF_INET_PRE_ROUTING]連結串列上的元素,也就是上面圖中的第一行的連結串列。如果三個鉤子函式執行的結果(verdict)都是NF_ACCEPT,那麼NF_HOOK指定的ip_rcv_finish就會被執行。

相關文章