Linux网络虚拟化

Veth Pair

veth pair就是一对的虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着。其基本工作原理如下图所示: 虚拟网卡对原理图

因此,它常常充当着一个桥梁,连接着各种虚拟网络设备。典型的例子像“两个network namespace之间的连接”和“Docker 容器之间的连接” 等等

创建Veth Pair

新创建的Veth Pair设备的默认MTU是1500,设备初始状态是DOWN

# 创建veth pair, 名字分别是veth0和veth1
ip link add veth0 type veth peer name veth1

启动Veth Pair并分配IP

# 分配IP
ip addr add 10.1.1.2/24 dev veth0
ip addr add 10.1.1.1/24 dev veth1
# 启动Veth Pair
ip link set dev veth0 up
ip link set dev veth1 up

Linux bridge

网桥是二层网络设备,两个端口分别有一条独立的交换信道,不共享一条背板主线

使用iproute2包中的ip名称创建bridge,也可以使用bridge-utils软件包中的brctl工具管理网桥

# 创建网桥
# 刚创建的网桥,它是一个独立的网络设备,只有一个端口连接着协议栈,这时的bridge没有任何实际功能
ip link add name br0 type bridge
ip link set br0 up

连接Veth Pair到网桥

ip link set dev veth0 master br0

连接Veth Pair到网桥

连接后的网络拓扑如下所示 网络拓扑图

br0veth0相连之后的变化如下: - br0veth0之间连接,并且是双向通道 - 协议栈和veth0之间变成了单通道. 协议栈可以发送数据到veth0, 但是veth0从外面收到的数据不会转发给协议栈

# 无法从veth0 ping通veth1
root@lima-k8s-network:~# ping -c 1 -I veth0 10.1.1.1
PING 10.1.1.1 (10.1.1.1) from 10.1.1.2 veth0: 56(84) bytes of data.
From 10.1.1.2 icmp_seq=1 Destination Host Unreachable

--- 10.1.1.1 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
# 抓包veth0,收到ARP(Request)包和应答(Reply)包
root@lima-k8s-network:~# tcpdump -nnt -i veth0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28

# 抓包veth1,收到ARP(Request)包和应答(Reply)包
root@lima-k8s-network:~# tcpdump -nnt -i veth1
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Request who-has 10.1.1.1 tell 10.1.1.2, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28

# 抓包br0,仅应答(Reply)包
root@lima-k8s-network:~# tcpdump -nnt -i br0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28
ARP, Reply 10.1.1.1 is-at 4a:83:66:06:47:88, length 28

把IP让给Bridge

通过以上分析,给veth0分配IP没有意义. 因为就算协议栈传数据包给veth0,回程报文也回不来

ip addr del 10.1.1.2/24 dev veth0
ip addr add 10.1.1.2/24 dev br0

绑定IP地址后的网络拓扑如下所示: 网络拓扑图

root@lima-k8s-network:~# ping -c 1 -I br0 10.1.1.1
PING 10.1.1.1 (10.1.1.1) from 10.1.1.2 br0: 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.035 ms

--- 10.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.035/0.035/0.035/0.000 ms

Linux Bridge在容器中的应用: Linux Bridge在容器中的应用

TUN/TAP设备

虚拟的点对点设备. tun/tap设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会

tun/tap设备到底是什么

  • 从Linux文件系统的角度看,它是用户可以用文件句柄操作的字符设备
  • 从网络虚拟化角度看,它是虚拟网卡,一端连着网络协议栈,另一端连着用户态

tun/tap设备的作用

tun/tap设备可以将TCP/IP协议栈处理好的网络包发送给任何一个使用tun/tap驱动的进程,由进程重新处理后发到物理链路中。

tun/tap设备的基本原理 tun/tap基本原理

tun设备的工作模式

普通的物理网卡通过网线收发数据包,而tun设备通过一个设备文件(/dev/tunX)收发数据包。所有对这个文件的写操作会通过tun设备转换成一个数据包传送给内核网络协议栈。当内核发送一个包给tun设备时,用户态的进程通过读取这个文件可以拿到包的内容 tun设备的工作模式

tap设备与tun设备的区别

tap和tun设备的工作原理完全相同

  • tun设备的/dev/tunX文件收发的是IP包,因此只能工作在L3,无法与物理网卡做桥接,但可以通过三层交换与物理网卡连通
  • tap设备的/dev/tapX文件收发的是链路层数据包,可以与物理网卡做桥接

使用tun设备搭建一个基于UDP的VPN

使用tun设备搭建一个基于UDP的VPN

  • App1是一个普通的程序,通过Socket API发送了一个数据包,假设这个数据包的目的IP地址是192.168.1.2(和tun0在同一个网段)
  • 程序A的数据包到达网络协议栈后,协议栈根据数据包的目的IP地址匹配到这个数据包应该由tun0网口出去,于是将数据包发送给tun0网卡
  • tun0网卡收到数据包之后,发现网卡的另一端被App2打开了(这也是tun/tap设备的特点,一端连着协议栈,另一端连着用户态程序),于是将数据包发送给App2
  • App2收到数据包之后,通过报文封装(将原来的数据包封装在新的数据报文中,假设新报文的原地址是eth0的地址,目的地址是和eth0在同一个网段的VPN对端IP地址),构造出一个新的数据包. App2通过同样的Socket API将数据包发送给协议栈 协议栈根据本地路由,发现这个数据包应该通过eth0发送出去,于是将数据包交给eth0,最后eth0通过物理网络将数据包发送给VPN的对端