前言:常见分流方案探讨

要实现透明上网冲浪,分流总是一个绕不开的难题。由此,诞生了很多不同的软件和方案,最常见的莫过于All in One的开源路由器固件,界面也非常的傻瓜小白,点点点就能用。但是缺点也很明显,良莠不齐、碎片化的软件包集成在一起的All in One路由器,稳定性肯定大打折扣,首先这类软件通常是接管了所有流量再分流,一旦软件运行崩溃,那就可能造成整个网络崩溃,其次碎片化、臃肿的软件包集成在一起运行机制变得很迷,主路由更应该做的是路由器的路由工作,跑太多服务反而拖慢了网络性能。最后,这个方案的应用场景也很有限,对企业用户来说,不太可能把整个网络寄托于一个开源路由器上。
于是也有很多人使用了旁路由的方案。跟上面的方案一样,只是把开源路由器接在主路由的下面,然后把设备的网关设置成旁路由或者dhcp网关信息,这样万一旁路由挂了的影响会变小,一些重要的设备可以设置固定的主路由网关。但这样做的话,使用旁路由的设备就相当于经过了两层nat,一些没必要经过旁路由的流量也经过了,经过两层网关,性能和稳定性也打折扣。
为了更灵活,我们把目光聚焦到路由本身。假设我们有一条企业专线(相当于旁路由),我们怎么把需要走专线的流量走专线呢?最朴素的方案,那就是维护路由表,比如很多商业路由器内置有GEOIP库,一种简单粗暴的方法就是把默认路由走企业专线,然后CN IP走宽带线路,这样确实能正常工作,使用VRRP等协议也能一定程度避免全炸,但把默认路由设置成专线可能不是个好主意,首先并不是所有流量都真的需要走专线,比如P2P流量等,某些服务会根据IP分配到国内节点等,其次IP库的更新不够及时也会对体验造成一定影响。而如果单独维护需要访问的网站的IP段,似乎又是个更繁琐的工作。
如果能基于域名进行分流,那么灵活性就会大大地提高,毕竟维护域名列表比维护IP段还是要轻松不少的,虽然有部分应用需要通过IP直连,但也只需要维护少部分IP段,大部分都可以域名规则解决。而如何通过域名来分流又是一个问题。如果你在主路由上,不乏有很多基于ipset的方案,原理就是使用一个第三方的DNS程序,当查询到对应域名的IP地址之后,把这个域名的解析结果加入ipset(相当于一个你需要分流的IP集合),然后把这些个ipset做策略路由。这种方案先不讨论稳定性,显然你需要对主路由动手脚,All in One 不在这里的讨论范围,实际上这种方案也是很多具备域名分流功能的路由器插件的原理。
有没有办法基本不改动主路由的情况下,基于域名进行分流呢?在很多年前尝试过最朴素的http反向代理,因为常见的流量协议就http和https,搭建一个sniproxy,然后把需要分流的域名解析到sniproxy的服务器IP,可行,网站能访问。但之后出现一个奇怪的现象,自从浏览器升级之后,有些https的网站有时候能访问,有时候不能,最后经过查找原来是http2导致的问题,http2有个特性,如果几个域名解析出来的IP地址一样的话,他会把这些请求合并在一起发送过去,节省开销加快访问速度。正好是这个特性,导致sniproxy无法理解和正确处理返回扔过去的流量。如果为sniproxy增加多个服务器内网IP可以缓解但也不太现实。再后来,一些开源软件比如v2开发了域名嗅探(sniffing)的特性,可以从流量中检测出http/https的域名,并重新以域名请求出站到目的地,有了这个特性,我们就可以搭建一个网关,流量扔过去,然后嗅探,再出站,而跟上面一样,每个域名的IP不能一样,于是写了一个对域名进行hash后生产对应网段IP的服务程序,比如在11.0.0.0/8网段内的“假IP池”,每个域名对应唯一的IP值(经过多个参数构造的hash出来在同一个网站相同的概率很小),然后我只需要把11.0.0.0/8,写一条静态路由到这个网关,那么被解析到11.0.0.0/8里面的域名就会自动走到这个网关出站,用起来也很顺畅。而这个方案也是有一些缺点的,比如,由于他是嗅探出来才能处理,像ssh这种协议他就无法知道原来的IP是什么,好在是http/https协议能满足大部分需求,剩下的维护路由表搞定。有没有更好的方案呢?这就是接下来要说的基于FakeIP网关的方案。

FakeIP网关的工作原理

FakeIP的概念源自RFC 3089,顾名思义就是给你返回一个假的IP地址,而这个假的IP地址作为key,来反向查找你的域名。与上面讨论的嗅探方案差不多,准备一个“假IP池”,不同的是,FakeIP把解析到的IP和域名记录下来,建立一个映射的关系,最终出站还原。以一个简单的访问qq为例:

