QUIC
QUIC (Quick UDP Internet Connections o Conexiones UDP rápidas en Internet) es un protocolo de red experimental sobre la capa de transporte diseñado por Jim Roskind en Google, inicialmente implantado en 2012, y anunciado como experimento ampliado en 2013. QUIC soporta un conjunto de conexiones multiplexadas entre dos extremos sobre UDP (User Datagram Protocol), y fue diseñado para proveer seguridad equivalente a TLS/SSL, junto con latencia de conexión y de transporte reducidas, y estimación de ancho de banda en cada dirección para evitar la congestión. El principal objetivo de QUIC es mejorar el rendimiento percibido de aplicaciones web orientadas a conexión que usan actualmente TCP. También proporciona un entorno para la iteración rápida de algoritmos de prevención de congestión, estableciendo control en el espacio de aplicación en ambos extremos, en lugar de hacerlo en el (lento de actualizar a nivel de cliente) espacio kernel.
En junio de 2015,[1] un borrador de trabajo (Internet Draft) sobre una especificación para QUIC fue presentado al IETF para su estandarización.[2] En 2016[3] se estableció un grupo de trabajo de QUIC.
Motivaciones y metas
Se quiere reducir la latencia a lo largo de todo Internet, proporcionando un conjunto de interacciones de usuario más receptivo. A medida que pase el tiempo, el ancho de banda irá creciendo, pero el tiempo de intercambio de mensajes, al depender de la velocidad de la luz, no podrá variar en sí mismo.[4][5] Por ello, se necesita un protocolo para que las peticiones, respuestas e interacciones en Internet tengan menor latencia con menores tiempos de retransmisión, algo a lo que QUIC consigue aproximarse. Los pares de direcciones IP y los sockets son recursos finitos. Un transporte multiplexado tiene el potencial de unificar el tráfico y de reducir la utilización de puertos, de unificar mensajes de reportes y respuestas y también de reducir la información redundante (en cabeceras por ejemplo). En resumen, desarrollar un protocolo que cumpla:
- Un uso extendido del mismo en todo el mundo.
- Reducir la pérdida de paquetes por bloqueo head-of-line.
- Baja latencia.
- Mejorar el soporte para móviles, en términos de latencia y eficiencia.
Detalles
QUIC apunta a ser el equivalente a una conexión TCP independiente, pero con una latencia mucho más reducida (el objetivo es de 0 RTT en el establecimiento de la conexión) y mejor soporte de multiplexado SPDY. Si las características de QUIC demuestran efectividad, estas características podrían migrar a una versión posterior de TCP y TLS (los cuales tienen un ciclo de despliegue notablemente más largo).
Establecimiento de conexión
Muy pocos protocolos en Internet trabajan sin al menos un intercambio de mensajes de inicialización.[6] La mayoría de los protocolos tienen una ronda de mensajes para TCP y otra para aquellos que también usan TLS antes de que los datos puedan empezar a ser enviados. Con QUIC una vez el cliente haya cacheado la información sobre el servidor, éste puede establecer una conexión encriptada sin intercambio de mensajes. QUIC soluciona los dos problemas más comunes por los cuales un establecimiento de conexión requiere varios intercambios.
En primer lugar, el spoofing de la dirección IP se soluciona de forma que el servidor envía un token de dirección IP origen. No es más que una cadena de bytes opaca desde el punto de vista del cliente. Desde el punto de vista del servidor, es un bloque encriptado autenticado que contiene, al menos, la dirección IP del cliente y una marca temporal por el servidor. Este token puede ser visto como el número de secuencia en una conexión TCP. Mientras que el cliente no cambie su dirección IP o el token no sea demasiado viejo, éste podrá seguirse usando de forma válida para el intercambio de información.
En segundo lugar, la protección a un ataque de repetición (Replay protection). Del lado del cliente, éste puede mandar un número aleatorio único (nonce) incluido en la derivación de la clave de la misma forma que lo hace TLS. En cambio para mantener una conexión sin rondas de intercambio previas, el servidor no puede realizar el mismo proceso, por lo tanto QUIC ofrece protección a este tipo de ataque únicamente después de la primera respuesta del servidor. Antes, depende de la aplicación el verificar que la información es segura. Por ejemplo, en Chrome, solo las peticiones GET son enviadas antes de una confirmación handshake.
En resumen, la primera vez que un cliente QUIC se conecta a un servidor, el cliente debe realizar un intercambio de mensajes enviando un mensaje hello (CHLO) vacío para adquirir la información necesaria. El servidor envía entonces una respuesta rejection (REJ) con la información que el cliente necesita incluyendo el token fuente de dirección y los certificados del servidor. Las próximas veces que el cliente envíe un CHLO, puede usar las credenciales cacheadas de la conexión anterior para mandar inmediatamente las peticiones encriptadas al servidor.
Control flexible de congestión
QUIC ofrece un mecanismo de control de congestión más completo que el ofrecido por TCP originalmente, lo que significa mayor información de valor. Actualmente, QUIC usa una reimplementación de TCP Cubic, aunque al ser un protocolo experimental aún se está buscando diferentes aproximaciones.[7]
Un ejemplo de esta información extra que ofrece QUIC es que cada paquete, tanto el original como el retransmitido, llevan un número de secuencia nuevo. Esto permite al emisor de QUIC distinguir ACKs para transmisiones o ACKs para retransmisiones, lo que evita el problema de ambigüedad que sufre TCP en sus retransmisiones. Un ACK de QUIC también porta explícitamente el retraso sufrido desde la recepción de un paquete y el propio reconocimiento del paquete por parte del receptor. Esto último junto con los crecientes números de secuencia permite un cálculo preciso del tiempo de intercambio de mensajes (RTT).
QUIC soporta hasta unos rangos de 256 NACK, lo que le hace más resistente al reordenamiento de bytes que TCP, además de mantener más bytes en la transferencia cuando hay reordenamiento o pérdida de estos. Tanto cliente y servidor tienen una visión más precisa de qué paquetes ha recibido su par.
Control de flujo a nivel de conexión y de paquetes de datos
QUIC implementa un control de flujo de paquetes y nivel de conexión muy parecido al seguido por HTTP/2. Funciona de la siguiente manera; el receptor informa del número total de bytes que es capaz de soportar en cada paquete de datos recibido. Durante el envío y recepción de paquetes individuales, el receptor envía paquetes de WINDOWS_UPDATE que incrementan el límite de bytes para ese flujo, permitiendo a su par enviar más datos en un mismo flujo.
Además de esta primera medida de control, QUIC implementa control de flujo a nivel de conexión para permitir cierta flexibilidad en los extremos. Es decir, si un extremo tiene un buffer con cierta capacidad de memoria para conexión, este control de flujo proporciona a los flujos de información que se reciben las ventanas de memoria adecuadas para su tamaño dentro del límite establecido. Ejemplo: Si un servidor tiene 5 MB disponibles para cada conexión, se podría limitar de forma que permita 5 flujos de 1 MB o 500 flujos de 10 KB. Gracias a este control de flujo, esta configuración puede ser dinámica e ir variando dependiendo de los flujos que recibe.
Similar al autoajuste de la ventana de recepción en TCP, QUIC implementa un autoajuste de las ventanas de flujo tanto para conexión como para el envío de información. Este autoajuste incrementa el tamaño de la ventana de datos si se percibe una limitación en el ritmo de envío, lo que provoca un aumento de velocidad en la transmisión de datos.
Multiplexado
HTTP/2 y TCP sufren del conocido bloqueo head-of-line. Puesto que HTTP/2 multiplexa muchos flujos de información sobre un único flujo TCP, la pérdida de un segmento TCP provoca el bloqueo de todos los segmentos siguientes hasta que se produzca una retransmisión, independientemente del flujo HTTP/2 que está encapsulado en estos segmentos.[8]
Debido a que QUIC está diseñado desde la base para operaciones de multiplexado, los paquetes de datos perdidos de un flujo individual generalmente solo afectarán a ese flujo en particular. Cada flujo de bits puede ser enviado inmediatamente a su llegada al destino correspondiente, de forma que los flujos sin pérdidas pueden continuar para ser ensamblados y seguir adelante en la aplicación. Únicamente los bits de cabecera HTTP de QUIC pueden producir bloqueo head-of-line ya que son tratados mediante compresión de cabeceras HPACK.
Autenticación y encriptación de la cabecera y carga útil de un paquete
Los paquetes QUIC están siempre encriptados. Aunque algunas partes de la cabecera del paquete no están encriptadas, siguen gozando de autenticación por el receptor lo que deniega la capacidad de inyección o de manipulación de datos por obra de terceros. QUIC protege las conexiones de la manipulación consciente o inconsciente de la información en los puntos intermedios entre los extremos de una comunicación. Como excepción, solamente los paquetes PUBLIC_RESET, cuya función es resetear una conexión, no están autenticados.[9]
Corrección de errores hacia delante
Con el objetivo de recuperar paquetes perdidos sin esperar a una retransmisión, QUIC actualmente trabaja con un esquema FEC (Forward Error Correction) sencillo basado en XOR. En el flujo de paquetes, se envía un paquete FEC que contiene la paridad de los paquetes de un grupo determinado. Si uno de los paquetes del grupo se pierde, el contenido de este se puede recuperar del análisis del paquete FEC y del resto de paquetes del grupo. El emisor decide cuándo enviar paquetes FEC para optimizar la transmisión en distintos escenarios. Por ejemplo, al comienzo y al final de una petición.[10]
Migración de conexión
Las conexiones QUIC están identificadas por una ID de conexión de 64 bits, generada de forma aleatoria por el cliente. QUIC puede sobrevivir a cambios de la dirección IP (al contrario que TCP) y a cambios de traducciones de NAT ya que la ID de conexión continúa siendo la misma durante estas migraciones. QUIC también proporciona verificación criptográfica automática de un cliente migrado debido a que este cliente continúa usando la misma clave de sesión para encriptar y desencriptar paquetes.[11]
Tipos de paquetes y formato
QUIC tiene paquetes especiales y paquetes normales. Hay dos tipos de paquetes especiales: Paquetes de negociación de versión y paquetes de reset públicos. Asimismo, existen dos tipos de paquetes: Paquetes de datos y paquetes FEC. Todos los paquetes QUIC deberían ser dimensionados para encajar en una MTU para evitar fragmentación IP. IPv4 implementa un máximo de 1370 bytes por paquete mientras que en IPv6 es de 1350 bytes.[12]
- Cabecera de un paquete QUIC, la cual tiene un tamaño mínimo de 2 bytes hasta 19 bytes. El formato de una cabecera es el siguiente:
- Flags públicos → Ocupa 8 bits y contiene la siguiente información: si la cabecera contiene la versión del protocolo, si es un paquete reset o no, tamaño de la ID de conexión y número de bytes de orden inferior presentes en cada paquete.
- ID de Conexión → Número aleatorio de 64 bits seleccionado por el cliente que identifica la conexión.
- Versión de QUIC → Número de 32 bits. Únicamente está presente si así lo indica el flag de versión.
- Número de paquete → Son los 8, 16, 32 o 48 bits del número de paquete, dependiendo del valor del flag correspondiente. Como mucho puede haber un número máximo de paquete de 64 bits.
- Paquetes especiales
- Paquete de negociación de versión → Solamente lo envía el servidor. Contiene las versiones del protocolo que el servidor soporta.
- Paquete de reset público → Contiene los flags públicos, una ID de conexión y el resto del paquete viene codificado con los datos de establecimiento de conexión.
- Paquetes normales
Están autenticados y encriptados, excepto la cabecera pública que está autenticada pero no encriptada. Tras desencriptar el contenido del paquete, el texto plano comienza con una cabecera privada. En esta cabecera privada se encuentra la información de FEC (Forward Error Correction). Tras esta cabecera tenemos el tipo de mensaje y su carga útil.
Fin de la conexión
Las conexiones permanecen abiertas hasta que se vuelvan inactivas durante un período de tiempo previamente negociado. Cuando un servidor decide terminar una conexión inactiva, no se lo notifica al cliente. Hay dos maneras de terminar:[13]
- Apagado explícito → Un extremo envía un paquete CONNECTION_CLOSE a su par iniciando así una finalización de la conexión. Antes de enviar este paquete, el extremo envía una advertencia (GOAWAY) avisando de que pronto se va a terminar la conexión. Una vez enviado este paquete GOAWAY, el extremo no aceptará más flujos de paquetes de su par.
- Apagado implícito → El tiempo de inactividad por defecto en una conexión QUIC es de 30 segundos, mientras que el máximo es de 10 minutos (se debe especificar en el parámetro ICSL). Tras este tiempo de inactividad, un extremo envía un paquete CONNECTION_CLOSE y finaliza la conexión.
También es posible que uno de los extremos envíe un paquete PUBLIC_RESET en cualquier momento de la conexión que provocará el fin de una conexión activa. Es el equivalente a un TCP RST.
Parámetros de transporte en QUIC
El handshake es responsable de negociar una variedad de parámetros de transporte para una conexión QUIC.[14]
Parámetros requeridos
- SFCW → Da el tamaño en bytes de la ventana de control de flujo de paquetes (Stream Flow Control Window).
- CFCW → Da el tamaño en bytes de la ventana de control de flujo de conexión (Connection Flow Control Window).
Parámetros opcionales
- SRBF → Tamaño del buffer del socket del receptor en bytes (Socket receive buffer size in bytes). El par puede querer limitar su máximo CWND a algo parecido al buffer del socket del receptor. Por defecto tiene un valor de 256 Kbytes y un mínimo de 16 Kbytes.
- TCID → Truncamiento del ID de conexión (Connection ID truncation). Si se envía por un par, indica que los ID de conexión deben ser truncados a 0 bytes. Es útil para los casos en los que un puerto del cliente es usado únicamente para una conexión.
- COPT → Opciones de conexión (Connection options). El campo contiene cualquier opción de conexión requerida por el cliente o el servidor. Son usados mayormente para experimentar e irán evolucionando con el tiempo.
Códigos de error de QUIC[15]
- QUIC_NO_ERROR → No hubo error. No es válido para mensajes de tipo RST_STREAM o CONNECTION_CLOSE.
- QUIC_STREAM_DATA_AFTER_TERMINATION → Había flujos de bits que mandar tras terminar la conexión o resetearla.
- QUIC_SERVER_ERROR_PROCESSING_STREAM → Hubo varios errores del servidor que detuvo el procesado del flujo.
- QUIC_MULTIPLE_TERMINATION_OFFSETS → El emisor recibió dos mensajes de fin de conexión en un mismo flujo.
- QUIC_BAD_APPLICATION_PAYLOAD → El emisor recibió datos de aplicación erróneos.
- QUIC_INVALID_PACKET_HEADER → El emisor recibió una cabecera de paquete errónea.
- QUIC_INVALID_FRAME_DATA → El emisor recibió un flujo bits de datos, dentro de los cuales algún segmento es erróneo.
- QUIC_INVALID_FEC_DATA → Los datos FEC son erróneos.
- QUIC_INVALID_RST_STREAM_DATA → El flujo de datos RST es erróneo.
- QUIC_INVALID_CONNECTION_CLOSE_DATA → Los datos de cerrar conexión son erróneos.
- QUIC_INVALID_ACK_DATA → Los datos ACK son erróneos.
- QUIC_DECRYPTION_FAILURE → Hubo un error al descifrar.
- QUIC_ENCRYPTION_FAILURE → Hubo un error al cifrar.
- QUIC_PACKET_TOO_LARGE → El paquete excedió el máximo tamaño de paquete.
- QUIC_PACKET_FOR_NONEXISTENT_STREAM → Los datos fueron enviados por un flujo que no existía.
- QUIC_CLIENT_GOING_AWAY → El cliente se va a desconectar. Por ejemplo, al cerrar el explorador.
- QUIC_SERVER_GOING_AWAY → El servidor se va a desconectar. Por ejemplo, al reiniciar el servidor.
- QUIC_INVALID_STREAM_ID → Un ID de flujo es inválido.
- QUIC_TOO_MANY_OPEN_STREAMS → Hay demasiados flujos abiertos.
- QUIC_CONNECTION_TIMED_OUT → Se ha llegado al límite de tiempo de inactividad prenegociado.
- QUIC_CRYPTO_TAGS_OUT_OF_ORDER → El mensaje handshake contenía etiquetas desordenadas.
- QUIC_CRYPTO_TOO_MANY_ENTRIES → El mensaje handshake contenía demasiadas entradas.
- QUIC_CRYPTO_INVALID_VALUE_LENGTH → El mensaje handshake contenía un valor de longitud inválido.
- QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE → Un mensaje con objetivos criptográficos fue enviado después del mensaje de handshake.
- QUIC_INVALID_CRYPTO_MESSAGE_TYPE → Un mensaje con objetivos criptográficos fue recibido con una etiqueta de mensaje errónea.
- QUIC_SEQUENCE_NUMBER_LIMIT_REACHED → Si se envía un paquete más causará que un número de paquete sea reusado.
Prioridad
QUIC usa el mecanismo de prioridad de HTTP/2. Un flujo puede depender de otro flujo. En esta situación, el flujo “padre” debería ser prioritario frente al flujo “hijo”. Además, los flujos “padres” tienen una prioridad explícita, pero no deberían priorizarse frente a otros flujos de la misma categoría.[16]
HTTP/2 sobre QUIC
Tanto QUIC como HTTP/2 contienen características y mecanismos que comparten entre ellos, por lo que QUIC permite a los mecanismos de HTTP/2 que sustituya estos últimos por los implementados por el protocolo de transporte, reduciendo la complejidad en el protocolo HTTP/2.[17]
Adopción
Lado cliente
[18] El código de QUIC fue desarrollado de manera experimental en Google Chrome en 2012, y fue anunciado como parte de Chromium en la versión 29 de Chrome (20 de agosto de 2013). Actualmente está activado por defecto en Chromium, y las sesiones activas se pueden ver en chrome://net-internals/#quic. También existe una extensión del explorador para indicar qué páginas web están usando QUIC.
De manera similar, QUIC ha sido introducido en Opera 16, y puede ser activado en opera://flags/#enable-quic y opera://flags/#enable-quic-https. Análogamente las sesiones activas se pueden ver en opera://net-internals/#quic.
Lado servidor
Los servidores de Google soportan QUIC. Google también ha publicado un prototipo de servidor. Además, hay varios proyectos entre la comunidad: libquic fue creado extrayendo la implementación en Chromium de QUIC y modificándola para minimizar los requisitos de dependencia; goquic provee compatibilidad de libquic en Go. También está disponible una implementación de Go llamada quic-go que impulsa el soporte experimental de QUIC en el Caddy server.[19] Finalmente tenemos quic-reverse-proxy, un canal de red que actúa como un servidor de proxy inverso, traduciendo las peticiones QUIC en HTTP plano que pueden ser comprendidas por el servidor origen.
Código fuente
Implementación | Lenguaje | Descripción |
---|---|---|
Chromium | C++ | Este es el código fuente del navegador web Chrome y la implementación de gQUIC de referencia. Contiene un cliente standalone de gQUIC y QUIC y programas de servidor que se pueden utilizar para realizar pruebas. Código fuente navegable. Esta versión también es la base de stellite de LINE y cronet de Google. |
QUIC Library (mvfst) | C++ | mvfst (pronunciado move fast) es una implementación de cliente y servidor del protocolo IETF QUIC en C++ por Facebook. |
LiteSpeed QUIC Library (lsquic) | C | Esta es la implementación de QUIC y HTTP/3 utilizada por LiteSpeed Web Server y OpenLiteSpeed. |
Quiche | Rust | Agnóstica de sockets y expone una API C para su uso en aplicaciones C/C++. |
quicly | C | Esta librería es la implementación de QUIC para el servidor web H2O. |
quic-go | Go | Esta librería proporciona soporte para QUIC en el servidor web Caddy. La funcionalidad del cliente también está disponible. |
Quinn | Rust | |
Neqo | Rust | Esta implementación de Mozilla está planeada para ser integrada en Necko, una librería de red utilizada en el navegador web Firefox |
aioquic | Python | Esta librería presenta una API sin E/S adecuada para incrustar tanto en clientes como en servidores. |
picoquic | C | Una implementación mínima de QUIC alineada con las especificaciones IETF |
pquic | C | Una implementación QUIC extensible que incluye una máquina virtual eBPF que puede cargar dinámicamente extensiones como plugins |
Véase también
- SPDY
- User Datagram Protocol
- TCP
- TLS
- Hypertext Transfer Protocol
- HTTP/2
- Protocolo de Control de Congestión de Datagramas
- Ventana deslizante
Referencias
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-00
- ↑ https://www.infoq.com/news/2015/04/google-quic-ietf-standard
- ↑ https://datatracker.ietf.org/wg/quic/documents/
- ↑ https://docs.google.com/document/d/1RNHkx_VvKWyWg6Lr8SZ-saqsQx7rFV-ev2jRFUoVD34/edit#
- ↑ https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/edit
- ↑ https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit#
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-5.2
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-5.4
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-5.5
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-5.6
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-5.7
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-6
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-7.3
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-9
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-10
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-11
- ↑ https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-12
- ↑ en:QUIC
- ↑ QUIC support in Caddy, Retrieved 13 July 2016.
Enlaces externos
- Entrada QUIC (en la Wikipedia en inglés).
- Criptografía QUIC
- QUIC por Jim Roskind
- ¿Qué es QUIC?
- QUIC, moviendo la web de TCP a UDP