前两天成功用 mptcp 搭建好出国上 github 的快速通道后,在想如何方便的无缝使用。如果不使用 vpn 的话,就无法用路由规则来把流量倒向隧道。当然修改内部 dns 的 github 域名解析是可以的,但终归不是很舒服。SA 试了一下用PeerVPN搭 vpn tunnel ,工作倒是正常,但是 mptcp 跑在隧道上等于是白费了。PeerVPN 是用 UDP 通讯的,实际上只走了单条路径,如果再在上面跑 mptcp 等于是自欺欺人。所以也曾经考虑过使用基于 TCP 连接的 VPN ,比如 openVPN 。在 SA 尝试做配置的时候,我突然有了个想法。既然 PeerVPN 是开源的,何不修改一下实现,让它支持多个 ip 间建立多条路径的通道呢。读了一下代码发现,其实在 linux 下实现 vpn 是如此简单。linux 已经负责实现了 tun/tap 设备,可以帮你把 ip 层的流量(或以太网层)倒入一个虚拟设备。我们要做的仅仅是处理一下互联网上的通道转发而已。google 了一下多路径的 vpn 方案,发现有这个想法的人太多了。比如Multipath-VPN就是,不过几百行 perl 代码 。PeerVPN 虽然也不复杂,但就我需要的应用来看,大部分功能都是不需要的。所以还不如自己实现一下,应该也就是几百行代码的量吧。如果在 IP 层做流量转发,UDP 肯定是最合适的。因为 IP 包可以允许丢掉,可以乱序,这样必定最大程度的利用 tunnel 的有效负载。如果我们 tunnel 的两端都有不同网络的出口,那么可以并行的通道就有 N * M 条。使用连接去管理也复杂的多。而且网络出口也不一定是稳定的,对于办公室, ISP 给你换 IP 也是常事。tunnel 本身是不需要有连接的。研究了两小时的 tun 开发手册,我便兴致盎然的写了一晚上,就有了这个:mptun。动态负载均衡的算法是我拍脑袋想的。一开始将每个出口 IP 都赋予一个均等的权值,而目的 IP 也如此。当有流量需要发出的时候,select 出可写的通路,然后按权值随机选择一个目的地和出发源地址,把从 tun 设备里读到的 IP 包打包成 UDP 转发出去。一旦从一条通路发出,就给源 IP 的权值加上一点。而当从某个对端 IP 接收到数据包时,也给对应的目的地加上点权值。这样,如果从某个位置收的包多,就有更大几率向其发包;而从一个出口发的多,也就更可能从它那里发出。原本还计划给每条通路做一个计数,来统计丢包率来修正权值的。不过后来发现上面的策略已经可以满意了,就懒得再加。最后我们实际测试了一下效率。我们办公室有 4 个不同运营商的出口,对端 VPS 只有一个。用了一个接近 40M 的 zip 文件,用 wget 测试速度。以直接拉取(只利用一个出口的 TCP 连接)为基准,发现使用 mptcp 下载,虽然也会建立 4 条连接(通过四条路径),但总的下载速度只提高了 2.6 倍左右;如果使用 mptun 做 tunnel 的话,几乎可以提高 4 倍,真的把 4 条出口的可以利用的带宽都跑满了。当然对于小文件或做 ping 测试,丢包率可能略微上升、响应速度下降了一点。我的直觉解释是:tunnel 间建立了多条路径,IP 包的转发是随机在不同路径中进行的,依赖请求回应的应用,来回很可能走的不同路径,很大概率受最慢的路径的影响。而对于大量的流量,总体带宽却能提升不少。目前没有测试对端也有多个入口的情况。我设想是这样的:租用日本和美国两个机房的 VPS 。由于没有墙的干扰,日本和美国间的通路是非常顺畅的。所以我们可以把日本 VPS 做 DNAT 将 UDP 对应端口映射到美国机房。这样,从办公室看来,对端机器就有两个 IP 且这两个 IP 在互联网上属于完全不同的两个网段。当我们从办公室发包过去的时候,上游路由看起来我们的 IP 包发向了截然不同的地方,这样就可以互不影响,出国的流量就可以大大提高了。目前暂时只是个想法,还没有做实验。