吳 華,鄧 波
(中國電子科技集團公司第三十研究所,四川 成都 610041)
隨著互聯網應用日益普及,越來越多的電子設備可以接入互聯網,導致可用IPv4 地址資源愈發緊缺。為解決這一難題,本文引入了網絡地址轉換技術[1](Network Address Translation,NAT),讓原本無法上網并且使用內部因特網互聯協議(Internet Protocol,IP)地址的電子設備可以成功連接互聯網。
Linux 操作系統是一個開源的操作系統,具有高效性、靈活性和良好的網絡性能等特點,大量應用于各類電子設備。Netfilter 是Linux 3.10 內核的一個子系統,可以完成數據包過濾、連接跟蹤、地址轉換等重要功能,是一個結構合理、功能強大、擴展性強的網絡架構[2-3]。本文將分析Linux 操作系統的Netfilter 框架和NAT 工作原理,并討論其具體的功能實現進。此外,本文基于Netfilter 框架和Linux系統,設計了一種新的進程間通信方式ukcomm。
Netfilter 在傳輸控制協議/因特網互聯協議(Transmission Control protocol/Internet Protocol,TCP/IP)協議棧中定義了5 個檢查點(hook)和相應的數據結構,規定了在檢查點上對這些數據結構引用的過程。用戶只需要在檢查點注冊一些處理函數(鉤子函數),編寫符合自己需求的過濾規則,就可對從檢查點獲取的數據包進行修改、丟棄或發送到用戶空間等處理[4]。
Linux 系統的Netfilter 部署于內核態,它在TCP/IP 協議棧的數據轉發/處理流程中提供了5 個鉤子函數掛接點[5],如圖1中5個虛線橢圓框圖所示。

圖1 Netfilter 框架流程
這5 個鉤子函數掛接點具體功能如下文所述。
(1)PRE_ROUTING:位于被路由代碼處理之前,因此所有收到的包在進一步接受任何處理之前都必須經過該鉤子函數進行處理,可實現非法網絡包的快速丟棄。
(2)LOCAL_IN:所有地址指向本網卡的傳入包都需要經過該鉤子函數的處理,在此,iptables模塊提供的INPUT 規則列表來篩選傳入的數據包。
(3)FORWARD:所有需要在內外網接口、不同網卡間轉發的報文都需經過該鉤子函數的處理。
(4)LOCAL_OUT:本網卡發送的報文首先經過該鉤子函數掛接點進行處理,在此,iptables 模塊提供OUTPUT 規則列表來篩選外發的數據包。
(5)POST_ROUTING:是在所有外發包通過網絡離開本網卡之前訪問它們的最后機會,可以對本單元主動發送或者轉發的報文進行最后的過濾/NAT 等處理。
網絡接口驅動收到數據包后,會先進行循環冗余校核(Cyclic Redundancy Check,CRC)校驗和報文合法性檢測;通過檢測的數據包會先進入PRE_ROUTING 檢查點,由協議棧查詢路由決定數據包是發送到本地處理還是轉發給其他網絡設備;發送到本地處理的數據包將進入LOCAL_IN 檢查點;需要轉發的數據包將進入FORWARD 檢查點,然后通過POST_ROUTING 檢查點后由網絡接口驅動輸出;而需要本地處理的數據包將繼續通過LOCAL_OUT檢查點和POST_ROUTING 檢查點后進入本地網絡處理[6-8]。
每一個檢查點注冊的鉤子函數,都必須返回一個值來通知內核,由內核進行相應的處理。鉤子函數的返回值定義如下文所述。
(1)NF_DROP:數據包丟棄,內核釋放為他分配的資源;
(2)NF_ACCEPT:接收數據包,內核繼續傳送報文;
(3)NF_STOLEN:數據包由鉤子函數處理,內核不再繼續傳送;
(4)NF_QUEUE:數據包發送到用戶程序處理,內核不再進行操作;
(5)NF_REPEAT:再次調用該鉤子函數。
NAT 技術是指替換IP 報文頭部的地址信息。該技術通常部署在網絡出口位置,實現私有IP 地址與公網IP 地址間的轉換[9]。
根據開放式系統互聯參考模型(Open System Interconnect Reference Model,OSI)的分層結構,網絡層將數據封裝成數據包時,包含兩個重要的信息:源IP 地址(發送數據包的網絡設備接口IP 地址)及目的IP 地址(接收數據包的網絡設備接口IP 地址)[10]。具有NAT 功能的網絡設備收到數據包后,按既定的規則對IP 數據包中的源IP 地址或目的IP地址進行轉換,然后將數據包轉發至外網或內網。
Linux 系統支持模塊化機制,因此可以利用可加載模塊對Netfilter 進行擴展[11]。
NAT 地址轉換模塊就是編寫為一個內核態模塊程序,程序編譯成ko 文件格式,通過Linux 系統自帶的insmod 和rmmod 命令動態地加載進內核或從內核卸載。
NAT 地址轉換模塊向上接收應用程序下發的NAT 地址轉換指示,向下依賴于Linux 操作系統提供的Netfilter 實現報文獲取。應用程序與NAT 地址轉換模塊之間的通信通過新設計的ukComm 實現。如圖2 所示。

圖2 NAT 功能模塊工作位置
Linux 系統中用戶態和內核態之間的通信方式主要包括系統調用、文件系統和NetLink 套接字等方式[12-13]。然而,在Linux 下要實現用戶態程序調用設備中自開發內核模塊的某種功能,不可能直接采用系統調用方式;但如果基于Netlink 這種異步通信方式又比較復雜;此外,如果每個內核模塊開發自己的為字符設備驅動,則代碼非常冗余。為此,特開發ukComm 模塊,為所有需要通過同步方式調用內核態模塊功能的用戶態/內核態程序開發一套公用的調用接口。
ukComm 模塊基于偽字符設備驅動,通過ioctl實現用戶態到內核態的統一同步調用接口,由用戶態與內核態兩個部分組成,軟件架構如圖3 所示。

圖3 ukComm 軟件模塊架構
ukComm 用戶態:為用戶態程序提供統一調用接口,接口形式為int uk_ioctl(int cmd,char*buf),通過不同的cmd 編碼即可實現調用內核態的不同“函數”,而buf 用于實現信息的讀取或者寫入。
ukComm 內核態:為內核態程序提供統一調用接口,接口形式為int uk_ioctl_reg (int cmd,ioctl_func func),內核態程序通過該接口注冊對應某命令(cmd)的處理函數。
ukComm 通信處理流程如圖4 所示。

圖4 ukComm 通信處理流程
在系統初始化過程中,各個支持通過ukComm向用戶態提供“函數調用”接口的內核模塊調用uk_ioctl_reg 函數到ukComm 內核態模塊注冊自己所支持的ioctl 命令以及所對應處理函數。隨后系統運行中,用戶態的程序通過uk_ioctl 調用內核態程序提供的“函數”,其總體處理流程如下:
(1)ukComm 用戶態程序打開ukComm 內核態模塊提供的偽字符設備,如果未能成功打開該偽字符設備則返回失敗,否則獲取到文件句柄,繼續下一步處理;
(2)ukComm 用戶態程序調用ioctl 函數,傳入基于上述步驟中得到的文件句柄、用戶程序給出的命令(cmd)和參數信息(buf);
(3)ukComm 內核態程序基于cmd 查找對應該處理函數,如果查詢不到則返回失敗,否則調用該處理函數,該處理函數的返回值作為本次ioctl 的返回值;
(4)ukComm 用戶態程序關閉本次打開偽字符設備的文件句柄,返回ioctl 函數的返回值。
NAT 地址轉換模塊通過ukComm 接收應用程序的指示,將應用程序下發的地址轉換映射關系保存在一個數組nat_addr_maps[]中,其結構如下:

利 用Netfilter 可擴展 的框架,在PRE_ROUTING 檢查點可以對除本地發出的數據包外的所有數據進行處理;在POST_ROUTING 檢查點對本地發出的報文進行處理。
本文對來自內網的數據,在POST_ROUTING檢查點創建鉤子函數對源地址進行轉換;對來自外網的數據,在PRE_ROUTING 檢查點創建鉤子函數對目的地址進行轉換。
Netfilter 提供了非常簡單的接口函數,編程人員可以非常方便地將自己寫的鉤子函數添加到Netfilter 中而被其調用。Netfilter 提供了int nf_register_hook(struct nf_hook_ops *ops)接口函數來在某一個檢查點注冊一個鉤子函數,ops 只是一個數據結構。本文所用的兩個檢查點鉤子函數的注冊處理如下:


內核模塊的退出處理可以調用nf_unregister_ hook(&nfInputHook) 和nf_unregister_hook(&nfOutput Hook)完成兩個鉤子函數的卸載。
以nf_hookfn 函數為模塊編寫鉤子函數input_hook_func 和output_hook_func,nf_hookfn 函數的原型為:

nf_hookfn 函數的5 個參數由NF_HOOK 宏進行傳遞:第1 個參數用于指定注冊鉤子函數的檢查點;第2 個參數用于指向一個sk_buff 數據結構[14-16],即數據包的地址;第3 個參數和第4 個參數用于描述網絡接口,參數in 用來描述數據包到達的接口,參數out 用來描述數據包離開的接口。通常情況下,參數in 用于PRE_ROUTING 檢查點的鉤子函數,參數out 用于POST_ROUTING 檢查點的鉤子函數,兩個參數中只有一個被提供。第5 個參數函數okfn 是當對應檢查點的鉤子函數注冊為空時,Netfilter 調用的處理函數,也是鉤子函數返回NF_ACCEPT 時Netfilter 調用的處理函數。
鉤子函數的具體實現如下:

其中find_innet_addr 和find_outnet_addr 都是從地址轉換映射表nat_addr_maps 中查找對應的轉換IP,若映射表中為查詢到對應的轉換IP 則無需進行轉換,直接將輸入的參數返回。
本文通過分析Netfilter 的框架和Linux 系統可動態加載的內核模塊機制,利用基于偽字符設備驅動設計的ukComm 模塊,實現了一個NAT 地址轉換模塊。該模塊可根據用戶下發的策略,進行源地址轉換和目的地址轉換,有助于緩解IP 地址不足的問題,還能有效避免來自網絡外部的攻擊,達到隱藏并保護網絡內部IP 的目的。此外,NAT 功能模塊采用Netfilter 框架,具有良好的代碼結構,易于維護和擴展,運行在Linux 內核態,所以運行非常高效。