基于TUN/TAP实现多个局域网设备之间的通讯

目标

本文会对tun/tap的原理进行基本说明,然后基于tun/tap实现多个局域网设备之间的通讯,实现跨网络通信。

例如:实现局域网A中的主机和局域网B中的主机进行通信。

原理

简要说明物理网卡的工作流程

在了解tun/tap之前,先简要说明物理网卡的工作流程。

网卡处于网络协议栈和物理网卡之间,一端连着网络协议栈,一端连着物理网络

  • 当发送数据时:将接收网络协议栈的数据,并将数据封装成帧,并通过网线(对无线网络来说就是电磁波)将数据发送到网络中。
  • 当接收数据时:接收网络上其他设备传过来的帧,并将帧进行解码,然后传递到网络协议栈中。

TUN、TAP基本原理

目前主流的虚拟网卡方案有tun/tapveth两种,在时间上tun/tap出现得更早,它是一组通用的虚拟驱动程序包,里面包含了两个设备,分别是用于网络数据包处理的虚拟网卡驱动,以及用于内核空间与用户空间交互的字符设备Character Devices,这里具体指/dev/net/tun)驱动。大概在 2000 年左右,Solaris系统为了实现隧道协议Tunneling Protocol)开发了这套驱动,从 Linux Kernel 2.1 版开始移植到Linux内核中,当时是源码中的可选模块,2.4 版之后发布的内核都会默认编译 tun/tap 的驱动。

tuntap 是两个相对独立的虚拟网络设备,其中 tap 模拟了以太网设备,具备操作二层数据包(以太帧)的能力,tun 则模拟了网络层设备,具备操作三层数据包(IP 报文)的能力。使用 tun/tap 设备的目的是实现把来自协议栈的数据包先交由某个打开了/dev/net/tun字符设备的用户进程处理后,再把数据包重新发回到链路中,你可以通俗地将它理解为这块虚拟化网卡一端连接着网络协议栈,另一端连接着用户态程序,只要协议栈中的数据包能被用户态程序截获并加工处理,程序员就有足够的舞台空间去玩出各种花样,譬如数据压缩、流量加密、透明代理等功能都能够以此为基础来实现。

就以我们此次要实现的应用程序为例,当程序发送给tun设备数据包时,整个工作流程如图所示:

在这里插入图片描述

实现思路

要实现局域网A中的主机和局域网B中的主机进行通信,我们需要有三端,分别为客户端A、客户端B、服务端C,其中服务端C部署在公有云上,它和客户端A、和客户端B的网络是通的。

分别启动服务端C客户端A、B,并且服务端C分别和客户端A、B建立TCP链接。

客户端A创建tun网卡,再对这个网卡配置IP以及子网掩码并且激活tun网卡,那么Linux会自动加上这个网段路由规则。

客户端B创建tun网卡,再对这个网卡配置IP以及子网掩码并且激活tun网卡,那么Linux会自动加上这个网段路由规则。

客户端A、B设置IP,需要在同一个网段,并且不与已有的网络冲突,例如:10.10.10.1/24、10.10.10.2/24

客户端A向客户端B的IP10.10.10.2)发起请求,通过路由规则的计算,将会路由到tun网卡,然后通过用户程序对tun网卡进行监听,将tun网卡的数据通过客户端A与服务端C建立的TCP连接发送出去(这里走的就是物理网卡)。

当C端接收到请求数据之后,向所有和它建立TCP链接的客户端发起广播请求(除了A端这个最初的发送端除外),如果是目的地IP,将会响应给服务端,否则将会丢弃,当服务端收到响应之后,再同理的将响应数据广播给最初的发送端。

在服务端和客户端的TCP交互过程中,我们要注意处理TCP的粘包问题。

我们前期设置tun网卡的IP和激活tun网卡,可以先手动操作,需要注意的一点就是,tun网卡它是通过用户程序进行创建的,将用户程序结束之后,tun网卡将会自动删除,所以我们需要每次维护它的IP并且手动激活它。

至此,这个程序的大致实现思路就结束了。

这里就没代码的具体实现了,主要讲讲思路,如果对代码有兴趣的话。easy-tun,注释比较细,应该很容易能够看懂。

测试

这里的测试主要是对easy-tun的代码进行测试。

前提

GO环境

启动服务端

# 设置go拉取依赖的地址
root@vultr:~/goWorkspace# go env -w GOPROXY=https://goproxy.cn,direct
# 拉取依赖
root@vultr:~/goWorkspace# go get
# 启动服务端
root@vultr:~/goWorkspace# go run Server.go

启动客户端并且设置IP

  • 启动客户端A
# 设置go拉取依赖的地址
root@vultr:~/goWorkspace# go env -w GOPROXY=https://goproxy.cn,direct
# 拉取依赖
root@vultr:~/goWorkspace# go get
# 通过-ser指定服务端的IP地址,这里填你服务端的IP
[root@localhost goworkspace]# go run Client.go -ser xxx.xxx.xxx.xxx
server address		:xxx.xxx.xxx.xxx
local tun device name :gtun
connect server succeed.
# 对指定网卡设置IP,gtun为你网卡的名称,如果你的tun网卡名称不是gtun,自行调整
[root@localhost goworkspace]# sudo ip addr add 10.10.10.1/24 dev gtun
# 将gtun网卡激活,如果你的tun网卡名称不是gtun,自行调整
[root@localhost goworkspace]# sudo ip link set gtun up
  • 启动客户端B
# 设置go拉取依赖的地址
root@vultr:~/goWorkspace# go env -w GOPROXY=https://goproxy.cn,direct
# 拉取依赖
root@vultr:~/goWorkspace# go get
# 通过-ser指定服务端的IP地址,这里填你服务端的IP
[root@localhost goworkspace]# go run Client.go -ser xxx.xxx.xxx.xxx
server address		:xxx.xxx.xxx.xxx
local tun device name :gtun
connect server succeed.
# 对指定网卡设置IP,gtun为你网卡的名称,如果你的tun网卡名称不是gtun,自行调整
[root@localhost goworkspace]# sudo ip addr add 10.10.10.2/24 dev gtun
# 将gtun网卡激活,如果你的tun网卡名称不是gtun,自行调整
[root@localhost goworkspace]# sudo ip link set gtun up

在客户端Aping客户端B

[root@localhost goworkspace]# ping 10.10.10.2
PING 10.10.10.2 (10.10.10.2) 56(84) bytes of data.
64 bytes from 10.10.10.2: icmp_seq=1 ttl=64 time=355 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=64 time=671 ms
64 bytes from 10.10.10.2: icmp_seq=3 ttl=64 time=363 ms
64 bytes from 10.10.10.2: icmp_seq=4 ttl=64 time=356 ms
64 bytes from 10.10.10.2: icmp_seq=5 ttl=64 time=1473 ms
64 bytes from 10.10.10.2: icmp_seq=6 ttl=64 time=592 ms

至此,我们实现了不在同一个局域网的两台机器,像访问局域网一样进行通讯。

参考

  • <<凤凰架构>>
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值