Tiempo de lectura: 10 minutos

El protocolo que combina lo mejor de UDP y TCP e integra seguridad criptográfica, comunicación full duplex con persistencia de sesión, reduce drásticamente la latencia y aumenta el rendimiento

Si te interesa la programación, la ciberseguridad o las redes, o simplemente eres curioso, debes aprender como funciona QUIC y porque va a ser tan importante en los próximos años.

La forma en que los ordenadores se comunican va a sufrir un cambio muy importante y ya trabajes haciendo servidores HTTP, analizando paquetes o interceptándolos (o simplemente te gusta entender la tecnología que utilizas), leer este artículo es un buen primer paso para entender el cambio que se aproxima.

OSI, TCP y UDP

Los ordenadores, al igual que hacemos los humanos, utilizan una serie de estándares para comunicarse entre sí. Estos “idiomas” son esencialmente normas que dictan la forma en que las comunicaciones entre ordenadores deben iniciarse, negociarse, mantenerse… Dichas normas establecen unas directrices necesarias para que dos ordenadores sepan como formar (para enviar) un paquete y como procesarlo.

Hay muchos protocolos distintos, ya cada uno soluciona su propio problema, y se pueden clasificar o bien por su finalidad, o bien por la capa en la que operan. El modelo OSI es un esquema teórico para clasificar las distintas capas en las que viaja información en redes de ordenadores, explicarlo en profundidad da para un artículo entero y hay muchos muy buenos en internet, pero eso no es lo que nos interesa hoy.

Imagen: Mario Hinojosa Freire

En la capa 4 de ese esquema se hallan dos protocolos que son la base de muchos otros que operan en capas más altas, TCP y UDP.
Transmission Control Protocol o TCP es un protocolo que sacrifica velocidad por tolerancia a fallos y garantía de integridad, esto se logra estableciendo las conexiones con un triple apretón de manos (explicado mejor más abajo) y verificando por separado mediante unos identificadores en cada paquete que todos han llegado correctamente a su destino.

Esto es clave para transportar información en la que la integridad es crítica. Por ejemplo, si descargamos un PDF que ha de dividirse en 10 paquetes, uno de ellos falla y no es reenviado, el archivo quedaría totalmente corrupto e inaccesible.

User Datagram Protocol o UDP es todo lo contrario, consiste en algo más “a lo bruto”, simplemente envía los paquetes sin importar si llegan o no, sin apretones de manos ni nada que se le parezca, esto resulta en fiabilidades reducidas y velocidades mayores, y es ideal para juegos multijugador o streaming de vídeo. Los paquetes son también más ligeros que los de TCP.

tcp-vs-udp
Imagen sacada del blog de zwoong

HTTP Y HTTPS

HTTP y HTTPS, los dos principales protocolos para intercambio de información en internet, están construidos sobre TCP. La “S” de HTTPS viene de secure (seguro), dicha seguridad se consigue cifrando la información con otro protocolo llamado TLS (Transport Layer Security), que significa Seguridad de la Capa de Transporte, en referencia al modelo OSI.


En el diagrama abajo, se ve lo que mencionaba arriba sobre el triple apretón de manos, para que TCP pueda llevar a cabo sus funciones de control de integridad, es necesario establecer una conexión con la que llevar la cuenta de los paquetes, esto se hace con tres paquetes iniciales, SYN, SYN + ACK y ACK.

Imagen: Mario Hinojosa Freire

Tras ese apretón inicial, entra la seguridad que aporta TLS, con una serie de paquetes en los que se intercambian llaves, se verifican certificados y se negocian algoritmos de cifrado a utilizar, se establece un canal seguro en el que la información viaja a salvo de potenciales intrusos.


En la siguiente imagen se aprecia la mejora hecha de TLS 1.2 a TLS 1.3 respecto a la cantidad de paquetes necesarios para establecer el canal seguro.

Imagen: Mario Hinojosa Freire

Los números de TCP + TLS

Sin embargo, cada vez que visitamos una página web, esto es lo que sucede en números. En este ejemplo real basado en una captura de tráfico tras visitar araintel.com se aprecia el peso de los distintos mensajes. Todos ellos suman un total de 3659 bytes, es decir, 3.7 kilobytes.

Algo más de una página de texto, apenas se nota, sin embargo, hemos de tener en cuenta la cantidad de veces que nuestra máquina hace esto al día. Para ponerlo en una mira más grande, si asusimos esa suma como la media y lo multiplicamos, con una estimación de 30.000.000.000.000 de secuencias como esta todos los días, una estimación plausible, estamos hablando de 1.0977e+17 (109770000000000000) bytes, o lo que es lo mismo, 109.77 petabytes.

La latencia en términos de tiempo varía en función de cada red, variando entre los 100 y 300ms en redes modernas por cada handshake TCP + TLS, pero es un tráfico que podría ahorrarse con QUIC.

Imagen: Mario Hinojosa Freire

QUIC

Nos apoyamos en TCP para construir muchos de los protocolos que más utilizamos para comunicar ordenadores entre sí, sin embargo, sus pros vienen con su propio conjunto de contras que ha habido que sortear, como su ausencia de estado (de las comunicaciones) o sus costosos 3-way handshake.

QUIC solventa varios de estos problemas, juntando las virtudes de TCP y UDP a la vez. El protocolo es un protoclo orientado a conexiones y stateful (mantiene un registro del estado de las comunicaciones) Aprovecha el tamaño de los datagramas UDP para ser más veloz, y la seguridad viene integrada con TLS 1.3.

Además permite la creación de distintos tipos de canales en función de la naturaleza de la conexión: unidireccionales y bidireccionales. Un esquema credit-based (basados en “saldo”) permite limitar la cantidad de creaciones de conexiones y la cantidad de información enviada en cada paquete.

Imagen: Mario Hinojosa Freire

Esto se logra con distintos bits que actúan como identificador del remitente del paquete y el tipo de conexión.

Tabla sacada de la especifación técnica de QUIC

Adicionalmente, se mantiene el estado de la conexión y se modifica en función de los paquetes que van llegando de la parte remitente.
En el diagrama bajo este parráfo los estados están en rectángulos, los paquetes (o frames) en fondo gris.

Cualquier endpoint que soporte el protocolo QUIC puede iniciar un stream, en este caso, imaginemos que crea uno bidireccional. Envía el frame correspondiente a la parte receptora para iniciar el stream.


Una vez este esta creado, el remitente entra en modo Ready, estado en el que se mantiene hasta que se envía el primer frame STREAM, que es el tipo de frame en el que viaja la información. Entonces el remitente entra en modo Send y continúa enviando frames STREAM (y retransmitiendo de ser necesario). El frame STREAM_DATA_BLOCKED se envía si el remitente es bloqueado por límites propios del protocolo (lo que se mencionaba arriba sobre el “saldo”, explicado mejor más abajo).


Cuando todos los STREAM que debían enviarse han salido, se envía un STREAM + FIN y el remitente se pone en estado Data Sent, entonces, espera a que el receptor verifique mediante frames ACK que todos los paquetes se han recibido correctamente y no es necesario retransmitir nada, entonces queda en estado Data Recvd, que es definitivo.


Cabe destacar que como se aprecia en el diagrama, en cualquier momento se puede enviar un frame RESET_STREAM o un STOP_SENDING. Ambos paquetes hacen entrar al remitente en estado Reset Sent, y una vez se recibe la verificación por parte del receptor de dicho frame, se entra en el estado definitivo Reset Recvd.

Imagen: Mario Hinojosa Freire

En la parte receptora es similar, el endpoint receptor entra en modo Recv una vez recibe el primer STREAM / STREAM_DATA_BLOCKED / RESET_STREAM, en streams bidireccionales, MAX_DATA_STREAM / STOP_SENDING también provocan entran en modo Recv.


A partir de entonces, el receptor y remitente intercambian paquetes y la información entrante es almacenada en un búfer para ser consumida por la aplicación. El receptor envía paquetes MAX_DATA_STREAM conforme la aplicación consume paquetes para avisar al remitente de que puede enviar nuevos paquetes. Cuando recibe el paquete STREAM + FIN, deja de enviar MAX_DATA_STREAM porque ya no es necesario, entra en estado Size Known.


