在防火墻上鉆孔:穿透防火墻建立UDP連接
知道現在流行的P2P軟件和IM軟件是如何讓兩臺分處在不同防火墻后面的電腦直接對話的嗎?SIP當然是一種,還有一種被廣泛應用的就是本文介紹的UDP Hole Puching技術。
為了便于講述,我們假設有這樣一個網絡拓撲結構:
IP=A.A.A.A IP=1.1.1.1
HostA----------FirewallA---------|
|
Server IP=S.S.S.S
|
HostB----------FirewallB---------|
IP=B.B.B.B IP=2.2.2.2
運用這個技術,必須滿足下面的條件:
1) HostA和HostB分別通過FirewallA和FirewallB經過NAT用UDP連接到了Server
2) FirewallA和FirewallB都滿足這樣的特性,即來自相同IP相同Port的數據包,不管目的地IP是多少, 都會NAT成相同的IP+Port,舉個例子吧:
HostA通過UDP Port 1234訪問主機S1時,防火墻會把數據包NAT成1.1.1.1:5668(舉例),那么HostA通過UDP Port 1234訪問主機S2時,防火墻仍然會把數據包NAT成1.1.1.1:5668。好在現在的NAT基本上都具備這個特性。
現在,HostA用UDP端口1111連接到Server的5555端口,HostB用端口2222連接到Server的5555端口,在Server看來,HostA來自1.1.1.1:9676(FirewallA NAT過了嘛),HostB則來自2.2.2.2:6573。當HostA想直接連接HostB時,它這樣做:
1)用UDP端口1111發一個數據包給2.2.2.2:6573,注意一定要用端口1111哦,這個數據包一定會被FirewallA NAT成 1.1.1.1:9676 -> 2.2.2.2:5668(不要問為什么,看看前面對防火墻的要求先); 千萬別期望HostB會收到這個數據包,因為當包到達FirewallB時,FirewallB被弄糊涂了,它根本不知道 1.1.1.1:9676 -> 2.2.2.2:6573的數據包應該轉給誰,當然這個包就會被丟棄并回一個ICMP包說Port不存在。但是,我們還是得到了我們想要的一些東西,那就是我們成功地告訴了FirewallA "如果有2.2.2.2:6573 -> 1.1.1.1:9676的數據包,請轉發到A.A.A.A:1111",這就是一個洞洞!!
2)接下來,和你想象的一樣,HostA通過Server中轉,告訴HostB,用端口2222發一個數據包到1.1.1.1:9676,HostB照辦了,而且這個包一定會被FirewallB NAT成 2.2.2.2:6573 -> 1.1.1.1:9676。這個回復的數據包同樣在FirewallB上鉆了個孔,凡是1.1.1.1:9676 -> 2.2.2.2:6573的包都會被轉發到B.B.B.B:2222,當數據包到達FirewallA時,FirewallA很高興地把2.2.2.2:6573 -> 1.1.1.1:9676的數據包轉發給A.A.A.A:1111。
3)大功告成了,HostA和HostB開始愉快的交談起來。
很簡單吧?不過實施起來還要注意幾點,否則你都不知道為什么總連不上:
1) 步驟1中那個Port不存在的ICMP是個殺手,至少對Linux上用iptables做的NAT來講是這樣,因為FirewallA收到這個ICMP會關閉剛鉆上的洞洞,想辦法不讓FirewallB發這個ICMP或者讓FirewallA丟掉這個ICMP吧;
2) 時間問題,步驟1在FirewallA上開的洞洞是有時間限制的,通常為30-60秒吧,如果超時了都沒收到2.2.2.2:6573 -> 1.1.1.1:9676的包,洞洞會自動關閉,同樣步驟2以后,HostA也應該及時在發個數據包給B,以保證FirewallB上的洞洞不會因為超時而關閉。值得提一下的是多數NAT防火墻會在看見進出雙向的數據包后延長關閉洞洞的時間,Linux默認設置時會延長到3分鐘。
3) HostA不能連到HostB,并不表示HostB一定連不到HostA,反一下方向試試也許會有意外驚喜。
好了,就寫這些吧,祝大家鉆孔愉快


