在小米 BE6500 上从零搭建全屋透明代理(踩坑实录)

把家里的小米 BE6500 折腾成一台”全屋透明代理”路由器:所有设备(有线 + Wi-Fi)自动科学上网、公司内网走 VPN、还顺手做了 WebRTC 防泄漏。过程一波三折,坑一个接一个,这篇把完整链路和每个坑都记下来。

mihomo 网页控制台:全屋设备的流量、连接、内存、延迟一屏尽览

缘起

需求很朴素:让整个家的设备都能科学上网 + 访问公司内网,而不用在每台设备上单独配代理。最自然的落点就是路由器——它是所有设备的必经之路。

手上是小米 BE6500,信心满满准备刷机,结果第一步就被泼了盆冷水。

认清硬件:RN02 的先天不足

小米 BE6500 有两个型号,命运完全不同:

型号 芯片 能刷 OpenWrt?
BE6500 Pro(RD08) 高通 IPQ5322 社区有实验性支持
BE6500 标准版(RN02) 高通 IPQ5312 ❌ 闭源,社区不做

我这台正是标准版 RN02。体检结果:

  • 刷不了 OpenWrt:高通闭源驱动,没法移植
  • 持久空间只剩 ~6MB(/data 分区),装不下 mihomo 内核(解压 ~20MB)
  • 没有 USB 口:物理上无法外挂存储扩容
  • 软件源全 404:opkg 配置指向失效的 QSDK 地址

⚠️ 网上搜”BE6500 刷 OpenWrt”的教程,绝大多数是京东云 BE6500(另一台完全不同的机器),照着刷标准版会变砖。

好在也有转机:

  • SSH 可解锁(root 权限)
  • /dev/net/tuniptablescurl(带 https)都在
  • boot_wait=onuart_en=1,救砖条件具备

整体架构

既然装不下内核,那就从公网服务器拉内核到内存里跑:

1
2
3
4
5
6
7
8
外网 ← 宽带 ← [小米 BE6500 主路由 192.168.31.1]
├─ mihomo 跑在内存 /tmp(开机从 VPS 拉)
├─ 白名单域名 → VLESS 节点(科学上网)
├─ 公司网段 → WireGuard / VLESS(内网)
└─ 其他 → 直连
有线 + Wi-Fi 所有设备,网关都是 .1,自动分流

你的 VPS = 内核 + 配置的"仓库"(HTTP 静态托管)

关键点:这是主路由内置代理(不是旁路由),流量进 PREROUTING 就被分流,无需改设备网关。

第一步:解锁 SSH

标准版稳定固件默认禁 SSH,先用 xmir-patcher(小米路由器解锁神器)拿到 root:

1
2
git clone https://github.com/openwrt-xiaomi/xmir-patcher
cd xmir-patcher && bash run.sh # 依赖装好后,菜单里填路由器 IP、选开启 SSH

macOS 上装 xmir-patcher 会踩两个编译坑:

  1. ssh2-python 需要 cmakebrew install cmake
  2. 编译出的 .so 找不到 libssh2(Library not loaded: @rpath/libssh2.1.dylib)→ 给它补上 brew 的库路径:
1
2
install_name_tool -add_rpath /opt/homebrew/opt/libssh2/lib \
venv/lib/python3.*/site-packages/ssh2/*.so

拿到 SSH 后再固化,让它重启也在:删掉 dropbear 启动脚本里”稳定版不开 SSH”的判断,并用社区的 auto_ssh.sh 注册成 firewall include 开机自启(顺带备份 host key,避免每次重启指纹变动):

1
2
3
# 删掉那段 channel 判断,让 dropbear 无条件启动
sed -i '/flg_ssh=.*ssh_en.*/,/fi/d' /etc/init.d/dropbear
/etc/init.d/dropbear enable && /etc/init.d/dropbear start

新版 macOS 连老式 ssh-rsa 主机密钥要加参数,否则报 no matching host key:

1
ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa root@192.168.31.1

第二步:内核从内存跑(持久服务设计)

内核只能塞进内存(/tmp),但内存重启就清空。所以要一套”开机自动拉、挂了自动救”的持久机制。

这台没有 systemd——小米固件是 OpenWrt 系的 procd/init.d,没法 systemctl enable。得用等价方案:

systemd 世界 这台路由器
WantedBy=multi-user.target(开机自启) firewall include(开机/防火墙重载时触发脚本)
Restart=always(挂了自动重启) cron 看门狗(每分钟检查,死了就拉起)
.service 单元文件 /data/proxy/bootstrap.sh

核心 bootstrap.sh 逻辑:

  1. 从 VPS 仓库 curl 拉 mihomo(armv7 静态)+ config 到 /tmp
  2. 健康检查:内核起来、端口监听后,应用路由规则
  3. 下载失败绝不改路由(不断网,沿用上次配置)
  4. 应用 fake-ip DNS 劫持 + 透明代理 iptables 规则

注册开机自启(firewall include,相当于 systemctl enable):

1
2
3
4
5
uci set firewall.proxy_boot=include
uci set firewall.proxy_boot.type=script
uci set firewall.proxy_boot.path=/data/proxy/bootstrap.sh
uci set firewall.proxy_boot.enabled=1
uci commit firewall

看门狗(cron 每分钟,相当于 Restart=always;内核挂了/内存被清/重启后没起来 → 自动重拉重启):

1
2
echo '* * * * * /data/proxy/watchdog.sh' >> /etc/crontabs/root
/etc/init.d/cron restart

