最近折腾家里的网络,主要解决的问题是国内各大视频音乐网站都有IP限制,无法观看。这个其实unblock youku插件在桌面可以解决,但是在手机上就不行了。于是进阶的方案是在国内买个VM,然后弄个http/socks proxy。这个方案的缺点是得给每个设备手动指定代理,而且国外的网站又不想走代理(会被墙!),自动选择代理的pac脚本也不是所有的设备都支持。所以就只剩最复杂的方案,透明代理。原则上来说,就是在家里的某个路由器上折腾一下,把所有的国内流量通过代理送过去,其他的流量走原来的路线。

透明代理

透明代理的原理是Linux Kernel提供的一个功能。它可以把符合某个条件的报文直接转发到一个端口,由软件来处理。这样的话,我们就可以把所有的中国IP都直接转发给本机的一个本地代理,然后本地代理转发给一个国内的远程代理,这样这个请求就看起来像国内的IP了。如果流量不是问题,这个其实实现起来非常简单,通过IPSET可以把所有的中国IP都包含在内,然后直接全部转发。只是很多网站是视频网站,我的国内虚拟机每月只有500G,可能不是很够,所以最好能像unblock youku一样,只是认证过程走国内流量,播放部分继续直接走默认路由。

代理选择

一开始我研究了squid。这个是上个世纪就存在的代理了。它可以缓存网页来达到加速效果,但是在https横行的世界,缓存并没有什么用。当然,有的代理(mitmproxy,Man In The Middle Proxy)可以自动生成中间人证书,就可以查看每个SSL/TLS的流量是什么内容了(这个将来也许可以用来做家长控制)。我折腾了很久的squid,就是不能让它集联起来,本地代理死活不去找远程代理。另外一种是著名的翻墙软件shadowsocks(ss),我这个其实应该不用那么复杂,因为并不需要隐藏内容,但是相关的软件实在多,所以我最后还是用了ss。普通的https proxy应该也是可以的。最后我的Client Proxy是clash,Server Proxy是shadowsocks-libev。Clash是一个中国小伙用go写的,可定制性高一些。

方案一

说到路由器,第一反应是搞open source的定制的ROM,比如OpenWRT。BTW DD-WRT已死,RIP。有人给OpenWRT做了一个shadowsocks的plugin,理论上就可以在路由器上配置哪些网站通过shadowsocks出去。这个显然是给国内翻墙出来用的。这个方案有这些问题:

  • 路由器得可以烧OpenWRT。我家的不行。
  • 还必须得搞某个国内大神自己编译的版本,才能有那个plugin,不是很靠谱。
  • 直接在x86上装openwrt当纯路由——OpenWRT不太支持UEFI,然而新的电脑都是UEFI,所以根本无法启动。
  • OpenWRT是嵌入式软件,改动起来很麻烦,懒得搞cross compile那些。

Tomato之类的也都类似,放弃了。

方案二

既然OpenWRT不靠谱,那么找些专门的路由软件吧。这个比较著名的有pfSenseOPNSense之类的防火墙兼路由系统。这个也有问题:

  • 都是基于FreeBSD的,缺少一个Linux Kernel提供的功能:iptables REDIRECT。
  • 既然主业是防火墙,那应该站在modem和wifi中间,需要一个双网卡的机器,然而我不想去买个新机器。用某些VLAN的方案也许可以,但是setup也很麻烦。

方案三

最后的方案还是fallback到vallina Linux,并且手动设置它为路由器,还用iptables做了转发。一开始不想用这个的方法其实是不想手动设置这些,不过最后看来也不是很复杂。具体内容包括

  • 设置Linux可以转发报文——这个只用改动一个内核配置就好了

  • 用ipset和iptables设置所有的中国IP都REDIRECT到本机的代理端口

    • 这个端口不是普通的proxy端口,必须是要能理解REDIRECT的端口。该服务要查询kernel询问原来的目的地才能做到转发,否则,这就是一个从device到本地代理的普通TCP链接请求,没有任何意义。这也是为什么必须用clash或者ss-redir(shadowsocks的一个组件),redsocks的原因。
  • 本地代理根据某些规则,进一步决定是否要转发去上级代理(国内),还是自己直接链接服务器。比如我们想要youku.com的域名全部去国内的代理,但是其他的一些CDN就直连。这个只有clash做的比较好,可以定义规则。golang想加点东西也很容易。

    • 本地代理从TCP上只能看到IP地址,如何知道其域名从而运行规则呢?当然我们可以做反向域名解析,但是这个太慢,而且不可靠。Clash做了两个黑科技。总体来说,它自带一个DNS,要求客户端链接它的DNS。这样客户端做了DNS解析后,它存一份在内存里,这样请求来了之后它就可以直接做内存里的反向查询。它还有一个叫fakeip的技术,就是每个DNS请求都返回一个不可路由的192.18.0.0/16的IP段地址,这样收到请求后它就可以一一对应IP和域名。当然,这个对于我这个只转发部分IP的情况不适用。
    • DNS还有另外一个问题,如果DNS解析发生在国外,如果该网站在国外有分舵(bilibili在LA就有),那么问题根本没有解决。所以要求中国的域名要发到中国的解析。还好Clash也有支持,虽然它是设计来给国内用的,但是也可以解决我们的问题:在fallback-filter.domains里面列出要去backup nameserver的,就可以了。支持域名后缀。
  • 接下来应该设置DHCP服务器,让所有的设备的网关和DNS都指向这个Linux,它就成了一个软件路由器。它的下级是原先的真·网关。可以在原有路由器的DHCP上改,或者直接关闭,在Linux上配置一个DHCP服务器。我们家的路由器即不能改DHCP,也不能将其关闭,只能手动一个一个设置静态IP了?。打算扔了重新买个。

其他的一些问题

IPV6

这个目前并没有好好支持,但是好在中国的网站支持ipv6的也不多。加上应该也不是很困难,原理是一样的。等IPv6再普及一些吧。

IPTables/ipset vs nftables

新的nftables应该可以替代iptables/ipset,但是现在这个规则也比较简单,以后再说了。。

这个是概述,下一篇写写具体的设置,命令。

参考

http://ivo-wang.github.io/2018/02/24/ss-redir/
https://0x01.io/2017/04/01/x86-软路由透明代理构建方案/