<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Netfileter & iptables 實(shí)現(xiàn)(一)— Netfilter實(shí)現(xiàn)

          共 5482字,需瀏覽 11分鐘

           ·

          2021-04-14 17:43

          在《Netfilter & iptables 原理》一文中,我們介紹了 Netfilteriptables 的原理,而本文主要通過(guò)源碼分析來(lái)介紹一下 Netfilter 與 iptables 的實(shí)現(xiàn)過(guò)程。

          一、Netfilter 掛載點(diǎn)

          我們先來(lái)回顧一下 Netfilter 的原理,Netfilter 是通過(guò)在網(wǎng)絡(luò)協(xié)議棧的不同階段注冊(cè)鉤子函數(shù)來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)包的處理與過(guò)濾,如 圖1 所示:

          (圖1 Netfilter掛載點(diǎn))


          在 圖1 中,藍(lán)色部分就是 Netfilter 掛載鉤子函數(shù)的位置,所以 Netfilter 定義了 5 個(gè)常量來(lái)表示這 5 個(gè)位置,如下代碼:

          // 文件: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 中掛載鉤子函數(shù)的位置一一對(duì)應(yīng),如常量 NF_IP_PRE_ROUTING 對(duì)應(yīng)著 圖1 的 PRE_ROUTING 處。

          二、Netfilter 鉤子函數(shù)鏈

          前面說(shuō)過(guò),Netfilter 是通過(guò)在網(wǎng)絡(luò)協(xié)議中的不同位置掛載鉤子函數(shù)來(lái)對(duì)數(shù)據(jù)包進(jìn)行過(guò)濾和處理,而且每個(gè)掛載點(diǎn)能夠掛載多個(gè)鉤子函數(shù),所以 Netfilter 使用鏈表結(jié)構(gòu)來(lái)存儲(chǔ)這些鉤子函數(shù),如 圖2 所示:

          (圖2 Netfilter鉤子函數(shù)鏈)


          如 圖2 所示,Netfilter 的每個(gè)掛載點(diǎn)都使用一個(gè)鏈表來(lái)存儲(chǔ)鉤子函數(shù)列表。在內(nèi)核中,定義了一個(gè)名為 nf_hooks 的數(shù)組來(lái)存儲(chǔ)這些鏈表,如下代碼:

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

          struct list_head 結(jié)構(gòu)是內(nèi)核的通用鏈表結(jié)構(gòu)。

          nf_hooks 變量定義為一個(gè)二維數(shù)組,第一維是用來(lái)表示不同的協(xié)議(如 IPv4 或者 IPv6,本文只討論 IPv4,所以可以把 nf_hooks 當(dāng)成是一維數(shù)組),而第二維用于表示不同的掛載點(diǎn),如 圖2 中的 5 個(gè)掛載點(diǎn)。

          三、鉤子函數(shù)

          接下來(lái)我們介紹一下鉤子函數(shù)在 Netfilter 中的存儲(chǔ)方式。

          前面我們介紹過(guò),Netfilter 通過(guò)鏈表來(lái)存儲(chǔ)鉤子函數(shù),而鉤子函數(shù)是通過(guò)結(jié)構(gòu) nf_hook_ops 來(lái)描述的,其定義如下:

          // 文件:include/linux/netfilter.h
          struct nf_hook_ops{ struct list_head list; // 連接相同掛載點(diǎn)的鉤子函數(shù) nf_hookfn *hook; // 鉤子函數(shù)指針 int pf; // 協(xié)議類型 int hooknum; // 鉤子函數(shù)所在鏈 int priority; // 優(yōu)先級(jí)};

          下面我們對(duì) nf_hook_ops 結(jié)構(gòu)的各個(gè)字段進(jìn)行說(shuō)明:

          • list:用于把處于相同掛載點(diǎn)的鉤子函數(shù)鏈接起來(lái)。

          • hook:鉤子函數(shù)指針,就是用于處理或者過(guò)濾數(shù)據(jù)包的函數(shù)。

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

          • hooknum:鉤子函數(shù)所在鏈(掛載點(diǎn)),如 NF_IP_PRE_ROUTING

          • priority:鉤子函數(shù)的優(yōu)先級(jí),用于管理鉤子函數(shù)的調(diào)用順序。

          其中 hook 字段的類型為 nf_hookfnnf_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 函數(shù)的各個(gè)參數(shù)的作用:

          • hooknum:鉤子函數(shù)所在鏈(掛載點(diǎn)),如 NF_IP_PRE_ROUTING

          • skb:數(shù)據(jù)包對(duì)象,就是要處理或者過(guò)濾的數(shù)據(jù)包。

          • in:接收數(shù)據(jù)包的設(shè)備對(duì)象。

          • out:發(fā)送數(shù)據(jù)包的設(shè)備對(duì)象。

          • okfn:當(dāng)掛載點(diǎn)上所有的鉤子函數(shù)都處理過(guò)數(shù)據(jù)包后,將會(huì)調(diào)用這個(gè)函數(shù)來(lái)對(duì)數(shù)據(jù)包進(jìn)行下一步處理。

          四、注冊(cè)鉤子函數(shù)

          當(dāng)定義好一個(gè)鉤子函數(shù)結(jié)構(gòu)后,需要調(diào)用 nf_register_hook 函數(shù)來(lái)將其注冊(cè)到 nf_hooks 數(shù)組中,nf_register_hook 函數(shù)的實(shí)現(xiàn)如下:

          // 文件:net/core/netfilter.c
          int nf_register_hook(struct nf_hook_ops *reg){ struct list_head *i;
          br_write_lock_bh(BR_NETPROTO_LOCK); // 對(duì) nf_hooks 進(jìn)行上鎖
          // priority 字段表示鉤子函數(shù)的優(yōu)先級(jí) // 所以通過(guò) priority 字段來(lái)找到鉤子函數(shù)的合適位置 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); // 把鉤子函數(shù)添加到鏈表中
          br_write_unlock_bh(BR_NETPROTO_LOCK); // 對(duì) nf_hooks 進(jìn)行解鎖
          return 0;}

          nf_register_hook 函數(shù)的實(shí)現(xiàn)比較簡(jiǎn)單,步驟如下:

          • 對(duì) nf_hooks 進(jìn)行上鎖操作,用于保護(hù) nf_hooks 變量不受并發(fā)競(jìng)爭(zhēng)。

          • 通過(guò)鉤子函數(shù)的優(yōu)先級(jí)來(lái)找到其在鉤子函數(shù)鏈表中的正確位置。

          • 把鉤子函數(shù)插入到鏈表中。

          • 對(duì) nf_hooks 進(jìn)行解鎖操作。

          插入過(guò)程如 圖3 所示:

          (圖3 鉤子函數(shù)插入過(guò)程)


          如 圖3 所示,我們要把優(yōu)先級(jí)為 20 的鉤子函數(shù)插入到 PRE_ROUTING 這個(gè)鏈中,而 PRE_ROUTING 鏈已經(jīng)存在兩個(gè)鉤子函數(shù),一個(gè)優(yōu)先級(jí)為 10, 另外一個(gè)優(yōu)先級(jí)為 30。

          通過(guò)與鏈表中的鉤子函數(shù)的優(yōu)先級(jí)進(jìn)行對(duì)比,發(fā)現(xiàn)新的鉤子函數(shù)應(yīng)該插入到優(yōu)先級(jí)為 10 的鉤子函數(shù)后面,所以就 如圖3 所示就把新的鉤子函數(shù)插入到優(yōu)先級(jí)為 10 的鉤子函數(shù)后面。

          五、觸發(fā)調(diào)用鉤子函數(shù)

          鉤子函數(shù)已經(jīng)被保存到不同的鏈上,那么什么時(shí)候才會(huì)觸發(fā)調(diào)用這些鉤子函數(shù)來(lái)處理數(shù)據(jù)包呢?

          要觸發(fā)調(diào)用某個(gè)掛載點(diǎn)上(鏈)的所有鉤子函數(shù),需要使用 NF_HOOK 宏來(lái)實(shí)現(xiàn),其定義如下:

          // 文件: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 宏的各個(gè)參數(shù)的作用:

          • pf:協(xié)議類型,就是 nf_hooks 數(shù)組的第一個(gè)維度,如 IPv4 協(xié)議就是 PF_INET

          • hook:要調(diào)用哪一條鏈(掛載點(diǎn))上的鉤子函數(shù),如 NF_IP_PRE_ROUTING

          • indev:接收數(shù)據(jù)包的設(shè)備對(duì)象。

          • outdev:發(fā)送數(shù)據(jù)包的設(shè)備對(duì)象。

          • okfn:當(dāng)鏈上的所有鉤子函數(shù)都處理完成,將會(huì)調(diào)用此函數(shù)繼續(xù)對(duì)數(shù)據(jù)包進(jìn)行處理。

          NF_HOOK 宏的實(shí)現(xiàn)也比較簡(jiǎn)單,首先判斷一下鉤子函數(shù)鏈表是否為空,如果是空的話,就直接調(diào)用 okfn 函數(shù)來(lái)處理數(shù)據(jù)包,否則就調(diào)用 nf_hook_slow 函數(shù)來(lái)處理數(shù)據(jù)包。我們來(lái)看看 nf_hook_slow 函數(shù)的實(shí)現(xiàn):

          // 文件: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]; // 獲取要調(diào)用的鉤子函數(shù)鏈表
          // 遍歷鉤子函數(shù)鏈表,并且調(diào)用鉤子函數(shù)對(duì)數(shù)據(jù)包進(jìn)行處理 verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn); ... // 如果處理結(jié)果為 NF_ACCEPT, 表示數(shù)據(jù)包通過(guò)所有鉤子函數(shù)的處理, 那么就調(diào)用 okfn 函數(shù)繼續(xù)處理數(shù)據(jù)包 // 如果處理結(jié)果為 NF_DROP, 表示數(shù)據(jù)包被拒絕, 應(yīng)該丟棄此數(shù)據(jù)包 switch (verdict) { case NF_ACCEPT: ret = okfn(skb); break; case NF_DROP: kfree_skb(skb); ret = -EPERM; break; }
          return ret;}

          nf_hook_slow 函數(shù)的實(shí)現(xiàn)也比較簡(jiǎn)單,過(guò)程如下:

          • 首先調(diào)用 nf_iterate 函數(shù)來(lái)遍歷鉤子函數(shù)鏈表,并調(diào)用鏈表上的鉤子函數(shù)來(lái)處理數(shù)據(jù)包。

          • 如果處理結(jié)果為 NF_ACCEPT,表示數(shù)據(jù)包通過(guò)所有鉤子函數(shù)的處理, 那么就調(diào)用 okfn 函數(shù)繼續(xù)處理數(shù)據(jù)包。

          • 如果處理結(jié)果為 NF_DROP,表示數(shù)據(jù)包沒(méi)有通過(guò)鉤子函數(shù)的處理,應(yīng)該丟棄此數(shù)據(jù)包。

          既然 Netfilter 是通過(guò)調(diào)用 NF_HOOK 宏來(lái)調(diào)用鉤子函數(shù)鏈表上的鉤子函數(shù),那么內(nèi)核在什么地方調(diào)用這個(gè)宏呢?

          比如數(shù)據(jù)包進(jìn)入 IPv4 協(xié)議層的處理函數(shù) ip_rcv 函數(shù)中就調(diào)用了 NF_HOOK 宏來(lái)處理數(shù)據(jù)包,代碼如下:

          // 文件: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 函數(shù)中調(diào)用了 NF_HOOK 宏來(lái)處理輸入的數(shù)據(jù)包,其調(diào)用的鉤子函數(shù)鏈(掛載點(diǎn))為 NF_IP_PRE_ROUTING。而 okfn 設(shè)置為 ip_rcv_finish,也就是說(shuō),當(dāng) NF_IP_PRE_ROUTING 鏈上的所有鉤子函數(shù)都成功對(duì)數(shù)據(jù)包進(jìn)行處理后,將會(huì)調(diào)用 ip_rcv_finish 函數(shù)來(lái)繼續(xù)對(duì)數(shù)據(jù)包進(jìn)行處理。

          六、總結(jié)

          本文主要介紹了 Netfilter 的實(shí)現(xiàn),因?yàn)?Netfilter 是 Linux 網(wǎng)絡(luò)數(shù)據(jù)包過(guò)濾的框架,而 iptables 就是建立在 Netfilter 之上的。所以,先了解 Netfilter 的實(shí)現(xiàn)對(duì)分析 iptables 的實(shí)現(xiàn)有非常大的幫助。

          而在下一章中,我們將會(huì)繼續(xù)分析 iptables 的實(shí)現(xiàn)。



          瀏覽 74
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  东方AV在线观看 | 国产一区二区大鸡巴操女女在线视频91 | 日本不卡视频在线 | 色婷婷国产在线视频 | 一级片在线免费观看 |