Linux的NAT是基于match的,即在滿足一系列條件的前提下執行SNAT或者DNAT,是以要求也就比較寬松,唯一的限制就是路由,即路由動作發生的時候,必須是基于最終的目标IP位址,是以DNAT必須發生在路由之前(對于本機發出的資料包,則在路由之後,然後重新路由),如下圖所示
<a href="http://blog.51cto.com/attachment/201310/113755707.jpg" target="_blank"></a>
附:Netfilter與ip_conntrackNetfilter
Linux的協定棧僅僅實作了基本的協定操作,對應TCP/IP标準,Linux的協定棧僅僅實作了一個最小集。其餘的所有擴充幾乎(并非所有!還有一部分由net schedule實作)均由Netfilter來實作,包括:IP Firewall,IP NAT,IPSec,IPVS等。
ip_conntrack
ip_conntrack是NAT實作的重中之重,Linux的NAT完全依賴ip_conntrack,依附于ip_conntrack之上。
基本資料結構:
<a href="http://blog.51cto.com/attachment/201310/113813690.jpg" target="_blank"></a>
值得注意的是,兩個方向的五元組節點在confirm之後統一處在一個哈希表中,并不差別對待,隻要使用一個五元組作為鍵查找到不管哪個方向的五元組節點,都可以找到ip_conntrack結構體本身。五元組節點除了包含五元組資訊之外,還包含方向資訊。
NAT如何依附于ip_conntrack之上的呢?如上圖所示,NAT修改了ip_conntrack中的反方向的五元組,對正方向的五元組并沒有影響,正是這個特點使得在Linux實作NAT的時候可以使用一種非常巧妙的方法。
NAT的流程如下所示:
<a href="http://blog.51cto.com/attachment/201310/113835818.jpg" target="_blank"></a>
<a href="http://blog.51cto.com/attachment/201310/113854773.jpg" target="_blank"></a>
注意:由于僅僅一個流的第一個包會建立ip_conntrack結構體,是以也是第一個包會去比對你用ipptables配置的NAT規則,這是個局限性
在2.6的早期核心中,NAT的資料,包括要轉換到的IP位址等資料都是儲存在CT的extension中的,叫做ip_nat_info,而該info可以從CT中取出來,在到達nf_nat_fn的時候執行NAT的時候使用裡面的ip_nat_info_manip來做NAT的依據。但是在後期的版本中,使用了一種更加高效得方式,需要轉換到的IP位址不再儲存在ip_nat_info中,而是直接使用上述流程圖中的計算方法得到,即擷取反向五元組,取逆,按結果執行。這麼做是有依據的,因為在NAT規則比對成功後,會直接修改掉反方向五元組的tuple,因為NAT規則比對成功肯定是針對正方向五元組的,畢竟隻有第一個包才會去比對NAT規則,無來怎能有回呢?是以根據NAT規則修改的既然是反方向五元組,那麼标準取逆後得到就是NAT後的正方向五元組了,在這個過程中,正方向的五元組一直儲存不變!對于反方向回來的包,它的反向就是正方向,由于正方向從來沒有被改過,取逆後即成為原始的反方向的五元組,傳回包的五元組得以還原!正因為如此,Linux NAT對方向才如此敏感:比對規則的正方向的資料包,修改的是反方向的五元組!注意正向,正方向以及反向,反方向并不是同一個意思,正向和反向是針對目前的方向來講的,正方向和反方向是針對資料流的發起方到接收方的方向來講的。
可以從這個實作看出Linux核心的發展,在2.6的早期版本中,實際上在NAT規則比對成功後也是去修改反方向五元組,但是那時怎麼就不是通過反向五元組取逆來作為位址轉換依據的呢?實際上那時一定可以這麼做!事實證明,Linux核心中布滿的逐漸逐漸被認識被發現的技巧。這也正展現了網絡社群開發的“90%特性”,即甚至90%的可用性即可,不要求100%的完美。
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1304562