歡迎您光臨本站 註冊首頁

linux防火牆實現技術比較

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  一 前言
此文是在aka(www.aka.org.cn)的一次講座稿【12】基礎之上修改而成(催稿,沒辦法),著重闡述linux下
的防火牆的不同實現之間的區別,以ipchains, iptables, checkpoint FW1為例。


二 基本概念

2.0
在進入正題之前,我將花少許篇幅闡述一些基本概念。儘管防火牆的術語這些年基本上沒有太大的變化,但是如果你以前只看過90年代初的一些文獻的話,有些概念仍然會讓你混淆。此處只列出一些最實用的,它們不是準確的定義,我只是儘可能的讓它們便於理解而已。

2.1 包過濾:
防火牆的一類。80年代便有論文來描述這種系統。傳統的包過濾功能在路由器上常可看到,而專門的防火牆系統一般在此之上加了功能的擴展,如狀態檢測等。它通過檢查單個包的地址,協議,埠等信息來決定是否允許此數據包
通過。

2.2 代理:
防火牆的一類。工作在應用層,特點是兩次連接(browser與proxy之間,proxy與web server之間)。如果對原理尚有疑惑,建議用sniffer抓一下包。代理不在此文的討論範圍之內。

2.3 狀態檢測:
又稱動態包過濾,是在傳統包過濾上的功能擴展,最早由checkpoint提出。傳統的包過濾在遇到利用動態埠的協議時會發生困難,如ftp。你事先無法知道哪些埠需要打開,而如果採用原始的靜態包過濾,又希望用到的此服務的話,就需要實現將所有可能用到的埠打開,而這往往是個非常大的範圍,會給安全帶來不必要的隱患。 而狀態檢測通過檢查應用程序信息(如ftp的PORT和PASS命令),來判斷此埠是否允許需要臨時打開,而當傳輸結束時,埠又馬上恢復為關閉狀態。

2.4 DMZ非軍事化區:
為了配置管理方便,內部網中需要向外提供服務的伺服器往往放在一個單獨的網段,這個網段便是非軍事化區。防火牆一般配備三塊網卡,在配置時一般分別分別連接內部網,internet和DMZ。

2.5
由於防火牆地理位置的優越(往往處於網路的關鍵出口上),防火牆一般附加了NAT,地址偽裝和VPN等功能,這些不在本文的討論範圍。


三 檢測點

3.0 綜述
包過濾需要檢查IP包,因此它工作在網路層,截獲IP包,並與用戶定義的規則做比較。

3.1 ipchains
摘自【3】

----------------------------------------------------------------
| ACCEPT/ lo interface |
v REDIRECT _______ |
--> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ -->
h a |input | e {Routing } |Chain | |output |ACCEPT
e n |Chain | m {Decision} |_______| --->|Chain |
c i |______| a ~~~~~~~~ | | ->|_______|
k t | s | | | | |
s y | q | v | | |
u | v e v DENY/ | | v
m | DENY/ r Local Process REJECT | | DENY/
| v REJECT a | | | REJECT
| DENY d --------------------- |
v e -----------------------------
DENY

總體來說,分為輸入檢測,輸出檢測和轉發檢測。但具體到代碼的時候,輸出檢測實際分散到了幾處(不同的上層協議走IP層的不同的流程):
UDP/RAW/ICMP報文:ip_build_xmit
TCP報文:ip_queue_xmit
轉發的包:ip_forward
其它:ip_build_and_send_pkt

正如ipchains項目的負責人Rusty Russell所說,在開始ipchians不久,便發現選擇的檢測點位置錯了,最終只能暫時將錯就錯。一個明顯的問題是轉發的包在此結構中必須經過三條鏈的匹配。地址偽裝功能與防火牆模塊牽扯過於緊密,如果不詳細了解其原理的話,配置規則很容易出錯。

此部分詳細的分析可參見我早期的一份文章【9】。


3.2 iptables

A Packet Traversing the Netfilter System:

--->PRE------>[ROUTE]--->FWD---------->POST------>
Conntrack | Filter ^ NAT (Src)
Mangle | | Conntrack
NAT (Dst) | [ROUTE]
(QDisc) v |
IN Filter OUT Conntrack
| Conntrack ^ Mangle
| | NAT (Dst)
v | Filter


2.4內核中的防火牆系統不是2.2的簡單增強,而是一次完全的重寫,在結構上發生了非常大的變化。相比2.2的內核,2.4的檢測點變為了五個。

在每個檢測點上登記了需要處理的函數(通過nf_register_hook()保存在全局變數nf_hooks中),當到達此檢測點的時候,實現登記的函數按照一定的優先順序來執行。嚴格的從概念上將,netfilter便是這麼一個框架,你可以在適當的位置上登記一些你需要的處理函數,正式代碼中已經登記了許多處理函數(在代碼中搜nf_register_hook的調用),如在NF_IP_FORWARD點上登記了裝發的包過濾功能。你也可以登記自己的處理函數,具體例子可參看【8】與【10】。

3.3 FW1
FW1是chekpoint推出的用於2.2內核的防火牆。由於其發布的模組文件帶了大量的調試信息,可以從反彙編的代碼中窺探到到許多實現細節。
FW1通過dev_add_pack的辦法載入輸入過濾函數,如果對這個函數不熟悉,請參看【14】。但是此處有個問題:在net_bh()中,傳往網路層的skbuff是克隆的,即
skb2=skb_clone(skb, GFP_ATOMIC);
if(skb2)
pt_prev->func(skb2, skb->dev, pt_prev);
這樣的話如果你想丟棄此包的話,光將其free掉是不夠的,因為它只是其中的一份拷貝而已。
FW1是怎麼解決這個問題的呢?見下面的代碼(從彙編代碼翻譯成的C程序):

packet_type *fw_type_list=NULL;

static struct packet_type fw_ip_packet_type =
{
__constant_htons(ETH_P_IP),
NULL, /* All devices */
fw_filterin,
NULL,
NULL, /* next */
};

fwinstallin(int isinstall )
{
packet_type *temp;

/*安裝*/
if(isinstall==0){
dev_add_pack(&fw_ip_packet_type);
fw_type_list = fw_ip_packet_type->next;

for(temp = fw_type_list; temp; temp=temp->temp)
dev_remove_pack(temp);
}
/*卸載*/
else {
dev_remove_pack(&fw_ip_packet_type);

for(temp = fw_ip_packet_type; temp; temp=temp->next)
dev_add_pack(temp);
}
}

不難看出,FW1把ip_packet_type歇載掉了,然後自己在自己的處理函數(fw_filterin)中調ip_recv。

輸出的掛載和lkm的手法一樣,更改dev->hard_start_xmit。dev結構在2.2版本的發展過程中變了一次,為了兼容FW1對這點也做了處理(通過檢查版本號來取偏移)。

還有一款linux下的防火牆產品WebGuard(http://www.gennet.com.tw/b5/csub_webguard.html)採用的手法與FW1其非常類似。有興趣的人可以自行研究一下。


四 規則

4.0 綜述

4.1 ipchains

man ipfw可以看到這一段的詳細解釋。關鍵數據結構如下:

規則鏈用ip_chain結構來表示,預設有input,ouptput,forward三條鏈。在配置規則的時候,也可以添加新的鏈。每條鏈事實上就是一組相關的規則,以鏈表的形式存儲。
struct ip_chain
{
ip_chainlabel label; /* Defines the label for each block */
struct ip_chain *next; /* Pointer to next block */
struct ip_fwkernel *chain; /* Pointer to first rule in block */
__u32 refcount; /* Number of refernces to block */
int policy; /* Default rule for chain. Only *
* used in built in chains */
struct ip_reent reent[0]; /* Actually several of these */
};

每條規則用一個ip_fwkernel結構表示:
struct ip_fwkernel
{
struct ip_fw ipfw;
struct ip_fwkernel *next; /* where to go next if current
* rule doesn't match */
struct ip_chain *branch; /* which branch to jump to if
* current rule matches */
int simplebranch; /* Use this if branch == NULL */
struct ip_counters counters[0]; /* Actually several of these */
};

ip_fwkernel中的一個重要部分就是ip_fw,用來表示待匹配的數據包消息:
struct ip_fw
{
struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
__u32 fw_mark; /* ID to stamp on packet */
__u16 fw_proto; /* Protocol, 0 = ANY */
__u16 fw_flg; /* Flags word */
__u16 fw_invflg; /* Inverse flags */
__u16 fw_spts[2]; /* Source port range. */
__u16 fw_dpts[2]; /* Destination port range. */
__u16 fw_redirpt; /* Port to redirect to. */
__u16 fw_outputsize; /* Max amount to output to
NETLINK */
char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
__u8 fw_tosand, fw_tosxor; /* Revised packet priority */
};

2.2內核中網路包與規則的實際匹配在ip_fw_check中進行。

4.2 iptables

一條規則分為三部分:
struct ipt_entry file://主要用來匹配IP頭
struct ip_match file://額外的匹配(tcp頭,mac地址等)
struct ip_target file://除預設的動作外(如ACCEPT,DROP),可以增加新的(如REJECT)。

man iptable:
>A firewall rule specifies criteria for a packet, and a
>target. If the packet does not match, the next rule in
>the chain is the examined; if it does match, then the next
>rule is specified by the value of the target, which can be
>the name of a user-defined chain, or one of the special
>values ACCEPT, DROP, QUEUE, or RETURN.

2.4內核中網路包與規則的實際匹配在ip_do_table中進行。這段代碼的流程在
netfilter hacking howto 4.1.3描述的非常清楚。

簡化代碼如下:
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(struct sk_buff **pskb,
unsigned int hook,
const struct net_device *in,
const struct net_device *out,
struct ipt_table *table,
void *userdata)
{
struct ipt_entry *e;
struct ipt_entry_target *t;
unsigned int verdict = NF_DROP;

table_base = (void *)table->private->entries
+ TABLE_OFFSET(table->private,
cpu_number_map(smp_processor_id()));
e = get_entry(table_base, table->private->hook_entry[hook]);

...
ip_packet_match(ip, indev, outdev, &e->ip, offset);

...
IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, protohdr, datalen, &hotdrop)

...
t = ipt_get_target(e);

...
verdict = t->u.kernel.target->target(pskb, hook, in, out, t->data, userdata);//非標準的target走這一步

...
return verdict;
}

流程:
--->NF_HOOK();(/include/linux/netfilter.h)
--->nf_hook_slow;(/net/core/netfilter.c)
--->nf_iterate();(/net/core/netfilter.c)
--->然後運行登記的函數;如果你希望有一套ipt_entry結構規則,並將它放到table里,你此時便可調用ipt_do_table來匹配。

在2.4內核中,規則本身也是可擴展的,體現可自己定義並添加新的ip_match和ip_target上。

4.2 FW1
未作分析。


五 與應用層的交互

5.0 綜述

防火牆除了內核里的功能以外,還需要在應用層有相應的的配置工具,如添加修改規則等,這就涉及如何與內核通信的問題。
內核模塊有三種辦法與進程打交道:首先是系統調用,缺點是必須添加新的系統調用或修改原有的,造成對內核代碼原有結構的變換;第二種辦法是通過設備文件(/dev目錄下的文件),不必修改編譯原有的代碼,但在使用之前要先用mknod命令產生一個這樣的設備;第三個辦法便是使用proc文件系統。

5.1 ipchains
由於ipchains是已經是內核的正式一部分,它採用了修改系統調用的辦法來添加修改命令,採用的辦法就是擴展setsockopt系統調用:
int setsockopt (int socket, IPPROTO_IP, int command, void *data, int length)

man ipfw可以獲得這方面的細節。

ipchains應用程序首先要需要建立一個raw socket(libipfwc.c),然後在之上調用setsockopt。
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)

