OpenWrt+R66s 软路由入门尝鲜
背景

随着家里电子设备越来越多,客户端维度的科学上网配置已经逐渐支持不了日常需求了:
- 设备数量越来越多;配置不过来,且容易触发机场的客户端连接上限。
- 设备类型越来越多;安卓端、IPhone端、IPAD端、Mac端、树莓派端、PC端、Kindle端,需要的客户端各不相同,配置的思维负担太大,甚至有些类型的设备没有靠谱的客户端。
- PAC规则越来越多;自己通常会有一个主要用的PAC规则,但是当换设备后,就需要重新配置,非常麻烦。
于是趁着工作之闲,研究了一下软路由,一站式解决了家庭上网问题。
硬件选购
市场上的可自由刷机的路由器还是很多的,不过对于入门级屌丝租客党来说,明白自己到底需要什么是很重要的:
- 不需要 WIFI 功能。移动光猫自带 WIFI 功能,软路由只需专注做网关服务器即可。后续如果对 WIFI 有更高要求再另买专用的 WIFI。所谓专业的东西做专业的事。
- 不需要 HDMI 功能。带 HDMI 功能的基本都是定位于迷你主机或者电视盒子的,其实做路由器根本用不到。
- 网口并不需要太多。考虑到家庭设备都是用光猫自带的WIFI连接,因此不需要很多网口。最低一个 LAN 口其实就足矣。
- 性能并不需要太高。毕竟租房的时候办的也就是200M带宽,网线再快也没用。同时家里也没搞什么视频工作站,内网也基本没啥带宽。况且性能高了的话、耗电、散热、占地都是问题。
- 芯片尽可能新一点。芯片类产品买新不买旧,就算不太在乎性能,也得考虑性价比,少买老古董不做垃圾佬。
- 尽量支持卡刷。线刷基本都需要去官方文档上查流程,比较麻烦。而卡刷的流程几乎都是一样的,拔卡写镜像开机即可。
基于上述原则,类似小米AX6000之类的 WIFI 路由器基本不会考虑了;类似J4125之类的性能较强、耗电较高的基于 X86 的路由器基本也不会考虑了;类似 R2s ,N1盒子之类的老东西也不考虑了。
搜寻了一番,发现电犀牛(FastRhino)的 R66s 刚好符合我的基本要求。
- 接口和大小刚合适。两个2.5G网口、两个USB3.0口、使用SD卡、无HDMI口,因此十分小巧。
- 产品和芯片比较新。产品是2022年5月发布,瑞芯微(Rockchip)RK3568芯片。
- 性价比较高。2G内存+32G卡配置到手320¥不到。

相比一些从电视盒子改来的玩意、或者羞羞答答才放开刷机功能的WIFI路由器、或者是仗着原装OpenWrt割韭菜的路由器来说,这个产品还是比较大胆和良心的。
固件选择
OpenWrt官方固件自然也是可以的,不过考虑到国人的常用需求,一般都是用国内维护的固件。常见的固件有:
个人感觉这些固件其实都大同小异,主要区别还是上游的 OpenWrt 版本不同、Linux 内核版本不同、默认自带的软件包不同(尤其是 iStoreOS 自带了很多私货应用)。其实 R66s 出厂自带的镜像也是够用的(其实就是 Lean 的固件),只是出于政策原因,没有自带科学上网工具。这些东西动动小手自己装下也行,不过为了方便大家开箱即用,直接写到固件里用起来更方便而已。
如果需要自己刷机,其实也不复杂。用读卡器插入PC,用 rufus 工具直接写入指定镜像即可。

这里还遇到了有一个有意思的点,原装的 32G SD 卡插进来之后,Windows系统的文件管理器只识别出了 64MB。原本以为是遇到了奸商,但是用磁盘管理工具一看的确是 32G 的盘。后来研究后才发现是因为这个是系统引导盘,因此有时只会识别系统主分区的大小,有时甚至不会被识别为外部存储,不影响使用。
正常刷完后,SD 卡分区大致如下。只使用了主分区,还有大量未分配的区域,如有需要等启动后再新增分区即可。

