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

寫於2013/08/07

 

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

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

寫在前面

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

 

TAP-Win32網卡設置和物理網卡同網段的第二個IP的方案

1.首先在TAP-Win32網卡上配置一個和物理網卡同網段的第二個IP地址
我的物理網卡地址是192.168.40.34/24,於是乎我就配置給TAP-Win32網卡一個192.168.40.35/24,TAP-Win32網卡上的另外一個地址是OpenUOM服務端推送下來的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地址,而是OpenUOM服務端tap0網卡的MAC地址:
arp -s 192.168.40.254  00-ff-c4-d3-2e-da 172.16.0.30
注意,上述命令的最後一個參數是TAP-Win32網卡的OpenUOM服務端推下來的IP地址,有了它,該條映射就會建立在TAP-Win32網卡上。
3.將OpenUOM服務端推下來的路由的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.測試
在OpenUOM服務端的tap0上抓取到達a.b.c.d的包,發現源IP是192.168.40.35,也就是TAP-Win32網卡上的和物理網段同網段的第二個IP,不是OpenUOM服務端推送的虛擬IP地址172.16.0.30
缺點:
雖然達到了效果,但是不得不佔用兩個同網段的IP,即使不會衝突,也會很不爽,因爲在UOM的盡頭,如果有源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.連接OpenUOM服務器成功

爲了確保橋接過程中隧道斷開,要把keepalive時間設置足夠長久,我給了橋接一分鐘時間。

 

2.橋接TAP-Win32網卡與物理網卡

按下Ctrl鍵,左鍵點選TAP-Win32網卡和物理網卡“本地連接”;右鍵點選“橋接”。於是就生成了一個網橋。

 

3.配置網橋的IP地址

趕緊,用最快的速度,在keepalive到期前將IP地址配置好,其實也簡單,就是將原來的“本地連接”的IP地址和OpenUOM服務端推送的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地址是OpenUOM服務端的tap0的MAC地址。測試,發包,通過虛擬網卡,在接收端看來,源IP是192.168.40.34這個真實地址,而不再是172.16.0.30這個UOM虛擬網段的地址了。

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

 

5.悲哀的結果

貌似Windows的網橋只能手工配置啊!這可怎麼和OpenUOM的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映射到OpenUOM服務端的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網卡數據包的下一跳,然後添加一條該下一跳到OpenUOM服務端tap0網卡MAC地址的arp映射。
14.寫下本文,備忘!

 

 

留下的問題

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章