OpenWrt 旁路由运行 mihomo 内核:透明代理部署要点
梳理主路由与旁路由的网关分工、mihomo 内核直跑的目录与服务配置、透明代理与 DNS 接管要点,以及断网回退的预案。
一、网络拓扑:先让一台终端经过旁路由
OpenWrt 旁路由部署 mihomo,包含三个独立环节:终端把流量交给旁路由,防火墙把连接送进内核,内核按规则选择出站。只启动 mihomo,或者只修改 DNS,都不等于完成透明代理。
本文采用单网口、同网段的 IPv4 测试拓扑,以 OpenWrt 23.05、24.10 系列常见的 firewall4 与 nftables 环境为基线。以下地址是完整示例,执行前应与实际网段逐项核对。已经运行 OpenClash、其他透明代理插件或自定义策略路由的设备,不应直接叠加这些规则。
| 设备 | 地址或设置 | 职责 |
|---|---|---|
| 主路由 | 192.168.10.1/24 | 宽带拨号、出口 NAT、继续提供 DHCP |
| OpenWrt 旁路由 | 192.168.10.2/24 | 运行 mihomo,接收测试终端流量 |
| 旁路由默认网关 | 192.168.10.1 | 供内核连接节点及直连上游 |
| 测试终端 | 192.168.10.100/24 | 网关与 DNS 均设为 192.168.10.2 |
| 旁路由 LAN 接口 | br-lan | 接收同一局域网内的测试流量 |
网关与 DHCP 的分工
- 在 OpenWrt 的“网络”→“接口”→“LAN”→“编辑”中设置静态地址、IPv4 网关和 DNS。确认旁路由自身能够访问网络。
- 在同一界面的“DHCP 服务器”→“常规设置”中勾选“忽略此接口”,避免主路由、旁路由同时发放地址。
- 先只修改测试终端的 IPv4 网关和 DNS。不要立即把主路由 DHCP 的网关选项改为旁路由地址。
- 检查“网络”→“防火墙”中的 LAN 区域策略。本文假定常见的 LAN 输入、输出和区域内转发均允许;受限网络需要另行设计放行规则。
同网段旁路由的直连响应可能由主路由直接返回终端,不一定再次经过旁路由。这种非对称路径会影响连接统计与状态防火墙判断。初次测试应保留主路由原有出口 NAT,不要为了“路径对称”直接在整个 LAN 上增加地址伪装。
二、内核目录:先验证二进制与最小配置
mihomo 是 Clash Meta 延续发展的代理内核,不是 OpenWrt 管理插件。直跑内核意味着配置目录、进程守护、订阅更新和防火墙规则均由管理员维护。应按 CPU 架构选择 Linux 二进制,不能仅凭设备商品名判断。
ubus call system board
uname -m
df -h /overlay
free -m
mkdir -p /etc/mihomo
chmod 700 /etc/mihomo
chmod 755 /usr/bin/mihomo
/usr/bin/mihomo -v
这些命令假定已把解压后的内核放到 /usr/bin/mihomo。例如 aarch64 通常对应 ARM64,但 ARMv7、MIPS 设备还需确认指令集、字节序和浮点 ABI。出现 Exec format error 时优先检查架构,而不是修改防火墙。版本号以本机 mihomo -v 输出为准,并保留这条输出用于后续排查。
配置文件:先用 DIRECT 验证接管链路
将以下内容保存为 /etc/mihomo/config.yaml。这是可测试的直连基线,没有配置代理节点,因此不会把流量送到代理服务器。先验证流量能够进入内核,再接入自己的节点和策略组,可以把网络问题与订阅问题分开。
mixed-port: 7890
tproxy-port: 7893
allow-lan: true
bind-address: 192.168.10.2
mode: rule
log-level: info
ipv6: false
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: redir-host
nameserver:
- udp://223.5.5.5:53
- udp://119.29.29.29:53
rules:
- MATCH,DIRECT
7890 是 HTTP/SOCKS 混合代理入口,7893 是 TPROXY 入口,1053 是 mihomo DNS 监听端口。这里使用 redir-host,避免首次部署同时引入 Fake-IP 地址池与额外路由。两个上游 DNS 是示例中的直连解析器,并不等于加密 DNS。
chmod 600 /etc/mihomo/config.yaml
/usr/bin/mihomo -t -d /etc/mihomo -f /etc/mihomo/config.yaml
必须先通过配置测试,再启用流量接管。本文基线没有 GEOIP、GEOSITE 或规则集依赖;若换成订阅配置,还需检查相应数据文件、规则提供者下载地址以及策略组引用。面向桌面客户端生成的订阅,也可能带有不适合路由器的 DNS、TUN 或控制接口设置。
三、进程守护:用 procd 启动 mihomo
OpenWrt 使用 procd 管理服务。不要把长期进程直接挂在 SSH 会话里,也不要在计划任务中每分钟重复启动。下面的脚本负责启动、异常退出后重启及日志接入,不负责安装透明代理规则。
把以下内容保存为 /etc/init.d/mihomo:
#!/bin/sh /etc/rc.common
START=95
STOP=10
USE_PROCD=1
start_service() {
/usr/bin/mihomo -t -d /etc/mihomo \
-f /etc/mihomo/config.yaml || return 1
procd_open_instance
procd_set_param command /usr/bin/mihomo \
-d /etc/mihomo -f /etc/mihomo/config.yaml
procd_set_param respawn 3600 5 5
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param file /etc/mihomo/config.yaml
procd_close_instance
}
chmod 755 /etc/init.d/mihomo
/etc/init.d/mihomo enable
/etc/init.d/mihomo start
logread -e mihomo
ss -lnptu | grep -E ':(7890|7893|1053)\b'
若系统没有 ss,可用 netstat -lnptu 检查监听。日志出现端口占用时,应先查找旧代理进程、重复服务或 DNS 插件;不要随意换一个端口却忘记同步修改防火墙。
四、透明代理:TPROXY、策略路由与防火墙分别做什么
Linux 上的 TPROXY 可以接收 TCP 和 UDP,并保留原始目标地址。它需要三部分共同工作:nftables 选择连接,策略路由把带标记的数据包交给本机,mihomo 的透明监听接收连接。只有 tproxy-port 配置而没有前两项,终端流量不会自动进入内核。
以下示例只匹配 192.168.10.100,仅用于已确认没有冲突的测试环境。执行前运行 ip -4 rule show、ip -4 route show table 100 和 nft list ruleset,确认路由表 100、规则优先级 100 和标记位 0x1 未被其他服务使用。多 WAN、VPN 与其他代理经常已经占用这些资源。
内核模块与转发开关
使用对应固件软件源安装提供 nftables TPROXY 支持的模块;采用 opkg 的固件通常可检查 kmod-nft-tproxy。软件包与运行内核版本必须匹配。出现 Operation not supported 或找不到 TPROXY 表达式时,应先处理模块问题。
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.send_redirects=0
sysctl -w net.ipv4.conf.br-lan.send_redirects=0
ip -4 rule add priority 100 fwmark 0x1/0x1 lookup 100
ip -4 route add local 0.0.0.0/0 dev lo table 100
关闭发送 ICMP Redirect,可避免旁路由建议终端改走同网段主路由。不要忽略接口级设置。若设备启用了严格反向路径过滤,还需要检查 net.ipv4.conf.all.rp_filter 与 net.ipv4.conf.br-lan.rp_filter;策略路由环境中,严格模式可能丢弃合法数据包,应按网络安全要求调整为宽松模式或关闭。
只接管一个源地址的 nftables 规则
把以下内容保存为 /etc/mihomo/test-tproxy.nft。这是独立测试表,不应直接粘贴到 firewall4 的表内 include 文件中。首次加载前先检查语法,再实际安装;已有同名表时不要重复加载。
table inet mihomo_test {
chain ingress_proxy {
type filter hook prerouting priority mangle; policy accept;
iifname != "br-lan" return
ip saddr != 192.168.10.100 return
meta l4proto { tcp, udp } th dport 53 return
ip daddr {
0.0.0.0/8,
10.0.0.0/8,
100.64.0.0/10,
127.0.0.0/8,
169.254.0.0/16,
172.16.0.0/12,
192.168.0.0/16,
224.0.0.0/4,
240.0.0.0/4
} return
meta l4proto { tcp, udp } counter \
tproxy to :7893 meta mark set 0x1 accept
}
chain ingress_dns {
type nat hook prerouting priority dstnat; policy accept;
iifname "br-lan" ip saddr 192.168.10.100 \
meta l4proto { tcp, udp } th dport 53 \
counter redirect to :1053
}
}
nft -c -f /etc/mihomo/test-tproxy.nft
nft -f /etc/mihomo/test-tproxy.nft
nft list table inet mihomo_test
DNS 的 53 端口先从透明代理链返回,再由后面的 NAT 链重定向到 1053,避免普通 DNS 请求先被 TPROXY 接走。私有地址和组播地址直接放行,便于保留路由器管理、局域网设备访问等基本功能;如果组织内部还有公网地址段或专用路由,也应纳入排除范围。
nftables 测试链中的 accept 不会跳过 firewall4 后续的其他基础链。TPROXY 数据包保留原始目标端口,不能仅放行“目标端口 7893”来替代输入策略。本文使用 LAN 输入允许的测试环境;采用 LAN 输入拒绝策略时,需要在 firewall4 中为 LAN 来源、相应标记的 TCP/UDP 数据包建立受限输入放行规则,并单独允许 DNS 1053。
五、DNS 接管:区分传统解析、加密 DNS 与解析回环
终端网关和 DNS 都指向旁路由,并不意味着所有域名解析都会进入 mihomo。上述规则接管的是经过旁路由的 TCP/UDP 53 请求;浏览器的安全 DNS、系统的 Private DNS,以及应用内置 DoH,可能通过 443 或 853 端口访问其他解析器。
- 基础测试:暂时关闭测试终端的加密 DNS,明确检查传统 DNS 链路,再恢复相关功能验证策略。
- 端口分工:保留 OpenWrt dnsmasq 的 53 端口,mihomo 使用 1053。不要让两个进程争抢同一个监听地址和端口。
- 上游回环:不要让 mihomo 的上游指向 dnsmasq,同时又把 dnsmasq 的上游指回 mihomo。这会造成解析循环或超时。
- 节点域名:增加代理节点后,检查节点服务器域名的解析路径。使用
proxy-server-nameserver可单独指定节点域名解析器,避免建立代理连接前还依赖该代理。 - 局域网域名:需要解析 DHCP 主机名时,应为内部域配置专门的解析策略,并确认没有形成上述回环。
示例只按测试终端源地址接管 DNS,因此旁路由自己发出的 DNS 不会被同一规则再次截获。若后续把规则扩大到所有源地址,必须重新审查内核上游、节点连接与设备本机流量的豁免条件,不能简单删除源地址限制。
TUN 模式为什么不是额外再开一个开关
mihomo 也支持 TUN,但 TUN 需要相应的内核设备支持、路由与 DNS 接管设计。auto-route、auto-redirect 等设置可能改变现有路由和 nftables 规则,不能与本文的手工 TPROXY 方案直接叠加。单网口旁路由还要验证转发流量是否真正进入 TUN,而不是只验证路由器本机请求。
如果选用 TUN,应先撤销本文的测试表和策略路由,再按当前 mihomo 版本的 TUN 配置重新部署。两种方式的目标相同,都是把需要处理的连接送入内核;排查时保持一条明确的接管路径,比同时启用多个入口更容易定位问题。
六、验收顺序:从直连基线到代理策略
先确认旁路由自身可联网,再验证终端流量。示例基线的最终规则是 MATCH,DIRECT,出口 IP 与普通直连一致是正常现象,不能据此认定没有接管。应结合计数器和内核日志判断。
- 配置检查:运行
mihomo -t,确认没有 YAML 缩进错误、未知策略组或规则提供者加载失败。 - 显式代理检查:在测试终端执行下方 curl 命令。该步骤只验证 7890 入口,不验证透明接管。
- DNS 检查:向
192.168.10.2查询域名,观察 DNS NAT 计数器是否增长。NAT 链计数反映新连接匹配,不等于每个 DNS 数据包的数量。 - 透明连接检查:关闭终端上的显式代理,再访问公开网站,检查 TPROXY 计数器及
logread -e mihomo中的连接记录。 - 代理出站检查:导入有效节点,配置真实策略组并替换最终规则后,重复请求。确认日志中的出站名称与预期一致,再测试大文件、视频和 UDP 应用。
# 在测试终端执行
curl -I --proxy http://192.168.10.2:7890 https://example.com/
nslookup example.com 192.168.10.2
curl -I --noproxy '*' https://example.com/
# 在旁路由执行
ip -4 rule show
ip -4 route show table 100
nft list table inet mihomo_test
logread -e mihomo
浏览器正常而游戏异常,常见原因包括 UDP 接管缺失、节点不支持 UDP、应用使用不同地址族。小网页正常但大连接停顿,则应继续检查 MTU、路径 MTU 发现和链路丢包。不要直接把这些问题都归为 DNS 故障,也不要在缺少日志时随意调整内核参数。
测试期间可在“网络”→“防火墙”中暂时关闭软件、硬件流量卸载,减少快速路径对观察结果的干扰。确认方案稳定后,再逐项恢复并复测。本文没有提供实测吞吐数字:设备 CPU、节点协议、加密算法和出口带宽都会影响结果,应以本机相同条件下的重复测试为准。
七、断网回退:先恢复网关,再撤销接管规则
把全网 DHCP 网关改为旁路由后,旁路由断电不会让客户端自动改回主路由。procd 能处理进程异常退出,但不能修复硬件掉电、错误规则或上游失联。因此回退方案应在扩大部署范围之前准备好。
单终端测试的恢复步骤
- 把测试终端的 IPv4 网关和 DNS 改回
192.168.10.1,或恢复由主路由 DHCP 自动获取。 - 在旁路由删除测试 nftables 表,停止继续拦截连接。
- 仅删除本次创建的策略路由和本地路由,然后停止内核。
- 重新建立网络连接或清理终端 DNS 缓存,再确认直连恢复。已经建立的代理连接通常需要重新连接。
nft delete table inet mihomo_test
ip -4 rule del priority 100 fwmark 0x1/0x1 lookup 100
ip -4 route del local 0.0.0.0/0 dev lo table 100
/etc/init.d/mihomo stop
/etc/init.d/mihomo disable
只在确认这些对象由本文实验创建时执行删除命令。不要使用 nft flush ruleset 清空整机规则,也不要清空整张策略路由表,这会影响 firewall4、VPN 和其他网络服务。
扩大到全网前的维护清单
- 保存主路由原有 DHCP 网关、DNS 设置,并记录租约更新时间;恢复选项后,旧租约终端仍可能继续使用旁路由。
- 保留不依赖旁路由的管理入口,例如固定地址的维护电脑,以及主路由的管理地址。
- 订阅更新采用“下载到临时文件 → 配置测试 → 替换 → 重启”的顺序,失败时保留上一份可用配置。
- 检查磁盘空间和日志量,避免长期开启 debug 把有限的闪存空间耗尽。
- 实际演练一次内核停止和旁路由断电,验证客户端恢复步骤,而不是只测试正常启动。
稳定部署的判断标准不是服务显示“运行中”,而是网关、DNS、透明接管、规则出站和故障回退都能分别验证。先用一台终端完成闭环,再逐步扩大范围,可以明显缩小每次变更的影响面。