Tutoríal de TCP/IP
Introducción:
En este tutoríal realizaremos la "disección" de una comunicación TCP/IP muy simple, con el fin de analizar qué ocurre a cada nivel.
A través de este sencillo experimento, podremos recorrer los conceptos fundamentales de las redes TCP/IP, para reafirmar la idea de que este protocolo es muy simple. El objetivo es lograr, sin demasiados conocimientos previos, una comprensión profunda de sus mecanismos.
Licencia:
Este tutoríal se distribuye bajo una Licencia Creative Commons Atribución -No Comercial-Compartir Obras Derivadas Igual 2.5 Argentina.
Requisitos previos:
No se requieren conocimientos de programación, aunque sí una base de conocimientos informáticos en general. Para continuar experimentando más allá de los expuesto aquí, se recomienda la utilización del programa Wireshark (antes conocido como Ethereal), disponible para GNU/Linux, Microsoft Windows, Mac OS/X y Solaris.
Quizás el requisito más importante sean las ganas de aprender, investigar, jugar y divertirse con redes TCP/IP.
Referencias:
Cada vez que se introduzca un término relevante, el mismo contendrá un enlace a una página con mayor información, por lo general de la Wikipedia y, de ser posible, en español (aunque se recomienda ampliamente visitar su equivalente en inglés).
Un libro muy recomendable y claro (en inglés) es "TCP/IP Illustrated Volume 1" de W. Richard Stevens.
Finalmente, la fuente de consulta definitiva, para conocer tanto los aspectos técnicos como la evolución histórica de cada uno de los protocolos y normas, son los Request for Comments (RFC), algunos de los cuales han sido traducidos al español.
Nuestro experimento:
Estableceremos una conexión TCP/IP entre un cliente y un servidor que intercambiarán información. Para ello, utilizaremos el servidor desarrollado en el tutoríal sobre programación en redes (no es necesario que lo lea completamente, si no le interesa la programación, pero sería conveniente que revise los conceptos introductorias y el protocolo definido).
Ejecutamos entonces el servidor en el host 100.0.0.1, utilizando el puerto 2222 (cualquiera de las versiones desarrolladas en el tutoríal anterior sirve, ya que el protocolo es idéntico) y usamos el comando telnet para actuar como cliente desde el host 200.0.0.1 (el texto en cursiva es introducido por nosotros y el texto en negrita es la respuesta del servidor):
javier@200.0.0.1:~$ telnet 100.0.0.1 2222
Trying 100.0.0.1...
Connected to 100.0.0.1.
Escape character is ’^]’.
Bienvenido.
salir
Adios.
Connection closed by foreign host.
javier@200.0.0.1:~$
Esto es todo. Ahora procederemos a analizar cómo se ha llevado a cabo esta comunicación a distintos niveles de abstracción.
Nivel de aplicación:
A "nivel de aplicación" (lo que "ven" los programas), la comunicación se ha desarrollado de la siguiente manera:
1. El cliente se conecta al servidor.
2. El servidor envía el mensaje "Bienvenido.".
3. El cliente envía el comando "salir".
4. El servidor responde con el mensaje "Adiós.".
5. El servidor cierra la conexión.
6. El cliente cierra la conexión.
Esto es prácticamente lo mismo que podemos observar de la salida del comando telnet utilizado, lo cual no es casual; intencionalmente a este nivel se ocultan todos los detalles de implementación, que aparecerán cuando analicemos los niveles inferiores.
Nivel de transporte:
El protocolo utilizado a "nivel de transporte" es TCP. Este protocolo es el encargado de establecer la conexión y dividir la información en paquetes, garantizando que los mismos son entregados correctamente (sin pérdidas y en el orden apropiado).
Cabe resaltar aquí que el otro protocolo de transporte de TCP/IP, UDP no garantiza ni el arribo de todos los paquetes enviados, ni el orden en que estos llegan a destino. Por esto es mucho más simple, no incluyendo algunas de las características de TCP como números de secuencia y asentimientos.
A continuación analizaremos algunos aspectos del protocolo TCP.
Puertos y direcciones:
El protocolo TCP se basa en direcciones IP para identificar los equipos (hosts) desde donde provienen y hacia donde se envían los paquetes.
Los puertos (ports) son valores numéricos (entre 0 y 65535) que se utilizan para identificar a los procesos que se están comunicando. En cada extremo, cada proceso interviniente en la comunicación utiliza un puerto único para enviar y recibir datos.
En conjunción, dos pares de puertos y direcciones IP identifican unívocamente a dos procesos en una red TCP/IP.
Números de secuencia:
TCP garantiza que la información es recibida en orden. Para ello, cada paquete enviado tiene un número de secuencia. Cada uno de los dos procesos involucrados mantiene su propia secuencia, que se inicia con un valor aleatorio y luego va incrementándose según la cantidad de bytes enviados.
Por ejemplo, si un paquete tiene número de secuencia x y contiene k bytes de datos, el número de secuencia del siguiente paquete emitido será x + k. (Sí, el número de secuencia va contando la cantidad de bytes enviados por cada host.)
Paquetes y acuses de recibo:
TCP también asegura que toda la información emitida es recibida. Para ello, por cada paquete emitido, debe recibirse un asentimiento (en inglés "acknowledgement", abreviado ACK). Si pasado determinado tiempo no se recibe el ACK correspondiente, la información será retransmitida.
El ACK hace referencia al número de secuencia (que ha su vez involucra la cantidad de bytes enviados).
Por ejemplo, para comunicar que se ha recibido correctamente el paquete cuyo número de secuencia es x, que contiene k bytes, se enviará un ACK con el valor x + k (que coincide con el próximo número de secuencia a utilizar por parte del emisor del paquete en cuestión). Si el número de secuencia inicial es x, un valor de ACK t significa que el receptor ha recibido correctamente los primeros t-x bytes (en este sentido, el ACK es acumulativo).
El ACK no es un paquete especial, sino un campo dentro de un paquete TCP normal. Por esto, puede ocurrir que se envíe un paquete a solo efecto de asentir una determinada cantidad de bytes, o como parte de un paquete de otro tipo (por ejemplo, aprovechando el envío de nuevos datos, para comunicar la recepción de datos anteriores). De hecho, aunque ya se haya enviado un paquete exclusivamente de ACK con un valor t, si luego se envía un paquete de datos, puede repetirse en él el ACK con el mismo valor t, sin que esto confunda al emisor de los datos que se están asintiendo. (Por simplicidad, en nuestro ejemplo hemos eliminado esta información redundante).
Otros campos de un paquete TCP:
El protocolo TCP incorpora mecanismos tales como control de integridad de los datos (checksum), prevención de congestiones, entre otros, que no serán mencionados aquí por la simplicidad de este tutoríal.
Inicio y fin de la conexión:
Para dar comienzo a la conexión, el cliente envía un paquete SYN al puerto e IP en donde "escucha" el servidor, con un número de secuencia inicial aleatorio. Este último, responde con otro paquete SYN, con un número de secuencia inicial aleatorio y un ACK con el número de secuencia del paquete SYN recibido, más uno. El cliente envía un paquete con el ACK del SYN recibido, y una vez hecho esto la conexión se encuentra establecida y puede darse comienzo a la transmisión de datos (iniciada por cualquiera de las partes, según el protocolo de aplicación que utilicen).
La razón por la cual se intercambian números de secuencia aleatorios es para evitar que se confunda el inicio de dos conexiones diferentes y algunos ataques que se basan en falsear el comienzo de una conexión (spoofing).
La siguiente figura ilustra el establecimiento de una conexión TCP, llamada "negociación de tres pasos" o "3-way handshake":