亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何實現Netfilter

發布時間:2021-10-13 15:28:40 來源:億速云 閱讀:101 作者:iii 欄目:web開發

本篇內容介紹了“如何實現Netfilter”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

一、Netfilter 掛載點

我們先來回顧一下 Netfilter 的原理,Netfilter 是通過在網絡協議棧的不同階段注冊鉤子函數來實現對數據包的處理與過濾,如 圖1  所示:

如何實現Netfilter

(圖1 Netfilter掛載點)

在 圖1 中,藍色部分就是 Netfilter 掛載鉤子函數的位置,所以 Netfilter 定義了 5 個常量來表示這 5 個位置,如下代碼:

// 文件:include/linux/netfilter_ipv4.h  #define NF_IP_PRE_ROUTING   0 #define NF_IP_LOCAL_IN      1 #define NF_IP_FORWARD       2 #define NF_IP_LOCAL_OUT     3 #define NF_IP_POST_ROUTING  4

上面代碼中的常量與 圖1 中掛載鉤子函數的位置一一對應,如常量 NF_IP_PRE_ROUTING 對應著 圖1 的 PRE_ROUTING 處。

二、Netfilter 鉤子函數鏈

前面說過,Netfilter 是通過在網絡協議中的不同位置掛載鉤子函數來對數據包進行過濾和處理,而且每個掛載點能夠掛載多個鉤子函數,所以  Netfilter 使用鏈表結構來存儲這些鉤子函數,如 圖2 所示:

如何實現Netfilter

(圖2 Netfilter鉤子函數鏈)

如 圖2 所示,Netfilter 的每個掛載點都使用一個鏈表來存儲鉤子函數列表。在內核中,定義了一個名為 nf_hooks  的數組來存儲這些鏈表,如下代碼:

// 文件:net/core/netfilter.c  struct list_head nf_hooks[32][5];

struct list_head 結構是內核的通用鏈表結構。

從 nf_hooks 變量定義為一個二維數組,第一維是用來表示不同的協議(如 IPv4 或者 IPv6,本文只討論 IPv4,所以可以把 nf_hooks  當成是一維數組),而第二維用于表示不同的掛載點,如 圖2 中的 5 個掛載點。

三、鉤子函數

接下來我們介紹一下鉤子函數在 Netfilter 中的存儲方式。

前面我們介紹過,Netfilter 通過鏈表來存儲鉤子函數,而鉤子函數是通過結構 nf_hook_ops 來描述的,其定義如下:

// 文件:include/linux/netfilter.h  struct nf_hook_ops {     struct list_head list; // 連接相同掛載點的鉤子函數     nf_hookfn *hook;       // 鉤子函數指針     int pf;                // 協議類型     int hooknum;           // 鉤子函數所在鏈     int priority;          // 優先級 };

下面我們對 nf_hook_ops 結構的各個字段進行說明:

  • list:用于把處于相同掛載點的鉤子函數鏈接起來。

  • hook:鉤子函數指針,就是用于處理或者過濾數據包的函數。

  • pf:協議類型,用于指定鉤子函數掛載在 nf_hooks 數組第一維的位置,如 IPv4 協議設置為 PF_INET。

  • hooknum:鉤子函數所在鏈(掛載點),如 NF_IP_PRE_ROUTING。

  • priority:鉤子函數的優先級,用于管理鉤子函數的調用順序。

其中 hook 字段的類型為 nf_hookfn,nf_hookfn 類型的定義如下:

// 文件:include/linux/netfilter.h  typedef unsigned int nf_hookfn(unsigned int hooknum,                                struct sk_buff **skb,                                const struct net_device *in,                                const struct net_device *out,                                int (*okfn)(struct sk_buff *));

我們也介紹一下 nf_hookfn 函數的各個參數的作用:

  • hooknum:鉤子函數所在鏈(掛載點),如 NF_IP_PRE_ROUTING。

  • skb:數據包對象,就是要處理或者過濾的數據包。

  • in:接收數據包的設備對象。

  • out:發送數據包的設備對象。

  • okfn:當掛載點上所有的鉤子函數都處理過數據包后,將會調用這個函數來對數據包進行下一步處理。

四、注冊鉤子函數

當定義好一個鉤子函數結構后,需要調用 nf_register_hook 函數來將其注冊到 nf_hooks 數組中,nf_register_hook  函數的實現如下:

// 文件:net/core/netfilter.c  int nf_register_hook(struct nf_hook_ops *reg) {     struct list_head *i;      br_write_lock_bh(BR_NETPROTO_LOCK); // 對 nf_hooks 進行上鎖      // priority 字段表示鉤子函數的優先級     // 所以通過 priority 字段來找到鉤子函數的合適位置     for (i = nf_hooks[reg->pf][reg->hooknum].next;          i != &nf_hooks[reg->pf][reg->hooknum];          i = i->next)     {         if (reg->priority < ((struct nf_hook_ops *)i)->priority)             break;     }      list_add(&reg->list, i->prev); // 把鉤子函數添加到鏈表中      br_write_unlock_bh(BR_NETPROTO_LOCK); // 對 nf_hooks 進行解鎖      return 0; }

