作为通信系统的一部分,也出于其他原因,本实验室需要研究一种代理程序。 本研究的基本设想是将数据通过多路方式无序地传送到对方服务器,然后自动重组, 建立一个在两个服务器之间的连接。
为了达到这个目的,我们需要至少建立一个能将TCP连接的通信传递到服务器上的隧道。 这样就有了本文将讨论的问题:我们如何获取和传递被代理的连接中的数据?
设想1: 编写一个TCP的监听服务器,将从TCP传递的数据流接收,然后拆分,通过多个渠道送出去,然后在目标服务器上重组。
这样做的问题是,通过多个渠道发送的数据包,在目标的顺序很可能被打乱。数据包也可能丢失。 为了解决这个问题,一个复杂的,用来解决重组数据包、重发丢失的数据包的程序就是必要的。 但是,TCP协议本来就是有类似这样的功能的。TCP协议处理下层的数据包的时候, 并不要求数据包按顺序到达,也不要求数据包没有丢失。 对于这些问题的处理,都是TCP协议的任务。相反,UDP方式传送数据的时候,就不能保证这一点:传送的数据可能丢失,也可能无序。
所以我们这样做,将是在重新制造轮子(reinvent the wheel),是个很复杂的事情。
设想2: 既然TCP数据包下层的IP数据包,是可以无序、无保证传送的,那么如果我们编写IP数据包的代理呢?
这应该是可行的!TCP现在就有多路TCP的概念。VPN也是基于这个原理实现的。 如果我们将IP数据包收集起来,用UDP包加密承载它们,就应该能做到一个代理服务器的事情。
本文就将谈一谈,如何将IP数据包转换成UDP封包,构建一个简单无加密的VPN。
需要的材料和工具
首先,为了模拟一个VPN,笔者建议建立两台计算机。
其次,这两台计算机可以位于互联网或者局域网,能通过UDP方式直接联系到对方。
两台计算机上都需要安装socat
这个命令。在Ubuntu操作系统下,可以通过apt-get install socat
安装。
原理
TUN/TAP设备
达成这个目的的关键是Linux的TUN/TAP设备。
内核中的TUN/TAP驱动程序在/dev/net/tun
或者/dev/tun
这里有个特殊设备。
当这个文件被一个程序打开的时候,内核就会给系统添加一块虚拟网卡,例如tun0
。
说网卡是虚拟的,是说,这块网卡没有任何与之对应的物理线路。
想象一个真实的网卡:
任何时候,当内核认为这块网卡上的数据包需要通过网线发送的时候,对于TUN/TAP设备,数据包就会直接发送给建立虚拟网卡的这个程序。
反过来,如果这个程序向/dev/net/tun
写入数据包,内核就会把这个数据包当作是从网线传来的数据包处理。
如果我们编写这样一个程序,能收发本地计算机上的虚拟网卡数据包, 然后直接原封不动,传递给另一台计算机上的接收程序,让接收程序把数据包写入另一台计算机上的虚拟网卡, 那么就完成了一个连接两台计算机的隧道,好像两台计算机通过一根虚拟的网线相互连接。 这就是本代理程序的原理。
这种隧道有TUN和TAP两种模式建立。TUN模式中,程序收发的是网卡中的IP数据帧。TAP模式则更底层一点,收发的是以太网帧。 本文由于知识局限,只使用第一种模式实验。
socat命令
socat
是一个很强大的命令,可以在两个数据源之间建立双向的连接。
socat
命令的基本用法很简单,就是socat <SRC1> <SRC2>
。
这样,由SRC1
和SRC2
定义的两个数据源之间就可以互相通信。
用man socat
命令可以查到很多数据源的定义。
例如:使用这个命令将例如标准输入输出发送到一个远端的TCP口:
socat - TCP:www.google.com:80
然后输入
GET /
回车,就会看到www.google.com
返回类似下文所见的结果(结果经过编辑并非原始数据):
HTTP/1.0 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.com/
Content-Length: 219
Date: Sun, 23 Aug 2015 16:00:00 GMT
Server: GFE/2.0
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
实验
实验1: 利用socat命令构建一个简单的VPN
首先登录两台计算机,然后启动命令行。下面的命令需要root
权限完成。
首先确定两台计算机的防火墙不会阻挡UDP连接。如果有ufw
命令的话,可以用ufw disable
暂时关闭防火墙。
我们假设连接到对方计算机时所用的对方IP地址是1.2.3.4
。
首先在对方计算机上运行:
socat UDP-LISTEN:5555 TUN:10.0.0.2/24,up
这样就在对方计算机上建立了一个TUN设备,并为之分配了虚拟IP地址10.0.0.2
,子网掩码是255.255.255.0
(/24
)。
这个设备的通信和一个在5555监听的UDP端口相连。
在本地计算机上我们运行:
socat UDP:1.2.3.4:5555 TUN:10.0.0.1/24,up
建立一个虚拟网卡,地址是10.0.0.1
,在这个网卡上收发的包发送到对方计算机的5555端口。
两个命令执行后,两台计算机就好象是位于10.0.0.0/24
局域网中了。
首先我们用ping来检验这个连接,在本地计算机上:
ping 10.0.0.2
应当能收到回复。我们还可以访问对方计算机的程序,例如,如果对方计算机上运行了HTTP服务,可以用浏览器打开http://10.0.0.1
进行访问。
如果对方计算机上运行了代理服务器,那么,通过10.0.0.2
这个IP地址,也可以访问到对方计算机的代理服务。
这样我们不需要修改路由表,也可以通过这个代理程序访问互联网了。
实验2: 用Python编写一个监听TUN数据包的程序
首先需要安装一个Python的模块python-pytun
:
pip install python-pytun
这个模块提供了比较简单的方法来为系统添加一个TUN设备。示范程序如下:
使用root
权限运行这个程序。将会在本计算机添加两块虚拟网卡。
在命令行使用ping 10.8.0.100
,可以看到ping所用的ICMP数据包应该出现。
如果ping 10.8.0.2
或者ping 10.8.0.1
,不会看到数据包,因为系统内核知道这两块虚拟网卡在本地计算机,所以不会通过虚拟网线发送数据。