
Открытый блокировщик рекламы на ESP32-C3 держит до 537 тыс. доменов без PSRAM
Прошивка хранит список блокировки не строками в оперативной памяти, а 40-битными хешами во флеш-памяти, и ищет по нему двоичным поиском. Больше 140 тыс. доменов укладываются в 0,7 МБ флеш-памяти и 50 КБ оперативной, а микроконтроллер стоит два доллара.
Разработанный Зедом (M-Abozaid) проект esp32-c3-adblock — открытый блокировщик рекламы на уровне службы доменных имён в духе Pi-hole, работающий на плате или модуле с микроконтроллером ESP32-C3 за два доллара и не требующий микросхемы PSRAM. Он фильтрует запросы к службе доменных имён и тем убирает рекламу и отслеживающие домены сразу на всех устройствах домашней сети или небольшого офиса.
Вместо того чтобы держать имена доменов строками в оперативной памяти, проект хранит их во флеш-памяти как упорядоченные 40-битные хеши FNV-1a и проверяет запросы двоичным поиском. Когда приходит запрос по протоколу UDP, прошивка на C++ вытаскивает из него имя домена, считает хеш (вместе с родительскими окончаниями имени) и выполняет двоичный поиск по таблице хешей во флеш-памяти. Если домен в списке блокировки, ESP32-C3 возвращает адрес 0.0.0.0 и тем перекрывает запрос. Если нет — пересылает запрос вышестоящему серверу имён и отдаёт ответ клиенту. Таким способом в примерно 0,7 МБ флеш-памяти укладывается более 140 тыс. доменов, а оперативной памяти расходуется лишь около 50 КБ. Один поиск занимает около 10 мс (включая время обмена по Wi-Fi) и требует примерно 18 чтений из флеш-памяти.
В проекте есть веб-панель по адресу c3adblock.local: в ней видно, сколько запросов заблокировано и пропущено по каждому клиенту, можно перекрыть отдельное устройство и добавить свои домены в список блокировки. Первоначальная настройка Wi-Fi идёт через перехватывающий портал, так что прописывать имя сети и пароль в коде не нужно.
Поддерживается и обновление по воздуху — как прошивки, так и списков блокировки, — вместе с автоматическим обновлением по расписанию, которое загружает готовые файлы списков с заданного адреса. На плате ESP32-C3 с 4 МБ флеш-памяти обновление прошивки по воздуху требует двух разделов под приложение, так что под список блокировки остаётся около 1,3 МБ: это позволяет держать до 250 тыс. доменов. Чтобы взять больший список на 537 тыс. доменов, нужно перейти на разметку с одним разделом приложения, а значит, отказаться от обновления прошивки по воздуху.
Любопытна сама разрядность в 40 бит: по словам разработчика, для такого запаса флеш-памяти она оптимальна. На 141 тыс. доменов совпадений хешей практически не бывает. При расширении списка до жёстких 537 тыс. доменов получается примерно одно совпадение, то есть один лишний заблокированный домен. Переход на обычный 32-битный хеш сэкономил бы флеш-память, но дал бы около семи совпадений уже на 250 тыс. доменов.
Разработчик упоминает, что проект вдохновлён ESP32_AdBlocker от s60sc, но реализован независимо и хранит большие списки блокировки во флеш-памяти, а не в PSRAM. Тот же подход годится и для более мощных микросхем: например, ESP32-S3 с 16 МБ флеш-памяти вместил бы при 40-битных хешах около 2,7 млн доменов, тогда как строками в 8 МБ PSRAM укладывается примерно 466 тыс.
Исходный код на C++, указания по сборке в PlatformIO и корпус для печати на трёхмерном принтере выложены в хранилище проекта на GitHub.