Entonces, cuando toda la información ha sido recibida, entra en estado Data Recvd, el receptor permanece en ese estado hasta que toda la información ha sido consumida por la app y el búfer queda vacío, en ese momento entra en estado Data Read, que es definitivo.


Similarmente al diagrama del remitente, en cualquier momento puede llevar un frame RESET_STREAM que provoque el cambio al estado Reset Sent, que según la implementación del protocolo puede cambiar o bien a Data Recvd o a Reset Recvd (que es definitivo) según cómo se quieran procesar los paquetes ya recibidos.

Imagen: Mario Hinojosa Freire

Aquí se aprecia en una tabla sacada de la especificación técnica de QUIC en la cual se ven los estados de la conexión según los estados individuales del emisor y el receptor.

Tabla sacada de la especificación técnica de QUIC

Control de flujo

Como se ha mencionado antes, QUIC implementa una serie de medidas de control de flujo para limitar la cantidad de paquetes que se almacenan en el búfer. Con esto se evita que un emisor muy rápido o un agente malicioso sobrecargue la capacidad del receptor.

Esta función de control se implementa en ambos endpoints individualmente así como en la conexión en sí para asegurar que se respetan correctamente estos mecanismos de protección de memoria.


En los diagramas se puede observar como la implementación de los frames MAX_STREAM_DATA / MAX_DATA controlan desde el lado receptor el máximo permitido, estos parámetros se establecen durante el handshake y han de ser respetados por el emisor si quiere mantener la conexión activa. Este máximo puede ser actualizado por el receptor enviando un MAX_DATA.

El control de flujo está pensado para ser eficiente y tener buen rendimiento, optimizando el tamaño de los paquetes que ambas partes intercambian para actualizar información. QUIC mantiene un duplex que se adapta rápidamente a los recursos de red y memoria disponibles en el emisor y el receptor, idealmente, se deben enviar las actualizaciones de control flujo junto a las “verificaciones” (ACK) para reducir la cantidad de paquetes enviados.

Las cancelaciones de stream cortan el tráfico en una dirección de forma brusca, de ser una conexión bidireccional, enviar un RESET_STREAM no interrumpiría la conexión al completo, si no la dirección en la que ese frame ha sido enviado. Esto hace que el protocolo sea aún más granular y permita iniciar conexiones bidireccionales, que cambian a unidireccionales cuando la información ya no requiere un duplex.

Conexiones

Se denomina conexión QUIC a un estado compartido entre un cliente y un servidor, cada conexión tiene un identificador único que permite al emisor mantener múltiples conexiones activas con varios clientes, y dicha conexión se establece sobre un handshake que establece un canal criptográficamente seguro gracias a QUIC-TLS.

La implementación de estos identificadores ligados a las sesiones permiten que las conexiones sobrevivan a cambios en la red, como por ejemplo al cambiar de Wi-Fi a datos móviles (una ocurrencia extremadamente común). Esto se llama migración de canal, y está integrada en QUIC, sucediendo de forma casi impercetible para el cliente aprovechando el identificador ligado a la conexión.

La especificación técnica tiene detalles mucho más extensos sobre los entresijos del protocolo, para no extenderme demasiado en los detalles, dejaremos los tecnicismos específicos aquí.

Las posibilidades de QUIC

QUIC es un protocolo pensado para el internet moderno, juntando las virtudes de UDP y TCP añadiendo varias mejoras propias sin apenas contras. TCP fue un gran aporte en su día, ya que las necesidades en lo que respecta a hardware e infraestructura de red eran otras totalmente distintas.

Hoy en día, los anchos de banda soportados por las redes internas e internet no tienen nada que ver con las que había cuando se diseñó TCP, y lo mismo sucede con el hardware de los equipos que participan en esas redes.

Sin embargo, seguimos utilizando protocolos basados en TCP para muchísimas de las comunicaciones diarias en las que nuestras máquinas participan, viéndonos obligados a buscar soluciones a una serie de problemas.

  • Alta latencia, debido a los handshakes “caros” de realizar por la cantidad de paquetes requeridos
  • Cuellos de botella, ya que un solo paquete fallido puede ralentizar la conexión completa
Imagen: Mario Hinojosa Freire
  • Seguridad criptográfica implementada en una capa distinta, (TCP + TLS)
  • Recuperación lenta en caso de perdida de paquetes

QUIC afronta estos problemas y ofrece soluciones:

  • La información fluye de forma inmediata (0-RTT: Zero Round Trip Time)Enlace al artículo de la imagen
  • El cifrado está incluido de forma nativa
  • Pueden abrirse múltiples streams simultáneos sin interferencias entre sí
  • Migración de sesión: QUIC puede mantener una sesión incluso al cambiar de redes, por ejemplo, al pasar de Wi-Fi a datos móviles.

Lo cual supone una serie de mejoras críticas en el internet moderno:

  • Latencia reducida
  • Mayor fiabilidad
  • Mejor rendimiento en móvil
  • Seguridad nativa incorporada
  • Rendimiento aumentado

En definitiva, QUIC, a diferencia de TCP, está pensado para la infraestructura moderna y las necesidades actuales de internet. Una página puede llegar a enviar cientos de paquetes solo para cargar, sobre TCP esto es ineficiente, pero las virtudes de QUIC se aprecian si imaginamos este escenario con el nuevo protocolo.


Al llegar a una página, la seguridad se establece a la vez que la propia comunicación, y podemos iniciar una comunicación bidireccional en la que enviamos al servidor información crítica como cookies de sesión y preferencias, todo mientras vamos recibiendo imágenes, archivos estáticos y JavaScript.

Todo esto sucede en modo full duplex y una vez el cliente ya no necesita enviar más información y solo recibirla, puede cortar la transmisión, cambiando a modo unidireccional para recibir contenido del servidor. Existe incluso la posibilidad de abrir multiples canales entre ambos endpoints para mayor rendimiento, por ejemplo, un canal para uniplex para archivos y uno full duplex para autenticación.

Con TCP esto sería mucho más complejo, sin estado ni seguridad nativa, con más paquetes, más bytes y más latencia.

Imagen: Mario Hinojosa Freire

El futuro de QUIC

Sin duda QUIC es el protocolo del futuro, además, debido a su versatilidad, tiene usos potenciales en campos más allá de web, como IoT, gaming, streaming… HTTP/3 ya prescinde de TCP + TLS para sustituirlo por QUIC, y cabe esperar que se convierta en el estándar en unos años. Sin embargo, como todas las transiciones, esto trae su propia colección de retos.

  • Capacidad reducida de diagnóstico y monitoreo: Al ser un protocolo más seguro desde un punto de vista criptográfico y de privacidad (ya que encripta más información que sus precedesores y lo hace antes cronológicamente en una sucesión de paquetes), la capacidad de monitorizar la red y diagnosticar problemas (para firewalls de capa 7 o WAFs, centros de SOC), se ve dificultada.

  • Problemas de infraestructura de legado: Actualizar toda la infraestructura de QUIC para que su uso se vuelva generalizado y estándar supone conseguir que todos los servidores y sobre todo los clientes (especialmente navegadores) soporten el protocolo, sin embargo, supone un aún mayor reto el hecho de que UDP ha tenido usos más bien minoritarios respecto a TCP, y algunos firewalls lo bloquean por defecto. A esto se suman los problemas arriba mencionados, SIEM, WAF, IDS, IPS… Todas estas tecnologías deben adaptarse para que la implementación sea completa.

En general, la transición hacia QUIC va a tener algunos retos en lo que respecta a que toda la infraestructura de internet se adapte al cambio y lograr convertirlo en su estándar, pero sus mejoras son notables e innegables, y cabe esperar que en unos años la mayoría de comunicaciones que ahora se establecen vía TCP cambien a QUIC.

MIS REDES/CONTACTO

YouTube
LinkedIn
GitHub
urban333xodus@gmail.com