1. 程式人生 > >(OK) 圖解幾個與Linux網路虛擬化相關的虛擬網絡卡-VETH/MACVLAN/MACVTAP/IPVLAN

(OK) 圖解幾個與Linux網路虛擬化相關的虛擬網絡卡-VETH/MACVLAN/MACVTAP/IPVLAN

Linux的網絡卡驅動中內含了很多“虛擬網絡卡”。早先的文章曾經詳細分析過tun,ifb等虛擬網絡卡,類似的思路,在虛擬化大行其道的趨勢下,Linux原始碼樹中不斷增加對“網路虛擬化”的支援,不光是為了支援“虛擬機器”技術,更多的是給了使用者和程式設計師更多的選擇。
       這些對網路虛擬化的支援技術包括任何重量級的虛擬化技術,比較重的比如對虛擬機器技術的支援,輕量級的則是net namespace技術。近期的工作基於net namespace技術,關於這個技術我也不多說了,它主要是提供了每個namespace獨立的協議棧以及網絡卡,對於網路協議棧以及網絡卡之外的部分,所有namespace是共享的,這種輕量級的針對網路的虛擬化技術對於模擬多客戶端網路連線特別有用而且操作簡單。我會單獨寫一篇文章來展示這種操作。
       如果僅僅為了完成工作,那麼我不會寫這篇文章,早在去年的時候,我寫過一篇關於net namespace的,根據那個裡面的step by step,工作就已經可以完成了,並且在去年年末到今年年初,這個工作我們也已經做過了,然而對於學習而言,就不是這樣了。學習應該是碰到一點折騰一點,我知道,很多人都知道,現在不比上學那會兒了,我們誰都沒有整塊的時間系統地進行學習,特別是對於我這種結了婚有了孩子,需要為了還貸款而不再任性的路人丙來講,更是這樣。因此就需要對所碰到的技術有一種可遇而不可求的相見恨晚的感覺,這樣就有動力把它吃透了。
       本文中,我想通過幾張圖來介紹一下Linux中常用的幾類和網路虛擬化相關的虛擬網絡卡,當然,這些虛擬網絡卡的使用場景並不僅限於net namespace,重量級的虛擬機器也可以使用,之所以用net namespace舉例是因為它的簡單性。總體來說,這些虛擬網絡卡的原理就擺在那裡,具體在什麼場景下使用它們,就看你自己的想象力了。

網路虛擬化

總體來講,所謂的網路虛擬化在本文中指的是主機中的網路虛擬化,側重於在一臺物理主機中,分離出多個TCP/IP協議棧的意思。網路虛擬化可以獨立實現,也可以依託別的技術實現。在Linux中,獨立的網路虛擬化實現就是net namespace技術,依託別的技術實現的網路虛擬化就是虛擬機器技術,我們當然知道,每個虛擬機器裡面都有自己的協議棧,而這種依託虛擬機器技術實現的網路虛擬化可能還要更簡單一些,因為宿主機並不需要去“實現”一個協議棧,而是把這個任務交給了虛擬機器的作業系統來完成,宿主機“相信”虛擬機器裡面一定執行著一個擁有協議棧的作業系統。

理解虛擬網絡卡的要旨

你要知道,一塊網絡卡就是一道門,一個介面,它上面一般接協議棧,下面一般接介質。最關鍵的是,你要明確它們確實在上面和下面接的是什麼。
       由於網絡卡的上介面在OS中實現,或者使用PF技術在使用者態實現,總而言之,它們是軟的,這就意味著你可以任意實現它們。反之,下介面便不受機器執行軟體的控制了,你無法通過軟體改變雙絞線的事實,不是嗎?故此,我們一般關注網絡卡下面接的是什麼,是什麼呢?姑且將它叫做endpoint吧。在開始正文之前,我先列舉幾個常見的endpoint:
乙太網ETHx
:普通雙絞線或者光纖;
TUN/TAP:使用者可以用檔案控制代碼操作的字元裝置;
IFB:一次到原始網絡卡的重定向操作;
VETH:觸發虛擬網絡卡對兒peer的RX;
VTI:加密引擎;
...
       關於資料在宿主網絡卡和虛擬網絡卡之間的路由(廣義的路由),有很多方式,在早期的核心中,對bridge(Linux的bridge也算是一種虛擬網絡卡)的支援是靠一個在netif_receive_skb中硬編碼呼叫的一個br_handle_frame_hook鉤子來實現的,這個鉤子由bridge模組註冊。但是隨著虛擬網絡卡種類的越來越多,總不能每一種都硬編碼這麼一種鉤子,這樣會使得netif_receive_skb顯得太臃腫,因此一種新的方式被提出來了,事實上很簡單,就是將這種鉤子向上抽象了一層,不再硬編碼,而是統一在netif_receive_skb中呼叫唯一的一個rx_handler的鉤子。具體如何設定這種鉤子,就看這個宿主網絡卡需要繫結哪種型別的虛擬網絡卡了,比如:
對於bridge
:呼叫netdev_rx_handler_register(dev, br_handle_frame, p),在netif_receive_skb中呼叫的是br_handle_frame;
對於bonding:呼叫netdev_rx_handler_register(slave_dev, bond_handle_frame, new_slave),在netif_receive_skb中呼叫的是bond_handle_frame;
對於MACVLAN:呼叫netdev_rx_handler_register(dev, macvlan_handle_frame, port),在netif_receive_skb中呼叫的是macvlan_handle_frame;
對於IPVLAN:呼叫netdev_rx_handler_register(dev, ipvlan_handle_frame, port),在netif_receive_skb中呼叫的是ipvlan_handle_frame;
對於...
每一塊宿主網絡卡只能註冊一個rx_handler,但是網絡卡和網絡卡卻可以疊加。

VETH虛擬網絡卡技術

關於這個虛擬網絡卡,我在《OpenVPN多處理之-netns容器與iptables CLUSTER》中有提到過,每一個VETH網絡卡都是一對兒乙太網卡,除了xmit介面與常規的乙太網卡驅動不同之外,其它的幾乎就是一塊標準的乙太網卡。VETH網絡卡既然是一對兒兩個,那麼我們把一塊稱作另一塊的peer,標準上也是這麼講的。其xmit的實現就是:將資料傳送到其peer,觸發其peer的RX。那麼問題來了,這些資料如何傳送到VETH網絡卡對兒之外呢?自問必有自答,自答如下:
1.如果確實需要將資料發到外部,通過將一塊VETH網絡卡和一塊普通ETHx網絡卡進行bridge,通過bridge邏輯將資料forward到ETHx,進而發出;
2.難道非要把資料包發往外部嗎?類似loopback那樣的,不就是自發自收嗎?使用VETH可以很方面並且隱祕地將資料包從一個net namespace傳送到同一臺機器的另一個net namespace,並且不被嗅探到。
       VETH虛擬網絡卡非常之簡單,原理圖如下所示:


VETH使用原始樸素的方式連線了不同的net namespace,符合UNIX的風格,因此你需要動用很多別的技術或者工具來完成net namespace的隔離以及資料的傳送。

MACVLAN虛擬網絡卡技術

MACVLAN技術可謂是提出一種將一塊乙太網卡虛擬成多塊乙太網卡的極簡單的方案。一塊乙太網卡需要有一個MAC地址,這就是乙太網卡的核心中的核心。
       以往,我們只能為一塊乙太網卡新增多個IP地址,卻不能新增多個MAC地址,因為MAC地址正是通過其全球唯一性來標識一塊乙太網卡的,即便你使用了建立ethx:y這樣的方式,你會發現所有這些“網絡卡”的MAC地址和ethx都是一樣的,本質上,它們還是一塊網絡卡,這將限制你做很多二層的操作。有了MACVLAN技術,你可以這麼做了。
       我們先來看一下MACVLAN技術的流程示意圖:


在具體的執行上,通過下面的命令,你可以建立一個MACVLAN網絡卡,它是基於eth0虛擬出來的:
ip link add link eth0 name macv1 type macvlan
你可以認為有人將雙絞線“物理上”每根一分為二,接了兩個水晶頭,從而連線了兩塊網絡卡,其中一塊是虛擬的MACVLAN網絡卡。但是既然共享介質,難道不用執行CSMA/CD嗎?當然不用,因為事實上,最終的資料是通過eth0發出的,而現代的乙太網卡工作的全雙工模式,只要是交換式全雙工(某些標準而言,這是必須的),eth0自己能做好。
       現在可以說一下MACVLAN技術構建的虛擬網絡卡的模式了。之所以MACVLAN擁有所謂的模式,是因為相比VETH,它更是將複雜性建立在了一個已經容不下什麼的乙太網概念上,因此相互互動的元素就會太多,它們之間的關係不同,導致最終MACVLAN的行為不同。還是圖解的方式:

1.bridge模式


這個bridge只是針對同屬於一塊宿主乙太網卡的MACVLAN網絡卡以及宿主網絡卡之間的通訊行為的,與外部通訊無關。所謂的bridge指的是在這些網絡卡之間,資料流可以實現直接轉發,不需要外部的協助,這有點類似於Linux BOX內建了一個bridge,即用brctl命令所做的那一切。

2.VEPA模式


VEPA模式我後面會專門講。現在要知道的是,在VEPA模式下,即使是MACVLANeth1和MACVLANeth2同時配在在eth0上,它們兩者之間的通訊也不能直接進行,而必須通過與eth0相連的外部的交換機協助,這通常是一個支援“髮夾彎”轉發的交換機。

