how minivtun works
5 min read

how minivtun works

B/y------------A/x -----------C/z ----------W

假设有以上四台机器,其中A/x为minivtun服务器,A为真实IP,x为虚拟IP。

B 和 C 是minivtun客户端,分别连接A。y, z为虚拟IP,跟x为同一网段。

W跟C是同一个网络,A, B不可以直接访问w,B和C之间也是不可直接互联的。

通过minivtun,我们可以让B和C通过y,z虚拟地址直接互联,还可以让B访问W。

  1. B和C之间通讯流程

a. B要访问z,根据路由表,tun0(minivtun创建的虚拟设备)的地址y跟z是同一个网段,所以被选中

B 机器 -->z ? ---> 根据路由表,选择 y->z ---> 发送到(tun0) --> minivtun程序读取, 把IP包封装发给服务器 B->A | y->z -->eth0 ---> A机器

A机器 eth0-->B->A |y->z ,去掉外壳,得到y->z --->根据路由表,发送到(tun0) ---> minivtun程序读取, 查找z对应的的机器C , 封装A->C|y->z -->eth0 --> C机器

C机器 eth0 -> A->C|y->, 去掉外卡,得到y->z ,并记录y的来源为A----> 目标地址z即为本机,送达!

A通过Keepalive消息来记录真实IP和虚拟IP的映射关系,这样B才能到达C。 C在B的数据到达之后,即可知道虚拟地址y的来源是A,即记住了y的来路。 这样,C的回复包就可以原路返回到B。现在假设 C要回复给B,

C机器 --> y ? ---> 根据路由表,选择 z->y ---> 发送到(tun0) --> minivtun程序读取, 然后查找 y对应的物理机器地址,即为A,于是封装 C->A|z->y -->eth0-->A机器

。。。

  1. B和W之间的通讯过程

B机器上需要增加一条路由规则,表示需要绕道x(注意不是A)。

ip route add W via x

A机器的minivtun服务器程序需要加入参数 -v W/prefix=z, 表示W需要转给z。 同时路由表要增加规则,

ip route add W via z

注意,这里使用的z是虚拟地址,这样所有的转发过程是在minivtun的隧道里完成的。

因为C和W之间已经有正常的路由关系,所以数据可以直接转给W。

W机器的回包要返回给B,这需要在W机器上增加路由规则

ip route add y via C

在Windows 10机器上,以上路由规则加上之后,从B Ping W是不通的,虽然W Ping B是OK的。换做其它协议,比如http,则双方发起都OK。所以说路由设置应该是正确的,可能Windows在防火墙设置上对来源IP有限制(只允许同内吧)

我们也可以在机器C上做SNAT,这样就不需要在W机器上添加路由规则了。甚至可以借由C,访问所有与C相连的网络,只要在A和B上设置这些ip的路由。

iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE

minivtun程序从eth0读取数据后,解密然后把原始ip包直接写入tunfd,并不会真的是写入tun0设备中,它还需要查找路由表!这里写入tunfd有误导性,以为所有数据都有tun来接收和处理,其实根本不是。所以,tunfd只是一个入口而已,因为只有tun设备才能直接写入原始的ip包数据!!!没错,如果要写原始ip包,你就必须先打开一个tun设备!这样理解就对了!!!

minivtun程序从tun0里读取的数据,一定是发给它的(同一网段的),或者经过它的(route add xxx via),这些都是受路由表控制的。

前面写入tunfd的ip包,如果属于tun0,自然就由minivtun读取并处理了。否则,一般是交给默认网关处理了。如果没有设置nat,出口ip就是虚拟地址,网关根本处理不了。如果设置了nat规则,这样网关就可以通过了。

tunfd写入可以随便什么数据,读出的则一定是给它的,这一点有点诡异,理解了就好!

所以基本流程就是这样,虚拟IP通过tun设备,然后tun设备加上真实IP发送到eth0设备,然后eth0设备又去掉真实IP得到虚拟IP,再发给tun设备 .....如此反复,知道到达目标地址。

VIP --tun0-> RIP -> eth0 -> VIP->tun0 ...

#ip r s t local
broadcast 10.8.0.0 dev mv0 proto kernel scope link src 10.8.0.1
local 10.8.0.1 dev mv0 proto kernel scope host src 10.8.0.1
broadcast 10.8.0.255 dev mv0 proto kernel scope link src 10.8.0.1
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 192.168.199.0 dev eth0 proto kernel scope link src 192.168.199.238
local 192.168.199.238 dev eth0 proto kernel scope host src 192.168.199.238
broadcast 192.168.199.255 dev eth0 proto kernel scope link src 192.168.199.238

路由local表示的就是本机作为终点的ip,只要目标地址是这些ip,就不会发送到网卡设备上,而直接交付给上层应用了!

原文时间:2016.4