Files
openwrt-build-helper/reports/2026-10-30-cudy-tr3000-adguardhome-memory.md

284 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Cudy TR3000:AdGuard Home 内存与限流问题、DHCP 不下发 DNS 导致 iPad 无网络
- 日期:2026-10-30
- 设备:Cudy TR3000 256MB v1(`cudy-tr3000.tail694d4.ts.net`,远程);另检查了 Linksys MX8500(`linksys-mx8500.tail694d4.ts.net`)
- 系统:OpenWrt 25.12.6 r33311-981c978eff,mediatek/filogic
- AdGuard Home:0.107.79
- 相关报告:[`2026-10-28-cudy-tr3000-wwan-watchdog.md`](2026-10-28-cudy-tr3000-wwan-watchdog.md)
## 摘要
| # | 问题 | 处理 |
|---|---|---|
| 1 | AdGuard Home 查询日志写在 tmpfs,6.5 小时涨到 122 MB,可用内存只剩 20 MB | 改为 6 小时轮转,并忽略 EasyTier 对端域名(MX8500 同样处理) |
| 2 | `ratelimit: 20` 按网段计算,整个局域网共用一个额度 | 改为 `ratelimit: 0`(MX8500 同样处理) |
| 3 | dnsmasq 改到 54 端口后,DHCP 不再下发 DNS 服务器,iPad 显示无网络 | 增加 `dhcp_option '6,192.168.156.65'` |
| 4 | 排查时逐包写内核日志,导致 Wi-Fi 固件超时重置,两条上联和两个热点断开约 80 秒;之后 5G 热点没有恢复 | 执行 `wifi` 后恢复 |
| 5 | Wi-Fi 重启后,iPad 用原来的私有 MAC 仍收发不通(真实 MAC 可以) | 重启路由器后恢复 |
## 背景
反馈“AdGuard DNS 好像崩溃了”。排查时 AdGuard Home 进程正常运行,路由器上解析也正常,但发现两个会导致 DNS 不稳定甚至整机卡死的问题:
1. 查询日志写在内存里(tmpfs),6.5 小时涨到 112 MB,可用内存只剩约 20 MB。
2. 限流 `ratelimit: 20` 按网段计算,所有局域网设备共用一个额度,高峰期已超过上限。
同时 iPad 连上 Wi-Fi 后一直显示“无互联网连接”,后来查明是另一个问题(第 3 项)。
## 现象
| 时间 | 可用内存 | `querylog.json` | 说明 |
|---|---|---|---|
| 17:31 | 86 MB | 112 MB | 开机 6.5 小时,负载约 2.9 |
| 17:53 | 20 MB | 121 MB | |
| 17:54 – 17:56 | — | 122 MB | SSH 和 Tailscale ping 均超时约 1 分钟,负载升到 9.9;没有重启 |
| 17:57(修复后) | 234 MB | 0.2 MB | `/tmp` 从 135 MB 降到 5.4 MB |
上述内存数据证明查询日志占用较大,但不能据此认定此前重启由内存耗尽引起。
17:54 的卡顿还有一个人为因素:排查时一条 `tail -20000 querylog.json` 命令超时被中断,但路由器上的 `tail` 进程没有退出,一直以 R 状态占用 CPU。它还持有已删除的 `querylog.json`,导致删除后约 112 MB 内存迟迟没有释放。手动 `kill` 后内存才回来。以后在路由器上分析大文件,要先复制一小段出来再处理,并确认远端进程已退出。
## 原因
### 1. 查询日志:几乎都是 EasyTier 解析对端域名
开机至 17:30 共 166000 条查询:
| 域名 | 次数 |
|---|---|
| `cloud.zhe-yuan.top` | 119301 |
| `home.zhe-yuan.top` | 18438 |
| `backup.zhe-yuan.top` | 16155 |
| 其他所有域名 | 约 12100 |
| 客户端 | 次数 | 设备 |
|---|---|---|
| `fd84:1963:e466::7c3` | 100529 | intel-nuc8i5beh |
| `127.0.0.1` / `::1` | 29596 / 29520 | 路由器自身 |
| `fd84:…:ec56:…` | 6148 | Windows-Dev-Kit |
| `fd84:…:ed94:…` | 207 | Galaxy-Z-Fold8 |
这三个域名正是 EasyTier 的对端地址(`/etc/easytier/config.toml` 的 `[[peer]]`)。`easytier-cli connector` 显示这些连接器一直处于 `Connecting`,每次重试都会重新解析域名。NUC 上的 EasyTier 应该也是同样情况。
上游 TTL 正常(`cloud` 约 300 s,`home` 约 250 s),AdGuard 都是从缓存回答,所以对上游没有压力。问题在于每条查询都写进了日志:
- `querylog.interval: 90d`:查询日志保留 90 天,实际上不会轮转。
- 日志目录在 `/var/lib/adguardhome`,也就是 tmpfs(内存)。
- 每条记录约 700 字节(包含完整的 base64 应答)。
平均每秒约 7 条,每小时约 17 MB。
### 2. 限流:所有局域网客户端共用一个额度
```yaml
ratelimit: 20
ratelimit_subnet_len_ipv4: 24
ratelimit_subnet_len_ipv6: 56
```
限流按 IPv4 /24 或 IPv6 /56 计算,整个局域网(`192.168.156.0/24`、`fd84:1963:e466::/48` 内的 /56)只有每秒 20 次的共用额度。抽样的 3000 条记录里,局域网客户端的高峰是每秒 24 次,主要来自 NUC 的 EasyTier 解析。被限流的查询直接丢弃且不写日志,其他设备会偶尔解析失败而查不到记录。
## 修改内容
备份:`/root/adguardhome.yaml.bak`
`/etc/adguardhome/adguardhome.yaml`:
```diff
dns:
- ratelimit: 20
+ ratelimit: 0
querylog:
- ignored: []
- interval: 90d
+ ignored:
+ - cloud.zhe-yuan.top
+ - home.zhe-yuan.top
+ - backup.zhe-yuan.top
+ interval: 6h
- ignored_enabled: false
+ ignored_enabled: true
```
另外停止 AdGuard Home 后删除了旧的 `querylog.json`,然后重启服务。
- **`interval: 6h`**:AdGuard Home 轮转时保留 `querylog.json` 和 `querylog.json.1` 两个文件,最多约 12 小时的记录。去掉 EasyTier 的查询后,每 6.5 小时约 12000 条、约 9 MB,最坏情况约 20 MB。
- **忽略三个 EasyTier 对端域名**:只是不写入查询日志,统计和解析不受影响。
- **`ratelimit: 0`**:关闭限流。防火墙 `wan` 区(`wan`、`wan6`、`wwan2`、`wwan5`、`wwan26`、`wwan56`)的入站是 `REJECT`,外网访问不到 53 端口;能访问的只有 `lan` 和 `meshvpn`(Tailscale、ZeroTier),都是自己的设备。
`/etc/adguardhome/adguardhome.yaml` 在软件包的 conffiles 里,sysupgrade 会保留,不需要加进 `/etc/sysupgrade.conf`。
## 验证
```
$ nslookup example.com 127.0.0.1
Address: 104.20.23.154
$ nslookup google.com 192.168.156.65
Address: 2607:f8b0:4009:812::200e
$ df -h /tmp
tmpfs 242.7M 5.4M 237.3M 2% /tmp
$ free
Mem: total 497044 used 207988 free 216488 shared 5568 available 233988
```
AdGuard Home 重启后约 15 秒才开始监听 53 端口(当时负载较高),这期间 DNS 不可用。
## 问题 3:DHCP 不下发 DNS,iPad 显示“无互联网连接”
### 现象
两台 iPad(`iPad` 7a:ca:e0:6d:f3:3f、`iPad-Pro` 80:a9:97:a6:b8:61)连上 Wi-Fi 后显示“无互联网连接”,浏览器报 `ERR_INTERNET_DISCONNECTED`。切换私有 Wi-Fi 地址也不管用。iPad 上的地址、子网掩码、路由器都正常:`192.168.156.123` / `255.255.255.192` / `192.168.156.65`。
### 排查过程
| 检查 | 结果 |
|---|---|
| 无线 | 关联正常,-44 dBm,HE-MCS 11 |
| DHCP | 正常拿到 `192.168.156.123` |
| 路由器 ping iPad | 通 |
| conntrack | 没有任何从 iPad 发起的连接 |
| AdGuard 查询日志 | 开机以来没有这台 iPad 的记录 |
| nft 计数(只计数,不写日志),3 分钟 | 只有 mDNS、IGMP/MLD 和 1 个查询网关的 ARP,没有 DNS,没有 TCP |
| 重新入网时双向抓包 | DHCP、ARP、RS/RA 都正常;之后 iPad 不发 DHCPv6、不发 DNS,完全静默 |
| 临时关闭局域网 RA 和 DHCPv6 | 没有变化,已恢复 |
| 打开 dnsmasq `logdhcp` | **DHCPACK 里没有 option 6(DNS 服务器)** |
`logdhcp` 记录的 DHCPACK 内容(iPad 请求了 option 6,但没有收到):
```
requested options: 1:netmask, 121:classless-static-route, 3:router,
requested options: 6:dns-server, 15:domain-name, 108:ipv6-only,
requested options: 114:captive-portal, 119:domain-search, 162, 252
sent option: 53 message-type 5
sent option: 54 server-identifier 192.168.156.65
sent option: 51 lease-time 12h
sent option: 1 netmask 255.255.255.192
sent option: 28 broadcast 192.168.156.127
sent option: 3 router 192.168.156.65
sent option: 15 domain-name lan
```
### 原因
为了让 AdGuard Home 使用 53 端口,dnsmasq 被改到了 `port=54`。dnsmasq 不在 53 端口提供 DNS 时,不会自动把自己作为 DNS 服务器(option 6)写进 DHCP 应答。
其他设备没受影响,是因为它们走 IPv6:odhcpd 的 RA 里带了 DNS(`fd84:1963:e466::1`),它们也有 ULA 地址。AdGuard 查询日志里的局域网客户端全是 `fd84:` 地址,正好印证了这一点。iPad 没有拿到 ULA 地址(原因见“附带发现”),用不了这个 IPv6 DNS,IPv4 又没有 DNS,iOS 就判定这个网络不能上网,连 DNS 查询和联网检测都不发。
MX8500 上本来就配了 `dhcp_option '3,192.168.156.1' '6,192.168.156.1' '15,lan'`,所以没有这个问题。TR3000 是漏配了。
### 修改
```sh
uci add_list dhcp.lan.dhcp_option='6,192.168.156.65'
uci commit dhcp
/etc/init.d/dnsmasq restart
```
修改后 iPad 重新入网,立刻开始正常解析,并发起大量连接(iCloud、Google 等)。conntrack 里能看到它到 `192.168.156.65:53` 的查询都有应答,一度建立了 112 条连接。
## 问题 4:排查导致 Wi-Fi 固件重置(我的操作引起)
### 经过
排查 iPad 时,在 `phy1-ap0` 入方向(netdev ingress)和 bridge output/forward 上加了 nft 规则,把进出 iPad 的**每个包都写进内核日志**。iPad 拿到 DNS 后流量突增,每个包要记录好几次(bridge 泛洪时 eth0、eth1、phy*-ap0 各记一次),CPU 被打满(负载 11.6):
| 时间 | 事件 |
|---|---|
| 18:14:28 | iPad 开始大量收发;`phy1-sta0`(wwan5)因不活动断开 |
| 18:14:30 | `eth0: NETDEV WATCHDOG: transmit queue 0 timed out 5020 ms` |
| 18:14:32 | `phy1-ap0` link down |
| 18:14:34 – 18:15:24 | `mt798x-wmac: Retry message ...`(Wi-Fi 固件 MCU 消息超时) |
| 18:14:43 | `phy0-sta0`(wwan2)断开 |
| 18:15:35、18:15:42 | `ieee80211 phy0/phy1: Hardware restart was requested`,固件重新加载 |
| 18:15:50 | wwan5、wwan2 重新上线 |
| 18:16:23 | 调试规则按计划自动删除 |
两条上联和两个热点断开约 80 秒,这期间 iPad 报 `ERR_NAME_NOT_RESOLVED`。
**教训**:在这台 MT7981 上不要用 nft `log` 逐包记录,哪怕只针对一台设备。只用 `counter` 计数(开销可以忽略),或者限速记录(`limit rate 10/second`)。
### 后遗症与恢复
- **5G 热点没有恢复**:固件重置期间,wwan5 从信道 52 漫游到了信道 165。`phy1-ap0` 一直处于 down 状态,hostapd 也没有再启用它。推测是 165 只支持 20 MHz,而热点设的是 HE80,但没有确证。
- **iPad 在 2.4G 上收发不通**:见问题 5。
18:20:17 执行 `wifi`(后台运行,避免 SSH 断开时被中断),两个射频都重新配置。5G 热点在 18:21:32 恢复(wwan5 此时在信道 112,热点 HE80,中心 5530 MHz,等 DFS 检测约 60 秒),两条上联在约 10 秒内恢复。
## 问题 5:iPad 无线链路不通(重启路由器后解决)
执行 `wifi` 后,Galaxy 重新连上并正常工作,但 iPad(7a:ca:e0:6d:f3:3f)在两个频段都连不通。iPad 自分配了 `169.254.237.114`。
| 项目 | iPad | Galaxy(同一个 `phy0-ap0`) |
|---|---|---|
| authorized / MFP | yes / yes | yes / yes |
| 信号 | -40 dBm | -39 dBm |
| rx packets | 239 | 14695 |
| tx packets / tx failed | 6 / **30** | 32392 / — |
| rx 速率 | **1.0 Mbit/s**(只有最低速率的管理帧) | 正常 |
| `phy0-ap0` netdev ingress 计数(25 秒) | **0** | — |
驱动收到了 iPad 的帧,但没有一个送到网络接口。路由器发给 iPad 的帧大多也没有得到确认。信号很强,不是距离问题。Galaxy 在同样的重置之后工作正常。
用户测试:iPad 用“固定”私有地址(7a:ca:e0:6d:f3:3f)连不通,改用真实 MAC 可以连通。说明问题跟这个 MAC 绑定在一起,最可能是 Wi-Fi 固件里这个 MAC 的站点表项在固件重置后坏了。`wifi` 只重建接口和 hostapd,不会重新加载 MT7981 固件,所以没能清掉。
### 解决:重启路由器
18:33 执行 `reboot`,用户随后确认恢复正常。
重启后检查结果(启动早期墙上时钟会校准,以下以状态和日志事件为准):
| 项目 | 结果 |
|---|---|
| wwan5 / wwan2 | 开机约 50 秒后上线 |
| 5G 热点 | 信道 112(DFS),18:36:57 CAC 完成后启用,HE80 |
| iPad(7a:ca:e0:6d:f3:3f,固定私有地址) | 先连 2.4G、后连 5G,DHCP 正常;5G 上 tx HE-MCS 8(206.5 Mbit/s)、rx HE-MCS 9,conntrack 71 条连接 |
| 设置 | `dhcp_option '6,192.168.156.65'`、AdGuard `ratelimit: 0` 与 `interval: 6h`、看门狗 crontab 都在 |
用户确认 iPad 恢复正常。
## 附带发现
- **RA 前缀**:原 `ip6assign=60` 不适用于普通客户端 SLAAC,已改为 `64`。没有可用公网前缀时不通告 IPv6 默认路由,属于正常行为。
- **DHCP 地址池**:原 /26 配合 `start=100 limit=150` 仅生成 `.100–.126`;已改为 `start=2 limit=61`,扩展为 `.66–.126`。
- **EasyTier**:用户删掉了无效的对端。TR3000 重启 EasyTier 后,现在是 `tcp://cloud…:11010` 和 `tcp://backup…:11010`。之前的 `wss://…:0/easytier/` 一直处于 Connecting,用 curl 测试服务器可达(返回 HTTP 400),推测问题出在端口写成了 `:0`。`easytier-core` 约 30% 的 CPU 主要用于给 `intel-nuc8i5beh` 转发流量(112 MB)。
## Linksys MX8500 检查
| 项目 | MX8500 | 处理 |
|---|---|---|
| DHCP option 6 | 已配置 `6,192.168.156.1`,生成的 dnsmasq 配置里也有 `dhcp-option=lan,6,192.168.156.1`;iPhone(`.43`、`.46`)在 AdGuard 里有 IPv4 查询记录;日志中没有 DHCPNAK 或地址耗尽 | 无需修改 |
| DHCP 地址池 | 原为 `.36–.62` | 已扩展为 `.2–.62`,保留 `.61`、`.62` 静态租约 |
| RA 前缀 | 原为 `/60` | 已改为 `/64`,`ra_default=0` |
| `ratelimit` | 20 | 改为 0(`wan` 区入站同样是 `REJECT`) |
| 查询日志 | 90 天,11.5 小时 59 MB(`/tmp/lib/adguardhome/data`),绝大部分是本机 EasyTier 的解析 | 改为 6 小时轮转,并忽略三个 EasyTier 对端域名 |
| 内存 | 887 MB 总量,修改前可用 243 MB;`/tmp` 61 MB → 5 MB | — |
| EasyTier | 旧配置包含未连接的 WSS 对端 | 18:47 保存清理后的配置并重启,仅剩 cloud、backup 两条 TCP,均为 Connected |
MX8500 备份:`/root/adguardhome.yaml.bak`。修改后 DNS 正常,53 端口在 20 秒内恢复监听。
### 两台 DHCP / RA 修复验证
两台均设置 `dhcp.lan.start=2`、`limit=61`、`network.lan.ip6assign=64`、`dhcp.lan.ra_default=0`。每台地址范围共 61 个,静态保留地址由 dnsmasq 排除动态分配。原有租约仍在新范围内。
配置备份位于各自 `/root/lan-address-fix-20261030/`。重载网络后前缀仍为 /60,执行 `ifup lan` 后两台 `br-lan` 地址和 `ubus call dhcp ipv6ra` 均确认变为 /64;dnsmasq 生成范围与预期一致。DNS 解析正常,双 IPv4 上联在线。
本次修复 LAN 地址分配;ULA 前缀本身不提供 IPv6 公网访问,客户端 SLAAC 地址获取仍需终端侧确认。
EasyTier 收尾验证:执行 `/etc/init.d/easytier restart` 后,`easytier-cli connector` 仅列出 `tcp://cloud.zhe-yuan.top:11010` 和 `tcp://backup.zhe-yuan.top:11010`,均为 `Connected`,配置和运行状态一致。
## 回滚
```sh
# AdGuard Home(两台路由器相同)
/etc/init.d/adguardhome stop
cp /root/adguardhome.yaml.bak /etc/adguardhome/adguardhome.yaml
/etc/init.d/adguardhome start
# TR3000 DHCP DNS(不建议回滚,否则只有 IPv6 DNS 可用)
uci del_list dhcp.lan.dhcp_option='6,192.168.156.65'
uci commit dhcp && /etc/init.d/dnsmasq restart
```