学校位于墙外,宿舍宽带上下 100Mbps,如此优势不拿来架代理简直浪费.. 问题在于宿舍网络位于 NAT 后面没公网 IP,也没有 UPnP 这类方便的东西。不仅架代理麻烦,开 MC / TR 服务器什么的也很麻烦.. 尤其是在双方都是内网的情况下。
困难
于是做了不少尝试,比如宿舍内通过 VPN 连接自家路由器,然后将数据包通过自家路由器转发到宿舍路由器上,但这样做怎加了额外的延时,且受家里带宽限制。再比如通过 UDP 打洞 来实现双方直连..
但是如此一来,要用 UDP 来传送 TCP 内容,就需要自己实现 TCP 的各种功能,还要追踪连接,太过复杂大大超出自己能力水平(太弱了。所以就想着用各种办法偷懒。
绕开难点
上学期做的一个尝试是,用 tun 虚拟网卡,直接转发IP包。如此一来,TCP 全由系统实现,避开了这些复杂的事… (参见《OpenWrt 上使用 Python 操作 TAP/TUN》)最后止步于放假回家,和 Windows 下修改路由表的一些问题(很想具体喷一下但是这就扯远太了)。
前段时间忽然想到,OpenVPN 可使用 UDP 建立连接。如此一来,只要自己先(用少量代码)完成 UDP 打洞,而后的操作就可以全权交由(相当完善的) OpenVPN 处理,工作量、复杂度急剧下降。
具体实现
先简单重复一下 UDP 打洞的具体过程。
- 在 NAT 后的双方,向一台外网设备发送 UDP 包,让该设备得到双方的 外网 IP 和 UDP 端口号;
- 该外网设备通知双方对方的 外网 IP 和 端口号;
- 双方互相向对方的 外网端口 发送 UDP 包,连接建立;
- 完成,通过该连接通讯。
实际使用时可能会受到一些限制,宿舍网络不存在此问题不再累述。
OpenVPN 服务器
首先,在宿舍内架 OpenVPN。我在 Cubieboard 上的 Ubuntu 架的,就按通常设置就行。
UDP 打洞要求在客户端连接前,服务器发送一个 UDP 包到客户端,且必须从 OpenVPN 监听端口上发出。为了在发送时不中断 openvpnd,我用了 pylibnet 跳过系统 socket API 直接发送该包。代码如下:
这段代码(ovpn-snatd.py)用于告知 VPS,OpenVPN 服务器的地址端口。同时接收由 VPS 转发的客户端地址端口信息:
(由于宿舍外网端口号实际上不改变,所以偷懒直接写代码里了,下同)
VPS 转发数据协助建立连接
然后,让 VPS 帮助传递端口双方外网端口地址,总共四段代码:
↑ 让用户得到自己的 OpenVPN 客户端对应的外网端口。
↑ 这是 flask 的代码片段。通过 HTTP,将用户的外网地址端口发给下面的ovpn-snatd.py
,同时返回给用户 OpenVPN 服务器的外网地址端口(为了方便,通过 memcache 读取)。
↑ 将 flask 发来的用户地址端口信息,继续转发给 OpenVPN 服务器。也将后者的地址端口信息,储存至 memcache,方便 flask 读取。
OpenVPN 客户端请求连接
用户启动 OpenVPN 前,先随机生成一个 UDP 端口,通过getported.py
取得该端口对应的外网端口。然后通过 HTTP 将端口号传递给view.py
,同时取得服务器地址。最后释放该 UDP 端口,让给 OpenVPN ,由它建立连接。代码如下:
用户使用起来还是很方便的。给 Windows 用的话用可用 py2exe 打包,然后将 OpenVPN 客户端放在bin\
。连接前只要安装 Tun/tap 驱动就行了。Linux 下把bin\openvpn.exe
改成openvpn
即可。在 Windows 7 和 Ubuntu 下测试过没问题。
问题
- 受 NAT 的具体实现方式限制,部分网络无法使用( 在拜托 @ipchihin同学用宿舍网络测试时,就遇到了这种情况);
- 某墙对 OpenVPN 的政策有时十分严格,甚至有报告因此被封 IP 的(1, 2, 3)。很难想像整个宿舍网络从此无法与国内联系的情形;
- 连接不便。Windows 需安装 Tun/tap 驱动,Android 和 iOS 需要 root / 越狱。在连接层实现,提供 Socks 5 / HTTP 代理才是王道啊!
另外,因为只是一次尝试并未正式投入实用,所以通信时并未做认证和加密。为避免安全隐患,代码发布前经过少量修改,但修改后未经实际测试。如有疏漏请留言,谢谢。
当时做得就挺乱的,现在讲得更乱,还请求轻喷。
看的不是很懂。。看来要去研究下NAT穿透方面的东西了
试试TeamViewer自带的双向VPN功能,你会发现一个大惊喜。。。
他是用第三方服务器中转数据还是直接打洞..?
另外还有似乎挺流行的 Hamachi..
貌似是直接打洞,我看到了IP地址和协议选择
为何不用n2n vpn