nf_register_hook 函數的實現比較簡單,步驟如下:

  • 對 nf_hooks 進行上鎖操作,用于保護 nf_hooks 變量不受并發競爭。

  • 通過鉤子函數的優先級來找到其在鉤子函數鏈表中的正確位置。

  • 把鉤子函數插入到鏈表中。

  • 對 nf_hooks 進行解鎖操作。

插入過程如 圖3 所示:

如何實現Netfilter

(圖3 鉤子函數插入過程)

如 圖3 所示,我們要把優先級為 20 的鉤子函數插入到 PRE_ROUTING 這個鏈中,而 PRE_ROUTING  鏈已經存在兩個鉤子函數,一個優先級為 10, 另外一個優先級為 30。

通過與鏈表中的鉤子函數的優先級進行對比,發現新的鉤子函數應該插入到優先級為 10 的鉤子函數后面,所以就 如圖3 所示就把新的鉤子函數插入到優先級為 10  的鉤子函數后面。

五、觸發調用鉤子函數

鉤子函數已經被保存到不同的鏈上,那么什么時候才會觸發調用這些鉤子函數來處理數據包呢?

要觸發調用某個掛載點上(鏈)的所有鉤子函數,需要使用 NF_HOOK 宏來實現,其定義如下:

// 文件:include/linux/netfilter.h  #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)    \     (list_empty(&nf_hooks[(pf)][(hook)])               \         ? (okfn)(skb)                                  \         : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))

首先介紹一下 NF_HOOK 宏的各個參數的作用:

  • pf:協議類型,就是 nf_hooks 數組的第一個維度,如 IPv4 協議就是 PF_INET。

  • hook:要調用哪一條鏈(掛載點)上的鉤子函數,如 NF_IP_PRE_ROUTING。

  • indev:接收數據包的設備對象。

  • outdev:發送數據包的設備對象。

  • okfn:當鏈上的所有鉤子函數都處理完成,將會調用此函數繼續對數據包進行處理。

而 NF_HOOK 宏的實現也比較簡單,首先判斷一下鉤子函數鏈表是否為空,如果是空的話,就直接調用 okfn 函數來處理數據包,否則就調用  nf_hook_slow 函數來處理數據包。我們來看看 nf_hook_slow 函數的實現:

// 文件:net/core/netfilter.c  int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,                  struct net_device *indev, struct net_device *outdev,                  int (*okfn)(struct sk_buff *)) {     struct list_head *elem;     unsigned int verdict;     int ret = 0;      elem = &nf_hooks[pf][hook]; // 獲取要調用的鉤子函數鏈表      // 遍歷鉤子函數鏈表,并且調用鉤子函數對數據包進行處理     verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn);     ...     // 如果處理結果為 NF_ACCEPT, 表示數據包通過所有鉤子函數的處理, 那么就調用 okfn 函數繼續處理數據包     // 如果處理結果為 NF_DROP, 表示數據包被拒絕, 應該丟棄此數據包     switch (verdict) {     case NF_ACCEPT:         ret = okfn(skb);         break;     case NF_DROP:         kfree_skb(skb);         ret = -EPERM;         break;     }      return ret; }

nf_hook_slow 函數的實現也比較簡單,過程如下:

  • 首先調用 nf_iterate 函數來遍歷鉤子函數鏈表,并調用鏈表上的鉤子函數來處理數據包。

  • 如果處理結果為 NF_ACCEPT,表示數據包通過所有鉤子函數的處理, 那么就調用 okfn 函數繼續處理數據包。

  • 如果處理結果為 NF_DROP,表示數據包沒有通過鉤子函數的處理,應該丟棄此數據包。

既然 Netfilter 是通過調用 NF_HOOK 宏來調用鉤子函數鏈表上的鉤子函數,那么內核在什么地方調用這個宏呢?

比如數據包進入 IPv4 協議層的處理函數 ip_rcv 函數中就調用了 NF_HOOK 宏來處理數據包,代碼如下:

// 文件:net/ipv4/ip_input.c  int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) {     ...     return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); }

如上代碼所示,在 ip_rcv 函數中調用了 NF_HOOK 宏來處理輸入的數據包,其調用的鉤子函數鏈(掛載點)為 NF_IP_PRE_ROUTING。而  okfn 設置為 ip_rcv_finish,也就是說,當 NF_IP_PRE_ROUTING 鏈上的所有鉤子函數都成功對數據包進行處理后,將會調用  ip_rcv_finish 函數來繼續對數據包進行處理。

“如何實現Netfilter”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

柘城县| 上犹县| 崇州市| 龙口市| 玉溪市| 太白县| 贞丰县| 东莞市| 红安县| 丹阳市| 榆树市| 陈巴尔虎旗| 永仁县| 定陶县| 新营市| 福鼎市| 横山县| 肃北| 溧水县| 伊金霍洛旗| 探索| 永吉县| 驻马店市| 张家川| 珲春市| SHOW| 十堰市| 湟源县| 石林| 搜索| 彰化县| 游戏| 边坝县| 东莞市| 自治县| 三穗县| 仁布县| 荔浦县| 青田县| 馆陶县| 宕昌县|