TCP握手的時候維護的隊列
- 半連接隊列(SYN隊列)
- 全連接隊列(accepted隊列)
半連接隊列是什么?
服務(wù)器收到客戶端SYN數(shù)據(jù)包后,linux內(nèi)核會把該連接存儲到半連接隊列中,并響應(yīng)SYN+ACK報文給客戶端。
全連接隊列是什么?
服務(wù)器在收到客戶端第三次握手的ACK報文后,內(nèi)核會把該連接從半連接隊列中移除,然后創(chuàng)建一個新的完全的連接并將其放入全連接隊列中,等待應(yīng)用程序調(diào)用accept函數(shù)把連接取走。
隊列溢出會有啥問題?
半連接和全連接隊列都有最大長度限制,超過限制時,內(nèi)核會直接丟棄,返回RST報文。
如何查看全連接隊列?
# -l 顯示正在listening的socket
# -n 不解析服務(wù)名稱
# -t 只顯示tcp socket
ss -lnt
Recv-Q/Send-Q在Listen狀態(tài)和非Listen狀態(tài)下代表的含義不一樣。
在Listen狀態(tài)下:
- Recv-Q:當(dāng)前全連接隊列的大小,完成三次握手等待accept的TCP連接
- Send-Q:全連接隊列的最大長度,上圖中表示80端口的TCP服務(wù)全連接隊列的最大長度為128。
在非Listen狀態(tài)下:
- Recv-Q:已收到但未被應(yīng)用程序讀取的字節(jié)數(shù)
- Send-Q:已發(fā)送但未收到確認的字節(jié)數(shù)
如何查看TCP全連接隊列溢出情況?
netstat -s | grep overflowed
通過上圖可以看出,全連接隊列一共溢出了1910次,如果這個數(shù)字在某個時間段一直增加,說明該時間段的全連接隊列已經(jīng)滿載溢出。
全連接隊列溢出后的情況?
全連接隊列溢出后的處理受內(nèi)核參數(shù)tcp_abort_on_overflow控制:
# 通過以下命令可以看出默認值是0
cat /proc/sys/net/ipv4/tcp_abort_on_overflow
- 0:如果全連接隊列滿了,服務(wù)端會直接丟棄客戶端發(fā)送過來的ACK數(shù)據(jù)包
- 1:如果全連接隊列滿了,服務(wù)端會發(fā)送一個reset包給客戶端,表示廢棄這個握手過程和連接
假設(shè)服務(wù)端將tcp_abort_on_overflow設(shè)置為1,在服務(wù)端全連接隊列滿載以后,客戶端如果還在不停的發(fā)起連接將會收到connection reset by peer的錯誤。
tcp_abort_on_overflow設(shè)置為0更有利于應(yīng)對突發(fā)流量。
如何增加TCP全連接隊列?
TCP全連接隊列的最大值取決于somaxconn和blacklog之間的最小值:
- somaxconn:linux內(nèi)核參數(shù),可以通過/proc/sys/net/core/somaxconn修改其值
- backlog:listen(int sockfd, int backlog)函數(shù)中的backlog大小,一般應(yīng)用程序都可以自定義該值
如何查看TCP半連接隊列長度?
# 執(zhí)行以下命令就可以查看半連接隊列的大小
netstat -natp | grep SYN_RECV | wc -l
如何查看TCP半連接隊列溢出的情況?
netstat -s | grep 'SYNs to LISTEN'
上圖中我們可以看出半連接隊列累計一共丟棄了220418個TCP連接,如果多次執(zhí)行該命令該值有上升趨勢,說明當(dāng)前時間段存在半連接隊列溢出現(xiàn)象。
半連接隊列大小受哪些條件影響?
半連接隊列的大小受內(nèi)核參數(shù)tcp_max_syn_backlog控制,但是該值不一定是半連接隊列的最大值(在較新版本的linux內(nèi)核中理論上半連接隊列的最大值是全連接隊列的最大值),隊列的溢出是有一定條件的:
- 如果半連接隊列滿了,并且沒有開啟tcp_syncookies,就會丟棄連接
- 如果全連接隊列滿了,并且沒有重傳SYN+ACK包的連接請求多余1個,也會丟棄連接
- 如果沒有開啟tcp_syncookies,并且tcp_max_syn_backlog減去當(dāng)前半連接隊列長度小于(tcp_max_syn_backlog >> 2),則會丟棄連接
什么是syncookies?
syncookies是服務(wù)器根據(jù)當(dāng)前狀態(tài)計算一個值,然后再SYN+ACK報文中發(fā)出,當(dāng)客戶端返回ACK報文時,服務(wù)端取出該值進行驗證,驗證成功則認為連接建立成功。
開啟syncookies功能可以在不使用半連接隊列的情況下成功建立連接。
如何設(shè)置syncookies的值?
通過內(nèi)核tcp_syncookies參數(shù)進行控制:
# 可以查看tcp_syncookies的值
cat /proc/sys/net/ipv4/tcp_syncookies
tcp_syncookies不同的值分別為不同的含義:
- 0:表示關(guān)閉tcp_syncookies功能
- 1:只有當(dāng)半連接隊列滿時再啟用tcp_syncookies功能
- 2:表示無條件開啟tcp_syncookies功能
如何抵御SYN攻擊?
SYN攻擊是指對服務(wù)端一直發(fā)送SYN包,但不回第三次握手的ACK數(shù)據(jù)包,此時會導(dǎo)致服務(wù)端有大量的SYN_RECV的TCP連接。
抵御SYN攻擊可以采取以下方法:
- 增大半連接隊列
- 開啟tcp_syncookies功能
- 減少SYN+ACK的重傳次數(shù)