Bloqueador de anuncios DNS de código abierto con ESP32-C3: admite más de 500.000 dominios sin PSRAM

Desarrollado por Zed (M-Abozaid), esp32-c3-adblock es un bloqueador de anuncios DNS de código abierto al estilo de Pi-hole que funciona en una placa o módulo de microcontrolador ESP32-C3 de 2 dólares sin necesidad de PSRAM. Bloquea anuncios y dominios de rastreo en todos los dispositivos de una red doméstica o de una pequeña oficina filtrando las consultas DNS.

En lugar de almacenar las cadenas de dominio en la RAM, el proyecto las guarda en la memoria flash como hashes FNV-1a de 40 bits ordenados y utiliza una búsqueda binaria para comprobar las consultas DNS. Cuando llega una consulta DNS por UDP, el firmware en C++ extrae el dominio, calcula su hash (incluidos los sufijos padre) y realiza una búsqueda binaria en la tabla de hashes de la flash. Si el dominio aparece en la lista de bloqueo, el ESP32-C3 devuelve 0.0.0.0 para bloquear la consulta. En caso contrario, la reenvía a un servidor DNS ascendente y devuelve la respuesta al cliente. Este método permite almacenar más de 140.000 dominios en unos 0,7 MB de memoria flash utilizando solo unos 50 KB de RAM. Cada búsqueda tarda alrededor de 10 ms (incluido el tiempo de ida y vuelta de la WiFi) y necesita unas 18 lecturas de flash.

Bloqueador de anuncios DNS ESP32-C3 SuperMini en una carcasa impresa en 3D con un adaptador USB-C a USB-A para conectarlo directamente a un router
Bloqueador de anuncios DNS ESP32-C3 SuperMini en una carcasa impresa en 3D con un adaptador USB-C a USB-A para conectarlo directamente a un router

Requisitos y características destacadas del esp32-c3-adblock:

  • Módulo inalámbrico: módulo ESP32-C3 Super Mini de 16 pines
    • SoC: ESP32-C3FH4 de Espressif Systems
      • CPU: microcontrolador RISC-V de 32 bits y un solo núcleo a hasta 160 MHz
      • Memoria del sistema: 400 KB de SRAM
      • Almacenamiento: 4 MB de flash
      • Conectividad inalámbrica: WiFi 4 de 2,4 GHz
    • USB: puerto USB Type-C para la programación
    • Antena cerámica
    • Probado por la comunidad también en placas ESP32 clásicas (DevKit / WROOM)
  • Almacenamiento: requiere un mínimo de 4 MB de flash
    • Unos 0,7 MB de flash utilizados para más de 140.000 dominios
    • Guarda los dominios directamente en la flash como hashes FNV-1a de 40 bits ordenados (5 bytes cada uno) en lugar de cadenas en RAM.
  • Rendimiento del sumidero DNS:
    • Tiempo de búsqueda: ~10 ms (incluye ~18 lecturas de flash y el RTT de la WiFi)
    • Opciones de capacidad: ~141.000 dominios (colisiones prácticamente nulas), ~250.000 dominios (con particiones de doble aplicación para la actualización OTA del firmware) o hasta ~537.000 dominios (partición de aplicación única, ~1 colisión).
  • Seguridad
    • Autenticación básica HTTP obligatoria para los endpoints que modifican el estado
    • Dominios personalizados escapados en HTML para evitar ataques XSS
    • Reforzado contra ataques CSRF
  • Alimentación: 5 V por USB (requiere una fuente de alimentación estable para evitar caídas del módulo de radio)
  • Carcasa: incluye un archivo STL imprimible en 3D para el formato del ESP32-C3 SuperMini
Bloqueador de anuncios DNS ESP32-C3 SuperMini conectado directamente al puerto USB de un router
Bloqueador de anuncios DNS ESP32-C3 SuperMini conectado directamente al puerto USB de un router (imagen mejorada con IA)

El proyecto incluye un panel web accesible en http://c3adblock.local, donde los usuarios pueden consultar las consultas DNS bloqueadas y permitidas de cada cliente, bloquear dispositivos concretos y añadir dominios personalizados a la lista de bloqueo. La configuración inicial del WiFi se realiza mediante un portal cautivo, por lo que no es necesario incluir las credenciales de WiFi en el código.

También admite actualizaciones OTA (over-the-air) tanto del firmware como de las listas de bloqueo, además de actualizaciones automáticas programadas que descargan archivos de listas de bloqueo ya compilados desde una URL especificada. En una placa ESP32-C3 de 4 MB, las actualizaciones OTA del firmware necesitan dos ranuras de aplicación, lo que deja alrededor de 1,3 MB de flash para la lista de bloqueo. Esto permite almacenar hasta 250.000 dominios. Para utilizar la lista más grande, de 537.000 dominios, hay que pasar a un diseño de partición de aplicación única, lo que desactiva las actualizaciones OTA del firmware.

Panel web de ESP32-C3 AdBlock con estadísticas de la lista de bloqueo, clientes conectados, intensidad de la señal WiFi, uso de memoria y dominios bloqueados personalizados
Panel web con estadísticas de la lista de bloqueo, clientes conectados, intensidad de la señal WiFi, uso de memoria y dominios bloqueados personalizados (imagen mejorada con IA)

Lo curioso es el ancho de 40 bits; el desarrollador asegura que es el tamaño óptimo para este presupuesto de flash. Con 141.000 dominios, las colisiones son prácticamente inexistentes. Ampliar la lista hasta unos agresivos 537.000 dominios provoca aproximadamente una colisión (un dominio bloqueado de más). Bajar a un hash estándar de 32 bits ahorraría espacio en la flash, pero provocaría unas siete colisiones con 250.000 dominios.

El desarrollador comenta que el proyecto se inspira en ESP32_AdBlocker de s60sc, pero se trata de una implementación independiente que utiliza memoria flash en lugar de PSRAM para almacenar listas de bloqueo grandes. Se puede aplicar el mismo enfoque en chips más potentes. Por ejemplo, un ESP32-S3 de 16 MB podría almacenar alrededor de 2,7 millones de dominios usando hashes de 40 bits, frente a unos 466.000 dominios almacenados como cadenas en 8 MB de PSRAM.

Puedes encontrar el código fuente en C++, las instrucciones de compilación con PlatformIO y la carcasa imprimible en 3D en el repositorio de GitHub.

Traducido del artículo en inglés «Open-source ESP32-C3 DNS ad blocker supports up to over 500,000 domains without PSRAM» de Debashis Das.