3.private模式


這種private模式的隔離強度比VEPA更強。在private模式下,即使是MACVLANeth1和MACVLANeth2同時配在在eth0上,eth0連線了外部交換機S,S支援“髮夾彎”轉發模式,即便這樣,MACVLANeth1的廣播/多播流量也無法到達MACVLANeth2,反之亦然,之所以隔離廣播流量,是因為乙太網是基於廣播的,隔離了廣播,乙太網將失去了依託。
       如果你想配置MACVLAN的模式,請在ip link命令後面新增mode引數:
ip link add link eth0 name macv1 type macvlan mode bridge|vepa|private

VETH網絡卡與MACVLAN網絡卡之間的異同

我們先看一下如何配置一個獨立的net namespace。

1.VETH方式

ip netns add ns1
ip link add v1 type veth peer name veth1
ip link set v1 netns ns1
brctl addbr br0
brctl addif br0 eth0
brctl addif br0 veth1

ifconfig br0 192.168.0.1/16

2.MACVLAN方式

ip link add link eth0 name macv1 type macvlan
ip link set macv1 netns ns1
可以看到,MACVLAN做起同樣的事,比VETH來的簡單了。那麼效率呢?Linux的bridge基於軟體實現,需要不斷查詢hash表,這個同樣也是MACVLAN bridge模式的做法,但是VEPA模式和private模式下,都是直接轉發的。它們的區別可以從下圖展示出來:


VEPA技術

VEPA是什麼?Virtual Ethernet Port Aggregator。它是HP在虛擬化支援領域對抗Cisco的VN-Tag的技術。所以說,Cisco的VN-Tag和VEPA旨在解決同一個問題或者說同一類問題。解決的是什麼問題呢?通俗點說,就是虛擬機器之間網路通訊的問題,特別是位於同一個宿主機內的虛擬機器之間的網路通訊問題。
       難道這個問題沒有解決嗎?我使用的VMWare可以在我的PC中建立多個虛擬機器,即便我拔掉我的PC機網線,這些虛擬機器之間也能通訊...VMWare內部有一個vSwitch。就是說,幾乎所有的虛擬機器技術,內建的交叉網路都能解決虛擬機器之間的通訊問題。那麼還要VN-Tag以及VEPA幹什麼?
       這個問題涉及到兩個領域,一個是擴充套件性問題,另一個是職責邊界問題。說明白點就是,內建的vSwitch之類的東西在效能和功能上足以滿足要求嗎?它屬於虛擬機器軟體廠商的邊緣產品,甚至說不是一個獨立的產品,它一般都是附屬虛擬機器軟體贈送的,沒有自己的銷售盈利模式,虛擬機器廠商之所以內建它是因為它只是為了讓使用者體驗到虛擬機器之間“有相互通訊的能力”,所以廠商是不會發力將這種內建的虛擬交換機或者虛擬路由器做完美的,它們推的是虛擬機器軟體本身。
       另外,千百年來,網路管理員和系統管理員之間的職責邊界是清晰的,直到到達了虛擬化時代。如果使用內建的虛擬交換機,那麼如果這個交換機出了故障或者有複雜的配置任務計劃,找誰呢?要知道這個虛擬交換機內置於宿主伺服器內部,這是系統管理員的領域,一般的網管設定無法觸控到這些裝置,資料中心複雜的三權分立管理模式也無法讓網管去登入伺服器。反過來,系統管理員對網路協議的認知程度又遠遠比不上專業網管。這就造成了內置於虛擬機器軟體的虛擬網路裝置的尷尬處境。另一方面,這個虛擬的網路裝置確實不是很專業的網路裝置。爆炸!
       Cisco不愧為網路界的大咖。它總是在出現這種尷尬場景的時候率先提出一個標準,於是它改造了乙太網協議,推出了VN-Tag,就像ISL之於IEEE802.1q那樣。VN-Tag在標準的協議頭中增加了一個全新的欄位,這種做法的前提是Cisco有能力用最快的速度推出一款裝置並讓其真正跑起來。在看看HP的反擊,HP沒有Cisco那樣的能力,它不會去修改協議頭,但是它可以修改協議的行為從而解決問題,雖然比Cisco晚了一步,但是HP提出的VEPA不愧是一種更加開放的方式,Linux可以很容易的增加對其的支援。
       VEPA,它很簡單,一個數據包從一個交換機的一個網口進入,然後從同一個網口發回去,好像是毫無意義的做法,但是它卻沒有改變乙太網的協議頭。這種做法在平常看來真的是毫無意義的,因為正常來講,一塊網絡卡連線一根網線,如果是自己發給自己的資料,那麼這個資料是不會到達網絡卡的,對於Linux而言,直接就被loopback給bypass了。但是對於虛擬化場景而言,情況就有所不同了,雖然物理宿主機上可能擁有一塊乙太網卡,但是從該網絡卡發出的資料包卻不一定來自同一個協議棧,它可能來自不同的虛擬機器或者不同的net namespace(僅針對Linux),因為在支援虛擬化OS的內部,一塊物理網絡卡被虛擬成了多塊虛擬網絡卡,每一塊虛擬網絡卡屬於一個虛擬機器...此時,如果不修改乙太網協議頭且又沒有內建的虛擬交換機,就需要外部的一臺交換機來協助轉發,典型的就是從一個交換口收到資料包,把它從該口再發出去,由宿主網絡卡決定是否接收以及如何接收。如下圖所示:


       對於乙太網卡而言,硬體上根本就不需要任何修改,軟體驅動修改即可,對於交換機而言,需要修改的很少,只要在MAC/Port對映表查詢失敗的情況下,將資料包廣播到包括入口的所有埠即可,對於STP協議,也是類似的修改。對於HP而言,發出VEPA是一個正確的選擇,因為它不像Cisco和Intel那樣,可以大量生產網絡卡和裝置,從而控制硬體標準。對於支援VEPA的交換機而言,僅僅需要支援一種“髮夾彎”的模式就可以了。爆炸!

