网络编程学习:vpn实现
想了解vpn的实现原理,找了个大佬开源的vpn代码,分析学习该代码
https://github.com/net-byte/vtun.git
先测试一下该工具,根据README.md的指引,编译。
准备一台linux服务器
分别查询linux服务器和本级的公网IP
curl -L ip.tool.lu
然后按照README.md分别启动linux服务端和本机客户端
结果发现默认使用ws需要TLS,改tcp也没法访问网络 ( •́ω•̩̥̀ )
开始阅读源码
开启一个新工程边看边写
所以先看一下使用udp实现服务端的代码
服务端
只关注核心部分代码,其中字节混淆,根据key(ip)缓存链接这些就不关注了
- 使用
"github.com/songgao/water"
创建一个虚拟网卡tun
c := water.Config{DeviceType: water.TUN}
iface, err := water.New(c)
tun/tap
可以是什么?
https://paper.seebug.org/1648/
图片.png
TUN/TAP 是操作系统内核中的虚拟网络设备,由软件进行实现,向操作系统和应用程序提供与硬件网络设备完全相同的功能。其中 TAP 是以太网设备(二层设备),操作和封装以太网数据帧,TUN 则是网络层设备(三层设备),操作和封装网络层数据帧。
当应用程序发出报文后,报文将通过操作系统协议栈处理,到达网络设备,硬件网络设备将收到的报文转化为电信号发出,而虚拟网络设备(TUN/TAP)不具备实际的物理功能,报文需要上层应用进行处理,如下:
在 VPN 中我们可以借助 TUN/TAP 来捕获用户发出的报文。可以通过命令来创建TUN/TAP 设备
ip tuntap 创建名为 tun0 的 tun 设备
sudo ip tuntap add dev tun0 mod tun
为 tun0 配置 ip
sudo ifconfig tun0 192.168.0.10 netmask 255.255.255.0
查看 tun0 网卡
ifconfig tun0
拓展~~~
创建 tap/tun 设备:
ip tuntap add dev tap0 mod tap # 创建 tap
ip tuntap add dev tun0 mod tun # 创建 tun
删除 tap/tun 设备:
ip tuntap del dev tap0 mod tap # 删除 tap
ip tuntap del dev tun0 mod tun # 删除 tun
- 配置网卡
分别使用了如下命令
/sbin/ip link set dev tun0 mtu 1500
ip link 其实就是链路层,这一步是设置 mtu
mtu: 从网络接口传输的数据包的最大大小,单位是字节(Byte)
/sbin/ip addr add 172.16.0.10/24 dev tun0
这一步是分配/添加IPv4地址
可以用命令
ip addr show tun0
来查看分配的IPv4地址
分配ipv6地址
/sbin/ip -6 addr add fced:9999::9999/64 dev tun0
更改链接状态
/sbin/ip link set dev tun0 up
与之相对应的
/sbin/ip link set dev tun0 down
查看链接状态
ip link show dev tun0
但是我这里ip link set dev tun0 up
一直没成功,不知道为什么,但是先不管
[root@VM-4-14-centos bin]# ip addr show tun0
9: tun0: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 500
link/none
inet 192.168.0.10/24 scope global tun0
valid_lft forever preferred_lft forever
inet 172.16.0.10/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fced:9999::9999/64 scope global
valid_lft forever preferred_lft forever
到这里就完成了服务端的网卡创建
总结一下:配置了mtu、ipv4地址,ipv6地址、链接状态。
-
创建服务端udp链接,监听
-
开启一个线程循环从tun网卡接口中读取数据,从缓存中取出对应的客户机地址,再把数据通过udp发送出去
-
循环将信息从udp链接中读出,再写入tun网卡接口,并缓存这个udp地址
客户端
-
创建一个虚拟网卡
tun
-
配置网卡
客户端使用的是mac端,使用了以下命令
ifconfig tun0 inet 172.16.0.10 172.16.0.1 up
ifconfig tun0 inet6 fced:9999::9999/64 fced:9999::1 up
看不太懂,但大意是设置ipv4地址和ipv6地址,如果用到是linux客户机,这一步用的命令和服务端虚拟网卡的设置一致
/sbin/ip link set dev tun0 mtu 1500
/sbin/ip addr add 172.16.0.10/24 dev tun0
/sbin/ip -6 addr add fced:9999::9999/64 dev tun0
/sbin/ip link set dev tun0 up
接下来获取本系统的一个不是环回、启动的物理网卡接口,其实我本地获取到的就是en0
获取需要远程的服务端的IP地址
获取本地路由表里的默认网关,在linux里就是标记为UG的,linux和mac命令分别为
linux:
route -n | grep 'UG[ ]' | awk '{print $2}'
mac:
route -n get default | grep 'gateway' | awk '{print $2}'
以下是mac客户机的路由配置
route add 服务端IP地址 本客户端网关
route add 8.8.8.8 本客户端网关
route add -inet6 ::/1 -interface tun0
route add 0.0.0.0/1 -interface tun0
route add 128.0.0.0/1 -interface tun0
route add default 172.16.0.10/24
route change default 172.16.0.10/24
如果是linux客户机,则是
/sbin/ip route add 0.0.0.0/1 dev tun0
/sbin/ip -6 route add ::/1 dev tun0
/sbin/ip route add 128.0.0.0/1 dev tun0
/sbin/ip route add 8.8.8.8/32 via 本客户端网关 dev en0
/sbin/ip route add 服务机IP地址/32 via 本客户端网关 dev en0
要理解上述配置首先要了解路由表
linux用route -n
查看路由表,mac用netstat -rn
命令查看,这里吐槽一下,网上mac的资料好少,以后我再也不用mac了,又贵又垃圾......
现在先来了解下linux的路由表和route
命令
route
命令可以参考这篇文章:https://www.kancloud.cn/chunyu/php_basic_knowledge/2106519
假设我的服务机是36.152.44.96
(其实这是百度的机器),配置下来路由表是这个样子
jenson@jenson-virtual-machine:~$ route -n
内核 IP 路由表
目标 网关 子网掩码 标志 跃点 引用 使用 接口
0.0.0.0 0.0.0.0 128.0.0.0 U 0 0 0 tun0
0.0.0.0 192.168.172.2 0.0.0.0 UG 100 0 0 ens33
8.8.8.8 192.168.172.2 255.255.255.255 UGH 0 0 0 ens33
36.152.44.96 192.168.172.2 255.255.255.255 UGH 0 0 0 ens33
128.0.0.0 0.0.0.0 128.0.0.0 U 0 0 0 tun0
169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 ens33
172.16.0.0 0.0.0.0 255.255.255.0 U 0 0 0 tun0
192.168.172.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
照这样配置的话,访问服务机IP
和 8.8.8.8
,会走ens33网卡,从192.168.172.2
。
其余目标IP匹配到第一条路由,tun0网卡,第一条路由和第二条路由的目标都是0.0.0.0
,但是第一条路由的网关的二进制为10000000.00000000.00000000.00000000
,第一位为1,根据路由最长匹配原则,会匹配到这一条。
就是将除服务机IP
和8.8.8.8
外地址的访问全部引流到tun0网卡。
由于mac的资料太难找了,懒得在上面弄了
-
开一个线程循环,读取udp链接中的数据写入tun网卡
-
主线程循环读取tun网卡,将数据通过udp发给服务机
总结一下
图片.png有个疑问是tun虚拟网卡能访问网络吗?因为我在客户机删除36.152.44.96
的路由,重新配置一条route add 服务机IP地址(36.152.44.96)/32 via 本客户端网关(192.168.172.2) dev en0
,是无法ping通36.152.44.96
的。
tun是不能直接访问互联网的,在服务端是通过路由转发到物理网卡来访问的网络
实践一下
根据这个原理,参考源码来自己写个最小实现