【转载】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)缓存链接这些就不关注了

  1. 使用"github.com/songgao/water"创建一个虚拟网卡tun

 

c := water.Config{DeviceType: water.TUN}
iface, err := water.New(c)

tun/tap可以是什么?

https://paper.seebug.org/1648/
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
  1. 配置网卡
    分别使用了如下命令

 

/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一直没成功,不知道为什么,但是先不管

 

[[email protected]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地址、链接状态。

  1. 创建服务端udp链接,监听
  2. 开启一个线程循环从tun网卡接口中读取数据,从缓存中取出对应的客户机地址,再把数据通过udp发送出去
  3. 循环将信息从udp链接中读出,再写入tun网卡接口,并缓存这个udp地址

客户端

  1. 创建一个虚拟网卡tun
  2. 配置网卡
    客户端使用的是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

照这样配置的话,访问服务机IP8.8.8.8,会走ens33网卡,从192.168.172.2
其余目标IP匹配到第一条路由,tun0网卡,第一条路由和第二条路由的目标都是0.0.0.0,但是第一条路由的网关的二进制为10000000.00000000.00000000.00000000,第一位为1,根据路由最长匹配原则,会匹配到这一条。
就是将除服务机IP8.8.8.8外地址的访问全部引流到tun0网卡。
由于mac的资料太难找了,懒得在上面弄了

  1. 开一个线程循环,读取udp链接中的数据写入tun网卡
  2. 主线程循环读取tun网卡,将数据通过udp发给服务机

总结

有个疑问是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是不能直接访问互联网的,在服务端是通过路由转发到物理网卡来访问的网络

作者:东南枝下
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。