Skip to content

Q3000 + R4S + Nikki:国内直连、国外按需代理的分流教程

一套不依赖爱快付费“IP 归属地”的家庭网络方案。国内和普通境外服务由 Q3000 直接访问;只有命中代理域名表的请求才交给 R4S 上的 Nikki/Mihomo。本文既讲配置,也解释每一步为什么这样做。

本文记录的是一套已经实际运行并解决淘宝、微信等国内应用卡顿问题的配置。它适合以下场景:

  • Q3000(爱快系统)作为主路由,负责拨号、DHCP、VLAN、Wi-Fi 和 NAT。
  • R4S 刷入 ImmortalWrt/OpenWrt,并安装 Nikki。
  • 希望国内流量尽量不进入 R4S,国外受限服务自动代理。
  • 希望保留 Tailscale 子网路由、出口节点等功能。
  • 不购买爱快“IP 归属地”功能。

文中的地址是实际示例。照做前,请先把它们换成自己的网段。本文默认读者已经能登录 Q3000 和 R4S,不再讲网线连接、SSH 登录和 LuCI 登录。

一、最终效果

配置完成后,普通客户端会获得:

项目示例值由谁提供
客户端 IP192.168.10.100Q3000 DHCP
默认网关192.168.10.1Q3000
DNS 服务器192.168.10.3R4S 上的 dnsmasq
Q3000 地址192.168.10.1主路由
R4S 地址192.168.10.3DNS 分类器、代理入口、Tailscale 节点
Mihomo Fake-IP 段198.18.0.0/16Mihomo

最重要的一句话是:

网关走 Q3000,DNS 问 R4S;只有 DNS 返回 198.18.x.x 的目标,Q3000 才把后续连接送到 R4S。

因此:

  • 淘宝、微信、银行、视频网站等国内服务,通常从 Q3000 直接出 WAN。
  • 能在国内直连的普通境外网站,也从 Q3000 直接出 WAN。
  • Google、TikTok 等命中代理域名表的服务,自动交给 R4S/Nikki/Mihomo。
  • R4S 不再是所有设备的默认网关,也不再处理绝大多数国内连接。

二、先弄懂四个组件的关系

很多配置混乱,源头不是某个选项,而是把 R4S、Nikki、Mihomo 当成了同一个东西。它们的关系如下:

text
Q3000(独立主路由)
├─ WAN 拨号与 NAT
├─ LAN/VLAN/Wi-Fi
├─ DHCP
└─ 静态路由:198.18.0.0/16 → R4S

R4S(硬件设备)
└─ ImmortalWrt/OpenWrt(操作系统)
   ├─ dnsmasq:本方案的前置 DNS 分类器
   ├─ Tailscale:独立的组网服务
   └─ Nikki:OpenWrt 上的代理管理插件
      ├─ 管理配置文件和防火墙规则
      └─ 启动、停止并管理 Mihomo
         ├─ 真正执行 Fake-IP、代理规则和节点连接
         └─ 向 Zashboard 提供控制 API

用通俗的话说:

  • R4S 是一台小电脑。
  • ImmortalWrt/OpenWrt 是它的操作系统。
  • Nikki 是系统里的管理工具,负责准备运行环境并叫醒 Mihomo。
  • Mihomo 才是真正执行代理和规则匹配的核心程序。
  • Zashboard 只是查看和操作 Mihomo 的网页控制台,不负责转发流量。
  • Tailscale 与 Nikki 是平级服务,不包含在 Nikki 里面。

所以,“流量经过 R4S”并不等于“流量经过 Mihomo”;只有被 Nikki 防火墙规则接管的连接,才会进入 Mihomo。

三、旧方案为什么会让国内 App 卡顿

旧方案把客户端的默认网关和 DNS 都设置为 192.168.10.3

text
手机/电脑 → R4S → Nikki/Mihomo 判断 → DIRECT 或代理

Mihomo 会根据 YAML 中的域名规则、IP 规则、GeoIP、规则集和最终 MATCH 判断走向。例如:

yaml
- RULE-SET,cn_domain,DIRECT
- RULE-SET,cn_ip,DIRECT,no-resolve
- MATCH,PROXY

这里的 DIRECT 只表示“不走代理节点”,不表示“绕开 R4S”。国内连接仍然先进入 R4S,再从 R4S 的同一个 LAN 网口送回 Q3000 出网,返回包也要经过相应路径。这种单臂旁路由折返会同时受到以下因素影响:

  • Nikki 的透明代理和 nftables 规则;
  • Fake-IP 与真实 IP 的映射;
  • DNS 劫持和 dnsmasq;
  • UDP/TCP 的不同接管方式;
  • 同一个物理接口上的来回转发;
  • 应用自身的多域名、多 IP、HTTP/3 和长连接。

这不代表传统旁路由一定不能用,但在这套设备、固件和配置组合中,曾出现淘宝卡住、微信打不开、手机提示网络异常,而国外代理流量反而正常的现象。问题的关键是:即使最终判定为 DIRECT,国内数据包仍然已经进入 R4S 的复杂转发路径。

四、新方案的核心原理

新方案把“是否需要送进 R4S”的判断提前到 DNS 阶段。

R4S 的 dnsmasq 中放入约四千多条代理域名规则。每条规则类似:

text
server=/google.com/127.0.0.1#1053

含义是:

  • 如果查询的域名命中列表,把 DNS 查询交给 Mihomo 的 1053 端口;
  • 如果没有命中,交给阿里公共 DNS,返回真实 IP。

1. 国内或普通直连域名

text
手机查询 taobao.com
→ R4S dnsmasq 没有命中代理域名表
→ 阿里 DNS 返回真实 IP
→ 手机连接真实 IP
→ 默认网关是 Q3000
→ Q3000 直接从 WAN 出网

这个过程中,R4S 只回答了一次 DNS。真正的业务流量没有进入 R4S、Nikki 或 Mihomo。

2. 需要代理的域名

text
手机查询 google.com
→ R4S dnsmasq 命中代理域名表
→ 查询转交 Mihomo:1053
→ Mihomo 返回 198.18.x.x Fake-IP
→ 手机连接这个 Fake-IP
→ 默认网关先到 Q3000
→ Q3000 静态路由把 198.18.0.0/16 送给 R4S
→ Nikki 防火墙接管连接
→ Mihomo 恢复原域名并选择代理节点

因此,并不是所有流量第一时间经过 Nikki。进入 Nikki 的主要是被标记成 Fake-IP 的连接。

3. 能直连的境外服务

一个域名只要没有命中代理列表,就会获得真实 IP,并由 Q3000 直接访问。它是不是“中国 IP”并不重要,所以不需要爱快付费的“IP 归属地”功能。

这也回答了一个常见问题:国外服务不等于都要代理。可直连的境外网站不会因为 R4S 停机而失去转发路径,但如果客户端 DNS 仍指向已经停机的 R4S,新域名仍会因为无法解析而打不开。后文会用备用 Wi-Fi 解决这个故障场景。

4. 这四千多条域名够不够

这份表不是“全世界所有国外域名”,而是社区维护的、通常需要代理的域名集合。它的覆盖面比旧方案的完整 YAML 规则窄:

  • 旧方案:所有连接先进入 Mihomo,再由完整规则体系决定 DIRECT 或代理。
  • 新方案:只有前置域名表命中的连接进入 Mihomo,其余连接直接走 Q3000。

优点是国内路径简单、稳定、延迟低;代价是新出现或未收录的受限域名可能被当作直连而失败。遇到漏网域名时,把它加入自定义代理域名表即可。

五、准备工作与地址规划

开始前至少准备:

  • Q3000 已能正常拨号上网;
  • R4S 已安装 ImmortalWrt/OpenWrt、Nikki 和 Mihomo 内核;
  • Nikki 已导入一份本身可用的订阅/YAML;
  • R4S 能使用 curl 下载文件;
  • 手边有一个完全直连的 Wi-Fi,方便配置失误时继续管理设备;
  • 已导出 Q3000 配置,并备份 R4S 的 /etc/config

本文地址规划:

设备/网段地址
Q3000 LAN1192.168.10.1/24
R4S LAN192.168.10.3/24
Q3000 DHCP 池192.168.10.100-192.168.10.200
客户端网关192.168.10.1
客户端 DNS192.168.10.3
Mihomo Fake-IP198.18.0.0/16

**安全提醒:**修改网关、DHCP、静态路由和防火墙可能导致暂时失联。先备份,再改一项、验证一项。不要把订阅链接、节点信息、SSH 密码、API 令牌或 Mihomo 控制密钥粘贴到公开文章和公开对话中。

六、配置 R4S 的基础网络

R4S 使用固定地址:

text
IP:192.168.10.3
掩码:255.255.255.0
网关:192.168.10.1
DNS:可先用 192.168.10.1 或可靠的国内 DNS

在 LuCI 中通常位于:

text
网络 → 接口 → LAN → 编辑 → 常规设置

R4S 不再负责给家庭主网分配地址,因此关闭 LAN 接口上的 DHCP 服务,但不要关闭 dnsmasq 服务本身。本方案仍需要 dnsmasq 在 192.168.10.3:53 回答 DNS。

通常路径:

text
网络 → 接口 → LAN → 编辑 → DHCP 服务器 → 忽略此接口

确认 R4S 自身可以访问互联网:

sh
ping -c 3 223.5.5.5
nslookup github.com 223.5.5.5

七、配置 Nikki 与 Mihomo

1. 先让原始 YAML 正常运行

先在 Nikki 中导入自己的订阅或 YAML,确认节点本身可以连接。本文不提供任何订阅、节点或密钥。

建议基础状态:

项目建议值
TCP 代理方式Redirect
UDP 代理方式TUN
LAN 代理开启
路由器自身代理按需开启
Fake-IP开启
Fake-IP 范围198.18.0.1/16
Mihomo DNS 监听127.0.0.1:1053 或配置实际使用的 1053 监听
大陆 IP 提前绕过关闭
Nikki IPv4 DNS 劫持关闭
Nikki IPv6 DNS 劫持关闭

Nikki 代理配置中 TCP、UDP、DNS 劫持与 IPv4/IPv6 代理选项

R4S 上的 Nikki 代理配置:TCP 使用 Redirect、UDP 使用 TUN,IPv4/IPv6 DNS 劫持关闭。

“大陆 IP 提前绕过”不是绝对不可用,而是不作为本文方案的核心。本文依靠“DNS 分类 + Fake-IP 静态路由”决定哪些连接进入 R4S;再叠加一套大陆 IP 提前绕过,会增加判断层级和排错难度。

2. 为什么要关闭 Nikki 的 DNS 劫持

我们要让 dnsmasq 成为第一层分类器:普通域名走国内 DNS,命中列表的域名才去 Mihomo。如果开启 Nikki 的全局 DNS 劫持,所有查询可能先被重定向到 Mihomo,前置分类就失去意义。

命令行等价设置为:

sh
uci set nikki.proxy.ipv4_dns_hijack='0'
uci set nikki.proxy.ipv6_dns_hijack='0'
uci commit nikki
/etc/init.d/nikki restart

3. OpenWrt 的“DNS 重定向”要不要关

LuCI 中:

text
网络 → DHCP/DNS → 常规 → DNS 重定向

R4S 的 DHCP/DNS 常规设置中 DNS 重定向处于开启状态

这里的 DNS 重定向属于 dnsmasq/OpenWrt,和 Nikki 的 DNS 劫持不是同一个开关。

这个选项把经过 R4S 的客户端 DNS 查询强制拉回 R4S 的 dnsmasq,和 Nikki 的 DNS 劫持不是同一个开关。本方案中,普通客户端已经明确使用 192.168.10.3 作为 DNS,而默认网关又是 Q3000,因此该选项不是核心。可以保留开启;关键是关闭 Nikki 的 IPv4/IPv6 DNS 劫持

补充说明:它究竟在什么情况下生效

192.168.10.3 就是 R4S。客户端使用 192.168.10.3 作为 DNS,确实就是主动向 R4S 上的 dnsmasq 查询。这种正常查询不需要“DNS 重定向”帮忙,因为目的地址本来就是 R4S。

“DNS 重定向”处理的是另一种情况:客户端没有使用 DHCP 下发的 DNS,而是自行查询其他 DNS。例如在旧方案中,客户端默认网关也是 R4S:

text
客户端默认网关:192.168.10.3
客户端手动 DNS:8.8.8.8

客户端 → 准备访问 8.8.8.8:53
       → DNS 请求先经过 R4S
       → R4S 的 DNS 重定向规则将它拦截
       → 改送给 R4S 自己的 dnsmasq

此时,即使客户端手动填写 8.8.8.8,传统的 UDP/TCP 53 端口查询仍可能被 R4S 拉回本机,因此这个开关在“所有请求都经过 R4S”的旧方案中很有作用。

当前方案的路径不同:

text
客户端默认网关:192.168.10.1(Q3000)
客户端手动 DNS:8.8.8.8

客户端 → Q3000 → 8.8.8.8

这个查询根本没有经过 R4S,所以 R4S 上的 DNS 重定向看不到、也拦不住它。结果是:

  • DNS 分类被绕过;
  • 需要代理的域名不会获得 198.18.x.x Fake-IP;
  • Q3000 的 Fake-IP 静态路由不会被触发;
  • Google 等服务会被当成普通直连,通常无法访问;
  • 在国内直接使用 8.8.8.8 还可能遇到超时、污染或不稳定。

浏览器 DoH、Android 私人 DNS、iCloud 专用代理一类加密 DNS 也可能绕过传统的 53 端口重定向。因此,当前方案依赖客户端使用 Q3000 DHCP 下发的 192.168.10.3。从当前拓扑的实际作用看,R4S 的“DNS 重定向”关闭即可;即使保留开启,也不能阻止以 Q3000 为网关的客户端绕过 R4S DNS。

4. YAML 和 Nikki UI 谁优先

Nikki 启动时可能把“混入配置”合并到原始 YAML,再生成真正交给 Mihomo 的运行配置。因此:

  • 原始 YAML 是基础;
  • Nikki UI 和混入配置可能覆盖或补充它;
  • 最终运行结果要看 Nikki 生成的配置、UCI 设置以及防火墙状态;
  • 不要直接修改 /etc/nikki/run/config.yaml,它可能在重启后重新生成。

例如,YAML 写了 Fake-IP,但 Nikki UI 又把 DNS 模式改掉,最后应以运行配置为准。排查时可查看:

sh
uci show nikki
grep -nE 'enhanced-mode|fake-ip-range|listen' /etc/nikki/run/config.yaml
nft list ruleset | grep -i nikki

八、在 R4S 安装“分流 DNS”

本文附带三份脚本:

text
scripts/update-gfw-dnsmasq.sh
scripts/install-split-dns.sh
scripts/rollback-split-dns.sh

它们不会包含你的订阅、密码或节点。安装脚本会先备份 /etc/config/dhcp/etc/config/nikki、dnsmasq 附加目录和 root 定时任务。

1. 上传脚本

把下面两份文件上传到 R4S 的 /tmp

text
update-gfw-dnsmasq.sh
install-split-dns.sh

执行:

sh
chmod +x /tmp/update-gfw-dnsmasq.sh /tmp/install-split-dns.sh
/tmp/install-split-dns.sh

脚本完成以下工作:

  1. 创建带时间戳的备份目录,并把路径写入 /root/last-split-dns-backup
  2. 让 dnsmasq 读取 /etc/dnsmasq.d
  3. 把普通域名上游设为 223.5.5.5223.6.6.6
  4. 关闭 DNS Rebind 保护,使 dnsmasq 接受 Mihomo 返回的 198.18.x.x Fake-IP。
  5. 允许经过路由的局域网客户端使用该 DNS。
  6. 关闭 Nikki 的 IPv4/IPv6 DNS 劫持。
  7. 下载代理域名表,生成指向 127.0.0.1#1053 的规则。
  8. 每天 04:17 自动更新域名表。
  9. 重启 cron、Nikki 和 dnsmasq。

关闭 Rebind 保护是为了接受 Fake-IP,会降低 dnsmasq 对“公网域名返回私有/保留地址”的防护。本方案的 R4S 位于 Q3000 内网,DNS 端口不得暴露到 WAN;防火墙应只允许可信 LAN/Tailscale 使用。

2. 检查结果

sh
wc -l /etc/dnsmasq.d/gwf-gfwlist.conf
grep -c '^server=/' /etc/dnsmasq.d/gwf-gfwlist.conf
dnsmasq --test
grep update-gfw-dnsmasq /etc/crontabs/root
uci get nikki.proxy.ipv4_dns_hijack
uci get nikki.proxy.ipv6_dns_hijack

期望看到:

  • 域名规则数量大于 3000,实际数量会随上游更新变化;
  • dnsmasq --test 返回语法正确;
  • 两个 Nikki DNS 劫持值都是 0
  • root 定时任务中存在更新命令。

3. 检查 DNS 分类

在 R4S 上执行:

sh
nslookup taobao.com 127.0.0.1
nslookup google.com 127.0.0.1

预期:

  • 淘宝返回真实公网 IP;
  • Google 返回 198.18.x.x 一类 Fake-IP。

4. 手动补充漏网域名

如果某个确定需要代理的域名没有命中,可新建:

sh
cat >/etc/dnsmasq.d/gwf-custom.conf <<'EOF'
server=/example.com/127.0.0.1#1053
server=/example.net/127.0.0.1#1053
EOF

dnsmasq --test && /etc/init.d/dnsmasq restart

一条 server=/example.com/... 通常也匹配其子域名。添加前应先确认它确实需要代理;把国内 CDN 域名误加进去,可能重新引入国内 App 变慢的问题。

九、配置 Q3000

不同爱快版本的文字可能略有差异,但功能位置基本一致。

1. LAN1 与 DHCP

进入:

text
网络设置 → DHCP 设置 → DHCP 服务端

主网配置为:

text
接口:LAN1
地址池:192.168.10.100 - 192.168.10.200
网关:192.168.10.1
首选 DNS:192.168.10.3
备用 DNS:留空
租期:120 分钟(可按需调整)

Q3000 主网 DHCP 服务端的地址池、网关与 DNS 设置

Q3000 主网 DHCP:默认网关为 192.168.10.1,首选 DNS 为 R4S 的 192.168.10.3,备用 DNS 留空。

备用 DNS 留空是有意的。若填入公共 DNS,部分手机和电脑会自行选择备用 DNS,绕过 R4S 的域名分类,表现为同一网站时好时坏。

2. 检查静态 DHCP 绑定

这是本次实际遇到的重要问题:DHCP 服务端已经把网关改为 .1,但 PC 反复禁用、启用网卡后,网关仍然是 .3。原因是该 PC 有一条静态 DHCP 绑定,里面单独写了旧网关。

逐条检查静态分配/终端绑定:

text
IP:按原计划保留
网关:192.168.10.1
DNS1:192.168.10.3
DNS2:留空

不要只检查全局 DHCP;终端专属设置通常优先于全局值。

3. 添加 Fake-IP 静态路由

进入:

text
网络设置 → 静态路由

添加:

项目
名称NikkiFakeIP
目标网段198.18.0.0/16
下一跳192.168.10.3
出口接口LAN1/自动选择正确 LAN
状态启用

Q3000 中指向 R4S 的 Fake-IP 与 Tailscale 静态路由

Q3000 当前静态路由:Fake-IP 网段以及 Tailscale/远端 LAN 的回程路由都指向 R4S。

这条路由就是整套方案的“搬运工”:Q3000 看到目标是 198.18.x.x,便知道这个地址不是从 WAN 直接访问,而应交给 R4S 解释。

4. 不需要配置的项目

本方案不需要:

  • 爱快付费“IP 归属地”;
  • 用五元组规则判断所有国外 IP;
  • 把默认路由或全网网关改成 R4S;
  • 按国家建立大量 IP 路由;
  • 为 GWF-5G 单独创建“全部下一跳 R4S”的策略路由。

5. Q3000 能否单独封禁或限制某台客户端

旧方案中,所有客户端先把流量交给 R4S,R4S 再转发或代理。若 R4S 对这些连接做了源地址转换,Q3000 看到的来源就可能全部是 192.168.10.3,从而无法在主路由上准确区分手机、电脑和电视。

当前方案中,每台客户端的默认网关都是 Q3000。对于国内和普通境外直连流量,路径为:

text
192.168.10.100(PC)   → Q3000 → WAN
192.168.10.101(手机) → Q3000 → WAN
192.168.10.102(电视) → Q3000 → WAN

Q3000 可以直接看到每台设备原来的 IP 和 MAC,因此可以分别查看在线状态、禁止上网、设置上网时段、进行访问控制或配置固定地址。

访问 Google 等代理服务时,第一段仍然经过 Q3000:

text
客户端 → Q3000 → 198.18.x.x → R4S/Mihomo

因此,如果 Q3000 使用客户端 IP/MAC,在流量进入转发路径时执行“终端禁止上网”或同类整机封禁,通常会在连接抵达 R4S 之前将其拦截。整机封禁不仅对国内和可直连服务有效,对 Google 等代理流量通常也同样有效。

但 Mihomo 接管连接后,会以 R4S 的身份重新连接代理节点:

text
R4S/Mihomo → 代理节点 → 目标网站

这个真正从 WAN 发出的外层代理连接,在 Q3000 看来通常来自 192.168.10.3。因此,“能否封禁设备”和“能否精确统计代理流量”是两件不同的事:

Q3000 上的操作国内/直连流量Google 等代理流量
按 IP/MAC 禁止整台设备上网有效通常同样有效,因为第一段先经过 Q3000
按 IP/MAC 设置上网时间有效只要规则作用于客户端转发入口,通常有效
查看每台设备的直连连接和流量可以准确区分代理出口部分可能统计到 R4S
对单台设备精确限制代理出口带宽可以直接限制直连部分是否准确取决于 Q3000 限速规则的作用阶段,需要实测
按最终网站域名进行封禁可使用 Q3000 的相关能力可能受 Fake-IP 和代理封装影响,宜在 Mihomo 规则中处理

为了让设备控制长期稳定,建议在 Q3000 中按 MAC 给需要管理的设备分配固定 IP,再使用 Q3000 的终端控制或 ACL。配置后应同时测试一个国内网站和一个代理网站,并查看规则命中情况;不同爱快版本中的“终端限速”“流控分流”和“访问控制”可能工作在不同阶段,不能仅凭界面上已经保存规则就判断代理流量一定被准确计量。

十、让客户端重新获取网络参数

完成 Q3000 配置后,让手机断开并重新连接 Wi-Fi;Windows 可执行:

bat
ipconfig /release
ipconfig /renew
ipconfig /flushdns
ipconfig /all

正确结果是:

text
IPv4:192.168.10.x
默认网关:192.168.10.1
DNS:192.168.10.3

这组结果看起来“不对称”,其实正是设计目标:

  • 网关 .1 决定业务流量默认从 Q3000 出去;
  • DNS .3 只负责给域名分类和返回地址。

