如何用IP Queue機制編寫使用者態防火牆

2021-07-23 08:18:21 字數 3485 閱讀 7949

一般而言,防火牆都是嵌入在linux核心協議棧的處理過程中,執行在核心位址空間。這就要求開發者深入理解和掌握核心,以及核心協議棧的**。對於普通開發者來說,此要求顯然過於苛刻。為此,linux核心在netfilter框架的基礎上提供了ip queue機制,使得基於使用者態(user mode)的防火牆開發成為可能。

下面詳細闡述ip queue的原理及其程式設計介面。

核心報文處理流程

首先,大致了解一下核心處理資料報文的流程。正確理解這個流程,對於編寫完善的防火牆非常有幫助。

如圖1所示,在核心的ip報文處理過程中,散布了一些hook點(圖中橢圓形的節點),在這些點上,可以掛載外部處理函式,進行定製的報文處理。

圖1 linux核心報文處理流程簡圖

資料報文從圖1左邊「接收」處進入核心協議棧,經過hook點pre_routing後,進行路由選擇。若是發往本機的ip報文,則在經過hook點local_in後提交給本地的上層協議處理。如果是需要**的ip報文,則該報文在依次經過forward和post_routing兩個hook點後,被發往網路介面。

本地輸出的ip報文將先通過hook點local_out,再根據路由選擇,經過hook點post_routing以後,被發往網路介面。

上述這些hook點掛載的處理函式將返回適當的值,告知協議棧應該如何繼續處理當前報文。具體分為以下各值:

nf_drop 丟棄該報文,釋放所有與該報文相關的資源;

nf_accept 接受該報文,並繼續處理;

nf_stolen 該報文已經被hook函式接管,協議棧無須繼續處理;

nf_queue 將該報文傳遞到使用者態去做進一步的處理;

nf_repeat 再次呼叫本hook函式。

當hook處理函式返回nf_queue值時,核心協議棧將通過linux netlink通訊機制把當前報文傳遞到使用者態,由使用者態的防火牆程式進行處理。這樣,只要能夠在相應的hook點上返回nf_queue值,就可以安心地在使用者態使用自己的程式來過濾報文了,這個功能可以由iptables實現。

netlink機制

上面提到,使用ip queue的使用者態防火牆程式是通過netlink機制和核心協議棧進行通訊的。netlink是linux系統特有的、基於socket程式設計介面的通訊機制。

它是乙個面向資料報文的服務,並提供「路由操作(netlink_route)」、「ip queue操作(netlink_firewall)」和「使用者態arp表操作(netlink_arpd)」等通訊協議。在建立ipqueue netlink socket時,將採用如下系統呼叫:

fd = socket(pf_netlink, sock_raw, netlink_firewall);

這裡,pf_netlink指明要建立netlink socket;sock_raw指明採用原始套接字,也可以採用sock_dgram,因為netlink機制的實現並不區分sock_raw和sock_dgram;引數netlink_firewall則指明通訊協議採用ip queue。

既然ip queue是基於netlink的,其訊息格式自然也遵從netlink的規範。netlink訊息由兩部分組成:訊息頭(struct nlmsghdr)和資料負載(data payload)。

訊息頭的定義如下:

struct nlmsghdr;

所有的ip queue訊息都將包含乙個struct nlmsghdr訊息頭,具體的ip queue訊息則包含在netlink訊息的資料負載中。有關netlink訊息格式的詳情可以參見手冊頁netlink(7)。

ip queue程式設計介面

使用ip queue機制的程式必須包含如下的標頭檔案:

#include

在這個標頭檔案中定義了所有ip queue訊息的格式。

ip queue訊息可以分為兩大類:由核心協議棧發給使用者態程序的ip queue訊息和由使用者態程序發給核心的ip queue訊息。

由核心協議棧發給使用者態程序的ip queue訊息(nlmsghdr.nlmsg_type = ipqm_packet),其資料型別為ipq_packet_msg_t,定義如下:

