詳解:Linux網(wǎng)絡虛擬化技術
Linux的網(wǎng)絡虛擬化是LXC項目中的一個子項目,LXC包括文件系統(tǒng)虛擬化,進程空間虛擬化,用戶虛擬化,網(wǎng)絡虛擬化,等等,這里使用LXC的網(wǎng)絡虛擬化來模擬多個網(wǎng)絡環(huán)境。
本文從基本的網(wǎng)絡設備講起,到具體實驗環(huán)節(jié),希望能對 Linux 本身的虛擬網(wǎng)絡環(huán)境有一個宏觀的了解。
TUN 設備
TUN 設備是一種虛擬網(wǎng)絡設備,通過此設備,程序可以方便得模擬網(wǎng)絡行為。先來看看物理設備是如何工作的:

所有物理網(wǎng)卡收到的包會交給內(nèi)核的 Network Stack 處理,然后通過 Socket API 通知給用戶程序。下面看看 TUN 的工作方式:

普通的網(wǎng)卡通過網(wǎng)線收發(fā)數(shù)據(jù)包,但是 TUN 設備通過一個文件收發(fā)數(shù)據(jù)包。所有對這個文件的寫操作會通過 TUN 設備轉換成一個數(shù)據(jù)包送給內(nèi)核;當內(nèi)核發(fā)送一個包給 TUN 設備時,通過讀這個文件可以拿到包的內(nèi)容。
如果我們使用 TUN 設備搭建一個基于 UDP VPN,那么整個處理過程就是這樣:

數(shù)據(jù)包會通過內(nèi)核網(wǎng)絡棧兩次。但是經(jīng)過 App 的處理后,數(shù)據(jù)包可能已經(jīng)加密,并且原有的 ip 頭被封裝在 udp 內(nèi)部,所以第二次通過網(wǎng)絡棧內(nèi)核看到的是截然不同的網(wǎng)絡包。
TAP 設備
TAP 設備與 TUN 設備工作方式完全相同,區(qū)別在于:
TUN 設備的 /dev/tunX 文件收發(fā)的是 IP 層數(shù)據(jù)包,只能工作在 IP 層,無法與物理網(wǎng)卡做 bridge,但是可以通過三層交換(如 ip_forward)與物理網(wǎng)卡連通。
TAP 設備的 /dev/tapX 文件收發(fā)的是 MAC 層數(shù)據(jù)包,擁有 MAC 層功能,可以與物理網(wǎng)卡做 bridge,支持 MAC 層廣播
MACVLAN
有時我們可能需要一塊物理網(wǎng)卡綁定多個 IP 以及多個 MAC 地址,雖然綁定多個 IP 很容易,但是這些 IP 會共享物理網(wǎng)卡的 MAC 地址,可能無法滿足我們的設計需求,所以有了 MACVLAN 設備,其工作方式如下:

MACVLAN 會根據(jù)收到包的目的 MAC 地址判斷這個包需要交給哪個虛擬網(wǎng)卡。單獨使用 MACVLAN 好像毫無意義,但是配合之前介紹的 network namespace 使用,我們可以構建這樣的網(wǎng)絡:

由于 macvlan 與 eth0 處于不同的 namespace,擁有不同的 network stack,這樣使用可以不需要建立 bridge 在 virtual namespace 里面使用網(wǎng)絡。
MACVTAP
MACVTAP 是對 MACVLAN的改進,把 MACVLAN 與 TAP 設備的特點綜合一下,使用 MACVLAN 的方式收發(fā)數(shù)據(jù)包,但是收到的包不交給 network stack 處理,而是生成一個 /dev/tapX 文件,交給這個文件:

由于 MACVLAN 是工作在 MAC 層的,所以 MACVTAP 也只能工作在 MAC 層,不會有 MACVTUN 這樣的設備。
創(chuàng)建虛擬網(wǎng)絡環(huán)境
使用命令
- $ ip netns add net0
可以創(chuàng)建一個完全隔離的新網(wǎng)絡環(huán)境,這個環(huán)境包括一個獨立的網(wǎng)卡空間,路由表,ARP表,ip地址表,iptables,ebtables,等等??傊c網(wǎng)絡有關的組件都是獨立的。
使用命令
- $ ip netns list
- net0
可以看到我們剛才創(chuàng)建的網(wǎng)絡環(huán)境
進入虛擬網(wǎng)絡環(huán)境
使用命令
- $ ip netns exec net0
- `command`
我們可以在 net0 虛擬環(huán)境中運行任何命令

這樣我們可以在新的網(wǎng)絡環(huán)境中打開一個shell,可以看到,新的網(wǎng)絡環(huán)境里面只有一個lo設備,并且這個lo設備與外面的lo設備是不同的,之間不能互相通訊。
連接兩個網(wǎng)絡環(huán)境
新的網(wǎng)絡環(huán)境里面沒有任何網(wǎng)絡設備,并且也無法和外部通訊,就是一個孤島,通過下面介紹的這個方法可以把兩個網(wǎng)絡環(huán)境連起來,簡單的說,就是在兩個網(wǎng)絡環(huán)境之間拉一根網(wǎng)線。
- $ ip netns add net1
先創(chuàng)建另一個網(wǎng)絡環(huán)境net1,我們的目標是把net0與net1連起來。

這里創(chuàng)建連一對veth虛擬網(wǎng)卡,類似pipe,發(fā)給veth0的數(shù)據(jù)包veth1那邊會收到,發(fā)給veth1的數(shù)據(jù)包veth0會收到。就相當于給機器安裝了兩個網(wǎng)卡,并且之間用網(wǎng)線連接起來了。
- $ ip link set veth0 netns net0
- $ ip link set veth1 netns net1
這兩條命令的意思就是把veth0移動到net0環(huán)境里面,把veth1移動到net1環(huán)境里面,我們看看結果。

veth0 veth1已經(jīng)在我們的環(huán)境里面消失了,并且分別出現(xiàn)在net0與net1里面。下面我們簡單測試一下net0與net1的聯(lián)通性。

分別配置好兩個設備,然后用ping測試一下聯(lián)通性:

一個稍微復雜的網(wǎng)絡環(huán)境

創(chuàng)建虛擬網(wǎng)絡環(huán)境并且連接網(wǎng)線。
- ip netns add net0
- ip netns add net1
- ip netns add bridge
- ip link add type veth
- ip link set dev veth0 name net0-bridge netns net0
- ip link set dev veth1 name bridge-net0 netns bridge
- ip link add type veth
- ip link set dev veth0 name net1-bridge netns net1
- ip link set dev veth1 name bridge-net1 netns bridge
在bridge中創(chuàng)建并且設置br設備:
- ip netns exec bridge brctl addbr br
- ip netns exec bridge ip link set dev br up
- ip netns exec bridge ip link set dev bridge-net0 up
- ip netns exec bridge ip link set dev bridge-net1 up
- ip netns exec bridge brctl addif br bridge-net0
- ip netns exec bridge brctl addif br bridge-net1
然后配置兩個虛擬環(huán)境的網(wǎng)卡:

測試:

OK,以上便是今天的內(nèi)容。
最后
NDK基礎開發(fā)流程—Linux流程大綱
