SYN Cookie原理及其在Linux內核中的實現

 

SYN Cookie原理及其在Linux內核中的實現

 

魏晉偉 ([email protected])

XML error: Please enter a value for the author element's jobtitle attribute, or the company-name element, or both.

 

 

簡介: 本文就分別介紹一下 SYN Flood ***和 SYN Cookie 的原理,更重要的是介紹 Linux 內核中實現SYN Cookie 的方式。最後,本文給出一種增強目前 Linux 中 SYN Cookie 功能的想法。

 
 

 

 

 

發佈日期: 2004 年 9 月 01 日 
級別: 初級 
訪問情況 377 次瀏覽 
建議: 0 (添加評論)

1 star2 stars3 stars4 stars5 stars 平均分 (共 2 個評分 )

 

概述

在目前以IPv4爲支撐的網絡協議上搭建的網絡環境中,SYN Flood是一種非常危險而常見的DoS***方式。到目前爲止,能夠有效防範SYN Flood***的手段並不多,而SYN Cookie就是其中最著名的一種。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發明。在很多操作系統上都有各種各樣的實現。其中包括Linux。本文就分別介紹一下SYN Flood***和SYN Cookie的原理,更重要的是介紹Linux內核中實現SYN Cookie的方式。最後,本文給出一種增強目前Linux中SYN Cookie功能的想法。

 

一 SYN Flood***

SYN Flood***是一種典型的拒絕服務型(Denial of Service)***。所謂拒絕服務型***就是通過進行***,使受害主機或網絡不能夠良好的提供服務,從而間接達到***的目的。

SYN Flood***利用的是IPv4中TCP協議的三次握手(Three-Way Handshake)過程進行的***。大家知道協議規定,如果一端想向另一端發起TCP連接,它需要首先發送TCP SYN 包到對方,對方收到後發送一個TCP SYN+ACK包回來,發起方再發送TCP ACK包回去,這樣三次握手就結束了。我們把TCP連接的發起方叫作"TCP客戶機(TCP Client)",TCP連接的接收方叫作"TCP服務器(TCP Server)"。值得注意的是在TCP服務器收到TCP SYN request包時,在發送TCP SYN+ACK包回TCP客戶機前,TCP服務器要先分配好一個數據區專門服務於這個即將形成的TCP連接。一般把收到SYN包而還未收到ACK包時的連接狀態成爲半開連接(Half-open Connection)。

在最常見的SYN Flood***中,***者在短時間內發送大量的TCP SYN包給受害者,這時***者是TCP客戶機,受害者是TCP服務器。根據上面的描述,受害者會爲每個TCP SYN包分配一個特定的數據區,只要這些SYN包具有不同的源地址(這一點對於***者來說是很容易僞造的)。這將給TCP服務器系統造成很大的系統負擔,最終導致系統不能正常工作。

 

二 SYN Cookie原理

SYN Cookie是對TCP服務器端的三次握手協議作一些修改,專門用來防範SYN Flood***的一種手段。它的原理是,在TCP服務器收到TCP SYN包並返回TCP SYN+ACK包時,不分配一個專門的數據區,而是根據這個SYN包計算出一個cookie值。在收到TCP ACK包時,TCP服務器在根據那個cookie值檢查這個TCP ACK包的合法性。如果合法,再分配專門的數據區進行處理未來的TCP連接。

從上面的介紹可以看出,SYN Cookie的原理比較簡單。到實際的應用中,它有多種不同的實現方式。

 

三 Linux內核中的SYN Cookie實現

Linux內核中對SYN Flood有很好的防護。以下的討論都是針對Linux2.4.20內核進行的。在每一個sock都有一個tcp_opt即這個sock的TCP選項。在tcp_opt其中有一個tcp_listen_opt,這裏存儲的是這個sock在LISTEN狀態下時保存的一些選項,其中有一個open_request結構的數組,數組長度爲TCP_SYNQ_HSIZE(512)。所有這些表示在一個sock,最多可以同時開啓512個半開連接(這是在不考慮其他約束條件時的最大值,實際情況中不會達到這個值)。當這個數組滿了時,新來的open_request會頂替掉一個老的open_request。這樣,即使沒有啓動SYN Cookie,也能夠在SYN Flood發生時保護系統免於癱瘓。問題是這種處理方法會在面對SYN Flood***時丟掉正常的TCP連接請求。SYN Cookie的作用恰恰是保證在面對SYN Flood***時,一方面能夠拒絕非法的TCP連接請求,一方面正常連接可以被建立。

Linux內核對TCP流程的處理主要在tcp_ipv4.c文件中的函數實現。具體的,當處理TCP SYN包時,系統進入tcp_v4_conn_request函數。其中調用cookie_v4_init_sequence生成一個ISN(Initial Sequence Number)。Linux內核把它作爲SYN Cookie流程中的cookie。

