(1). tun 与 tap 设备
这两个都是虚拟网络设备,tun 设备用来实现三层隧道(三层 ip 数据报),tap 设备用来实现二层隧道(二层以太网数据帧)。

tun和tap都是虚拟网卡设备,但是:

  • tun是三层设备,其封装的外层是IP头
  • tap是二层设备,其封装的外层是以太网帧(frame)头
  • tun是PPP点对点设备,没有MAC地址
  • tap是以太网设备,有MAC地址
  • tap比tun更接近于物理网卡,可以认为,tap设备等价于去掉了硬件功能的物理网卡

这意味着,如果提供了用户空间的程序去收发tun/tap虚拟网卡的数据,所收发的内容是不同的:

  • 收发tun设备的用户程序,只能间接提供封装和解封数据包的IP头的功能
  • 收发tap设备的用户程序,只能间接提供封装和解封数据包的帧头的功能
  • 注意,此处用词是【收发数据】而非【处理数据】,是【间接提供】而非【直接提供】,因为在不绕过内核网络协议栈的情况下,读写虚拟网卡的用户程序是不能封装和解封数据的,只有内核的网络协议栈才能封装和解封数据。

前面说过,虚拟网卡的两个主要功能是:

  • 连接其它设备(虚拟网卡或物理网卡)和虚拟交换机(bridge)
  • 提供用户空间程序去收发虚拟网卡上的数据
    基于这两个功能,tap设备通常用来连接其它网络设备(它更像网卡),tun设备通常用来结合用户空间程序实现再次封装。换句话说,tap设备通常接入到虚拟交换机(bridge)上作为局域网的一个节点,tun设备通常用来实现三层的ip隧道。

但tun/tap的用法是灵活的,只不过上面两种使用场景更为广泛。例如,除了可以使用tun设备来实现ip层隧道,使用tap设备实现二层隧道的场景也颇为常见。

(2). 物理网卡与虚拟网卡
物理网卡需要通过网卡驱动在内核中注册后才能工作,它在内核网络协议栈和外界网络之间传递数据,用户可以为物理网卡配置网卡接口属性,比如IP地址,这些属性都配置在内核的网络协议栈中。

内核也可以直接创建虚拟的网卡,只要为虚拟网卡提供网卡驱动程序,使其在内核中可以注册成为网卡设备,它就可以工作。

其实,从Linux内核3.x版本开始,物理网卡和虚拟网卡是平等的设备,它们都会在注册时创建net_device数据结构来保存(物理或虚拟)设备信息。

相比于物理网卡负责内核网络协议栈和外界网络之间的数据传输,虚拟网卡的两端则是内核网络协议栈和用户空间,它负责在内核网络协议栈和用户空间的程序之间传递数据:

(3). 虚拟网卡和物理网卡的对比

和物理网卡对比一下,物理网卡是硬件网卡,它位于硬件层,虚拟网卡则可以看作是用户空间的网卡,就像用户空间的文件系统(fuse)一样。

物理网卡和虚拟网卡唯一的不同点在于物理网卡本身的硬件功能:物理网卡以比特流的方式传输数据。

也就是说,内核会公平对待物理网卡和虚拟网卡,物理网卡能做的配置,虚拟网卡也能做。比如可以为虚拟网卡接口配置IP地址、设置子网掩码,可以将虚拟网卡接入网桥等等。

只有在数据流经物理网卡和虚拟网卡的那一刻,才会体现出它们的不同,即传输数据的方式不同:物理网卡以比特流的方式传输数据,虚拟网卡则直接在内存中拷贝数据(即,在内核之间和读写虚拟网卡的程序之间传输)。

正因为虚拟网卡不具备物理网卡以比特流方式传输数据的硬件功能,所以,绝不可能通过虚拟网卡向外界发送数据,外界数据也不可能直接发送到虚拟网卡上。能够直接收发外界数据的,只能是物理设备。

虽然虚拟网卡无法将数据传输到外界网络,但却:

可以将数据传输到本机的另一个网卡(虚拟网卡或物理网卡)或其它虚拟设备(如虚拟交换机)上
可以在用户空间运行一个可读写虚拟网卡的程序,该程序可将流经虚拟网卡的数据包进行处理,这个用户程序就像是物理网卡的硬件功能一样,可以收发数据(可将物理网卡的硬件功能看作是嵌入在网卡上的程序),比如OpenVPN就是这样的工具

一定请注意,用户空间的程序是无法对数据包做任何封装和解封操作的,所有的封装和解封都只能由内核的网络协议栈来完成。

(4). 创建并使用tun/tap设备

创建tun/tap设备时,内核会自动为tun、tap提供网卡驱动程序,使其能正常工作。此外,内核还会为tun、tap提供字符设备驱动,使其能够在用户空间和内核空间传递数据。
其实,tun和tap都是基于/dev/net/tun字符设备所创建(或称为克隆)的虚拟网络设备:

root@LEDE:~# ls -l /dev/net/tun 
crw-------    1 root     root       10, 200 Apr  8 18:20 /dev/net/tun

Linux中创建tun、tap时,要求打开/dev/net/tun设备,打开后会返回一个文件描述符fd,再使用ioctl()在此fd上注册tun或tap设备,注册后将自动创建tunX或tapX设备(这取决于ioctl()中注册的设备类型),其中X是一个从0开始的正整数。创建成功后,在ifconfig等命令中就可以看到tunX或tapX。

在使用ioctl()注册时,还可以指定创建的tun/tap设备是否持久保留,如果不持久保留,那么程序退出或关闭fd,都会自动移除对应的tun/tap设备。比如tunctl、ip、openvpn等工具创建的都是持久化的tun/tap设备,即使这些程序退出了,虚拟网卡设备也仍然保留。

注册tun或tap之后,可使用fd来读写tunX或tapX设备文件。

用户程序读写tunX或tapX设备,即表示虚拟网卡收发数据(在用户空间和内核网络协议栈之间传输数据):

  • 读虚拟网卡表示从虚拟网卡收,即读取来自内核网络协议栈的数据,这些数据是内核决策后决定要从tun/tap设备发送出去的数据,一般是已经被内核封装过的数据
  • 写虚拟网卡表示向虚拟网卡发,即用户空间程序将数据写入网卡tun/tap,内核网络协议栈将收到这些数据并对数据进行解封(就像外界数据经过物理网卡后进入内核协议栈一样)

参考链接:

https://www.junmajinlong.com/virtual/network/all_about_tun_tap/