徹底征服Windows上OpenVPN用戶端的源地址選擇問題

來源:互聯網
上載者:User
一個問題困擾了我多年,相信也困擾了很多人多年!那就是Windows上使用OpenVPN時,通過隧道的包的源IP地址總是OpenVPN虛擬網卡網段的IP地址,由於Windows的路由選擇是自動進行的,除非你在應用程式中bind一個地址,否則它選什麼你用什麼,配置路由時,你無法指定Linux的iproute2的src參數。因此只要是要通過TAP-Win32網卡走的包,其源地址均是TAP-Win32網卡上配置的虛擬IP地址!目前的解決方案有三種:
1.OpenVPN的2.3.0版本有NAT的配置選項:
--client-nat snat|dnat network netmask alias
但是我不敢用,怕應用程式的校正碼用到了原始IP資訊;
2.可以用Windows的LSP來強制bind物理網卡的IP,但是我還是覺得不夠完美;
3.在服務端做SNAT。然而為了WIndows用戶端做定製,也不完美;

網路的問題就要網路自己解決,不要依賴太多上層的東西!雖然Windows網路不給力,但是想降伏它還是有辦法的,大不了就搞NDIS啊!用戶端自己的事情自己解決,別麻煩服務端,為瞭解決用戶端的問題,在服務端加配置算什麼事啊!
寫在前面

這篇文章描述了Windows選擇源IP地址的詳細過程,另外微軟的文檔中也有介紹,這裡就不多說了。總之,不管是強主機模式還是弱主機模式,都是自動為你選擇的,你無法通過配置來幹預。
        就是這個自動選擇的問題讓OpenVPN的虛擬子網地址泄露到了真實的子網中,如果容忍這個的發生,你不得不針對OpenVPN服務端後面的防火牆等網路裝置進行修改,比如運行虛擬子網的IP地址通過等,你不得不對應用程式的IP地址審計策略進行修改;如果不容忍這個的發生,那麼代價就是通訊可能會變成單向發起的...不一而足。
        為瞭解決這個問題,花過不少時間,但是都不徹底。如今,我想一勞永逸解決它,徹底征服它。好在當前沒有什麼急迫的事情,公司和家裡都沒有,正是征服它的好機會,於是,小小爸爸來了,熬過了一個夜晚,半個白天,有了結果。
TAP-Win32網卡設定和物理網卡同網段的第二個IP的方案

1.首先在TAP-Win32網卡上配置一個和物理網卡同網段的第二個IP地址
我的物理網卡地址是192.168.40.34/24,於是乎我就配置給TAP-Win32網卡一個192.168.40.35/24,TAP-Win32網卡上的另外一個地址是OpenVPN服務端推送下來的172.16.0.30/16,這樣TAP-Win32網卡上就有了兩個IP地址。注意,同網段的第二個IP可以隨意選擇,不會衝突,因為只要不從該TAP-Win32網卡發包,是不會有到該IP的arp的,反之,要從TAP-Win32網卡發包,下面的步驟可以保證該IP僅僅會作為資料包的源IP。它的作用也僅僅在此。
2.在TAP-Win32網卡上盜用原始預設閘道的arp映射
預設閘道的作用其實就是擷取一個MAC地址用於封裝以太幀的目標MAC地址,獲得MAC地址後,網關的使命就完成了。Windows比較好的地方就是可以指定在哪個網卡上建立arp映射,雖然原先就有到預設閘道192.168.40.254的arp映射,但是那條映射是建立在物理網卡上的,現在我建立一條同樣到192.168.40.254的arp映射在TAP-Win32網卡上,只不過映射的MAC地址不是真正40.254的MAC地址,而是OpenVPN服務端tap0網卡的MAC地址:
arp -s 192.168.40.254  00-ff-c4-d3-2e-da 172.16.0.30
注意,上述命令的最後一個參數是TAP-Win32網卡的OpenVPN服務端推下來的IP地址,有了它,該條映射就會建立在TAP-Win32網卡上。
3.將OpenVPN服務端推下來的路由的gateway全部改在TAP-Win32網卡的Fake出來的192.168.40.254上:
route add a.b.c.d mask A.B.C.D 192.168.40.254 IF 0x3
注意上述命令的最後一個參數0x3,它是TAP-Win32網卡的Index ID,說明該條路由相關的IP是配置在TAP-Win32網卡的和物理網卡同網段的IP地址。
4.測試
在OpenVPN服務端的tap0上抓取到達a.b.c.d的包,發現源IP是192.168.40.35,也就是TAP-Win32網卡上的和物理網段同網段的第二個IP,不是OpenVPN服務端推送的虛擬IP地址172.16.0.30
缺點:
雖然達到了效果,但是不得不佔用兩個同網段的IP,即使不會衝突,也會很不爽,因為在VPN的盡頭,如果有源IP審計的話,管理上就會很麻煩,你不得不把自己的主用IP設定在TAP-Win32網卡上,而物理網卡上設定一個隨意的不衝突的IP,找到這個IP本身就是一個困難的事。於是乎,下一個想法就出來了,能不能在TAP-Win32網卡上設定一個和物理網卡相同的IP呢?
物理網卡和TAP-Win32網卡共用物理IP方案

這個小節會比較短,因為這個方案失敗了,唯一的內容可能就是抱怨了。
有想法是好事,但是實現想法的過程往往是痛苦的,因為總是有很多你想不到的攔路虎,特別是在WIndows上折騰網路,更是如此!我在TAP-Win32網卡上配置第二個IP地址,即我的物理網卡的IP地址192.168.40.34時,直接彈出警告:
---------------------------
Microsoft TCP/IP
---------------------------
為這個網卡輸入的 IP 位址 192.168.40.34 已被指派給這個電腦上的另一個網卡 MAC 橋微型連接埠。如果同樣的地址被指派給兩個網卡,並且兩個網卡都在使用中,只有一個會使用這個地址。這可能會導致不正確的系統配置。

要從“進階”對話方塊中的 IP 位址列表中為該網卡輸入一個不同的 IP 位址嗎?
---------------------------
是(Y)   否(N)  
---------------------------

我知道這是一個錯誤的配置,因為會衝突,我比誰都明白,但是我是故意這麼搞的,因為我有辦法解決衝突,於是我選擇了“否”,確定後,彈出:
---------------------------
Microsoft TCP/IP
---------------------------
剛配置的靜態 IP 位址已在網路上使用。請重新設定一個不同的 IP 位址。
---------------------------
確定  
---------------------------

確定後,我發現這個IP已經加入TAP-Win32網卡了,然而沒有生效,Windows根本不給我犯錯誤的機會...我們看下Linux上發生的情況:
root@Debian60b1-AMD64-DEV:~# ifconfig eth0 1.1.1.1/24;echo $?
0
root@Debian60b1-AMD64-DEV:~# ifconfig eth1 1.1.1.1/24;echo $?
0

...無語了!
Windows之所以不給你犯錯的機會,是因為設計者預設你沒有解決錯誤的能力,因此就阻止你犯錯誤,而UNIX系列的系統開放了巨大的許可權給root使用者,因為設計者認為如果你犯了錯誤,你一定有一個這麼做的理由!Mac OS則介於二者之間,對於UNIX使用者,它提供了一個強大的BSD命令列,對於普通使用者,有很絢麗的UI,它比Windows更加嚴格,設定將“添加第二個IP”這種行為都禁止了,而暴露給使用者“位置”的概念。
        既然不讓在UI上配,我覺得底層API肯定有辦法強制生效,於是開啟了久違的VS2005(太老的版本!),MSDN的例子各種嘗試,熟悉Win32 API的同事各種請教,無果!
        算了,既然不讓我成功,又何必勉強呢?於是第三種想法出生,既然不讓我在兩個網卡配置同一個IP地址,那麼我把兩塊網卡合并成一個邏輯網卡總可以了吧,於是上網搜各種方案,各種bonding不支援,要下載第三方軟體...Windows內建的就一個:MAC橋微型連接埠!那麼,就是它了。
物理網卡和TAP-Win32網卡的橋接方案

該方案是逐步思考的結果,因此方案過程中亦包含很多對Windows網路的抱怨。
1.串連OpenVPN伺服器成功

為了確保橋接過程中隧道斷開,要把keepalive時間設定足夠長久,我給了橋接一分鐘時間。
2.橋接TAP-Win32網卡與物理網卡

按下Ctrl鍵,左鍵點選TAP-Win32網卡和物理網卡“本地串連”;右鍵點選“橋接”。於是就產生了一個橋接器。
3.配置橋接器的IP地址

趕緊,用最快的速度,在keepalive到期前將IP地址配置好,其實也簡單,就是將原來的“本地串連”的IP地址和OpenVPN服務端推送的IP地址統一加到橋接器上,注意“本地串連”的預設閘道以及DNS也要加到橋接器上。接下來的事情夠我喝一壺的了!
4.痛苦的曆程

地址配置好了,問題又來了,如今怎麼建立arp映射?不能再用物理網卡的原始預設閘道了,因為此時TAP-Win32網卡和物理網卡bridge成了一個,不再分離,一旦盜取重定義了原始預設閘道的arp映射,將導致本機應用全部斷網,那麼顯而易見的就是選一個沒人用的本網段的IP了,這個有點難度。
4.1.選取要建立arp映射的IP地址
其實就是選擇一個將流量匯入TAP-Win32網卡的網關,該地址的作用就是建立一條arp映射,然後讓資料包通過橋接器發出時,會選擇物理IP地址作為源地址。本網段找個沒人用的?難啊,涉及到管理問題,那麼怎麼辦?還算比較懂網路的我此時有了辦法,那就是橋接器上設定的原始物理網卡上的IP位址遮罩往後退1位,即縮小1位,這樣原來網段的廣播位址和網路地址不就可以用了嗎?以下是計算過程:
IP地址:addr;原始子網路遮罩:prefix(mask的prefix形式)
a.設定新的子網路遮罩為prefix-1
b.如果addr的第prefix位為0,則所求的網關地址為原始網段的廣播位址(因為新的廣播位址要求其prefix位為1)
c.如果addr的第prefix位為1,則所求的網關地址為原始網段的網路地址(因為新的網路地址要求其prefix位為0)
d.如果addr的第prefix位為0,還可以使用將addr的prefix為設定為1後,後面全0的地址作為所求的網關地址
e.由於prefix縮小了1位,那麼代價就是增加一條指向原始預設閘道的路由,該路由覆蓋和addr的prefix位不同的原始predfix路由

結果呢?Windows又設障了!我明明的掩碼是255.255.254.0,可是在route print的時候還是會有:
192.168.40.255  255.255.255.255    192.168.40.34   192.168.40.34       20
按照我將prefix設定成23,它本應該是:
192.168.41.255  255.255.255.255    192.168.40.34   192.168.40.34       20
才對啊!可是不知Windows賣的什麼藥,開始以為是cache,可是禁用/啟用網卡依然如故,這樣我也就無法使用以上演算法選擇的網關了。網上也有人有類似疑問,既然解決不了,我也就不追了,在Windows上哪怕一個小問題一追就是好幾天頭大,因為你很難去debug它,很難去看個究竟!繞開它總是好的。
4.2.通過路由來解決
我懷疑WIndows的IP地址是有類和無類的混合,但是找不到什麼配置方法去改變這種行為,註冊表中也許有,但是我怕。於是我索性將prefix一下子退8位,即改成16位,於是路由表中有以下1項:
#鏈路路由
 192.168.0.0      255.255.0.0    192.168.40.34   192.168.40.34       20
也就是說它將192.168.0.0/16全部當成鏈路直連路由了,而事實上只有192.168.40.0/24才是,於是需要將非40網段的全部指向原始的預設閘道。天啊,Windows又很難做Policy Routing,不支援反掩碼,這樣添加多少條路由啊!!於是我只能靠Metric來覆蓋掉上述的路由了,也就是說增加兩條路由:
#增加真實的鏈路直連路由
route add 192.168.40.0 mask 255.255.255.0 192.168.40.34
#覆蓋掉假的鏈路直連路由
route add 192.168.0.0 mask 255.255.0.0 192.168.40.254 metric 1
這樣就OK了,為了防止人家自動產生的Metric就比你的小,最好關掉橋接器卡TCP/IP進階屬性中的“自動躍點計數”,然後手工添一個比較大的值。至於說選哪個做TAP-Win32的網關,在192.168.0.0/16中隨意找一個即可,我還是找了那個原始的網路地址,即192.168.40.0。然後添加一條arp映射,事情就完了:
arp -s 192.168.40.0 00-ff-c4-d3-2e-da
MAC地址是OpenVPN服務端的tap0的MAC地址。測試,發包,通過虛擬網卡,在接收端看來,源IP是192.168.40.34這個真真實位址,而不再是172.16.0.30這個VPN虛擬網段的地址了。

4.3.加密流量的泄漏問題
由於使用了橋接器,而橋接器在MAC無緩衝的情況下預設是在所有介面廣播包的,因此會有一些資料包同時送往物理網卡以及TAP-Win32虛擬網卡,造成本應該加密的流量泄露到物理網卡上,但是由於封裝的目標MAC地址是OpenVPN服務端的tap0的MAC,因此只在本網段可能會泄漏,不會穿越原始預設閘道。
        事實上,想解決這個問題很容易,只需要設定一條永不到期的MAC/連接埠映射或者配置防火牆就可以了,然則這不是我的強項,我也不想再折騰Windows網路了,HOLD不住啊!另外這個問題也可以通過OpenVPN服務端來幫忙解決,在Windows用戶端接入初始的holdon時間內,不斷通過tap0往對應用戶端發包,這樣Windows上的橋介面就會記錄下MAC/連接埠的映射,相當於學習到了MAC地址。然而如果Windows的橋接器不是學習型的,那就悲哀了。
5.悲哀的結果

貌似Windows的橋接器只能手工配置啊!這可怎麼和OpenVPN的plugin結合啊,我本來想寫一個指令碼,在Windows用戶端的up指令碼中來建立橋接器,配置IP,設定路由,添加arp映射的,然而Windows的指令碼功能實在太弱,就準備寫plugin,直接調Windows的API來做,可是如果只能通過手工的方式來搞的話,那就悲劇了,也許滑鼠精靈之類的玩意兒要派上用場了吧...
帶條件的縮小1位prefix的方案

怎嗎?又回來了?上一個方案不是說這個縮小1位的方案不可行嗎?是的,上一個方案時是不可行,但是解決問題的過程是一個不斷思考的過程,現在它真的就可行了,雖然這種可行是帶條件的,幸運的是,50%的機率,我們可以符合這個條件!
        Windows的IP地址和路由尋找都是Classful的的,並不是VLSM的,所以上一個方案中明明把prefix縮小了1位,其廣播位址卻還是192.168.40.255,因為這是一個C類地址。但是即便是Classful地址,其網路ID也還是很明確的,即:
 192.168.40.0    255.255.254.0    192.168.40.34   192.168.40.34       20
注意到下一跳演算法中的:
d.如果addr的第prefix位為0,還可以使用將addr的prefix為設定為1後,後面全0的地址作為所求的網關地址
恰好對於192.168.40.34而言,其prefix即第24位就是0,因此按上述演算法求得的地址是192.168.41.0,它顯然不屬於原有的物理網卡上的IP網段,但它卻屬於prefix後退1位後的該IP地址網段,可以作為下一跳使用:
route add a.b.c.d mask A.B.C.D 192.168.41.0
arp -s 192.168.41.0 00-ff-c4-d3-2e-da
# 補償prefix後退1位的代價
route add 192.168.41.0 mask 255.255.255.0 192.168.40.254

就此結束,不用再去設定什麼Metric更小的路由了,也不用再區分什麼真的或者假的鏈路直連路由了。
思考的過程

其實思考的過程就是一個原創的過程,即便你用到了很多“巨人”的資料,然而沒有人的思考過程和你是一樣的。
1.當我知道了Windows選擇源IP的邏輯時,我就決定在TAP-Win32網卡上添加一個和物理網卡同網段的IP,接下來就是如何把它選中;
2.雖然我期望把它配置成primary IP而不是secondary IP讓它優先順序更高一些,但是最終起決定性作用的還是下一跳地址是什麼;
3.我知道所謂的下一跳的唯一重要作用就是獲得一個被封裝成目標MAC地址的MAC地址而已,所以它到底是什麼並不重要;
4.於是我就偽裝了一個和物理網卡IP同網段的IP地址作為下一跳,然後添加一條靜態arp映射到OpenVPN服務端的tap0網卡的MAC地址;
5.想法到此基本結束,接下來的實現過程一直都是上述的想法,但是為何最終卻扯到橋接器了呢?雖然基本思想不變,但是實現過程其實是一個不斷試錯並迂迴前進的過程。錯了,就要微調,微調到最後,其實就是成功;
6.第一個障礙是發現使用同網段的兩個IP不好,我就想用一個,為了這個最佳化,逐步把我帶入橋接器。其實在此之前,都有一些小的障礙,比如如何針對網卡添加arp映射,如何配置路由時指定網卡等;
7.到了橋接器之後,選取通過TAP-Win32網卡資料包的下一跳成了問題,為了最少衝突和最少費解,我決定使用原始物理網卡網卡的全1廣播位址或者全0網路地址,遂決定prefix縮小1位,按照標準演算法求得下一跳;
8.遂碰到了Windows路由表的奇怪問題,有類無類混淆,廣播路由計算錯誤,我不是bug reporter,遂繞過,你愛怎麼著我依你還不行嗎?遂將prefix回退8位,這下終於和你要求的8,16,24對齊了;
9.prefix後退了8位,相當於一個大的匯總,鏈路直連路由覆蓋了原本根本就不是直連網段的網路,於是需要刨開這些,但是又不能死磕,怎麼辦?
10.按照最長掩碼匹配,先添加一條24位元遮罩的鏈路直連路由,它是真實的鏈路直連路由;
11.將原來的擴大了的16位元遮罩的假的鏈路直連路由用一條指向原始預設閘道192.168.40.254的網段路由覆蓋,但是假的直連路由又刪不掉,怎麼辦?
12.你愛留著就留著,我用Metric更小的路由覆蓋掉你!
13.將原始網段的網路地址192.168.40.0作為通過TAP-Win32網卡資料包的下一跳,然後添加一條該下一跳到OpenVPN服務端tap0網卡MAC地址的arp映射。
14.寫下本文,備忘!

留下的問題

基本上問題都解決了,留下來的僅僅是如何和OpenVPN結合的問題了,也就是如何把上述的那一大堆手工操作寫入到plugin(我已經徹底放棄指令碼了),再者就是TAP-Win32的初始化問題,如果它不從橋接器斷開,它還能被初始化嗎?即便不能也不難,修改OpenVPN Windows用戶端的代碼便是了,也就是ifdef WIN32那些段落。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.