天天看點

VXLAN原理介紹以及Linux和OvS的實作分析

雲計算環境的一個典型屬性是多租戶共享實體資源。其中每個租戶可以建構自己專屬的虛拟邏輯網絡,而每個邏輯網絡都需要由唯一的辨別符來辨別。不同的邏輯網絡預設情況下互相隔離。傳統上,網絡工程師一般使用VLAN來隔離不同二層網絡,但VLAN的辨別符命名空間隻有12位,隻能提供4096個辨別符,這無法滿足大型雲計算環境的需求。另外,使用VLAN隔離虛拟邏輯網絡,往往需要對底層實體網絡裝置進行手動配置,這無法滿足雲計算環境的自動化需求。為了解決VLAN在網絡虛拟化環境中應用存在的種種問題,Cisco,VMware等廠商提出了新的網絡協定VXLAN來隔離虛拟邏輯網絡。

VxLAN基本原理

VXLAN的全稱為Virtual eXtensible LAN,從名稱看,它的目标就是擴充VLAN協定。802.1Q的VLAN TAG隻占12位,隻能提供4096個網絡辨別符。而在VXLAN中,辨別符擴充到24位,能提供16777216個邏輯網絡辨別符,VXLAN的辨別符稱為VNI(VXLAN Network Identifier)。另外,VLAN隻能應用在一個二層網絡中,而VXLAN通過将原始二層以太網幀封裝在IP協定包中,在IP基礎網絡之上建構overlay的邏輯大二層網絡。

我們來看具體協定包結構。VXLAN将二層資料幀封裝在UDP資料包中,建構隧道在不同節點間通信。包結構如圖:

VXLAN原理介紹以及Linux和OvS的實作分析

從包結構上可以看到,VXLAN會額外消耗50位元組的空間。為了防止因資料包大小超過網絡裝置的MTU值而被丢棄,需要将VM的MTU減少50甚至更多,或者調整中間網絡裝置的MTU。

VXLAN協定中将對原始資料包進行封裝和解封裝的裝置稱為VTEP(VXLAN Tunnel End Point),它可以由硬體裝置實作,也可以由軟體實作。

VXLAN的整體應用示意拓樸結構如圖:

VXLAN原理介紹以及Linux和OvS的實作分析

我們來看VXLAN的通信過程。在上圖的虛拟機VM1和VM2處于邏輯二層網絡中。VM1發出的二層以太網幀由VTEP封裝進IP資料包,之後發送到VM2所在主機。VM2所在主機接收到IP封包後,解封裝出原始的以太網幀再轉發給VM2。然而,VM1所在主機的VTEP做完資料封裝後,如何知道要将封裝後的資料包發到哪個VTEP呢?實際上,VTEP通過查詢轉發表來确定目标VTEP位址,而轉發表通過泛洪和學習機制來建構。目标MAC位址在轉發表中不存在的流量稱為未知單點傳播(Unknown unicast)。廣播(broadcast)、未知單點傳播(unknown unicast)群組播(multicast)一般統稱為BUM流量。VXLAN規範要求BUM流量使用IP多點傳播進行洪泛,将資料包發送到除源VTEP外的所有VTEP。目标VTEP發送回響應資料包時,源VTEP從中學習MAC位址、VNI和VTEP的映射關系,并添加到轉發表中。後續VTEP再次發送資料包給該MAC位址時,VTEP會從轉發表中直接确定目标VTEP,進而隻發送單點傳播資料到目标VTEP。

OpenvSwitch沒有實作IP多點傳播,而是使用多個單點傳播來實作洪泛。洪泛流量本身對性能有一定影響,可以通過由controller收集相應資訊來填充轉發表而避免洪泛。

Linux環境中常用的VXLAN實作有兩種:

  • Linux核心實作
  • OpenvSwitch實作 接下來分别以執行個體來說明。

Linux 的實作

首先看Linux核心實作執行個體。我們的測試環境有三台主機,實體網卡IP分别為192.168.33.12/24, 192.168.33.13/24和192.168.33.14/24。我們在每台機器上建立一個Linux Bridge, 三台主機上的Linux Bridge預設接口的IP分别設定為10.1.1.2/24, 10.1.1.3/24和10.1.1.4/24。此時三台主機的Linux網橋處于同一虛拟二層網絡,但由于沒有互相連接配接,是以無法互相通路。我們通過建立VXLAN隧道使其可互相通路實作虛拟二層網絡10.1.1.0/24。網絡結構如圖:

VXLAN原理介紹以及Linux和OvS的實作分析

首先在主機1上建立Linux網橋:

brctl addbr br0
           

給網橋接口設定IP并啟動:

ip addr add 10.1.1.2/24 dev br0
ip link set up br0
           

我們從主機1通路主機2上的虛拟二層網絡IP, 通路失敗:

[root@localhost vagrant]# ping 10.1.1.3
PING 10.1.1.3 (10.1.1.3): 56 data bytes
^C
--- 10.1.1.3 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
           

接下來,我們添加VTEP虛拟接口vxlan0, 并加入多點傳播IP:239.1.1.1, 後續發送到該多點傳播IP的資料包,該VTEP都可以接收到:

ip link add vxlan0 type vxlan id 1 group 239.1.1.1 dev eth1 dstport 4789
           

将虛拟接口vxlan0連接配接到網橋:

brctl addif br0 vxlan0
           

在另外兩台主機上也完成相似配置後,我們開始測試。

首先在主機1檢視VTEP的轉發表,可以看到此時隻有一條多點傳播條目,所有發出流量都将發送給該多點傳播IP:

[root@localhost vagrant]# bridge fdb show dev vxlan0
00:00:00:00:00:00 dst 239.1.1.1 via eth1 self permanent
           

我們再次從主機1上通路主機2上的網橋IP, 此時通路成功:

[root@localhost vagrant]# ping 10.1.1.3
PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=1.58 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.610 ms
^C
--- 10.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.610/1.096/1.582/0.486 ms
           

此時再次在主機1上檢視VTEP轉發表,可以看到轉發表中已經學習到10.1.1.3所在主機的VTEP位址:192.168.33.13,下次再發送資料給10.1.13所對應的MAC該直接發送到192.168.33.13:

[root@localhost vagrant]# bridge fdb show dev vxlan0
00:00:00:00:00:00 dst 239.1.1.1 via eth1 self permanent
2e:7a:a2:53:7b:31 dst 192.168.33.13 self
           

我們根據主機2上tcpdump的抓包結果來分析具體過程:

14:57:54.330846 IP 192.168.33.12.38538 > 239.1.1.1.4789: VXLAN, flags [I] (0x08), vni 1
ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28
           

10.1.1.2所在主機不知道10.1.1.3對應的MAC位址,因而發送ARP廣播,ARP資料包發送至VTEP,VTEP進行封裝并洪泛給多點傳播IP:239.1.1.1。

14:57:54.330975 IP 192.168.33.13.58823 > 192.168.33.12.4789: VXLAN, flags [I] (0x08), vni 1
ARP, Reply 10.1.1.3 is-at 2e:7a:a2:53:7b:31, length 28
           

主機2收到資料包之後解封裝,VTEP會學習資料包的MAC及VTEP位址,将其添加到轉發表,并将解封後的資料幀發送到網橋接口10.1.1.3。10.1.1.3的接口發送ARP響應。

14:57:54.332055 IP 192.168.33.12.48980 > 192.168.33.13.4789: VXLAN, flags [I] (0x08), vni 1
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 4478, seq 1, length 64
           

主機1之後開始發送ICMP資料包,從這裡可以看出外層IP位址不再為多點傳播IP,而是學習到的192.168.33.13。

14:57:54.332225 IP 192.168.33.13.55921 > 192.168.33.12.4789: VXLAN, flags [I] (0x08), vni 1
IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 4478, seq 1, length 64
           

接着,10.1.1.3發送回ICMP響應包。

OvS 的實作

下面再來說明OpenvSwitch實作執行個體。

OVS不支援多點傳播,需要為任意兩個主機之間建立VXLAN單點傳播隧道。與上邊示例的拓樸結構相比,我們使用了兩個OVS網橋,将虛拟邏輯網絡的接口接入網橋br-int,将所有VXLAN接口接入br-tun。兩個網橋使用PATCH類型接口進行連接配接。由于網橋br-tun上有多個VTEP,當BUM資料包從其中某個VTEP流入時,資料包會從其他VTEP接口再流出,這會導緻資料包在主機之間無限循環。因而我們需要添加流表使VTEP流入的資料包不再轉發至其他VTEP。若邏輯網絡接口與VTEP連接配接同一網橋,配置流表将比較繁瑣。單獨将邏輯網絡接口放到獨立的網橋上,可以使流表配置非常簡單,隻需要設定VTEP流入的資料包從PATCH接口流出。

拓樸結構如圖:

VXLAN原理介紹以及Linux和OvS的實作分析

首先在主機1上建立網橋br-int和br-tun:

ovs-vsctl add-br br-int
ovs-vsctl add-br br-tun
           

建立PATCH接口連接配接br-int和br-tun:

