OpenWrt 旁路由跑 mihomo 内核:透明代理部署流程概览
梳理在 OpenWrt 主路由或旁路由上直接运行 mihomo 内核的整体流程,包括内核放置、配置目录、透明代理转发与局域网设备网关设置。
先确定部署边界:内核、网关与透明代理是三件事
在 OpenWrt 上直接运行 mihomo,不等于安装了一个带界面的 Clash 客户端。内核负责读取配置、建立代理连接和执行规则;系统负责把终端流量送进内核。启动日志正常,只能证明进程已经运行,不能证明局域网设备正在使用代理。
本文以 OpenWrt 23.05、24.10 常见的 firewall4 与 nftables 环境为背景,采用 Linux TUN 加自动路由的部署思路。具体字段是否可用,以实际 mihomo 版本的配置检查结果为准。旧版 firewall3、厂商定制防火墙和已有代理插件,不应直接套用同一套接管规则。
- 主路由部署:OpenWrt 本来就是终端默认网关,重点是内核接管、防火墙放行与 DNS 转发。
- 旁路由部署:OpenWrt 与主路由处于同一局域网,还需要调整测试终端的网关与 DNS。
- 显式代理测试:应用主动连接
7890混合端口,只用于先验证节点与规则,不代表透明代理已经完成。
规划旁路由地址与终端网关
先用一台终端验证,不要立即修改整个局域网的 DHCP 下发参数。下面的地址是一套完整示例;实际部署时,应按现有子网统一调整,不能只改其中一个地址。
| 设备或参数 | 示例值 | 用途 |
|---|---|---|
| 局域网网段 | 192.168.10.0/24 | 主路由、旁路由与终端处于同一子网 |
| 主路由 | 192.168.10.1 | 连接互联网,继续提供 DHCP |
| OpenWrt 旁路由 | 192.168.10.2 | 静态地址,避免与 DHCP 地址池冲突 |
| 旁路由默认网关 | 192.168.10.1 | 内核连接节点及直连目的地的上游 |
| 测试终端 | 192.168.10.100 | 保留原有子网掩码 |
| 终端网关与 DNS | 192.168.10.2 | 将测试终端流量与解析请求交给旁路由 |
LuCI 中先检查「网络」→「接口」→「LAN」→「编辑」的静态地址、IPv4 网关和 DNS 设置。单网口旁路由通常通过 br-lan 访问主路由;双网口主路由则可能通过 PPPoE 或其他 WAN 设备出站,不能根据网卡名称猜出口。
ip -4 addr show
ip -4 route show
ip route get 223.5.5.5
cat /etc/resolv.conf
旁路由自身的默认路由应指向主路由,而不是指向自身。初次部署时,旁路由系统 DNS 先使用可直接访问的解析服务,避免出现“内核启动需要解析节点域名,但解析又依赖尚未启动的内核”的循环。
主路由保留唯一的 DHCP 服务即可。旁路由不要再向同一广播域发放另一套地址。IPv6 也必须单独规划:即使手工改了终端 IPv4 网关,终端仍可能通过主路由的 IPv6 路由通告直接出站。
放置 mihomo 内核与配置目录
确认架构、内核模块和存储空间
先检查 CPU 架构与固件信息,再选择对应的 Linux 二进制。x86_64、aarch64 与不同 MIPS 变体不能混用;部分发布包还区分指令集等级。文件名看起来相近,不代表能够在同一设备上执行。
uname -m
ubus call system board
df -h /overlay /tmp
opkg list-installed | grep -E 'kmod-tun|firewall4|nftables'
在使用 opkg 的官方固件上,可通过 opkg update 与 opkg install kmod-tun 安装 TUN 模块。模块必须与当前固件内核匹配;遇到依赖不匹配时,应修复固件与软件源对应关系,不要强制安装其他内核版本的模块。自动接管所需的 nftables 功能也取决于固件编译选项。
固定程序路径与工作目录
将已解压且架构匹配的内核上传至 /usr/bin/mihomo,配置放入 /etc/mihomo/config.yaml。配置可能包含订阅凭据,目录与文件不应对普通用户开放。设备的持久空间不足时,应先规划外部存储,而不是把正式配置长期放在重启会清空的 /tmp。
mkdir -p /etc/mihomo
chmod 700 /etc/mihomo
chmod 755 /usr/bin/mihomo
chmod 600 /etc/mihomo/config.yaml
/usr/bin/mihomo -v
/usr/bin/mihomo -t -d /etc/mihomo -f /etc/mihomo/config.yaml
-d 指定工作目录,-f 指定配置文件,-t 只检查配置。若配置引用 GeoIP、GeoSite、规则集或代理提供者文件,还需检查相应路径、读取权限和更新网络。首次排障可以先去掉这些外部依赖,避免把下载失败误判成 TUN 故障。
先验证显式代理,再启用 TUN 接管
推荐分两次启动。第一次使用实际可用的节点配置,将 tun.enable 设为 false,在路由器本机通过混合端口测试连接。第二次确认节点可用后,再开启 TUN 与自动路由。这样可以把节点问题和转发问题分开。
下面展示一个最小结构,用于检查配置与直连基线。它没有代理节点,策略组也只有 DIRECT,因此不会产生代理出口。正式使用时,应合并订阅中的有效节点或 proxy-providers,并将实际节点加入策略组;不要直接覆盖已有完整配置。
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
ipv6: false
tun:
enable: false
device: mihomo
stack: mixed
auto-route: true
auto-redirect: true
auto-detect-interface: true
dns-hijack:
- any:53
dns:
enable: true
listen: 127.0.0.1:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
default-nameserver:
- 223.5.5.5
nameserver:
- 223.5.5.5
- 119.29.29.29
proxies: []
proxy-groups:
- name: 出口选择
type: select
proxies:
- DIRECT
rules:
- IP-CIDR,192.168.10.0/24,DIRECT,no-resolve
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- MATCH,出口选择
先在前台运行 /usr/bin/mihomo -d /etc/mihomo -f /etc/mihomo/config.yaml,通过另一条 SSH 会话观察端口。如果固件已安装 curl,可执行下列请求;该命令只检验路由器本机到混合端口的链路,不检验局域网透明转发。
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/
导入真实节点后,确认「出口选择」不再停留在 DIRECT,并结合日志中的匹配规则、策略组和节点名称判断请求路径。单看网页是否打开,无法区分直连成功与代理成功。
打通 TUN、策略路由与 OpenWrt 防火墙
显式代理通过后,将 tun.enable 改为 true,重新检查配置并启动。auto-route 负责建立接管所需路由,Linux 下的 auto-redirect 使用 nftables 等机制协助流量重定向。自动生成的路由表编号、规则优先级和链名可能随版本变化,不应把网上某一组固定编号当作通用标准。
ip link show mihomo
ip rule show
ip route show table all
nft list ruleset
sysctl net.ipv4.ip_forward
确认三类系统条件
- TUN 设备存在:应能看到配置中指定的
mihomo设备。若缺失,先检查/dev/net/tun、模块和启动权限。 - 路由接管生效:对比启停前后的
ip rule、路由表和 nftables 规则。内核日志不应持续出现创建接口或添加规则失败。 - 转发未被防火墙阻断:IPv4 转发应启用,LAN 到 TUN 的转发路径也要被允许。只看到 TUN 接口,并不能证明 firewall4 已经允许转发。
在 LuCI「网络」→「防火墙」→「常规设置」检查区域与转发。采用独立 TUN 区域时,可按实际界面将设备 mihomo 关联到该区域,并允许 LAN 到该区域的转发;区域的入站权限与转发权限是两回事。不要为了测试直接把 WAN 入站改为接受。
不同固件对非托管设备的区域绑定方式、自动重定向与 firewall4 的衔接存在差异。因此,这一步必须结合当前规则集判断,不宜复制一份固定的 iptables 脚本。尤其不要同时追加 REDIRECT、TPROXY 和自动 TUN 三套接管规则;本文流程采用的是 TUN 路线。
单臂旁路由还要观察同接口转发、回程路径与连接跟踪。部分直连流量可能从 br-lan 进入后又从同一接口发给主路由。如果出现只通单向、连接间歇中断,应抓包确认源地址和回包路径,再决定是否需要针对性源地址转换;不要先对整个 LAN 盲目开启伪装。
首次排障建议在「网络」→「防火墙」中暂时关闭软件流量分载与硬件流量分载。待链路稳定后再逐项恢复并复测。旁路由可以上网而终端不能上网时,优先检查转发路径,不要先更换节点。
安排 DNS 转发,避免解析回环与 IPv6 旁路
示例中的 DNS 链路是:终端访问旁路由 53 端口,由 dnsmasq 转发到本机 mihomo 的 1053 端口,再访问上游解析器。端口分开后,dnsmasq 与 mihomo 不会争抢同一个监听地址。
在 LuCI「网络」→「DHCP/DNS」→「常规设置」中,将 DNS 转发目标设为 127.0.0.1#1053;在对应的高级设置中检查“不使用上游提供的解析服务器”选项。界面标签会随 LuCI 版本变化,关键是让 dnsmasq 不再把请求同时交给其他自动获得的上游。
# 先备份,以下操作针对第一个 dnsmasq 实例
cp /etc/config/dhcp /etc/config/dhcp.before-mihomo
uci set dhcp.@dnsmasq[0].noresolv='1'
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server='127.0.0.1#1053'
uci commit dhcp
/etc/init.d/dnsmasq restart
nslookup example.com 127.0.0.1
这组命令会替换该实例已有的 DNS 转发列表。若原来配置了企业内网域名的条件转发,应先记录并保留相应条目;多 dnsmasq 实例也不能直接假定第一个就是 LAN 使用的实例。终端还需确认其 DNS 地址确实是 192.168.10.2。
区分普通 DNS、fake-ip 与加密 DNS
- fake-ip:使用此模式时,查询可能返回
198.18.0.0/16中的地址。这是虚拟映射,不是目标网站的真实公网地址;对应连接必须继续由内核接管。 - 普通 DNS 劫持:
any:53用于接管相应的普通 DNS 请求,不代表能识别并重定向所有解析协议。 - DoH 与 DoT:浏览器安全 DNS、应用内 DoH、TCP 853 的 DoT 不会因为配置了普通 53 端口劫持就自动服从 dnsmasq。测试时应先确认应用解析路径。
- 上游选择:示例使用普通 IP 上游是为了缩短启动依赖链,并未启用加密解析。改用带域名的 DoH 地址时,应另行规划引导解析与节点域名解析。
ipv6: false 只限制内核相应行为,不会关闭终端收到的 IPv6 默认路由。初次验证可以暂时关闭测试终端 IPv6,随后再规划 IPv6 TUN、路由通告和防火墙。不要用“IPv4 出口正确”推断双栈流量都已接管。
通过 procd 管理启动与重启
确认前台运行、DNS 与终端访问都正常后,再注册开机服务。下面的 procd 脚本只负责守护进程,不包含订阅更新或自定义防火墙逻辑。保存为 /etc/init.d/mihomo,不要同时保留另一个插件的同名服务。
#!/bin/sh /etc/rc.common
START=99
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
启动服务前,先结束原先前台运行的内核,避免两个实例争抢端口与 TUN 设备。此脚本默认以 root 运行,因为创建 TUN、配置路由与防火墙需要相应权限;这也意味着配置文件、订阅来源和内核文件都需要严格控制。
修改配置后,先运行 mihomo -t 再执行 /etc/init.d/mihomo restart。脚本中的文件声明不等于自动订阅更新器;更新订阅仍需独立安排下载、配置检查与原子替换。配置包含凭据时,也不要将完整启动日志直接发布到公开渠道。
开机顺序较晚不等于网络已经可用。必须做一次整机重启测试,观察接口上线后是否能解析节点域名、建立连接并恢复终端访问。依赖远程规则集的部署,还要考虑冷启动时外部文件暂时不可达的情况。
按链路验收,并保留回退路径
验收应从路由器本机推进到单台终端,最后才扩展到 DHCP 下发。每次只改变一个变量,记录对应日志与网关设置。测速结果只能说明当时的传输表现,不能替代路由和 DNS 路径检查。
| 现象 | 优先检查 | 判断依据 |
|---|---|---|
| 内核无法启动 | 架构、配置、端口、TUN 模块 | 查看配置检查与启动错误,不先改终端网关 |
| 显式代理正常,终端失败 | 终端网关、TUN 接管、LAN 转发 | 比较终端请求是否进入内核日志 |
| IP 可达,域名访问失败 | dnsmasq、1053 监听、上游解析 | 分别测试旁路由和终端的 DNS 请求 |
| 部分应用仍然直连 | 规则、IPv6、应用内 DNS | 核对实际匹配策略与双栈出口 |
| 防火墙重载后中断 | 自动生成规则是否仍存在 | 对比重载前后 nftables 与策略路由 |
| 重启后无法恢复 | 服务启用状态、接口时序、冷启动依赖 | 从系统日志定位第一处失败 |
单终端通过后再扩展部署
- 终端先访问主路由和旁路由管理地址,确认局域网直连正常。
- 检查终端 IPv4 默认网关与 DNS 均指向旁路由,清理旧 DNS 缓存,再建立新的浏览器连接。
- 分别验证应当直连与应当代理的请求,查看对应规则和节点,不只观察页面加载结果。
- 确认 UDP 应用、IPv6 策略及防火墙重载后的表现,再调整主 DHCP 服务的网关和 DNS 下发值。
正常停止后,检查 TUN 接口、策略路由和自动生成的防火墙规则是否已清理。若仍有残留,依据保存的基线恢复;不要远程执行清空整个 nftables 规则集的命令。需要清理运行状态时,可在恢复持久配置后安排重启。
OpenWrt 旁路由部署的核心不是把二进制放到路由器上,而是把终端网关、DNS、流量接管、规则出口和服务生命周期接成一条可验证的链路。按这个顺序完成检查,后续更换订阅、调整分流或升级内核时,才能明确问题发生在哪一层。