SmartDNS nftset 多目标写入:从 Bug 到 PR 合并的完整路径
摘要:本文记录了 SmartDNS nftset 功能在多表写入场景下的一个 Bug(#1944)及其完整修复历程。在路由器 DNS 分流架构中,需要将解析出的国内域名 IP 同时写入两个 nftables 表的同名集合,但 SmartDNS 的 nftset 实现只支持单目标写入。通过临时 workaround(看门狗定时同步)维持系统运行后,最终通过 PR#2400 实现了 nftset 多目标支持,该修复已合并到 master 分支,将在下个版本发布后部署。
2026-06-28 | SmartDNS PR#2400 | pymumu/smartdns
问题发现
N100 路由器运行 surflare-watchdog 作为 VPN 看门狗。SmartDNS 负责 DNS 分流:国内域名列表(111K 条)解析后,IP 通过 nftset 功能写入 nftables 集合,被 tproxy 规则用于旁路国内流量。
架构要求同一批 IP 存在于两个 nft 表的同名集合中:
- inet sw_lan_tproxy / cn_domain_ips:tproxy 分流规则使用,LAN 设备访问国内域名时走直连
- inet killswitch / cn_domain_ips:VPN 断开时的防火墙旁路,确保国内域名在 killswitch 激活期间仍可访问
Bug: nftset 只写一个表 (SmartDNS #1944)
SmartDNS 的 nftset 实现在解析域名规则时,每条规则只能指定一个 nft 目标。即使配置了两条 nftset 规则指向不同表的同名集合,SmartDNS 内部只保留最后一条的目标。结果:cn_domain_ips 只被写入一个表。
Workaround: 看门狗定时同步
在看门狗中实现了 _sync_cn_domain_ips 函数,每600秒将 tproxy 表的 cn_domain_ips 同步到 killswitch 表。这引入了最多10分钟的数据不一致窗口,但在实践中可接受,因为 killswitch 只在 VPN 断开时短暂激活。
修复: PR#2400
直接修改 SmartDNS 的 nftset 处理逻辑,允许单条域名规则的 nftset 字段指定多个目标(逗号分隔)。DNS 解析完成后,IP 被原子地写入所有指定的 nft 集合。
PR 标题:"nftset: support multiple targets per domain rule"
由项目维护者 pymumu 于 2026-06-28 23:29 UTC 合并到 master 分支。
发版状态
text
事件 时间 状态
============== ===================== =============
Release48.2 2026-06-28 08:12 UTC 早于 PR 15小时
PR#2400 合并 2026-06-28 23:29 UTC 已在 master
下次发版 待定 将包含此修复
Release48.2 发布时间早于 PR 合并15小时,因此该版本不包含此修复。需要等下一个版本。
部署计划
SmartDNS 发布包含 PR#2400 的新版本后:
- 升级 N100 上的 SmartDNS 包
- 修改 SmartDNS 配置:nftset 字段同时指定两个表的目标
- 移除
surflare_watchdog.sh中的_sync_cn_domain_ips600秒定时同步 workaround - 验证两个表的 cn_domain_ips 集合实时同步
收获
这是一个典型的"上游 Bug 影响下游架构"的案例。workaround(定时同步)引入了延迟和复杂度,但在等待上游修复期间是务实的选择。关键是保持 workaround 可追溯(看门狗代码注释引用了 #1944),以便上游修复后能干净地移除。
链接
- PR: pymumu/smartdns#2400
- Issue: pymumu/smartdns#1944