Linux 虚拟网络设备 veth-pair 详解,看这一篇就够了

本文首发于我的公众号 cloud_dev,专注于干货分享,号内有大量书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。前面这篇文章介绍了 tap/tun 设备之后,大家应该对虚拟网络设备有了一定的了解,本文来看另外一种虚拟网络设备 veth-pair。01 veth-pair 是什么顾名思义,veth-pair 就是一对的虚拟设备接口,和 tap/tun 设备不同的是,它都是成对出现的。一端连着协议栈,一端彼此相连着。如下图所示:正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备,典型的例子像“两个 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 不通。抓个包看看,tcpdump -nnt -i veth0root@ubuntu:# tcpdump -nnt -i veth0tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytesARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28ARP, 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_localecho 1 > /proc/sys/net/ipv4/conf/veth0/accept_localecho 0 > /proc/sys/net/ipv4/conf/all/rp_filterecho 0 > /proc/sys/net/ipv4/conf/veth0/rp_filterecho 0 > /proc/sys/net/ipv4/conf/veth1/rp_filter完了再 ping 就行了。root@ubuntu:# ping -I veth0 10.1.1.3 -c 2PING 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 ms64 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 3008msrtt min/avg/max/mdev = 0.047/0.072/0.113/0.025 ms我们对这个通信过程比较感兴趣,可以抓包看看。对于 veth0 口:root@ubuntu:# tcpdump -nnt -i veth0tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytesARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28ARP, Reply 10.1.1.3 is-at 5a:07:76:8e:fb:cd, length 28IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 1, length 64IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 2, length 64IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 3, length 64IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2244, seq 1, length 64对于 veth1 口:root@ubuntu:# tcpdump -nnt -i veth1tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytesARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 28ARP, Reply 10.1.1.3 is-at 5a:07:76:8e:fb:cd, length 28IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 1, length 64IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 2, length 64IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 2189, seq 3, length 64IP 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 lotcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on lo, link-type EN10MB (Ethernet), capture size 262144 bytesIP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 1, length 64IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 2, length 64IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 3, length 64IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 2244, seq 4, length 64为什么?我们看下整个通信流程就明白了。首先 ping 程序构造 ICMP echo request,通过 socket 发给协议栈。由于 ping 指定了走 veth0 口,如果是第一次,则需要发 ARP 请求,否则协议栈直接将数据包交给 veth0。由于 veth0 连着 veth1,所以 ICMP request 直接发给 veth1。veth1 收到请求后,交给另一端的协议栈。协议栈看本地有 10.1.1.3 这个 IP,于是构造 ICMP reply 包,查看路由表,发现回给 10.1.1.0 网段的数据包应该走 localback 口,于是将 reply 包交给 lo 口(会优先查看路由表的 0 号表,ip route show table 0 查看)。lo 收到协议栈的 reply 包后,啥都没干,转手又回给协议栈。协议栈收到 reply 包之后,发现有 socket 在等待包,于是将包给 socket。等待在用户态的 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,测试连通性:# 创建 namespaceip netns a ns1ip netns a ns2# 创建一对 veth-pair veth0 veth1ip l a veth0 type veth peer name veth1# 将 veth0 veth1 分别加入两个 nsip l s veth0 netns ns1ip l s veth1 netns ns2# 给两个 veth0 veth1 配上 IP 并启用ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0ip netns exec ns1 ip l s veth0 upip netns exec ns2 ip a a 10.1.1.3/24 dev veth1ip netns exec ns2 ip l s veth1 up# 从 veth0 ping veth1[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3PING 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 ms64 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 14000msrtt min/avg/max/mdev = 0.068/0.084/0.201/0.032 ms3.2 通过 Bridge 相连Linux Bridge 相当于一台交换机,可以中转两个 namespace 的流量,我们看看 veth-pair 在其中扮演什么角色。如下图,两对 veth-pair 分别将两个 namespace 连到 Bridge 上。同样给 veth-pair 配置 IP,测试其连通性:# 首先创建 bridge br0ip l a br0 type bridgeip l s br0 up # 然后创建两对 veth-pairip l a veth0 type veth peer name br-veth0ip l a veth1 type veth peer name br-veth1# 分别将两对 veth-pair 加入两个 ns 和 br0ip l s veth0 netns ns1ip l s br-veth0 master br0ip l s br-veth0 upip l s veth1 netns ns2ip l s br-veth1 master br0ip l s br-veth1 up# 给两个 ns 中的 veth 配置 IP 并启用ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0ip netns exec ns1 ip l s veth0 upip netns exec ns2 ip a a 10.1.1.3/24 dev veth1ip netns exec ns2 ip l s veth1 up# veth0 ping veth1[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3PING 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 ms64 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 999msrtt min/avg/max/mdev = 0.060/0.082/0.105/0.024 ms3.3 通过 OVS 相连OVS 是第三方开源的 Bridge,功能比 Linux Bridge 要更强大,对于同样的实验,我们用 OVS 来看看是什么效果。如下图所示:同样测试两个 namespace 之间的连通性:# 用 ovs 提供的命令创建一个 ovs bridgeovs-vsctl add-br ovs-br# 创建两对 veth-pairip l a veth0 type veth peer name ovs-veth0ip l a veth1 type veth peer name ovs-veth1# 将 veth-pair 两端分别加入到 ns 和 ovs bridge 中ip l s veth0 netns ns1ovs-vsctl add-port ovs-br ovs-veth0ip l s ovs-veth0 upip l s veth1 netns ns2ovs-vsctl add-port ovs-br ovs-veth1ip l s ovs-veth1 up# 给 ns 中的 veth 配置 IP 并启用ip netns exec ns1 ip a a 10.1.1.2/24 dev veth0ip netns exec ns1 ip l s veth0 upip netns exec ns2 ip a a 10.1.1.3/24 dev veth1ip netns exec ns2 ip l s veth1 up# veth0 ping veth1[root@localhost ~]# ip netns exec ns1 ping 10.1.1.3PING 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 ms64 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 999msrtt min/avg/max/mdev = 0.087/0.199/0.311/0.112 ms总结veth-pair 在虚拟网络中充当着桥梁的角色,连接多种网络设备构成复杂的网络。veth-pair 的三个经典实验,直接相连、通过 Bridge 相连和通过 OVS 相连。参考http://www.opencloudblog.com/…https://segmentfault.com/a/11…我的公众号 cloud_dev,号内有大量书籍和视频资源,后台回复「1024」即可领取,分享的内容包括但不限于云计算虚拟化、容器、OpenStack、K8S、雾计算、网络、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++编程技术等内容,欢迎大家关注。 ...

