標籤:linux 處理器
上周有廠商到公司測試,拿了一塊據說很猛的網路處理加速PCIe板卡,拎在手裡沉甸甸的很有分量,最讓人意淫的是那4個萬兆光口,於是我迫不及待的想要一覽光口轉寄時那種看不見的震撼。
可是,僅憑4個光口怎麼測試?起碼你要有個“對端”啊!任何人應該都不想扛著三台機器在客戶們之間跑來跑去測試其轉寄效能,當然你也不能指望客戶那裡就一定有你需要的“對端”裝置,比如我們公司就沒有這種和萬兆光口對接的裝置,不過趕巧的是,那天還真有一台裝置帶有萬兆光口,但是只是碰巧了。最佳的測試方式當然是不依賴任何外部裝置了,顯而易見的方法就是做自環。
RJ45口的雙絞線可以做物理層自環,1/3,2/6短接即可,這樣一台機器的一塊網卡自己就可以既發又收了,但是你能對比頭髮略粗的光纖做什麼呢?真實的做法當然是用軟體解決了,在Linux上可以使用netns來解決,即net namespace。
netns是一個很好玩的東西,它可以讓你在一台機器上類比多個網路裝置,這樣做的意義是非同一般的:
1.使用netns可以充分利用閑置的處理器資源,特別是你的多塊網卡效能壓不滿CPU的時候;
2.使用netns可以將不同類型的網路應用隔離,針對每一類實施不同的策略;
3.使用netns有點玩虛擬化的意思,不過比虛擬機器更靈活。
一個net namespace有自己獨立的路由表,iptables策略,裝置管理機構,和其它的netns完全隔離,比如你將eth0加入了netns1,那麼netns2中的應用程式就看不到eth0,網卡裝置管理只是netns中的一個元素,還有很多,比如你在netns1中配置的iptables策略對netns2中的資料包沒有任何影響。總之,如果你懂Linux核心源碼,那麼只要附著有net結構體欄位的那些結構,比如skb,net_device,都和netns有關。
那麼我應該怎麼做自環呢?我的裝置有4個網卡,我希望1和4之間通訊,通過2和3轉寄,它的邏輯拓撲如下:
PC1/eth0----PC2/eth1(forward)PC2/eth2----PC3/eth3
很簡單,將eth0和eth3設定在兩個不同的netns,然後用線纜串連eth0和eth1,同樣串連eth2和eth3,最後將eth0和eth1的IP地址設定在一個網段,將eth2和eth3的IP地址設定在另一個不同的網段即可。光說不練假把式,具體應該怎麼做呢?同樣很簡單:
1.添加兩個netns
ip netns add t1
ip netns add t2
2.將eth0加入t1,並且設定IP地址
ip link set eth0 netns t1
此時再ifconfig就看不到eth0了,你甚至執行ls /sys/class/net也看不到eth0了,只有執行ip netns exec t1 ls /sys/class/net才能看到。
ip netns exec t1 ifconfig eth0 192.168.1.200/24
3.將eth3加入t2,並且設定IP地址
ip link set eth3 netns t2
此時ifconfig就看不到eth3了,你甚至執行ls /sys/class/net也看不到eth3了,只有執行ip netns exec t2 ls /sys/class/net才能看到。
ip netns exec t1 ifconfig eth3 172.16.1.200/24
4.設定eth1和eth2的地址
ifconfig eth1 192.168.1.1/24
ifconfig eth2 172.16.1.1/24
5.設定兩個netns的預設路由
ip netns exec t1 route add default gw 192.168.1.1
ip netns exec t2 route add default gw 172.16.1.1
6.測試
在netns t1中ping netns t2中的eth3地址
ip netns exec t1 ping 172.16.1.200
上述配置之後,從eth0發出的包會通過網線到達eth1(而不是走local路由表的loopback),然後經過eth1的forward從eth2發出。經由網線到達目的地eth3杯接收。整個過程中就一台機器,展示出的效果好像三台機器的樣子。有了這個機制,是不是再也不用為搭建測試環境而發愁了呢?
除了自環測試之外,netns還可以用於設定策略路由,這種策略路由不需要ip rule。試想一種情境,你同時運行了P1和P2兩個程式,本機所在的區域網路有兩個出口到達外網,你希望P1通過gw1和外界通訊,P2通過gw2和外界通訊,約束條件是你的機器只有一張網卡eth0,怎麼辦呢?通過iptables為P1和P2的資料包打上不同的mark,然後通過ip rule設定策略路由無疑可以解決,另外直接在P1和P2應用程式中用setsockopt也是可以設定ipmark的,這就不需要iptables了。然而這一切都過時了,2014年我需要一種不同的方式。
我不知道怎麼表達我思考的過程,但是給出一個操作序列是簡單的事情,因為照著這麼做確實可以滿足需求,然後看到這篇文章的人照著操作步驟反向推算回去,就可以得到一個思考過程。首先你要明白的是Linux核心支援一種虛擬網卡類型,即veth,一般而言veth是成對的,從一個veth發出的資料包可以直接到達它的peer veth,感興趣的可以看Linux核心的drivers/net/veth.c,和drivers/net/tun.c沒什麼不同,更簡單些罷了。第一步要做的就是建立一對veth:
ip link add veth1 type veth peer name veth2
此時系統中除了eth0之外又多了兩塊網卡,所有的網卡為lo,eth0,veth1,veth2。中間隱含著一個事實,即veth1和veth2之間有一條虛擬鏈路將兩塊網卡串連起來,就好像一條雙絞線串連的兩塊物理網卡一樣。我現在希望P1的資料包通過veth1發出,然後自然而然地就能發到veth2,但是隨後怎麼通過eth0發到物理線路呢?太簡單,太簡單,使用bridge吧:
brctl addbr br0
brctl addif br0 eth0 veth2
同時,veth1和br0所在的區域網路設定在一個IP網段中,這下子就全通了,該二層網路的邏輯拓撲為:
veth1----veth2(bridge)eth0----gw(1,2)
怎麼設定netns我本來不想說了,但是由於小小暫時不跟我玩了,我還是寫完吧。首先將veth1設定到netns1(具體怎麼建立netns,不再贅述)並設定路由:
ip link set veth1 netns netns1
ip netns exec netns1 route add default gw $gw1
route add default gw $gw2
這就完了?是的,完事了。事實上,保留br0的預設netns即可,沒有必要建立netns2了。接下來需要做的就是啟動P1和P2了:
ip netns exec netns1 P1
P2
好了,一切結束。
我始終都覺得,在Linux上一般都是不用修改源碼就能解決問題,可是我還是喜歡修改代碼,原因何在?很簡單,源碼很容易獲得,並且源碼很容易修改,我走火入魔般地寫了大量的Netfilter擴充以及做了大量的nf_conntrack修改,甚至還添加了一些該死的socket filter...雖然這些行為都是自娛自樂型的,並沒有被應用在工作中,但是這些行為說明我不是網路系統管理員,而是一名程式員,哈哈,自封的資深軟體工程師(我還是覺得這些成果能被應用)。然而,做一名技術精湛的網路管理員的難度卻遠遠超過做程式員的難度。這不,又一次遇到了OpenVPN的多執行個體問題,我覺得,單純的程式員搞不定它,單純的網管也不行。
TAP模式的多執行個體已經被我用Linux Bridge完美蹂躪了,但是TUN模式的多執行個體問題仍然沒有完美的方案,雖然修改tun驅動,使用broadcast mode bonding+tun filter可以解決,但是我還是覺得那是一種走火入魔的方式,因此就算在公司我也沒能將整個調試測試進行下去,結果落了個不了了之,事實上,是我太不喜歡那種方式。tun的IP filter是我改出來的方案,並非標準的,能不能使用標準的方式進行定址呢?使用netns,答案就是肯定的。
假設在GW上啟動了2個OpenVPN執行個體ovpn1和ovpn2,虛擬網卡分別為tun1和tun2,在client-connect指令碼中得知ovpn2負責N1,ovpn2負責N2。現在問題的關鍵是,GW後方出發的資料包如何知道是將資料包發送到tun1還是tun2,這個判斷能不能自動進行?如果使用netns,那就是可以的,我可以將2個tun分別設定在不同的netns,然後每一個netns對應一個同處一個netns的veth虛擬網卡,這些veth的peer們處在另外一個netns中,這樣就可以實現IP層TUN模式虛擬網卡到乙太網路的TAP模式虛擬網卡的適配。最後將這些peer們Bridge成一個br0,那麼TUN模式的OpenVPN就能和TAP模式的OpenVPN採用同一種方式處理了。
不管怎樣,當你玩弄netns的時候,你要知道你並不是在玩弄冷酷無情的虛擬化作業系統,也不是真的類比了兩台物理上相互隔離的機器,因為雖然兩個程式的網路是隔離的,但是檔案系統卻是共用的。你要時刻準備著,使用網路隔離和使用記憶體,檔案系統共用相結合。將一台機器既可以作為多台機器使用,又可以作為一台機器共用資源!
理解了上述的例子和最後的總結,那麼我來發問,單網卡或者沒有網卡怎麼玩自環?這個需求可能就是為了測試一下協議棧而已。略去思考的過程,很簡單,多加一個層次。比如你有一台機器一塊網卡也沒有,那麼你只需要下面的命令就可以在你的機器上實現IP轉寄或者bridge轉寄了:
ip link add v1 type veth peer name vp1
ip link add v2 type veth peer name vp2
brctl addbr br0
brctl addif vp1 vp2
ifconfig vp1 up
ifconfig vp2 up
sysctl -w net.ipv4.ip_forward=1
ip netns add t1
ip netns add t2
ip link set v1 netns t1
ip link set v2 netns t2
ip netns exec t1 ifconfig v1 1.1.1.1/24
ip netns exec t2 ifconfig v2 1.1.1.2/24
ip netns exec t1 ping 1.1.1.2
...