昨天今天花了一天的时间,终于把网络环境配好了。当然了,中间还有一些杂七杂八的事情暂且不提(但是真的很舒服啊,比如下面这个,我好了)——
嘛,总之是配置完了,现在一切就都正常多了,整体的架构也很符合直觉。当然了,还有一些比较 dirty 的实现需要后期修正,那就是之后的事了(逃
当然,这些这是后话了。话说这一切,都要从……
目标?
其实原来目标很简单。本机能够实现流畅访问互联网。透明代理真的太快乐了
然后后来就开始慢慢加戏了。你说本机流畅了,那 create_ap
的热点呢?这个需求是解决了,但如果我要深度去广告怎么办?这又涉及到 MITM 和脚本的问题。还不是给刘大爷惯的
于是,最终的目标就成型了。我们的目标必须满足下列要求:
- 能使本机流畅访问互联网
- 能使连接了本机通过 create_ap 共享的网络的设备流畅访问互联网
- 能够根据简单的域名规则屏蔽广告
- 能够根据复杂的脚本规则屏蔽广告
方案 -2.0
最开始我使用的方案是旧白话文里的普通配置,这个配置最大的优点就是简单。我们这里不妨摸一份过来(有修改):
这种方案的本质是 NAT
。我们这里只讨论 TCP 部分:对于局域网内的流量,我们希望能够直接连接,因此我们让 192.168.0.0/16
的流量 RETURN
;对于任意的 TCP 流量,我们希望它能够默认被送至 dokodemo
,但又不希望从 dokodemo
出来的流量再被送回 dokodemo
,于是我们给它打上 fwmark
;对于剩下的流量,我们就把它送到 dokodemo
对应的端口,也就是文中对应的 12345。
最后,我们需要让这条链(也可以说是我们定义的规则组)运作。我们需要把它加到两条链里:nat
表的 PREROUTING
链和 OUTPUT
链,前者是转发报文处理过程中的一环,而后者则是本机发出报文的必经之路。
经过这样的处理之后,无论是对于本机,又或是外部设备,访问互联网都是透明的了。那这种方案有什么问题呢?
其实问题是相对主观的。这个版本的配置我也用了一段时间,但最后还是换成了下一个版本的,其中最关键的因素就是速度。虽然没有经过 benchmark
,但从个人的体感速度上来说是不如下一个版本的。另外,NAT 不支持 IPv6(原文)。
你去用 ip6tables 啊 kora!
方案 -1.0
这个版本的方案就是新白话文中的 TProxy 了,这也是上一个方案中处理 UDP 的做法,就一并放到这里讲好了。
TProxy 的详细原理在我后来写的这篇文章中有详细解释,感兴趣的不妨去看看(
TProxy 的方案非常优秀,整个过程中没有任何问题,这也是我使用时间最长的方案,将近一年了吧,但最后我还是放弃了这个方案主要的原因其实并不是它的过错,而是 JSON
的配置格式极大地限制了整个文档的修改与扩充。于是,在第114513次尝试扩充之后,我放弃了。
方案 0.0
这时候我遇到了 clash。yaml
的格式很对我胃口,类 Surge 的配置方式也很顺眼(谁让我几个月前刚买了一年订阅呢)。正好,有新的 PR 支持了 Tun
,虽然并不是太稳定,但也能用了不是,于是我就准备尝试一下。
这个过程持续了大约两个月的时间,使用的脚本是 AUR
clash-tun
的个人修改版本,期间给 PR
解决了一个 [bug](https://github.com/Dreamacro/clash/pull/393#discussion_r379273286)
。但是这个方案有一个最大的问题:热点共享失效了。
现在回过头来看其实原因很简单,是动了 PREROUTING 的奶酪,但这个问题着实困扰了我长大一个月的时间,期间各种补课,总算理解了整个过程。
// TODO: 有空补充一下为什么会炸
MITM?
MITM 的想法是在使用 Surge 脚本的时候浮现出来的,简单来说就是可以改请求改返回之类。由于脚本的功能非常强大,因此我一直想怎么才能在本机上实现这个功能。
在不需要自己手动实现的前提下,我很快就瞄准了 mitmproxy。正好,mitmproxy
也有 upstream
模式。于是在群友的帮助下,我确定了配置的基本架构:
为了简化设计,在 TUN -> MITMProxy 的中间件选择上,我使用了 clash 的 TUN 模式。于是——
方案 1.0
总之话不多说,先上配置好了。首先是第一层的配置:
先来解释这一层吧。这一层是需要权限的一层,在启动这一层的时候需要执行 iptables
命令,正好和 53 端口分配重合了,于是就放在一起了。但事实证明,如果不放在同一个配置文件里,则会导致整个系统无法正常运行,原因未知。
(个人感觉是因为 TUN 要处理 IP<->Domain,因此 DNS 和 TUN 必须在同一层,有待验证。)
在这里,我们定义了两个规则:一个是需要 MITM 的,默认的 mitm.it 肯定是要的;接下来就是默认的规则,负责处理那些不需要经手的规则,通过前置代理直接送到下一层对应的 socks5 端口。
接下来是之前各个解决方案的本体,也是最终处理链接的层次:
这一层就没什么好说的了。
展望
回顾整个过程,从想法,到思维误区,到跳出习惯的舒服,我学到了 iptables
的基本规则和使用方法,Linux 下 TunTap 的一些常识,并且对网络的认识进一步加深了。
现在的方案存在一定的问题,首先就是 DNS 的问题,需要将所有发往 53 端口的DNS 请求都修改为发送至本机 DNS,换句话说就是 DNS 交给 clash 处理;其次是用户权限,root 还是太大了,在非 root 的情况下其实很多事情也能完成;最后就是这两层能不能相对融合?我什么时候造轮子?(逃
总之,路漫漫其修远兮,坑还有114514个。最近还在讨论 PUG 的设计,总之肯定是不会闲下来的(笑)
那暂时就这样,おやすみなせ〜