這篇文檔說明了對netfilter conntrack(還有NAT)性能調整時你必須知道的一些事情。
本文檔最新版可以在下面這個地址找到:
http://www.wallfire.org/misc/netfilter_conntrack_perf.txt
------------------------------------------------------------------------------
這裡我們可以調整兩個參數:
-允許的最大跟蹤連接條目,在這篇文檔中我們叫作CONNTRACK_MAX
-存儲跟蹤連接條目列表的哈西表的大小,在這篇文檔中我們叫做HASHSIZE(下面是這個結構的描述)
CONNTRACK_MAX是在內核內存中netfilter可以同時處理的“任務”(連接跟蹤條目)。
一個跟蹤連接的條目是存儲在一個鏈接起來的列表的一個節點上,每個列表都是一個哈西表的元素。因此每個哈西表的條目(也叫一個桶-bUCket)包含了一個鏈接起來的跟蹤連接條目。
要訪問一個特定包的跟蹤連接條目,內核必須:
-針對一個包中的已經定義的一些字符計算哈西值。這是一個不間斷的計算。
這個哈西值就會被當作哈西表的索引來使用,而跟蹤連接條目的列表就存儲在這裡。
-反復的查看鏈接列表中的跟蹤連接條目以找到匹配的那一個。
這是一個耗資源的操作,依賴於列表的大小(也依賴於列表中被操作的跟蹤連接條目的位置)。
哈西表包含了HASHSIZE大小的鏈接條目。當條目滿的時候(總的跟蹤連接條目數達到了CONNTRACK_MAX),理想狀態下,每個列表(在最優化的條件下)將包含大約CONNTRACK_MAX/HASHSIZE的條目數。
不管你是否有連接,哈西表都將占用一個固定大小的非交換內核內存。但是最大連接跟蹤條目會檢測最多可以存儲多少條目(globally into the
linked lists),也就是說他們最多可以占用多少的內核內存。
這篇文檔將給你一些關於為了達到最好的netfilter conntracking/NAT系統性能,如何調優HASHSIZE和CONNTRACK_MAX值的提示。
CONNTRACK_MAX和HASHSIZE的默認值
============================================
一般來說,CONNTRACK_MAX和HASHSIZE都會設置在“合理”使用的值上,依據可使用的RAM的大小來計算這個值。
CONNTRACK_MAX的默認值
------------------------------
在i386架構上,CONNTRACK_MAX = RAMSIZE (以bytes記) / 16384 =
RAMSIZE (以MegaBytes記) * 64,
因此,一個32位的帶512M內存的PC在默認情況下能夠處理512*1024^2/16384 = 512*64 = 32768個並發的netfilter連接。
但是真正的公式是:
CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (x / 32)
這裡x是指針的bit數,(例如,32或者64bit)
請注意:
-默認的CONNTRACK_MAX值不會低於128
-對於帶有超過1G內存的系統,CONNTRACK_MAX的默認值會被限制在65536(但是可以手工設置成更大的值)
HASHSIZE的默認值
-------------------------
通常,CONNTRACK_MAX = HASHSIZE * 8。這意味著每個鏈接的列表平均包含8個conntrack的條目(在優化的情況並且CONNTRACK_MAX達到的情況下),每個鏈接的列表就是一個哈西表條目(一個桶)。
在i386架構上,HASHSIZE = CONNTRACK_MAX / 8 =
RAMSIZE (以bytes記) / 131072 = RAMSIZE (以MegaBytes記) * 8。
舉例來說,一個32位、帶512M內存的PC可以存儲512*1024^2/128/1024 =
512*8 = 4096 個桶(鏈接表)
但是真正的公式是:
HASHSIZE = CONNTRACK_MAX / 8 = RAMSIZE (以bytes記) / 131072 / (x / 32)
這裡x是指針的bit數,(例如,32或者64bit)
請注意:
-默認HASHSIZE的值不會小於16
-對於帶有超過1G內存的系統,HASHSIZE的默認值會被限制在8192(但是可以手工設置成更大的值)
讀取CONNTRACK_MAX和HASHSIZE
==================================
現在通過/proc文件系統我們可以在運行時讀取CONNTRACK_MAX的值。
在Linux kernel 2.4.23版本前,使用:
# cat /proc/sys/net/ipv4/ip_conntrack_max
在Linux kernel 2.4.23版本後,使用:
# cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max
(舊的 /proc/sys/net/ipv4/ip_conntrack_max是不建議使用的!)
當前的HASHSIZE總是可以在syslog信息中找到(對任何一個內核版本),桶(也就是HASHSIZE)的數目是在ip_conntrack初始化的時候顯示出來的。
對於linux內核2.4.24以後,當前的HASHSIZE值可以在運行時使用下面的命令讀取:
# cat /proc/sys/net/ipv4/netfilter/ip_conntrack_buckets
修改CONNTRACK_MAX和HASHSIZE
====================================
默認的CONNTRACK_MAX和HASHSIZE的值都會因主機的不同而不同,但你可以在只做防火牆的高負載的系統上增加他們。
因此CONNTRACK_MAX和HASHSIZE值如果需要的話可以手工更改。
讀取桶是一個連續性的操作(我們的興趣在於得到一個哈西列表),請記得內核需要不停的遍歷一個鏈接的列表去查找一個跟蹤連接條目。因此一個鏈接列表(CONNTRACK_MAX/HASHSIZE的值在優化的狀態下並且達到上限)的平均值不能設置太大。這個比值默認值是8(當值是自動計算的時候)。
在系統有足夠的內存並且性能真的很重要的時候,你可以試著使平均值是一個跟蹤連接條目配一個哈西桶,這意味著HASHSIZE = CONNTRACK_MAX。
設置CONNTRACK_MAX
---------------------
跟蹤連接的條目是存儲在鏈接的表中的,因此最大的跟蹤鏈接條目(CONNTRACK_MAX)可以很容易的動態調整。
linux內核2.4.23之前,使用:
# echo $CONNTRACK_MAX > /proc/sys/net/ipv4/ip_conntrack_max
linux內核2.4.23之後,使用:
# echo $CONNTRACK_MAX > /proc/sys/net/ipv4/netfilter/ip_conntrack_max
這裡$CONNTRACK_MAX是一個整數。
設置HASHSIZE
----------------
因為數學上的原因,哈西表占有固定的大小。因此HASHSIZE必須在哈西表被創建和開始填充之前就確定。
在linux內核2.4.21之前,必須使用素數作為哈西表的大小,而且要保證這個哈西表能夠有效並通用。非素數的奇數或者其他的數值都是強烈不推薦使用的,因為這樣哈西的分配不能達到最優化的狀態。
從linux內核2.4.21(還有2.6內核)跟蹤連接使用jenkins2b算法,這樣就可以使用所有的數值,但是使用2^n次方運作的最有效。
如果netfilter的跟蹤連接是被編譯進內核中的,哈西表的大小就可以在編譯的時候設置,或者(2.6內核之後)可以作為一個啟動選項ip_conntrack.hashsize=$HASHSIZE。
如果netfilter的跟蹤連接是編譯成一個模塊,哈西表的大小可以在加載模塊的時候設置,使用下面的命令:
# modprobe ip_conntrack hashsize=$HASHSIZE
這裡$HASHSIZE是一個整數。
一個理想的例子:只做防火牆的機器
------------------------------------
在理想的例子中,你有一台機器只做包過濾和NAT(也就是說,基本上沒有用戶空間的使用,至少不會有象代理這樣會不斷的耗費內存空間的東西......)
netfilter跟蹤連接使用的內核內存大小是:
size_of_mem_used_by_conntrack (以bytes記) =
CONNTRACK_MAX * sizeof(struct ip_conntrack) +
HASHSIZE * sizeof(struct list_head)
-這裡:sizeof(struct ip_conntrack)可以有很大的區別,依賴於機器的體系架構,內核版本和編譯時間的配置。要想知道它的大小,可以查看ip_conntrack初始化時候kenel的日志信息。sizeof(struct ip_conntrack)在i386架構、 2.6.5內核上大約是300bytes,但是在2.6.10的內核上,這個值可以在352至192bytes之間變化!
-sizeof(struct list_head) = 2 * size_of_a_pointer
在i386上,size_of_a_pointer是4bytes。
因此在i386,2.6.5內核上,size_of_mem_used_by_conntrack大約是CONNTRACK_MAX * 300 + HASHSIZE * 8 (bytes)。
如果我們使HASHSIZE = CONNTRACK_MAX(如果我們將大部分的內存用來做防火牆的工作,參見“修改 CONNTRACK_MAX和HASHSIZE”部分),在i386架構、2.6.5內核上, size_of_mem_used_by_conntrack大概是CONNTRACK_MAX * 308 bytes。
現在我們假定你使用512M的內存拿來做一個只做防火牆的機器,並且使用128MB以外的內存來做跟蹤連接,對於使用終端模式只做防火牆來說應該是足夠的大的,例如:
你可以同時設置CONNTRACK_MAX和HASHSIZE大致如下:
(512 - 128) * 1024^2 / 308 =~ 1307315 (instead of 32768 for CONNTRACK_MAX,
and 4096 for HASHSIZE by default)。
對於linux2.4.21(和linux2.6),哈西算法最好使用“2的次方”大小(之前是使用素數)。
因此在這裡我們可以將CONNTRACK_MAX和HASHSIZE設置成1048576(2^20)。
這樣,你可以存儲默認值32倍的跟蹤連接條目,而且可以得到更好的跟蹤連接性能。
對於linux2.4.21(和linux2.6),哈西算法最好使用“2的次方”大小(之前是使用素數)。
因此在這裡我們可以將CONNTRACK_MAX和HASHSIZE設置成1048576(2^20)。
這樣,你可以存儲默認值32倍的跟蹤連接條目,而且可以得到更好的跟蹤連接性能。