分工:/data(持久,~6MB)只放脚本;/tmp(内存,断电即失)放内核和配置——开机第一件事就是把内核重新拉进内存。
这台是 armv7(32 位),内核必须下 mihomo-linux-armv7,不是 arm64。

第三步:透明代理——TPROXY 走不通

第一个大坑:想用 TPROXY(TCP+UDP 全透明),结果:

1
iptables v1.6.2: unknown option "--on-port"

排查发现:内核有 xt_TPROXY 模块,但缺用户态库 libxt_TPROXY.so,原厂固件没带,又没软件源补装。TPROXY 这条路死了。

退而求其次用 REDIRECT(nat 表),只支持 TCP。对白名单网站代理够用(基本都是 TCP/HTTPS),UDP(QUIC)不走代理会自动回退 TCP。

第四步:TUN 模式与那些拦路的坑

后来为了支持 UDP,换 TUN 模式(fake-ip + 虚拟网卡)。这一步坑最多,逐个记录:

坑 1:GeoIP 库下载卡死

mihomo 的 fallback-filtergeoip 默认是 true,会去 GitHub 下 MMDB,国内直接卡 90 秒超时。显式关掉:

1
2
3
dns:
fallback-filter:
geoip: false # 默认 true,会下载 GeoIP 库卡死

坑 2:FORWARD 默认 DROP

TUN 需要把 LAN 流量转发进 utun 网卡,而路由器防火墙 FORWARD 默认策略是 DROP,utun 不在任何区域 → 流量被拦,到不了 mihomo。手动放行:

1
2
iptables -I FORWARD 1 -o utun -j ACCEPT
iptables -I FORWARD 1 -i utun -j ACCEPT

坑 3:IPv6 泄漏

现象:公司内网(IPv4)能通,外网全废。抓包发现设备优先走 IPv6,而 tun 只路由 IPv4 → IPv6 流量绕过整个代理直接出去被墙。关掉 LAN 的 IPv6:

1
2
3
uci set dhcp.lan.ra=disabled
uci set dhcp.lan.dhcpv6=disabled
uci commit dhcp && /etc/init.d/odhcpd restart

坑 4:DNS 用国外 DoH 会超时

配置里 nameserver 用了国外 DoH(public.dns.iij.jp 之类),国内直连连不上 → mihomo DNS 解析超时 → fake-ip 被拖垮 → 外网全废。换国内 DNS:

1
2
3
4
dns:
nameserver:
- 223.5.5.5
- 119.29.29.29

fake-ip 负责代理路由,代理域名由节点在远端解析,根本不需要国外 DoH。

坑 5:MTU 黑洞

部分网站转圈打不开。tun 口 mtu 默认 9000,而实际出口(WG 1380 / VLESS ~1400)放不下大包 → 被丢。加 MSS 钳制 + 降 mtu:

1
2
ip link set utun mtu 1400
iptables -t mangle -A FORWARD -o utun -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

坑 6:僵尸进程

频繁 restart 会留下多个 mihomo 实例抢 7893/9090 端口,导致 API 打到旧实例、行为诡异(google 时好时坏)。stop() 改成杀全部:

1
for p in $(ps | grep '[m]ihomo' | awk '{print $1}'); do kill -9 "$p"; done

第五步:公司内网

公司同时提供 WireGuard 和 L2TP/IPsec。但这台路由器只有纯 L2TP/PPTP,没有 IPsec 协议栈,连不了 L2TP/IPsec。于是走 mihomo 的 WireGuard outbound,把公司网段路由过去:

1
2
3
rules:
- IP-CIDR,10.x.0.0/24,company-wg,no-resolve
- IP-CIDR,172.16.x.0/23,company-wg,no-resolve

⚠️ 落在私有段(10/172.16/192.168)内的公司子网,还得在 iptables 层单独放行给 mihomo,否则被”内网直连”规则挡掉。

另一个坑:fake-ip 模式下域名规则优先于 IP 规则。公司某域名走了 DIRECT 规则,把 IP 规则盖掉了,改成指向 WG 才通。

第六步:WebRTC 防泄漏

透明代理的经典难题:WebRTC 靠 STUN(UDP)问”我的公网 IP”,可能暴露真实 IP。处理:

  1. IPv6 已关(前面那步),堵掉 IPv6 候选
  2. DROP 直连 STUN:WAN 出口封掉常用 STUN 端口,WebRTC 要么走代理 STUN(拿到节点 IP),要么拿不到,永远不暴露真实 IP
1
2
iptables -A FORWARD -o eth0.1 -p udp -m multiport \
--dports 3478,3479,5349,5350,19302 -j DROP

踩坑合集(TL;DR)

给赶时间的人一份速查:

现象 根因 解法
unknown option --on-port 缺 libxt_TPROXY.so 改用 REDIRECT 或 TUN
启动卡 90 秒 GeoIP 库下载超时 geoip: false
内网通、外网全废 IPv6 泄漏 / DNS 超时 关 IPv6 + 换国内 DNS
LAN 设备连不上代理 FORWARD 默认 DROP 放行 utun 转发
部分网站转圈 MTU 黑洞 MSS 钳制 + 降 mtu
行为诡异、时好时坏 僵尸 mihomo 进程 stop 时杀全部
公司域名不走 VPN fake-ip 下域名规则优先 域名规则指向 WG

结语

一台”刷不了机、6MB 空间、没 USB”的封闭盒子,最后硬是做成了全屋透明代理:有线 + Wi-Fi 全设备、公司内网、WebRTC 防泄漏、开机自愈、网页控制台一应俱全。

核心思路:内核放内存、配置放公网、路由靠 fake-ip + 透明劫持。硬件再弱,只要 SSH 能进、tun 能用,就有的玩。