cookie_v4_init_sequence函數在syncookies.c文件中定義,它又調用random.c文件中的secure_tcp_syn_cookie函數。cookie的實質計算是在這個函數中進行的。

在random.c文件裏給出secure_tcp_syn_cookie函數的定義之前給出兩個宏,它們的定義分別爲


    #define COOKIEBITS 24
    #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
    

COOKIEBITS表示cookie的比特長度;COOKIEMASK是一個COOKIEBITS長的比特 串,所有比特都是1。

還有兩個比特串,被定義成一個__u32的二維數組


    static __u32	syncookie_secret[2][16-3+HASH_BUFFER_SIZE];
    

其中所有的比特值在secure_tcp_syn_cookie中被隨機的賦予,用get_random_bytes函數。它們成爲製作cookie的密鑰。這兩個被隨機產生的比特串是整個SYN Cookie實現方案的關鍵。另外還有一個開關syncookie_init控制對這兩個密鑰的改動。

還需要指出,在文件syncookies.c中定義有一個__u16組成的表static __u16 const msstab[],這個表中保存的是一些可能的MSS(Maximum Segment Size)值。

secure_tcp_syn_cookie函數的返回值就是計算得到的ISN值,即cookie。爲了描述方便,我們給出如下定義:


	tmp1 := saddr + daddr + ((sport<<16)+dport) + syncookie_secret[0]
	tmp2 := saddr + daddr + ((sport<<16)+dport) + syncookie_secret[1]
	tmp11 := HASH_TRANSFORM(tmp1[16], tmp1)
	tmp22 := HASH_TRANSFORM(tmp2[16], tmp2)
    A := tmp11[0][17]
	B := tmp22[1][17]
	

sseq := ntohl(skb->h.th->seq) 這裏的skb是攜帶TCP SYN的那個skb 
count1 := jiffies/(HZ*60) 當前時間的分鐘值 
data1 := msstab 
從前往後最後一個小於skb中攜帶的MSS值的值的索引(值得注意的是兩個密鑰在第一次被初始化後,就不會再有改動,直到系統重新啓動。因此可以認爲它是一個常值。)

有了上面的定義我們可以得到cookie等於


        isn := A+sseq + (count1<<COOKIEBITS) + (B+data1)&COOKIEMASK
        

這個isn被賦予返回的TCP SYN+ACK包中,作爲其中的ISN值。這就是cookie 的產生過程。在這個過程中,沒有在本地爲這個連接請求分配任何存儲空間。

在TCP服務器收到TCP ACK包時,相應的要進行SYN Cookie的檢查。這個檢查 過程在函數tcp_v4_hnd_req中的cookie_v4_check函數開始。cookie_v4_check調用cookie_check函數,cookie_check函數調用check_tcp_syn_cookie函數。

check_tcp_syn_cookie函數在random.c中定義,是與前面介紹的 
secure_tcp_syn_cookie函數對應的函數,檢查從TCP ACK中提取出的ISN值。

在check_tcp_syn_cookie中假定ISN的值如下


        isn := A+sseq + (count2<<COOKIEBITS) + (B+data2)&COOKIEMASK
        

這裏的A、B都是根據當前這個skb中的地址信息和syncookie_secret算出來的;sseq是根據這個skb中的seq值算出的。

有了上面這些值,TCP服務器就可以反算出count2和data2。理論上來說,只要這個isn是原來那個isn,應該有


count2 == count1  
data2 == data1

但是這種結論僅僅是一個理論情況。因爲在TCP服務器端並沒有保存原來的count1和data1,因此不能直接進行比較。TCP服務器採取的方法是:

1)計算出當前的分鐘值 
count3 := jiffies/(HZ*60) 
用count3與count2比較,如果差值超過COUNTER_TRIES(4)分鐘,則認爲這 個ACK包不合法。 
2)看data2是不是一個合法的msstab的索引,也就是說是不是小於NUM_MSS, 即(sizeof(msstab)/sizeof(msstab[0]) - 1)。如果小於,則認爲這個ACK 合法,否則認爲非法。

上面介紹的就是Linux內核Linux2.4.20中對SYN Cookie的實現方式。下面討論一下它的合理性。希望得到的結論是這種方案可以有效的實現一般TCP的連接,同時可以防止SYN Flood***。

從上面的介紹來說,合法的TCP連接請求一定可以通過SYN Cookie流程。 另一方面我們看SYN Cookie在系統受到各種SYN Flood***時會採取的行爲。 最一般的SYN Flood***方式是***者作爲TCP客戶機發送大量TCP SYN包而不再發送其他的包。這時SYN Cookie會爲每個SYN包計算出相應的ISN值,並返回SYN+ACK包,而在本地將不分配任何存儲空間,因此不會被成功***。