网络设计
相关背景知识可以先看一下油管 jack stone的家庭网络设计,讲的挺好,颇有老司机风范。
考虑到我这里光猫自带的WIFI也够用,但是光猫后台没有admin权限,很多东西不好改。并且出于社恐也不想打电话找宽带师傅把光猫的路由模式改桥接模式。因此我还是采用了 光猫做主路由+R66s做旁路由 的方案:
- 软路由和光猫以 LAN口-LAN口相连。
- 整个网络中只使用光猫的网段和光猫的DHCP服务。
- 光猫不做任何配置,由接入的客户端手动配置网关和 DNS 到软路由。
网络配置
对于新手小白来说,这一步最容易踩坑。尤其是当接线和配置不对时,连后台都打不开,这是最让人沮丧的。我个人也是尝试了很多姿势才了解其中的弯弯绕。
错误姿势:软路由LAN口直接接光猫LAN口
唉?前面不是说软路由和光猫就是要LAN口相连么?没错,但那是指的配置完成后的连接方式。
在设备出厂、或者刷了新固件后,说明书或文档上都会写清楚当前固件默认配置的IP。例如 R66s 刷的 Lean 固件默认 IP 就是 192.168.100.1。这个配置的意思是,当前设备的 LAN 口配置的是 192.168.100.1 的静态 IP。
而我这边光猫的网段是 192.168.1.0/24 ,软路由的静态 IP 不在这个网段内。这就导致连接光猫的PC设备和软路由是无法连接的,自然也就访问不到后台了。
错误姿势:软路由WAN口直接接光猫LAN口
既然软路由配置静态IP不行,那是不是直接用 WAN 口和光猫的 LAN 口相连就行了?毕竟 WAN 口的默认配置是 DHCP 协议,肯定可以直接加到光猫的网段中。
理论上说当然可以,但是实际上还是要看固件的配置。因为出于安全考虑,很多固件的默认防火墙配置是只允许从 LAN 口访问后台,不允许从 WAN 口访问。

如果我记得不错的话,iStore OS 默认是允许从 WAN 口访问的,而 Lean 固件默认是不允许从 WAN 口访问的。
正确姿势:软路由LAN口先和电脑直连配置好后再接光猫LAN口
为了避免上面的各种问题,最稳妥的方法还是电脑端直接和光猫LAN口相连(或者通过交换机相连),这样就避免了各种上面的幺蛾子。如果一不小心光猫的静态IP还和光猫的IP冲突了,直接断开光猫即可。
首次连接到软路由后台后,按照旁路由的配置思路,需要在 网络-> 接口 -> LAN 中进行如下配置:
- 配置协议为静态地址。
- 配置 IPV4 地址为当前光猫网段下的一个未被占用的IP地址。
- 配置子网掩码为光猫的掩码。
- 配置 IPV4 网关为光猫的 IP 地址。
- 配置 DNS 服务器为光猫的 IP 地址。
- 关闭 DHCP 开关。

配置完成后,当前页面应当就加载不出来了,这时候将软路由和光猫 LAN-LAN 相连,然后通过这里配置的静态IP就又可访问了。
OpenWrt重要路径
OpenWrt 基于 Linux,但是一些重要的配置和Linux 还是有些区别的。重点要关注下面路径下的文件:
/etc/config
这里记录了各个应用的通用配置。操作这里的文件和在 luci 页面中操作是等价的。/etc/init.d
这里记录了各个应用的启动命令,类似 service 或是 systemctl 的功能。/usr/lib/lua/luci/
这里记录了 luci 的页面信息,用于操作/etc/config
的配置。
配置 SSR-PLUS
Lean 国际版固件自带,或者直接在软件包下搜索安装下面两个包,配置上自己准备好的机场,就能开心使用了。
luci-app-ssr-plus
luci-i18n-ssr-plus-zh-cn
由于 SSR 会深入影响 DNS 解析,路由转发等基础功能。因此一般来说,SSR可以算是家庭网络的最大不稳定因素。所以这里有必要简单了解下 SSR-PLUS 的工作原理,方便定位网络问题。
单纯的 SSR 其实就是一个基于 libshadowsocks 的加密 socks 代理。不过仅此而已肯定不够,毕竟更重要的问题是“选择哪些流量走代理,哪些流量不走代理“。因此我理解 SSR-PLUS 就是 OpenWrt 下 SSR 结合了 iptables + ipset + dnsmasq 的组合工具。
SSR-PLUS 在启动时会加载两个外部订阅的数据。这些配置可以在 /etc/ssrplus
下看到:
- 国内IP列表。里面记录了部署在国内不会被墙的IP网段。
- GFW 列表。里面记录了被墙的域名。
国内IP列表比较简单,就是在 ipset 里添加一个 china 的路由规则集,方便后续进行 iptables 设置。而GFW 列表除了设置 ipset 之外,还要进行 DNS 选路。
进行 DNS 选路
众所周知,DNS 是明文报文,无良运营商可以很方便的进行 DNS 污染,导致直接解析到错误 IP,更别提后续的连接了。
一种简单的解决方案是将所有 DNS 请求都走 SSR 的 socks 代理,但这样毕竟效率较低。因此实践中常用的办法就是:
- 本地启动一个走 SSR 代理的 DNS 服务。可以用 dns2tcp 或者 dns2socks 之类的工具。通常暴露的是 5335 端口。
- 根据预先配置的 GFW 列表,告知本地的 dnsmasq 服务,对 GFW 列表内的域名走加密通道,对GFW列表外的域名走普通通道。SSR-PLUS 对 dnsmasq 的配置可以参见
/tmp/dnsmasq.d/dnsmasq-ssrplus.d
。
这样就能做到安全的 DNS 了。
配置 ipset 规则
我们知道 GFW 列表只记录域名,而在用 iptables 对流量路由转发时只能拿到 IP。因此 dnsmasq 在解析 GFW 后,还会将解析出来的 IP 加入到一个名为 gfwlist 的 ipset 规则。
root@OpenWrt:~# ipset list gfwlist
Name: gfwlist
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 56232
References: 2
Number of entries: 1686
Members:
20.198.162.78
65.21.236.8
142.250.186.54
203.77.190.0
54.148.90.231
....
这样一来,再配合 iptables 的 NAT 的 PREROUTING 设置,就能做到针对 GFW 内的域名进行代理转发:
root@OpenWrt:~# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
SS_SPEC_WAN_AC tcp -- anywhere anywhere /* _SS_SPEC_RULE_ */
...
Chain SS_SPEC_WAN_AC (2 references)
SS_SPEC_WAN_FW all -- anywhere anywhere match-set gfwlist dst
...
Chain SS_SPEC_WAN_FW (4 references)
target prot opt source destination
RETURN all -- anywhere 0.0.0.0/8
RETURN all -- anywhere 10.0.0.0/8
RETURN all -- anywhere 127.0.0.0/8
RETURN all -- anywhere 169.254.0.0/16
RETURN all -- anywhere 172.16.0.0/12
RETURN all -- anywhere 192.168.0.0/16
RETURN all -- anywhere 0.0.0.224.in-addr.arpa/4
RETURN all -- anywhere 240.0.0.0/4
REDIRECT tcp -- anywhere anywhere redir ports 1234
GFW模式和绕过中国大陆IP模式的区别
有了上面的基础,我们就明白了其实这两种模式的区别也很简单。看下两种模式下的 iptables 表就清楚了。
GFW模式:
Chain SS_SPEC_WAN_AC (2 references)
target prot opt source destination
RETURN all -- anywhere anywhere match-set whitelist dst
SS_SPEC_WAN_FW all -- anywhere anywhere match-set blacklist dst
RETURN all -- anywhere anywhere match-set bplan src
SS_SPEC_WAN_FW all -- anywhere anywhere match-set fplan src
RETURN tcp -- anywhere 36.156.102.177 tcp dpt:!domain
RETURN all -- anywhere anywhere match-set china dst
SS_SPEC_WAN_FW all -- anywhere anywhere match-set gfwlist dst
简要的工作流程是:
- 判断目的地址是否在白名单,如果是则不转发。
- 判断目的地址是否在黑名单,如果是则直接转发。
- 判断目的地址是否是大陆IP,如果是则不转发。
- 判断目的地址是否在GFW列表中,如果是则直接转发。
- 最后默认不转发。
绕过中国大陆IP模式:
Chain SS_SPEC_WAN_AC (2 references)
target prot opt source destination
RETURN all -- anywhere anywhere match-set whitelist dst
SS_SPEC_WAN_FW all -- anywhere anywhere match-set blacklist dst
RETURN all -- anywhere anywhere match-set bplan src
SS_SPEC_WAN_FW all -- anywhere anywhere match-set fplan src
RETURN tcp -- anywhere 36.156.102.177 tcp dpt:!domain
RETURN all -- anywhere anywhere match-set ss_spec_wan_ac dst
RETURN all -- anywhere anywhere match-set china dst
SS_SPEC_WAN_FW all -- anywhere anywhere
简要的工作流程是:
- 判断目的地址是否在白名单,如果是则不转发。
- 判断目的地址是否在黑名单,如果是则直接转发。
- 判断目的地址是否是大陆IP,如果是则不转发。
- 最后默认转发。
大白话总结就是:
- GFW 列表模式是:默认不翻墙,除非你在GFW列表中配置。
- 绕过大陆IP模式是:默认翻墙,除非你在国内。
那么具体家里选哪个更合适呢?我个人建议选 GFW 列表模式。虽然绕过大陆IP模式看起来方便,但是:
- 考虑到正常大部分流量还是不希望转发的,任何网站只有在GFW列表中、或者你配置的黑名单中才会翻墙。这样你会明确的知道哪些是翻的,哪些是不翻的。
- 中国大陆IP列表其实并不完善,很多运营商会偷摸摸使用一些保留IP作为局域网IP(例如类似 224.0.0.68 这类地址)。这就导致这些局域网IP会被中国大陆IP列表当成是海外IP进行转发。这显然就会导致难以排查的网络问题。(尤其是使用 N2N 之类的 P2P VPN 工具时)
配置 n2n
使用 v3 版的 n2n 需要先更新下 opkg 的源,否则拉不到适配 v3 版本的 luci-app 。虽然可以直接改 /etc/config/n2n
配置文件,不过毕竟不太优雅。
不知道为啥固件自带的源长这样,luci 的版本都跟其他的不一样。。。
src/gz openwrt_base https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/base
src/gz openwrt_luci src/gz openwrt_luci https://downloads.immortalwrt.org/releases/packages-18.06-k5.4/aarch64_generic/luci
src/gz openwrt_packages https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/packages
src/gz openwrt_routing https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/routing
src/gz openwrt_telephony https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/telephony
改成一样即可:
src/gz openwrt_base https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/base
src/gz openwrt_luci https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/luci
src/gz openwrt_packages https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/packages
src/gz openwrt_routing https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/routing
src/gz openwrt_telephony https://downloads.immortalwrt.org/releases/21.02.1/packages/aarch64_generic/telephony
配置好 n2n 客户端后,再在服务端配置好 nginx,挂上 ssl 域名 ,就能从远端访问家里的 OpenWrt 配置后台了。
当然,由于有些 luci 组件是需要额外暴露端口的,因此直挂 80 端口的 nginx 的话,实时监控、tty终端之类的功能还是没法用的。
运行一段时间后发现 n2n 的客户端 edge 经常时不时的挂掉,logread 看了下日志:
# logread |grep n2n
Thu Mar 30 19:24:48 2023 daemon.info n2n-edge[21183]: 30/Mar/2023 11:24:48 [n2n.c:38] ERROR: Unable to create socket [No file descriptors available][-1]
Thu Mar 30 19:24:48 2023 daemon.info n2n-edge[21183]: 30/Mar/2023 11:24:48 [edge_utils.c:273] ERROR: failed to bind main UDP port 0
...
看起来似乎是进程打开的最大句柄数超过了上限,确认了一下发现 ulimit -a
里看到的限制是 1024 ,实际 ls -lA /proc/{pid}/fd|wc -l
看到的正好也是 1024 (ls 的 -lA 参数忽略了 .
目录和 ..
目录,如果直接 ls -la
则是 1026)。。。
解决方式是改 ulimit 增大全局配置,或者直接修改 /etc/init.d/n2n
配置文件,增加进程的配额。而我选择后者(参考 openwrt 配置):
procd_set_param limits core="unlimited"
procd_set_param limits nofile="10240"
procd_set_param limits nproc="10240"
(更新)发现上面的配置没用, cat /proc/{pid}/limits
发现 nofile 的配置并没有生效。于是自己写了一个脚本,当 fd 超过限制就自动重启下 n2n,放在 cron 里定期执行:
n2n_pid=`ps -ef|grep /usr/bin/n2n-edge |grep -v grep |awk '{print $1}'`
if [ ! $n2n_pid ]
then
echo 'n2n not started'
exit 1
fi
fd_cnt=`ls /proc/${n2n_pid}/fd |wc -l`
if [ ${fd_cnt} -ge 1024 ]
then
/etc/init.d/n2n restart
fi
配置 Dropbear
Dropbear 可以理解是 OpenWrt 下的 sshd 。为了安全起见,dropbear 默认只对 lan 口过来的请求开放 ssh 连接。因此在配置好 n2n 之后,也是无法直接访问这个服务的。这里需要先关闭下 dropbear 对 lan 口的绑定:
root@OpenWrt:/etc/config# cat /etc/config/dropbear
config dropbear
option PasswordAuth 'on'
option RootPasswordAuth 'on'
option Port '22'
# option Interface 'lan'
# option BannerFile '/etc/banner
配置好后 restart 即可生效。
root@OpenWrt:/etc/config# /etc/init.d/dropbear restart
配置各个客户端
软路由配置好了后先不要急着连接设备。建议先重启几次路由器,确保配置正常生效,且重启不丢失。确认完成后,各个需要连接的客户端再手动配置下:
- 关闭 DHCP,IP 地址设置为原先自动分配的IP即可。
- 将网关/路由器设置为软路由IP。
- 将子网掩码设置为和主路由一致。
- 将 DNS 设置为软路由IP。
正常情况下,配置好后即可感受科学的魅力,不过偶尔也可能出现一些奇怪的坑。此时可以先尝试:
- 关闭 IPV6。SSR 不支持 IPV6 ,如果一不小心走了 IPV6,则基本上是翻不出去的。
- 重启设备。像 IPhone 这类的设备似乎配置完后要重启一下,否则容易报诸如 SSL Error 之类的错。