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
)來匹配防火牆過濾規則。
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
如果業務沒有涉及到狀態鏈路相關的應用,直接不加載這個模塊就可以了。