硬路由(0)—— 编译属于自己的 OpenWrt
OpenWrt
OpenWrt 项目是针对嵌入式设备的 Linux 操作系统,常用在路由器上。OpenWrt 高度模块化、自动化,不仅占用空间小,而且具有强大的网络组件。
OpenWrt 项目始于 2004 年 1 月,其第一个版本采用了 LinkSys 的源码。在 LinkSys 的代码收费后,改为 Linux 内核集成,并将 OpenWrt 完全模块化,不断推出补丁和驱动。
更多的信息可以在 OpenWrt Wiki 找到。
OpenWrt 有数个民间衍生出的版本,加入了一些官方列表没有,针对国内具体情况定制的插件,且源码和开发规范与官方同步,保证了软件的纯净和稳定,目前网络上的很多固件都是针对这3个固件进行编译。
- Lienol:紧跟官方版本进行更新,其编译出来的固件体积比其它版本小,集成的软件少,源码改动少,使用起来稳定性更强。
Lean:由恩山论坛 Lean 分享了全部开源代码,由于加入了很多中国特色的功能,并告诉所有人怎么通过编译去得到自己想要的 OpenWRT,定制灵活性很强。(不推荐)- ImmortalWrt:OpenWrt 的一个分支,移植了更多的软件包,支持了更多的设备,更好的性能,并为中国大陆用户进行了特殊的优化。
前两者需要自行编译,ImmortalWrt 可直接选择固件下载。
OpenWrt 的当前稳定版本系列是 24.10,其中 “v24.10.0” 是该系列的最新版本。它于 2025 年 2 月 6 日发布。
来自 OpenWrt Wiki:
如果您熟悉 Linux 类系统,那么 OpenWrt 对于您就非常简单。如果您不熟悉,那我们就需要首先从一些基础概念开始。
您应该已经知道 OpenWrt 是一个针对嵌入式设备的 Linux 发行版,Linux“发行版“是一个通过创建和维护一些软件包实现根据用户需求来定制 Linux 系统的项目。一个”软件包“是包含一个应用程序或一些脚本的压缩档案,其配置文件中还包含了用于将其集成到操作系统中的信息。软件包可被一个能够下载、打开、安装、卸载的应用程序——包管理器(OpenWrt 中为 opkg)处理。所以,一个 OpenWrt 固件是一些围绕 Linux 内核的软件包组装而成。
每个软件包都被独立进行编译,当软件包编译完成后,所需的软件包都被“安装”在一个临时文件夹中。其后,该文件夹会被压缩并变为设备固件的只读压缩分区(squashfs)。
内核也被当作一个软件包处理,但却通过特殊的方式添加到固件映像中以便设备的引导程序可以发现它。所以,您可以在不触及设备的引导程序(比较危险而且不是总是可行)的情况下替换设备原版的固件。
构建程序的最后一步才是生成一个固件文件(也就是您用于安装或升级 OpenWrt 的文件),这个文件通常是一个准备写入内置闪存储存装置磁盘映像,所以您会发现许多开发人员在 IRC 或邮件列表称其为“映像”。
为什么不推荐 Lean’s LEDE
知乎上的回答:https://www.zhihu.com/question/635802944。
根据笔者自己使用 Lean’s LEDE 及其改版的体验,它对 OpenWrt 主线(老的主线)进行了大量魔改,并且安装了大量臃肿的软件包,导致实际体验很差。由于深度魔改的原因,我们也无法使用一般的软件源,经常运行更新后整个系统都无法使用。
从一个入门者的角度,在一开始就使用这种定制系统是十分不合适的。笔者同时也是 Arch Linux 用户,对这种做法不太赞成。再加上 Lean 等人如此圈地自萌,与官方主线越走越远的行为,无疑是在分裂这个社区,更是开源精神所不能容忍的。
自己编译 OpenWrt 的好处
- 高度定制化。预编译的固件通常包含很多默认的软件包,而自己编译可以去除不需要的组件,节省存储空间,提升性能。比如,如果不需要 IPv6 支持或者某些 VPN 功能,去掉这些可以减少固件大小,可能让旧设备运行得更流畅。
- 便于刷写固件。如果在使用过程中发现某个内核模块未安装,我们只需加入该模块并重新编译即可,并且相比于第一次编译会减少很多时间。将编译好的 sysupgrade 映像文件直接上传到 LuCI 内,OpenWrt 会完成剩下的工作。
- 硬件兼容性。不同路由器硬件差异大,官方可能没有针对特定型号的优化。自己编译时可以选择适合的驱动和内核模块,提升稳定性和性能。比如某些无线网卡可能需要特定的驱动,官方固件没包含,自己编译就可以加入。
- 功能扩展。OpenWrt 的软件仓库有很多第三方包,我们可以添加官方未包含的功能。如内网穿透工具 frp、zerotier,或者网络监控工具如 ntopng。对于开发者来说,可能需要集成调试工具或开发环境,自己编译就能方便地加入这些工具。
- 学习价值。编译过程涉及 Linux 系统、交叉编译、网络配置等知识,对技术提升有帮助。我们可以通过这个过程更深入了解操作系统的工作原理,或者为将来开发自己的项目打基础。
为新设备添加 OpenWrt 支持
此部分主要来自于 OpenWrt Wiki。
为了全面了解如何添加新设备支持,我们建议您查看一台新设备相关的最新的 commit,摸清楚哪些文件发生了修改和如何修改它们。我们只需要去 GitHub 仓库找到某设备对应的 PR 查看就能理解。
重要文件
最重要的文件一般存放于以下目录:
/target/linux/<arch_name>/base-files/etc/…
该目录的文件和文件夹最终将会存放于设备固件的/etc
目录。
它一般包含下列的文件和文件夹:
- …board.d/:定义设备专用的默认硬件的脚本,如 LED 和网络接口。
- …hotplug.d/:定义设备专用的,在插入热插拔设备时自动运行的脚本
- …init.d/:定义设备专用的在启动时自动运行的脚本
- …uci-defaults/:定义设备专用的 UCI 默认设置
- …diag.sh:定义设备显示的错误代码
/target/linux/<arch_name>/base-files/lib/…
该文件夹下的文件夹和文件对应固件中的/lib
目录下文件夹和文件。
它的子文件夹和文件有:
- …<arch_name>.sh:将 阅读友好的设备名 转化为 脚本安全的设备名 的脚本
- …preinit/:通用 <arch_name> 预初始化脚本
- …upgrade/:通用 <arch_name> 升级脚本
/target/linux/<arch_name>/base-files/sbin
该文件夹对应固件中的/sbin
文件夹, 一般为通用的 <arch_name> sbin 脚本和工具。
/target/linux/<arch_name>/dts/
设备树源文件(Device tree source files,简写为dts)。
/target/linux/<arch_name>/
用于构建写入用镜像的设置文件。
/target/linux/<arch_name>/<board_name>/
设备专用的设置文件。
/target/linux/<arch_name>/modules.mk
menuconfig 中使用的架构专用(Arch-specific)配置文件。
使新设备出现在 make menuconfig 中
在编辑了上述的文件后, 你需要对 Makefile 使用 touch 命令:
1 | touch target/linux/*/Makefile |
DTS(设备树)
Linux 内核从 V2.6 开始引入设备树的概念,其起源于OF:OpenFirmware
, 用于描述一个硬件平台的硬件资源信息,这些信息包括:CPU 的数量和类别、内存基地址和大小、总线和桥、外设连接、中断控制器和中断使用情况、GPIO 控制器和 GPIO 使用情况、Clock 控制器和 Clock 使用情况等等。
以下是 DeepSeek 生成的关于 N60 Pro 的部分 DTS 注释,仅供参考。
1 | // SPDX-License-Identifier: GPL-2.0-or-later OR MIT |
补丁
子目录patches-xxx
是对一个目标版本为 xxx 的内核补丁。
它包含的文件名称(3个数字-全小写字母的简介.patch
)含义如下:
0xx - 上游需要回退的内容补丁
1xx - 等待上游合并的代码补丁
2xx - 内核构建、配置和头文件的补丁
3xx - 用于特定体系结构的补丁
4xx - MTD相关的补丁(系统和设备方面)
5xx - 文件系统相关的补丁
6xx - 网络通用补丁
7xx - 网络物理层驱动补丁
8xx - 其他设备补丁
9xx - 未分类的其他补丁
Uboot
先让我们来看看更常见的 PC 机的启动过程:
- BIOS / UEFI 程序部署在主板特定芯片上,操作系统部署在硬盘上
- 上电后,PC 先执行 BIOS 程序,由 BIOS 程序负责初始化内存、硬盘,将 OS 镜像读取到内存中,最后跳转到内存中执行 OS 直至启动
相对应地,嵌入式系统的启动过程:
- Uboot 程序部署在能作为启动设备的 Flash 上,操作系统部署在 Flash 上
- 上电后,嵌入式系统先执行 Uboot 程序,由 Uboot 程序负责初始化内存、Flash,将 OS 镜像读取到内存中,最后跳转到内存中执行 OS 直至启动
因此,可以将 Uboot 程序类比成 PC 中的 BIOS,引导系统启动。对于这一类程序,更通用的描述是 BootLoader,Uboot 只是其中一种。
不死 Uboot(Breed)是路由器刷机常见的 Uboot 之一,由国内个人hackpascal 开发。
为什么会被称为“不死鸟”?有些官方升级固件自带 Bootloader,如果从官方固件升级,会导致现有 Bootloader 被覆盖。而当 Breed 更新固件时,它会自动删除固件附带的引导加载程序,因此可以防止 Breed 被覆盖。
UCI
UCI
是一个用 C 语言编写的功能组件,为了 集中化 管理运行 OpenWrt 系统的设备的配置文件。
UCI 是在 OpenWrt 历史版本 White Russian 中存在的基于 NVRAM 的配置文件的 替代版本 和 其附带的标准配置文件程序 的封装,例如/etc/network/interfaces
,/etc/exports
,/etc/dnsmasq.conf
,/etc/samba/samba.conf
等。
他们可以通过任何文本编辑器、命令行功能组件uci
、各种编程API(如 Shell,Lua 和 C)实现更改。
LuCI
LuCI 建立于 2008 年 3 月,最初名为“FFLuCI”,作为将 OpenWrt 分支 White Russian 的 Freifunk-Firmware 移植到其继任分支 Kamikaze 的努力的一部分。这个项目的起因是在嵌入式设备上缺乏一个免费、干净、可扩展且易于维护的 Web 用户界面。虽然大多数类似的配置界面都大量使用了 Shell 脚本语言,但 LuCI:
- 使用 Lua 编程语言
- 将接口分割成逻辑部分,如模型和视图,使用面向对象的库和模板。
这确保了更好的性能、更小的安装体积、更快的运行时间和简单的可维护性。
同时,LuCI 从 MVC 框架发展成为一个包含多个库、应用程序和用户界面的集合,适用于 Lua 程序员,同时仍然专注于 Web 用户界面,这也成为了 OpenWrt Kamikaze 8.09 以来的 OpenWrt 官方发布的一部分。
SquashFS
Squashfs(.sfs)是一套供 Linux 核心使用的 GPL 开源只读压缩文件系统。Squashfs 能够为文件系统内的文件、inode 及目录结构进行压缩,并支持最大 1024k 字节的块大小,以提供更大的压缩比。
Squashfs 常被用于各 Linux 发行版的 LiveCD 中,也用于 OpenWrt 和 DD-WRT 的路由器固件。Chromecast 也是该文件系统的用户。在 LiveCD 中,Squashfs 通常与 UnionFS,OverlayFS 和 aufs 等联合挂载文件系统结合使用,以便在 LiveCD 系统中提供可读写支持。Appimage 项目也使用 Squashfs 作为镜像。
OverlayFS
此部分来源于这篇文章。
OverlayFS,顾名思义是一种堆叠文件系统,可以将多个目录的内容叠加到另一个目录上。OverlayFS 并不直接涉及磁盘空间结构,看起来像是将多个目录的文件按照规则合并到同一个目录。且对多个源目录具体使用文件系统类型没有要求,即使各个源目录的文件系统类型不同也不影响使用。
在使用如上 mount 进行 OverlayFS 合并之后,遵循如下规则:
- lowerdir 和 upperdir 两个目录存在同名文件时,lowerdir 的文件将会被隐藏,用户只能看到 upperdir 的文件。
- lowerdir 低优先级的同目录同名文件将会被隐藏。
- 如果存在同名目录,那么 lowerdir 和 upperdir 目录中的内容将会合并。
- 当用户修改 mergedir 中来自 upperdir 的数据时,数据将直接写入 upperdir 中原来目录中,删除文件也同理。
- 当用户修改 mergedir 中来自 lowerdir 的数据时,lowerdir 中内容均不会发生任何改变。因为 lowerdir 是只读的。用户想修改来自 lowerdir 数据时,overlayfs 会首先拷贝一份 lowerdir 中文件副本到 upperdir 中(这也被称作 OverlayFS 的 copy-up 特性)。后续修改或删除将会在 upperdir 下的副本中进行,lowerdir 中原文件将会被隐藏。
- 如果某一个目录单纯来自 lowerdir 或者 lowerdir 和 upperdir 合并,默认无法进行 rename 系统调用。但是可以通过 mv 重命名。
一般 lowerdir 为只读文件系统,upperdir 为可写文件系统,这形成了一个有趣的机制,似乎我们可以修改 lowerdir 下的文件或目录,lowerdir 看上去变成了一个可读写的文件系统。
删除文件和目录
为了支持 rm 和 rmdir 而又不修改 lower 文件系统,需要在 upper 文件系统中记录文件或目录已经被删除。OverlayFS 引入了 whiteout 文件的概念。如果需要删除 lower 层的文件或目录,需要在 upper 层创建一个 whiteout 文件。因此会导致删除文件反而会导致占用空间增加的特性。
OverlayFS 以其独特的优势正得到越来越广泛的应用,OpenWrt 系统也利用 OverlayFS 减少擦写闪存的次数,延长闪存的使用寿命。
编译 OpenWrt
以下操作请使用普通用户,不要使用 root 以及 sudo。
拉取官方源码
1 | git clone https://github.com/openwrt/openwrt.git |
切换分支,一般选择最新的稳定版本:
1 | git branch -a |
更新并安装软件包列表
可能需要使用代理。
1 | ./scripts/feeds update -a |
选取设备文件
终端输入:
1 | make menuconfig |
在出现的菜单中按照目标设备依次选择Target System
,Subtarget
与Target Profile
。
挑选内核模块与软件包
这是 OpenWrt 编译过程中最激动人心的时刻。你可以随意挑选自己所需的软件包与内核模块。
我们可能会加入如下类别的软件包:
- Base system:基础包,如
dnsmasq
、firewall4
等 - Administration:权限相关以及数据监控等管理功能
- Boot Loaders
- Firmware:固件支持,请根据设备芯片选择
- Libraries:支持库文件
- LuCI:包括 LuCI 模块、应用、主题等
- Network:网络相关包
- Utilities:其他基础工具软件
别忘了选择对应的内核模块(在 Kernel modules 中):
我们还可以在界面中键入/
(和 Vim 一样)以查询软件包。
可能会安装的内核模块 / 软件包
kmod-tun
:TUN 模块luci-theme-argon
:这是一个较为用户友好的 LuCI 主题,需要手动安装。wpad
:是wpad-basic
的完整版,支持 WPA 企业热点(PEAP 认证)登录。odhcp6c
:轻量级 DHCPv6 和 RA 客户端- 如选用
dnsmasq-full
请不要加入odhcpd
与odhcpd-ipv6only
包
- 如选用
ca-bundle
与ca-certificate
:SSL 支持
编译前操作
安装依赖
以 Debian 系为例:
1 | sudo apt update |
预下载
使用make download
在最终构建前下载所有依赖,并激活多线程编译。可能需要使用代理。
正式编译
经过了这么多的准备工作,我们终于要迎来最后一步了:
1 | make -jN V=s |
-jN
: make 命令可以加上-j
参数用于指定使用多少 CPU 核编译,可以加速编译过程。例如:make download -j4
,make -j5
。V=s
:make命令可以加上V=s
以输出更多的编译错误信息。
在实际的编译过程中,可能会出现一些错误和警告。例如某个包没有安装,package 依赖冲突等等。根据提示的信息进行查询和解决即可。
编译完成后,我们可以在bin/targets/xxx
目录下找到我们最终需要的文件。
其中,
- …imagebuilder…tar.zst:Image Builder
- …uboot.fip:Uboot FIP(Firmware Image Package)分区映像
- …initramfs-recovery.itb:临时的 InitramFS 恢复镜像(用于安装到内存中)
- …preloader.bin:Flash BL2 分区映像
- …squashfs-sysupgrade.itb:SquashFS 构建的系统升级镜像
关于 Image Builder
Image Builder(以前称为 Image Generator)是一个预编译环境,适用于创建自定义映像,而无需从源代码编译它们。它下载预编译的软件包并将它们集成到单个可闪存映像中。
在以下情况下,这样做很有用:
- 您想在较小的闪存中容纳更多的封装
- 您想关注开发快照
- 您的设备具有 32MB 或更少的 RAM 并且 opkg 无法正常工作
- 您想批量刷新几十个设备,并且需要一个特定的映像设置
使用 Image Builder 进行编译的速度相比从零开始很快,但定制性会下降。
为了生成 Image Builder,我们在make menuconfig
中勾选Build the Openwrt Image Builder
即可。
使用 Image Builder
解压存档并更改工作目录:
1 | tar -J -x -f openwrt-imagebuilder-*.tar.xz |
选择适当的配置文件,插件和自定义文件夹,将其传递给make image
命令。
1 | make image \ |
make
命令完成后,生成的镜像存放在bin/<设备架构>
目录下,就像编译一样。
下一章节:概述 & OpenWrt 安装