nf_conntrack: table full, dropping packet 問題排查和解決


 

nf_conntrack模塊在kernel 2.6.15(2006-01-03發布) 被引入,支持ipv4和ipv6,取代只支持ipv4的ip_connktrack,用於跟蹤連接的狀態,供其他模塊使用。

 

最常見的使用場景是 iptables 的 nat 和 state 模塊:

 

  • nat 根據轉發規則修改IP包的源/目標地址,靠nf_conntrack的記錄才能讓返回的包能路由到發請求的機器。
  • state 直接用 nf_conntrack 記錄的連接狀態(NEW/ESTABLISHED/RELATED/INVALID)來匹配防火牆過濾規則。

 

 

 

iptables
 

 

 

 

nf_conntrack用1個哈希表記錄已建立的連接,包括其他機器到本機、本機到其他機器、本機到本機(例如 ping 127.0.0.1也會被跟蹤)。

 

如果連接進來比釋放的快,把哈希表塞滿了,新連接的數據包會被丟掉,此時netfilter變成了一個黑洞,導致拒絕服務。 這發生在3層(網絡層),應用程序毫無辦法。

 

各發行版區別:

 

  • CentOS (7.3) 默認加載該模塊
  • Ubuntu (16.10+) 和 Kali Linux (2016.1+) 默認不加載,不會有這問題

 

為什么需要這種狀態跟蹤機制呢?比如你的80端口開啟,而你的程序被植入反彈式木馬,導致服務器主動從80端口向外部發起連接請求,這個時候你怎么控制呢。關掉80端口,那么你的網站也無法正常運行了。

但有了連接跟蹤你就可以設置只允許回復關於80端口的外部請求(ESATBLISHED狀態),而無法發起向外部的請求(NEW狀態)。所以有了連接跟蹤就可以做到在這種層面上的限制,慢慢往下看就明白了各狀態的意義。

在內核中由Netfilter的特定框架做的連接跟蹤稱作conntrack(connection tracking)。conntrack可以作為模塊安裝,也可以作為內核的一部分。大部分情況下,我們想要也需要更詳細的連接跟蹤。

conntrack中有許多用來處理TCP,UDP或ICMP協議的部件。這些模塊從數據包中提取詳細的、唯一的信息,因此能保持對每一個數據流的跟蹤。這些信息也告知conntrack流當前的狀態。

例如,UDP流一般由他們的目的地址、源地址、目的端口和源端口唯一確定。

在以前的內核里,我們可以打開或關閉重組功能。然而連接跟蹤被引入內核后,這個選項就被取消了。因為沒有包的重組,連接跟蹤就不能正常工作。現在重組已經整合入conntrack,並且在conntrack啟動時自動啟動。不要關閉重組功能,除非你要關閉連接跟蹤。除了本地產生的包由OUTPUT鏈處理外,所有連接跟蹤都是在PREROUTING鏈里進行處理的,意思就是, iptables會在PREROUTING鏈里從新計算所有的狀態。

如果我們發送一個流的初始化包,狀態就會在OUTPUT鏈里被設置為NEW,當我們收到回應的包時,狀態就會在PREROUTING鏈里被設置為ESTABLISHED。如果第一個包不是本地產生的,那就會在PREROUTING鏈里被設置為NEW狀態。綜上,所有狀態的改變和計算都是在nat表中的PREROUTING鏈和OUTPUT鏈里完成的。conntrack默認最大跟蹤65536個連接

連接跟蹤表溢出
連接跟蹤表nf_conntrack,Linux為每個經過內核網絡棧的數據包,生成一個新的連接記錄項,當服務器處理的連接過多時,連接跟蹤表被打滿,服務器會丟棄新建連接的數據包。
如何確認
通過dmesg可以確認是否有該情況發生

dmesg | grep nf_conntrack

如果輸出值中有“nf_conntrack: table full, dropping packet”,說明服務器nf_conntrack表已經被打滿。

通過/proc文件系統查看nf_conntrack表實時狀態:

跟蹤連接詳細信息:

cat /proc/net/nf_conntrack

查看nf_conntrack表最大連接數

cat /proc/sys/net/netfilter/nf_conntrack_max


查看nf_conntrack表當前連接數

cat /proc/sys/net/netfilter/nf_conntrack_count

查看nf_conntrack_buckets哈希表最大大小

cat /proc/sys/net/netfilter/nf_conntrack_buckets

查看netfilter相關的內核參數

sysctl -a | grep conntrack

如果確認服務器因連接跟蹤表溢出而開始丟包,首先需要判斷是否正遭受DDOS攻擊,如果是正常的業務流量造成,可以考慮調整nf_conntrack的參數
nf_conntrack_max決定連接跟蹤表的大小,默認值是65536,可以根據系統內存大小計算一個合理值:CONNTRACK_MAX = RAMSIZE(in bytes)/16384/(ARCH/32),如32G內存可以設置1048576;
一個連接占用312字節內存,65536大約25M內存占用,存儲在內核空間,這部分內存不能swap,當CONNTRACK_MAX為 1048576,HASHSIZE buckets為 262144 時,最多占390MB內存開銷,對現在的vps服務器來說毫無壓力,你用512M的vps跑業務當我沒說。
nf_conntrack_buckets決定存儲conntrack條目的哈希表大小,默認值是nf_conntrack_max的1/4,延續這種計算方式:BUCKETS = CONNTRACK_MAX/4,如32G內存可以設置262144;
net.netfilter.nf_conntrack_tcp_timeout_established # 決定ESTABLISHED狀態連接的超時時間,默認值是5天,可以縮短到1小時,即3600,這個值基本上沒什么效果,還是加上吧。
net.netfilter.nf_conntrack_tcp_timeout_fin_wait # 默認 120 秒
net.netfilter.nf_conntrack_tcp_timeout_time_wait # 默認 120 秒,可以適當修改這個值
net.netfilter.nf_conntrack_tcp_timeout_close_wait # 默認 60 秒,CLOSE_WAIT是被動方收到FIN發ACK,然后會轉到LAST_ACK發FIN,除非程序寫得有問題,正常來說這狀態持續時間很短。
net.netfilter.nf_conntrack_generic_timeout # 默認 600 秒(10分鍾)通用超時設置,作用於4層(傳輸層)未知或不支持的協議
net.netfilter.nf_conntrack_tcp_timeout_max_retrans # 默認 300 秒
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged # 默認 300 秒
推薦bucket至少 262144,max至少 1048576,不夠再繼續加,或者縮短超時時間可以讓netfilter更快地把跟蹤的記錄從哈希表里移除。
哈希表使用情況:

grep conntrack /proc/slabinfo

前面三個數字代表
當前活動對象數、當前使用對象總數、每個對象的內存占用大小(字節)

sysctl -w net.netfilter.nf_conntrack_max=1048576

echo 262144 > /sys/module/nf_conntrack/parameters/hashsize

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

通過sysctl -w和echo的命令,是臨時生效的,重啟會復原也可以編輯文件添加:

vim /etc/sysctl.conf

net.netfilter.nf_conntrack_max = 1048576

net.netfilter.nf_conntrack_tcp_timeout_established = 3600

修改后,執行sysctl -p 生效
和buckets表開機自動修改

echo "echo 262144 > /sys/module/nf_conntrack/parameters/hashsize" >> /etc/rc.d/rc.local

如果業務沒有涉及到狀態鏈路相關的應用,直接不加載這個模塊就可以了。

 

 

 

 

 

 

 

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM