Разработчик под ником Zed (M-Abozaid) выложил в открытый доступ проект esp32-c3-adblock — DNS-блокировщик рекламы в духе Pi-hole, который работает на микроконтроллерной плате ESP32-C3 стоимостью около 2 долларов и не требует PSRAM. Устройство фильтрует DNS-запросы и тем самым отсекает рекламные и трекинговые домены сразу для всех устройств в домашней или небольшой офисной сети.

Главная идея проекта — не хранить строки доменов в оперативной памяти. Вместо этого блоклист записывается во flash-память в виде отсортированных 40-битных хешей FNV-1a, а проверка запроса сводится к двоичному поиску по этой таблице. Благодаря такому подходу примерно в 0,7 МБ flash помещается более 140 000 доменов, а оперативной памяти прошивке требуется всего около 50 КБ.

Блокировщик рекламы на ESP32-C3 SuperMini в напечатанном на 3D-принтере корпусе с переходником USB-C — USB-A для прямого подключения к роутеруБлокировщик рекламы на ESP32-C3 SuperMini в напечатанном на 3D-принтере корпусе с переходником USB-C — USB-A для прямого подключения к роутеру

Как устроена проверка DNS-запросов

Когда на устройство приходит UDP-запрос DNS, прошивка на C++ извлекает из него доменное имя и вычисляет хеш — в том числе для родительских суффиксов домена. Затем она двоичным поиском ищет полученное значение в таблице хешей во flash-памяти. Если домен числится в блоклисте, ESP32-C3 отвечает адресом 0.0.0.0, и запрос оказывается заблокирован. В противном случае он пересылается на вышестоящий DNS-сервер, а полученный ответ возвращается клиенту.

Проверка занимает около 10 мс с учётом времени прохождения сигнала по Wi-Fi и требует примерно 18 обращений к flash-памяти. Именно двоичный поиск позволяет обойтись небольшим числом чтений вместо перебора всей таблицы целиком.

реклама кормит Уточку 🦆

Требования и характеристики

Основные требования и особенности esp32-c3-adblock:

Блокировщик рекламы на ESP32-C3 SuperMini, подключённый напрямую к USB-порту роутера (изображение улучшено с помощью ИИ)Блокировщик рекламы на ESP32-C3 SuperMini, подключённый напрямую к USB-порту роутера (изображение улучшено с помощью ИИ)

  • Беспроводной модуль — 16-контактный ESP32-C3 Super Mini
    • SoC — Espressif Systems ESP32-C3FH4
      • Процессор — одноядерный 32-битный микроконтроллер RISC-V с частотой до 160 МГц
      • ОЗУ — 400 КБ SRAM
      • Накопитель — 4 МБ flash
      • Беспроводная связь — Wi-Fi 4 на частоте 2,4 ГГц
    • USB — порт USB Type-C для прошивки
    • Керамическая антенна
    • Сообщество проверило работу и на классических платах ESP32 (DevKit / WROOM)
  • Хранилище — минимум 4 МБ flash
    • около 0,7 МБ flash занимают более 140 000 доменов
    • домены хранятся прямо во flash в виде отсортированных 40-битных хешей FNV-1a (по 5 байт каждый), а не строками в ОЗУ
  • Производительность DNS-блокировки:
    • время проверки — около 10 мс (включая ~18 чтений flash и задержку Wi-Fi)
    • варианты ёмкости — ~141 тыс. доменов (практически без коллизий), ~250 тыс. доменов (с двумя разделами приложения для OTA-обновления прошивки) или до ~537 тыс. доменов (один раздел приложения, ~1 коллизия)
  • Безопасность
    • HTTP Basic Auth для эндпоинтов, изменяющих состояние
    • экранирование HTML в пользовательских доменах для защиты от XSS
    • защита от CSRF-атак
  • Питание — 5 В по USB (нужен стабильный источник питания, чтобы избежать просадок, из-за которых сбоит радиомодуль)
  • Корпус — файл STL для печати на 3D-принтере под форм-фактор ESP32-C3 SuperMini

Веб-панель, настройка Wi-Fi и обновления

У проекта есть веб-панель, доступная по адресу http://c3adblock.local: в ней можно смотреть заблокированные и разрешённые DNS-запросы по каждому клиенту, блокировать отдельные устройства и добавлять собственные домены в блоклист. Первоначальная настройка Wi-Fi выполняется через captive portal, поэтому данные сети не нужно зашивать в код.

Веб-панель блокировщика: статистика блоклиста, подключённые клиенты, уровень Wi-Fi-сигнала, использование памяти и пользовательские заблокированные домены (изображение улучшено с помощью ИИ)Веб-панель со статистикой блоклиста, списком клиентов, уровнем Wi-Fi-сигнала, использованием памяти и пользовательскими заблокированными доменами (изображение улучшено с помощью ИИ)

Поддерживается обновление «по воздуху» (OTA) как прошивки, так и блоклистов, а также автоматические обновления по расписанию: готовые файлы блоклиста скачиваются по указанному адресу.

реклама кормит Уточку 🦆

На плате ESP32-C3 с 4 МБ flash для OTA-обновления прошивки нужны два слота приложения, и под блоклист остаётся около 1,3 МБ — этого хватает на 250 000 доменов. Чтобы воспользоваться большим списком на 537 000 доменов, придётся перейти на раздел с одним слотом приложения, но тогда OTA-обновление прошивки станет недоступно.

Почему выбран 40-битный хеш

Необычно выглядит сама ширина хеша — 40 бит. По словам разработчика, для такого бюджета flash-памяти это оптимальный размер. При 141 тыс. доменов коллизий почти нет, а при агрессивном расширении списка до 537 тыс. доменов получается примерно одна коллизия, то есть один ошибочно заблокированный домен. Переход на стандартный 32-битный хеш сэкономил бы место, но дал бы около семи коллизий уже на 250 тыс. доменов.

реклама кормит Уточку 🦆

Проект вдохновлён разработкой ESP32_AdBlocker от s60sc, однако это независимая реализация: для больших блоклистов она использует flash-память вместо PSRAM. Тот же подход применим и на более мощных чипах: например, ESP32-S3 с 16 МБ flash при 40-битных хешах вместит около 2,7 млн доменов — против примерно 466 тысяч доменов, которые удаётся хранить строками в 8 МБ PSRAM.

Исходный код на C++, инструкции по сборке через PlatformIO и модель корпуса для 3D-печати доступны в репозитории проекта на GitHub.