linux虛擬網絡設備之veth

有了上一篇關於tun/tap的介紹之後,大家應該對虛擬網絡設備有了一定的瞭解,本篇將接着介紹另一種虛擬網絡設備veth。

veth設備的特點

  • veth和其它的網絡設備都一樣,一端連接的是內核協議棧。
  • veth設備是成對出現的,另一端兩個設備彼此相連
  • 一個設備收到協議棧的數據發送請求後,會將數據發送到另一個設備上去。

下面這張關係圖很清楚的說明了veth設備的特點:

+----------------------------------------------------------------+
|                                                                |
|       +------------------------------------------------+       |
|       |             Newwork Protocol Stack             |       |
|       +------------------------------------------------+       |
|              ↑               ↑               ↑                 |
|..............|...............|...............|.................|
|              ↓               ↓               ↓                 |
|        +----------+    +-----------+   +-----------+           |
|        |   eth0   |    |   veth0   |   |   veth1   |           |
|        +----------+    +-----------+   +-----------+           |
|192.168.1.11  ↑               ↑               ↑                 |
|              |               +---------------+                 |
|              |         192.168.2.11     192.168.2.1            |
+--------------|-------------------------------------------------+
               ↓
         Physical Network

上圖中,我們給物理網卡eth0配置的IP爲192.168.1.11, 而veth0和veth1的IP分別是192.168.2.11和192.168.2.1。

示例

我們通過示例的方式來一步一步的看看veth設備的特點。

只給一個veth設備配置IP

先通過ip link命令添加veth0和veth1,然後配置veth0的IP,並將兩個設備都啓動起來

dev@debian:~$ sudo ip link add veth0 type veth peer name veth1
dev@debian:~$ sudo ip addr add 192.168.2.11/24 dev veth0
dev@debian:~$ sudo ip link set veth0 up
dev@debian:~$ sudo ip link set veth1 up

這裏不給veth1設備配置IP的原因就是想看看在veth1沒有IP的情況下,veth0收到協議棧的數據後會不會轉發給veth1。

ping一下192.168.2.1,由於veth1還沒配置IP,所以肯定不通

dev@debian:~$ ping -c 4 192.168.2.1
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
From 192.168.2.11 icmp_seq=1 Destination Host Unreachable
From 192.168.2.11 icmp_seq=2 Destination Host Unreachable
From 192.168.2.11 icmp_seq=3 Destination Host Unreachable
From 192.168.2.11 icmp_seq=4 Destination Host Unreachable

--- 192.168.2.1 ping statistics ---
4 packets transmitted, 0 received, +4 errors, 100% packet loss, time 3015ms
pipe 3

但爲什麼ping不通呢?是到哪一步失敗的呢?

先看看抓包的情況,從下面的輸出可以看出,veth0和veth1收到了同樣的ARP請求包,但沒有看到ARP應答包:

dev@debian:~$ sudo tcpdump -n -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
20:20:18.285230 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:19.282018 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:20.282038 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:21.300320 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:22.298783 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:23.298923 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28

dev@debian:~$ sudo tcpdump -n -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
20:20:48.570459 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:49.570012 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:50.570023 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:51.570023 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:52.569988 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28
20:20:53.570833 ARP, Request who-has 192.168.2.1 tell 192.168.2.11, length 28

爲什麼會這樣呢?瞭解ping背後發生的事情後就明白了:

  1. ping進程構造ICMP echo請求包,並通過socket發給協議棧,
  2. 協議棧根據目的IP地址和系統路由表,知道去192.168.2.1的數據包應該要由192.168.2.11口出去
  3. 由於是第一次訪問192.168.2.1,且目的IP和本地IP在同一個網段,所以協議棧會先發送ARP出去,詢問192.168.2.1的mac地址
  4. 協議棧將ARP包交給veth0,讓它發出去 由於veth0的另一端連的是veth1,所以ARP請求包就轉發給了veth1
  5. veth1收到ARP包後,轉交給另一端的協議棧
  6. 協議棧一看自己的設備列表,發現本地沒有192.168.2.1這個IP,於是就丟棄了該ARP請求包,這就是爲什麼只能看到ARP請求包,看不到應答包的原因

給兩個veth設備都配置IP

給veth1也配置上IP

dev@debian:~$ sudo ip addr add 192.168.2.1/24 dev veth1

再ping 192.168.2.1成功(由於192.168.2.1是本地IP,所以默認會走lo設備,爲了避免這種情況,這裏使用ping命令帶上了-I參數,指定數據包走指定設備)

dev@debian:~$ ping -c 4 192.168.2.1 -I veth0
PING 192.168.2.1 (192.168.2.1) from 192.168.2.11 veth0: 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=0.032 ms
64 bytes from 192.168.2.1: icmp_seq=2 ttl=64 time=0.048 ms
64 bytes from 192.168.2.1: icmp_seq=3 ttl=64 time=0.055 ms
64 bytes from 192.168.2.1: icmp_seq=4 ttl=64 time=0.050 ms

--- 192.168.2.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 0.032/0.046/0.055/0.009 ms

注意:對於非debian系統,這裏有可能ping不通,主要是因爲內核中的一些ARP相關配置導致veth1不返回ARP應答包,如ubuntu上就會出現這種情況,解決辦法如下:

root@ubuntu:~# echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
root@ubuntu:~# echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
root@ubuntu:~# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
root@ubuntu:~# echo 0 > /proc/sys/net/ipv4/conf/veth0/rp_filter
root@ubuntu:~# echo 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter

再來看看抓包情況,我們在veth0和veth1上都看到了ICMP echo的請求包,但爲什麼沒有應答包呢?上面不是顯示ping進程已經成功收到了應答包嗎?

dev@debian:~$ sudo tcpdump -n -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
20:23:43.113062 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24169, seq 1, length 64
20:23:44.112078 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24169, seq 2, length 64
20:23:45.111091 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24169, seq 3, length 64
20:23:46.110082 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24169, seq 4, length 64


dev@debian:~$ sudo tcpdump -n -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
20:24:12.221372 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24174, seq 1, length 64
20:24:13.222089 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24174, seq 2, length 64
20:24:14.224836 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24174, seq 3, length 64
20:24:15.223826 IP 192.168.2.11 > 192.168.2.1: ICMP echo request, id 24174, seq 4, length 64

看看數據包的流程就明白了:

  1. ping進程構造ICMP echo請求包,並通過socket發給協議棧,
  2. 由於ping程序指定了走veth0,並且本地ARP緩存裏面已經有了相關記錄,所以不用再發送ARP出去,協議棧就直接將該數據包交給了veth0
  3. 由於veth0的另一端連的是veth1,所以ICMP echo請求包就轉發給了veth1
  4. veth1收到ICMP echo請求包後,轉交給另一端的協議棧
  5. 協議棧一看自己的設備列表,發現本地有192.168.2.1這個IP,於是構造ICMP echo應答包,準備返回
  6. 協議棧查看自己的路由表,發現回給192.168.2.11的數據包應該走lo口,於是將應答包交給lo設備
  7. lo接到協議棧的應答包後,啥都沒幹,轉手又把數據包還給了協議棧(相當於協議棧通過發送流程把數據包給lo,然後lo再將數據包交給協議棧的接收流程)
  8. 協議棧收到應答包後,發現有socket需要該包,於是交給了相應的socket
  9. 這個socket正好是ping進程創建的socket,於是ping進程收到了應答包

抓一下lo設備上的數據,發現應答包確實是從lo口回來的:

dev@debian:~$ sudo tcpdump -n -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
20:25:49.590273 IP 192.168.2.1 > 192.168.2.11: ICMP echo reply, id 24177, seq 1, length 64
20:25:50.590018 IP 192.168.2.1 > 192.168.2.11: ICMP echo reply, id 24177, seq 2, length 64
20:25:51.590027 IP 192.168.2.1 > 192.168.2.11: ICMP echo reply, id 24177, seq 3, length 64
20:25:52.590030 IP 192.168.2.1 > 192.168.2.11: ICMP echo reply, id 24177, seq 4, length 64

試着ping下其它的IP

ping 192.168.2.0/24網段的其它IP失敗,ping一個公網的IP也失敗:

dev@debian:~$ ping -c 1 -I veth0 192.168.2.2
PING 192.168.2.2 (192.168.2.2) from 192.168.2.11 veth0: 56(84) bytes of data.
From 192.168.2.11 icmp_seq=1 Destination Host Unreachable

--- 192.168.2.2 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

dev@debian:~$ ping -c 1 -I veth0 baidu.com
PING baidu.com (111.13.101.208) from 192.168.2.11 veth0: 56(84) bytes of data.
From 192.168.2.11 icmp_seq=1 Destination Host Unreachable

--- baidu.com ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

從抓包來看,和上面第一種veth1沒有配置IP的情況是一樣的,ARP請求沒人處理

dev@debian:~$ sudo tcpdump -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
02:25:23.223947 ARP, Request who-has 192.168.2.2 tell 192.168.2.11, length 28
02:25:24.224352 ARP, Request who-has 192.168.2.2 tell 192.168.2.11, length 28
02:25:25.223471 ARP, Request who-has 192.168.2.2 tell 192.168.2.11, length 28
02:25:27.946539 ARP, Request who-has 123.125.114.144 tell 192.168.2.11, length 28
02:25:28.946633 ARP, Request who-has 123.125.114.144 tell 192.168.2.11, length 28
02:25:29.948055 ARP, Request who-has 123.125.114.144 tell 192.168.2.11, length 28

結束語

從上面的介紹中可以看出,從veth0設備出去的數據包,會轉發到veth1上,如果目的地址是veth1的IP的話,就能被協議棧處理,否則連ARP那關都過不了,IP forward啥的都用不上,所以不借助其它虛擬設備的話,這樣的數據包只能在本地協議棧裏面打轉轉,沒法走到eth0上去,即沒法發送到外面的網絡中去。

原文鏈接:https://segmentfault.com/a/1190000009251098

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章