根據SYN Cookie的原理,***者有可能直接發送大量ACK包。這時SYN Cookie提取出每個包的isn值,並假定它有下面的格式


        isn := A+sseq + (count<<COOKIEBITS) + (B+data)&COOKIEMASK
        

反算出count和data。

因爲***者並不知道這裏的A和B,因此經過反算出的count和data幾乎不可能都合理,因此TCP服務器也幾乎不可能爲這些ACK包分配存儲空間,這也就說明了SYN Cookie達到起到了抵擋SYN Flood***的作用。

 

四 SYN Cookie Firewall

從上面的介紹可以看到,Linux內核中的SYN Cookie機制主要的功能是防止本機遭受SYN Flood***的,但是在很多情況下,僅僅實現這樣的SYN Cookie機制是不夠的。如果我們要考慮的是一個網關模式的防火牆,它不僅要保護本機免受各種網絡***,還要保護它後面的所有對外有開放TCP端口的主機免受這些***。比如一個局域網中有個服務器開放了FTP服務給外界,這個服務器主機就有可能遭受到來自互聯網上的SYN Flood***。而這時的防火牆會將所有的***SYN包轉發給受害主機。

一種杜絕這種情況的方法是SYN Cookie Firewall。它是SYN Cookie的一種擴展形式。總的來說,它是利用原來SYN Cookie的原理在內網和外網之間實現TCP三次握手過程的代理(proxy)的機制。

爲了方便描述,我們假定一個外在的TCP客戶機C希望通過防火牆F連接到局域網中的一個TCP服務器S。

在防火牆收到來自外網的SYN包時,它並不直接進行轉發,而是緩存在本地,再按照原來SYN Cookie的機制製作好一個針對這個SYN包的SYN+ACK包,注意,這個SYN+ACK包中的ack順序號爲特製的cookie值c,更重要的是這個包的的源地址被僞造成了S的地址(爲了描述方便,我們這裏暫時不考慮NAT等其他因素)。這樣C會接收到這個SYN+ACK包,並認爲是從S反饋回來的。於是C再響應一個ACK包,並認爲與S的TCP連接已經建立起來。這時防火牆F收到這個ACK包,按照前面的描述的SYN Cookie原理來檢查這個ACK中的ack順序號。如果認爲合法,F將本地緩存的來自C的SYN包發送給S,這時S會響應一個SYN+ACK包到C,其中也攜帶一個seq號, 我們設爲c`。當然這個包不會到達C,而是由防火牆F截取,F根據這個包中的序列號等信息,造一個ACK包響應到S。這時的情況是:C認爲自己已經與S建立了TCP連接;S認爲自己與C建立了TCP連接。以後的TCP數據內容可以直接穿過防火牆F,在S和C之間交互。


 

上圖是SYN Cookie Firewall的工作原理,它相當於在TCP Server與TCP Client之間實現了對三次握手協議的代理。第一次"三次握手"在TCP Client與防火牆之間進行,第二次"三次握手"在防火牆與TCP Server之間。在第一次"三次握手"時使用前面介紹的SYN Cookie流程。有一個問題在進行兩次"三次握手"時出現了:如圖所示,進行第一次"三次握手"後,TCP Client認爲後續數據包的seq值從c+1開始,而進行第二次"三次握手"後,TCP Server認爲後續發來的數據包的seq值從c`+1開始, c是cookie,c`是TCP Server隨機產生的。c和c`幾乎不可能相等,也就是說在完成上面的兩個"三次握手"後,如果不進行其他操作,後續從TCP Client到TCP Server的數據包都將被認爲順序號不對而被丟掉。一種補救方法就是在防火牆本地保存一個值δ 
δ = |c - c`| 
利用這個差值,在每個數據包經過防火牆時,將其seq值修改一下,這樣,後續的數據流量可以完美地在TCP Server和TCP Client之間傳輸了。

 

總結

現在普遍使用的IPv4協議帶有很多安全上的問題,其中面對SYN Flood***的軟弱就是一點。在不改變TCP三次握手流程的情況下,TCP Server幾乎不可能有效的防範SYN Flood的***。要保證完全防範SYN Flood,必須修改三次握手協議。SYN Cookie是一種很有效的方法。它的思想比較簡單,主要是如何具體的實現,Linux系統也提供了一種實現。作者通過研讀Linux2.4.20內核中的代碼,基本瞭解了Linux內核中實現SYN Cookie的手段,將其總結成文字,與對SYN Cookie同樣感興趣的朋友分享、交流。


關於作者

魏晉偉,通過 [email protected]可以跟他聯繫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章