(一)洞悉linux下的Netfilter&iptables:什麼是Netfilter?

来源:https://www.cnblogs.com/linhaostudy/archive/2019/02/08/10355957.html
-Advertisement-
Play Games

本人研究linux的防火牆系統也有一段時間了,由於近來涉及到的工作比較紛雜,久而久之怕生熟了。趁有時間,好好把這方面的東西總結一番。一來是給自己做個沉澱,二來也歡迎這方面比較牛的前輩給小弟予以指點,共同學習,共同進步。 能在CU上混的人絕非等閑之輩。因此,小弟這裡說明一下:本系列博文主要側重於分析N ...


本人研究linux的防火牆系統也有一段時間了,由於近來涉及到的工作比較紛雜,久而久之怕生熟了。趁有時間,好好把這方面的東西總結一番。一來是給自己做個沉澱,二來也歡迎這方面比較牛的前輩給小弟予以指點,共同學習,共同進步。

能在CU上混的人絕非等閑之輩。因此,小弟這裡說明一下:本系列博文主要側重於分析Netfilter的實現機制,原理和設計思想層面的東西,同時從用戶態的iptables到內核態的Netfilter其交互過程和通信手段等。至於iptables的入門用法方面的東西,網上隨便一搜羅就有一大堆,我這裡不浪費筆墨了。

很多人在接觸iptables之後就會這麼一種感覺:我通過iptables命令配下去的每一條規則,到底是如何生效的呢?內核又是怎麼去執行這些規則匹配呢?如果iptables不能滿足我當下的需求,那麼我是否可以去對其進行擴展呢?這些問題,都是我在接下來的博文中一一和大家分享的話題。這裡需要指出:因為Netfilter與IP協議棧是無縫契合的,所以如果你要是有協議棧方面的基礎,在閱讀本文時一定會感覺輕車熟路。當然,如果沒有也沒關係,因為我會在關鍵點就協議棧的入門知識給大家做個普及。只是普及哦,不會詳細深入下去的,因為涉及的東西太多了,目前我還正在研究摸索當中呢。好了,廢話不多說,進入正題。

備註:我研究的內核版本是2.6.21,iptables的版本1.4.0。

什麼是Netfilter?

為了說明這個問題,首先看一個網路通信的基本模型:

image

在數據的發送過程中,從上至下依次是“加頭”的過程,每到達一層數據就被會加上該層的頭部;與此同時,接受數據方就是個“剝頭”的過程,從網卡收上包來之後,在往協議棧的上層傳遞過程中依次剝去每層的頭部,最終到達用戶那兒的就是裸數據了。

那麼,“棧”模式底層機制基本就是像下麵這個樣子:

image

對於發送的每個數據包,首先也有一個路由判決,以確定該包是從哪個介面出去,然後經過“D”點,最後也是順著“E”點將該包發送出去。

協議棧那五個關鍵點A,B,C,D和E就是我們Netfilter大展拳腳的地方了。

Netfilter是Linux 2.4.x引入的一個子系統,它作為一個通用的、抽象的框架,提供一整套的hook函數的管理機制,使得諸如數據包過濾、網路地址轉換(NAT)和基於協議類型的連接跟蹤成為了可能。Netfilter在內核中位置如下圖所示:

image

這幅圖,很直觀的反應了用戶空間的iptables和內核空間的基於Netfilter的ip_tables模塊之間的關係和其通訊方式,以及Netfilter在這其中所扮演的角色。

回到前面討論的關於協議棧那五個關鍵點“ABCDE”上來。Netfilter在netfilter_ipv4.h中將這個五個點重新命了個名,如下圖所示,意思我就不再解釋了,貓叫咪咪而已:

image

