注解:不要過度減小NEW以及TCP的establish的CT狀態(tài)的timeout的原因
盡量不要減小NEW狀態(tài)時(shí)間,因?yàn)閷?duì)于某些惡劣的網(wǎng)絡(luò),一個(gè)數(shù)據(jù)包的來回確實(shí)需要很長(zhǎng)時(shí)間,對(duì)于TCP而言,此時(shí)RTT還沒有測(cè)量呢。如果NEW狀態(tài)的conntrack保留時(shí)間過短,就會(huì)導(dǎo)致大量NEW狀態(tài)的連接,而對(duì)于很多依賴ctstate的模塊而言,這樣就會(huì)有問題,比如iptables的filter表中使用ESTABLISH狀態(tài)來放過前向包的返回包就會(huì)有問題,此時(shí)ip_conntrack很有可能由于NEW狀態(tài)時(shí)間過短而將返回包作為NEW狀態(tài)處理而不是ESTABLISH狀態(tài),如此一來,返回包就無法通過了。如下圖所示:
使用簡(jiǎn)單的實(shí)驗(yàn)可以很容易證實(shí)上面的圖示,以簡(jiǎn)單的udp通信為例,編寫一個(gè)udp-echo程序,服務(wù)器簡(jiǎn)單echo客戶端送達(dá)的字符串:
for(;;)
{
n = recvfrom(sd, msg, MAXLINE, 0, pcliaddr, &len);
sleep(5);
sendto(sd, msg, n, 0, pcliaddr, len);
}
然后在客戶端上執(zhí)行echo $sec /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
其中sec要比服務(wù)器端的sleep參數(shù)更小即可。
如此udp客戶端將收不到服務(wù)器eho回來的字符串,因?yàn)榭蛻舳酥皇欠判袪顟B(tài)為establish的入流量,如果ip_conntrack_udp_timeout配置過于短暫,NEW狀態(tài)的conntrack過早被釋放,這樣將不會(huì)有establish狀態(tài)的流量了。對(duì)于UDP而言,由于它是不確認(rèn)無連接允許丟包的,因此影響還不是很大,TCP也有類似的問題,那就是如果你連接一個(gè)很遠(yuǎn)的且網(wǎng)絡(luò)狀況很惡劣的TCP服務(wù)器,然后你把ip_conntrack_tcp_timeout_synsent設(shè)置很小,這樣就幾乎完不成三次握手了,更進(jìn)一步,如果你把ip_conntrack_tcp_timeout_established設(shè)置過小,那么一旦三次握手建立連接之后,客戶端和服務(wù)器之間很久不發(fā)包,當(dāng)establish狀態(tài)到期后,conntrack被釋放,此時(shí)服務(wù)器端主動(dòng)發(fā)來一個(gè)包,該包的conntrack狀態(tài)會(huì)是什么呢?因此給予tcp的establish狀態(tài)5天的時(shí)間,是可以理解的。需要注意的是,對(duì)于tcp而言,由于無法簡(jiǎn)單的控制服務(wù)器發(fā)送syn-ack的延時(shí),因此需要在establish狀態(tài)而不是new狀態(tài)做文章了(實(shí)際上,ip_conntrack的establish狀態(tài)映射成了tcp的多個(gè)狀態(tài),包括syn-ack,ack,established),試試看,效果和udp的一樣。
前面關(guān)于ip_conntrack扯的太遠(yuǎn)了,我們的首要問題是conntrack full的問題。實(shí)際上,如果深入思考這個(gè)conntrack full的問題,就會(huì)發(fā)現(xiàn),并不是conntrack容量太小或者表項(xiàng)保留時(shí)間過長(zhǎng)引發(fā)的full。現(xiàn)實(shí)中的萬事萬物都不是無限的,對(duì)于計(jì)算機(jī)資源而言,更應(yīng)該節(jié)約使用,不能讓無關(guān)人士浪費(fèi)這種資源,另外既然內(nèi)核默認(rèn)了一個(gè)表項(xiàng)的存活時(shí)間,那肯定是經(jīng)過測(cè)試的經(jīng)驗(yàn)值,自有它的道理。因此本質(zhì)問題在于很多不需要conntrack的包也被conntrack了,這樣就會(huì)擠掉很多真正需要conntrack的流量。
那么都是哪些流量需要conntrack呢?常用的就兩個(gè),一個(gè)是任何使用ctstate或者state這些match的iptables規(guī)則,另外一個(gè)就是所有的iptables的nat表中的規(guī)則,如果我們事先知道哪些流量需要使用iptables的[ct]state來控制,并且也知道哪些流量需要做NAT,那么余下的流量就都是和conntrack無關(guān)的流量了,可以不被ip_conntrack來跟蹤。
幸運(yùn)的是,Linux的Netfilter在PREROUTING以及OUTPUT這兩個(gè)HOOK的conntrack之前安插了一個(gè)優(yōu)先級(jí)更高的table,那就是raw,通過它就可以分離出不需要被conntrack的流量。如果你確定只有某個(gè)網(wǎng)卡進(jìn)來的流量才需要做NAT,那么就執(zhí)行下面的規(guī)則:
iptables -t raw -A PREROUTING ! –I $網(wǎng)卡 -j NOTRACK
iptables –t raw –A OUTPUT –j NOTRACK
這樣一來,資源就不會(huì)浪費(fèi)在無關(guān)人士身上了,性能也會(huì)有所提高,因?yàn)榉彩荖OTRACK的流量,都不會(huì)去查詢conntrack的hash表,因?yàn)樵趇p(nf)_conntrack_in的內(nèi)部的開始有一個(gè)判斷:
if ((*pskb)->nfct)
return NF_ACCEPT;
而NOTRACK這個(gè)target的實(shí)現(xiàn)也很簡(jiǎn)單:
(*pskb)->nfct = &ip_conntrack_untracked.info[IP_CT_NEW];
事實(shí)上將一個(gè)占位者設(shè)置給skb的nfct,這樣可以保持其它代碼的一致性。
可見,必要時(shí)同時(shí)采取三種方式比較有效:1.增大conntrack_max;2.減少狀態(tài)保存時(shí)間;3.分離無關(guān)流量。然而除了第三種方式,其余兩種方式在操作時(shí)必須給自己十足的理由那么做才行,對(duì)于1,比必須明白內(nèi)核內(nèi)存被占有的方式,對(duì)于2,看看本文的前半部分。
iptables -A FORWARD -m state --state UNTRACKED -j ACCEPT
最后有個(gè)提問:
對(duì)于沒有keepalive的TCP連接而言,試想服務(wù)器和客戶端在establish狀態(tài)之后5天內(nèi)都沒有互相通信,5天后的一天,服務(wù)器主動(dòng)發(fā)送了一個(gè)數(shù)據(jù)包給客戶端,然而此時(shí)防火墻/NAT設(shè)備上的conntrack狀態(tài)已經(jīng)過期被刪除,此時(shí)該數(shù)據(jù)包將會(huì)被認(rèn)為是NEW狀態(tài)的數(shù)據(jù)包,被DROP,客戶端永遠(yuǎn)收不到這個(gè)數(shù)據(jù)包,進(jìn)而也不會(huì)發(fā)送ACK,服務(wù)器端不斷重發(fā),不斷被防火墻DROP,當(dāng)重發(fā)次數(shù)達(dá)到一定次數(shù)后,服務(wù)器RESET該連接,然而客戶端如何得知,只有客戶端主動(dòng)發(fā)包才能打破這個(gè)僵局,然而誰能保證客戶端一定會(huì)主動(dòng)發(fā)包?這是不是Linux的ip_conntrack的一種缺陷,設(shè)計(jì)5天時(shí)間的establish狀態(tài)是不是一種極限措施,然而誰又能保證5天內(nèi)兩端不斷通信呢?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

