Tunneling: Por qué tunelizar TCP sobre TCP no es bueno

Hoy hablaremos de tunneling, particularmente de encapsulamiento de segmentos tcp sobre tcp, y por qué muchas veces esto no es buena idea.


En una oportunidad con un alumno en la Universidad, durante clases de tunneling y vpn, discutimos un caso particular de por qué no es bueno armar un túnel de red encapsulando, directa o indirectamente, una conexión TCP sobre otra TCP.

Justamente hoy leyendo un artículo interesante me vino el tema a la cabeza, así que pasaré a explicar un poco.

Tunneling y más tunneling

Sabemos que montar un túnel de red implica encapsular un protocolo de una determinada capa sobre otro protocolo de la misma capa, o capas superiores.

Ahora bien, cuando montamos, principalmente, túneles para redes virtuales o redes privadas virtuales (VPN) muchas veces tenemos la duda de si montar un túnel encapsulando un protocolo de capa superior que trabaja sobre tcp (como ser http, ssh, u openvpn) sobre un protocolo de transporte TCP o UDP. La respuesta rápida sería TCP, sin dudas, es más confiable, y las vpn’s irán mejor… analicemos un poco por qué esto es una falacia en la mayoría de los casos.

Algoritmo de retransmisiones TCP

TCP divide el flujo de datos en segmentos que luego son encapsulados en paquetes IP para poder enviarse por la red. Cada segmento lleva su número de secuencia, que es el número de bytes que corresponden dentro del stream, y un número de acuse de recibo, o ack (del Inglés, acknouledge number, o ack), que indica en un segmento recibido que número de secuencia está esperando recibir el otro nodo de la red (en conexiones tcp en la que ambos transmiten) (ver RFC793).

Ahora bien, los paquetes IP pueden perderse, o duplicarse, o desordenarse, los números de secuencia de tcp permiten re-ordenar los segmentos y rearmar los flujos de datos en el receptor.

El número de ack también le permite al receptor de un segmento saber si el segmento que envió anteriormente fue recibido por el otro extremo. Si un segmento no se recibe en determinado tiempo (timeout de tcp) el emisor asume que el paquete subyacente se perdió, y procederá a re-enviarlo.

TCP usa timeouts adaptativos, debido a que no todas las conexiones tienen las mismas velocidades, por ejemplo, una conexión TCP/IP local en una LAN, y una entre cliente y servidor en Internet.

Las conexiones TCP inician con valores estimados, y van cambiando dinámicamente con cada segmento recibido. El algoritmo utilizado para esto, si interesa, está explicado en la RFC2001.

Los detalles explicados en la RFC no son importantes en nuestro contexto, pero solo una cosa es interesante, cuando un segmento vence su timeout, el siguiente segmento será transmitido con un timeout mayor, y en general el incremento es exponencial.

Apilando segmentos TCP en el stack

Los timeouts de TCP funcionan bien en la mayoría de las conexiones. Si las comunicaciones son muy malas, los timeouts se incrementarán, pero los segmentos TCP llegarán verificados al destinatario.

Si en alguna conexión estamos apilando un segmento tcp sobre otro, por ejemplo, una encapsulación PPP sobre SSH, u OpenVPN sobre TCP, por ejemplo, el TCP interior tendrá unos determinados timers, y el TCP exterior otros diferentes.

Supongamos ahora una situación en la que el el TCP interior tenga timers cortos y el TCP exterior timers largos debido a diversas características de la comunicación. Supongamos que la conexión exterior comienza perdiendo paquetes, la capa TCP exterior incrementará sus timers y pedirá retransmisiones de los segmentos perdidos. Dado que esto demorará la llegada de nuevos segmentos al destinatario, los timers del TCP interior también se vencerán, y también pedirá retransmisiones.

Ahora bien, como el TCP interior tiene menores timers, éste pedirá más retransmisiones que el TCP exterior, puesto que se vencerán los timeouts más rápido, y esta cantidad de retransmisiones será mayor que las que el TCP exterior puede procesar con sus timers, produciendo acumulación de retransmisiones.

El TCP interior estará pidiendo, entonces, retransmisiones de segmentos, sin «saber» que el TCP exterior también lo está haciendo, que también es un transporte confiable.

En el siguiente gráfico he intentado representar de una manera muy burda el encapsulamiento de una conexión OpenVPN y similares, y he marcado en color los headers TCP que estarían entrando en conflicto, generando colisiones de retransmisiones.

tcpovertcp tunneling
Para cerrar este post, podemos decir que no por tratarse de un protocolo confiable (que no seguro) como lo es TCP siempre las aplicaciones van a comportarse mejor… de hecho, muchas veces puede resultar todo lo contrario.

¡Espero que sirva para aclarar tópicos!

Hasta la próxima!!