BPF – ¿qué es Berkeley Packet Filter?

El Berkeley Packet Filter (BPF) o Berkeley Filter puede ser de utilidad para todos los sistemas operativos similares a Unix como, por ejemplo, Linux. La principal tarea de esta “máquina virtual para fines especialesˮ desarrollada en 1992 es filtrar los paquetes de datos de las redes e integrarlos en el núcleo del sistema operativo. El BPF forma una interfaz de capas de seguridad de la unidad de datos o de los programas. Estas capas de seguridad tienen la función de garantizar la transmisión fiable de paquetes de datos y regular el acceso a estos paquetes.

Si uno de estos paquetes de datos llega al destinatario, el BPF lee los datos de las capas de seguridad del paquete y busca errores. De este modo, el destinatario puede resolverlos. Asimismo, permite comparar los datos con las definiciones de los filtros y así aceptar o rechazar un paquete que no se considere pertinente. Esto puede ahorrar mucha capacidad de computación.

¿Cómo funciona el Berkeley Packet Filter?

Para realizar sus funciones, el Berkeley Packet Filter se integró como un intérprete de código máquina dentro de una máquina virtual. Como resultado, el BPF ejecuta unas instrucciones en formato predefinido. Como intérprete, Berkeley Filter lee los archivos de origen, los analiza y ejecuta sus instrucciones sucesivamente. Traduce las instrucciones en código máquina para permitir la ejecución directa.

Berkeley Filter utiliza las llamadas al sistema (SysCalls), que son llamadas a funciones específicas del sistema, listas para ser utilizadas, para hacer peticiones al núcleo del sistema operativo, también llamado núcleo. Este se encarga de comprobar los derechos de acceso antes de confirmar o rechazar la solicitud. Entre las aproximadamente 330 SysCalls de Linux están las siguientes:

  • read - Permiso de lectura, con el que se puede leer un archivo
  • write - Permiso de escritura, para escribir en un archivo
  • open - Permiso para abrir los archivos o dispositivos
  • close - Permiso para los archivos o dispositivos
  • stat - Se recupera el estado de un archivo

Como el BPF está en constante desarrollo, hoy en día funciona como una máquina universal y virtual directamente en el núcleo del sistema operativo, donde tiene lugar todo el procesamiento y la organización de los datos. Con muchas nuevas características, el filtro se conoce como Extended BPF o por su abreviatura eBPF. Con esto, puede ejecutar cualquier código intermedio (código de bytes) de forma segura y durante el tiempo de ejecución (compilación just in time) directamente en el núcleo operativo. Extended BPF funciona en el núcleo operativo dentro de un entorno de pruebas y por lo tanto está protegido. Este entorno de pruebas, conocido también como sandbox o caja de arena, ayuda a minimizar el riesgo de que el sistema afecte negativamente a la lógica del núcleo operativo.

Nota

El Berkeley Filter puede funcionar tanto en modo núcleo (acceso máximo a los recursos de la máquina) como en modo usuario (acceso limitado a los recursos de la máquina).

Ventajas del Berkeley Filter

El eBPF permite filtrar los paquetes de datos para evitar que los datos irrelevantes ralenticen el rendimiento de tu PC. Así, los registros de datos inutilizables o defectuosos pueden ser rechazados o reparados desde el principio. Además, Extended BPF proporciona mayor seguridad gracias a las llamadas al sistema, ya que permite medir fácilmente el rendimiento o realizar un seguimiento con las llamadas al sistema.

En 2007 la implementación de BPF se amplió con las Zero Copy buffer extensions. Estas extensiones permiten a los controladores de los dispositivos guardar los paquetes de datos recibidos directamente en el programa sin tener que copiar los datos primero.

Programar filtros con BPF

En el modo de usuario, se pueden definir filtros individuales para la interfaz de Berkeley Filter en cualquier momento. Anteriormente, los códigos correspondientes se escribían a mano y se traducían a un código de bytes BPF. Hoy en día, gracias al Compilador de Clang LLVM, los códigos de bytes se pueden compilar directamente.

Las bibliotecas del núcleo operativo también contienen programas de ejemplo que simplifican la definición de los programas de eBPF. Existen también varias funciones de ayuda que te facilitarán la tarea.

El verificador de seguridad eBPF

La ejecución de llamadas al sistema en el núcleo siempre está asociada a algunos riesgos de seguridad y estabilidad. Antes de que se cargue una llamada al sistema de eBPF, esta debe pasar una serie de comprobaciones:

  1. En primer lugar, el sistema comprueba si la llamada del sistema ha terminado y no contiene bucles. Esto podría causar fallos en el núcleo. El gráfico de flujo de control del programa verifica que no haya instrucciones inalcanzables que no se carguen posteriormente.
  2. Antes y después de que se ejecute una instrucción, se comprueba el estado de la llamada al sistema eBPF. Esto asegura que el Extended BPF solo opera en los rangos permitidos y no accede a los datos fuera del entorno de pruebas. Para esto, no es necesario comprobar cada ruta individualmente. Normalmente basta con comprobar algunas de ellas.
  3. Por último, se configura también el tipo de SysCall. Este paso es importante para restringir qué funciones del núcleo se pueden llamar y a qué estructuras de datos se puede acceder desde SysCall. De este modo, puedes utilizar llamadas al sistema que acceden directamente a paquetes de red.

Los tipos de SysCall tienen estas cuatro funciones aproximadamente: dónde se puede adjuntar el programa, a qué funciones de ayuda del núcleo se puede acceder, si se puede acceder directamente a los datos de los paquetes de red o no, y qué tipo de objeto se pasa con prioridad en una llamada al sistema.

En la actualidad, los siguientes tipos de SysCall de eBPF son compatibles con el núcleo:

  • BPF_PROG_TYPE_SOCKET_FILTER
  • BPF_PROG_TYPE_KPROBE
  • BPF_PROG_TYPE_SCHED_CLS
  • BPF_PROG_TYPE_SCHED_ACT
  • BPF_PROG_TYPE_TRACEPOINT
  • BPF_PROG_TYPE_XDP
  • BPF_PROG_TYPE_PERF_EVENT
  • BPF_PROG_TYPE_CGROUP_SKB
  • BPF_PROG_TYPE_CGROUP_SOCK
  • BPF_PROG_TYPE_LWT_ *
  • BPF_PROG_TYPE_SOCK_OPS
  • BPF_PROG_TYPE_SK_SKB
  • BPF_PROG_CGROUP_DEVICE