April 1, 2019 · 5 min · jiezi

一文总结虚拟网络设备 eth, tap/tun, veth-pair(多图)

本文首发于我的公众号 CloudDeveloper(ID: cloud_dev),专注于干货分享,号内有大量书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。本文翻译自:https://www.fir3net.com/Netwo…Linux 虚拟网络的背后都是由一个个的虚拟设备构成的。虚拟化技术没出现之前,计算机网络系统都只包含物理的网卡设备,通过网卡适配器,线缆介质,连接外部网络,构成庞大的 Internet。然而,随着虚拟化技术的出现,网络也随之被虚拟化,相较于单一的物理网络,虚拟网络变得非常复杂,在一个主机系统里面,需要实现诸如交换、路由、隧道、隔离、聚合等多种网络功能。而实现这些功能的基本元素就是虚拟的网络设备,比如 tap、tun 和 veth-pair。tap/tuntap/tun 提供了一台主机内用户空间的数据传输机制。它虚拟了一套网络接口,这套接口和物理的接口无任何区别,可以配置 IP,可以路由流量,不同的是,它的流量只在主机内流通。tap/tun 有些许的不同,tun 只操作三层的 IP 包,而 tap 操作二层的以太网帧。veth-pairveth-pair 是成对出现的一种虚拟网络设备,一端连接着协议栈,一端连接着彼此,数据从一端出,从另一端进。它的这个特性常常用来连接不同的虚拟网络组件,构建大规模的虚拟网络拓扑,比如连接 Linux Bridge、OVS、LXC 容器等。一个很常见的案例就是它被用于 OpenStack Neutron,构建非常复杂的网络形态。总结最后,总结一下,我们提到几种网络设备,eth0、tap、tun、veth-pair,这些都构成了如今云网络必不可少的元素。我的公众号 CloudDeveloper(ID: cloud_dev),号内有大量书籍和视频资源,后台回复「1024」即可领取,分享的内容包括但不限于云计算虚拟化、容器、OpenStack、K8S、雾计算、网络、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++编程技术等内容,欢迎大家关注。

March 8, 2019 · 1 min · jiezi

用 tap/tun 做虚拟机的网卡

