Linux快速統計TCP半連線的數量

2021-10-04 05:33:34 字數 2407 閱讀 4687

如果你想知道當前系統(執行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模式 一文 來提高單位時間內的併發量。但是...