eBPF: ejecutar código en el kernel de Linux sin recompilarlo
@programacionLo esencial
- eBPF permite ejecutar programas dentro del kernel de Linux sin modificar su código fuente ni cargar módulos del sistema.
- Nació en 1992 como Berkeley Packet Filter; Alexei Starovoitov lo extendió a eBPF en el kernel 3.18 (2014).
- Un verificador analiza cada programa antes de cargarlo: garantiza que termina y que no accede a memoria inválida.
- Los programas se compilan con un JIT a código máquina nativo, así corren casi a la velocidad del propio kernel.
- Se enganchan en kprobes, tracepoints, XDP y tc; los 'mapas' comparten datos con el espacio de usuario.
- Cilium, Katran (Meta) y Falco usan eBPF para redes, balanceo de carga y seguridad en producción.
- CO-RE (Compile Once, Run Everywhere) usa BTF para que un mismo binario funcione en distintas versiones del kernel.
¿Qué es eBPF?
El kernel es el corazón de Linux: gestiona la red, los discos, la memoria y los procesos. Tradicionalmente, cambiar su comportamiento implicaba dos caminos incómodos: modificar el código fuente del kernel y recompilarlo (algo lento y arriesgado), o escribir un módulo de kernel, que corre con permisos totales y un error puede tumbar toda la máquina.
eBPF (extended Berkeley Packet Filter) ofrece un tercer camino: cargar pequeños programas en una máquina virtual que vive dentro del kernel. Esos programas se ejecutan cuando ocurre un evento concreto —llega un paquete de red, se invoca una llamada al sistema, se abre un archivo— y pueden observar o alterar lo que pasa, sin recompilar nada y sin el riesgo de un módulo tradicional.
La analogía útil es la de JavaScript en el navegador. El navegador no te deja tocar su motor, pero sí ejecutar código tuyo en un entorno controlado (un sandbox) que reacciona a eventos. eBPF hace lo mismo para el kernel: te da un entorno seguro y reactivo dentro de la parte más sensible del sistema operativo.
De tcpdump a una máquina virtual en el kernel
El BPF original lo crearon Steven McCanne y Van Jacobson en 1992 con un objetivo muy acotado: filtrar paquetes de red de forma eficiente. Cuando usas tcpdump port 443, esa expresión se compila a un pequeño programa BPF que el kernel ejecuta sobre cada paquete para decidir si lo entrega o lo descarta. Era rápido porque evitaba copiar todos los paquetes al espacio de usuario.
En 2014, con el kernel 3.18, Alexei Starovoitov reescribió aquel diseño y nació eBPF: registros de 64 bits, un conjunto de instrucciones más rico, estructuras de datos compartidas y la posibilidad de engancharse en muchos más puntos que solo la red. Lo que empezó como un filtro de paquetes se convirtió en una plataforma de propósito general para extender el kernel. La documentación y el ecosistema viven hoy en ebpf.io.
El verificador: la pieza que lo hace seguro
La pregunta obvia es: si dejas correr código de cualquiera dentro del kernel, ¿no es eso una receta para el desastre? La respuesta de eBPF es el verificador, un componente que analiza el programa antes de cargarlo y rechaza todo lo que no pueda demostrar como seguro. Es un análisis estático: recorre todos los caminos de ejecución posibles y comprueba propiedades estrictas.
- El programa siempre termina — históricamente no se permitían bucles sin límite; hoy se aceptan bucles acotados que el verificador pueda probar que finalizan. Nada de bucles infinitos que congelen el kernel.
- No hay accesos a memoria inválida — cada lectura o escritura debe estar dentro de límites conocidos. Si tocas un puntero sin comprobar antes que no es nulo, el verificador lo rechaza.
- Solo se llaman funciones permitidas — el programa no puede invocar cualquier función del kernel, solo una lista blanca de 'helpers' aprobados.
- Complejidad acotada — hay un límite de instrucciones e estados que el verificador explora, para que la propia verificación no se eternice.
Si el programa pasa el examen, un compilador JIT (Just-In-Time) lo traduce a código máquina nativo de la arquitectura. El resultado corre prácticamente a la velocidad del kernel, sin la sobrecarga de un intérprete. Seguridad por verificación previa y rendimiento por compilación nativa: esa es la combinación que hace especial a eBPF.
Cómo funciona por dentro: puntos de enganche, mapas y helpers
Un programa eBPF no se ejecuta solo: se asocia a un evento del kernel. Esos eventos se llaman puntos de enganche, y hay muchos:
- kprobes y tracepoints — se disparan al entrar o salir de funciones internas del kernel. Sirven para depurar y medir qué hace el sistema por dentro.
- uprobes — lo mismo pero en funciones de programas en espacio de usuario.
- XDP (eXpress Data Path) — procesa cada paquete de red en el punto más temprano posible, incluso antes de que el kernel arme sus estructuras. Es lo que permite filtrar millones de paquetes por segundo.
- tc y hooks de cgroup/LSM — control de tráfico, políticas por contenedor y ganchos de seguridad.
¿Y cómo le saca un programa eBPF la información al espacio de usuario, donde está tu dashboard o tu CLI? Mediante mapas: estructuras de datos compartidas (tablas hash, arreglos, ring buffers) que viven en el kernel y que tanto el programa eBPF como tu aplicación de usuario pueden leer y escribir. El programa cuenta eventos en un mapa; tu herramienta los lee. Para todo lo demás, el programa usa helpers, esa lista acotada de funciones que el kernel le presta para tareas seguras.
Un ejemplo real con bpftrace
Escribir eBPF a mano es complejo, así que existen lenguajes de alto nivel. bpftrace es el más cómodo para empezar: con una sola línea puedes preguntarle cosas al kernel. Por ejemplo, contar cuántas llamadas al sistema hace cada proceso en vivo:
# Contar syscalls agrupadas por nombre de proceso
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
# Al pulsar Ctrl-C imprime algo como:
# @[sshd]: 142
# @[node]: 3871
# @[postgres]: 9204Detrás de esa línea, bpftrace compila tu expresión a un programa eBPF, lo pasa por el verificador, lo engancha al tracepoint correspondiente y va acumulando el conteo en un mapa. Para casos más serios se programa en C y se compila con clang. Un programa XDP mínimo que descarta todos los paquetes (útil como base para mitigar ataques) se ve así:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int drop_all(struct xdp_md *ctx) {
return XDP_DROP; // descarta el paquete antes del stack de red
}
char _license[] SEC("license") = "GPL";Ese diminuto programa, una vez enganchado a la tarjeta de red, se ejecuta sobre cada paquete entrante a velocidad de línea. Cambiar XDP_DROP por lógica que inspeccione la IP de origen es la base de los filtros anti-DDoS modernos.
CO-RE: compilar una vez, ejecutar en todas partes
Un problema histórico era que el código del kernel cambia entre versiones: una estructura tiene campos distintos en Ubuntu 20.04 que en Debian 12. Antes había que recompilar el programa eBPF en cada máquina objetivo. La solución es CO-RE (Compile Once, Run Everywhere), que apoyándose en BTF (BPF Type Format) describe la disposición de las estructuras y permite que el cargador ajuste los desplazamientos en tiempo de carga.
El resultado es que un único binario portable funciona en distintos kernels sin recompilar ni tener las cabeceras del kernel instaladas en cada servidor. Los detalles del subsistema están en la documentación oficial del kernel.
Para qué se usa en producción
eBPF dejó de ser una curiosidad de kernel hackers y hoy sostiene infraestructura crítica:
- Redes en Kubernetes — Cilium reemplaza iptables con eBPF para enrutar y aplicar políticas entre miles de pods, con mucho menos overhead.
- Balanceo de carga — Katran, el balanceador de Meta, usa XDP para repartir tráfico a escala de millones de conexiones por segundo.
- Seguridad en runtime — Falco y Tetragon detectan comportamientos sospechosos (procesos raros, accesos a archivos sensibles) observando syscalls desde el kernel.
- Observabilidad — herramientas como BCC y bpftrace permiten medir latencia de disco, conexiones TCP o llamadas lentas sin instrumentar las aplicaciones.
Por qué importa
eBPF cambia una ecuación que parecía fija: para extender el sistema operativo no hace falta confiar ciegamente en código privilegiado ni esperar a que llegue una nueva versión del kernel. Puedes añadir capacidades nuevas —monitoreo, redes, seguridad— de forma segura y dinámica, y quitarlas igual de fácil. Por eso a veces se describe como 'convertir el kernel en algo programable', comparable a lo que JavaScript hizo con la web.
Para quien construye plataformas, observabilidad o defensa, significa visibilidad y control profundos con un costo de rendimiento mínimo. Y como el verificador es la red de seguridad, ese poder llega sin el riesgo clásico de un panic de kernel por un módulo mal escrito.
Conclusión
eBPF es, en esencia, una pequeña máquina virtual segura dentro de Linux: tú escribes programas que reaccionan a eventos del kernel, un verificador comprueba que no harán daño y un JIT los ejecuta a velocidad nativa. De ese mecanismo salen Cilium, Katran, Falco y casi todas las herramientas de observabilidad modernas. Entender eBPF es entender cómo se está reescribiendo, sin recompilarse, la capa más profunda de la infraestructura actual.
📖 Versión extendida con más detalle: https://elsolitario.org/2026/05/28/ebpf-codigo-kernel-linux-sin-recompilar/?utm_source=telegraph&utm_medium=instant_view&utm_campaign=programacion