Linux 虛擬網絡設備 veth-pair 詳解,看這一篇就夠了

本文首發於個人公衆號 Linux雲計算網絡(id: cloud_dev),專一於乾貨分享,號內有 10T 書籍和視頻資源,後臺回覆「1024」便可領取,歡迎你們關注,二維碼文末能夠掃。編程

前面這篇文章介紹了 tap/tun 設備以後,你們應該對虛擬網絡設備有了必定的瞭解,本文來看另一種虛擬網絡設備 veth-pair。ubuntu

01 veth-pair 是什麼

顧名思義,veth-pair 就是一對的虛擬設備接口,和 tap/tun 設備不一樣的是,它都是成對出現的。一端連着協議棧,一端彼此相連着。以下圖所示:segmentfault

正由於有這個特性,它經常充當着一個橋樑,鏈接着各類虛擬網絡設備,典型的例子像「兩個 namespace 之間的鏈接」,「Bridge、OVS 之間的鏈接」,「Docker 容器之間的鏈接」 等等,以此構建出很是複雜的虛擬網絡結構,好比 OpenStack Neutron。網絡

02 veth-pair 的連通性

咱們給上圖中的 veth0 和 veth1 分別配上 IP:10.1.1.2 和 10.1.1.3,而後從 veth0 ping 一下 veth1。理論上它們處於同網段,是能 ping 通的,但結果倒是 ping 不通。socket

抓個包看看,tcpdump -nnt -i veth0tcp

root@ubuntu:~# tcpdump -nnt -i veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28
ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28

能夠看到,因爲 veth0 和 veth1 處於同一個網段,且是第一次鏈接,因此會事先發 ARP 包,但 veth1 並無響應 ARP 包。工具

經查閱,這是因爲我使用的 Ubuntu 系統內核中一些 ARP 相關的默認配置限制所致使的,須要修改一下配置項:測試

echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter

完了再 ping 就好了。雲計算

root@ubuntu:~# ping -I veth0 10.1.1.3 -c 2
PING 10.1.1.3 (10.1.1.3) from 10.1.1.2 veth0: 56(84) bytes of data.
64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.064 ms

--- 10.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3008ms
rtt min/avg/max/mdev = 0.047/0.072/0.113/0.025 ms

咱們對這個通訊過程比較感興趣,能夠抓包看看。spa

對於 veth0 口:

root@ubuntu:~# tcpdump -nnt -i veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.3 is-at 5a:07:76:8e:fb:cd, length 28
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 1, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 2, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 3, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2244, seq 1, length 64

對於 veth1 口:

root@ubuntu:~# tcpdump -nnt -i veth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.3 is-at 5a:07:76:8e:fb:cd, length 28
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 1, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 2, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 3, length 64
IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2244, seq 1, length 64

奇怪,咱們並無看到 ICMP 的 echo reply 包,那它是怎麼 ping 通的?

其實這裏 echo reply 走的是 localback 口,不信抓個包看看:

root@ubuntu:~# tcpdump -nnt -i lo
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 1, length 64
IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 2, length 64
IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 3, length 64
IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 4, length 64

爲何?

咱們看下整個通訊流程就明白了。

  1. 首先 ping 程序構造 ICMP echo request,經過 socket 發給協議棧。
  2. 因爲 ping 指定了走 veth0 口,若是是第一次,則須要發 ARP 請求,不然協議棧直接將數據包交給 veth0。
  3. 因爲 veth0 連着 veth1,因此 ICMP request 直接發給 veth1。
  4. veth1 收到請求後,交給另外一端的協議棧。
  5. 協議棧看本地有 10.1.1.3 這個 IP,因而構造 ICMP reply 包,查看路由表,發現回給 10.1.1.0 網段的數據包應該走 localback 口,因而將 reply 包交給 lo 口(會優先查看路由表的 0 號表,ip route show table 0 查看)。
  6. lo 收到協議棧的 reply 包後,啥都沒幹,轉手又回給協議棧。
  7. 協議棧收到 reply 包以後,發現有 socket 在等待包,因而將包給 socket。
  8. 等待在用戶態的 ping 程序發現 socket 返回,因而就收到 ICMP 的 reply 包。

整個過程以下圖所示:

03 兩個 namespace 之間的連通性

namespace 是 Linux 2.6.x 內核版本以後支持的特性,主要用於資源的隔離。有了 namespace,一個 Linux 系統就能夠抽象出多個網絡子系統,各子系統間都有本身的網絡設備,協議棧等,彼此之間互不影響。

若是各個 namespace 之間須要通訊,怎麼辦呢,答案就是用 veth-pair 來作橋樑。

根據鏈接的方式和規模,能夠分爲「直接相連」,「經過 Bridge 相連」 和 「經過 OVS 相連」。

3.1 直接相連

直接相連是最簡單的方式,以下圖,一對 veth-pair 直接將兩個 namespace 鏈接在一塊兒。

給 veth-pair 配置 IP,測試連通性:

# 建立 namespace
ip netns a ns1
ip netns a ns2

# 建立一對 veth-pair veth0 veth1
ip l a veth0 type veth peer name veth1

# 將 veth0 veth1 分別加入兩個 ns
ip l s veth0 netns ns1
ip l s veth1 netns ns2

# 給兩個 veth0 veth1 配上 IP 並啓用
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
ip netns exec ns1 ip l s veth0 up
ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
ip netns exec ns2 ip l s veth1 up

# 從 veth0 ping veth1
[root@localhost ~]# ip netns exec ns1 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=0.073 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.068 ms

--- 10.1.1.3 ping statistics ---
15 packets transmitted, 15 received, 0% packet loss, time 14000ms
rtt min/avg/max/mdev = 0.068/0.084/0.201/0.032 ms

3.2 經過 Bridge 相連

Linux Bridge 至關於一臺交換機,能夠中轉兩個 namespace 的流量,咱們看看 veth-pair 在其中扮演什麼角色。

以下圖,兩對 veth-pair 分別將兩個 namespace 連到 Bridge 上。

一樣給 veth-pair 配置 IP,測試其連通性:

# 首先建立 bridge br0
ip l a br0 type bridge
ip l s br0 up 

# 而後建立兩對 veth-pair
ip l a veth0 type veth peer name br-veth0
ip l a veth1 type veth peer name br-veth1

# 分別將兩對 veth-pair 加入兩個 ns 和 br0
ip l s veth0 netns ns1
ip l s br-veth0 master br0
ip l s br-veth0 up

ip l s veth1 netns ns2
ip l s br-veth1 master br0
ip l s br-veth1 up

# 給兩個 ns 中的 veth 配置 IP 並啓用
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
ip netns exec ns1 ip l s veth0 up

ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
ip netns exec ns2 ip l s veth1 up

# veth0 ping veth1
[root@localhost ~]# ip netns exec ns1 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=0.060 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.105 ms

--- 10.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.060/0.082/0.105/0.024 ms

3.3 經過 OVS 相連

OVS 是第三方開源的 Bridge,功能比 Linux Bridge 要更強大,對於一樣的實驗,咱們用 OVS 來看看是什麼效果。

以下圖所示:

一樣測試兩個 namespace 之間的連通性:

# 用 ovs 提供的命令建立一個 ovs bridge
ovs-vsctl add-br ovs-br

# 建立兩對 veth-pair
ip l a veth0 type veth peer name ovs-veth0
ip l a veth1 type veth peer name ovs-veth1

# 將 veth-pair 兩端分別加入到 ns 和 ovs bridge 中
ip l s veth0 netns ns1
ovs-vsctl add-port ovs-br ovs-veth0
ip l s ovs-veth0 up

ip l s veth1 netns ns2
ovs-vsctl add-port ovs-br ovs-veth1
ip l s ovs-veth1 up

# 給 ns 中的 veth 配置 IP 並啓用
ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0
ip netns exec ns1 ip l s veth0 up

ip netns exec ns2 ip a a 10.1.1.3/24 dev veth1
ip netns exec ns2 ip l s veth1 up

# veth0 ping veth1
[root@localhost ~]# ip netns exec ns1 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=0.311 ms
64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.087 ms
^C
--- 10.1.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.087/0.199/0.311/0.112 ms

總結

veth-pair 在虛擬網絡中充當着橋樑的角色,鏈接多種網絡設備構成複雜的網絡。

veth-pair 的三個經典實驗,直接相連、經過 Bridge 相連和經過 OVS 相連。

參考

http://www.opencloudblog.com/?p=66

https://segmentfault.com/a/1190000009251098


個人公衆號 「Linux雲計算網絡」(id: cloud_dev) ,號內有 10T 書籍和視頻資源,後臺回覆 「1024」 便可領取,分享的內容包括但不限於 Linux、網絡、雲計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++編程技術等內容,歡迎你們關注。

相關文章
相關標籤/搜索