找回密码
立即注册
搜索
发回帖 发新帖

6236

积分

0

好友

773

主题
发表于 昨天 23:24 | 查看: 2| 回复: 0

很多人想给家里搭建全网广告拦截,常规操作不是翻出一台落灰的树莓派,就是在软路由、NAS 甚至小主机里开个 Docker 跑 Pi-hole 或 AdGuard Home。这套方案成熟好用,但总让人感觉有点“大炮打蚊子”——一台功耗几瓦到十几瓦、算力充裕的小电脑,整天开机就是为在局域网里接发几十字节的 UDP 报文,顺手把恶意域名解析成 0.0.0.0。

前阵子社区里出现了一个让人眼前一亮的开源项目:esp32-c3-adblock。作者用一块淘宝上十块钱出头(约 2 美元)、指甲盖大小的 ESP32-C3 核心板,硬生生跑起了媲美 Pi-hole 的 DNS 广告拦截服务,而且能稳稳装下 14 万甚至多达 53 万条过滤规则。

最绝的一点在于,这块板子完全没有外扩 PSRAM,全靠一套极致的数据结构设计把硬件资源压榨到了极限。

为什么之前没人这么做?

玩过嵌入式或者单片机网络开发的朋友都知道,在微控制器上做 DNS 域名过滤,最棘手的问题从来不是网络吞吐,而是内存(RAM)。

像 AdGuard Home 或常规的 DNS 拦截器,通常需要把几万到几十万条域名字符串加载进内存里做快速匹配。一个普通域名按 20 个字节计算,十万条规则光是纯文本就要占掉 2MB 以上内存;如果算上哈希表节点指针、前缀树(Trie)开销,轻松突破数兆字节。

但普通的 ESP32-C3 只有区区 400KB 左右的片上 SRAM,扣除 Wi-Fi 协议栈、FreeRTOS 内核和 TCP/IP 缓冲之后,留给应用层能自由支配的堆内存通常只有一两百 KB。如果走传统的“把规则全读进 RAM”路线,十块钱的单片机当场就会 OOM 崩溃。以往想在单片机上跑类似项目,大家只能多花几倍价钱买带 PSRAM(伪静态内存)的高配 ESP32 模组。

这个项目的作者打破了这种惯性思维:既然单片机的 RAM 贵如黄金,但板载的 SPI Flash 却有整整 4MB,为什么非得把域名字符串塞进内存?

核心设计:Hash-in-Flash + 二分查找

项目的核心思路简单粗暴,却极为精妙,作者将其称为 Hash-in-Flash。

整个工作流程可以拆成两步。

1. 离线预处理:用 40-bit 哈希压缩空间

在将规则写入设备之前,Python 脚本会先清洗规则库(支持 hosts 格式、AdGuard 基础规则以及普通域名列表),提取出所有要拦截的域名及其父级域名。

脚本并不存储原始域名字符,而是对每个域名计算一次 FNV-1a 哈希,并且只保留 5 字节(40-bit)。处理完后,脚本将所有 40-bit 的哈希值按升序严格排序,打包成一个二进制文件 blocklist.bin 烧录进 ESP32 的 Flash 分区。

原本十几兆的文本规则,被压缩成了纯粹的 5 字节数字流:

  • 14 万条规则仅占 Flash 约 0.7 MB
  • 扩展到极限的 53.7 万条规则也只占约 2.6 MB

2. 在线查询:Flash 上的极速二分查找

当路由器或终端设备向 ESP32 发送 DNS 查询 时,单片机解析出域名,计算其 40-bit 哈希,然后直接在 Flash 存储空间内执行二分查找。

因为数据已经预先排序,在 14 万条记录中进行二分查找,最坏情况也只需要读取约 18 次 Flash。ESP32 的 SPI Flash 映射读取非常迅速,整个拦截查询从收到请求到返回,包含 Wi-Fi 空口延迟在内仅需约 10 毫秒。

更可怕的是内存消耗:由于所有匹配逻辑都在 Flash 上原地完成,系统运行时占用的堆内存只有 50 KB 左右,C3 自带的片上 SRAM 甚至还有大把结余。

为什么是 40-bit?

这里还藏着一个有趣的工程取舍。单片机存储精打细算,为什么不用标准的 32 位(4 字节)或者 64 位(8 字节)?

作者根据哈希碰撞的“生日悖论”算了一笔账:

  • 如果用 32 位哈希,省了空间,但在 25 万条规则下大概率会出现 7 次以上的哈希碰撞。这意味着会有正常网站被误杀,在 DNS 服务里不可接受;
  • 如果用 64 位哈希,完全不会碰撞,但每条规则凭空多出 3 字节,Flash 空间直接爆掉;
  • 而 40-bit 刚好是甜点位:14 万条规则下碰撞概率基本为 0;即便堆到 53.7 万条规则,理论上也只会出现约 1 次碰撞(即全网可能只有一个无辜域名被误判拦截)。用一次微乎其微的误杀代价,换取了 4MB Flash 容纳半百万级规则的能力。

不只是玩具,细节全部拉满

很多嵌入式 PoC 项目往往只能通过串口看日志,改个配置就得重新插线烧录代码,但 esp32-c3-adblock 在实用性上做得非常成熟:

  1. 直接插在路由器屁股上
    硬件首选是 ESP32-C3 SuperMini,尺寸几乎和拇指盖一样大。加一个几块钱的 USB-A 转 Type-C 转接头,就可以直接插在路由器背面的 USB 口上取电。不用外接电源适配器,不占插排,也没有杂乱的网线和电源线。仓库里甚至提供了配套的外壳 3D 打印 STL 文件。

  2. 配网与 Web 面板
    设备首次上电若连不上 Wi-Fi,会自动开启名为 C3-AdBlock-XXXX 的热点并弹出强制门户(Captive Portal),手机连上去直接选 Wi-Fi 输密码即可。板子自带一个轻量 Web 控制台,不仅能看每个客户端的拦截/放行统计,还支持手动临时封禁设备或添加自定义域名。

  3. 真正的全无线 OTA 维护
    初次通过串口烧录完固件后,后续完全不需要再拔下来插电脑:

    • 固件支持通过网页上传或 ArduinoOTA 局域网推送
    • 规则库(Blocklist)同样支持 OTA 上传。更省心的是,设备支持配置定时拉取远程规则。项目作者在 GitHub Actions 上配置了自动化工作流,每周一自动重新构建最新规则包发布在 Releases 中,填入 URL 后它每周自己静默更新
  4. 安全边界考虑
    对于一个串在家庭网络关键路径上的 DNS 设备,安全绝不是小事。项目对面板里的所有敏感操作(上传固件、修改黑名单、重置 Wi-Fi)强制要求 HTTP Basic Auth 验证。为了防范内网 CSRF 攻击,前端在所有修改状态的请求上都附加了自定义请求头(X-Requested-With),杜绝了通过外部恶意网页伪造请求把设备重置或篡改的隐患。

工程师视角的浪漫

在硬件算力廉价膨胀的今天,我们早已习惯了用更快的 CPU、更宽的 RAM 去堆砌功能,动辄几十兆内存起步的 Electron 应用充斥着桌面。

但这个项目展现了一种久违的工程美感:没有昂贵的硬件,没有高大上的复杂框架,依靠对数据结构的深刻理解、对哈希碰撞概率的严谨推导,以及对存储特性的精准把控,用十几块钱的边缘硬件干掉了需要整套软路由环境才能解决的需求。

如果家里正好有闲置的 ESP32-C3 板子,不妨花上十几分钟把它刷成一个静默工作的小拦截器——插在路由器背面,功耗不到 1W,安安静静守护全屋的网络体验。作为 云栈社区 的一员,这种把边缘硬件玩出花样的项目,正是极客精神最生动的体现。




上一篇:Skyworks完成220亿美元合并Qorvo:苹果自研芯片逼出的射频巨头抱团
下一篇:Docker Compose 5.6 发布:新增 jobs 支持,一次性任务有了专属位置
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-7 01:38 , Processed in 0.068730 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表