Volver al inicio

Túnel ICMP en el kernel de Linux: encapsulación TCP/UDP

El artículo detalla la implementación de un túnel ICMP en el kernel de Linux que encapsula tráfico TCP/UDP en solicitudes de echo ICMP. Cubre hooks de Netfilter, trabajo con sk_buff, tasklets e inyección de paquetes en loopback.

Profundizando en el kernel: cómo TCP/UDP se esconde en echo ICMP
Advertisement 728x90

Implementación de un túnel ICMP en el kernel de Linux: Encapsulando TCP/UDP en solicitudes Echo

Un túnel ICMP a nivel del kernel de Linux permite encapsular tráfico TCP/UDP dentro de paquetes ICMP Echo Request, eludiendo filtros de red estándar. Este enfoque es útil para investigar la pila de red, probar la intercepción de paquetes y comprender cómo funciona Netfilter. El artículo desglosa la arquitectura del módulo del kernel que implementa la traducción de tráfico a través de los hooks NF_INET_POST_ROUTING y NF_INET_LOCAL_IN utilizando tasklets para el envío asíncrono.

Arquitectura del túnel ICMP en el kernel de Linux

El módulo consta de tres componentes clave: dos hooks de Netfilter y un tasklet para el envío de paquetes. El hook NF_INET_POST_ROUTING intercepta los paquetes TCP/UDP salientes antes de que se envíen a la red, los convierte en solicitudes ICMP echo y pasa el control al tasklet. El segundo hook —NF_INET_LOCAL_IN— captura los paquetes ICMP entrantes, extrae el tráfico original y lo enruta hacia la interfaz loopback para su entrega a las aplicaciones. El tasklet maneja el envío diferido de paquetes mediante dev_queue_xmit, minimizando el bloqueo en el contexto de interrupción.

El registro de hooks utiliza la prioridad NF_IP_PRI_FIRST para garantizar un procesamiento en primer orden. Esto es crítico, ya que la interferencia de otros módulos previos podría alterar la integridad de la encapsulación o desencapsulación. La gestión de memoria se basa en sk_buff —la estructura central de la pila de red de Linux—, que requiere una reserva cuidadosa de cabeceras y una liberación adecuada mediante kfree_skb en caso de errores.

Google AdInline article slot

Desglose detallado de output_hook y create_packet_output

La función output_hook se llama para cada paquete saliente. Delega la creación del envoltorio ICMP a la función create_packet_output, que:

  • Verifica si el protocolo es TCP o UDP —si no lo es, devuelve NULL y el paquete continúa su ruta sin cambios.
  • Obtiene la dirección MAC del destinatario mediante __ipv4_neigh_lookup, que es necesaria para formar la cabecera Ethernet.
  • Lineariza el sk_buff con skb_linearize, ya que las operaciones posteriores de copia de datos requieren un búfer contiguo.
  • Determina el tipo de protocolo de transporte (0 para UDP, 1 para TCP), la longitud de la cabecera y el tamaño de la carga útil.
  • Asigna un nuevo sk_buff considerando el espacio reservado bajo las cabeceras (LL_RESERVED_SPACE) y los datos de cola necesarios (needed_tailroom).
  • Construye secuencialmente las cabeceras: Ethernet, IPv4, ICMP, copiando la cabecera de transporte y los datos en el cuerpo ICMP.
  • Calcula las sumas de verificación para IP e ICMP utilizando ip_fast_csum e ip_compute_csum.
  • Devuelve el paquete listo para enviar.

Un detalle clave es el uso del campo icmp->un.echo.id para pasar información de servicio sobre el tipo de protocolo original e identificador de flujo. Esto permite al lado receptor reconstruir correctamente el paquete original sin metadatos adicionales fuera de la estructura ICMP.

Manejo de paquetes entrantes y enrutamiento loopback

En el lado receptor, la función input_hook analiza cada paquete ICMP entrante. Si coincide con el formato del túnel (Echo Request con un patrón conocido en el campo ID), el módulo:

Google AdInline article slot
  • Extrae la cabecera de transporte y los datos del cuerpo ICMP.
  • Crea un nuevo sk_buff que simula un paquete TCP/UDP entrante.
  • Establece el dispositivo de destino como la interfaz loopback (dev_set_name(skb, "lo")).
  • Cambia las direcciones MAC a localhost y llama a netif_rx_ni() para inyectar el paquete en la pila de red.

Esto hace que el kernel procese el paquete como si viniera de fuera, pero a través de la interfaz lo. Las aplicaciones reciben los datos sin cambios, sin ser conscientes de la encapsulación. Importante: no se usa NF_STOLEN aquí —el paquete no se "roba" sino que se reemplaza, por lo que se devuelve NF_ACCEPT tras una inyección exitosa.

Detalles técnicos críticos y limitaciones

La implementación enfrenta varias limitaciones técnicas:

  • Fragmentación: Los paquetes ICMP pueden fragmentarse en ruta, pero la implementación actual no maneja fragmentos —asumiendo un MTU suficiente para la encapsulación completa.
  • Seguridad: La falta de cifrado o autenticación hace que el túnel sea vulnerable a suplantaciones y ataques de hombre en el medio.
  • Rendimiento: Cada paquete requiere asignar un nuevo sk_buff, copiar datos y calcular sumas de verificación —lo que genera sobrecarga de CPU.
  • Solo IPv4: El módulo no soporta IPv6, ya que utiliza ip_hdr y __ipv4_neigh_lookup.
  • Dependencia de MAC: Requiere una entrada ARP para la IP destino; de lo contrario, el paquete se descarta.

A pesar de estas limitaciones, el proyecto es excelente para aprender: cubre operaciones de Netfilter, gestión de memoria del kernel, construcción de cabeceras de red e interacciones en tiempo real de las capas OSI.

Google AdInline article slot

Lecciones clave

  • El túnel ICMP encapsula TCP/UDP en solicitudes Echo, permitiendo eludir algunos filtros de red.
  • Se utilizan hooks de Netfilter con prioridad máxima para interceptar tráfico antes/después del enrutamiento.
  • Los tasklets permiten el envío asíncrono sin bloquear el contexto de interrupción.
  • En el lado receptor, los paquetes se inyectan en loopback para una entrega fluida a las aplicaciones.
  • La implementación es educativa —no está destinada a producción debido a la falta de seguridad y manejo de fragmentos.

— Editorial Team

Advertisement 728x90

Leer después