一、概述
近期在在搞網絡安全HCIE、CISP的認證的事,順便將VXLAN技術再次系統的學習一下,學習過程中看到雲原生實驗室里的一篇文章,就是關於VXLAN在Linux系統中的實踐,感覺文章寫得很好,所以整理后,和大家分享,話不多說,直接上干貨。
VXLAN是Virtual eXtensible Local Area Network的縮寫,RFC 7348的標題“A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks”,說明了VXLAN是一個在傳統Layer 3網絡上架設出來的Layer 2 overlay網絡。RFC Abstract如下:
This document describes Virtual eXtensible Local Area Network (VXLAN), which is used to address the need for overlay networks within virtualized data centers accommodating multiple tenants. The scheme and the related protocols can be used in networks for cloud service providers and enterprise data centers. This memo documents the deployed VXLAN protocol for the benefit of the Internet community.
二、VXLAN應用場景實踐
1、點對點的VXLAN
1)點對點場景中有兩服務器,由三層物理網絡連接。這兩台服務器可能在同一個機架上,或者在不同的機架上,亦或在物理距離相距甚遠的不同數據中心里。有4個VxLAN overlay網絡,標識分別為VNI 22, 34, 74, 98。來看看Server 1上的虛擬機VM1-1和Server 2上的虛擬機VM2-4,它們屬於相同的標識為VNI 22的VXLAN overlay網絡。虛擬機並不知道overlay網絡,也不知道VxLAN報文的封裝和解封過程,因為這些過程都由物理服務器上的VTEP完成。看到這里,即使你沒聽說過VTEP這個概念,也大概知道它的作用了吧,VTEP (VXLAN Tunnel End Point), An entity that originates and/or terminates VXLAN tunnels,VTEP負責VxLAN的封裝和解封裝。
這種部署方法是基於物理機能夠感知VXLAN協議,從而可以作為VxLAN的封裝和解封點(VTEP) 。另外一種部署方法是把VTEP設備的位置放在網絡設備中,比如交換機,可以作為一個VXLAN網絡來負責VxLAN報文的封裝和解封。在數據中心的部署中,物理機作為VTEP的部署方式能更好地和物理網絡設備解耦,並且更加方便運維。
2)接下來我們先實驗最簡單的點對點 VXLAN
網絡,點對點 VXLAN
即兩台主機構建的 VXLAN
網絡,每台主機上有一個 VTEP
,VTEP
之間通過它們的 IP 地址進行通信。點對點 VXLAN 網絡拓撲圖如圖所示:
為了不影響主機的網絡環境,我們可以使用 Linux VRF
來隔離 root network namespace 的路由。VRF(Virtual Routing and Forwarding)是由路由表和一組網絡設備組成的路由實例,你可以理解為輕量級的 network namespace
,只虛擬了三層的網絡協議棧,而 network namespace
虛擬了整個網絡協議棧。
注:Linux Kernel 版本大於 4.3
才支持 VRF,建議做本文實驗的同學先升級內核。升級內核方法:https://www.cnblogs.com/easonscx/p/12598255.html
下面結合VRF來創建一個點對點VXLAN網絡。如果你有兩台環境比較干凈的主機,可以直接進行配置,無需使用VRF 。
a.首先在192.168.57.50 機器上創建VXLAN接口
[root@localhost ~]# ip link add vxlan0 type vxlan id 42 dstport 4789 remote 192.168.57.54 local 192.168.57.50 dev eth0
重要參數解釋:
id 42 : 指定 VNI 的值,有效值在 1 到 之間。
dstport : VTEP 通信的端口,IANA 分配的端口是 4789。如果不指定,Linux 默認使用 8472。
remote : 對端 VTEP 的地址。
local : 當前節點 VTEP 要使用的 IP 地址,即當前節點隧道口的 IP 地址。
dev eth0 : 當前節點用於 VTEP 通信的設備,用來獲取 VTEP IP 地址。這個參數與 local 參數目的相同,二選一即可
b.查看VXLAN0的信息
[root@localhost ~]# ip -d link show vxlan0 11: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master vrf-test state UNKNOWN mode DEFAULT group default qlen 1000 link/ether 82:f3:76:95:ab:e1 brd ff:ff:ff:ff:ff:ff promiscuity 0 vxlan id 42 remote 192.168.57.54 local 192.168.57.50 srcport 0 0 dstport 4789 ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrxc
c.接下來創建個VRF,將vxlan0 綁定到VRF中,檢查vxlan0信息,你會發現多了 VRF 的信息。
[root@localhost ~]# ip link add vrf0 type vrf table 10 [root@localhost ~]# ip link set vrf0 up [root@localhost ~]# ip link set vxlan0 master vrf0
[root@localhost ~]# ip -d link show vxlan0 13: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master vrf0 state UNKNOWN mode DEFAULT group default qlen 1000 link/ether aa:4d:80:e3:75:e0 brd ff:ff:ff:ff:ff:ff promiscuity 0 vxlan id 42 remote 192.168.57.54 local 192.168.57.50 srcport 0 0 dstport 4789 ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx vrf_slave table 10 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
d.接下來為vxlan0配置IP地址並啟動它。
[root@localhost ~]# ip addr add 172.18.1.2/24 dev vxlan0 [root@localhost ~]# ip link set vxlan0 up
執行成功后會發現 VRF 路由表項多了下面的內容,所有目的地址是 172.18.1.0/24
網絡包要通過 vxlan0 轉發:
[root@localhost ~]# ip route show vrf vrf0 172.18.1.0/24 dev vxlan0 proto kernel scope link src 172.18.1.2
同時也會增加一條 FDB
轉發表:
[root@localhost ~]# bridge fdb show 00:00:00:00:00:00 dev vxlan0 dst 192.168.57.54 self permanent
這個表項的意思是,默認的 VTEP
對端地址為 192.168.57.54
。換句話說,原始報文經過 vxlan0
后會被內核添加上 VXLAN 頭部,而外部 UDP 頭的目的 IP 地址會被冠上 192.168.57.54
。
e.在另外一台主機上進行同樣配置
[root@localhost ~]# ip link add vxlan0 type vxlan id 42 dstport 4789 remote 192.168.57.50 [root@localhost ~]# ip link add vrf0 type vrf table 10 [root@localhost ~]# ip link set vrf0 up [root@localhost ~]# ip link set vxlan0 master vrf0 [root@localhost ~]# ip addr add 172.18.1.3/24 dev vxlan0 [root@localhost ~]# ip link set vxlan0 up
一切大功告成之后,就可以相互通信了,在 192.168.57.50
上 ping 172.18.1.3
:
[root@localhost ~]# ping 172.18.1.3 -I vrf0
同時使用 wireshark
遠程抓包:
[root@localhost ~]# ssh root@192.168.57.54 'tcpdump -i any -s0 -c 10 -nn -w - port 4789' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i
Tcpdump的使用請參考:https://www.cnblogs.com/easonscx/p/10616059.html
參考示例:
報文解釋:
VXLAN 報文可以分為三塊:
1、最內層是 overlay 網絡中實際通信的實體看到的報文(比如這里的 ARP 請求),它們和經典網絡的通信報文沒有任何區別,除了因為 MTU 導致有些報文比較小。
2、中間一層是 VXLAN 頭部,我們最關心的字段 VNI 確實是 42。
3、最外層是 VTEP 所在主機的通信報文頭部,目的 IP 地址為對端 192.168.57.54。
下面來分析這個最簡單的模式下 vxlan 通信的過程:
1、發送 ping 報文到 172.18.1.3,查看路由表,報文會從 vxlan0 發出去。
2、內核發現 vxlan0 的 IP 是 172.18.1.2/24,和目的 IP 在同一個網段,所以在同一個局域網,需要知道對方的 MAC 地址,因此會發送 ARP 報文查詢。
3、ARP 報文源 MAC 地址為 vxlan0 的 MAC 地址,目的 MAC 地址為全 1 的廣播地址(ff:ff:ff:ff:ff:ff)。
4、VXLAN 根據配置(VNI 42)添加上頭部。
5、對端的 VTEP 地址為 192.168.57.54,將報文發送到該地址。
6、對端主機接收到這個報文,內核發現是 VXLAN 報文,會根據 VNI 發送給對應的 VTEP。
7、VTEP 去掉 VXLAN 頭部,取出真正的 ARP 請求報文,同時,VTEP 會記錄源 MAC 地址和 IP 地址信息到 FDB 表中,這便是一次學習過程。然后生成 ARP 應答報文。
[root@localhost ~]# bridge fdb show 00:00:00:00:00:00 dev vxlan0 dst 192.168.57.50 self permanent aa:4d:80:e3:75:e0 dev vxlan0 dst 192.168.57.50 self
8、應答報文目的 MAC 地址是發送方 VTEP 的 MAC 地址,目的 IP 是發送方 VTEP 的 IP 地址,直接發送給目的 VTEP。
9、應答報文通過 underlay 網絡直接返回給發送方主機,發送方主機根據 VNI 把報文轉發給 VTEP,VTEP 解包取出 ARP 應答報文,添加 ARP 緩存到內核,並根據報文學習到目的 VTEP 的 IP 地址和目的 MAC 地址,添加到 FDB 表中。
[root@localhost ~]# ip neigh show vrf vrf0 172.18.1.3 dev vxlan0 lladdr 76:06:5c:15:d9:78 STALE [root@localhost ~]# bridge fdb show 00:00:00:00:00:00 dev vxlan0 dst 192.168.57.54 self permanent fe:4a:7e:a2:b5:5d dev vxlan0 dst 192.168.57.54 self
10、至此 VTEP 已經知道了通信需要的所有信息,后續 ICMP 的 ping 報文都是在這條邏輯隧道中單播進行的,不再需要發送 ARP 報文查詢。
總結以上過程:一個 VXLAN 網絡的 ping 報文要經歷 ARP 尋址 + ICMP 響應 兩個過程,一旦 VTEP 設備學習到了對方 ARP 地址,后續通信就可以免去 ARP 尋址的過程。
2、VXLAN+Bridge
前面學習了點對點 VXLAN
網絡通信雙方只有一個 VTEP
,且只有一個通信實體,而在實際生產中,每台主機上都有幾十台甚至上百台虛擬機或容器需要通信,因此需要一種機制將這些通信實體組織起來,再通過隧道口 VTEP
轉發出去。
方案其實也很常見,Linux Bridge 就可以將多塊虛擬網卡連接起來,因此可以選擇使用 Bridge
將多個虛擬機或容器放到同一個 VXLAN 網絡中,網絡拓撲圖如圖所示:
和點對點模式相比,這里只是多了一個 Bridge
,用來連接不同 network namespace 中的 veth pair
,同時 VXLAN
網卡也需要連接到該 Bridge
。
1)首先在 192.168.57.50
上創建 VXLAN 接口: (此處配置可以采用另外一種方法)
[root@localhost ~]# ip link add vxlan1 type vxlan \ > id 42 \ > dstport 4789 \ > local 192.168.57.50 \ > remote 192.168.57.54
然后創建網橋 bridge0
,把 VXLAN 網卡 vxlan0
綁定到上面,然后將 bridge0
綁定到 VRF
中,並啟動它們:
[root@localhost ~]# ip link add br0 type bridge [root@localhost ~]# ip link set vxlan0 master br0 [root@localhost ~]# ip link add vrf0 type vrf table 10 [root@localhost ~]# ip link set br0 master vrf0 [root@localhost ~]# ip link set vxlan0 up [root@localhost ~]# ip link set br0 up [root@localhost ~]# ip link set vrf0 up
下面創建 network namespace
和一對 veth pair
,並把 veth pair 的其中一端綁定到網橋,然后把另一端放到 network namespace 並綁定 IP 地址 172.18.1.2
:
[root@localhost ~]# ip netns add ns0 ............................................................................. [root@localhost ~]# ip link add veth0 type veth peer name eth0 netns ns0 [root@localhost ~]# ip link set veth0 master br0 [root@localhost ~]# ip link set veth0 up ............................................................................. [root@localhost ~]# ip -n ns0 link set lo up [root@localhost ~]# ip -n ns0 addr add 172.18.1.2/24 dev eth0 [root@localhost ~]# ip -n ns0 link set eth0 up
用同樣的方法在另一台主機上配置 VXLAN 網絡,綁定 172.18.1.3
到另外一個 network namespace
中的 eth0:
[root@localhost ~]# ip link add vxlan0 type vxlan \ > id 42 \ > dstport 4789 \ > local 192.168.57.54 \ > remote 192.168.57.50 [root@localhost ~]# ip link add br0 type bridge [root@localhost ~]# ip link set vxlan0 master br0 [root@localhost ~]# ip link add vrf0 type vrf table 10 [root@localhost ~]# ip link set br0 master vrf0 [root@localhost ~]# ip link set vxlan0 up [root@localhost ~]# ip link set br0 up [root@localhost ~]# ip link set vrf0 up [root@localhost ~]# ip netns add ns0 [root@localhost ~]# ip link add veth0 type veth peer name eth0 netns ns0 [root@localhost ~]# ip link set veth0 master br0 [root@localhost ~]# ip link set veth0 up [root@localhost ~]# ip -n ns0 link set lo up [root@localhost ~]# ip -n ns0 addr add 172.18.1.3/24 dev eth0 [root@localhost ~]# ip -n ns0 link set eth0 up
從 172.18.1.2
ping 172.18.1.3
發現整個通信過程和前面的實驗類似,只不過容器發出的 ARP
報文會先經過網橋,再轉發給 vxlan0
,然后在 vxlan0
處由 Linux 內核添加 VXLAN 頭部,最后發送給對端。
邏輯上,VXLAN 網絡下不同主機上的 network namespace
中的網卡都被連接到了同一個網橋上,這樣就可以在同一個主機上創建同一 VXLAN
網絡下的多個容器,並相互通信了。
3、多播模式下的VXLAN
前面兩種模式只能點對點連接,也就是說同一個VXLAN網絡中只能有兩個節點,這怎么能忍.....有沒有辦法讓同一個VXLAN網絡中容納多個節點呢?我們先來回顧一下VXLAN通信的兩個關鍵信息:
1)對方虛擬機(或容器)的MAC地址
2)對方所在主機的 IP 地址(即對端VTEP的IP地址)
跨主機的容器之間首次通信時需要知道對方的 MAC 地址,因此會發送 ARP 報文查詢。如果有多個節點,就要把 ARP 查詢報文發送到所有節點,但傳統的 ARP 報文廣播是做不到的,因為 Underlay 和 Overlay 不在同一個二層網絡,默認情況下 ARP 廣播是逃不出主機的。要想實現 Overlay 網絡的廣播,必須要把報文發送到所有 VTEP 所在的節點,為了解決這個問題,大概有兩種思路:
1)使用多播,把網絡中的某些節點組成一個虛擬的整體。
2)事先知道 MAC 地址和 VTEP IP 信息,直接把 ARP 和 FDB 信息告訴發送方 VTEP。一般是通過外部的分布式控制中心來收集這些信息,收集到的信息會分發給同一個 VXLAN 網絡的所有節點。
我們先來看看多播是怎么實現的,分布式控制中心留到下一篇再講。
注:如果 VXLAN 要使用多播模式,需要底層的網絡支持多播功能,多播地址范圍為 224.0.0.0~239.255.255.255
。
和上面的 點對點 VXLAN + Bridge 模式相比,這里只是將對端的參數改成 group
參數,其他不變,命令如下:
# 在主機 192.168.57.50 上執行 [root@localhost ~]# ip link add vxlan0 type vxlan \ > id 42 \ > dstport 4789 \ > local 192.168.57.50 \ > group 224.1.1.1 [root@localhost ~]# ip link add br0 type bridge [root@localhost ~]# ip link set vxlan0 master br0 [root@localhost ~]# ip link add vrf0 type vrf table 10 [root@localhost ~]# ip link set br0 master vrf0 [root@localhost ~]# ip link set vxlan0 up [root@localhost ~]# ip link set br0 up [root@localhost ~]# ip link set vrf0 up [root@localhost ~]# ip netns add ns0 [root@localhost ~]# ip link add veth0 type veth peer name eth0 netns ns0 [root@localhost ~]# ip link set veth0 master br0 [root@localhost ~]# ip link set veth0 up [root@localhost ~]# ip -n ns0 link set lo up [root@localhost ~]# ip -n ns0 addr add 172.18.1.2/24 dev eth0 [root@localhost ~]# ip -n ns0 link set eth0 up
# 在主機 192.168.57.54 上執行 [root@localhost ~]# ip link add vxlan0 type vxlan \ > id 42 \ > dstport 4789 \ > local 192.168.57.54 \ > group 224.1.1.1 $ ip link add br0 type bridge $ ip link set vxlan0 master br0 $ ip link add vrf0 type vrf table 10 $ ip link set br0 master vrf0 $ ip link set vxlan0 up $ ip link set br0 up $ ip link set vrf0 up $ ip netns add ns0 $ ip link add veth0 type veth peer name eth0 netns ns0 $ ip link set veth0 master br0 $ ip link set veth0 up $ ip -n ns0 link set lo up $ ip -n ns0 addr add 172.18.1.3/24 dev eth0 $ ip -n ns0 link set eth0 up
和上面的實驗明顯有區別的是 FDB 表項的內容:
[root@localhost ~]# bridge fdb show 00:00:00:00:00:00 dev vxlan0 dst 224.1.1.1 self permanent
dst 字段的值變成了多播地址 224.1.1.1,而不是之前對方的 VTEP 地址,VTEP 會通過 IGMP(Internet Group Management Protocol)[2] 加入同一個多播組 224.1.1.1。
我們來分析下多播模式下 VXLAN 通信的全過程:
1、發送 ping 報文到 172.18.1.3,查看路由表,報文會從 vxlan0發出去。
2、內核發現 vxlan0 的 IP 是 172.18.1.2/24,和目的IP在同一個網段,所以在同一個局域網,需要知道對方的MAC地址,因此會發送 ARP 報文查詢。
3、ARP 報文源 MAC 地址為 vxlan0 的 MAC 地址,目的MAC地址為全 1 的廣播地址(ff:ff:ff:ff:ff:ff)。
4、VXLAN 根據配置(VNI 42)添加上頭部。
5、到這一步就和之前不一樣了,由於不知道對端VTEP在哪台主機,根據多播配置,VTEP會往多播地址 224.1.1.1 發送多播報文。
6、多播組中的所有主機都會收到這個報文,內核發現是 VXLAN 報文,就會根據 VNI 發送給相應的 VTEP。
7、收到報文的所有主機的 VTEP 會去掉 VXLAN 的頭部,取出真正的ARP請求報文。同時,VTEP會記錄源 MAC地址和 IP地址信息到FDB表中,這便是一次學習過程。如果發現ARP 不是發送給自己的,就直接丟棄;如果是發送給自己的,則生成ARP應答報文。
8、面的步驟就和上面的實驗相同了。
整個通信過程和之前比較類似,只是 Underlay 采用組播的方式發送報文,對於多節點的VXLAN網絡來說比較簡單高效。但多播也是有它的問題的,並不是所有網絡設備都支持多播(比如公有雲),再加上多播方式帶來的報文浪費,在實際生成中很少被采用。下篇文章就着重介紹如何通過分布式控制中心來自動發現 VTEP 和 MAC 地址等信息。
-----------------------------------------------------------書山有路勤為徑,學海無涯苦作舟-------------------------------------------------------------
參考資料:
https://mp.weixin.qq.com/s/sCRf61OGrNPrQOEulQr8lg
Linux VRF(Virtual Routing Forwarding)的原理和實現: https://blog.csdn.net/dog250/article/details/78069964
IGMP(Internet Group Management Protocol): https://zh.wikipedia.org/wiki/%E5%9B%A0%E7%89%B9%E7%BD%91%E7%BB%84%E7%AE%A1%E7%90%86%E5%8D%8F%E8%AE%AE
linux 上實現 vxlan 網絡: https://cizixs.com/2017/09/28/linux-vxlan/