ovs-vsctl add-port br-int patch-int -- set interface patch-int type=patch options:peer=patch-tun
ovs-vsctl add-port br-tun patch-tun -- set interface patch-tun type=patch options:peer=patch-int
           

建立單點傳播VTEP連接配接主機2:

ovs-vsctl add-port br-tun vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=192.168.33.13
           

建立單點傳播VTEP連接配接主機3:

ovs-vsctl add-port br-tun vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=192.168.33.14
           

接下來,我們給br-tun添加流表來處理流量。

首先檢視br-tun上各接口的PORT ID,從結果看到Patch Port為1,VTEP分别為5和6:

[root@localhost vagrant]# ovs-ofctl show br-tun
OFPT_FEATURES_REPLY (xid=0x2): dpid:00006e12f4fd6949
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
1(patch-tun): addr:92:fa:08:fb:3c:bf
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
5(vxlan0): addr:e6:65:c2:85:79:ea
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
6(vxlan1): addr:2a:38:b0:fa:b2:98
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
LOCAL(br-tun): addr:6e:12:f4:fd:69:49
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
           

清空br-tun流表:

ovs-ofctl del-flows br-tun
           

資料包進入br-tun後開始比對table 0的流。我們使用table 0區分流量來源。來源于br-int的資料包由table 1處理,VTEP流入的資料交由table 2處理, 并丢棄其他PORT進入的資料包:

ovs-ofctl add-flow br-tun "table=0,priority=1,in_port=1 actions=resubmit(,1)”
ovs-ofctl add-flow br-tun "table=0,priority=1,in_port=5 actions=resubmit(,2)" 
ovs-ofctl add-flow br-tun "table=0,priority=1,in_port=6 actions=resubmit(,2)”
ovs-ofctl add-flow br-tun "table=0,priority=0,actions=drop”
           

接着添加table 1的流, table 1用于處理來自br-int的流量,單點傳播資料包交由table 20處理,多點傳播或廣播資料包交由table 21處理:

ovs-ofctl add-flow br-tun "table=1,priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00,actions=resubmit(,20)”
ovs-ofctl add-flow br-tun "table=1,priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=resubmit(,21)”
           

table 21處理廣播流量,将資料包從所有VTEP的PORT口發出:

ovs-ofctl add-flow br-tun "table=21,priority=0,actions=output:5,output:6”
           

table 2處理VTEP流入的資料包,在這裡我們實作學習機制。來自VTEP的資料包到達後,table 2從中學習MAC位址,VNI、PORT資訊,并将學習到的流寫入table 20中,并将流量由PATCH口發送到br-int上, 并将學習到的流優先級設為1:

ovs-ofctl add-flow br-tun "table=2,priority=0,actions=learn(table=20,hard_timeout=300,priority=1,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]), output:1”
           

table 20處理單點傳播流量,我們将預設流優先級設定為0。因為學習到的流優先級設定為1,因而隻有比對不到目标MAC的未知單點傳播交由table 21處理,table 21将流量廣播到所有VTEP:

ovs-ofctl add-flow br-tun "table=20,priority=0,actions=resubmit(,21)"
           

整體處理邏輯如圖:

我們檢視流表, 發現table 20中隻有一條預設添加的流:

[root@localhost vagrant]# ovs-ofctl dump-flows br-tun table=20
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=1.832s, table=20, n_packets=0, n_bytes=0, idle_age=1, priority=0 actions=resubmit(,21)
           

我們從主機1通路主機3上的虛拟網絡IP,通路成功:

[root@localhost vagrant]# ping 10.1.1.4 -c 2
PING 10.1.1.4 (10.1.1.4) 56(84) bytes of data.
64 bytes from 10.1.1.4: icmp_seq=1 ttl=64 time=1.45 ms
64 bytes from 10.1.1.4: icmp_seq=2 ttl=64 time=0.538 ms

--- 10.1.1.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.538/0.994/1.451/0.457 ms
           

我們再次檢視流表, 發現table 20中已經多了一條學習到的流,下次再向該MAC發送資料包,資料将直接從PORT 6中發出:

[root@localhost vagrant]# ovs-ofctl dump-flows br-tun table=20
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=164.902s, table=20, n_packets=36, n_bytes=3360, hard_timeout=300, idle_age=19, hard_age=19, priority=1,dl_dst=ee:2c:09:42:0e:46 actions=load:0->NXM_NX_TUN_ID[],output:6
cookie=0x0, duration=223.811s, table=20, n_packets=1, n_bytes=98, idle_age=164, priorit           

繼續閱讀