我的路由器浪漫

小标题致敬我的化学浪漫乐队(逃)

大学时期我就听闻了所谓的路由器改造,就是用开源的 OpenWrt 系统刷写原本的路由器,让它拥有更多有趣的功能。但那时候我并没有太深入了解,同时也不知道有一群玩软路由的爱好者群体。那时候的室友在某鱼上买了一个已经刷好的路由器,听说自带代理功能,还挺羡慕。但是,由于代理在每个客户端上都可以实现,没必要专门用一个路由器去做,所以我当时自觉对这方面的需求不大。

就这样,我与路由器的定制化错过了将近5年时间。这5年里我一直没注意正在使用的路由器,也从未去看过软路由相关的文章和论坛。然而,就在今年,我又重新关注起了这个小盒子。原因有很多,一是大半年来一直在捣鼓东西,从 Emacs 到 Arch Linux 再到 PowerShell … 真的是生命不息,折腾不止了。第二个原因是我对路由器层面代理的需求也日渐旺盛,特别是在技术领域。很多开源软件、脚本都不会自动识别出系统代理,走的还是原本的请求方式,导致编译程序或更新程序的时候会遇到各种各样的 Bug 。最简单的例子就是 GHCup 安装 Haskell 开发环境的时候,我甚至写了专门的文章来讲 这个事情 。这种问题一次两次还好,都能通过配置环境变量或设置镜像源来碰碰运气,但遇到太多次难免不让人抓狂。

本着 折腾至上 的想法,我立马从某鱼上下单了一个二手小米 R3G 路由器。下单之前我已在论坛上确认过这个路由器用比较轻松的方式破解,刷写的过程也比较简单。刚开始的时候,我直接从 OpenWrt 官网下载编译好的 OpenWrt 二进制内核文件,按照网上的教程刷到路由器里面(这样的教程太多了)。在代理上,我也直接下载了现成的 OpenClash 插件,安装到路由器的 OpenWrt 系统。系统稳定运行,就这样安稳地过了1个多月。

但是后来我发现了糟心的地方。OpenClash 对于我这多年前的小路由器来说太重了,经常内存占用达到90%以上。OpenClash 失效的次数也多,期间我还要重启路由器,十分麻烦。

经过一翻搜寻,终于发现了更轻量化的 SSR Plus 。然而,任凭我怎么找也找不到现成的为小米 R3G 编译的 SSR Plus 插件。有一些仓库里有定时的编译任务,但基本都不是最新的。我算半个追新派,想让用的软件都保持在最新状态(商业软件除外)。一翻思想斗争之后,我决定直接从零开始编译一个路由器系统。

源码选用了 OpenWrt 一个比较出名的 Fork : LEDE 。编译环境选用了 Arch Linux 。作者推荐使用 Ubuntu 或 Debian ,但毕竟我算是 Arch 爱好者,想试试在喜欢的系统上编译一下。

讲了这么多废话,下面终于可以进入正题了。

一次就过的编译指南

如果你正在阅读这篇指南,请注意下面几点:

  • 这篇指南只是一个编译 LEDE 内核的教程,不涉及破解路由器、刷 Breed 以及所有编译以外的教程
  • 本次是为了编译出小米 R3G (MIPS)上可用的 LEDE 镜像,过程中踩的坑可能是这个架构上特有的,如果你是其他架构,请有选择地参考本篇指南
  • 编译时 LEDE 仓库的最新提交是 6e84aeb589e322813e353d7f13c3c0e873664cec ,指南也只能对这个提交负责

下面就进入编译之旅吧!

1. 准备环境

更新 Arch Linux 到最新状态:

1
2
sudo pacman -Syu
sudo yay -Syu

下载 AUR 中好心人准备好的 Openwrt 编译工具集:

1
yay -S openwrt-devel

2. 准备源码

打开 LEDE仓库 浏览一边上面的编译安装步骤。

拉取源码:

1
2
git clone https://github.com/coolsnowwolf/lede
cd lede

打开代理插件源的仓库 helloworld ,浏览一遍上面的配置方法。

添加代理插件源 helloworld :

1
2
sed -i "/helloworld/d" "feeds.conf.default"
echo "src-git helloworld https://github.com/fw876/helloworld.git" >> "feeds.conf.default"

下载编译所需的所有脚本:

1
2
./scripts/feeds update -a
./scripts/feeds install -a

进行配置:

1
make menuconfig

配置如下(LEDE默认选的组件不去动它,这是额外添加的组件):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Target System >> MediaTek Ralink MIPS
Subtarget >> MT7621 based boards
Target Profile >> Xiaomi Mi Router 3G
Extra packages >> ipv6helper
Firewall >> ipv6tables >> ip6tables-extra 和 ip6table-mod-nat
Base system >> firewall4 (取消 firewall 或设置为 M)
Kernel modules >> Network Support >> kmod-wireguard
LuCI >> Collections >> luci-ssl-openssl
LuCI >> Themes >> luci-theme-argon
LuCI >> Applications >> luci-app-passwall2 (进下面的 Configuration 选中全部)

LuCI >> Applications >> luci-app-ssr-plus
Shadowsocks Client Selection -> Shadowsocks-rust
Shadowsocks Server Selection -> Shadowsocks-rust
V2ray-core Selection -> Xray-core
然后选中下面 ssr-plus 包的所有可选项(Include... )

LuCI >> Applications >> luci-app-smartdns
LuCI >> Applications >> luci-app-mosdns
LuCI >> Applications >> luci-app-uhttpd
LuCI >> Protocls >> luci-proto-wireguard
LuCI >> Protocls >> luci-proto-openconnect

你可以在 附件下载 中下载我的 .config 配置文件。

至此按道理已经可以开始编译,但为了避免可能的问题,下面要对源码进行部分修改。

3. 修改源码

修复 NaiveProxy 编译失败问题

具体报错为:naiveproxy clang++: error: clang frontend command failed with exit code 70 。

这里的解决办法是直接用官方为 MIPS 编译好的二进制包。具体操作过程如下:

1
2
3
4
5
# 在 lede 源代码根目录
# 备份 Makefile 文件
cp ./feeds/helloworld/naiveproxy/Makefile ./feeds/helloworld/naiveproxy/Makefile.bak
# 修改 Makefile
vim ./feeds/helloworld/naiveproxy/Makefile

直接将整个内容替换成(来源于 Bard大佬 ):

 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
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021 ImmortalWrt.org

include $(TOPDIR)/rules.mk

PKG_NAME:=naiveproxy
PKG_VERSION:=131.0.6778.86-1
PKG_RELEASE:=1

PKG_SOURCE:=naiveproxy-v$(PKG_VERSION)-openwrt-$(ARCH_PACKAGES).tar.xz
PKG_SOURCE_URL:=https://github.com/klzgrad/naiveproxy/releases/download/v$(PKG_VERSION)/
PKG_HASH:=skip

PKG_LICENSE:=BSD 3-Clause
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <[email protected]>

PKG_BUILD_DIR:=$(BUILD_DIR)/naiveproxy-v$(PKG_VERSION)-openwrt-$(ARCH_PACKAGES)

include $(INCLUDE_DIR)/package.mk

define Package/naiveproxy
  SECTION:=net
  CATEGORY:=Network
  SUBMENU:=Web Servers/Proxies
  TITLE:=Make a fortune quietly
  URL:=https://github.com/klzgrad/naiveproxy
  DEPENDS:=@!(arc||armeb||mips||mips64||powerpc||TARGET_gemini) +libatomic
endef

define Package/naiveproxy/description
  NaïveProxy uses Chrome's network stack to camouflage traffic with strong
  censorship resistance and low detectability. Reusing Chrome's stack also
  ensures best practices in performance and security.
endef

define Build/Compile
endef

define Package/naiveproxy/install
	$(INSTALL_DIR) $(1)/usr/bin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/naive $(1)/usr/bin/naive
endef

$(eval $(call BuildPackage,naiveproxy))

你可能需要将 PKG_VERSION 替换成最新版本的编号,请首先去 NaiveProxy官方仓库 确认一下最新预编译的编号是什么。

修复 shadowsocks-rust 编译失败问题

问题源自 getrandom 这个 crate 在 0.3.2 版本里引用了 libc::getrandom ,但是 libc 并没有定义这个函数。我推测是 MIPS 下的 libc 没有支持。

这里使用补丁解决,在 Cargo.toml 配置里将 getrandom 的版本回退到 0.3.1 。

1
2
3
4
5
# 在 lede 源代码根目录
# 创建补丁目录
mkdir -p ./feeds/helloworld/shadowsocks-rust/patches
# 新建一个补丁
vim ./feeds/helloworld/shadowsocks-rust/patches/001-fix-getrandom.patch

001-fix-getrandom.patch 的具体内容为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Index: shadowsocks-rust-1.22.0/Cargo.toml
===================================================================
--- shadowsocks-rust-1.22.0.orig/Cargo.toml
+++ shadowsocks-rust-1.22.0/Cargo.toml
@@ -246,6 +246,9 @@ shadowsocks-service = { version = "1.22.

 windows-service = { version = "0.7", optional = true }

+[patch.crates-io]
+getrandom = { git = 'https://github.com/rust-random/getrandom.git', tag = 'v0.3.1' }
+
 [target.'cfg(unix)'.dependencies]
 daemonize = "0.5"

你可以在 附件下载 中下载到这个 patch 文件。

你也可以在编译失败的时候直接修改 Shadowsocks-rust 的源码:

1
2
3
# 在 lede 源代码根目录
# 编辑源码中的 Cargo.toml ,具体路径依据你的平台和版本可能有变化
vim ./build_dir/target-mipsel_24kc_musl/shadowsocks-rust-1.22.0/Cargo.toml

然后在文件里 覆盖 Cargo 依赖

1
2
3
# 添加这两行
[patch.crates-io]
getrandom = { git = 'https://github.com/rust-random/getrandom.git', tag = 'v0.3.1' }

为了方便后续使用,你可以使用 quilt 生成补丁文件:

1
2
3
4
5
6
7
sudo pacman -S quilt
# 进入 shadowsocks 源码目录
quilt new 001-fix-getrandom.patch
quilt edit Cargo.toml
quilt refresh
# 回到 lede 源码根目录,直接拷贝补丁
cp -r ./build_dir/target-mipsel_24kc_musl/shadowsocks-rust-1.22.0/patches ./feeds/helloworld/shadowsocks-rust/

修复 v2dat 编译失败问题

问题和解决方法参见 这个 issue

直接添加编译选项

1
vim ./feeds/packages/utils/v2dat/Makefile

添加一项 PKG_USE_MIPS16:=0 就能解决问题。

4. 开始编译

简单的两行,不必多言。

1
2
make download -j8
make V=s -j1

然后等待大约3个小时,镜像就能编译完成。在经历了 3. 修改源码 后,我是一次编译通过的。

后记

附上一张编译成功结束的截图纪念一下:

这是刷入路由器之后的 LuCI 界面:

给路由器刷上编译完成的镜像后,到目前为止运行都是正常的。我又开启了 IPv6 功能,为每个设备分配了一个地址。代理目前也正常,路由器内存占用不超过50% ,代理速度比 OpenClash 快太多了。

软路由这块能折腾的东西太多,等空闲的时候再来研究研究吧。

附件下载