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++编程技术等内容,欢迎你们关注。

相关文章
相关标签/搜索