天天看點

Linux的NAT實作淺談

NAT實際上就是一個代理,代理NAT内部的所有機器,NAT的分類就是按照“如何代理”來講的,不管是基于連接配接的NAT實作,還是基于包的NAT實作,都要處理好一個映射關系,那就是轉換後的包的傳回包如何轉換回原始的。

        Linux的NAT是對稱NAT,而不是錐形NAT,雖然很多時候Linux的NAT看起來更像是一個錐形NAT。

        隻需要明白Linux實作NAT的本質,即使不明白NAT的分類也無所謂。Linux實作NAT的原則很簡單:

用最小的代價保證一個tuple五元組在轉換前的五元組和轉換後的五元組中的唯一性。

注意,這兩個五元組是分開存放的,它們在hash連結清單層面并沒有任何關聯。實際上這是ip_conntrack的實質,ip_conntrack在存放連接配接的時候是按照“兩個方向”分别存放的,是以需要一個五元組在兩個方向的hash表中是唯一的才可以。有人說Linux可以實作錐形NAT,然而這個錐形NAT實際上是很脆弱的,考慮以下的連接配接:

(PC:1234->SERVER:80)

是以NAT上會出現下面的tuple(五元組):

正方向:(PC1:1234->SERVER:80)

反方向:(SERVER:80->NAT:1234)

如果此時PC2同樣使用端口1234連接配接了SERVER2的80,那麼NAT上将會出現新的兩個tuple:

正方向:(PC2:1234->SERVER2:80)

反方向:(SERVER2:80->NAT:1234)

此時如果PC1也想用1234端口連接配接SERVER2的80,如下:

正方向:(PC1:1234->SERVER2:80)

反方向:?

為何是一個“?”呢?如果Linux NAT是錐形的NAT,那麼根據錐形的含義,NAT需要将PC2的源組轉換為(NAT:1234->SERVER2:80),這樣的話,反方向的tuple就是(SERVER2:80->NAT:1234)了,和PC2的反方向tuple相沖突,這是不可以的,Linux自動将其源端口作了改變,轉換為(NAT:不是1234->SERVER2:80),進而反方向tuple成了(SERVER2:80->NAT:不是1234)了,是以解決了唯一性問題,在“第三者的幫助下”,Linux被動成了對稱NAT...

        至于錐形NAT中的Port Restricted Cone,Restricted Cone,Full Cone這三類錐子,就不用讨論了,這三類錐子的目的在于限制反向流量的穿透性,一種分類上的清晰化會使政策實作起來更加友善,之是以将NAT分為錐形和對稱的,并且錐形的又分為三個類别,這完全是為了實作RFC3489,而這個RFC是打洞技術的基石,也就是說,分類是為了描述打洞實作時更加友善簡潔。反過來,NAT之是以可以被穿越(成功打洞),正是在于描述NAT本身的RFC沒有規定其實作方式,也沒有規定“不讓穿越”,Linux隻是按照RFC保證了NAT的結果,這也是實作之一而不是唯一的實作。

        本文最開始談到,NAT需要解決一個映射問題,而一個包攜帶的所有識别資訊的交集就是五元組,是以NAT可以起到連接配接限制的作用,NAT裝置能開放的所有連接配接數量由它所持有的公網IP位址以及可用的端口數量決定,比如下面的規則:

iptables -t nat -A POSTROUTING -p --dport 80 -j SNAT --to-source X.X.X.X:80

那麼整個NAT後方,同時則隻有一台機器的一個socket能連接配接到同一個目标的80端口,否則反方向的tuple就會沖突。可以通過/proc/net/stat/ip_conntrack檔案的insert_failed那一列看到所有後續的連接配接反方向tuple由于和既有的反方向tuple相沖突而計數器遞增。

        Linux不區分以上這些由RFC3489而引發的XX,甚至都不區分錐形NAT和對稱NAT,隻保證NAT的結果即可。

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

繼續閱讀