IPVLAN虛擬網絡卡技術

這個小節我們來看下IPVLAN。在理解了MACVLAN之後,理解IPVLAN就十分容易了。IPVLAN和MACVLAN的區別在於它在IP層進行流量分離而不是基於MAC地址,因此,你可以看到,同屬於一塊宿主乙太網卡的所有IPVLAN虛擬網絡卡的MAC地址都是一樣的,因為宿主乙太網卡根本不是用MAC地址來分流IPVLAN虛擬網絡卡的流量的。具體的流程如下圖所示:


IPVLAN的建立命令如下:
ip link add link <master-dev> <slave-dev> type ipvlan mode { l2 | L3 }
將一個IPVLAN虛擬網絡卡放入一個獨立的net namespace的方式和MACVLAN完全一樣,但是它倆之間改如何作出選擇呢?好在IPVLAN有Linux原始碼樹上的Document,因此我就不多嘴了:
4.1 L2 mode: In this mode TX processing happens on the stack instance attached to the slave device and packets are switched and queued to the master device to send out. In this mode the slaves will RX/TX multicast and broadcast (if applicable) as well.
4.2 L3 mode: In this mode TX processing upto L3 happens on the stack instance attached to the slave device and packets are switched to the stack instance of the master device for the L2 processing and routing from that instance will be used before packets are queued on the outbound device. In this mode the slaves will not receive nor can send multicast / broadcast traffic.
5. What to choose (macvlan vs. ipvlan)? These two devices are very similar in many regards and the specific use case could very well define which device to choose. if one of the following situations defines your use case then you can choose to use ipvlan - (a) The Linux host that is connected to the external switch / router has policy configured that allows only one mac per port. (b) No of virtual devices created on a master exceed the mac capacity and puts the NIC in promiscous mode and degraded performance is a concern. (c) If the slave device is to be put into the hostile / untrusted network namespace where L2 on the slave could be changed / misused.

MACVTAP虛擬網絡卡技術

這是本文談到的最後一種虛擬網絡卡。為什麼會有這種虛擬網絡卡呢?我們還是從問題說起。
       如果一個使用者態實現的虛擬機器或者模擬器,它在執行OS的時候,怎麼模擬網絡卡呢?或者說我們實現了一個使用者態的協議棧,和核心協議棧完全獨立,你可以把它們想象成兩個net namespace,此時如何把物理網絡卡的流量路由到使用者態呢?或者反過來,如何將使用者態協議棧發出的資料路由到BOX外部呢?按照常規的想法,我們知道TAP網絡卡的endpoint是一個使用者態可訪問的字元裝置,OpenVPN使用的就是它,很多輕量級使用者態協議棧也有用到它,我們會給出下面的方案:


又要用到“萬能的bridge”。這是多麼的麻煩,這是多麼的可悲。

       正如MACVLAN替代VETH+Bridge一樣,稍微該一下的MACVLAN也能替代TAP+Bridge,很簡單,那就是將rx_handler實現修改一下,宿主乙太網卡收到包之後,不交給MACVLAN的虛擬網絡卡上介面連線的協議棧,而是發到一個字元裝置佇列。很簡單吧,這就是MACVTAP!


遺憾的多佇列TUN/TAP虛擬網絡卡技術

這是老溼在2014年的時候做的,其實只是做了一些移植和修改工作。但是發現有了MACVTAP之後,我的這個版本瞬間就被爆了。遺憾!向之所欣,俯仰之間,已為陳跡。