标题中特别注明“无需 OpenWRT”,是的,通常来说,大家都是习惯性用 OpenWRT 来作为主路由/旁路由/软路由。 但是我一直觉得,#OpenWRT 家族(原版➕改版)实在太大了,以至于我总是不知道应该选哪一款。 并且它们总是做得功能非常复杂,比较吃资源。然后绝大多数人应该只是需要其中的那个 Passwall 面板,仅此而已。
它们还需要各种适配硬件,应该大都是以 ISO 或者 IMG 的方式提供的,需要完整安装或者导入,并且提供的安装包一般也比较大(超过 100MB),还很可能找不到自己的硬件对应的资源。总之就是,操作复杂,成本太高,大材小用了。
起因
一些朋友应该知道我最近离开了深圳的家,去北方旅居了几个月。 北方的家里没有宽带,靠一个几十块钱的便携 Wi-Fi 支撑着1。 然后,莫名其妙地,中国联通的 IPv6 好像连不上中国电信的 IPv6,所以我暂时也没法通过 IPv6 回深圳的家里的网络走 OpenWRT 了2。“赖以生存”多年的网络架构突然有点儿“绷不住了”。 然后我就意识到,我应该重新搭一个 #旁路由 了。
为什么是旁路由?
稳定性大于一切。
我一直相信那句话很有名的话:“如果你家里有位网络工程师,那你的网会经常出问题。” 我不想把自己家里的网络搞得经常出问题(我自己没关系,不应该影响其他人/设备)。 所以我总是推荐把软路由作为旁路由而不是主路由。
旁路由和其它设备一样处于同一个网段,就是一个普通的设备,完全不影响未把旁路由作为网关的设备的网络。此时的旁路由它其实并没有路由功能,更只是一个网关程序——拦截流量,重新发出去。所以我们好像没有任何理由用 OpenWRT,我们平等地处于同一个网络,我们需要的根本就不是一个路由器。
如何拦截流量?
正常情况下,设备的网关地址是指向路由器的,路由器简单处理一下(通常是 NAT)后直接发出去:
如果我们把设备的网关(Gateway)地址设置成旁路由(此时还是一个普通设备/Linux 主机),路由器就会把来自设备的、发给服务器的流量发给旁路由设备(本文的树莓派):
那我们能不能在旁路由上把它还原成一个 TCP 连接?如果可以的话,是不是就接管了此连接?
可以! 过程其实很简单:旁路由假定自己就是设备要连接的服务器,然后把所有来自设备的网络包当作是发给自己的。
简单回顾一下 TCP/IP 模型中应用层的两个程序是如何一层一层地通过物理网络通信的:
- 发送端: 应用层 → 传输层 → 网络层 → 链路层 → 物理层;
- 接收端: 物理层 → 链路层 → 网络层 → 传输层 → 应用层。
如果要在内核中 HACK 一下,把接收端的网络层的“目标 IP”改一下,是不是就可以欺骗内核中的网络子系统说这些包就是发给本系统的? 然后,网络子系统就会把它还原成一条像是到本系统的连接一样,listen 后 accept 到此连接。
实际上,Linux 中早在 2008 年就有此技术了。其名字叫“TProxy”,即“Transparent Proxy”/“透明代理”。其实现更加细节:比如它还支持通过特定 socket 选项操作获取到原始设备的地址、原始服务器地址、模拟原始设备重新把数据发出去。无敌透明🫥!
拦截实现
按照上面的 tproxy 模块的文档,完成流量拦截需要两个主要步骤:
-
监听连接请求,socket 选项要支持
IP_TRANSPARENT
以允许作为透明代理的传入:1
setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
-
用 iptables 把流量转发到名为 TPROXY 的 Target 上:
1
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 50080
此 target 支持名为
--on-port
的端口参数。tproxy 会将透明代理来的连接转发到监听此端口的 socket 上。
经过上面两个步骤以后,所有来自“设备”的连接,都被 tproxy 接管并转发到树莓派上的监听程序上了,并且还可以拿到原始的服务器地址。
实现 tproxy 的几个例子:
- KatelynHaworth/go-tproxy: Linux Transparent Proxy library for Golang
- zfl9/ipt2socks: 将 iptables/nftables 传入的透明代理流量转为 socks5 流量的实用工具
第一个是很好的学习、研究例子。 第二个则可以把连接再次转发到 SOCKS5 服务器上,这是目前被所有主流代理软件提供的接入协议,所以使用更友好。
搭建旁路由网关
前面所讲的内容全部是为本节作铺垫。
-
常规代理软件
在设备上运行任何一个众所周知的代理软件即可。此软件会监听“localhost:1080”的 SOCKS5 连接。
-
tproxy
上述第二个程序(ipt2socks)将来自 tproxy 的流量转发到代理软件。
无任何参数运行后,会将 tproxy 的 “localhost:60080” 转发到 SOCKS5 的 “localhost:1080” 端口上。完成第一步操作。
-
iptables
同样来自此作者的 ss-tproxy,主要用来操作 iptables,将来自设备的流量转发到 tproxy 上。同时还提供了“特供版” DNS 解析器。完成了第二步操作。
作者的帮助文档写得非常详细,我不多说了。
完全上述3️⃣个步骤(运行3️⃣个程序)后,基本上,在树莓派本机上已经可以自由访问网络了。
同样,以“稳定”为前提,由于主路由上几乎都有开启 DHCP 功能,所以我不会在旁路由上面开启。 所以需要把需要代理的设备的网络设置改一下:
- 把 网关 改成树莓派的 IP 地址;
- 把 DNS 改成树莓派的 IP 地址。
此修改一直有效,断开 Wi-Fi 后重连也如此。
当然,建议把树莓派的 IP 地址在主路由器把和 MAC 地址绑定一下,使主路由分配固定的 IP 地址给旁路由。比如我的主路由是 192.168.0.1
,我的旁路由是 192.168.0.2
。其它设备的地址从 192.168.0.10
开始。
基本上,“无需 OpenWRT,用树莓派搭建旁路由网关”就成功结束了。
随机附一张图:
结束语
我觉得这是我目前体验最好、搭建起来最方便的方式了。
本文虽以树莓派为例,实际上完全不限硬件平台,任意能跑 Linux 的小主机都行。 甚至是虚拟机也没问题,只不过我不希望在 MacBook 上跑虚拟机(要一直开着,不然手机咋用?)。
我对旁路由的要求中“稳定性” > “网速”。 树莓派被很多人瞧不起,因为它的网络确实不算很好。不过,我很容易满足:慢一点可以,不要老是断网!虽然你们看不上树莓派,但是我用它刷网页、看油管很流畅,不开玩笑。
树莓派便宜啊!我买的 #树莓派 Zero 2 W 才 💯 刚出头…… 能用如此性价比的硬件带来全家设备自由上网,还要什么自行车吗?等等,我在旅游哎!树莓派这么小巧的东西谁见不爱呢?
参考配置文件
以下是 ss-tproxy.conf
我的修改部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
另外,还写了个 tmux 脚本用于开机自启动(启动上面几个程序的方式,不用完全参考我的做法):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
树莓派装系统?
用官方的 Imager 装个 Lite(无桌面)版就可以了,足够。 安装的时候在 Imager 上面设置好 Wi-Fi 和 SSH 的密码/公钥🔐。
然后再建议安装上我特意为树莓派编写的一款走蓝牙通信连接 SSH 的一款极其简单的软件,并设置开机自启动,方便在树莓派网络设置失败以至于失联的时候用 SSH 连上去补救一下,见《SSH 通过蓝牙连接树莓派》。