⏱️ Lectura: 12 min

El kernel de Linux es el corazón de casi toda la infraestructura moderna: servidores en la nube, teléfonos Android, routers y supercomputadoras. Durante décadas, cambiar su comportamiento exigía recompilar el núcleo entero o cargar módulos arriesgados que, ante el menor error, tumbaban toda la máquina. eBPF rompió ese muro: permite ejecutar pequeños programas dentro del kernel de forma segura, sin reiniciar y sin tocar su código fuente, como si le instalaras extensiones verificadas a tu sistema operativo.

📑 En este artículo
  1. TL;DR
  2. Qué es eBPF y por qué importa
  3. Cómo funciona: del código C al kernel
  4. Un ejemplo práctico: bpftrace en una línea
  5. Casos de uso reales
  6. Ventajas y desventajas
    1. Ventajas
    2. Desventajas
  7. Preguntas frecuentes
    1. ¿eBPF solo funciona en Linux?
    2. ¿Necesito saber C para usar eBPF?
    3. ¿Es seguro ejecutar código dentro del kernel?
    4. ¿Qué diferencia hay entre BPF clásico y eBPF?
    5. ¿Qué versión de kernel necesito?
    6. ¿eBPF reemplaza a iptables?
  8. Referencias

En esta guía vas a entender qué es eBPF, cómo logra ejecutar código en un lugar tan delicado sin romper nada y por qué proyectos como Cilium, Falco o Katran lo usan en producción a escala masiva.

TL;DR

  • eBPF (extended Berkeley Packet Filter) ejecuta programas dentro del kernel de Linux sin recompilarlo ni cargar módulos.
  • Un verificador estático analiza cada programa antes de cargarlo: si puede colgar el kernel o leer memoria indebida, lo rechaza.
  • El programa se compila a un bytecode propio y un compilador JIT lo traduce a código máquina nativo para correr a toda velocidad.
  • Se engancha a eventos del kernel: llamadas al sistema, paquetes de red, funciones internas (kprobes) y puntos de trazado (tracepoints).
  • Casos de uso: observabilidad (bpftrace, BCC), redes y balanceo (Cilium, Katran de Meta) y seguridad en runtime (Falco).
  • XDP procesa paquetes en la tarjeta de red antes de que el kernel los toque: ideal para mitigar DDoS a millones de paquetes por segundo.
  • Requiere Linux moderno (kernel 4.x+ para lo básico, 5.x+ para features actuales) y privilegios de administrador para cargar programas.

Qué es eBPF y por qué importa

eBPF significa extended Berkeley Packet Filter, pero el nombre se quedó corto frente a lo que hace hoy. Su antepasado, el BPF clásico de 1992, era un mini-lenguaje para filtrar paquetes de red: cuando usás tcpdump y escribís un filtro como port 443, por debajo se compila a BPF para descartar dentro del kernel los paquetes que no te interesan, sin copiarlos al espacio de usuario. Era rápido, pero servía para una sola cosa.

A partir de 2014, los desarrolladores del kernel extendieron ese motor hasta convertirlo en una máquina virtual de propósito general dentro de Linux. Esa es la parte interesante: eBPF ya no filtra solo paquetes, sino que puede engancharse a casi cualquier evento del sistema —una llamada al sistema, la ejecución de una función interna del kernel, la llegada de un paquete— y correr código tuyo en ese instante exacto.

La mejor analogía es el navegador web. Tu navegador no te deja instalar un programa nativo con acceso a toda la computadora cada vez que entrás a una página; en su lugar, ejecuta JavaScript dentro de un sandbox controlado. eBPF hace lo mismo con el kernel: te deja ejecutar lógica personalizada en el núcleo, pero dentro de una caja con reglas estrictas que impiden que tu código cuelgue la máquina o lea memoria que no debe. Ganás poder sin ganar la capacidad de destruirlo todo.

Esquema de eBPF ejecutando programas dentro del kernel de Linux
eBPF ejecuta código en el kernel sin recompilarlo ni reiniciar.

Cómo funciona: del código C al kernel

El recorrido de un programa eBPF tiene etapas bien definidas. Entenderlas es clave para no verlo como magia:

  1. Escritura — Normalmente escribís el programa en un subconjunto de C (o en Rust con frameworks como Aya). Es código pequeño y acotado, sin bucles infinitos ni asignación dinámica de memoria.
  2. Compilación — Un compilador como clang/LLVM traduce ese C a bytecode eBPF, un conjunto de instrucciones propio e independiente de la arquitectura del procesador.
  3. Carga y verificación — El bytecode entra al kernel mediante la llamada al sistema bpf(). Antes de aceptarlo, el verificador analiza todos los caminos posibles de ejecución para demostrar que el programa termina, no accede a memoria fuera de límites y no filtra datos del kernel. Si no puede probarlo, lo rechaza.
  4. Compilación JIT — Una vez aprobado, un compilador Just-In-Time traduce el bytecode a código máquina nativo del procesador, de modo que corre a la misma velocidad que el resto del kernel.
  5. Anclaje (hook) — El programa queda enganchado a un evento concreto: un tracepoint, una kprobe sobre una función del kernel o un punto de la pila de red.
  6. Comunicación — Para compartir datos con el espacio de usuario, eBPF usa mapas (estructuras tipo tabla hash o array) a las que acceden tanto el programa en el kernel como tu aplicación.
graph LR
  A["Código C / Rust"] --> B["clang / LLVM"]
  B --> C["Bytecode eBPF"]
  C --> D["Verificador del kernel"]
  D -->|rechazado| E["Error: no se carga"]
  D -->|aprobado| F["Compilador JIT"]
  F --> G["Código máquina nativo"]
  G --> H["Hook: kprobe / red / syscall"]
  H --> I["Mapas compartidos con userspace"]

El verificador es la pieza que hace todo esto seguro. Es, en esencia, un demostrador matemático: recorre el grafo de ejecución del programa y rechaza cualquier cosa que no pueda garantizar como segura. Por eso eBPF prohíbe bucles sin límite conocido y pone un tope al tamaño del programa.

// Programa eBPF que cuenta ejecuciones de execve()
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 1);
    __type(key, __u32);
    __type(value, __u64);
} contador SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_execve")
int contar_execve(void *ctx) {
    __u32 clave = 0;
    __u64 *valor = bpf_map_lookup_elem(&contador, &clave);
    if (valor)
        __sync_fetch_and_add(valor, 1);
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

Este ejemplo declara un mapa con un contador, se engancha al tracepoint que se dispara cada vez que un proceso ejecuta execve() (es decir, cada vez que se lanza un programa nuevo) e incrementa el contador de forma atómica. Tu aplicación en espacio de usuario puede leer ese mapa cuando quiera para saber cuántos procesos se han lanzado.

💭 Clave: el verificador no es un detalle de implementación, es la razón por la que eBPF existe. Sin él, ejecutar código arbitrario en el kernel sería tan peligroso como un módulo tradicional, que con un solo puntero mal escrito puede caer en un kernel panic.

Un ejemplo práctico: bpftrace en una línea

Escribir C y manejar el ciclo de compilación y carga es tedioso. Por suerte existen herramientas de alto nivel que generan todo eso por vos. La más popular es bpftrace, que te deja escribir sondas en una sola línea con una sintaxis parecida a awk.

# Contar qué procesos abren archivos, en vivo
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'

Ese comando se engancha al momento en que cualquier proceso abre un archivo y, agrupando por nombre de proceso (comm), cuenta cuántas veces lo hace cada uno. Al cortar con Ctrl-C, bpftrace imprime una tabla ordenada. Todo esto ocurre en vivo, sobre un sistema en producción, sin reiniciar nada ni modificar las aplicaciones que estás observando.

# Distribución de tamaños de lectura de bloque, como histograma
sudo bpftrace -e 'tracepoint:block:block_rq_issue { @bytes = hist(args->bytes); }'

Con un histograma podés ver, por ejemplo, la distribución de las operaciones de disco en tiempo real. Este tipo de visibilidad —medir lo que pasa dentro del kernel sin instrumentar el código de tus apps— es exactamente lo que hizo famoso a eBPF en el mundo de la observabilidad.

💡 Tip: si querés empezar hoy, instalá bpftrace desde el gestor de paquetes de tu distribución y probá los one-liners. Es la puerta de entrada más amable a eBPF: ves resultados en segundos sin escribir una sola línea de C.
Terminal mostrando bpftrace trazando llamadas al sistema en vivo
bpftrace permite trazar el kernel con una sola línea.

Casos de uso reales

eBPF dejó de ser un experimento de kernel hackers para sostener infraestructura crítica en empresas enormes. Estos son los frentes donde más se usa:

  • Observabilidad y trazado — Herramientas como BCC y bpftrace permiten medir latencias, llamadas al sistema y uso de recursos sin overhead apreciable. Brendan Gregg popularizó este enfoque en Netflix, y proyectos como Pixie o Parca lo llevan a Kubernetes.
  • Redes y balanceo de cargaCilium usa eBPF para enrutar y filtrar tráfico entre contenedores en Kubernetes, reemplazando las viejas reglas de iptables por un plano de datos mucho más rápido. Meta desarrolló Katran, un balanceador de capa 4 basado en eBPF que distribuye su tráfico a escala global.
  • Seguridad en tiempo de ejecuciónFalco (proyecto de la CNCF) y Tetragon observan las llamadas al sistema en vivo para detectar comportamientos sospechosos: un proceso que abre una shell inesperada, lecturas a archivos sensibles o conexiones de red anómalas.
  • Procesamiento de paquetes con XDPeXpress Data Path ejecuta programas eBPF en el punto más temprano posible, en el driver de la tarjeta de red, antes de que el kernel construya sus estructuras. Cloudflare lo usa para descartar tráfico de ataques DDoS a millones de paquetes por segundo, gastando una fracción de la CPU.
📌 Nota: en Kubernetes, Cilium puede sustituir buena parte de iptables y kube-proxy. Cuando hay miles de servicios y reglas, el enfoque clásico se vuelve lento; el plano de datos eBPF escala mucho mejor porque resuelve el reenvío con tablas hash en el kernel en lugar de listas de reglas lineales.

Ventajas y desventajas

Como toda tecnología, eBPF resuelve problemas concretos a cambio de ciertos costos. Vale la pena tener claros ambos lados.

Ventajas

  • Seguridad — El verificador garantiza que un programa mal escrito no cuelgue el kernel, algo imposible de asegurar con un módulo tradicional.
  • Rendimiento — Al correr dentro del kernel y compilarse a código nativo con JIT, evita los costosos cambios de contexto entre el espacio de usuario y el kernel.
  • Sin reinicios ni módulos — Cargás y descargás funcionalidad en caliente, sin recompilar el kernel ni reiniciar el servidor.
  • Visibilidad total — Podés observar el sistema sin modificar las aplicaciones que corren sobre él.

Desventajas

  • Curva de aprendizaje — Entender el verificador y depurar por qué rechaza un programa aparentemente válido puede ser frustrante al principio.
  • Dependencia del kernel — Necesitás una versión moderna de Linux; las features más nuevas exigen kernels recientes. La portabilidad entre versiones mejoró mucho con CO-RE y BTF, pero todavía requiere cuidado.
  • Superficie de ataque — Es una tecnología muy poderosa que exige privilegios elevados; un eBPF mal gestionado amplía la superficie de ataque del sistema.
  • Casi exclusivo de Linux — Aunque existe un port de eBPF para Windows, el ecosistema maduro vive en Linux.
⚠️ Ojo: cargar programas eBPF requiere privilegios de administrador (históricamente CAP_SYS_ADMIN, hoy también la más acotada CAP_BPF). No es algo que un usuario sin permisos pueda hacer, y por buenas razones: quien carga eBPF puede observar e influir en lo que ocurre dentro del kernel.

📖 Resumen en Telegram: Ver resumen

Preguntas frecuentes

¿eBPF solo funciona en Linux?

Nació en el kernel de Linux y ahí vive su ecosistema más maduro. Existe un proyecto que lleva eBPF a Windows, impulsado por Microsoft, pero la inmensa mayoría de herramientas y casos de producción siguen siendo sobre Linux.

¿Necesito saber C para usar eBPF?

No para empezar. Herramientas como bpftrace te dejan escribir sondas potentes en una sola línea sin tocar C. Si querés construir programas eBPF complejos y a medida, ahí sí conviene saber C o usar frameworks en Rust como Aya.

¿Es seguro ejecutar código dentro del kernel?

Sí, y esa es precisamente la gran ventaja de eBPF frente a los módulos tradicionales. El verificador del kernel analiza cada programa antes de cargarlo y rechaza cualquiera que pueda colgar el sistema, entrar en un bucle infinito o acceder a memoria indebida.

¿Qué diferencia hay entre BPF clásico y eBPF?

El BPF clásico (cBPF) servía casi únicamente para filtrar paquetes de red. eBPF amplió el motor a una máquina virtual de propósito general que se engancha a syscalls, funciones del kernel y eventos de trazado, con registros de 64 bits, mapas y un compilador JIT.

¿Qué versión de kernel necesito?

Lo básico funciona desde Linux 4.x, pero para aprovechar las features modernas (como CO-RE para portabilidad o los hooks más recientes) conviene un kernel 5.x o superior. Cuanto más nuevo, mejor soporte.

¿eBPF reemplaza a iptables?

En muchos escenarios, sí. Cilium, por ejemplo, sustituye buena parte de iptables y kube-proxy en Kubernetes con un plano de datos basado en eBPF que escala mejor cuando hay miles de servicios y reglas.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.


Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.