調用順序:
ipchains在應用層調用setsockopt,進入內核:
--->sys_socketcall(net/socket.c)
--->sys_setsockopt(net/socket.c)
--->inet_setsockopt(net/ipv4/af_inet.c)
--->sock_setsockopt(net/core/sock.c)
--->raw_setsockopt(net/ipv4/raw.c)
--->ip_setsockopt(net/ipv4/ip_sockglue.c)
--->ip_fw_ctl(net/ipv4/ip_fw.c)

5.2 iptables
原理同ipchains, 但內部命令格式作了大幅簡化。詳見nf_setsockopt()。

5.3 FW1
FW1 登記了一個字元設備,通過它來進行用戶空間與內核空間的交互。相關代碼(從彙編代碼翻譯成的C程序)如下:

static unsigned int fw_major=0;

static struct file_operations fw_fops=
{
NULL, /* lseek */
fw_read, /* read */
fw_write, /* write */
NULL, /* readdir */
fw_poll, /* poll */
fw_ioctl, /* ioctl */
NULL, /* mmap */
fw_open, /* open */
NULL, /* flush */
fw_release /* release */
NULL, /* fsync */
};

int init_module()
{
...
/*man register_chrdev
On success, register_chrdev returns 0 if major is a number
other then 0, otherwise Linux will choose a major number and
return the chosen value.*/
if(fw_major=register_chrdev(UNNAMED_MAJOR, 「fw」, &fw_fops))
return -1;
...
}

void cleanup_module()
{
...
unregister_chrdev(fw_major, "fw");
...

}

fw_ioctl()用來做配置工作。


六 碎片的處理

6.0 綜述
關於分片重組的實現可參看【13】。

6.1 ipchains
在2.2內核中除非設置了alway_defrag,否則包過濾模塊不會對經過的包進行重組。它採用的辦法是只看第一片,因為只有這一片中有完整的頭信息,而後序的分片一律允許通過。為了防止分片欺騙(比如第一片極小,把傳輸層信息放到了第二片中),對這種正常情況中不可能出現的包做了而外的處理(太小的分片會被丟棄)。

6.2 iptables
在2.4內核有些變化,如果啟動了conncetion track,所有到達防火牆的碎片都會重組,這點在以後可能會變化,正如howto 中說的,現在考慮的還只是功能的完備性,效率還要在以後的版本中改進。檢測點也有了變化,輸入檢測在改在重組之後。

6.3 FW1
FW1對分片也做了額外處理,但目前尚未對其實現做仔細的分析。


七 狀態檢測

7.0 綜述
基於狀態的檢測對管理規則提出了非常大的方便,現在已成了防火牆的一項基本要求。但目的明確之後,其實現可以選擇多種不同的方法。

7.1 ipchains
ipchains本身不能完成狀態檢測,但有幾份pacth為它做了一下這方面的補充,採用的是簡單的動態添加規則的辦法,這是作者對其的介紹:
> I believe it does exactly what I want: Installing a temporary
> "backward"-rule to let packets in as a response to an
> outgoing request.

7.2 iptables
在2.4內核中,基於狀態的檢測已經實現,利用的是connection track模塊。此模塊檢查所有到來的數據包,將得到的狀態(enum ip_conntrack_status)保留在sk_buff結構中(即skb->nfct,可通過ip_conntrack_get()得到)。
在規則中要指明狀態信息(作為一個ipt_match),既實際上仍是比較每一條規則。從效率上,這種處理方式感覺不如下面FW1採用的方式好。

7.3 FW1
這段的代碼沒有做分析,但有一些文章通過黑箱操作的辦法「猜測「出了它的實現原理,如【1】。除規則表以外,FW1另外維護一份狀態表。當一個新的連接發生的時候,FW1與規則表配備,如果允許通過的話,則在狀態表中建立相應表項。以後的數據過來的時候首先匹配狀態表,如果它屬於一個連接,便允許通過,而不再檢查規則表。
草草看了一下BSD下的防火牆ipfilter的howto,感覺它的實現與FW1基本相同。


