在现在的云时代,虚拟机和容器无处不在,其背后的网络管理离不开虚拟网络设备,所以了解虚拟网络设备有利于我们更好的理解网络结构在云时代。从本文开始,我们将介绍Linux下的虚拟网络设备。虚拟设备和物理设备的区别在Linux网络数据包的接收过程和数据包的发送过程两篇文章中,介绍了数据包的发送和接收过程。众所周知,Linux内核中有一个网络设备管理层,它位于网络设备驱动程序和协议栈之间,负责桥接它们之间的数据交互。驱动程序不需要知道协议栈的细节,协议栈也不需要知道设备驱动程序的细节。对于网络设备来说,就像管道(pipe)一样,有两端,从任何一端接收到的数据都会从另一端发送出去。例如物理网卡eth0,它的两端分别是内核协议栈(通过内核网络设备管理模块间接通信)和外部物理网络,从物理网络接收到的数据会转发给内核协议栈,而应用程序从协议栈发出的数据,会通过物理网络发送出去。那么虚拟网络设备呢?首先,它也是由内核的网络设备管理子系统管理的。对于Linux内核网络设备管理模块,虚拟设备和物理设备没有区别。它们都是网络设备,可以配置IP。来自网络设备的数据将被转发。到协议栈,来自协议栈的数据也会由网络设备发送。至于怎么发送,发送到哪里,那是设备驱动的事情,和Linux内核无关,所以说虚拟网络设备协议栈的一端也是协议栈,而另一端是什么取决于虚拟网络设备的驱动程序实现。tun/tap的另一端是什么?说话先看图:+-------------------------------------------------------------+|||+--------------------++--------------------+|||用户应用程序A||用户应用程序B|<-----+||+------------------------++----------------+||||1|5|||......................|......................................|..................................|.......||↓↓||+---------++---------+||||插座A||插座B||||+---------++---------+||||2|6|||...............|.......|.......|.......||↓↓|||+------------------------+4||||网络协议栈||||+--------------------------+||||7|3|||................|....................|.............................|.......||↓↓|||+--------------++----------------+||||eth0||tun0||||+------------------++----------------+|||10.32.0.11||192.168.3.11||||8+----------------------+||||+----------------|--------------------------------------------+↓PhysicalNetwork上图中有两个应用程序A和B,都在用户层,而其他套接字、协议栈(NewworkProtocolStack)和网络设备(eth0和tun0)部分在内核层。实际上,套接字是协议栈的一部分。这里把它分开的目的是为了更直观。tun0是一个Tun/Tap虚拟设备。从上图我们可以看出它和物理设备eth0的区别,虽然它们的一端连接的是协议栈,但是另一端就不一样了,eth0的另一端是一个物理网络,这个物理网络可能是交换机,tun0的另一端是用户层程序,协议栈发送给tun0的数据包可以被这个应用程序读取,应用程序可以直接向tun0写入数据。这里假设eth0配置的IP为10.32.0.11,tun0配置的IP为192.168.3.11。这里是一个tun/tap设备的典型应用场景,通过程序B向192.168.3.0/24网络发送数据,隧道使用10.32.0.11发送到远程网络的10.33.0.1,然后10.33.0.1转发给相应的设备,从而实现VPN。我们看一下数据包的流程:应用程序A是一个普通程序,通过socketA发送一个数据包,假设数据包的目的IP地址为192.168.3.1socket将数据包丢给协议栈根据数据包的目的IP地址,匹配本地的路由规则,知道数据包应该从tun0出去,所以把数据包交给tun0。tun0收到数据包后发现另一端被进程B打开,于是将数据包丢给进程B进程B收到数据包后做一些业务相关的处理,然后构造一个新的数据包,嵌入新数据包中的原始数据包,最后通过socketB转发数据包。此时新数据包的源地址变成了eth0的地址,目的IP地址变成了另外一个地址,比如10.33.0.1.socketB将数据包丢到协议栈。协议栈根据本地路由发现这个数据包应该是通过eth0发送出去的,于是将数据包交给eth0eth0通过物理网络发送数据包10.33.0.1收到数据包后,会打开数据包,读取里面的原始数据包,转发给本地192.168.3.1,收到192.168.3.1的响应后,构造一个新的响应包,将原来的响应包封装在里面,然后返回通过原路径到应用B,应用B取出包内的原始响应,最后返回给应用A。这里不讨论Tun/Tap设备tun0如何与用户级进程B通信。对于Linux内核,有很多方法可以让内核空间和用户空间的进程交换数据。从上面的过程可以看出,数据包选择走哪个网络设备完全是由路由表控制的,所以如果我们想让部分网络流量经过应用B的转发过程,就需要对路由表进行配置让这部分数据通过tun0。tun/tap设备有什么用?从上面描述的过程可以看出,tun/tap设备的目的是将协议栈中的一些数据包转发给用户空间的应用程序,给用户空间的程序一个处理的机会数据包。因此,常用的数据压缩、加密等功能都可以在应用B中实现。tun/tap设备最常用的场景是VPN,包括应用层的tunnel和IPSec。比较有名的项目是VTun。有兴趣的可以去了解一下。tun和tap的区别用户层程序只能通过tun设备读写IP数据包,但是可以通过tap设备读写链路层数据包,类似于普通socket和rawsocket的区别,格式处理数据包的方式不同。Samplesampleprogram下面是一个编写的程序。tun设备收到数据包后,只打印出收到了多少字节的数据包,其他什么都不做。如何编程请参考以下参考链接。#include