在每個關鍵點上,有很多已經按照優先順序預先註冊了的回調函數(後面再說這些函數是什麼,乾什麼用的。有些人喜歡把這些函數稱為“鉤子函數”,說的是同一個東西)埋伏在這些關鍵點,形成了一條鏈。對於每個到來的數據包會依次被那些回調函數“調戲”一番再視情況是將其放行,丟棄還是怎麼滴。但是無論如何,這些回調函數最後必須向Netfilter報告一下該數據包的死活情況,因為畢竟每個數據包都是Netfilter從人家協議棧那兒借調過來給兄弟們Happy的,別個再怎麼滴也總得“活要見人,死要見屍”吧。每個鉤子函數最後必須向Netfilter框架返回下列幾個值其中之一:

  • NF_ACCEPT 繼續正常傳輸數據報。這個返回值告訴 Netfilter:到目前為止,該數據包還是被接受的並且該數據包應當被遞交到網路協議棧的下一個階段。
  • NF_DROP 丟棄該數據報,不再傳輸。
  • NF_STOLEN 模塊接管該數據報,告訴Netfilter“忘掉”該數據報。該回調函數將從此開始對數據包的處理,並且Netfilter應當放棄對該數據包做任何的處理。但是,這並不意味著該數據包的資源已經被釋放。這個數據包以及它獨自的sk_buff數據結構仍然有效,只是回調函數從Netfilter 獲取了該數據包的所有權。
  • NF_QUEUE 對該數據報進行排隊(通常用於將數據報給用戶空間的進程進行處理)
  • NF_REPEAT 再次調用該回調函數,應當謹慎使用這個值,以免造成死迴圈。

為了讓我們顯得更專業些,我們開始做些約定:上面提到的五個關鍵點後面我們就叫它們為hook點,每個hook點所註冊的那些回調函數都將其稱為hook函數。

image

對於每種類型的協議,數據包都會依次按照hook點的方向進行傳輸,每個hook點上Netfilter又按照優先順序掛了很多hook函數。這些hook函數就是用來處理數據包用的。

Netfilter使用NF_HOOK(include/linux/netfilter.h)巨集在協議棧內部切入到Netfilter框架中。相比於2.4版本,2.6版內核在該巨集的定義上顯得更加靈活一些,定義如下:

#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \

         NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)

關於巨集NF_HOOK各個參數的解釋說明:

  1. pf:協議族名,Netfilter架構同樣可以用於IP層之外,因此這個變數還可以有諸如PF_INET6,PF_DECnet等名字。
  2. hook:HOOK點的名字,對於IP層,就是取上面的五個值;
  3. skb:不解釋;
  4. indev:數據包進來的設備,以struct net_device結構表示;
  5. outdev:數據包出去的設備,以struct net_device結構表示;
    (後面可以看到,以上五個參數將傳遞給nf_register_hook中註冊的處理函數。)
  6. okfn:是個函數指針,當所有的該HOOK點的所有登記函數調用完後,轉而走此流程。
    而NF_HOOK_THRESH又是一個巨集:
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)                \

({int __ret;                                                                                \

if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, thresh, 1)) == 1)\

         __ret = (okfn)(skb);                                                       \

__ret;})

我們發現NF_HOOK_THRESH巨集只增加了一個thresh參數,這個參數就是用來指定通過該巨集去遍歷鉤子函數時的優先順序,同時,該巨集內部又調用了nf_hook_thresh函數:


static inline int nf_hook_thresh(int pf, unsigned int hook,

                            struct sk_buff **pskb,

                            struct net_device *indev,

                            struct net_device *outdev,

                            int (*okfn)(struct sk_buff *), int thresh,

                            int cond)

{

if (!cond) 

return 1;

#ifndef CONFIG_NETFILTER_DEBUG

if (list_empty(&nf_hooks[pf][hook]))

         return 1;

#endif

return nf_hook_slow(pf, hook, pskb, indev, outdev, okfn, thresh);

}

這個函數又只增加了一個參數cond,該參數為0則放棄遍歷,並且也不執行okfn函數;為1則執行nf_hook_slow去完成鉤子函數okfn的順序遍歷(優先順序從小到大依次執行)。

在net/netfilter/core.h文件中定義了一個二維的結構體數組,用來存儲不同協議棧鉤子點的回調處理函數。

struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];

其中,行數NPROTO為32,即目前內核所支持的最大協議簇;列數NF_MAX_HOOKS為掛載點的個數,目前在2.6內核中該值為8。nf_hooks數組的最終結構如下圖所示。

image

在include/linux/socket.h中IP協議AF_INET(PF_INET)的序號為2,因此我們就可以得到TCP/IP協議族的鉤子函數掛載點為:

PRE_ROUTING:     nf_hooks[2][0]

LOCAL_IN:        nf_hooks[2][1]

FORWARD:      nf_hooks[2][2]

LOCAL_OUT:      nf_hooks[2][3]

POST_ROUTING:          nf_hooks[2][4]

同時我們看到,在2.6內核的IP協議棧里,從協議棧正常的流程切入到Netfilter框架中,然後順序、依次去調用每個HOOK點所有的鉤子函數的相關操作有如下幾處:

  1. net/ipv4/ip_input.c里的ip_rcv函數。該函數主要用來處理網路層的IP報文的入口函數,它到Netfilter框架的切入點為:
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)

根據前面的理解,這句代碼意義已經很直觀明確了。那就是:如果協議棧當前收到了一個IP報文(PF_INET),那麼就把這個報文傳到Netfilter的NF_IP_PRE_ROUTING過濾點,去檢查[R]在那個過濾點(nf_hooks[2][0])是否已經有人註冊了相關的用於處理數據包的鉤子函數。如果有,則挨個去遍歷鏈表nf_hooks[2][0]去尋找匹配的match和相應的target,根據返回到Netfilter框架中的值來進一步決定該如何處理該數據包(由鉤子模塊處理還是交由ip_rcv_finish函數繼續處理)。

[R]:剛纔說到所謂的“檢查”。其核心就是nf_hook_slow()函數。該函數本質上做的事情很簡單,根據優先順序查找雙向鏈表nf_hooks[][],找到對應的回調函數來處理數據包:

struct list_head **i;

list_for_each_continue_rcu(*i, head) {

struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;

if (hook_thresh > elem->priority)

                  continue;

         verdict = elem->hook(hook, skb, indev, outdev, okfn);

         if (verdict != NF_ACCEPT) { … … }

    return NF_ACCEPT;

}

上面的代碼是net/netfilter/core.c中的nf_iterate()函數的部分核心代碼,該函數被nf_hook_slow函數所調用,然後根據其返回值做進一步處理。

  1. net/ipv4/ip_forward.c中的ip_forward函數,它的切入點為:
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,ip_forward_finish);

在經過路由抉擇後,所有需要本機轉發的報文都會交由ip_forward函數進行處理。這裡,該函數由NF_IP_FOWARD過濾點切入到Netfilter框架,在nf_hooks[2][2]過濾點執行匹配查找。最後根據返回值來確定ip_forward_finish函數的執行情況。

  1. net/ipv4/ip_output.c中的ip_output函數,它切入Netfilter框架的形式為:
NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,ip_finish_output,

                                !(IPCB(skb)->flags & IPSKB_REROUTED));

如果需要陷入Netfilter框架則數據包會在nf_hooks[2][4]過濾點去進行匹配查找。

  1. 還是在net/ipv4/ip_input.c中的ip_local_deliver函數。該函數處理所有目的地址是本機的數據包,其切入函數為:
NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish);

發給本機的數據包,首先全部會去nf_hooks[2][1]過濾點上檢測是否有相關數據包的回調處理函數,如果有則執行匹配和動作,最後根據返回值執行ip_local_deliver_finish函數。

  1. net/ipv4/ip_output.c中的ip_push_pending_frames函數。該函數是將IP分片重組成完整的IP報文,然後發送出去。進入Netfilter框架的切入點為:
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);

對於所有從本機發出去的報文都會首先去Netfilter的nf_hooks[2][3]過濾點去過濾。一般情況下來來說,不管是路由器還是PC中端,很少有人限制自己機器發出去的報文。因為這樣做的潛在風險也是顯而易見的,往往會因為一些不恰當的設置導致某些服務失效,所以在這個過濾點上攔截數據包的情況非常少。當然也不排除真的有特殊需求的情況。

小節:整個Linux內核中Netfilter框架的HOOK機制可以概括如下:

在數據包流經內核協議棧的整個過程中,在一些已預定義的關鍵點上PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT和POST_ROUTING會根據數據包的協議簇PF_INET到這些關鍵點去查找是否註冊有鉤子函數。如果沒有,則直接返回okfn函數指針所指向的函數繼續走協議棧;如果有,則調用nf_hook_slow函數,從而進入到Netfilter框架中去進一步調用已註冊在該過濾點下的鉤子函數,再根據其返回值來確定是否繼續執行由函數指針okfn所指向的函數。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.概述 在asp.net core中,Host主機負責應用程式啟動和生存期管理。host主機包括Web 主機(IWebHostBuilder)和通用主機(IHostBuilder)。Web 主機是適用於托管 Web 應用;通用主機(ASP.NET Core 2.1 或更高版本)是適用於托管非 We ...
  • 1.進入VS2010,新建項目—>Visual C#—>Web—>ASP.NET空Web應用程式,如圖所示: 2.在解決方案處右擊—>新建項目—>Windows—>類庫,分別創建三層架構,Model(實體層)、Dll(邏輯層)、DAL(數據層),在每一層中創建如下圖。 選中Model類庫--》Shi ...
  • 在Windows系統上,通過xshell連接Linux系統。 第一種使用方式:從Linux系統上下載文件到Windows系統。 準備工作: $ sudo apt-get install lrzsz 安裝完畢後,具體操作命令是: sz 【file】 >可將伺服器的文件下載到本地 從xshell環境下登 ...
  • 1.安裝Docker 操作系統:Ubuntu 18.04 原本虛擬機安裝的CentOS因為內核版本太低,所以重新安裝了一個最新的Ubuntu。 使用Root用戶,不是的話自己加sudo首碼。官方腳本安裝的話可以,使用微軟的鏡像加速 如果腳本安裝不成功的話,可以手動安裝,其實就是上面那個sh里的內容 ...
  • 將本地電腦添加到域或工作組,可通過Add-Computer命令操作,具體信息如下:語法:Add-Computer [-DomainName] [-ComputerName ] [-Confirm] -Credential [-Force] [-Loc alCredential ] [-NewNam... ...
  • 恢復內容開始 Python的作者,Guido von Rossum,確實是荷蘭人。1982年,Guido從阿姆斯特丹大學(University of Amsterdam)獲得了數學和電腦碩士學位。然而,儘管他算得上是一位數學家,但他更加享受電腦帶來的樂趣。用他的話說,儘管擁有數學和電腦雙料資質 ...
  • 上章節我們介紹瞭如何通過圖形化界面實現用戶加域操作,本章節為大家簡單介紹如何通過netdom join命令實現電腦加域操作。具體信息如下:將工作站或成員伺服器加入域。將電腦加入域的行為會為域中的電腦創建一個帳戶(如果該帳戶尚不存在)。Netdom是一個內置於Windows Server 200... ...
  • 要使用域環境,您必須將電腦加入域。只有加入域的電腦才能使用域資源。在連接期間,將在域中創建一個電腦帳戶,以便將電腦作為成員進行身份驗證。前邊兩節我們寫到Windows Server 2016-客戶端加域埠彙總及Windows Server 2016-客戶端加域準備工作,那接下來三章我們簡單... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...