1
2
3
4
5
6
7
8
9
# 假设你的FakeIP池是11.0.0.0/8
# 第一步,客户端发起访问请求
curl https://qq.com
# 第二步,客户端需要知道qq.com的IP地址
DNS从FakeIP地址池里返回一个FakeIP: 11.0.0.1,并把他和qq.com关联到自己的映射表里面
也就是在网关看来11.0.0.1=qq.com
# 第三步,11.0.0.0/8网段被静态路由到网关(此处不同方案策略不同,有的是在主路由上搞的)
网关接受到11.0.0.1的流量请求,从映射表里面找到11.0.0.1=qq.com
使用qq.com还原真实的请求,出站。

与嗅探的方案不同,FakeIP通过地址池和域名建立的映射关系,最终可以真实还原请求,也就是说,他理论上兼容任何协议,不管是ssh还是rdp,不管是TCP还是UDP。他还顺便解决了DNS污染和DNS泄露的问题,解析可以交给远端进行,而本地没有真实的解析过程,相比嗅探也有更低的性能开销和更好的延迟。但你也可能很早就听过了FakeIP的存在,这玩意怎么跟你说的不一样好像有很多bug?比如有什么跳过FakeIP的列表,什么米家设备不能用的问题?因为这些方案里面是All in One,先把所有域名不管是什么域名都解析成FakeIP,再扔给具备分流功能的程序里面处理分流,这样自然会产生很多奇奇怪怪的问题,因为有些协议不是所有域名请求都必须有回应的,而有些协议需要直连,而且本质稳定性跟前面的一炸全炸的方案一样。如果我们只把需要分流的域名解析到FakeIP池,除了和嗅探方案一样部分应用需要维护少量的静态路由IP段之外,理论上不会有啥问题,唯一的问题是,当FakeIP停止工作后,再重新启动,FakeIP的地址池和映射的域名就对不上了,由于客户端本地有DNS缓存,因此会经历短暂的无法连接。这个问题的缓解办法就是把FakeIP的TTL设置的尽量低(比如TTL=1),当FakeIP停止工作后DNS缓存马上失效,只要客户端遵从标准的TTL设置一般不会有太大的感知。简单来说,FakeIP的工作流程原理如下图:

主路由
主路由
FakeIP网关
FakeIP网关
客户端
客户端
DNS服务
DNS服务
FakeIP池
11.0.0.1=qq.com
......
........
FakeIP池...
被分流的域名
被分流的域名
静态路由11.0.0.0/8 下一跳是192.168.1.200
静态路由11.0.0.0/8 下一跳是192.168.1.200
192.168.1.1
192.16...
192.168.1.200
192.16...
192.168.1.2
192.16...
qq.com
qq.com
qq.com
.....
qq.com...
DNS查询
DNS查询
11.0.0.1
11.0.0...
域名命中分流列表
域名命中分流列表
返回解析11.0.0.1
返回解析11.0.0.1
192.168.1.53
192.16...
向FakeIP网关查询
向FakeIP网关查询
返回解析11.0.0.1
返回解析11.0.0.1
请求
请求
Viewer does not support full SVG 1.1

如何搭建一个FakeIP网关

网上搭建FakeIP网关的教程太多了,这里不打算讨论具体的搭建细节,而是方案选型。首先,搞个docker镜像是否可行?答案是可行,也不可行。因为docker本身的网络特性不太适合作为网关使用,搞个macvlan已经够复杂了,性能也不够好,其次更重要的原因是受到内核的限制。docker本身并不能改变宿主的内核,而无论是通过tun,tproxy或者其他方式,都依赖内核的实现,如果内核没有相应的模块,或者模块有但是对不上(比如nft和ipt),那么你做的镜像别人就不一定能跑起来。最后,因为涉及到路由转发和内核,你往往需要给容器特权,这对容器来说也不是一个好主意。简单来说,使用docker会让问题变得复杂,失去了标准化的意义。因此,我们选择使用虚拟机来搭建,linux系统选什么不太重要,能装模块就行。流量入口方面,建议使用tproxy,因为对TCP和UDP支持都很好,特别是UDP,处理链也比较高效。支持FakeIP的程序有很多,程序的官方文档一般也有附带教程,推荐的话,建议选择配置比较清晰简明的clash,配置FakeIP的DNS也比较方便。最后就是一些配合程序使用的iptables规则/nft规则,以及策略路由,这个可以很轻松在网上找到(可以搜索tproxy透明代理)。如果你觉得这个过程还是太复杂太难了,那么接下来就是我做好的一个现成的网关,轻松上手即用。

PaoPao GateWay

PaoPao GateWay是一个体积小巧、稳定强大的FakeIP网关,由Rust实现的转发内核,内含智能嗅探和高效rapidhash的FakeIP算法,支持Full Cone NAT ,支持多种方式下发配置,支持多种出站方式,包括自定义socks5、自定义openvpn、自定义yaml节点、订阅模式和自由出站,支持节点测速自动选择、节点排除等功能,并附带web面板可供查看日志连接信息等。PaoPao GateWay可以和其他DNS服务器一起结合使用,比如配合PaoPaoDNSCUSTOM_FORWARD功能就可以完成简单精巧的分流。
Github 项目地址https://github.com/kkkgo/PaoPaoGateWay
(っ◞‸◟c)都看到这里了,点个Star
你可以从Github Release下载到最新的镜像:https://github.com/kkkgo/PaoPaoGateWay/releases

文档说明以Github最新文档为准,此处不再提供