Se presenta el proyecto bpftime, que desarrolla un runtime y una máquina virtual para ejecutar controladores eBPF en el espacio de usuario. Bpftime permite ejecutar por completo programas eBPF de trazado e interferencia en el funcionamiento de procesos, utilizando capacidades como uprobe y la interceptación programática de llamadas al sistema. Se señala que, al excluir cambios de contexto innecesarios, bpftime permite lograr una reducción de diez veces en los costos generales en comparación con el uso de la funcionalidad uprobe y uretprobe que proporciona el núcleo de Linux. Además, bpftime simplifica significativamente la depuración, puede aplicarse potencialmente en sistemas sin núcleo de Linux y no requiere privilegios ampliados necesarios para cargar la aplicación eBPF en el núcleo. El código del proyecto está escrito en C/C++ y se distribuye bajo la licencia MIT.
La interceptación de llamadas al sistema y la integración de comprobaciones uprobe se implementa utilizando la técnica de reescritura de código ejecutable (binary rewriting), en la que las llamadas a las funciones del sistema, puntos de entrada y funciones locales se sustituyen por un salto a controladores de depuración a través de la modificación del código máquina de la aplicación en ejecución, lo que es significativamente más eficiente que organizar la interceptación utilizando uprobe a nivel del núcleo de Linux.
Se admiten operaciones de reemplazo o modificación de funciones, adjunto de controladores (hooks) y filtros, redirección, bloqueo o sustitución de parámetros de llamadas al sistema, interceptación de puntos de entrada y salida de funciones, así como la sustitución de un controlador en un desplazamiento arbitrario en el código. Bpftime puede ser adjuntado a cualquier proceso en ejecución en el sistema sin necesidad de reiniciarlos o recompilarlos. La inserción de bpftime en procesos se puede realizar para procesos en ejecución a través de ptrace, y para aquellos que se están cargando a través de LD_PRELOAD.
Dentro de bpftime se desarrolla un runtime que permite adjuntar programas eBPF a puntos de trazado de llamadas al sistema y uprobe; máquina virtual eBPF con JIT para la ejecución aislada de programas eBPF a nivel de proceso de usuario (también se admite la compilación AOT); un proceso en segundo plano para interactuar con el núcleo y organizar la compatibilidad con el subsistema uprobe del núcleo (bpftime admite el modo de carga de eBPF en el espacio de usuario desde el núcleo para facilitar la colaboración con programas eBPF en el núcleo, utilizados, por ejemplo, para el procesamiento de kprobe o para establecer filtros de red).
La máquina virtual eBPF está diseñada como una biblioteca conectable y proporciona una API similar a ubpf, lo que permite utilizarla para integrar la funcionalidad eBPF en otros proyectos. Para la agregación total de datos de varios procesos, se admite la creación de eBPF Maps compartidas que se colocan en memoria compartida. Junto con bpftime, se pueden utilizar controladores eBPF estándar escritos para uso en el núcleo, y se pueden aplicar herramientas estándar basadas en clang y libbpf para la construcción.
Con bpftime en el espacio de usuario, pueden ejecutarse sistemas de trazado como BCC, bpftrace y Deepflow. Por ejemplo, se ha demostrado la aplicación del script sslsniff del marco BCC para analizar y guardar el tráfico cifrado en nginx. En las pruebas realizadas, el rendimiento de nginx al ejecutar sslsniff en el lado del núcleo disminuye en un 58%, mientras que al mover el controlador al espacio de usuario disminuye en un 12.3%.
Arquitectura de trazado de procesos utilizando el eBPF original en el núcleo:

Arquitectura de trazado en el espacio de usuario utilizando bpftime:

Modo híbrido, en el que bpftime trabaja en conjunto con eBPF en el núcleo, por ejemplo, para establecer filtros de red o mover controladores individuales al espacio de usuario:

Entre los planes futuros se destacan: la posibilidad de inyección de fallos (Fault Injection); parcheado en caliente (Hot Patching) para modificar la lógica de operación o corregir errores en compilaciones binarios; creación de un módulo para Nginx que permita crear extensiones mediante programas eBPF (por ejemplo, para la selección dinámica de rutas, almacenamiento en caché, aplicación de políticas de seguridad y balanceo de carga); expansión de las capacidades del subsistema FUSE (por ejemplo, creando extensiones a FS en forma de programas eBPF para almacenamiento en caché o gestión de acceso).
Fuente: opennet.ru
