Image

家庭网络搭建03-踩坑两年:DNS 慢、流媒体卡、规则难维护

Viewed: loading...

13 minutes to read


本文由 AI 优化组织完成,非个人完整编写,AI 参与度:90%

Hello 朋友们,上一篇家庭网络系列写到了软路由的基本原理,也搭了一套「看起来能跑」的方案。这两年一直在用,大部分时候确实挺顺,但有几个问题一直在后台静静地折腾我——有时候 GitHub 要等将近一分钟才能开、小红书在家 WiFi 下图片不加载、换了个 VPN 服务商自定义规则全失效……每次出问题都感觉「就差一点」,但就是搞不清楚根在哪里。

趁着这次把三件事全搞清楚了,顺手整理成这篇踩坑记录。

如果还没看过前两篇的朋友,可以先补补背景:


先说说家里的网络是怎么布的

家里用了两台路由器。一台是普通的主路由,负责连光猫、管全家上网;另一台是一个小主机,跑着「软路由」,专门负责让网络变聪明——帮你决定哪些流量直连国内服务器,哪些走代理绕出去。

这次换了更好的主路由之后,顺带解决了一个以前很烦的操作:以前每台设备想走代理,得手动去改网络设置,改错了还会断网。现在直接在路由器上建了两个 WiFi 信号,一个普通的,一个「科学上网专用的」,连哪个就走哪条路,完全自动,手机根本不用动任何设置。软路由要是出问题了,也只需要切换一下 WiFi,5 秒恢复正常上网。

家庭网络整体架构

技术细节

DNS 解析链路

主路由从小米 AX3600 换成了优倍快(Ubiquiti UniFi),旁路由这套没动,OpenClash 跑 Fake-IP Mix 模式,AdGuard Home 负责 DNS 广告过滤。UniFi 支持划分多个子网,每个子网可以单独配置 DHCP 下发的 DNS 和网关,所以能做到连上某个 SSID 就自动走全链路。

DNS 的完整链路是:

设备
 └─ dnsmasq(53)         ← OpenWRT 内置,DHCP 告诉设备 DNS 在路由器上
     └─ OpenClash(7874)  ← 实现 Fake-IP,必须在此拦截
         └─ ADG(5335)    ← 广告过滤
             └─ 223.5.5.5 ← 真实上游

OpenClash 必须夹在 dnsmasq 和 ADG 之间,因为 Fake-IP 的核心逻辑要求它必须是 DNS 链路里最靠近设备的那一层,否则假 IP 机制完全没法工作。4 跳全在本地回环或局域网内,延迟是微秒级,感知不到。


「玄学的一分钟」:等很久,突然通了

有一段时间,GitHub 打开要转圈将近一分钟,有时候 Notion 也这样。换节点、重连 WiFi 都没用,但一切手机流量就立刻好——说明不是节点的问题,锅在路由器上。

根本原因是路由器「记住了旧地址,不肯更新」。

网站的访问原理有点像查电话本:你说「我要联系 GitHub」,路由器先查自己的记事本(DNS 缓存),有记录就直接拿去用,没有才去问别人。问题是,GitHub 换了地址(比如服务器迁移),路由器的记事本却死记着旧地址,还以为能用,于是你的请求就一直在那儿等——等了将近一分钟才超时放弃,路由器才去重新查了真实地址,连接突然就通了。

更讽刺的是,路由器里还开了一个「聪明」的优化功能:记事本里的记录快过期时,不等真正更新,先把旧地址返回给你,同时偷偷在后台更新。本来是为了加速,结果和「强制延长记忆时间」叠加,就导致你拿着一个早就作废的旧地址去连,必然失败。

修法很简单: 去掉「强制延长记忆时间」(改回自动),关掉那个「先返回旧地址」的优化,顺手清一次缓存。改完之后那个玄学一分钟彻底消失了。

问题一根因与修法

技术细节

问题出在 AdGuard Home 的两个配置项叠加:

  1. Override minimum TTL = 3600:不管域名原始 TTL 是多少,ADG 强制把 DNS 缓存延长到至少一小时。GitHub 的 DNS TTL 是 60 秒,但被强制延到 3600,对方 IP 早已切换,我这边最多要等一小时才能感知到变化。

  2. Optimistic Caching(乐观缓存):缓存过期时不等真正刷新,先把旧 IP 返回,同时后台更新。和 TTL 强制延长叠在一起,旧 IP 被返回,TCP SYN 发出去对方根本不在那个地址,超时要等 63 秒(TCP SYN 重传机制:1+2+4+8+16+32 = 63s)。

修法:ADG DNS 设置里把 Override minimum TTL 改为 0,Optimistic Caching 关掉,Fallback DNS 填 8.8.8.8 和 1.1.1.1 做兜底,手动清空一次 DNS 缓存。


小红书在家里加载慢,切 4G 秒好

家里 WiFi 下刷小红书,图片经常加载不出来,抖音视频也反复缓冲;一切手机流量就立刻正常。问题不是 App,是我的网络链路。

根本原因是路由器「不认识」这些 App 的图片服务器,把它们当成境外流量绕了一大圈。

路由器在分流流量时,有一个兜底规则:不认识的地址,一律走代理出境。小红书、抖音的图片和视频是存在国内服务器上的,本来直连就很快;但路由器没有这些地址的记录,就把它们走了代理——相当于你去隔壁买东西,快递员先把货运到日本再寄回来,当然慢。

修法: 告诉路由器「这些图片服务器是国内的,直接连」,加几条直连规则,图片秒加载。

问题二:加规则前后对比

技术细节

OpenClash 分流规则从上往下匹配,全都没命中就走最底部的 MATCH, Proxies——所有未知流量一律走代理节点。小红书、抖音的图片和视频资源走国内 CDN,这些域名在订阅配置里没有,于是命中 MATCH,绕代理节点出境再回来,不慢才怪。

需要加入直连规则的 CDN 域名:

域名归属
xhscdn.com小红书图片 CDN
douyinvod.com抖音视频
douyinpic.com抖音图片
byteimg.com字节系图片 CDN
pstatp.com字节系静态资源
kuaishou.com快手
ksyun.com快手云 CDN

真正的麻烦:换了 VPN 服务商,自己加的规则全没了

我平时会自己给路由器「加功能」:让某个网站走代理,让另一个直连。但这些自定义设置之前是和 VPN 服务商的订阅绑在一起的——有一次换了服务商,整个合并流程断掉,自定义规则全部失效。不是规则写错了,是「管道」断了。

更麻烦的是,新服务商的网站有防机器人验证(Cloudflare),之前用的那套自动合并工具直接被挡掉,什么节点都拿不到。

解法是把「用哪些节点」和「规则」彻底分开,互不影响。

节点让路由器自己去订阅服务商那里拉取,它能绕过那个验证;规则单独放在 GitHub 上,路由器定期来取。以后换节点就改一行链接,规则完全不动;想加新规则就去 GitHub 改一行,最多 24 小时自动生效,急着用就重启路由器立即生效。

技术细节

原来的方案是通过 subconverter 把 VPN 订阅和自定义规则合并成一个 Clash 配置文件。问题在于:

  1. 规则和订阅强绑定,换订阅就断管道
  2. subconverter 是服务端程序去拉节点,Cloudflare 直接把它挡掉,返回人机验证页,报「No nodes were found!」

新方案:让 OpenClash 自己去拉订阅(有正确的 User-Agent,能过 Cloudflare),规则用 clash.meta 原生的 rule-providers 机制独立加载,通过 OpenClash 的「Custom Config Overwrite Scripts」注入配置:

#!/bin/sh
. /usr/share/openclash/ruby.sh
. /usr/share/openclash/log.sh

CONFIG_FILE="$1"

ruby - "$CONFIG_FILE" << 'ENDRB'
require 'yaml'

config = YAML.load_file(ARGV[0])

config['rule-providers'] ||= {}
config['rule-providers']['peter-proxy'] = {
  'type' => 'http',
  'behavior' => 'classical',
  'url' => 'https://raw.githubusercontent.com/PeterChen1997/peter-config/master/config/clash/rule-providers/proxy-rules.yaml',
  'path' => './peter-proxy.yaml',
  'interval' => 86400
}
config['rule-providers']['peter-direct'] = {
  'type' => 'http',
  'behavior' => 'classical',
  'url' => 'https://raw.githubusercontent.com/PeterChen1997/peter-config/master/config/clash/rule-providers/direct-rules.yaml',
  'path' => './peter-direct.yaml',
  'interval' => 86400
}

config['rules'] ||= []
config['rules'].unshift('RULE-SET,peter-direct,DIRECT')
config['rules'].unshift('RULE-SET,peter-proxy,Proxies')

File.write(ARGV[0], config.to_yaml)
ENDRB

GitHub 上的规则文件格式很简单:

# proxy-rules.yaml
payload:
  - DOMAIN-KEYWORD,supabase
  - DOMAIN-KEYWORD,n8n
  - DOMAIN-SUFFIX,docker.io
  - DOMAIN-KEYWORD,instagram
# direct-rules.yaml
payload:
  - DOMAIN-SUFFIX,xhscdn.com
  - DOMAIN-KEYWORD,xiaohongshu
  - DOMAIN-SUFFIX,douyinvod.com
  - DOMAIN-KEYWORD,byteimg

Apply Settings 重启 OpenClash 后,Core Log 里能看到 Start initial provider peter-proxy / peter-direct,流量日志里能确认 xiaohongshu.com → RuleSet(peter-direct) → DIRECT,说明规则已生效。


写在最后

这套方案有一个轻微的套娃风险:规则文件托管在 GitHub,而 GitHub 有时需要代理才能访问 hhh。不过规则文件本地缓存 24 小时,就算 GitHub 临时抽风,已有的规则还是照常跑的,影响不大。

感兴趣的朋友可以看看我的 peter-config 仓库,里面有完整的规则文件。


附录:Fake-IP 是什么,和其他模式有什么区别

既然全文提了好几次 Fake-IP,顺便展开讲一下,这是理解整套方案的一个关键概念。

传统代理为什么有局限

你在浏览器里输入 github.com,第一件事是 DNS 解析——浏览器问 DNS「github.com 是哪个 IP?」,拿到 IP 之后再去建 TCP 连接。

Clash 想在这里做流量分流,但如果它只拦截 TCP 连接层,拿到的已经是 IP 了,域名信息丢失了。要用 DOMAIN-KEYWORD,github 这类规则来匹配,就得反向查「这个 IP 是从哪个域名解析来的」,又慢又容易出错,尤其是 CDN 域名一个 IP 对应大量域名,根本查不准。

Fake-IP 怎么解决这个问题

Fake-IP 的思路是在 DNS 阶段就把信息拿住:

设备问 DNS:「github.com 是哪个 IP?」

OpenClash 拦截,不做真实解析
直接返回假 IP:198.18.0.123(RFC 预留测试地址段)
记下映射:198.18.0.123 ↔ github.com

设备拿着 198.18.0.123 发起 TCP 连接
TUN 虚拟网卡拦截这个连接
查表:198.18.0.123 → github.com
匹配规则 → 走代理
代理节点去真正解析并连接 github.com

设备全程以为自己在连 198.18.0.123,背后是代理帮你解析了真正的 IP。好处是 DNS 响应极快(不用问上游,直接返回假 IP),域名规则匹配精准,GEOIP 规则也正常。

Fake-IP 的一个坑

198.18.0.0/16 是 IANA 测试地址段,正常网络里不会出现。但一些国内 App、游戏 SDK 或 IoT 设备会校验 IP 是否合法,看到 198.18.x.x 就拒绝连接——直播 App 打不开房间、游戏 SDK 初始化失败,切 4G 就好了,就是这个原因。

解决办法是把有问题的域名加到 fake-ip-filter 列表里,加进去的域名走真实 DNS 解析,不返回假 IP。OpenClash 默认已内置了 NTP、STUN、Nintendo、PlayStation 等常见场景,大部分情况够用。

模式对比:该怎么选

OpenClash 的模式分两个维度:

DNS 模式:

模式DNS 返回什么优点缺点
Fake-IP假 IP(198.18.x.x)快、域名匹配准可能触发「非法 IP」校验
Redir-Host真实 IP完全兼容,无非法 IP 问题多一次 DNS 解析延迟,GEOIP 规则慢

流量拦截方式(Enhance Mode):

模式拦截方式覆盖范围
TUN虚拟网卡,接管所有 IP 流量最全,UDP 也能拦
Mix(混合)TUN + HTTP/S 系统代理覆盖面略小,兼容性更好
Normal(普通)仅 HTTP/S 系统代理只有应用层代理,系统级流量会漏

我目前用的是 Fake-IP + Mix:Fake-IP 保证规则匹配速度和准确性,Mix 在 TUN 基础上补了系统代理,兼容性比纯 TUN 更好,偶尔遇到「非法 IP」就在 fake-ip-filter 里加一条。国内 App 多或有 IoT 设备的场景,可以考虑换成 Redir-Host + TUN,牺牲一点 DNS 速度换更好的兼容性。


好啦,这篇写得比前两篇长了不少 hhh,每一个坑都是真实踩过的,希望对有同款配置的朋友有点参考价值。咱们下次见

Comments

There are comments.

📰 邮箱订阅 📰
不错过每篇更新~
如有发现问题,请点击这里勘误🐶