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