1. 背景介紹
為了防范SYN Flood攻擊,
Daniel J. Bernstein等人提出了SYN Cookie機(jī)制,主要思想就是收到SYN報(bào)文時(shí),在服務(wù)器端不保存任何信息,構(gòu)造特殊的序列號(hào)后回復(fù)SYN-ACK報(bào)文,減少這種拒絕服務(wù)攻擊對(duì)服務(wù)器的壓力。收到ACK報(bào)文后,通過(guò)序列號(hào)解析判斷其是否合法,決定連接是否建立。關(guān)于SYN Cookie的詳細(xì)資料,請(qǐng)參考wikipedia的說(shuō)明[1]。
本文主要描述IPV4協(xié)議棧收到SYN包后回復(fù)SYN-ACK報(bào)文、收到ACK包時(shí)檢驗(yàn)其合法性時(shí),計(jì)算與校驗(yàn)Cookie值的方法。參考源碼為L(zhǎng)inux 2.6.28的net/ipv4/syncookies.c。
2. 實(shí)現(xiàn)分析
目前的cookie是一個(gè)__u32的值,相關(guān)的算法實(shí)現(xiàn)主要包括兩部分:
計(jì)算Cookie值做為回復(fù)SYN-ACK的序列號(hào),在處理SYN報(bào)文的tcp_v4_conn_request()中調(diào)用,入口為cookie_v4_init_sequence ();
檢驗(yàn)Cookie值是否合法,在tcp_v4_hnd_req ()中調(diào)用cookie_v4_check()->cookie_check()檢查cookie值是否合法;
此外,在初始化時(shí),需要調(diào)用init_syncookies()初始化數(shù)組__u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS],以備計(jì)算cookie時(shí)使用,通過(guò)調(diào)用random.c的get_random_bytes()實(shí)現(xiàn),此處不再綴述。
2.1 Cookie的計(jì)算
函數(shù)調(diào)用關(guān)系主要為:
cookie_v4_init_sequence()=>secure_tcp_syn_cookie()=>cookie_hash()
根據(jù)secure_tcp_syn_cookie ()中的代碼,我們可以得出,Cookie主要是由一些信息及其hash值相加得到的。代碼內(nèi)容如下:
|
return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
sseq + (count << COOKIEBITS) +
((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
& COOKIEMASK));
|
其中,COOKIEBITS為24,COOKIEMASK為低24位的掩碼,即((__u32)1 << COOKIEBITS) - 1。而cookie_hash()的前四個(gè)參數(shù)為地址、端口信息,第五個(gè)參數(shù)count是系統(tǒng)的分鐘數(shù),用jiffies/(HZ*60)計(jì)算得到,第六個(gè)參數(shù)為0或1,指明使用syncookie_secret[0]還是syncookie_secret[1]來(lái)計(jì)算hash值。
Cookie的組成由圖1所示:
圖1 Cookie的組成
如圖1說(shuō)明,Cookie一共由三部分加和而成:
以源/目的地址和端口、0以及syncookie_secret[0]為輸入的hash值
sseq序列號(hào)
高八位為當(dāng)前分鐘數(shù),即jiffies/(60*HZ),低24位為hash值+data后的24位,其中,hash值以源/目的地址和端口、當(dāng)前分鐘數(shù)和syncookie_secret[1]為輸入。
在cookie中加入的data只包括了低24位信息,在當(dāng)前版本(8-3-0-3)的BVS中,data其實(shí)只保存了協(xié)商后的MSS值在msstab中的索引值。
2.2 Cookie的計(jì)算
如圖1所示,三部分加和中,第一和三部分所用的hash算法相同。同時(shí),顯然可以得出,收到ACK報(bào)文時(shí),第一個(gè)hash值是不變的,sseq是很容易求出的(ntohl(ack_th->ack_seq) - 1)即可)。而count值可以取出,但不是現(xiàn)在的系統(tǒng)分鐘數(shù)。設(shè)發(fā)送SYNACK時(shí)時(shí)間jiffies,系統(tǒng)分鐘數(shù)為count,收到ACK時(shí)時(shí)間為jiffies,系統(tǒng)分鐘數(shù)為count,即
count = 向下取整(jiffies/(60*HZ)),count = 向下取整(jiffies/(60*HZ))
那么,分鐘數(shù)的差值diff=count-count <!--[if !vml]--><!--[endif]-->
據(jù)此,我們可以進(jìn)行cookie的第一步校驗(yàn)——檢查時(shí)間值,代碼與分析如下:
|
//首先,去掉cookie三部分加和中的前兩部分
cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
//接下來(lái),取出cookie的高八位,即發(fā)送SYN-ACK時(shí)編碼進(jìn)去的當(dāng)時(shí)的系統(tǒng)分鐘數(shù),用
//當(dāng)前分鐘數(shù)去減,即為前后時(shí)間的差值,精確到分鐘。
diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);
//如果diff值過(guò)大,則認(rèn)定cookie非法
if (diff >= maxdiff)
return (__u32)-1;
|
如果diff值合理,則進(jìn)一步解出data的低24位:
|
return (cookie -
cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
& COOKIEMASK; /* Leaving the data behind */
|
接下來(lái),判斷data的低24位是否合法,即判斷data的低24位是否屬于msstab的合法索引,如果合法,則通過(guò)校驗(yàn),否則,校驗(yàn)失敗,判斷ACK報(bào)文非法。
|
return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;
|
從上面的分析可以看出,cookie將一段數(shù)據(jù)編碼進(jìn)序列號(hào),然后通過(guò)拆解ACK報(bào)文的序列號(hào),確定tcp協(xié)商的選項(xiàng)MSS的協(xié)商結(jié)果。data可用的位一共24位,全部用來(lái)存儲(chǔ)mssind以便解析檢驗(yàn)。
2.3 其他TCP選項(xiàng)的處理
Linux 2.6.26中的syncookie機(jī)制實(shí)現(xiàn)了對(duì)MSS外的一些TCP選項(xiàng)的支持,但并未像MSS一樣編碼進(jìn)cookie,即序列號(hào),而是在支持timestamp的情況下,放入了timestamp的低9位。
收到SYN,在構(gòu)造序列號(hào)完成后回復(fù)SYNACK時(shí),調(diào)用如下函數(shù)處理其他TCP選項(xiàng)支持的問(wèn)題:
__tcp_v4_send_synack()
=>__tcp_v4_send_synack()
=>tcp_make_synack()
=>cookie_init_timestamp()
在cookie_init_timestamp()中,將TCP選項(xiàng)支持寫(xiě)入了timestamp的低九位。
在校驗(yàn)時(shí)的調(diào)用cookie_v4_check()校驗(yàn)完MSS后,調(diào)用cookie_check_timestamp()從timestamp中拆出TCP選項(xiàng)。參考[2]中給出了當(dāng)年這個(gè)實(shí)現(xiàn)的patch。
3. 參考資料
4.主要函數(shù)