摘要:Linux內(nèi)核2.4及以上版本中的Netfilter/Iptables防火墻對每個進來的數(shù)據(jù)包記錄一條日志信息,造成信息冗余,耗費大量的日志存儲空間。基于Netfilter的連接跟蹤功能,以網(wǎng)絡連接為相關信息,擴展Netfilter/Iptables的核心數(shù)據(jù)結(jié)構(gòu),對同一連接的所有數(shù)據(jù)包信息進行組織與記錄,并動態(tài)從內(nèi)核空間獲取日志信息,從而減少日志冗余,方便日志分析與管理。
關鍵詞:Netfilter; Linux 2.6; 防火墻; 日志
中圖分類號:TP393.08文獻標志碼:A
文章編號:1001-3695(2008)04-1120-03
Linux是當今流行的操作系統(tǒng),具有強大的網(wǎng)絡功能,其內(nèi)嵌的Netfilter/Iptables防火墻具有良好的可擴展性,允許程序員對防火墻的功能進行擴充[1]。Netfilter/Iptables良好的可擴展性是由其很好的軟件結(jié)構(gòu)來支持的。相比于2.2內(nèi)核中的防火墻ipchain,它的引入解決了ipchain傳遞數(shù)據(jù)到用戶空間、透明代理等方面的問題,也解決了ipchain結(jié)構(gòu)不清晰、不模塊化、不易于擴展的問題[2]。
Netfilter/Iptables提供了日志功能,用來記錄流入流出的網(wǎng)絡數(shù)據(jù)包的信息,在iptables命令中使用-j LOG選項,可以啟動防火墻日志記錄功能。其命令格式如下:iptables-A FORWARD-p tcp-j LOG [選項]。在其選項中可以指定日志的等級、前綴等。其日志中的內(nèi)容包括源IP和端口、目的IP和端口、時間、協(xié)議類型、發(fā)送和接收的字節(jié)數(shù)等。日志通過syslog工具記錄到/var/log/messages或者指定的文件中,但是防火墻對于進來的在重要等級之上的每個數(shù)據(jù)包都生成一條日志信息,即使數(shù)據(jù)包中實際的數(shù)據(jù)很少。這樣,像源目的IP、端口、協(xié)議、主機名等信息就會被大量重復記錄,不但會占用大量的存儲空間,也會使日志分析的效率大大降低。
目前對于數(shù)據(jù)冗余的解決辦法,大多數(shù)系統(tǒng)是采用對于防火墻生成的日志數(shù)據(jù)進行預處理[3,4]。一般是先獲取防火墻生成的日志文件,然后再對日志進行處理,包括清除無關日志條目,日志預統(tǒng)計,即把一定時間內(nèi),相同源目的IP、端口、協(xié)議條目的相應項合并在一起,把傳輸字節(jié)累加,以減少日志的條目和存儲空間。這種事后處理的辦法增加了日志分析模塊的負擔,影響了分析的效率,特別是對要求實時監(jiān)控流量的日志分析系統(tǒng),是難以滿足要求的。
1Netfilter架構(gòu)
Netfilter是一種位于Linux 2.4內(nèi)核中用于擴展各種網(wǎng)絡服務的結(jié)構(gòu)化底層框架,Netfilter構(gòu)建在Linux的TCP/IP協(xié)議棧的IP層中,但它未對網(wǎng)絡結(jié)構(gòu)造成破壞,而是通過Netfilter結(jié)構(gòu)將防火墻對數(shù)據(jù)包的處理引入IP層中,防火墻代碼和IP層的代碼是完全分離的。Netfilter的設計思想是生成一個模塊化結(jié)構(gòu)使之具有良好的擴展性,是從事網(wǎng)絡底層的開發(fā)人員可以集中精力進行網(wǎng)絡新特性的擴充[5]。
Netfilter主要功能有包過濾、NAT、數(shù)據(jù)包處理、連接跟蹤。
連接跟蹤是NAT的基礎,但是已經(jīng)作為一個單獨的模塊被實現(xiàn)。該功能用于對包過濾功能的一個擴展,使用連接跟蹤來實現(xiàn)基于狀態(tài)的防火墻。
Netfilter框架非常清晰。數(shù)據(jù)包進入Netfilter后,首先進行校驗和、完整性檢查,然后通過路由,確定數(shù)據(jù)包的類型,否則將數(shù)據(jù)包傳給輸入鏈。如果目的地址不是本機,則將數(shù)據(jù)包傳給轉(zhuǎn)發(fā)鏈;本地進程產(chǎn)生的數(shù)據(jù)包只經(jīng)過輸出鏈,經(jīng)路由轉(zhuǎn)發(fā)出去。
不同的數(shù)據(jù)包所要經(jīng)過的三條路徑如下:
a)發(fā)往本機上層的包。經(jīng)過的檢查點是NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN。
b)由本機轉(zhuǎn)發(fā)的包。經(jīng)過的檢查點是NF_IP_PRE_ROUTING、NF_IP_FORWARD、NF_IP_POST_ROUTING。c)從本機發(fā)出的包。NF_IP_LOCAL_OUT、NF_IP_POST_ROUTING。
在每一條路徑上,連接跟蹤和地址轉(zhuǎn)換都注冊了相應的函數(shù),并創(chuàng)建與之相關的數(shù)據(jù)結(jié)構(gòu),完成其功能[6]。Iptables是用戶空間用來配置Netfilter過濾規(guī)則的工具。它使插入修改和除去信息包過濾表中的規(guī)則變得容易。Linux網(wǎng)絡協(xié)議棧之間傳送數(shù)據(jù)用的是struct sk_buff結(jié)構(gòu),每個數(shù)據(jù)包都存放在此結(jié)構(gòu)的一個實體中。這些實體以雙向循環(huán)鏈表鏈接在一起。此結(jié)構(gòu)中記錄了數(shù)據(jù)包接收時間、設備、網(wǎng)絡層頭指針、數(shù)據(jù)包的大小等信息。
2解決方案
本文基于Netfilter連接跟蹤的功能,利用Netfilter良好的可擴展性,采用事前聚合的辦法,在內(nèi)核中對進入的每個數(shù)據(jù)根據(jù)它們所屬的連接進行合并。
2.1相關數(shù)據(jù)結(jié)構(gòu)
連接跟蹤(CONNTRACK)就是跟蹤連接并且記錄連接的信息和狀態(tài)。這里的連接并不僅僅指TCP連接,它也包括UDP、ICMP等協(xié)議的虛擬連接。連接跟蹤是實現(xiàn)NAT的基礎,在使用NAT功能時必須要加載連接跟蹤模塊。
每一條連接在Netfilter中都以一個struct ip_conntrack(定義于/include/linux/netfilter_ipv4/ ip_conntrack.h中)結(jié)構(gòu)體來描述。其中成員記錄了結(jié)構(gòu)的引用計數(shù)、結(jié)構(gòu)的狀態(tài)、與此結(jié)構(gòu)關聯(lián)的expect鏈表等信息。它有兩個struct ip_conntrack_tuple_hash成員,即指向初始信息包(tuplehash[IP_CT_DIR_ORIGINAL])與指向回復信息包(tuplehash[IP_CT_DIR_REPLY] )。
Struct ip_conntrack_tuple_hash結(jié)構(gòu)如下:
struct ip_conntrack_tuple_hash
{
struct list_head list;
struct ip_conntrack_tuple tuple;
/* this == ctrack->tuplehash[DIRECTION(this)]. */
struct ip_conntrack *ctrack;
};
Struct list_head list成員把struct ip_conntrack_tuple_hash以雙向鏈表的形式連接起來。
Struct ip_conntrack_tuple tuple 用來記錄一個方向上連接的IP、端口、協(xié)議等信息。
需要在struct ip_contrack結(jié)構(gòu)中增加本文需要的信息,首先定義如下結(jié)構(gòu):
struct ipct_log
{
struct list_head list;
struct ipct_log_data data;
void (*ct_destroy)(struct nf_conntrack *nfct); /*before ct destroy, we print info */
struct ip_conntrack *ct;
};
其中:struct ipct_log_data data是用來記錄連接的其他信息。定義如下:
struct ipct_log_data
{
unsigned long jiff;
char idev[CLEN_NAME];
char odev[CLEN_NAME];
struct packet_counters cnt;/* define in ip_tables.h */
struct ipctlog_data_rule filter;
struct ipctlog_data_rule nat;
};
其中:char idev[CLEN_NAME]用來記錄數(shù)據(jù)包進入的設備名;char odev[CLEN_NAME]記錄數(shù)據(jù)包出去的設備名;struct packet_counters cnt用來記錄這條連接已經(jīng)發(fā)送數(shù)據(jù)包和總的字節(jié)數(shù)。定義如下:
struct packet_counters
{
u_int64_t pcnt, bcnt;
};
在struct ip_conntrack增加struct ipct_log log成員用來記錄連接傳送字節(jié)數(shù)等信息。
綜上所述,各結(jié)構(gòu)的關系如圖1所示。
由圖1可見,Linux中日志系統(tǒng)通過結(jié)構(gòu)嵌套來記錄連接的信息,使連接的信息具有層次性,方便理解與應用。
2.2記錄相應的連接信息
接下來要做的事就是在有網(wǎng)絡流量時記錄相應信息。
當一個數(shù)據(jù)包通過內(nèi)核防火墻時,Netfilter會去通過源目的IP、端口、協(xié)議這樣一個五元組查找狀態(tài)連接表的元素。若沒有找到,認為這是一個新的連接,會建立相應的struct ip_conntrack結(jié)構(gòu),并記錄相應的五元組。同時,從第一個包開始,Netfilter會在記錄數(shù)據(jù)包信息的struct sk_buff類型的結(jié)構(gòu)中記錄此數(shù)據(jù)包所屬連接的ip_conntrack結(jié)構(gòu)的地址,記錄在struct sk_buff類型中的struct nf_conntrack*nfct成員中。
本文在nf_ip_pre_routing這個檢查點上注冊記錄數(shù)據(jù)包大小和數(shù)量函數(shù):
unsigned int log_prerouting(unsigned int hooknum, struct sk_buff **pskb, const struct net_device in,const struct net_device out, int (okfn)(struct sk_buff ))
此注冊函數(shù)在Netfilter框架的位置如圖2所示。
對于每一個進來的數(shù)據(jù)包,通過其struct sk_buff類型的skb變量中的struct nf_conntrack nfct成員找到其所屬連接的struct ip_conntrack結(jié)構(gòu),從skb成員變量中獲取數(shù)據(jù)包的長度,加入到其所屬的連接的struct ip_conntrack結(jié)構(gòu)中相應的成員變量。
其主要算法如下:
輸入:數(shù)據(jù)包信息結(jié)構(gòu)體
輸出:此數(shù)據(jù)包所屬連接的信息
a)nf_ip_pre_routing檢查點上獲取數(shù)據(jù)包。
b)從數(shù)據(jù)包struct sk_buff結(jié)構(gòu)體的nfct成員查找其所屬的ip_conntrack結(jié)構(gòu)。
c)if(所屬ip_conntrack結(jié)構(gòu)存在) 返回此結(jié)構(gòu)指針, else 新建此連接的ip_conntrack結(jié)構(gòu),并返回此結(jié)構(gòu)指針。
d)從數(shù)據(jù)包struct sk_buff結(jié)構(gòu)體提取數(shù)據(jù)包大小、個數(shù)等信息。
e)將信息記入其所屬的ip_conntrack結(jié)構(gòu)中。
2.3在用戶空間獲取日志數(shù)據(jù)
由于用戶空間的進程不能直接訪問內(nèi)核空間的數(shù)據(jù),必須通過相應的系統(tǒng)調(diào)用來獲取內(nèi)核空間的數(shù)據(jù)。在Netfilter中,面向用戶的接口實質(zhì)上是通過s/getsockopt系統(tǒng)調(diào)用完成的,核外程序可以通過創(chuàng)建一個原始IP套接字獲得訪問Netfilter的句柄,然后通過getsockopt()和setsockopt()系統(tǒng)調(diào)用來讀取,設置相關數(shù)據(jù)。
在內(nèi)核空間,通過struct nf_sockopt_ops向用戶進程提供的防火墻接口,用戶進程通過該接口設置規(guī)則。其中的兩個函數(shù)指針用來指向具體的處理函數(shù)。
struct nf_sockopt_ops
{
struct list_head list;
int pf;
/ Non-inclusive ranges: use 0/0/NULL to never get called. /
int set_optmin;
int set_optmax;
int (set)(struct sock *sk, int optval, void __user *user, unsigned int len);
int get_optmin;
int get_optmax;
int (get)(struct sock sk, int optval, void __user user, int len);
/ Number of users inside set() or get(). /
unsigned int use;
struct task_struct *cleanup_task;
};
s/getsockopt系統(tǒng)調(diào)用通過調(diào)用nf_sockopt函數(shù)來完成相應的功能。
static int nf_sockopt(struct sock *sk, int pf, int val, char opt, int len, int get)
只需定義自己的處理函數(shù),并登記在struct nf_sockopt_ops ipt_sockopts結(jié)構(gòu)中
static struct nf_sockopt_ops ipt_sockopts= { { NULL, NULL }, PF_INET, IPT_CTLOG_BASE_CTL, IPT_CTLOG_SO_SET_MAX+1, do_ipt_set_ctl,IPT_CTLOG_BASE_CTL, IPT_CTLOG_SO_GET_MAX+1,do_ipt_get_ctl, 0, NULL};
do_ipt_get_ctl函數(shù)具體完成日志數(shù)據(jù)從內(nèi)核空間到用戶空間的拷貝工作:
static int do_ipt_get_ctl(struct sock sk, int cmd, void user, int len)
在該函數(shù)中,使用內(nèi)核提供的copy_to_user宏,把數(shù)據(jù)拷貝到void user所指的用戶數(shù)據(jù)空間。
其主要算法如下:
a)定義協(xié)議常量。
b)調(diào)用getsockopt系統(tǒng)調(diào)用,傳入用戶空間指針、協(xié)議常量。
c)在內(nèi)核空間根據(jù)傳入?yún)f(xié)議常量,查找此協(xié)議對應的處理函數(shù)。
d)調(diào)用處理函數(shù)do_ipt_get_ct拷貝日志數(shù)據(jù)到用戶空間。
e)將ip_conntrack結(jié)構(gòu)中,記錄數(shù)據(jù)包大小和數(shù)量的變量清0。
2.4數(shù)據(jù)的發(fā)送
在用戶空間獲得數(shù)據(jù)后,以welf格式為基礎,按照預定的日志格式發(fā)送到指定的日志服務器上。在具體的實現(xiàn)上,本文采用了Linux自帶的syslogd服務,把日志用UDP協(xié)議通過514端口發(fā)往日志服務器。直接在用戶空間程序中調(diào)用syslog函數(shù)就可把格式化好的日志數(shù)據(jù)發(fā)往制定機器。
使用syslogd服務往遠程機器上發(fā)送數(shù)據(jù),配置/etc/syslog.conf文件,在文件中指定*.*@172.31.0.1,可把日志發(fā)往IP為172.31.0.1的機器。在接收日志的服務器上需要將/etc/sysconfig/syslog文件中SYSLOGD_PARAMS變量的值設置為-r,表示接收從遠程主機發(fā)送過來的日志信息。
3性能分析
對于按ASCII碼存儲的日志條目,最基本的流量日志有如下條目:id、time、fw、proto、pri、src_port、dst_port、type、rule、action、sent、rcvd、sent_pkt、rcvd_pkt、duration、src、dst。其中,除了time、sent、rcvd、sent_pkt、rcvd_pkt、duration是與數(shù)據(jù)包有關的項之外,其余都是與連接有關的數(shù)據(jù)項。
筆者可以依據(jù)以下公式計算從連接跟蹤表中獲取日志條目的間隔時間段中,不同規(guī)格的防火墻日志節(jié)約的空間:1/time*data_exchange_fre。其中:time是具體的一段時間,即每隔多少時間從連接跟蹤表中取出條目寫往日志文件中,單位取為min;data_exchange_fre是平均數(shù)據(jù)交換頻率,指的是某一個連接雙方互發(fā)數(shù)據(jù)的頻繁程度,單位取為次/min。一段較長的時間產(chǎn)生的日志大小是每次從連接跟蹤表中取得日志大小的累加。
浙江大學也有學者采用事后處理的辦法[3,4],先用獲取沒有聚合的日志數(shù)據(jù),再把相同源目的IP、端口、協(xié)議的日志條目合并在一起,把傳送數(shù)據(jù)數(shù)量累加。這樣也能達到相同的目的,但是會耗費存儲日志的服務器空間。依據(jù)機器性能、日志文件大小的不同,也會耗費相當?shù)奶幚頃r間。對于需要提供實時監(jiān)控網(wǎng)絡流量功能的日志服務器來說,會產(chǎn)生相當?shù)臅r間延遲。因此采用事前聚合的辦法可以節(jié)約存儲空間,提高日志分析效率。
在一個大約有100臺機器的網(wǎng)絡上測試這種方法,同時用本方法和Iptables自帶的日志功能記錄網(wǎng)絡流量。其各自生成的日志大小如圖3所示,可見采用該方法,在通常的數(shù)據(jù)交換頻率下,大約能節(jié)約三分之二左右的日志存儲空間。
4結(jié)束語
Netfilter/Iptables是集成于Linux內(nèi)核中的擴展性極強的防火墻,開發(fā)人員可以根據(jù)自己的需要在防火墻中添加功能。本文針對Netfilter/Iptables日志系統(tǒng)易造成冗余的情況,基于Netfilter連接跟蹤的功能,以連接為單元記錄網(wǎng)絡數(shù)據(jù)流量的情況,減少了事后處理的麻煩,節(jié)約了日志存儲空間。
參考文獻:
[1]RUSSELL R.Linux Netfilter hacking HOWTO [EB/OL].[2006]. http://www.netfilter.org/.
[2]博嘉科技.Linux防火墻技術探秘[M].北京:國防工業(yè)出版社,2002.
[3]鄭長慶.基于日志分析的網(wǎng)絡監(jiān)視系統(tǒng)的設計與實現(xiàn)[D].杭州:浙江大學, 2005:32-40.
[4]李循律.基于Firewall日志的數(shù)據(jù)挖掘研究[D].杭州:浙江大學,2005:30-36.
[5]李曉峰,張玉清,李星.Linux 2.4內(nèi)核防火墻底層結(jié)構(gòu)分析[J].計算機工程與應用, 2002,38(14):2-3.
[6]劉曉萍,郭玉東,金霞.Linux 2.4 內(nèi)核防火墻的連接跟蹤技術[J].微機發(fā)展,2004,14(5):2-3.
“本文中所涉及到的圖表、注解、公式等內(nèi)容請以PDF格式閱讀原文”