OrangePi Zero2安装OpenWRT做旁路由
买了很多开发板,除了Raspberry Pi 5每天都在用,其他板子:Raspberry Pi Zero W、Raspberry Pi Pico、OrangePi Zero2、ESP32、ESP8266、microbit、合宙Air101都在吃灰。Raspberry Pi Zero W不支持64位,可玩性低,也就OrangePi Zero2还凑合,打算装个OpenWRT做旁路由实现:解锁网*云音乐+广告过滤。
准备工作
- 准备一个可以连接互联网的路由器作为主路由,IP:
192.168.2.1; - ~~Orange Pi官网下载OpenWRT镜像:Orange Pi Zero2,~~Orange Pi官方使用的镜像名为
openwrt-aarch64-opizero2-23.12-linux6.1.62-ext4.img,经测试官方更新包地址已失效,无法更新系统。所以到OpenWRT官网下载,然后用rufus烧录; - 主路由WAN口通过交换机,连接互联网;
- Orange Pi作为旁路由网口连接主路由LAN口;
Orange Pi官网镜像配置
此步可以跳过,因为官网镜像无法更新系统,可玩性低。
- OpenWRT会占用主路由IP,打开http://192.168.2.1,
登录 OpenWrt → 网络 → 接口 → LAN → 编辑 → 常规设置,修改旁路由的IP地址和网关,把IP改为192.168.2.3,把网关改为主路由IP地址,以接入互联网。

DHCP 服务器 → 常规设置关闭旁路由IPV4和IPV6的DHCP。


高级设置中修改DNS服务器。

测试配置效果,网关设置成功,可以访问互联网:

查看固件版本:
root@OpenWrt:~# cat /etc/openwrt_release
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='SNAPSHOT'
DISTRIB_REVISION='r0+24458-1f701cc1dd'
DISTRIB_TARGET='sunxi/cortexa53'
DISTRIB_ARCH='aarch64_cortex-a53'
DISTRIB_DESCRIPTION='OpenWrt SNAPSHOT r0+24458-1f701cc1dd'
DISTRIB_TAINTS='no-all'
OpenWRT官网镜像配置
OpenWrt Firmware Selector下载镜像前,点击自定义预安装软件包和/或首次启动脚本做以下配置:
预安装的软件包
base-files busybox ca-bundle dropbear e2fsprogs firewall4 fstools kmod-nft-offload libc libgcc libopenssl openssl-util libustream-openssl logd mkf2fs mtd netifd nftables odhcp6c odhcpd-ipv6only partx-utils ppp ppp-mod-pppoe procd procd-seccomp procd-ujail uboot-envtools uci uclient-fetch urandom-seed urngd coreutils-nohup bash dnsmasq-full curl ca-certificates ipset ip-full libcap libcap-bin ruby ruby-yaml kmod-tun kmod-inet-diag unzip kmod-nft-tproxy luci-compat luci luci-base openssh-sftp-server fdisk losetup resize2fs kmod-fs-ext4 mount-utils parted kmod-nft-socket luci-i18n-base-zh-cn libatomic1 libev libsodium libpcre2 libudns kmod-netlink-diag libstdcpp6 boost boost-system boost-program_options coreutils-base64 libuci-lua resolveip aria2 luci-app-aria2 luci-i18n-aria2-zh-cn luci-app-samba4 opkg mosquitto-client git make build-essential
首次启动时运行的脚本(uci-defaults)
以下脚本只支持有线接入,不支持PPPOE拨号和WLAN无线接入。
root_password改为OpenWRT系统root密码;
lan_ip_address改为旁路由应设置的IP地址;
network.lan.gateway改为主路由的IP地址;
network.lan.dns改为公网DNS解析地址。
root_password="123456"
lan_ip_address="192.168.2.2"
# log potential errors
exec >/tmp/setup.log 2>&1
if [ -n "$root_password" ]; then
(echo "$root_password"; sleep 1; echo "$root_password") | passwd > /dev/null
fi
# Configure LAN
# More options: https://openwrt.org/docs/guide-user/base-system/basic-networking
if [ -n "$lan_ip_address" ]; then
uci set network.lan.ipaddr="$lan_ip_address"
uci set network.lan.gateway="192.168.2.1"
uci set dhcp.lan.ignore="1"
uci set network.lan.dns="114.114.114.114 199.29.29.29 223.5.5.5"
uci commit network
fi
cat << "EOF" > /etc/uci-defaults/70-rootpt-resize
if [ ! -e /etc/rootpt-resize ] \
&& type parted > /dev/null \
&& lock -n /var/lock/root-resize
then
ROOT_BLK="$(readlink -f /sys/dev/block/"$(awk -e \
'$9=="/dev/root"{print $3}' /proc/self/mountinfo)")"
ROOT_DISK="/dev/$(basename "${ROOT_BLK%/*}")"
ROOT_PART="${ROOT_BLK##*[^0-9]}"
parted -f -s "${ROOT_DISK}" \
resizepart "${ROOT_PART}" 100%
mount_root done
touch /etc/rootpt-resize
reboot
fi
exit 1
EOF
cat << "EOF" > /etc/uci-defaults/80-rootfs-resize
if [ ! -e /etc/rootfs-resize ] \
&& [ -e /etc/rootpt-resize ] \
&& type losetup > /dev/null \
&& type resize2fs > /dev/null \
&& lock -n /var/lock/root-resize
then
ROOT_BLK="$(readlink -f /sys/dev/block/"$(awk -e \
'$9=="/dev/root"{print $3}' /proc/self/mountinfo)")"
ROOT_DEV="/dev/${ROOT_BLK##*/}"
LOOP_DEV="$(awk -e '$5=="/overlay"{print $9}' \
/proc/self/mountinfo)"
if [ -z "${LOOP_DEV}" ]
then
LOOP_DEV="$(losetup -f)"
losetup "${LOOP_DEV}" "${ROOT_DEV}"
fi
resize2fs -f "${LOOP_DEV}"
mount_root done
touch /etc/rootfs-resize
reboot
fi
exit 1
EOF
cat << "EOF" >> /etc/sysupgrade.conf
/etc/uci-defaults/70-rootpt-resize
/etc/uci-defaults/80-rootfs-resize
EOF
sh /etc/uci-defaults/70-rootpt-resize
echo "All done!"
点击请求构建,半分钟左右构建成功。

烧录镜像后,关闭旁路由IPV4和IPV6的DHCP。。
更换国内源
cp /etc/opkg/distfeeds.conf /etc/opkg/distfeeds.conf.backup
vim /etc/opkg/distfeeds.conf
src/gz openwrt_core https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/targets/x86/64/packages
src/gz openwrt_base https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/packages/x86_64/base
src/gz openwrt_luci https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/packages/x86_64/luci
src/gz openwrt_packages https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/packages/x86_64/packages
src/gz openwrt_routing https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/packages/x86_64/routing
src/gz openwrt_telephony https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/24.10.3/packages/x86_64/telephony
opkg update
# 验证更换是否成功
opkg install luci
旁路由作为MQTT Client
LuCI 为OpenWRT增加菜单:/usr/lib/lua/luci/controller/mqttclient.lua
module("luci.controller.mqttclient", package.seeall)
function index()
entry({"admin", "services", "mqttclient"}, cbi("mqttclient"), _("MQTT Client"), 90).dependent = true
end
MQTT Client页面:/usr/lib/lua/luci/model/cbi/mqttclient.lua
local m, s, o
local sys = require "luci.sys"
-- 日志函数(记录到同一个日志文件)
local function log(message)
local f = io.open("/var/log/mqttclient.log", "a")
if f then
f:write(string.format("[%s] %s\n", os.date("%Y-%m-%d %H:%M:%S"), message))
f:close()
end
end
m = Map("mqttclient", translate("MQTT Client"),
translate("Configure MQTT client settings and control connection."))
-- === Broker Settings ===
s = m:section(TypedSection, "mqttclient", translate("Broker Settings"))
s.anonymous = true
o = s:option(Value, "broker_host", translate("Broker Host"))
o.placeholder = "127.0.0.1"
o.default = "127.0.0.1"
o.rmempty = false
o = s:option(Value, "broker_port", translate("Broker Port"))
o.placeholder = "1883"
o.default = "1883"
o.datatype = "port"
o.rmempty = false
o = s:option(Value, "username", translate("Username"))
o.rmempty = true
o = s:option(Value, "password", translate("Password"))
o.password = true
o.rmempty = true
o = s:option(Value, "client_id", translate("Client ID"))
o.placeholder = "openwrt-client"
o.default = "openwrt-client"
o.rmempty = false
-- === Connection Control ===
s = m:section(TypedSection, "mqttclient", translate("Connection Control"))
s.anonymous = true
o = s:option(Button, "_connect", translate("Connect"))
o.inputstyle = "apply"
function o.write(self, section)
log("User pressed Connect button from web interface")
-- 调用 init 脚本并把输出追加到日志
sys.call("/etc/init.d/mqttclient connect >> /var/log/mqttclient.log 2>&1")
end
o = s:option(Button, "_disconnect", translate("Disconnect"))
o.inputstyle = "reset"
function o.write(self, section)
log("User pressed Disconnect button from web interface")
sys.call("/etc/init.d/mqttclient disconnect >> /var/log/mqttclient.log 2>&1")
end
-- === Publish Message ===
s = m:section(TypedSection, "mqttclient", translate("Publish Message"))
s.anonymous = true
o = s:option(Value, "pub_topic", translate("Topic"))
o.placeholder = "test/topic"
o.rmempty = false
o = s:option(Value, "pub_message", translate("Message"))
o.placeholder = "Hello, MQTT!"
o.rmempty = false
o = s:option(Button, "_publish", translate("Publish"))
o.inputstyle = "action"
function o.write(self, section)
local topic = m.uci:get("mqttclient", section, "pub_topic") or ""
local message = m.uci:get("mqttclient", section, "pub_message") or ""
log("User pressed Publish button. Topic: " .. topic .. ", Message: " .. message)
-- 调用 init 脚本发布消息,同时输出到日志
sys.call(string.format(
"/etc/init.d/mqttclient publish '%s' '%s' >> /var/log/mqttclient.log 2>&1",
topic:gsub("'", "'\\''"),
message:gsub("'", "'\\''")
))
end
return m
UCI配置文件:/etc/config/mqttclient
config mqttclient
option broker_host '10.0.0.1'
option broker_port '1883'
option username 'mqtt-username'
option password 'mqtt-password'
option client_id 'openwrt-client'
option pub_topic 'data/test'
option pub_message 'Hello, MQTT.'
后端服务脚本:/etc/init.d/mqttclient
#!/bin/sh /etc/rc.common
START=99
STOP=10
LOG_FILE="/var/log/mqttclient.log"
EXTRA_COMMANDS="connect disconnect publish config"
EXTRA_HELP=" connect Connect to MQTT broker
disconnect Disconnect from MQTT broker
publish Publish a message to a topic
config Show current MQTT client configuration"
USE_PROCD=1
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
uci_get() {
uci get "mqttclient.@mqttclient[0].$1" 2>/dev/null
}
start_service() {
if ! command_exists /usr/bin/mosquitto_sub; then
log "Error: mosquitto_sub not found. Please install mosquitto-client."
return 1
fi
local host=$(uci_get broker_host)
local port=$(uci_get broker_port)
local user=$(uci_get username)
local pass=$(uci_get password)
local client_id=$(uci_get client_id)
log "Starting MQTT client: host=$host, port=$port, client_id=$client_id"
procd_open_instance
procd_set_param command /usr/bin/mosquitto_sub
procd_append_param command -h "$host"
procd_append_param command -p "$port"
[ -n "$user" ] && procd_append_param command -u "$user"
[ -n "$pass" ] && procd_append_param command -P "$pass"
procd_append_param command -i "$client_id"
procd_append_param command -t "#"
procd_set_param respawn
procd_set_param stdout "$LOG_FILE"
procd_set_param stderr "$LOG_FILE"
procd_close_instance
}
stop_service() {
log "Stopping MQTT client"
killall mosquitto_sub 2>/dev/null
}
connect() {
log "Connect button pressed"
start
}
disconnect() {
log "Disconnect button pressed"
stop
}
publish() {
local topic="$1"
local message="$2"
if ! command_exists /usr/bin/mosquitto_pub; then
log "Error: mosquitto_pub not found. Please install mosquitto-client."
return 1
fi
local host=$(uci_get broker_host)
local port=$(uci_get broker_port)
local user=$(uci_get username)
local pass=$(uci_get password)
local client_id=$(uci_get client_id)
log "Publishing to topic: $topic"
if /usr/bin/mosquitto_pub -h "$host" -p "$port" \
${user:+-u "$user"} ${pass:+-P "$pass"} \
-i "$client_id" -t "$topic" -m "$message" >> "$LOG_FILE" 2>&1; then
log "Successfully published to $topic"
else
log "Failed to publish to $topic"
fi
}
config() {
log "=== Dumping MQTT client configuration ==="
log "broker_host=$(uci_get broker_host)"
log "broker_port=$(uci_get broker_port)"
log "username=$(uci_get username)"
log "password=******"
log "client_id=$(uci_get client_id)"
log "pub_topic=$(uci_get pub_topic)"
log "pub_message=$(uci_get pub_message)"
log "=========================================="
}
boot() {
start
}
# 添加执行权限
chmod +x /etc/init.d/mqttclient
# 启动服务
/etc/init.d/mqttclient enable
/etc/init.d/mqttclient restart
# 调试配置读取
/etc/init.d/mqttclient config
# 测试发布消息
/etc/init.d/mqttclient publish "data/test1" "Hello from OpenWrt"
# 查看测试日志
tail -f /var/log/mqttclient.log
测试通过:

稍微改改代码,定时上传心跳,监控设备是否离线。
安装.Net8环境
# 使用Arm64 Alpine
wget https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/8.0.20/aspnetcore-runtime-8.0.20-linux-musl-arm64.tar.gz\
./dotnet --info
Host:
Version: 8.0.20
Architecture: arm64
Commit: 574100b692
RID: linux-musl-arm64
开发测试环境备用。
解锁网*云音乐
本来想安装UnblockNeteaseMusic,后来发现了更好用的YesPlayMusic客户端,所以不再折腾。
安装拦截广告应用AdGuardhome
系统 → SoftWare,在过滤器中输入adguardhome,点击Install安装。
cd /usr/bin/
https://github.com/AdguardTeam/AdGuardHome/releases/download/v0.107.67/AdGuardHome_linux_arm64.tar.gz
tar -zxvf AdGuardHome_linux_arm64.tar.gz
touch /etc/AdGuardHome.yaml
cd /usr/bin/AdGuardHome && ./AdGuardHome

进入WEB管理页:http://192.168.2.2:3000

设置上游DNS
设置 -> DNS设置,上游 DNS 服务器,请求方式改为并行请求。
doh.360.cn/dns-query
dns.alidns.com/dns-query
应用后配置文件保存在/usr/bin/AdGuardHome/AdGuardHome.yaml
停止进程./AdGuardHome,删除/etc/AdGuardHome.yaml
安装OpenWRT AdGuardhome管理
下载luci-app-adguardhome
cd /opt/
wget https://github.com/rufengsuixing/luci-app-adguardhome/releases/download/1.8-9/luci-app-adguardhome_1.8-9_all.ipk
opkg install luci-app-adguardhome_1.8-9_all.ipk
配置OpenWRT AdGuardhome参数
重启设备,之后进入服务 -> AdGuard Home
启用:选中
网页管理端口:81
重定向:作为dnsmasq的上游服务器
配置文件路径:/usr/bin/AdGuardHome/AdGuardHome.yaml
保存并应用之后进入网络 -> DHCP/DNS,发现DNS已经转发到AdGuardhome。

在主路由上设置DNS为旁路由IP地址。

随机访问几个网站,已经存在拦截数据。

ps:测试中发现重启旁路由后,AdGuardhome并不会自动拉起进程,需要手动到AdGuardHome菜单应用一下才可以启动进程。
参考文章: