前言:常见分流方案探讨
要实现透明上网冲浪,分流总是一个绕不开的难题。由此,诞生了很多不同的软件和方案,最常见的莫过于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为例:
与嗅探的方案不同,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网关的教程太多了,这里不打算讨论具体的搭建细节,而是方案选型。首先,搞个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服务器一起结合使用,比如配合PaoPaoDNS的CUSTOM_FORWARD功能就可以完成简单精巧的分流。
Github 项目地址: https://github.com/kkkgo/PaoPaoGateWay
(っ◞‸◟c)都看到这里了,点个Star吧
你可以从Github Release下载到最新的镜像:https://github.com/kkkgo/PaoPaoGateWay/releases
文档说明以Github最新文档为准,此处不再提供