typedef struct ipq_packet_msg ipq_packet_msg_t;

這個資料結構也被稱為「報文的元資料」。核心除可以單獨向使用者程序傳遞「報文的元資料」以外,也可以同時傳遞報文本身。此時,報文本身將儲存在ipq_packet_msg_t資料成員payload開始的地方。

由使用者態程序發給核心的訊息,其資料型別為ipq_peer_msg_t,定義如下:

typedef struct ipq_peer_msg msg;

} ipq_peer_msg_t;

由上述定義可知,這類訊息又分為「模式設定訊息(nlmsghdr.nlmsg_type = ipqm_mode)」和「斷言訊息(nlmsghdr.nlmsg_type = ipqm_verdict)」兩個子類。

「模式設定訊息」的資料型別定義如下:

typedef struct ipq_mode_msg ipq_mode_msg_t;

這裡,請求模式value的值可以是ipq_copy_none、ipq_copy_meta和ipq_copy_packet。當指定請求模式value為ipq_copy_none時,報文將被丟棄;當為ipq_copy_meta時,核心將在其後的報文傳遞中只傳遞「報文的元資料」;當為ipq_copy_packet時,核心將同時傳遞「報文的元資料」和報文本身,報文本身的傳遞長度由ipq_mode_msg_t的另乙個資料成員range指定。ip報文的最大長度為0xffff。

另一子類即「斷言訊息」,其資料型別定義如下:

typedef struct ipq_verdict_msg ipq_verdict_msg_t;

其中,value是使用者態程式回傳給核心的當前報文的處理意見,可以是nf_accept或nf_drop等值。id則是用以區分報文的標識號,即核心傳來的ipq_packet_msg_t結構中的packet_id。當使用者態程式修改了當前報文以後,需要將報文重新傳遞歸核心,此時,新的報文內容必須儲存在payload的開始處,並由data_len指明新報文的長度。

從上述內容可以看出,在整個ip queue的報文傳遞過程中,使用者態程式和核心協議棧之間的互動順序是,首先,使用者態程式利用「模式設定訊息」告訴核心協議棧所請求的報文傳遞模式。

然後,根據這個模式,核心組織好等待傳遞的訊息,通過netlink socket發給使用者態程式。最後,使用者態程式根據自己的防火牆規則,得出該報文的處理意見(可能同時修改當前報文),並回傳給核心。

編譯和執行防火牆

要編譯和執行防火牆程式,執行如下命令:

# gcc o ipqfw ipqfw.c

# ./ipqfw &

ipqfw.c是防火牆的**檔案。

為了在路由器上過濾**的ip報文,需要執行如下命令:

# iptables a forward j queue

這樣,所有經過hook點forward的報文都將被送到使用者態防火牆進行處理,使用者可以自行編寫適合具體情況的防火牆。

如何用vc編寫dll檔案

如何用vc編寫dll檔案 動態連線庫最大的特點就是能節省磁碟空間.當多個程序共享同乙個dll的時候,記憶體中只有乙個dll的 通過對映來使各個程序得以呼叫.1.用vc建立乙個win32 dll 我們利用vc編寫dll有幾種方法.如果用vc建立乙個win32 dll 工程.那這個工程就應該只匯出c 的...

Python 如何用python編寫無限猴子定理

python資料結構與演算法 一書中有無限猴子定理一題,問題如下 蒐集網上 除錯如下 import random num 1000 char list chr c ord a for c in range 26 返回c ord a 對應的ascii碼,a z 新增空格 該列表內的字元一共有27個 t...

如何用linux系統編寫c程式

目錄 toc 本部落格在我學習linux過程將持續更新 1 1 按下快捷鍵開啟終端 ctrl alt t 2 從左側工具欄中開啟終端。1.cd cd指返回當前目錄 如cd desktop program 便是指返回到desktop 桌面 的program 資料夾 2.gedit gedit指開啟某檔...