DNS 服务器不是默认网关,它们完全可以是两台不同设备。

十一、从一次请求看完整运行过程

场景 A:打开淘宝

  1. 手机向 192.168.10.3 查询淘宝相关域名。
  2. dnsmasq 未在代理域名表中命中。
  3. dnsmasq 向阿里 DNS 查询,返回真实 IP。
  4. 手机把业务包发给默认网关 192.168.10.1
  5. Q3000 直接 NAT 到 WAN。
  6. R4S、Nikki 和 Mihomo不参与业务转发。

场景 B:打开 Google

  1. 手机向 R4S 查询域名。
  2. dnsmasq 命中代理域名表,把查询送到 Mihomo 1053
  3. Mihomo 返回 198.18.x.x
  4. 手机把连接交给 Q3000。
  5. Q3000 命中 198.18.0.0/16 → 192.168.10.3 静态路由。
  6. R4S 上的 Nikki 防火墙捕获连接。
  7. Mihomo 根据 Fake-IP 映射还原域名,并按 YAML 选择代理组和节点。
  8. 返回数据沿连接状态回到手机。

场景 C:访问可直连的境外网站

  1. 域名未命中代理表。
  2. 客户端得到真实 IP。
  3. Q3000 直接访问该境外 IP。
  4. R4S 不参与业务流量。

场景 D:应用直接写死 IP

如果应用完全不做 DNS 查询,而是直接连接某个真实 IP,就没有域名可供前置分类。这种连接默认由 Q3000 直连。若它必须代理,需要额外的 IP 路由、策略规则,或让该设备改用旧式“全量进入 Mihomo”的网络。不要为了极少数情况把整个主网重新改成全量旁路由。

十二、Zashboard 为什么看不到国内连接

旧方案中,所有流量都进入 Mihomo,因此 Zashboard 能看到淘宝、微信等国内连接,并显示最终走 DIRECT。

新方案中,国内业务包从 Q3000 直接出 WAN,不进入 Mihomo,所以 Zashboard 通常只能看到代理域名产生的连接。它不是丢包,也不是监控坏了,而是说明分流发生在 Mihomo 之前。

查看全网连接应使用 Q3000 的终端/连接监控;查看代理连接和节点选择才使用 Zashboard。

十三、备用直连 Wi-Fi 有没有必要

建议保留三个用途不同的网络:

Wi-Fi用途网关/DNS思路
GWF-5G日常自动分流网关 Q3000,DNS R4S
WRT-5G故障应急、完全直连独立 VLAN,由 Q3000 提供网关和公共/运营商 DNS
Guest-5G访客隔离独立访客 VLAN

主网虽然不是把默认网关设成 R4S,但 DNS 依赖 R4S。R4S 停机后,已经建立的国内 IP 连接可能暂时继续,新的域名查询却会失败。备用 Wi-Fi 的价值就是完全不依赖 R4S:

  • 使用独立 VLAN;
  • 网关指向该 VLAN 的 Q3000 地址;
  • DNS 使用 Q3000、运营商 DNS 或可信公共 DNS;
  • 不添加“下一跳 R4S”的策略路由;
  • Fake-IP 静态路由即使是全局路由,也不会产生 Fake-IP,因为该 VLAN 不使用 R4S DNS。

所以备用 Wi-Fi 不是为了日常分流,而是为了 R4S 重启、升级、配置失败时还能访问国内网络和管理设备。

十四、Tailscale 的兼容配置

Tailscale 和 Nikki 是 R4S 上两个独立服务:Tailscale 建立虚拟专网,Nikki 管理 Mihomo 代理。当前分流方案不会自动删除 Tailscale 设置,但回程路由必须正确。

1. Q3000 添加 Tailscale 回程路由

如果 R4S 关闭 Tailscale SNAT(NoSNAT=true),Q3000 必须知道怎样回到 Tailscale 地址和远端 LAN。

添加:

名称目标网段下一跳
TailscaleCGNAT100.64.0.0/10192.168.10.3
R2SRemoteLAN192.168.110.0/24192.168.10.3

第一条让家庭 LAN 设备能把响应送回 Tailscale 客户端;第二条让本地设备知道公司/远端 LAN 位于 R4S 的 Tailscale 通道后面。

2. Tailscale 出口节点能否继续使用代理

可以,但需要 Nikki 接管来自 Tailscale 接口的流量。检查 Nikki 入站接口中是否包含 tailscale,并确认防火墙允许 Tailscale 与 LAN/WAN 互通。

逻辑是:

text
远端设备 → Tailscale 隧道 → R4S tailscale 接口
→ Nikki 透明代理规则(若包含该接口)
→ Mihomo 按 YAML 判断 DIRECT 或代理

如果不希望出口节点流量经过代理,就不要让 Nikki 接管 Tailscale 入站,或添加明确的绕过规则。这里没有“一律正确”的开关,取决于出口节点的用途。

3. 为什么远程桌面可能真的变流畅

这不一定是错觉。旧方案把本地大量国内流量也送进 R4S/Nikki,增加 R4S 的连接跟踪、DNS、透明代理和单臂折返负担。新方案让绝大多数国内连接由 Q3000 直接处理,R4S 更空闲,Tailscale 的 CPU、队列和连接跟踪竞争都可能减少。

不过远程桌面还受到宽带上行、运营商互联、Wi-Fi、Tailscale 是 direct 还是 DERP 等影响。要验证是否真实提速,可在相同时间段对比:

sh
tailscale ping 对端Tailscale地址
tailscale status

direct 表示两端直接打洞,通常更快;relay/DERP 表示经中继,延迟一般更高。

4. 手机访问远端 LAN 慢,是否一定与本方案有关

不一定。若 R4S 到远端 R2S 和远端 LAN 都很快,而手机到远端慢,应继续检查:

  • 手机到 R4S 是 direct 还是 DERP;
  • 手机移动网络是否限制 UDP;
  • 远端 R2S 是否正确发布 192.168.110.0/24
  • Tailscale 管理后台是否批准子网路由;
  • 远端设备的默认网关和回程路由;
  • 公司侧曾做过的端口映射或防火墙规则。

手机自身运行 Tailscale 时,访问 192.168.110.0/24 通常由手机 Tailscale 客户端直接送入隧道,不一定经过家庭 Q3000 的分流路径。因此不能仅凭“手机慢”认定是当前 DNS 分流造成的。

十五、完整验收清单

按顺序验证,不要只测试一个网站:

检查项正确结果
客户端默认网关192.168.10.1
客户端 DNS192.168.10.3
淘宝/微信/银行 App正常且无明显卡顿
普通国内网站正常
Google/TikTok可通过代理访问
国内域名解析返回真实公网 IP
代理域名解析返回 198.18.x.x
Q3000 Fake-IP 路由198.18.0.0/16 → 192.168.10.3
Nikki DNS 劫持IPv4、IPv6 均关闭
Zashboard主要看到代理连接,看不到多数国内连接
WRT-5GR4S 停机时仍可直连国内网络
Tailscale需要时显示 direct,远端子网可达

建议重启一次 R4S,再让客户端重新联网,确认配置不会只在当前运行状态下生效。

十六、常见故障与排查

1. 淘宝、微信卡住,Google 却正常

优先检查:

text
客户端网关是不是还在指向 192.168.10.3?
静态 DHCP 绑定里是不是保留了旧网关?
国内域名是不是错误返回了 198.18.x.x?
Nikki DNS 劫持是不是又被打开?

不要一上来反复修改 YAML 节点规则。先确认国内业务流量有没有真的绕开 R4S。

2. PC 更新租约后网关仍是 .3

检查 Q3000 中该 PC 的静态 DHCP/终端绑定。专属绑定中的网关会覆盖 DHCP 服务端的全局网关。修改后再执行 ipconfig /releaseipconfig /renew

3. 手机 DNS 仍是 192.168.10.3

这是正确状态,不是残留配置。R4S 在本方案中仍是 DNS 分类器,只是不再是默认网关。

4. 国内正常,某个受限网站打不开

执行:

sh
nslookup 目标域名 192.168.10.3
grep -F '目标域名' /etc/dnsmasq.d/gwf-gfwlist.conf

若返回真实 IP 且列表没有该域名,将正确的主域名加入 gwf-custom.conf。若已经返回 Fake-IP,再去检查 Q3000 静态路由、Nikki 防火墙、Mihomo 规则和节点。

5. 所有域名都打不开,但直接访问 IP 可能正常

检查 R4S 是否在线、dnsmasq 是否监听 53 端口、Q3000 DHCP 是否仍只发布 R4S DNS:

sh
/etc/init.d/dnsmasq status
netstat -lnup | grep ':53 '
dnsmasq --test
logread -e dnsmasq

然后临时连接 WRT-5G 直连网络,不要急着给主网增加公共 DNS2,否则会破坏稳定分类。

6. Zashboard 没有国内连接

这是设计结果。国内业务没有进入 Mihomo,去 Q3000 查看全网连接。

7. 重启 Nikki 后行为变化

Nikki 的混入配置可能重新生成运行配置。比较:

sh
uci show nikki
grep -nE 'dns:|enhanced-mode|fake-ip-range|listen:' /etc/nikki/run/config.yaml

不要直接编辑运行目录中的 YAML。应修改原始配置或 Nikki UI/混入配置中的对应项目。

8. mmstat.com 这类国内域名怎么判断

本方案不是让 Mihomo 猜它是不是国内域名。dnsmasq 只问一个更直接的问题:“它是否在需要代理的域名表里?”不在列表就返回真实 IP,让 Q3000 直连。这样不需要维护千千万万个国内域名,也不依赖 GeoIP 先查归属地。

9. RULE-SET,cn_ip,DIRECT,no-resolve 还要不要改

它仍可保留在 Mihomo YAML 中,作为已经进入 Mihomo 后的保护规则。no-resolve 表示这条 IP 规则不为了匹配而额外触发 DNS 解析。新方案的关键不在于删改这一条,而是多数国内连接根本不再进入 Mihomo。

十七、回滚方法

1. 回滚 R4S

rollback-split-dns.sh 上传到 R4S 的 /tmp,执行:

sh
chmod +x /tmp/rollback-split-dns.sh
/tmp/rollback-split-dns.sh

脚本读取 /root/last-split-dns-backup,恢复安装前的 DHCP/DNS 与 Nikki 配置,并删除自动更新任务和生成的域名表。

2. 回滚 Q3000 为完全直连

为了最快恢复基本上网:

  1. 保持客户端网关为 Q3000,例如 192.168.10.1
  2. 把 DHCP DNS 临时改为 Q3000、运营商 DNS 或可靠公共 DNS。
  3. 禁用或删除 198.18.0.0/16 → 192.168.10.3 静态路由。
  4. 客户端重新获取 DHCP 并清除 DNS 缓存。

这会失去自动代理,但能先恢复稳定直连。确认网络正常后,再逐层恢复分流,不要同时改动 Q3000、dnsmasq、Nikki 和 YAML。

十八、让 Codex 协助配置的提示词

下面的提示词可以直接改地址后使用。不要在提示词中写明文密码、订阅链接和 API 密钥;需要登录时单独安全提供,并在完成后更换临时凭据。

1. 只读审计现状

text
请只读检查我的 Q3000 与 R4S 当前网络配置,不要修改任何设置。目标方案是:Q3000 做默认网关和 DHCP,R4S 的 dnsmasq 做前置 DNS 分类;未命中代理域名表的请求由 Q3000 直连,命中的域名由 Mihomo 返回 198.18.0.0/16 Fake-IP,再由 Q3000 静态路由送往 R4S。请检查 DHCP、静态 DHCP、静态路由、Nikki DNS 劫持、dnsmasq 上游、Fake-IP 范围、Mihomo 1053 DNS 监听、Tailscale 回程路由,并先给出风险和回滚点。不要显示或记录任何密钥。

2. 实施前备份并分阶段修改

text
请按“先备份、一次只改一层、每层验证、失败自动回滚”的方式实施 Q3000 + R4S 分流。我的主网是 192.168.10.0/24,Q3000 是 192.168.10.1,R4S 是 192.168.10.3,Mihomo Fake-IP 是 198.18.0.0/16。先备份 Q3000 和 R4S 配置;先改 R4S DNS 分类并验证,再改 Q3000 DHCP 与 Fake-IP 静态路由。修改主网网关前提醒我切到备用 Wi-Fi。完成后列出每个 UI 路径、旧值、新值、验证结果和回滚命令。

3. 排查国内 App 卡顿

text
当前表现是国外代理正常,但淘宝、微信或其他国内 App 卡顿。请不要先改节点或重写 YAML。先只读确认客户端实际网关和 DNS、Q3000 静态 DHCP 是否覆盖全局 DHCP、国内域名是否返回真实 IP、Nikki IPv4/IPv6 DNS 劫持是否关闭、国内业务包是否仍进入 Mihomo。请用证据定位是哪一层出错,再提出最小修改,并准备回滚。

4. 排查单个网站未代理

text
某个域名在当前“dnsmasq 域名分类 + Mihomo Fake-IP”方案下无法访问。请检查它及实际使用的子域名/CNAME 是否命中 /etc/dnsmasq.d/gwf-gfwlist.conf,解析结果是实际 IP 还是 198.18.x.x,Q3000 的 198.18.0.0/16 静态路由是否命中,以及连接是否出现在 Zashboard。只有确认是列表漏收后,才把最小必要主域名加入 gwf-custom.conf,并测试 dnsmasq 语法和访问结果。

5. 检查 Tailscale,不动代理主方案

text
请单独诊断 R4S 上的 Tailscale,不修改现有 Nikki/Mihomo 分流。检查 peer 是 direct 还是 DERP、R4S 与远端子网路由器的延迟、子网路由发布/批准状态、NoSNAT 设置,以及 Q3000 是否有 100.64.0.0/10 和远端 LAN 经 192.168.10.3 的回程路由。请区分“家庭分流问题”和“手机运营商/Tailscale 打洞问题”。

6. 生成重装后的复原报告

text
请根据实际运行状态生成一份可复原报告,内容包括 Q3000 的 LAN、DHCP、静态 DHCP、VLAN/Wi-Fi、静态路由;R4S 的网络、dnsmasq、Nikki UCI、Mihomo Fake-IP/DNS 监听、Tailscale 与防火墙;并隐去密码、令牌、订阅地址和节点信息。报告中同时给出验证命令和完整回滚顺序。

十九、重装时只记住这四个核心改动

如果以后重新刷机,整套方案最核心的是:

  1. Q3000 做主网默认网关和 DHCP:客户端网关是 Q3000,不是 R4S。
  2. R4S dnsmasq 做域名分类:普通域名走国内 DNS,代理域名转给 Mihomo 1053
  3. Q3000 添加 Fake-IP 静态路由198.18.0.0/16 → R4S
  4. Nikki 只接管送到 R4S 的连接:关闭 Nikki 全局 DNS 劫持,保留 Mihomo Fake-IP 与代理规则。

YAML、UI 混入、Tailscale 和备用 Wi-Fi 都是在这四个核心之上的功能层。理解这四点,就不容易在重装时又退回“所有流量先过 R4S”的旧结构。

二十、附录:完整脚本

以下脚本与本文附件一致。复制时请保留 Unix 换行符;若从 Windows 编辑器上传,建议先执行 sed -i 's/\r$//' 文件名 清除可能的 CRLF。

1. update-gfw-dnsmasq.sh

sh
#!/bin/sh

set -eu

TARGET_DIR="/etc/dnsmasq.d"
TARGET_FILE="$TARGET_DIR/gwf-gfwlist.conf"
TMP_LIST="/tmp/gwf-gfwlist.$$.txt"
TMP_CONF="/tmp/gwf-gfwlist.$$.conf"
URL_PRIMARY="https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
URL_FALLBACK="https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt"

cleanup() {
    rm -f "$TMP_LIST" "$TMP_CONF"
}
trap cleanup EXIT INT TERM

mkdir -p "$TARGET_DIR"

if ! curl -fsSL --connect-timeout 15 --max-time 120 "$URL_PRIMARY" -o "$TMP_LIST"; then
    curl -fsSL --connect-timeout 15 --max-time 120 "$URL_FALLBACK" -o "$TMP_LIST"
fi

awk '
    BEGIN {
        print "# Generated from Loyalsoldier/v2ray-rules-dat gfw.txt"
        print "# Matching domains are resolved by Mihomo fake-IP DNS."
    }
    {
        gsub(/\r/, "")
        domain = tolower($0)
        if (domain ~ /^[a-z0-9][a-z0-9._-]*[a-z0-9]$/ && domain ~ /\./) {
            print "server=/" domain "/127.0.0.1#1053"
        }
    }
' "$TMP_LIST" | sort -u > "$TMP_CONF"

rule_count="$(grep -c '^server=/' "$TMP_CONF" || true)"
if [ "$rule_count" -lt 3000 ]; then
    echo "Refusing to install incomplete GFW list: $rule_count rules" >&2
    exit 1
fi

if ! dnsmasq --test --conf-file="$TMP_CONF" 2>&1 | grep -q 'syntax check OK'; then
    echo "dnsmasq rejected the downloaded configuration" >&2
    exit 1
fi

if [ -f "$TARGET_FILE" ] && cmp -s "$TMP_CONF" "$TARGET_FILE"; then
    echo "GFW DNS list is already current: $rule_count rules"
    exit 0
fi

PREVIOUS_FILE=""
if [ -f "$TARGET_FILE" ]; then
    PREVIOUS_FILE="$TARGET_FILE.previous"
    cp "$TARGET_FILE" "$PREVIOUS_FILE"
fi

cp "$TMP_CONF" "$TARGET_FILE.new"
mv "$TARGET_FILE.new" "$TARGET_FILE"

if ! dnsmasq --test 2>&1 | grep -q 'syntax check OK'; then
    if [ -n "$PREVIOUS_FILE" ] && [ -f "$PREVIOUS_FILE" ]; then
        mv "$PREVIOUS_FILE" "$TARGET_FILE"
    else
        rm -f "$TARGET_FILE"
    fi
    echo "dnsmasq rejected the generated configuration" >&2
    exit 1
fi

if [ -n "$PREVIOUS_FILE" ]; then
    rm -f "$PREVIOUS_FILE"
fi

/etc/init.d/dnsmasq restart
echo "Installed $rule_count GFW DNS rules"

2. install-split-dns.sh

sh
#!/bin/sh

set -eu

if [ ! -f /tmp/update-gfw-dnsmasq.sh ]; then
    echo "Upload update-gfw-dnsmasq.sh to /tmp first" >&2
    exit 1
fi

BACKUP_DIR="/root/before-split-dns-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -a /etc/config/dhcp /etc/config/nikki "$BACKUP_DIR/"
cp -a /etc/crontabs/root "$BACKUP_DIR/crontab-root" 2>/dev/null || true
cp -a /etc/dnsmasq.d "$BACKUP_DIR/dnsmasq.d" 2>/dev/null || true
cp -a /usr/local/sbin/update-gfw-dnsmasq "$BACKUP_DIR/update-gfw-dnsmasq" 2>/dev/null || true
echo "$BACKUP_DIR" > /root/last-split-dns-backup

mkdir -p /etc/dnsmasq.d /usr/local/sbin

uci set dhcp.@dnsmasq[0].confdir='/etc/dnsmasq.d'
uci set dhcp.@dnsmasq[0].noresolv='1'
uci set dhcp.@dnsmasq[0].rebind_protection='0'
uci set dhcp.@dnsmasq[0].localservice='0'
uci -q delete dhcp.@dnsmasq[0].server || true
uci add_list dhcp.@dnsmasq[0].server='223.5.5.5'
uci add_list dhcp.@dnsmasq[0].server='223.6.6.6'
uci commit dhcp

uci set nikki.proxy.ipv4_dns_hijack='0'
uci set nikki.proxy.ipv6_dns_hijack='0'
uci commit nikki

cp /tmp/update-gfw-dnsmasq.sh /usr/local/sbin/update-gfw-dnsmasq
chmod 0755 /usr/local/sbin/update-gfw-dnsmasq
/usr/local/sbin/update-gfw-dnsmasq

cron_line='17 4 * * * /usr/local/sbin/update-gfw-dnsmasq >/tmp/update-gfw-dnsmasq.log 2>&1'
touch /etc/crontabs/root
if ! grep -Fqx "$cron_line" /etc/crontabs/root; then
    printf '%s\n' "$cron_line" >> /etc/crontabs/root
fi

/etc/init.d/cron restart
/etc/init.d/nikki restart
/etc/init.d/dnsmasq restart

echo "Backup: $BACKUP_DIR"
echo "Split DNS installed"

3. rollback-split-dns.sh

sh
#!/bin/sh

set -eu

POINTER="/root/last-split-dns-backup"
if [ ! -s "$POINTER" ]; then
    echo "Backup pointer not found: $POINTER" >&2
    exit 1
fi

BACKUP_DIR="$(cat "$POINTER")"
if [ ! -d "$BACKUP_DIR" ]; then
    echo "Backup directory not found: $BACKUP_DIR" >&2
    exit 1
fi

cp "$BACKUP_DIR/dhcp" /etc/config/dhcp
cp "$BACKUP_DIR/nikki" /etc/config/nikki

if [ -f "$BACKUP_DIR/crontab-root" ]; then
    cp "$BACKUP_DIR/crontab-root" /etc/crontabs/root
else
    sed -i '\|/usr/local/sbin/update-gfw-dnsmasq|d' /etc/crontabs/root 2>/dev/null || true
fi

if [ -f "$BACKUP_DIR/update-gfw-dnsmasq" ]; then
    cp "$BACKUP_DIR/update-gfw-dnsmasq" /usr/local/sbin/update-gfw-dnsmasq
    chmod 0755 /usr/local/sbin/update-gfw-dnsmasq
else
    rm -f /usr/local/sbin/update-gfw-dnsmasq
fi

if [ -f "$BACKUP_DIR/dnsmasq.d/gwf-gfwlist.conf" ]; then
    cp "$BACKUP_DIR/dnsmasq.d/gwf-gfwlist.conf" /etc/dnsmasq.d/gwf-gfwlist.conf
else
    rm -f /etc/dnsmasq.d/gwf-gfwlist.conf
fi

/etc/init.d/cron restart
/etc/init.d/nikki restart
/etc/init.d/dnsmasq restart

echo "R4S split DNS rolled back from $BACKUP_DIR"

二十一、参考资料


**最后提醒:**这套方案的目标不是让规则数量最多,而是把国内直连路径变得简单,把复杂代理判断限制在真正需要代理的连接上。修改后必须以客户端实际拿到的网关/DNS、DNS 返回结果、Q3000 路由命中和 Mihomo 连接记录为准,不能只看 UI 中某个开关。

内容由 WordPress 管理,静态页面由 VitePress 生成