1. 簡介 |
2. 官方站點及列表 |
2.1 什么是NAT? |
2.2 我為什么需要NAT? |
3. NAT的兩種類型 |
4. 從2.0和2.2內核的快速轉換 |
4.1 我只想偽裝!救命! |
4.2 關于ipmasqadm |
5. NAT可以控制什么 |
5.1 用iptables做簡單的選擇 |
5.2 關于應當挑選哪些包來拆分(mangle)的要點 |
6. 說說如何拆分包吧 |
6.1 源地址NAT |
6.1.1 偽裝 |
6.2 目的地址NAT |
6.2.1 重定向 |
6.3 深層次的映射 |
6.3.1 一個范圍內多地址的選擇 |
6.3.2 建立空的NAT映射 |
6.3.3 標準NAT行為 |
6.3.4 內部源端口映射 |
6.3.5 如果NAT失敗會怎樣? |
6.3.6 多重映射,重疊和沖突 |
6.3.7 修改本地生成的連接的目標地址 |
7. 特定的協議 |
8. 關于NAT的警告 |
9. 源地址NAT和選路 |
10. 同一網絡內的目標地址NAT |
11. 感謝 |
1. 簡介 |
歡迎,親愛的讀者。 |
你將要深入迷人的(有時是令人厭煩的)NAT世界:網絡地址轉換,這篇HOWTO可以成為你的Linux2.4內核及其以后的準確指南。 |
在Linux2.4(內核版本),引入了一個叫“netfilter”的部分,專門用于拆分(mangling) (IP)包的。他上一層提供NAT,是完全依靠以前的內核制作的。 |
(譯者注:mangle實在找不出什么合適的翻譯,抱歉) |
(C) 2000 Paul `Rusty' Russell. Licensed under the GNU GPL. |
2、 官方站點及列表位置 |
這里有三個官方站點: o Thanks to Filewatcher http://netfilter.filewatcher.org. o Thanks to The Samba Team and SGI http://netfilter.samba.org. o Thanks to Harald Welte http://netfilter.gnumonks.org. 你可以通過以下站點訪問全部相關站點。 http://www.netfilter.org and http://www.iptables.org 以下是netfilter官方郵件列表 http://www.netfilter.org/contact.html#list. |
2、1 什么是網絡地址轉換(Network Address Translation)? |
通常,,網絡中的(IP)包從他們的源(地址)出發(比如你家的電腦),到他們的目的地(比如www.gnumonks.org),會經過很多不同的連接(links):例如我所在澳大利亞就有19個。這些連接不會真去修改你的包:他們只是照原樣傳出去。 |
(譯者注:這里的links應當認為是所有網絡節點,包括主機、路由器等。通常,路由器并不是原樣傳送包,它至少會修改其中一點:TTL) |
如果這些連接有一個做NAT,那么它(們)就會修改通過它們的包的源或者目標(地址)。正如你猜象的那樣,這并非系統設計成那樣的,而是NAT做了一些事情。通常進行NAT的連接(主機、服務器、路由器)會記住它是如何拆分包的,而當另一頭響應的包通過時,它會對響應的包做相反的拆分,所以世界仍在運轉。 |
(譯者注:這一段的mangle應該想象為修改更合適) |
2、2 我為什么要NAT? |
在完美的世界里,你不需要。同時,主要的理由是: |
用調制解調器連接Internet |
在你撥號上網時,大部分ISP只會給你一個IP地址,你可以發送你想發送的任何源地址包,但是只有響應這個(ISP給你的)地址的包才會返回。如果這種情況下你想有多臺不同的機器上網(比如一個家庭網絡),你就需要NAT。 |
這是現在NAT用得最多的功能,Linux世界的"masquerading"(偽裝)非常出名,我稱之為SNAT(SNAT即Source NAT,源地址轉換),因為你改變了第一個包的源地址。 |
(譯者:關于IP數據報的第一個包等內容,請參見各TCP/IP書籍) |
多(重)服務器 |
有時你想改變進入網絡中的包的目標地址(路由)。經常的,這是因為(就像上面的例子),你只有一個IP地址,但是你希望大家可以通過到那個“真實”的IP地址進入內部。如果你重寫了進入包的目標地址,這樣就沒問題了。這種NAT在以前的Linux版本中被稱為端口轉發。 |
一個常見的變種是負載均衡,在一組機器上做映射。如果你要進行嚴格的比例限制, 可能需要參考Linux Virtual Server。http://linuxvirtualserver.org |
透明代理 |
有時你可能想要經過你的Linux的包被送往本機的一個程序。這就需要用到透明代理了:代理是位于你的網絡和外部世界之間的一個程序,幫助二者進行通信。之所以稱為透明,是你的網絡根本不知道他在和代理交談,當然直到代理沒有正常工作。 |
Squid可以配置為干這項工作,在以前Linux版本中它被稱作重定向或者透明代理。 |
3、 NAT的兩種類型 |
我把NAT分為兩種不同的類型:源NAT(SNAT)和目標NAT(DNAT)。(譯者注:以下不再翻譯SNAT和DNAT,直接用Source NAT和Destination NAT) |
Source NAT是指修改第一個包的源地址:也就是說,改變連接的來源地。Source NAT會在包送出之前的最后一刻做好post-routing(動作),偽裝是SNAT的一種特殊形式。 |
Destination NAT 是指修改第一個包的目標地址:也就是說,改變連接的目的地。Destination NAT 總是在包進入以后(馬上)進行before routing(動作)。端口轉發、負載均衡和透明代理都屬于DNAT。 |
4、 從2.0和2.2內核的快速轉換 |
如果你還在為從2.0(ipfwadm)到2.2(ipchains)的轉換手忙腳亂的話,很抱歉。不過這也算是個喜憂半參的消息。 |
首先,你可以輕松的使用ipchains和ipfwadm,就像從前一樣。不過你需要安裝最新發布的netfilter中的“ipchains.o”或者“ipfwadm.o”內核模塊。它們是互斥的(你會被警告),而且不能和任何其他netfilter模塊結合。 |
一旦這其中某個模塊被載入,你可以像以前一樣使用ipchains和ipfwadm,不過仍有以下區別: |
用ipchains -M -S,或者用ipfwadm -M -s設置偽裝超時不再有效。因為超時已經轉移到新的NAT構架中,所以這不能做任何事。 |
在詳細的偽裝列表中,init_seq、delta和previous_delat字段始終為零。 |
歸零和列表計數器的 -Z -L不再有效:計數器不能被歸零。 |
這類向后兼容的部分可能和大部分連接都不能有效配合:不要在你的公司網關中使用 |
開發者們還要注意: |
無論是否使用偽裝,現在可以綁定61000 - 65095之間的端口。以前的偽裝代碼占用了這部分端口,因此不能使用。 |
尚未成文的“getsockname”,透明代理程序可以用來發現那些已不再工作的連接的真實目的地址。 |
尚未成文的“bind-to-foreign-address”同樣還未啟用:這個用于完整透明代理的設想。 |
4、1 我只想偽裝!救命! |
這是絕大部分人想要的。如果你用PPP撥號上網來動態得到IP (如果你不知道,那應該就是的)你可能只想告訴你的機器,所有來自內部網絡的包,要看上去同PPP連接服務器上的包一樣。 |
# 裝載NAT模塊(這取代了其他的) |
modprobe iptable_nat |
# 在NAT表中(-t nat),路由后 POSTROUTING 加入一條規則(-A) |
# 所有由ppp0送出的包(-o ppp0) 會被偽裝( -j MASQUERADE)。 |
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE |
# 開啟IP轉發 |
echo 1 > /proc/sys/net/ipv4/ip_forward |
注意這時你沒有做任何的包過濾:如果需要,參見 the Packet Filtering HOWTO。 |
4、2 那么ipmasqadm呢? |
這個完全取決于用戶,所以我不擔心向后兼容的問題。你可以簡單的使用“iptables -t nat”做端口轉發。例如,在Linux2.2你要做: |
# 在2.2內核,把指向1.2.3.4 8080端口的TCP包轉到192.168.1.1的80端口 |
ipmasqadm portfw -a -P tcp -L 1.2.3.4 8080 -R 192.168.1.1 80 |
現在你可以這樣: |
# 2.4內核,在NAT(-t nat)表中加入一條規則,在路由之前(-A PREROUTING)指向 |
# 1.2.3.4(-d 1.2.3.4)8080端口(--dport 8080)的TCP包(-p tcp)目標地址(-j DNAT) |
# 被重定向到 192.168.1.1的80端口(--to 192.168.1.1:80)。 |
iptables -A PREROUTING -t nat -p tcp -d 1.2.3.4 --dport 8080 -j DNAT --to 192.168.1.1:80 |
5、NAT可以控制什么 |
你需要創建NAT規則,以告訴內核哪些連接將被改變和如何改變。要做到這一點,我們要用到一個用處很多的iptables工具,并告訴它用指定的“-t nat”選項修改NAT表。 |
NAT規則表包含三個稱為“鏈”的列表:每個規則都按順序檢查包,直到有一個匹配。其中兩個被稱為PREROUTING(用于Destination NAT,當包進入時檢查),POSTROUTING(用于Source NAT,包離開時檢查),第三個叫OUTPUT,這里可以忽略。 |
如果我有足夠的藝術天分的話,下面的見圖會準確的說明上述概念: |
_____ _____ / \ / PREROUTING -->[Routing ]----------------->POSTROUTING-----> \D-NAT/ [Decision] \S-NAT/ | ^ | | | | | | | | | | | | --------> Local Process ------ |
上述每一點,當我們查看連接(中)的包時,如果是一個新的連接,我們查看 NAT表中相對應的鏈,看看需要做些什么。其結果就會作為對這個連接后面所有包 的反應。 |
(譯者注:此處的連接是指一個HTTP會話之類的連接,而非物理上的線路、節點) |
5、1 用iptables做簡單的選擇 |
下面列出了iptables的一些標準選項。所有雙橫杠(--)的選項都是可以縮寫的。只要iptables可以將它們與其他選項區分開來就夠了。如果你的內核是以模塊方式支持iptables的,你需要先用命令:insmod ip_tables來加載ip_tables.o |
這是最重要的選項表格選擇,“-t”。對所有NAT操作,你都需要使用'-t nat'以指定NAT表。其次重要的是'-A',添加一個新的規則到鏈的末尾(例如'-A POSTROUTING'到POSTROUTING鏈),或者'-I'從最開始插入一個規則(例如'-I PREROUTING')。 |
你可以指定你想要進行NAT的包的源(地址)('-s' 或者'--source')和目的(地址)('-d' 或 '--destination')。這兩個選項可以跟單個IP地址(例如192.168.1.1),一個名字(例如www.gnumonks.org),或者一個網絡地址(例如192.168.1.0/24或192.168.1.0/255.255.255.0[譯者:這二者是等價的,只是表示方法不同])。 |
你可以指定進入或送出的匹配接口。不過能否指定取決于你想要寫入規則的那個鏈:PREROUTING鏈你只能選擇進入接口,POSTROUTING你只能選擇送出接口。如果用錯了,iptables會給出一個錯誤。 |
5、2 關于應當挑選哪些包來拆分(mangle)的要點 |
我上面說了你可以指定源地址和目標地址。如果忽略源地址選項,那么所有源地址都會被匹配,同樣,如果忽略目標地址,所有目標地址都將被匹配。 |
你還可以標出一個指定的協議('-p'或'--protocol'),諸如TCP或者UDP;那么只有這類協議的包會被匹配。這么做的主要原因是指定了協議,就可以增加額外的選項:指定'--source-port'源端口和'--destination-port'目的端口選項(可縮寫為'-sport'和'-dport')。 |
這些選項讓你可以只匹配那些特定源端口及目標端口的包。這些對于重定向Web請求(TCP 80或8080端口)而不影響其他包非常有用。 |
這些選項必須跟在'-p'選項后面(這可能會對加載該協議的連接庫有一定影響)。你可以使用端口號,或者來自/etc/serverices文件的(端口)名。 |
所有這些你可以對一個包作出的不同選擇都詳細的列在那詳細得可怕的使用手冊中了(man iptables)。(譯者注:參見iptables man page中文版) |
6、說說如何拆分包吧 |
現在我們知道如何選擇我們想要拆分的包了。為完成我們的規則,我們需要準確的告訴內核我們想要它如何做。 |
6、1 Source NAT |
你想要進行Source NAT,改變連接的源地址。這在POSTROUIING鏈中完成,就在它將送出去的最后一刻。這是一個重要的細節,所有Linux本機上的其他任何東西(路由、包過濾)都會看見那個尚未改變的包。也意味著'-o'(送出接口)選項可用了。 |
用指定'-j SNAT'來進行Source NAT,'--to-source'選項指定一個或一段IP地址,(加上)一個或一段可選的端口號(只能用于UDP和TCP協議)。 |
# 改變源地址為1.2.3.4 |
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4 |
# 改變源地址為1.2.3.4、1.2.3.5或者1.2.3.6 |
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4-1.2.3.6 |
# 改變源地址為1.2.3.4,端口1-1023 |
# iptables -t nat -A POSTROUTING -p tcp -o eth0 -j SNAT --to 1.2.3.4:1-1023 |
6、1、1 偽裝 |
Source NAT的一個特例被稱作偽裝。它只能被用于動態分配IP地址的情況。例如標準撥號服務(靜態IP地址請用SNAT)。 |
你無需為IP偽裝明確指定源地址。它會使用包送出的那個接口(地址)作為源地址。不過更重要的是,如果那個線路關閉了的話,連接(無論如何都會丟失了)會被忘掉,意味著啟用新的IP后返回的包就會有點問題了(指那些響應掉線前發出的包的包)。 |
# 偽裝所有由ppp0送出的東西 |
# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE |
6、2 Destination NAT |
用于PREROUTING鏈,包剛剛進入的時候。意味著本機上的任何東西看見的都是“真正”的目的地(譯者注:即已修改過的目的地址)。也意味著'-i'(進入接口)可用了。 |
用指定'-j DNAT'來進行Destination NAT,'--to-destination'選項指定定一個或一段IP地址,(加上)一個或一段可選的端口號(只能用于UDP和TCP協議)。 |
# 改變目標地址為5.6.7.8 |
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8 |
# 改變目標地址為5.6.7.8、5.6.7.9或5.6.7.10 |
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8-5.6.7.10 |
# 改變Web傳送的目標地址為5.6.7.8,8080端口 |
# iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 5.6.7.8:8080 |
6、2、1 重定向 |
Destination NAT的一個特例被稱為重定向。它相當于對進入接口進行DNAT的簡單方便的一種形式。 |
# 發送進入的80端口的Web傳輸到我們的Squid(透明)代理 |
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128 |
注意Squid需要被配置為透明代理。 |
6、3 深層次的映射 |
還有一些可能大部分人不會用到的NAT的細節。 |
6、3、1 一個范圍內多地址的選擇 |
如果指定了IP地址的范圍,那么機器會選擇當前使用最少的那個IP地址。這就實現了最儉樸的負載均衡。 |
6、3、2 建立空的NAT映射。 |
你可以使用 '-j ACCEPT' 目標讓連接通過,不需要NAT參與。 |
6、3、3 標準NAT行為 |
默認行為是根據用戶給定的內在約束規則,對連接作最小的改動。也就是除非必要 不要進行端口重映射。 |
6、3、4 內部源端口映射 |
如果其他的連接覆蓋了一個連接,即使這個連接不需要使用NAT,源地址轉換仍會發生??紤]IP偽裝,這種情況就非常普遍。 |
1、一個從192.168.1.1 1024端口到www.netscape.com 80端口的Web連接已建立 |
2、它被偽裝成IP偽裝服務器的IP地址(1.2.3.4) |
3、IP偽裝服務器試圖建立一個從www.netscape.com 80端口到1.2.3.4 1024端口的Web連接(它自己的外部接口IP地址) |
4、NAT代碼會修改第二個連接的源地址到1025,這樣兩個(連接)就不會沖突了。 |
當這種內部源地址映射發生時,端口分為三級: |
512以下的端口 |
512至1023之間的端口 |
1024以上的端口 |
內部端口映射決不會被映射到(除此之外的)其他種類。 |
6、3、5 如果NAT失敗會怎樣? |
如果無法按照用戶請求的那樣,為連接建一個單獨的映射,(包)會被刪除。這也適用于那些無法被歸為任何連接的包,因為它們是畸形的,或者是主機內存溢出了。 |
6、3、6 多重映射,重疊和沖突 |
你的NAT規則可以把包映射到相同的范圍。NAT代碼聰明到可以避免它們的沖突。因此,兩條規則把192.168.1.1和192.168.1.2的源地址映射都映射到1.2.3.4是沒有問題的。 |
而且,你可以映射到真實的、已在使用的IP地址,只要那些地址也通過這個服務器。所以如果你分配到一個網絡(1.2.3.0/24),但有一個內部網絡使用了這些地址,另一個使用的是私有地址192.168.1.0/24,你可以簡單的NAT 192.168.1.0/24的源地址 到1.2.3.0網絡,不必擔心沖突。 |
# iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.3.0/24 |
相同的邏輯也適用于NAT服務器本身的地址。這就是得以偽裝工作的原因(由偽裝后的包和來自本身的“真實”的包共享一個接口地址)。 |
甚至,你可以映射相同的包到許多不同的目標,它們會被共享。例如,如果你不希望映射任何東西到1.2.3.5,你可以這樣做: |
# iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.3.0-1.2.3.4 --to 1.2.3.6-1.2.3.254 |
6、3、7 修改本地生成的連接的目標地址 |
NAT代碼允許你插入DNAT規則到OUTPUT鏈,不過這在2.4中尚未完全支持(可以使用,但必須用新的配置選項,某些測試中的代碼。所以除非有人在瘋狂的寫這部分代碼,我不相信它會很快實現)。 |
當前的限制是你只能修改目標地址到本機(例如'-j DNAT --to 127.0.0.1'), 不能到任何其他機器,否則相應可能不能夠被正確轉換。 |
7、 特定的協議 |
有些協議不希望被NAT。這些協議,兩種延伸必須指明:一個是協議的連接跟蹤,一個是真實NAT。 |
在發布的netfilter中,有可用的FTP模塊:ip_conntrack_ftp.o和ip_nat_ftp.o。如果你加載了任一種模塊到你的內核(或者編譯進去),那么任何關于FTP連接的NAT都是可行的。如果沒有,那么只能使用被動FTP(passive ftp),而且如果做了一些Source NAT,它(指FTP)也許不能可靠的工作。 |
8、 關于NAT的警告 |
如果你對連接做NAT,所有雙向傳送的包(進入和送出網絡的)必須通過NAT服務器,否則NAT服務器的工作可能不可靠。特別是,連接跟蹤代碼重組了分片,也就意味著不光是連接跟蹤不能可靠工作,甚至所有包都無法通過,因為分片被丟棄。 |
9、 Source NAT 和路由 |
如果你要做SNAT,你必須注意所有機器被SNAT的包的回應都將發送到NAT服務器。例如,如果你映射了一些送出的包的源地址為1.2.3.4,那么外部的路由器必須知道發送回應包的地址到NAT服務器??梢赃@樣做: |
1、如果你對本機地址做SNAT(路由等所有事情都正常),你不需要做任何事。 |
2、如果你在本地LAN上做SNAT到未用地址(例如,你映射為1.2.3.99,你的1.2.3.0/24網絡中未用的IP),你的NAT服務器需要像那個地址(99)一樣正確響應ARP請求。最簡單的辦法是建立一個IP別名,例如: |
# ip address add 1.2.3.99 dev eth0 |
3、如果你對完全不同的地址做SNAT,你必須保證被SNAT的包到達的機器會返回NAT服務器。如果NAT服務器是它們的默認網關,那么就已經行了,否則你需要發布一個路由(如果運行了路由協議)或者對每個機器手工添加路由。 |
10、 同一個網絡內的Destination NAT。 |
如果你要對同一個網絡做端口轉發,你需要確認所有以后的包和回應包都通過NAT服務器(這樣它們才能被修改)。NAT代碼現在(自2.4.0-test6),會屏蔽掉同組的被NAT的包送出的ICMP重定向,不過收到的服務器會繼續嘗試直接響應客戶。(不會理解這個回應) |
經典的情況是,內部人員試圖訪問你的“公用”Web服務器,而它實際上從公用地址(1.2.3.4)被DNAT到內部機器(192.168.1.1),例如: |
# iptables -t nat -A PREROUTING -d 1.2.3.4 -p tcp --dport 80 -j DNAT --to 192.168.1.1 |
一種辦法是運行一臺內部DNS服務器,它知道你的公用(外部)Web服務器的真實的(內部)IP地址,并轉發所有其他請求到外部DNS服務器。也就是說你的Web服務器能夠記錄真實的內部IP地址。 |
另一種辦法是讓NAT服務器映射那些連接的源地址到它自己,讓服務器通過它發出響應。例如,我們可以這樣做(假設NAT服務器內部IP地址是192.168.1.250): |
# iptables -t nat -A POSTROUTING -d 192.168.1.1 -s 192.168.1.0/24 -p tcp --dport 80 -j SNAT --to 192.168.1.250 |
因為PREROUTING規則會首先運行,對內部Web服務器來說,包的去向早已確定。我們可以確定好源IP地址。 |
11. Thanks |
Thanks first to WatchGuard, and David Bonn, who believed in the netfilter idea enough to support me while I worked on it. And to everyone else who put up with my ranting as I learnt about the ugliness of NAT, especially those who read my diary. |
首先感謝在我工作期間相信netfilter設想并支持我的WatchGuard和David Bonn。以及所有對NAT提出指正的朋友,尤其是讀過了我的日記的。 |
Rusty. |
All pages ended here. |
原文轉自:http://www.anti-gravitydesign.com