最近在优化自己 PC 上的网络,因为我在工作中是使用的 CentOS,并且默认情况下都是连接 VPN 的,所以无论是内网资源还是外网资源都是走的 VPN 网络,这虽然在使用中没有什么问题,但是,我因为有一些个人的偏执,所以我希望内网的流量走 VPN,外网的流量走普通的网卡(反过来就更有意思了,我不知道自己在说什么,:))。于是乎我就对我电脑中的路由表做了一些特殊的操作,这仅仅只是开始,因为在我操作这一切的时候,发现了一些有意思的东西,其中一个就是 TUN 和 TAP,所以今天就来聊聊这两货。
在正式开始介绍 TAP 和 TUN 之前,请允许我先介绍一下这两个其实都是 Linux 下的虚拟网络概念,所谓的虚拟网络其实就是在 Linux 系统内部用来模拟真实世界的网络结构的一套系统,基本上在真实网络中常见的设备或模块都能在内核中找到对应的概念,这就是虚拟化网络的基础,下面就上一张图,总览一下对应的关系,应该后面我会介绍大部分的东西,所以有兴趣的同学可以期待一波。
TAP 和 TUN
介绍这两个概念,我想我的描述应该是不及 Wikipedia 的,所以,这里我就引用 Wiki 的描述来介绍 TAP 和 TUN:
- 在计算机网络中,TUN 与 TAP 是操作系统内核中的虚拟网络设备。
- 不同于普通靠硬件网路板卡实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能。
- TAP 等同于一个以太网设备,它操作第二层数据包如以太网数据帧。
- TUN 模拟了网络层设备,操作第三层数据包比如IP数据封包。
- 操作系统通过 TUN/TAP 设备向绑定该设备的用户空间的程序发送数据,反之,用户空间的程序也可以像操作硬件网络设备那样,通过 TUN/TAP 设备发送数据。在后种情况下,TUN/TAP 设备向操作系统的网络栈投递(或”注入”)数据包,从而模拟从外部接受数据的过程。
这样不知道你看懂了没有,因为 TAP 和 TUN 其实是对应到不同的层,所以,我这里就不复写了,直接用 TUN 来描述了,因为多数情况下我们接触的是 TUN,而不是 TAP。其实你可以将 TUN 认为是一个网卡管道,这个管道的一头是作为网卡被各种应用使用(例如服务器监听在这个 IP, 或者发送数据给这个 IP),另外一头可以作为一个文件(字符设备)被有需要的程序使用,例如我使用的虚拟专用网络,这样的话,其实示意图就是这样的:
这就是一个比较简单的模型,假设我用 Chrome 访问我的网站 https://liqiang.io,因为我开启了虚拟专用网络,所以所有流量都会被设置成走向 tap0,然后虚拟专用网络进程会通过字符设备读取 tap0 上的流量,进行处理之后,就会通过正常的网卡 eth0 将数据发送出去,然后就进入虚拟专用网络的服务端的处理了,这里就不赘述了。
从这样子看应该是比较简单明了得介绍这个东西了,那么下面我就来实践一下,怎么模拟这个过程,注意我的环境是 CentOS 7.4,如果你的环境不一样,应该也相差不会很远,可能有一些细节需要调整,可以自行 Google 查询。
Tap CentOS 下的实作(使用 Go 语言)
其实在我们了解了原理之后,事情就变得很简单了,剩下的就看有没有一些比较方便的库可以帮助我们了,这里我不准备重现上面的 ①②③ 的所有环节,我就演示 ① 和 ② 就好了,毕竟 ③ 已经是一个常见的 Socket 通信模型了。这里要感谢一下 Github 上的用户:songgao,因为他开源了一个还挺好用的 Go 语言库,所以我这里就依照他提供的示例来演示一番,首先是先上代码,如果想复现的同学,可以访问:这个链接 复制源代码:
将这份代码保存为 main.go
之后执行它,注意,我是以 root 用户执行的,这点很重要,普通用户应该是没有操作网络设备的权限的。
再强调一遍哦,这里你应该是已经使用 go run main.go
将上面这段程序运行起来了哦,这很重要,因为他会监听 TAP 的字符设备,随时准备接受传过来的数据,在你确定执行之后,我们继续下面的步骤,打开另外一个终端,执行一下命令:
这个时候 TUN 这个虚拟网络设备已经就绪了,是时候给他来点流量了: ping -c1 -b 10.1.0.255
,然后你就会在 main.go 代码的输出中看到类似这样的结构,这表示你已经通过 TUN 收到另外一端发送的请求了:
OK,这就是一个简单的示例,虽然简单,但是可以扩展的功能却往往只限制于你的想象。
那么这篇文章的分享就到这了,后面我还会有其他相关的更多分享,如果你感兴趣,可以考虑订阅本站的 RSS 或者公众号,这些都在侧边栏的最下面的图标中。
命令行实作
除了使用代码控制之外,通过命令行你也可以玩转 Tun 和 Tap,这里是一些可以参考的命令: