基於matches的NAT
Linux的NAT是基於match的,即在滿足一系列條件的前提下執行SNAT或者DNAT,因此要求也就比較寬鬆,唯一的約束就是路由,即路由動作發生的時候,必須是基於最終的目標IP地址,因此DNAT必鬚髮生在路由之前(對於本機發出的資料包,則在路由之後,然後重新路由),如所示
附:Netfilter與ip_conntrack
Netfilter
Linux的協議棧僅僅實現了基本的協議操作,對應TCP/IP標準,Linux的協議棧僅僅實現了一個最小集。其餘的所有擴充幾乎(並非所有!還有一部分由net schedule實現)均由Netfilter來實現,包括:IP Firewall,IP NAT,IPSec,IPVS等。
ip_conntrack
ip_conntrack是NAT實現的重中之重,Linux的NAT完全依賴ip_conntrack,依附於ip_conntrack之上。
基本資料結構:
值得注意的是,兩個方向的五元組節點在confirm之後統一處在一個雜湊表中,並不區別對待,只要使用一個五元組作為鍵尋找到不管哪個方向的五元組節點,都可以找到ip_conntrack結構體本身。五元組節點除了包含五元組資訊之外,還包含方向資訊。
NAT流程NAT如何依附於ip_conntrack之上的呢?如所示,NAT修改了ip_conntrack中的反方向的五元組,對正方向的五元組並沒有影響,正是這個特點使得在Linux實現NAT的時候可以使用一種非常巧妙的方法。
NAT的流程如下所示:
注意:由於僅僅一個流的第一個包會建立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%的完美。