
本文永久鏈接: https://www.xtplayer.cn/linux/netfilter/linux-netfilter-optimization/
如果您正在為高流量的 Web/DNS 服務器提供服務,并且最近使該服務器 PING 丟失并且并非所有 HTTP 請求都成功。您可以開始檢查系統日志。并且如果您看到類似下面的內容,那么下面的指南將幫助您調整 Linux 服務器以正確處理流量負載。
Mar 22 21:25:55 localhost kernel: nf_conntrack: table full, dropping packet.
Mar 22 21:26:00 localhost kernel: printk: 11 messages suppressed.
Mar 22 21:26:00 localhost kernel: nf_conntrack: table full, dropping packet.
Mar 22 21:26:05 localhost kernel: printk: 16 messages suppressed.
狀態(tài)查看
-
buckets 哈希表大小,max 最大記錄的連接條數
sudo dmesg | grep conntrack - [ 8.782060] nf_conntrack version 0.5.0 (16384 buckets, 65536 max)
-
哈希表使用情況
grep conntrack /proc/slabinfo - nf_conntrack_1 102 102 320 51 4 : tunables 0 0 0 : slabdata 2 2 0
-
當前跟蹤的連接數
sudo sysctl net.netfilter.nf_conntrack_count -
跟蹤連接詳細信息
# centos cat /proc/net/nf_conntrack # ubuntu,可能需要安裝 conntrack 工具,yum install -y conntrack 或者 apt-getinstall -y conntrack conntrack -L
最大連接跟蹤數
為了完成任務,NAT-server(一般指的是 iptables) 需要記錄所有通過它的連接。無論是 “ping” 還是某人的 “ICQ”,NAT-server 都會記錄在一個特殊的表中并跟蹤所有會話。當會話關閉時,相關記錄將從連接跟蹤表中刪除。這個記錄表的大小是固定的,所以如果通過服務器的流量很大,但表太小,那么 NAT-server 就會開始丟棄數據包,中斷會話。為了避免這樣的麻煩,有必要適當增加連接跟蹤表的大小。
-
最大連接跟蹤數默認為 nf_conntrack_buckets * 4,可以通過以下命令查看當前值:
sysctl net.netfilter.nf_conntrack_buckets - sysctl net.netfilter.nf_conntrack_max
-
CONNTRACK_MAX 默認計算公式
CONNTRACK_MAX = 內存?zhèn)€數*1024*1024*1024/16384/(ARCH/32)- 其中 ARCH 為 CPU 架構,值為 32 或 64。
- 比如:64 位 8G 內存的機器:(8*1024^3)/16384/(64/32) = 262144
臨時調整
臨時調整是臨時性的,重啟節(jié)點好配置值將會丟失。
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.nf_conntrack_max=1048576
永久調整
要使其配置在重新啟動后永久存在,需要將這些值添加到 sysctl.conf 中
echo 'net.netfilter.nf_conntrack_max' = 1048576 >> /etc/sysctl.conf
echo 'net.nf_conntrack_max = 1048576' >> /etc/sysctl.conf sysctl -p
如果服務器中的 RAM 少于 1 GB,建議不要設置太大的值。
哈希表(hash-table)
哈希表大小是只讀的,不能在 /etc/sysctl.conf 文件中設置值。64 位 Linux 系統中,4G 內存默認 16384,8G 內存默認 65536,16G 翻倍,以此類推。
給哈希表擴容的影響
主要是內存使用增加,32 位系統還要關心內核態(tài)的地址空間夠不夠。
netfilter 的哈希表存儲在內核態(tài)的內存空間,這部分內存不能 swap,操作系統為了兼容 32 位,默認值往往比較保守。
-
32 位系統的虛擬地址空間最多 4G,其中內核態(tài)最多 1G,通常能用的只有前 896M。
給 netfilter 分配太多地址空間可能會導致其他內核進程不夠分配。1 條跟蹤記錄 300 字節(jié)左右,因此當年 nf_conntrack_max 默認 65535 條,占 20 多 MB。
64 位系統的虛擬地址空間有 256 TB,內核態(tài)能用一半,只需要關心物理內存的使用情況。
-
計算內存使用的公式
size_of_mem_used_by_conntrack (in bytes) = CONNTRACK_MAX * sizeof(struct ip_conntrack) + HASHSIZE * sizeof(struct list_head)sizeof(struct ip_conntrack) 在不同架構、內核版本、編譯選項下不一樣。這里按 352 字節(jié)算。
sizeof(struct list_head) = 2 * size_of_a_pointer(32 位系統的指針大小是 4 字節(jié),64 位是 8 字節(jié))
64 位系統,8G 內存的機器,按默認 CONNTRACK_MAX 為 262144,HASHSIZE 為 65536 時:262144 * 352 + 65536 * 8 = 92798976(88.5 MB)
-
互聯網公司的服務器通常內存沒那么緊張,可以放開點:
- CONNTRACK_MAX 為 1048576,HASHSIZE 為 262144 ,內存大概使用:1048576 * 352 + 262144 * 8 = 371195904(354 MB)
哈希表大小調整
需要通過內核模塊的方式修改:
-
臨時生效:
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize -
永久生效
將以下內容添加到文件:/etc/modprobe.d/iptables.conf(如果沒有則新建)
echo 'options nf_conntrack hashsize=262144' >> /etc/modprobe.d/iptables.conf
減少超時時間
NAT-server 只跟蹤通過它的 活動 的會話。如果一個會話很長時間是空閑的,不活躍,它將會因為超值而被關閉。當會話關閉時,關于它的信息將被刪除,以便連接跟蹤表不會溢出。
但是,如果超時的默認值很大,流量較大時候,即使將 nf_conntrack_max 擴展到了極限,跟蹤表仍然有溢出的風險。為此,必須在 NAT-server 上正確設置連接跟蹤超時。
可以執(zhí)行以下命令查看默認值:
sysctl -a | grep conntrack | grep timeout
-
Ubuntu 16.04
net.netfilter.nf_conntrack_generic_timeout = 600 - net.netfilter.nf_conntrack_icmp_timeout = 30
- net.netfilter.nf_conntrack_tcp_timeout_close = 10 net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60 net.netfilter.nf_conntrack_tcp_timeout_established = 432000 net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120 net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30 net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300 net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60 net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120 net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300 net.netfilter.nf_conntrack_udp_timeout = 30
- net.netfilter.nf_conntrack_udp_timeout_stream = 180
-
centos 7.8
net.netfilter.nf_conntrack_dccp_timeout_closereq = 64 net.netfilter.nf_conntrack_dccp_timeout_closing = 64 net.netfilter.nf_conntrack_dccp_timeout_open = 43200 net.netfilter.nf_conntrack_dccp_timeout_partopen = 480 net.netfilter.nf_conntrack_dccp_timeout_request = 240 net.netfilter.nf_conntrack_dccp_timeout_respond = 480 net.netfilter.nf_conntrack_dccp_timeout_timewait = 240 net.netfilter.nf_conntrack_events_retry_timeout = 15 net.netfilter.nf_conntrack_generic_timeout = 600 - net.netfilter.nf_conntrack_icmp_timeout = 30 net.netfilter.nf_conntrack_sctp_timeout_closed = 10 net.netfilter.nf_conntrack_sctp_timeout_cookie_echoed = 3 net.netfilter.nf_conntrack_sctp_timeout_cookie_wait = 3 net.netfilter.nf_conntrack_sctp_timeout_established = 432000 net.netfilter.nf_conntrack_sctp_timeout_heartbeat_acked = 210 net.netfilter.nf_conntrack_sctp_timeout_heartbeat_sent = 30 net.netfilter.nf_conntrack_sctp_timeout_shutdown_ack_sent = 3 net.netfilter.nf_conntrack_sctp_timeout_shutdown_recd = 0 net.netfilter.nf_conntrack_sctp_timeout_shutdown_sent = 0 net.netfilter.nf_conntrack_tcp_timeout_close = 10 net.netfilter.nf_conntrack_tcp_timeout_close_wait = 3600 net.netfilter.nf_conntrack_tcp_timeout_established = 86400 net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120 net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30 net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300 net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60 net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120 net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300 net.netfilter.nf_conntrack_udp_timeout = 30
- net.netfilter.nf_conntrack_udp_timeout_stream = 180
以上均是以秒為單位的超時值。
對于通外網的服務器,考慮調整以下參數,減少 DDoS 的危害:
-
net.netfilter.nf_conntrack_tcp_timeout_established:默認 432000(5 天)
- 這個值對應的場景是 “雙方建立了連接后一直不發(fā)包,直到 5 天后才發(fā)”
- 但默認 keep-alive 超時時間只有 2 小時 11 分(net.ipv4.tcp_keepalive_time + net.ipv4.tcp_keepalive_intvl * net.ipv4.tcp_keepalive_probes),由于超時關 socket 不發(fā)包,conntrack 無法根據包頭的標識知道狀態(tài)的變化,記錄會一直處于 ESTABLISHED 狀態(tài),直到 5 天后倒計時結束才刪掉。
- 空連接攻擊的最佳目標。攻擊者把 IP 包頭的源地址改成隨機 IP,握完手就關 socket,用一臺機發(fā)請求就能把你的哈希表填滿。
-
net.netfilter.nf_conntrack_tcp_timeout_syn_recv:默認 60
- 類似,故意不發(fā)握手的 ACK 即可。但這個超時時間沒那么夸張,系統也有 syn cookie 機制來緩解 syn flood 攻擊。
其他值得注意的參數:
-
net.netfilter.nf_conntrack_tcp_timeout_syn_sent:默認 120
- 一般程序不需要這么長的超時時間。
-
net.netfilter.nf_conntrack_tcp_timeout_fin_wait:默認 120
- net.ipv4.tcp_fin_timeout 默認 60 秒,通常還會參考 BSD 和 macOS 設成更小的值。這里往往也沒必要這么大。
-
net.netfilter.nf_conntrack_icmp_timeout:默認 30
- 一般 ping 不會等 30 秒才超時。
這幾個倒是比較合理,小于等于可能遇到的極端情況,但如果不想半關閉的連接的記錄繼續(xù)占著寶貴的哈希表,提早清了似乎也沒什么問題:
-
net.netfilter.nf_conntrack_tcp_timeout_time_wait:默認 120
- Linux 里的 MSL 寫死 60 秒(而不是 TCP 標準里拍腦袋的 120 秒),TIME_WAIT 要等 2MSL,這里 120 算是個合理的值。但現在默認有PAWS(net.ipv4.tcp_timestamps),不會出現標準制定時擔心的迷途報文回來碰巧污染了序列號相同的新連接的數據的情況。互聯網公司基本都開 net.ipv4.tcp_tw_reuse,既然半連接都不留這么久,記錄似乎也不需要留這么久。
-
net.netfilter.nf_conntrack_tcp_timeout_close_wait:默認 60
- CLOSE_WAIT 狀態(tài)是讓被動關閉方把該傳的數據傳完。如果程序寫得不好,這里拋了未捕捉的異常,也許就走不到發(fā) FIN 那步了,一直停在這里。
-
net.netfilter.nf_conntrack_tcp_timeout_last_ack:默認 30
- 被動關閉方發(fā) FIN 后如果一直收不到對面的 ACK 或 RST,會不斷重發(fā),直到超時才 CLOSE。net.ipv4.tcp_retries2 的默認值是 15,最多要等 924.6 秒……不過一般都會調小這個值。
調整參數
添加以下配置參數到 /etc/sysctl.conf 文件,最后執(zhí)行 sysctl -p。
net.netfilter.nf_conntrack_icmp_timeout=10 net.netfilter.nf_conntrack_tcp_timeout_syn_recv=5 net.netfilter.nf_conntrack_tcp_timeout_syn_sent=5 net.netfilter.nf_conntrack_tcp_timeout_established=600 net.netfilter.nf_conntrack_tcp_timeout_fin_wait=10 net.netfilter.nf_conntrack_tcp_timeout_time_wait=10 net.netfilter.nf_conntrack_tcp_timeout_close_wait=10 net.netfilter.nf_conntrack_tcp_timeout_last_ack=10
參考鏈接
https://testerhome.com/topics/15824
https://www.cnblogs.com/xiangsikai/p/9525287.html