Member of The Internet Defense League Últimos cambios
Últimos Cambios
Blog personal: El hilo del laberinto Geocaching

¿Cómo atravesar NATs en redes P2P?

Última Actualización: 21 de Mayo de 2004 - Viernes

De todos es sabido que uno de los problemas más graves a los que se enfrenta un sistema "Peer To Peer" es la existencia de cortafuegos y, sobre todo, de entornos NAT (Network Address Translation, traducción de direcciones) entre los diferentes peers. Este hecho desanima pero, estudiando el problema con atención, puede verse que existen algunas posibilidades que pueden aliviar el problema.

Lo dicho aquí se refiere a entornos IPv4. Los sistemas IPv6 usarán cortafuegos, pero no deberían emplear NAT, ya que cada usuario puede recibir millones de IPs.

La característica fundamental del problema que vamos a estudiar es que se permiten las conexiones salientes (aunque el tráfico en sí sea entrante, como ocurre con HTTP), pero no es posible, de forma "simple", tener conexiones entrantes. Un caso habitual en España, a fecha de hoy (Mayo 2004), son los ADSLs.

  • Conexión inversa

    Si sólo uno de los extremos está detrás de un NAT, una posibilidad simple es enviarle (mediante un servicio intermedio) una petición para que sea él quien se conecte al otro nodo, que acepta conexiones externas sin problemas.

    Este esquema no es aplicable si ambos "peers" tienen problemas para aceptar conexiones externas, lo que es muy habitual.

  • "Bouncer" o "repetidor" externo

    Una solución evidente es que los "peers" que pretenden conectarse utilicen un repetidor externo que les haga de repetidor. Básicamente los dos "peers" se conectan a dicho equipo con conexiones salientes, que no tienen problemas. El repetidor se situaría en medio, copiando lo que recibe por cada conexión hacia la otra.

    Este esquema tiene varias desventajas muy graves:

    • Sólo pueden ser nodos "repetidores" aquellos nodos que no tengan problemas con cortafuegos o NAT.

    • Todo el tráfico circula por dichos nodos, lo que compromete la naturaleza de una red "peer to peer", supone un consumo de recursos considerable, problemas de rendimiento y privacidad.

    • La aplicación debe definir un mecanismo para que el repetidor pueda identificar las dos conexiones asociadas a un enlace "lógico", ya que el mismo repetidor puede ser empleado por un gran número de "peers".

  • UDP

    Este caso es también muy simple: enviar un datagrama hacia el exterior abre, automáticamente, una regla para aceptar la respuesta. Así que basta con que ambos extremos empiecen a enviar datagramas con las IPs y los puertos correctos (origen y destino) hasta que empiecen a recibir los datagramas del otro "peer". Hay varios casos, no obstante:

    • Si los NAT no hacen traducción de puertos, la comunicación es inmediata. Este caso es raro, no obstante.

    • Si los NAT hacen traducción de puertos, que es lo habitual, un "peer" no sabrá con qué puerto real va a salir a Internet, ya que su NAT intermedio lo modificará de forma arbitraria. Aquí tenemos dos casos, nuevamente:

      • En el primer caso el NAT simplemente se guarda una tabla "puerto origen interno<->Puerto origen NAT". En este caso el NAT dejará entrar cualquier UDP con el puerto correcto, independientemente de su IP o puerto de origen.

        En este caso los pasos serían:

        1. El nodo A envía al nodo C (que no puede tener filtros, como en el caso del "repetidor" externo) una petición de apertura de NAT. Por UDP.

        2. El nodo C envía una respuesta UDP a A, desde otra IP y otro puerto.

        3. Si A recibe dicha respuesta, su NAT cumple este supuesto y es "utilizable".

        4. Ahora A envía los datos de contacto (en especial, la IP y el puerto "externos") a B utilizando a C, por ejemplo.

        5. B se conecta a A usando esos datos.

      • Si el NAT también guarda en sus tablas internas la IP y el puerto de destino, el esquema anterior no funcionaría. Pero si la asignación del puerto externo sigue una método sistemático fácilmente identificable (por ejemplo, puertos correlativos) una posibilidad sería:

        1. El nodo A envía al nodo C (que no puede tener filtros, como en el caso del "repetidor" externo) una petición de apertura de NAT. Por UDP.

        2. El nodo C envía una respuesta UDP a A, con la misma IP y puerto.

        3. Ahora A envía los datos de contacto (en especial, la IP y el puerto "externos") a B utilizando a C, por ejemplo.

        4. El nodo A enviará un datagrama UDP a B, que éste no recibirá pero que creará una entrada en la tabla del NAT de A.

        5. El nodo B intenta contactar con A mediante el envío de varios datagramas UDP en el "entorno" de donde cree que debe estar el nuevo puerto externo de A. Con suerte, lo encontrará.

        Por suerte, este tipo de NATs es menos frecuente.

  • TCP

    En general, la gestión de TCP es bastante más compleja que la de UDP, ya que el protocolo mantiene estado y requiere negociación al principio de la conexión.

    • Como en el caso anterior, si el NAT simplemente se guarda "puerto origen<->puerto NAT", el servidor NAT simplemente dejará pasar todos los datagramas dirigidos a ese puerto externo, independientemente de la IP o el puerto de origen, o de los números de secuencia TCP. El procedimiento sería similar, por tanto, al caso UDP, con la salvedad de que el "peer" debe asegurarse de de utilizar el mismo puerto origen para las conexiones a C y la posterior a B.

  • Implementar TCP (o similar) sobre UDP

    Como ya hemos visto, intentar conectar a través de NATs es un procedimiento frágil y, cuando es posible, resulta más simple utilizar UDP que TCP. Lamentablemente TCP proporciona funcionalidades no disponibles en UDP, como control de carga y congestión en la red, o entrega fiable y ordenada de datos.

    Una posibilidad relativamente asequible es reimplementar esas funcionalidades sobre UDP, para tener lo mejor de ambos mundos, como se realiza, por ejemplo en la tecnología Airhook.

    Otra opción simple es "tunelizar" los datagramas TCP sobre datagramas UDP, como se realiza en CIPE, iproxy o Almost TCP over UDP (atou), por ejemplo.


Historia

  • 21/May/04: Primera versión de esta documento.



Python Zope ©2004 jcea@jcea.es

Más información sobre los OpenBadges

Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS