Todo con las pasadas vacaciones!
Decidimos dedicar nuestro primer artículo después de las vacaciones a Linux, es decir, a nuestro maravilloso curso de
Administrador de Linux , que tenemos en la cohorte de los cursos más dinámicos, es decir, con los materiales y prácticas más relevantes. Bueno y, en consecuencia, ofrecemos artículos interesantes y
una lección abierta .
Publicado por Matteo Croce
Título original: Depuración de red con eBPF (RHEL 8 Beta)IntroduccionLa creación de redes es una experiencia emocionante, pero no siempre se evitan los problemas. La resolución de problemas puede ser complicada, ya que intenta reproducir el comportamiento incorrecto que ocurre "en el campo".
Afortunadamente, existen herramientas que pueden ayudar con esto: espacios de nombres de red, máquinas virtuales,
tc
y
netfilter
. Se pueden reproducir configuraciones de red simples usando espacios de nombres de red y dispositivos veth, mientras que configuraciones más complejas requieren conectar máquinas virtuales con un puente de software y usar herramientas de red estándar, como
iptables
o
tc
, para simular un comportamiento incorrecto. Si hay un problema con las respuestas ICMP generadas cuando el servidor SSH
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
,
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
en el espacio de nombres correcto puede ayudar a resolver el problema.
Este artículo describe cómo solucionar problemas complejos de red con
eBPF (BPF extendido) , una versión avanzada del Berkeley Packet Filter. eBPF es una tecnología relativamente nueva, el proyecto se encuentra en una etapa temprana, por lo que la documentación y el SDK aún no están listos. Pero esperemos mejoras, especialmente porque el XDP (eXpress Data Path) viene con
Red Hat Enterprise Linux 8 Beta , que puede descargar y ejecutar ahora mismo.
eBPF no resolverá todos los problemas, pero sigue siendo una poderosa herramienta de depuración de red que merece atención. Estoy seguro de que jugará un papel muy importante en el futuro de las redes.
El problemaDepuré el problema de red
Open vSwitch (OVS) , que implicó una instalación muy compleja: algunos paquetes TCP se dispersaron y se entregaron en el orden incorrecto, y el ancho de banda de las máquinas virtuales cayó de 6 Gb / s estables a 2-4 Gb / s fluctuantes. El análisis mostró que el primer paquete TCP de cada conexión con el indicador PSH se envió en el orden incorrecto: solo el primero y el único por conexión.
Traté de reproducir esta configuración con dos máquinas virtuales y, después de muchos artículos de ayuda y consultas de búsqueda, descubrí que ni
iptables
ni
nftables
pueden manipular indicadores TCP, mientras que
tc
puede, sino solo sobrescribiendo los indicadores e interrumpiendo nuevas conexiones y TCP en general
Podría ser posible resolver el problema con una combinación de
iptables
,
conntrack
y
tc
, pero decidí que este es un gran trabajo para eBPF.
¿Qué es eBPF?eBPF es una versión mejorada del filtro de paquetes Berkeley. Ella trae muchas mejoras a BPF. En particular, le permite escribir en la memoria, y no solo leer, por lo que los paquetes no solo se pueden filtrar, sino también editar.
A menudo, eBPF simplemente se llama BPF, y el propio BPF se llama cBPF (BPF clásico (clásico)), por lo que la palabra "BPF" puede usarse para referirse a ambas versiones, dependiendo del contexto: en este artículo siempre hablo de la versión extendida.
"Under the hood" eBPF tiene una máquina virtual muy simple que puede ejecutar pequeños fragmentos de bytecode y editar algunos buffers de memoria. Existen limitaciones en eBPF que lo protegen del uso malicioso:
- Los ciclos están prohibidos para que el programa siempre finalice en un momento específico;
- Solo puede acceder a la memoria a través de la pila y el búfer de memoria virtual;
- Solo se pueden llamar las funciones permitidas del kernel.
Un programa puede cargarse en el kernel de varias formas mediante
depuración y rastreo . En nuestro caso, eBPF está interesado en trabajar con subsistemas de red. Hay dos formas de usar el programa eBPF:
- Conectado a través de XDP al comienzo de la ruta RX de una tarjeta de red física o virtual;
- Conectado vía
tc
a qdisc en entrada o salida.
Para crear un programa eBPF para conectarse, simplemente escriba el código C y conviértalo a bytecode. El siguiente es un ejemplo simple con XDP:
SEC("prog") int xdp_main(struct xdp_md *ctx) { void *data_end = (void *)(uintptr_t)ctx->data_end; void *data = (void *)(uintptr_t)ctx->data; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct icmphdr *icmph = (struct icmphdr *)(iph + 1); if (icmph + 1 > data_end) return XDP_PASS; if (eth->h_proto != ntohs(ETH_P_IP) || iph->protocol != IPPROTO_ICMP || icmph->type != ICMP_ECHOREPLY) return XDP_PASS; if (iph->ttl) { uint16_t *ttlproto = (uint16_t *)&iph->ttl; uint16_t old_ttlproto = *ttlproto; iph->ttl = bpf_get_prandom_u32() % iph->ttl + 1; csum_replace2(&iph->check, old_ttlproto, *ttlproto); } return XDP_PASS; } char _license[] SEC("license") = "GPL";
El fragmento anterior, sin
include
expresiones, ayudantes y código opcional, es un programa XDP que cambia el TTL de las respuestas de eco ICMP recibidas, es decir, pongs, por un número aleatorio. La función principal obtiene la estructura
xdp_md
, que contiene dos punteros al principio y al final del paquete.
Para compilar nuestro código en el bytecode eBPF, se requiere un compilador con el soporte adecuado. Clang lo admite y crea el bytecode eBPF especificando bpf como destino en el momento de la compilación:
$ clang -O2 -target bpf -c xdp_manglepong.c -o xdp_manglepong.o
El comando anterior crea un archivo que, a primera vista, parece un archivo de objeto normal, pero después de una inspección más cercana, resulta que el tipo de computadora especificado es Linux eBPF, y no el tipo nativo de sistema operativo:
$ readelf -h xdp_manglepong.o ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Linux BPF <--- HERE [...]
Una vez recibido el contenedor de un archivo de objeto normal, el programa eBPF está listo para descargar y conectarse al dispositivo a través de XDP. Esto se puede hacer usando
ip
del paquete
iproute2
con la siguiente sintaxis:
Este comando especifica la interfaz wlan0 de destino y, gracias a la opción -force, sobrescribe cualquier código eBPF existente que ya se haya cargado. Después de cargar el bytecode eBPF, el sistema se comporta de la siguiente manera:
$ ping -c10 192.168.85.1 PING 192.168.85.1 (192.168.85.1) 56(84) bytes of data. 64 bytes from 192.168.85.1: icmp_seq=1 ttl=41 time=0.929 ms 64 bytes from 192.168.85.1: icmp_seq=2 ttl=7 time=0.954 ms 64 bytes from 192.168.85.1: icmp_seq=3 ttl=17 time=0.944 ms 64 bytes from 192.168.85.1: icmp_seq=4 ttl=64 time=0.948 ms 64 bytes from 192.168.85.1: icmp_seq=5 ttl=9 time=0.803 ms 64 bytes from 192.168.85.1: icmp_seq=6 ttl=22 time=0.780 ms 64 bytes from 192.168.85.1: icmp_seq=7 ttl=32 time=0.847 ms 64 bytes from 192.168.85.1: icmp_seq=8 ttl=50 time=0.750 ms 64 bytes from 192.168.85.1: icmp_seq=9 ttl=24 time=0.744 ms 64 bytes from 192.168.85.1: icmp_seq=10 ttl=42 time=0.791 ms --- 192.168.85.1 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 125ms rtt min/avg/max/mdev = 0.744/0.849/0.954/0.082 ms
Cada paquete pasa a través de eBPF, que finalmente realiza algunos cambios y decide si descarta el paquete o se salta.
Cómo puede ayudar eBPFVolviendo al problema de red original, recordamos que era necesario marcar varios indicadores TCP, uno por conexión, y ni
iptables
ni
tc
podían hacer esto. Escribir código para este escenario no es nada difícil: configure dos máquinas virtuales conectadas por un puente OVS y simplemente conecte eBPF a uno de los dispositivos virtuales de VM.
Parece una gran solución, pero tenga en cuenta que XDP solo admite el procesamiento de paquetes recibidos, y conectar el eBPF a la ruta
rx
de la máquina virtual receptora no tendrá ningún efecto en el conmutador.
Para resolver este problema, eBPF debe cargarse usando
tc
y conectarse a la ruta de salida de la VM, porque
tc
puede cargar y conectar programas eBPF a qdisk. Para marcar los paquetes que salen del host, eBPF debe estar conectado al qdisk de salida.
Al cargar el programa eBPF, existen algunas diferencias entre la API
XDP
y
tc
: por defecto, diferentes nombres de sección, el tipo de estructura del argumento de la función principal, diferentes valores de retorno. Pero esto no es un problema. A continuación se muestra un fragmento de un programa que marca TCP cuando se une a una acción tc:
#define RATIO 10 SEC("action") int bpf_main(struct __sk_buff *skb) { void *data = (void *)(uintptr_t)skb->data; void *data_end = (void *)(uintptr_t)skb->data_end; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct tcphdr *tcphdr = (struct tcphdr *)(iph + 1); if ((void *)(tcphdr + 1) > data_end) return TC_ACT_OK; if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) return TC_ACT_OK; if (tcphdr->syn || tcphdr->fin || tcphdr->rst || tcphdr->psh) return TC_ACT_OK; if (bpf_get_prandom_u32() % RATIO == 0) tcphdr->psh = 1; return TC_ACT_OK; } char _license[] SEC("license") = "GPL";
La compilación en bytecode se realiza como se muestra en el ejemplo XDP anterior utilizando lo siguiente:
clang -O2 -target bpf -c tcp_psh.c -o tcp_psh.o
Pero la descarga es diferente:
Ahora el eBPF se carga en el lugar correcto y los paquetes que salen de la VM están marcados. Después de verificar los paquetes recibidos en la segunda VM, veremos lo siguiente:

tcpdump
confirma que el nuevo código eBPF está funcionando y que aproximadamente 1 de cada 10 paquetes TCP tiene establecido el indicador PSH. ¡Solo se necesitaron 20 líneas de código C para marcar selectivamente los paquetes TCP que salen de la máquina virtual, reproducir el error que ocurre "en la batalla", y todo sin recompilar o incluso reiniciar! Esto simplificó enormemente la verificación de la
solución Open vSwitch , que era imposible de lograr con otras herramientas.
ConclusióneBPF es una tecnología bastante nueva, y la comunidad tiene una opinión clara sobre su implementación. También vale la pena señalar que los proyectos basados en
eBPF , como
bpfilter , se están volviendo más populares y, como resultado, muchos proveedores de equipos están comenzando a implementar el soporte de eBPF directamente en las tarjetas de red.
eBPF no resolverá todos los problemas, por lo que no debe abusar de él, pero sigue siendo una herramienta muy poderosa para la depuración de la red y merece atención. Estoy seguro de que jugará un papel importante en el futuro de las redes.
El finEstamos esperando sus comentarios aquí, y también lo invitamos a visitar nuestra
lección abierta , donde también puede hacer preguntas.