Linux Netfilter 調優

語言: CN / TW / HK

本文永久連結:  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.

狀態檢視

  • 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 = 記憶體個數*1024*1024*1024/16384/(ARCH/32)
    
    • 其中 ARCH 為 CPU 架構,值為 32 或 64。
    • 比如:64 位 8G 記憶體的機器:(8*1024^3)/16384/(64/32) = 262144

臨時調整

臨時調整是臨時性的,重啟節點好配置值將會丟失。

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 位系統還要關心核心態的地址空間夠不夠。

netfilter 的雜湊表儲存在核心態的記憶體空間,這部分記憶體不能 swap,作業系統為了相容 32 位,預設值往往比較保守。

  • 32 位系統的虛擬地址空間最多 4G,其中核心態最多 1G,通常能用的只有前 896M。

    給 netfilter 分配太多地址空間可能會導致其他核心程序不夠分配。1 條跟蹤記錄 300 位元組左右,因此當年 nf_conntrack_max 預設 65535 條,佔 20 多 MB。

  • 64 位系統的虛擬地址空間有 256 TB,核心態能用一半,只需要關心實體記憶體的使用情況。

  • 計算記憶體使用的公式

    size_of_mem_used_by_conntrack (in bytes) = CONNTRACK_MAX * sizeof(struct ip_conntrack) + HASHSIZE * sizeof(struct list_head)
    
    • sizeof(struct ip_conntrack) 在不同架構、核心版本、編譯選項下不一樣。這裡按 352 位元組算。

    • sizeof(struct list_head) = 2 * size_of_a_pointer(32 位系統的指標大小是 4 位元組,64 位是 8 位元組)

    • 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 上正確設定連線跟蹤超時。

可以執行以下命令檢視預設值:

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 天)

    • 這個值對應的場景是 “雙方建立了連線後一直不發包,直到 5 天后才發”
    • 但預設 keep-alive 超時時間只有 2 小時 11 分(net.ipv4.tcp_keepalive_time + net.ipv4.tcp_keepalive_intvl * net.ipv4.tcp_keepalive_probes),由於超時關 socket 不發包,conntrack 無法根據包頭的標識知道狀態的變化,記錄會一直處於 ESTABLISHED 狀態,直到 5 天后倒計時結束才刪掉。
    • 空連線攻擊的最佳目標。攻擊者把 IP 包頭的源地址改成隨機 IP,握完手就關 socket,用一臺機發請求就能把你的雜湊表填滿。
  • net.netfilter.nf_conntrack_tcp_timeout_syn_recv:預設 60

    • 類似,故意不發握手的 ACK 即可。但這個超時時間沒那麼誇張,系統也有 syn cookie 機制來緩解 syn flood 攻擊。

其他值得注意的引數:

  • net.netfilter.nf_conntrack_tcp_timeout_syn_sent:預設 120

    • 你的程式的 connect timeout 有這麼長嗎?
  • 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 秒才超時?

這幾個倒是比較合理,小於等於可能遇到的極端情況,但如果不想半關閉的連線的記錄繼續佔著寶貴的雜湊表,提早清了似乎也沒什麼問題:

  • 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 狀態是讓被動關閉方把該傳的資料傳完。如果程式寫得不好,這裡拋了未捕捉的異常,也許就走不到發 FIN 那步了,一直停在這裡。
  • net.netfilter.nf_conntrack_tcp_timeout_last_ack:預設 30

    • 被動關閉方發 FIN 後如果一直收不到對面的 ACK 或 RST,會不斷重發,直到超時才 CLOSE。net.ipv4.tcp_retries2 的預設值是 15,最多要等 924.6 秒……不過一般都會調小這個值。

調整引數

新增以下配置引數到 /etc/sysctl.conf 檔案,最後執行 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