本文首发于我的公众号 CloudDeveloper(ID: cloud_dev),专注于干货分享,号内有大量书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。在云计算时代,虚拟机和容器已经成为标配。它们背后的网络管理都离不开一样东西,就是虚拟网络设备,或者叫虚拟网卡,tap/tun 就是在云计算时代非常重要的虚拟网络网卡。tap/tun 是什么tap/tun 是 Linux 内核 2.4.x 版本之后实现的虚拟网络设备,不同于物理网卡靠硬件网路板卡实现,tap/tun 虚拟网卡完全由软件来实现,功能和硬件实现完全没有差别,它们都属于网络设备,都可以配置 IP,都归 Linux 网络设备管理模块统一管理。作为网络设备,tap/tun 也需要配套相应的驱动程序才能工作。tap/tun 驱动程序包括两个部分,一个是字符设备驱动,一个是网卡驱动。这两部分驱动程序分工不太一样,字符驱动负责数据包在内核空间和用户空间的传送,网卡驱动负责数据包在 TCP/IP 网络协议栈上的传输和处理。用户空间与内核空间的数据传输在 Linux 中,用户空间和内核空间的数据传输有多种方式,字符设备就是其中的一种。tap/tun 通过驱动程序和一个与之关联的字符设备,来实现用户空间和内核空间的通信接口。在 Linux 内核 2.6.x 之后的版本中,tap/tun 对应的字符设备文件分别为:tap:/dev/tap0tun:/dev/net/tun设备文件即充当了用户空间和内核空间通信的接口。当应用程序打开设备文件时,驱动程序就会创建并注册相应的虚拟设备接口,一般以 tunX 或 tapX 命名。当应用程序关闭文件时,驱动也会自动删除 tunX 和 tapX 设备,还会删除已经建立起来的路由等信息。tap/tun 设备文件就像一个管道,一端连接着用户空间,一端连接着内核空间。当用户程序向文件 /dev/net/tun 或 /dev/tap0 写数据时,内核就可以从对应的 tunX 或 tapX 接口读到数据,反之,内核可以通过相反的方式向用户程序发送数据。tap/tun 和网络协议栈的数据传输tap/tun 通过实现相应的网卡驱动程序来和网络协议栈通信。一般的流程和物理网卡和协议栈的交互流程是一样的,不同的是物理网卡一端是连接物理网络,而 tap/tun 虚拟网卡一般连接到用户空间。如下图的示意图,我们有两个应用程序 A、B,物理网卡 eth0 和虚拟网卡 tun0 分别配置 IP:10.1.1.11 和 192.168.1.11,程序 A 希望构造数据包发往 192.168.1.0/24 网段的主机 192.168.1.1。基于上图,我们看看数据包的流程:应用程序 A 构造数据包,目的 IP 是 192.168.1.1,通过 socket A 将这个数据包发给协议栈。协议栈根据数据包的目的 IP 地址,匹配路由规则,发现要从 tun0 出去。tun0 发现自己的另一端被应用程序 B 打开了,于是将数据发给程序 B.程序 B 收到数据后,做一些跟业务相关的操作,然后构造一个新的数据包,源 IP 是 eth0 的 IP,目的 IP 是 10.1.1.0/24 的网关 10.1.1.1,封装原来的数据的数据包,重新发给协议栈。协议栈再根据本地路由,将这个数据包从 eth0 发出。后续步骤,当 10.1.1.1 收到数据包后,会进行解封装,读取里面的原始数据包,继而转发给本地的主机 192.168.1.1。当接收回包时,也遵循同样的流程。在这个流程中,应用程序 B 的作用其实是利用 tun0 对数据包做了一层隧道封装。其实 tun 设备的最大用途就是用于隧道通信的。tap/tun 的区别看到这里,你可能还不大明白 tap/tun 的区别。tap 和 tun 虽然都是虚拟网络设备,但它们的工作层次还不太一样。tap 是一个二层设备(或者以太网设备),只能处理二层的以太网帧;tun 是一个点对点的三层设备(或网络层设备),只能处理三层的 IP 数据包。tap/tun 的应用从上面的数据流程中可以看到,tun 设备充当了一层隧道,所以,tap/tun 最常见的应用也就是用于隧道通信,比如 VPN,包括 tunnel 和应用层的 IPsec 等,其中比较有名的两个开源项目是 openvpn 和 VTun。总结tun/tap 虚拟网卡,对应于物理网卡,如 eth0。tun/tap 驱动包括字符设备驱动和网卡驱动。tun/tap 常用于隧道通信。我的公众号 CloudDeveloper(ID: cloud_dev),号内有大量书籍和视频资源,后台回复「1024」即可领取,分享的内容包括但不限于云计算虚拟化、容器、OpenStack、K8S、雾计算、网络、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++编程技术等内容,欢迎大家关注。 ...

February 28, 2019 · 1 min · jiezi