徹底理解Cisco NAT內部的一些事
一.Inside和Outside
很多在Cisco配置過NAT的人都有過一個疑問,那就是inside和outside的區別!以下是Cisco官方文件上關於NAT執行順序的說明:注意紅色和藍色圈住的部分,對於inside-outside而言,NAT發生在路由之後,而對於outside-inside而言,NAT發生在路由之前。這是目前為止,我們唯一需要記住的。1.問題
迷惑的原因不在別的,就在inside,outside這個名字不好,實際上如果將inside-outside換成POST-ROUTING,將outside-inside換成PRE-ROUTING的話,就非常好理解了,最重要的是,換了名字之後,NAT看起來不再和裝置的inside/outside網口域相關,而和“路由”發生了關係,雖然本質上沒有任何變化。 後面會介紹,實際上,在理解Cisco的NAT的時候,根本不能將inside和outside單獨拿出來理解,inside和outside僅僅是一個位置限定詞,代表“某地”,而具體的是“到某地去”還是“從某地來”,還需要一個副詞,這就是source和destination。在詳述這個之前,姑且先將inside和outside單獨拿出來使用。 接下來我來說明一下NAT和路由的關係是多麼重要!考慮以下的資料流,我以“路由”這個動作為中心:正向包:-->NAT point1-->路由-->NAT point2-->返回包:<--NAT point1<--路由<--NAT point2<--我們看一下在NAT point1和NAT point2上要做些什麼動作才合理。首先我們先考慮轉換正向包的源IP地址發生在NAT point2,那麼對於返回包,目標地址轉換就發生在NAT point2,返回包轉換完目標地址後,發生路由查詢,資料包正常返回,沒有任何問題。現在考慮正向包的源IP地址轉換髮生在NAT point1,那麼按照將NAT鉤子操作安裝在資料流同一位置的原則,返回包的目標地址轉換隻能發生在NAT point1,此時已經經過了路由查詢,路由查詢是基於目標地址轉換前的目標地址來的,也就是說這個路由結果並非真正的路由結果,真正要想將返回資料包送到目的地,必須基於轉換後目標地址來查詢路由表才可以,然而即便這個針對轉換前目標的路由查詢結果實際上是個假的結果,你也要必須把它對映成一個真的結果(這就是ip nat outside source中add-route引數要做的事情,下面的例子詳述)。以下給出一個例項:
2.關於policy routing
我們知道,標準的IP路由是基於目標地址的,但是為了增加更多的策略,policy routing可以用源地址來影響路由查詢結果。在這種意義上,源地址轉換在路由之前就是必要的,然而這樣就會導致反向的目標地址地址轉換髮生在路由之後!到底是:a.為了policy routing將SNAT置於路由之前b.為了不必add-route(雖然它確實不是什麼問題,而且是自動的),將DNAT置於路由之前需要一個權衡!然而Cisco不像Linux那樣去對稱設計那5個HOOK點,Cisco的方式就劃分Domain的,即inside和outside3.關於Domain和NAT domain
Cisco裝置一般連線兩種網路環境,一種是自己內部的,另一種是外部公共的,這就將介面分為了兩個域,一個是內部域,即inside,另外一種為外部域,即outside!這種分法的名字叫得特別好,以至於它被用在了很多的領域,比如nat,然而一旦用在了nat方面,就讓人糊塗了。因此我提議,對Cisco架構不是很理解的,請嘗試用POST/PRE ROUTING來理解inside和outside。但是本小節想做的就是闡明使用inside和outside是合理的。 對於NAT來講,轉換的是IP地址,而IP地址可以分為Global地址以及Local地址,前者是公網可路由的地址,後者是私有地址。從inside域到outside域,需要將所有的Local地址轉換為Global地址,一個首要原則就是,Global的地址在未經允許是不能出現在inside域和DMZ域(路由器可能沒有)的!決策點就是路由!因此outside到inside方向的地址轉換必然要在路由之前完成。這就要求inside到outside方向的地址轉換必然要在路由之後完成。即:ip nat inside source必然發生在路由之後,而:ip nat outside source 必然發生在路由之前。如果你定義了某個介面比如FE0/0為outside,那麼需要在介面上使能ip nat outside,這樣的話,從該介面進入的包就會在路由之前去查詢NAT表,如果找到對應的表項,就會執行NAT。同理如果定義某介面比如FE0/1為inside,那麼需要在介面上使能ip nat inside,這樣的話,從該介面進入的包先執行路由查詢,然後去查詢NAT表,如果找到對應的表項,則執行NAT操作。3.1.轉換方向以及轉換點-Cisco NAT的設計
這個小節涉及到了對Domain的使用以及如何解讀ip nat inside|outside source|destination命令。本小節總結了Cisco NAT設計的終極理論。為了簡單,我不再引入Cisco定義的那四種地址以及它們和源/目標IP地址,方向的關聯,這些概念都是額外的概念,最容易使人跑偏而最終陷進去。 Cisco沒有像Linux那樣使用“pre/post路由”這麼技術化的術語來定義NAT的行為,而是完全根據Domain來定義,所謂的Domain,即路由器兩邊一邊屬於inside,另一邊屬於outside。那麼所有的NAT無外乎就以下4種類型:1>從inside到outside時轉換源地址2>從inside到outside時轉換目標地址3>從outside到inside時轉換源地址4>從outside到inside時轉換目標地址其中1和4互相隱含,2和3互相隱含。到此為止,我們發現Cisco的NAT並沒有像Linux那麼簡單,Linux實際上就定義了兩種NAT,即:i> SNAT,源地址轉換ii>DNAT,目標地址轉換然後其它的約束都是設計的時候內建的:路由前執行DNAT,路由後執行SNAT,包含隱含規則。這就是Cisco和Linux的NAT設計的終極區別!它們側重點不同,Cisco強調使用者的使用域,Linux強調技術本身的合理性(如何配置就需要發揮想象力了)。我們先看一下Linux的NAT設計基準是什麼。Linux的NAT是全域性生效的,沒有“將NAT應用於介面”的說法,因此介面就成了一個match。因此管理員只需要寫match/target就可以了。對於Cisco而言,為了將4種NAT配置介面全部匯出給工程師,需要一個前提操作,那就是定義inside介面和outside介面,即在哪個介面上應用inside nat,在哪個介面上應用outside nat。到此,所有的4種NAT都必須能和任意型別(inside/outside)的介面單獨組合。這就打破了平衡點,變成了馬鞍面,你無法找到一個點,在PRE ROUTING和POST ROUTING中完成一切,舉例,如果介面E1使能inside nat,E0使能outside nat,說明E1是inside介面,E0是outside介面,那麼我們考慮從inside到outside方向的兩種轉換,一種是轉換源地址,另一種是轉換目標地址,我們把它們放在一個位置還是放在一個“虛擬的平衡點”(不一定是routing)兩邊,即兩個位置呢?我們看下面的兩個圖,實際上代表了兩種約束:
這兩個圖展開後是個典型的馬鞍面,原點就是路由,之所以要有個原點,是因為基於Domain的配置中,資料包從inside到outside或者反過來必然要經過一個點,從本文最上面的那幅Cisco網站上的圖,我們看到基於Domain的NAT行為不僅僅是一個NAT,它需要和ACL匹配,加解密等操作聯動,這些操作所依賴的IP地址和NAT發生了關聯,因此基於Domain的NAT行為一定要位於路由行為的兩邊。由於Cisco是按照Domain即inside/outside來進行配置的(在介面上應用特定Domain的規則),因此必然是這種設計方式,而對於Linux,NAT是全域性的,介面只是一個match而已,因此就完全按照路由的約束來設計。 現在,我們可以來總結一下ip nat inside|outside source|destination的含義了。我把這個命令公式化:ip nat P H其中H代表要做源轉換還是要做目標轉換,H還有一個更加隱蔽的含義,那就是它和P指名了資料的方向,也就是說:資料是以P為H的。舉例,ip nat inside destination表示資料是以inside為目標的(來自outside),做目標地址轉換;ip nat outside source表示資料是以outside為源發往inside的,做源地址轉換。4.Cisco的destination轉換
但是,但是如何對目標地址進行轉換,即將訪問一個公共Global地址時,將其轉到一個內部的Local地址,這就是目的地址轉換,也叫地址對映,Cisco如何來做呢?實際上,很多Cisco裝置的ISO版本不允許你像Linux那樣無限制做DNAT,而僅僅允許對映特定的IP地址+TCP/UDP埠對或者全IP。這肯定是在outside上做目標地址轉換了,在相反的方向就是在inside上做源地址轉換,即:ip nat inside source static tcp $local_ip $local_port $global_ip $global_port注意,必須是static的NAT,這涉及到下一節要說的“如何安裝NAT” 對於TCP負載均衡做的ip nat inside destination這種定製化的NAT則不在本文討論範圍內。二.如何安裝NAT
0.風格
Linux的NAT是基於5元組的,也就是NAT結果和一個流(conntrack)關聯在一起,這種關聯導致同屬於一個五元組的一個流的所有資料包的NAT策略必須一致,對於這一硬性規定有點太強硬了,因此我在Linux上做了好幾個補丁來彌補Linux的不足,當然也可以用RAWNAT。 對於Cisco,NAT不和一個流關聯,除非是Stateful的。既然不和流關聯,那麼如何做呢?Cisco會在特定的時間將“一條NAT對映策略”安裝到系統的inside NAT表或者outside NAT表中,對於從網口進入的資料包,會根據網口是inside還是outside去匹配inside NAT表或者outside NAT表中的NAT規則,僅此而已。 不管是inside NAT表還是outside NAT表,都各有兩張,一張是SNAT表,另一張是DNAT表,NAT表的拍腦袋想出的資料結構可以是:NAT table {type:SNAT or DNATdirection:inside or outsidenodes:local/global mapping}
對於每一個數據包,都要用源IP地址去查詢SNAT表,用目標IP地址去查詢DNAT表。然而對於Linux而言,需要的僅僅是查詢conntrack結構,然後取出第一個包查詢時記錄於此的nat結果。