天天看點

Linux系統如何平滑生效NAT-DNAT改進以及解釋

上述的所有修改均沒有加鎖,不知道會不會有影響,特别是在conntrack将被timeout或者event删除的邊界上,不過我自己在LoadRunner下做稍微大的壓力測試,沒有問題,是以也就不引入lock了,我的測試機擁有4個核心,核心開啟了核心搶占...

在Netfilter的conntrack中,有個概念特别重要,那就是tuple,一個tuple就是一個五元組: 

{

    source address;

    destination address;

    source port;

    destination port;

    protocol;

} tuple;

    original tuple;

    reply tuple;

} conntrack;

一個流由兩個tuple構成,一個正方向的tuple,一個反方向的tuple,比如以下一個連接配接:

1.1.1.1:1234-TCP->2.2.2.2:4321

正方向的tuple就是:

{1.1.1.1,2.2.2.2,1234,4321,TCP}

反方向的tuple就是:

{2.2.2.2,1.1.1.1,4321,1234,TCP}

之是以要有反方向的tuple,就是因為當資料包傳回時經過BOX的時候,BOX可以将識别該tuple,然後對應到一個流。

        我們來看一下NAT對tuple的影響,首先看一下DNAT,DNAT雖然發生在路由之前,不管怎麼說也是在流識别之後,資料包進入IP層的第一件事就是根據五元組來識别一個tuple,而此時DNAT還沒有發生,那麼很顯然該tuple就是原始的五元組,緊接着發生了DNAT,對目标位址進行了修改,這件事的效果就是對reply tuple産生了影響,因為reply tuple的源就是original tuple的目标,是以上面的例子,如果将目标轉換為了3.3.3.3,那麼它的正方向tuple保持不變,反方向tuple變成了:

{3.3.3.3,1.1.1.1,4321,1234,TCP}

僅僅reply tuple發生了改變,是以需要做的就是僅僅将reply tuple從tuple哈希表删除,重新計算哈希值,入隊即可!

        下面看一下SNAT,實際上情形和DNAT一樣,SNAT發生在資料包離開BOX之前,效果是對源位址進行改變,是以在資料包剛剛進入BOX的時候,tuple不會改變,是以其源tuple保持不變,改變的依然是reply tuple,original tuple的源就是reply tuple的目标,是以上述例子,如果源位址變成了4.4.4.4的話,其reply tuple将會變成:

{2.2.2.2,4.4.4.4,4321,1234,TCP}

和DNAT一樣,僅僅需要将reply tuple重新計算哈希值并插入即可。

        綜上,在NAT發生後,不管什麼NAT,都不要操作其original tuple,都需重新計算reply tuple的哈希。可是看Linux的實作,上述這麼顯而易見的事實,在實作的時候因為過于追求和諧,竟然引出了alloc_null_binding這樣的事情,我覺得這可能和conntrack的接口有關,因為conntrack并沒有導出諸如單tuple入隊,計算哈希等接口,極少的幾個接口之一就是nf_conntrack_hash_insert,而這個接口是将兩個方向的tuple都入隊。是以我沒有辦法,也隻能将兩個tuple都出隊,再入隊了,實際上根本就沒有這個必要。要是接口提供得再友善一點,我會直接将alloc_null_binding調用删除,另行實作延遲NAT,當下的NAT實作中,NAT結構體的填充必須在confirm之前,confirm全權包攬了所有的tuple入隊操作,是以為了都有兩個tuple,即便根本就不需要做NAT,也要僞造一個:alloc_null_binding...一旦conntrack tuples被confirm了,後面再操作它就難了,隻能等待其過期或者人工删除,否則就永遠無法操作它,要是連續觸動這個conntrack,就别想讓它過期了,Netfilter等它過期,應用并不知情,依然期待下一秒的成功,不斷重試,大家就僵持在那裡了!

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1268847

繼續閱讀