如果你想知道當前系統(執行linux核心)中處在syn recv狀態的tcp半連線數量的大小,最樸素的方法莫過如下:
netstat -antp|
grep syn_recv|
wc -l
ss -ant|
grep syn-recv|
wc -l
有什麼問題嗎?
方法樸素歸樸素,但只能應對日常需求,如果遇到syn flood攻擊,執行netstat/ss -ant命令需要掃瞄遍歷系統中所有的連線,然後再grep出半連線,在系統已經遭受攻擊時,遍歷操作無疑是雪上加霜!
你想如果上百萬的半連線到達你的系統,會怎樣?
然而,令人遺憾的是,系統中沒有這樣的關於syn recv狀態的連線總量的之際統計值,至少我是沒有找到,無論從ss -s還是從netstat -s,或者說snmp中,都沒有找到這樣的統計值。
那麼如何在系統在面臨攻擊已經扛不住的情況下,快速統計半連線的數量,這無疑是乙個很有意思的工作。
也許你會想到tcp_diag模組,但是它依然是遍歷,只不過是採用更加複雜的netlink介面(研究的不多)。當我看inet_diag_dump_icsk函式時,我失望至極!所以,激動的心瞬間又沉了下去。
2023年第乙個雷雨天,開了一下午會,飯也沒吃一口,寫下這篇文章。
在系統壓力很大時,遍歷所有的socket不現實,但是listen socket的數量是固定的,你見過系統的listener超過1萬的嗎?不超過1萬,遍歷開銷就不是事兒。
系統的listener不會隨著系統壓力的增加而增加,它們分布在inet_lhtable_size個hash桶中,每乙個listener都儲存著自己的半連線計數,用其listen_sock的qlen欄位計數,我們只需要把這些加起來就是結果了。
看來只有自己動手了。下面是**:
#include
#include
static
unsigned
intdump_syn_recv
(void
)spin_unlock_bh
(&ilb->lock);}
return num;
}static ssize_t dump_read
(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos);if
(*ppos !=0)
n =snprintf
(kbuf,16,
"%d\n"
, num)
;memcpy
(ubuf, kbuf, n)
;*ppos +
= n;
return n;
}static
struct file_operations dump_ops =
;static
struct proc_dir_entry *ent;
static
int __init dumpstat_init
(void
)static
void __exit dumpstat_exit
(void
)module_init
(dumpstat_init)
;module_exit
(dumpstat_exit)
;module_license
("gpl"
);
下面是乙個模擬攻擊時的測試結果:
# cat /proc/syn-recv-cnt
82719
也許有人會質疑這裡面的兩把鎖,其實這種質疑是徒勞的:
由於這個dump操作基本上是乙個oneshot的低頻操作,且在listener固定的情況下,它的時間複雜度是o(1)的,所以我覺得是ok的,這主要要感謝的是,核心自己做了lopt->qlen計數統計,我們只需要將每乙個listener的該字段累加起來就是了。
沒有必要看見有鎖操作就dis,這是一種因噎廢食的偏見。
我不明白為什麼tcp diag沒有提供乙個輕量級的如此這般的dump機制,也許是我沒有注意到,不過隨他去吧,反正我這個已經夠輕量了。
netstat/ss工具好不好?當然好,但是它是常規意義上的好。同樣的案例還有延伸到iptables/nftables,ebpf/xdp,如果精準化特定場景,還是需要自己動手來搞非通用方案。
為什麼linux核心為什麼直到現在都沒有乙個計數器來統計半連線的數量?因為那是針對tcp的專有需求,linux核心顯然不是偏向於tcp的。無論如何,我覺得為其增加一些percpu的無鎖計數器,是優雅的做法,你至少可以應對別人說你引入原子變數鎖匯流排的開銷,是吧,哈哈。
當然,這些話並不是對經理說的。
浙江溫州皮鞋溼,下雨進水不會胖。
TCP半連線佇列和全連線佇列
半連線佇列 syn queue 全連線佇列 accept queue ss lnt recv q send q local address port peer address port 0 100 8080 當連線處於時listen狀態,send q表示accept queue的最大值,recv q...
Linux 半連線佇列,全連線佇列
socket 中 listen api中引數backlog指定的是 全佇列大小 accept api是從全佇列中獲取,沒有就阻塞了,直到有新連線進來.listen中指定的值大小,有乙個最大上限,這個上限是系統核心中設定的.在配置檔案中 proc sys net core somaxconn 這個值預...
linux 系統優化tcp連線
提高伺服器的負載能力,是乙個永恆的話題。在一台伺服器cpu和記憶體資源額定有限的情況下,最大的壓榨伺服器的效能,是最終的目的。要提高linux系統下的負載能力,可以先啟用apache的worker模式 參考我寫的 ubuntu下配置apache的worker模式 一文 來提高單位時間內的併發量。但是...