八 函數指針的問題
許多初讀內核的人對函數指針的應用很不適應,在netfilter中更是用的非常廣泛。大量register函數的應用,使得netfilter非常的模塊化,但是給初學者帶來的問題也不小。

這裡是linuxforum上的一份帖子,如果看代碼時對函數指針的指向總是糊裡糊塗的話,可借鑒一下這個思路(當然關鍵還是要找到指針初始化的地方):

>Linux內核技術
>herze (stranger ) 01/15/01 02:54 PM
>高手指點:PPP的發送函數在那裡?
>在Linux內核2.4.0中對於PPP數據包已經打好的包,內核中的ppp_generic.c文件中發送的流程好像如下
>ppp_file_write()->ppp_xmit_process()->ppp_push()(可能也由其它的發送流程,但是最後都是
>用到了ppp_push())這個函數,而這個函數調用了一個struct channel中struct ppp_channel中的
>struct ppp_channel_ops 中的一個函數指針
>int (*start_xmit)(struct ppp_channel *, struct sk_buff *)來進行發送的,但是下面我就不明白了。
>雖然在drivers/char/cyclades.c和drivers/char/serial167.c中找到了
>start_xmit( struct cyclades_port *info )但是函數說明都不相同。
>請教:
>int (*start_xmit)(struct ppp_channel *, struct sk_buff *)
>到底這個函數指針是指到了什麼地方?
>是不是和具體的硬體有關,但是我怎麼在內核中找不到對應的函數?

>Linux內核技術
>yawl (stranger ) 01/15/01 11:31 PM
>思路這樣 [re: herze]
>內核中常有這樣的類似處理,查找這種函數指針的一個好辦法,就是找那種結構的實例,對於你的問題,就是找
>ppp_channel_ops,最終會找到async_ops(ppp_async.c)和sync_ops(ppp_synctty.c),沒看過這塊的
>具體代碼,不敢多說,但思路如此。


九 後記
儘管此文中是在【12】的基礎之上完成的,但是在內容上並未完全包括前者,感興趣的朋友在那篇文章上可能能找到一些有趣的原始信息。由於時間關係,本文在此主題上的探討仍顯粗淺,對此只能說抱歉了。


十 參考文獻
【1】了解Check Point FW-1狀態表
http://magazine.nsfocus.com/detail.asp?id=538
【2】A Stateful Inspection of FireWall-1
http://www.dataprotect.com/bh2000/
【3】Linux IPCHAINS-HOWTO
http://www.linuxdoc.org
【4】防火牆新生代:Stateful-inspection
http://www.liuxuan.com/safe/anquan/html/firewall/04.htm
【5】netfilter站點上的文檔
http://netfilter.kernelnotes.org
【6】Application Gateways and Stateful Inspection:A Brief Note Comparing and Contrasting
http://www.avolio.com/apgw+spf.html
【7】Internet Firewalls:Frequently Asked Questions
http://www.interhack.net/pubs/fwfaq
【8】Writing a Module for netfilter
http://www.linux-mag.com/2000-06/gear_01.html
【9】ipchains的源代碼分析
http://www.lisoleg.net/lisoleg/network/ipchains.zip
【10】內核防火牆netfilter入門
http://magazine.nsfocus.com/detail.asp?id=637
【11】Check Point Firewall-1 on Linux, Part Two
http://www.securityfocus.com/frames/?focus=linux&content=/focus/linux/articles/checkpoint2.html
【12】防火牆技術分析講義
http://bj.aka.org.cn/Lectures/003/Lecture-3.1.1/Lecture-3.1.1/firewall.txt
【13】IP分片重組的分析和常見碎片攻擊 v0.2
http://magazine.nsfocus.com/detail.asp?id=584
【14】利用LLKM處理網路通信---對抗IDS、Firewall
http://security.nsfocus.com/showQueryL.asp?libID=431


[火星人 ] linux防火牆實現技術比較已經有765次圍觀

http://coctec.com/docs/security/show-post-72949.html