吳 華,鄧 波
(中國電子科技集團公司第三十研究所,四川 成都 610041)
隨著互聯(lián)網(wǎng)應用日益普及,越來越多的電子設備可以接入互聯(lián)網(wǎng),導致可用IPv4 地址資源愈發(fā)緊缺。為解決這一難題,本文引入了網(wǎng)絡地址轉(zhuǎn)換技術[1](Network Address Translation,NAT),讓原本無法上網(wǎng)并且使用內(nèi)部因特網(wǎng)互聯(lián)協(xié)議(Internet Protocol,IP)地址的電子設備可以成功連接互聯(lián)網(wǎng)。
Linux 操作系統(tǒng)是一個開源的操作系統(tǒng),具有高效性、靈活性和良好的網(wǎng)絡性能等特點,大量應用于各類電子設備。Netfilter 是Linux 3.10 內(nèi)核的一個子系統(tǒng),可以完成數(shù)據(jù)包過濾、連接跟蹤、地址轉(zhuǎn)換等重要功能,是一個結(jié)構合理、功能強大、擴展性強的網(wǎng)絡架構[2-3]。本文將分析Linux 操作系統(tǒng)的Netfilter 框架和NAT 工作原理,并討論其具體的功能實現(xiàn)進。此外,本文基于Netfilter 框架和Linux系統(tǒng),設計了一種新的進程間通信方式ukcomm。
Netfilter 在傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議(Transmission Control protocol/Internet Protocol,TCP/IP)協(xié)議棧中定義了5 個檢查點(hook)和相應的數(shù)據(jù)結(jié)構,規(guī)定了在檢查點上對這些數(shù)據(jù)結(jié)構引用的過程。用戶只需要在檢查點注冊一些處理函數(shù)(鉤子函數(shù)),編寫符合自己需求的過濾規(guī)則,就可對從檢查點獲取的數(shù)據(jù)包進行修改、丟棄或發(fā)送到用戶空間等處理[4]。
Linux 系統(tǒng)的Netfilter 部署于內(nèi)核態(tài),它在TCP/IP 協(xié)議棧的數(shù)據(jù)轉(zhuǎn)發(fā)/處理流程中提供了5 個鉤子函數(shù)掛接點[5],如圖1中5個虛線橢圓框圖所示。
圖1 Netfilter 框架流程
這5 個鉤子函數(shù)掛接點具體功能如下文所述。
(1)PRE_ROUTING:位于被路由代碼處理之前,因此所有收到的包在進一步接受任何處理之前都必須經(jīng)過該鉤子函數(shù)進行處理,可實現(xiàn)非法網(wǎng)絡包的快速丟棄。
(2)LOCAL_IN:所有地址指向本網(wǎng)卡的傳入包都需要經(jīng)過該鉤子函數(shù)的處理,在此,iptables模塊提供的INPUT 規(guī)則列表來篩選傳入的數(shù)據(jù)包。
(3)FORWARD:所有需要在內(nèi)外網(wǎng)接口、不同網(wǎng)卡間轉(zhuǎn)發(fā)的報文都需經(jīng)過該鉤子函數(shù)的處理。
(4)LOCAL_OUT:本網(wǎng)卡發(fā)送的報文首先經(jīng)過該鉤子函數(shù)掛接點進行處理,在此,iptables 模塊提供OUTPUT 規(guī)則列表來篩選外發(fā)的數(shù)據(jù)包。
(5)POST_ROUTING:是在所有外發(fā)包通過網(wǎng)絡離開本網(wǎng)卡之前訪問它們的最后機會,可以對本單元主動發(fā)送或者轉(zhuǎn)發(fā)的報文進行最后的過濾/NAT 等處理。
網(wǎng)絡接口驅(qū)動收到數(shù)據(jù)包后,會先進行循環(huán)冗余校核(Cyclic Redundancy Check,CRC)校驗和報文合法性檢測;通過檢測的數(shù)據(jù)包會先進入PRE_ROUTING 檢查點,由協(xié)議棧查詢路由決定數(shù)據(jù)包是發(fā)送到本地處理還是轉(zhuǎn)發(fā)給其他網(wǎng)絡設備;發(fā)送到本地處理的數(shù)據(jù)包將進入LOCAL_IN 檢查點;需要轉(zhuǎn)發(fā)的數(shù)據(jù)包將進入FORWARD 檢查點,然后通過POST_ROUTING 檢查點后由網(wǎng)絡接口驅(qū)動輸出;而需要本地處理的數(shù)據(jù)包將繼續(xù)通過LOCAL_OUT檢查點和POST_ROUTING 檢查點后進入本地網(wǎng)絡處理[6-8]。
每一個檢查點注冊的鉤子函數(shù),都必須返回一個值來通知內(nèi)核,由內(nèi)核進行相應的處理。鉤子函數(shù)的返回值定義如下文所述。
(1)NF_DROP:數(shù)據(jù)包丟棄,內(nèi)核釋放為他分配的資源;
(2)NF_ACCEPT:接收數(shù)據(jù)包,內(nèi)核繼續(xù)傳送報文;
(3)NF_STOLEN:數(shù)據(jù)包由鉤子函數(shù)處理,內(nèi)核不再繼續(xù)傳送;
(4)NF_QUEUE:數(shù)據(jù)包發(fā)送到用戶程序處理,內(nèi)核不再進行操作;
(5)NF_REPEAT:再次調(diào)用該鉤子函數(shù)。
NAT 技術是指替換IP 報文頭部的地址信息。該技術通常部署在網(wǎng)絡出口位置,實現(xiàn)私有IP 地址與公網(wǎng)IP 地址間的轉(zhuǎn)換[9]。
根據(jù)開放式系統(tǒng)互聯(lián)參考模型(Open System Interconnect Reference Model,OSI)的分層結(jié)構,網(wǎng)絡層將數(shù)據(jù)封裝成數(shù)據(jù)包時,包含兩個重要的信息:源IP 地址(發(fā)送數(shù)據(jù)包的網(wǎng)絡設備接口IP 地址)及目的IP 地址(接收數(shù)據(jù)包的網(wǎng)絡設備接口IP 地址)[10]。具有NAT 功能的網(wǎng)絡設備收到數(shù)據(jù)包后,按既定的規(guī)則對IP 數(shù)據(jù)包中的源IP 地址或目的IP地址進行轉(zhuǎn)換,然后將數(shù)據(jù)包轉(zhuǎn)發(fā)至外網(wǎng)或內(nèi)網(wǎng)。
Linux 系統(tǒng)支持模塊化機制,因此可以利用可加載模塊對Netfilter 進行擴展[11]。
NAT 地址轉(zhuǎn)換模塊就是編寫為一個內(nèi)核態(tài)模塊程序,程序編譯成ko 文件格式,通過Linux 系統(tǒng)自帶的insmod 和rmmod 命令動態(tài)地加載進內(nèi)核或從內(nèi)核卸載。
NAT 地址轉(zhuǎn)換模塊向上接收應用程序下發(fā)的NAT 地址轉(zhuǎn)換指示,向下依賴于Linux 操作系統(tǒng)提供的Netfilter 實現(xiàn)報文獲取。應用程序與NAT 地址轉(zhuǎn)換模塊之間的通信通過新設計的ukComm 實現(xiàn)。如圖2 所示。
圖2 NAT 功能模塊工作位置
Linux 系統(tǒng)中用戶態(tài)和內(nèi)核態(tài)之間的通信方式主要包括系統(tǒng)調(diào)用、文件系統(tǒng)和NetLink 套接字等方式[12-13]。然而,在Linux 下要實現(xiàn)用戶態(tài)程序調(diào)用設備中自開發(fā)內(nèi)核模塊的某種功能,不可能直接采用系統(tǒng)調(diào)用方式;但如果基于Netlink 這種異步通信方式又比較復雜;此外,如果每個內(nèi)核模塊開發(fā)自己的為字符設備驅(qū)動,則代碼非常冗余。為此,特開發(fā)ukComm 模塊,為所有需要通過同步方式調(diào)用內(nèi)核態(tài)模塊功能的用戶態(tài)/內(nèi)核態(tài)程序開發(fā)一套公用的調(diào)用接口。
ukComm 模塊基于偽字符設備驅(qū)動,通過ioctl實現(xiàn)用戶態(tài)到內(nèi)核態(tài)的統(tǒng)一同步調(diào)用接口,由用戶態(tài)與內(nèi)核態(tài)兩個部分組成,軟件架構如圖3 所示。
圖3 ukComm 軟件模塊架構
ukComm 用戶態(tài):為用戶態(tài)程序提供統(tǒng)一調(diào)用接口,接口形式為int uk_ioctl(int cmd,char*buf),通過不同的cmd 編碼即可實現(xiàn)調(diào)用內(nèi)核態(tài)的不同“函數(shù)”,而buf 用于實現(xiàn)信息的讀取或者寫入。
ukComm 內(nèi)核態(tài):為內(nèi)核態(tài)程序提供統(tǒng)一調(diào)用接口,接口形式為int uk_ioctl_reg (int cmd,ioctl_func func),內(nèi)核態(tài)程序通過該接口注冊對應某命令(cmd)的處理函數(shù)。
ukComm 通信處理流程如圖4 所示。
圖4 ukComm 通信處理流程
在系統(tǒng)初始化過程中,各個支持通過ukComm向用戶態(tài)提供“函數(shù)調(diào)用”接口的內(nèi)核模塊調(diào)用uk_ioctl_reg 函數(shù)到ukComm 內(nèi)核態(tài)模塊注冊自己所支持的ioctl 命令以及所對應處理函數(shù)。隨后系統(tǒng)運行中,用戶態(tài)的程序通過uk_ioctl 調(diào)用內(nèi)核態(tài)程序提供的“函數(shù)”,其總體處理流程如下:
(1)ukComm 用戶態(tài)程序打開ukComm 內(nèi)核態(tài)模塊提供的偽字符設備,如果未能成功打開該偽字符設備則返回失敗,否則獲取到文件句柄,繼續(xù)下一步處理;
(2)ukComm 用戶態(tài)程序調(diào)用ioctl 函數(shù),傳入基于上述步驟中得到的文件句柄、用戶程序給出的命令(cmd)和參數(shù)信息(buf);
(3)ukComm 內(nèi)核態(tài)程序基于cmd 查找對應該處理函數(shù),如果查詢不到則返回失敗,否則調(diào)用該處理函數(shù),該處理函數(shù)的返回值作為本次ioctl 的返回值;
(4)ukComm 用戶態(tài)程序關閉本次打開偽字符設備的文件句柄,返回ioctl 函數(shù)的返回值。
NAT 地址轉(zhuǎn)換模塊通過ukComm 接收應用程序的指示,將應用程序下發(fā)的地址轉(zhuǎn)換映射關系保存在一個數(shù)組nat_addr_maps[]中,其結(jié)構如下:
利 用Netfilter 可擴展 的框架,在PRE_ROUTING 檢查點可以對除本地發(fā)出的數(shù)據(jù)包外的所有數(shù)據(jù)進行處理;在POST_ROUTING 檢查點對本地發(fā)出的報文進行處理。
本文對來自內(nèi)網(wǎng)的數(shù)據(jù),在POST_ROUTING檢查點創(chuàng)建鉤子函數(shù)對源地址進行轉(zhuǎn)換;對來自外網(wǎng)的數(shù)據(jù),在PRE_ROUTING 檢查點創(chuàng)建鉤子函數(shù)對目的地址進行轉(zhuǎn)換。
Netfilter 提供了非常簡單的接口函數(shù),編程人員可以非常方便地將自己寫的鉤子函數(shù)添加到Netfilter 中而被其調(diào)用。Netfilter 提供了int nf_register_hook(struct nf_hook_ops *ops)接口函數(shù)來在某一個檢查點注冊一個鉤子函數(shù),ops 只是一個數(shù)據(jù)結(jié)構。本文所用的兩個檢查點鉤子函數(shù)的注冊處理如下:
內(nèi)核模塊的退出處理可以調(diào)用nf_unregister_ hook(&nfInputHook) 和nf_unregister_hook(&nfOutput Hook)完成兩個鉤子函數(shù)的卸載。
以nf_hookfn 函數(shù)為模塊編寫鉤子函數(shù)input_hook_func 和output_hook_func,nf_hookfn 函數(shù)的原型為:
nf_hookfn 函數(shù)的5 個參數(shù)由NF_HOOK 宏進行傳遞:第1 個參數(shù)用于指定注冊鉤子函數(shù)的檢查點;第2 個參數(shù)用于指向一個sk_buff 數(shù)據(jù)結(jié)構[14-16],即數(shù)據(jù)包的地址;第3 個參數(shù)和第4 個參數(shù)用于描述網(wǎng)絡接口,參數(shù)in 用來描述數(shù)據(jù)包到達的接口,參數(shù)out 用來描述數(shù)據(jù)包離開的接口。通常情況下,參數(shù)in 用于PRE_ROUTING 檢查點的鉤子函數(shù),參數(shù)out 用于POST_ROUTING 檢查點的鉤子函數(shù),兩個參數(shù)中只有一個被提供。第5 個參數(shù)函數(shù)okfn 是當對應檢查點的鉤子函數(shù)注冊為空時,Netfilter 調(diào)用的處理函數(shù),也是鉤子函數(shù)返回NF_ACCEPT 時Netfilter 調(diào)用的處理函數(shù)。
鉤子函數(shù)的具體實現(xiàn)如下:
其中find_innet_addr 和find_outnet_addr 都是從地址轉(zhuǎn)換映射表nat_addr_maps 中查找對應的轉(zhuǎn)換IP,若映射表中為查詢到對應的轉(zhuǎn)換IP 則無需進行轉(zhuǎn)換,直接將輸入的參數(shù)返回。
本文通過分析Netfilter 的框架和Linux 系統(tǒng)可動態(tài)加載的內(nèi)核模塊機制,利用基于偽字符設備驅(qū)動設計的ukComm 模塊,實現(xiàn)了一個NAT 地址轉(zhuǎn)換模塊。該模塊可根據(jù)用戶下發(fā)的策略,進行源地址轉(zhuǎn)換和目的地址轉(zhuǎn)換,有助于緩解IP 地址不足的問題,還能有效避免來自網(wǎng)絡外部的攻擊,達到隱藏并保護網(wǎng)絡內(nèi)部IP 的目的。此外,NAT 功能模塊采用Netfilter 框架,具有良好的代碼結(jié)構,易于維護和擴展,運行在Linux 內(nèi)核態(tài),所以運行非常高效。