摘要:對(duì)于普通的網(wǎng)絡(luò)應(yīng)用程序Linux可以通過(guò)用戶層網(wǎng)絡(luò)編程實(shí)現(xiàn),但是對(duì)于一些特殊要求用戶層網(wǎng)絡(luò)編程無(wú)法實(shí)現(xiàn),必須通過(guò)內(nèi)核層網(wǎng)絡(luò)編程。Linux內(nèi)核在內(nèi)核可加載模塊機(jī)制的基礎(chǔ)上在網(wǎng)絡(luò)協(xié)議棧的網(wǎng)絡(luò)層實(shí)現(xiàn)了NetFilter機(jī)制和sk_buff網(wǎng)絡(luò)數(shù)據(jù)包內(nèi)存操作機(jī)制。通過(guò)將三種機(jī)制結(jié)合起來(lái)可以實(shí)現(xiàn)對(duì)流經(jīng)本地主機(jī)的網(wǎng)絡(luò)數(shù)據(jù)包的各種操作。
關(guān)鍵詞:Linux;內(nèi)核可加載模塊;NetFilter;sk_buff
中圖分類號(hào):B82文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1674-7712 (2014) 08-0000-01
Linux網(wǎng)絡(luò)編程包括用戶層網(wǎng)絡(luò)編程和內(nèi)核層網(wǎng)絡(luò)編程,對(duì)于普通的網(wǎng)絡(luò)應(yīng)用程序可以通過(guò)用戶層網(wǎng)絡(luò)編程實(shí)現(xiàn),但是對(duì)于一些特殊要求用戶層網(wǎng)絡(luò)編程無(wú)法實(shí)現(xiàn),必須通過(guò)內(nèi)核層網(wǎng)絡(luò)編程。Linux內(nèi)核為實(shí)現(xiàn)內(nèi)核網(wǎng)絡(luò)編程在內(nèi)核可加載模塊機(jī)制的基礎(chǔ)上,在網(wǎng)絡(luò)協(xié)議棧的網(wǎng)絡(luò)層實(shí)現(xiàn)了NetFilter機(jī)制和sk_buff網(wǎng)絡(luò)數(shù)據(jù)包內(nèi)存操作機(jī)制。通過(guò)內(nèi)核可加載模塊機(jī)制可以將針對(duì)網(wǎng)絡(luò)數(shù)據(jù)包操作的函數(shù)掛載到NetFilter框架的檢查點(diǎn)上,從而實(shí)現(xiàn)對(duì)流經(jīng)本地主機(jī)的網(wǎng)絡(luò)數(shù)據(jù)包的各種操作。
一、Linux內(nèi)核可加載模塊編程
Linux操作系統(tǒng)可以分為用戶空間和內(nèi)核空間,在內(nèi)核空間編寫程序和在用戶空間編寫程序有很大的區(qū)別[11]。典型的用戶空間應(yīng)用程序有一個(gè)main()函數(shù)作為函數(shù)執(zhí)行的入口點(diǎn),而內(nèi)核程序需要一個(gè)初始化函數(shù)和一個(gè)清理函數(shù),在向內(nèi)核空間插入模塊時(shí)調(diào)用初始化函數(shù),卸載模塊時(shí)調(diào)用清理函數(shù)。Linux內(nèi)核編程通常用可加載模塊的方式,與直接編譯內(nèi)核相比可加載內(nèi)核模塊的方便性表現(xiàn)在不用重新編譯內(nèi)核、可動(dòng)態(tài)加載卸載和便于調(diào)試。
內(nèi)核模塊編程的基本架構(gòu)包括五部分:初始化函數(shù)、清除函數(shù)、描述信息聲明、可導(dǎo)出符號(hào)表和加載模塊,其中初始化函數(shù)和清除函數(shù)是不可或缺的部分。初始化函數(shù)在使用insmod命令的時(shí)候自動(dòng)調(diào)用進(jìn)行模塊的初始化,主要是資源申請(qǐng)。清除函數(shù)在使用rmmod命令的時(shí)候自動(dòng)調(diào)用進(jìn)行模塊退出之前的清理工作,主要是狀態(tài)重置和資源釋放。描述信息聲明用來(lái)描述模塊許可證聲明、作者、用途、版本號(hào)、別名等信息。可導(dǎo)出符號(hào)表與用戶空間編程時(shí)的庫(kù)類似,內(nèi)核模塊中也可以調(diào)用其他模塊中的函數(shù)或例程,或者允許其他模塊調(diào)用本模塊中的函數(shù)。導(dǎo)出的符號(hào)必須在文件的全局可見(jiàn),不能將局部的符號(hào)導(dǎo)出。需要導(dǎo)出的變量要在函數(shù)體外進(jìn)行聲明,不需要導(dǎo)出的變量和函數(shù)等內(nèi)核符號(hào)可以用static進(jìn)行聲明從而對(duì)內(nèi)核的安全性有一定的保證。用戶空間的應(yīng)用函數(shù)可以接受用戶的參數(shù),Linux內(nèi)核模塊在加載的時(shí)候也可以通過(guò)加載參數(shù)部分來(lái)加載參數(shù)。在內(nèi)核模塊函數(shù)中可以調(diào)用printk()函數(shù),然后通過(guò)dmesg命令查看內(nèi)核輸出日志文件便于調(diào)試。
二、Linux內(nèi)核網(wǎng)絡(luò)編程
Linux網(wǎng)絡(luò)編程分為用戶層網(wǎng)絡(luò)編程和內(nèi)核層網(wǎng)絡(luò)編程。用戶層網(wǎng)絡(luò)編程即為套接字編程。套接字編程又分為標(biāo)準(zhǔn)套接字編程和原始套接字編程,標(biāo)準(zhǔn)套接字包括流式套接字和數(shù)據(jù)報(bào)套接字,其涵蓋了一般應(yīng)用層次的TCP/IP應(yīng)用,幾乎所有的應(yīng)用程序都可以使用這兩類套接字來(lái)實(shí)現(xiàn)。但是當(dāng)要發(fā)送自定義的IP包時(shí),或者監(jiān)聽(tīng)網(wǎng)絡(luò)上的數(shù)據(jù)包時(shí),以及實(shí)現(xiàn)自定義的協(xié)議時(shí),標(biāo)準(zhǔn)套接字無(wú)法實(shí)現(xiàn)需要使用原始套接字,原始套接字主要應(yīng)用在底層網(wǎng)絡(luò)編程。Linux為研究網(wǎng)絡(luò)安全在內(nèi)核層提供了網(wǎng)絡(luò)編程框架NetFilter。利用NetFilter框架可以在內(nèi)核層對(duì)進(jìn)出主機(jī)的網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行監(jiān)聽(tīng)、捕獲、修改等所有操作,可以實(shí)現(xiàn)防火墻、VPN、NAT、信息加密解密等。
現(xiàn)行實(shí)際網(wǎng)絡(luò)協(xié)議簇標(biāo)準(zhǔn)是TCP/IP協(xié)議簇,TCP/IP協(xié)議簇自上而下分為應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層。網(wǎng)絡(luò)層存在IPv4和IPv6兩種協(xié)議棧,NetFilter在Linux內(nèi)核的IPv4和IPv6協(xié)議棧都有相應(yīng)的實(shí)現(xiàn),本文主要分析IPv4環(huán)境下的NetFilter的實(shí)現(xiàn),IPv4協(xié)議棧為了實(shí)現(xiàn)對(duì)NetFilter框架的支持,在IP數(shù)據(jù)包的傳遞過(guò)程中設(shè)置了5個(gè)檢查點(diǎn),通過(guò)在檢查點(diǎn)上掛處理載函數(shù)可以完成對(duì)網(wǎng)絡(luò)數(shù)據(jù)包的各種的處理,這5個(gè)檢查點(diǎn)的分布如圖1所示。
圖1
通過(guò)觀察可以發(fā)現(xiàn)進(jìn)出主機(jī)的網(wǎng)絡(luò)數(shù)據(jù)包共有三條路徑:接收網(wǎng)絡(luò)數(shù)據(jù)包路徑、轉(zhuǎn)發(fā)網(wǎng)絡(luò)數(shù)據(jù)包路徑和發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包路徑。當(dāng)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包從網(wǎng)絡(luò)到達(dá)主機(jī)的網(wǎng)絡(luò)層時(shí)首先經(jīng)過(guò)NF_IP_PRE_ROUTING檢查點(diǎn),當(dāng)檢查點(diǎn)的處理函數(shù)處理完數(shù)據(jù)包之后會(huì)查詢本地路由表,如果修改后的網(wǎng)絡(luò)數(shù)據(jù)包是發(fā)送給本地主機(jī)的則將網(wǎng)絡(luò)數(shù)據(jù)包交給NF_IP_LOCAL_IN檢查點(diǎn),當(dāng)檢查點(diǎn)的處理函數(shù)處理完數(shù)據(jù)包之后會(huì)將數(shù)據(jù)包交付給傳輸層協(xié)議。如果修改后的數(shù)據(jù)包不是發(fā)送給本地主機(jī)的且主機(jī)允許路由的條件下會(huì)將數(shù)據(jù)包交付給NF_IP_FORWARD檢查點(diǎn),當(dāng)檢查點(diǎn)的處理函數(shù)處理完數(shù)據(jù)包之后會(huì)將數(shù)據(jù)包交付給NF_IP_POST_ROUTING檢查點(diǎn),最終交付給數(shù)據(jù)鏈路層通過(guò)網(wǎng)卡將數(shù)據(jù)包發(fā)送到網(wǎng)絡(luò)上。當(dāng)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包從主機(jī)的傳輸層到達(dá)IP層時(shí),首先經(jīng)過(guò)NF_IP_LOCAL_OUT檢查點(diǎn),當(dāng)檢查點(diǎn)的處理函數(shù)處理完網(wǎng)絡(luò)數(shù)據(jù)包時(shí)會(huì)查詢本地路由表,然后將網(wǎng)絡(luò)數(shù)據(jù)包交付給NF_IP_POST_ROUTING檢查點(diǎn),最終交付給數(shù)據(jù)鏈路層并通過(guò)網(wǎng)卡將網(wǎng)絡(luò)數(shù)據(jù)包從主機(jī)發(fā)送到網(wǎng)絡(luò)。
三、sk_buff內(nèi)核網(wǎng)絡(luò)數(shù)據(jù)操作系統(tǒng)簡(jiǎn)介
sk_buff是Linux內(nèi)核專門為網(wǎng)絡(luò)編程設(shè)計(jì)的內(nèi)存管理系統(tǒng),這套內(nèi)存管理系統(tǒng)充分利用了通用的內(nèi)存分配機(jī)制。Linux內(nèi)核從第2.2版本開(kāi)始引入內(nèi)存碎片分配器機(jī)制,最終將網(wǎng)絡(luò)數(shù)據(jù)包的信息分配到高速緩存中,這種機(jī)制使得物理上不連續(xù)的內(nèi)存塊邏輯上看起來(lái)是連續(xù)的。
sk_buff是Linux內(nèi)核網(wǎng)絡(luò)編程最重要的數(shù)據(jù)結(jié)構(gòu)。每個(gè)網(wǎng)絡(luò)數(shù)據(jù)包在內(nèi)存中都對(duì)應(yīng)一個(gè)套接字緩存,每個(gè)套接字緩存對(duì)應(yīng)一個(gè)sk_buff結(jié)構(gòu),在sk_buff結(jié)構(gòu)中有大量指針指向網(wǎng)絡(luò)數(shù)據(jù)包的數(shù)據(jù)部分,同時(shí)sk_buff結(jié)構(gòu)中也保存了有關(guān)網(wǎng)絡(luò)數(shù)據(jù)包的所有的狀態(tài)信息。大量的網(wǎng)絡(luò)數(shù)據(jù)包以sk_buff隊(duì)列的形式存儲(chǔ)在網(wǎng)卡中,通過(guò)使用next和prev指針可以雙向遍歷sk_buff隊(duì)列,每個(gè)sk_buff的list指針指向sk_buff隊(duì)列的首部,sk指針指向該套接字緩存的套接字信息(源IP地址、目的IP地址、源端口號(hào)和目的端口號(hào)),dev指針指向網(wǎng)絡(luò)數(shù)據(jù)包來(lái)自或?qū)⑷サ木W(wǎng)卡。transport_header指針指向網(wǎng)絡(luò)數(shù)據(jù)包的傳輸層首部,network_header指針指向網(wǎng)絡(luò)數(shù)據(jù)包的網(wǎng)絡(luò)層首部,mac_header指針指向網(wǎng)絡(luò)數(shù)據(jù)包的數(shù)據(jù)鏈路層首部,head指針指向sk_buff的開(kāi)始,data指針指向sk_buff數(shù)據(jù)部分的開(kāi)始,tail指針指向數(shù)據(jù)部分的結(jié)束,end指針指向sk_buff的結(jié)束。有關(guān)sk_buff更詳細(xì)的信息請(qǐng)參考Linux內(nèi)核源代碼。
四、Linux內(nèi)核網(wǎng)絡(luò)編程流程
通過(guò)上述介紹了解到Linux內(nèi)核網(wǎng)絡(luò)編程用到的機(jī)制,在進(jìn)行Linux內(nèi)核網(wǎng)絡(luò)編程時(shí),首先需要熟悉sk_buff機(jī)制,利用Linux內(nèi)核提供給用戶的接口完成對(duì)網(wǎng)絡(luò)數(shù)據(jù)包操作的模塊函數(shù),然后分析出該模塊函數(shù)應(yīng)該在網(wǎng)絡(luò)數(shù)據(jù)包傳輸時(shí)對(duì)數(shù)據(jù)包進(jìn)行處理的階段,從而選擇正確的檢查點(diǎn)并通過(guò)內(nèi)核可加載模塊機(jī)制將模塊函數(shù)掛載到NetFilter的檢查點(diǎn)上,最后通過(guò)發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包來(lái)觸發(fā)掛載的模塊函數(shù)來(lái)進(jìn)行調(diào)試。
五、結(jié)束語(yǔ)
本文只是介紹了Linux內(nèi)核網(wǎng)絡(luò)編程用到的機(jī)制和流程,利用Linux內(nèi)核網(wǎng)絡(luò)編程可以實(shí)現(xiàn)多種多樣的功能,如對(duì)網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行分類與過(guò)濾,對(duì)網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行流量統(tǒng)計(jì),實(shí)現(xiàn)防火墻技術(shù)、入侵檢測(cè)技術(shù)、VPN和NAT技術(shù)等。掌握Linux內(nèi)核網(wǎng)絡(luò)編程無(wú)論對(duì)于網(wǎng)絡(luò)技術(shù)的應(yīng)用還是對(duì)于網(wǎng)絡(luò)安全的研究都具有重要意義。
參考文獻(xiàn):
[1]宋敬彬,孫海濱. Linux網(wǎng)絡(luò)編程[M].北京:清華大學(xué)出版社,2010.
[作者簡(jiǎn)介]韓宇(1987-),男,河北邢臺(tái)人,研究生,研究方向:信息安全與計(jì)算機(jī)犯罪偵查;朗加曲珍(1990-),女,西藏昌都人,本科,研究方向:安全防范。