CC51C Comunicación de Datos Apuntes · 2 Índice Introducción 4 Capa Física 4

45
1 CC51C Comunicación de Datos Apuntes José M. Piquer

Transcript of CC51C Comunicación de Datos Apuntes · 2 Índice Introducción 4 Capa Física 4

1

CC51C Comunicación de Datos Apuntes

José M. Piquer

2

Índice Introducción...................................................................................................................... 4 Capa Física ....................................................................................................................... 4 Límites Teóricos ............................................................................................................... 4 Medio Físico ..................................................................................................................... 5 Cable Coaxial ............................................................................................................... 5 Par Trenzado................................................................................................................. 5 Fibra Óptica .................................................................................................................. 5 Antenas directas............................................................................................................ 6 Satélites......................................................................................................................... 6 Red Telefónica.............................................................................................................. 6

Técnicas de Interconexión ................................................................................................ 7 Multiplexión ................................................................................................................. 7 Conmutación................................................................................................................. 7

MAC: Acceso al Medio.................................................................................................... 8 Protocolos ALOHA...................................................................................................... 8 CSMA........................................................................................................................... 9 IEEE 802.3: Ethernet .................................................................................................... 9 Token Ring ................................................................................................................. 10

Redes de Banda Ancha ................................................................................................... 11 Interconexión de Redes Físicas ...................................................................................... 12 Repeaters .................................................................................................................... 12 Bridges........................................................................................................................ 13 Switches...................................................................................................................... 13 VLANs ....................................................................................................................... 13

Capa Datos...................................................................................................................... 14 Encapsulamieto (Framing) ............................................................................................. 14 Control de Errores .......................................................................................................... 15 Protocolos de Retransmisión .......................................................................................... 16 Stop and Wait ............................................................................................................. 16 Protocolos de Ventana Corredera ............................................................................... 21 Go-Back-N ............................................................................................................. 22 Selective Repeat ..................................................................................................... 24 Optimizaciones ....................................................................................................... 25

Nivel Red........................................................................................................................ 26 Introducción.................................................................................................................... 26 Direcciones IP ................................................................................................................ 27 Sub-redes .................................................................................................................... 28 Super-Redes y CIDR .................................................................................................. 29

Paquetes IP: Datagramas ................................................................................................ 30 Encabezamiento.......................................................................................................... 30 Fragmentación ............................................................................................................ 31 ICMP .......................................................................................................................... 32

Ruteo............................................................................................................................... 32 Ruteo Básico............................................................................................................... 32 Manejo de Errores ...................................................................................................... 34 Entrega Directa ........................................................................................................... 34 Ruteo Interno .............................................................................................................. 36

3

RIP .......................................................................................................................... 36 OSPF....................................................................................................................... 38

Ruteo Externo............................................................................................................. 39 Nivel Transporte ............................................................................................................. 40 UDP ............................................................................................................................ 40 TCP............................................................................................................................. 42 Ventanas de TCP .................................................................................................... 42 Segmentos TCP ...................................................................................................... 43 Tamaño del Segmento ............................................................................................ 43 Timeouts ................................................................................................................. 44 Control de Congestión ............................................................................................ 45

Interfaz de red: sockets ................................................................................................... 45

4

Introducción

El área de redes y sistemas distribuidos es una de las de mayor crecimiento y desarrollo, tanto en investigación como en productos comerciales, lo que hace difícil mantener un curso estructurado sin grandes cambios en contenido.

En general, este apunte está estructurado siguiendo dos grandes líneas: una estructura lógica basada en las capas del modelo ISO-OSI y ejemplos reales tomados de la implementación de TCP/IP. Esto entrega una interesante contradicción, puesto que TCP/IP no sigue el modelo OSI, lo que permite discutir las diversas alternativas de implementación.

El apunte está estructurado siguiendo una fuerte inspiración de los contenidos de los dos libros que forman el curso: Computer Networks, de Andrew Tanenbaum (segunda y tercera edición) y Internetworking with TCP/IP, Vol I de Douglas Comer.

Capa Física

En este capítulo estudiamos los protocolos de bajo nivel utilizados para transmitir datos punto a punto. Tradicionalmente se estudiaba mucho el problema de codificar datos digitales (ondas cuadradas) en señales análogas (ondas sinusoidales). Actualmente, el principal problema es aprovechar bien el medio físico, dando acceso a múltiples emisores ya sea multiplexando o compartiendo la señal física. Por otro lado, protocolos como ATM pueden verse como niveles físicos, pero realmente son cada vez más software, y la codificación final de la señal en el cable es cada vez menos importante.

Por razones de tiempo, este capítulo se concentra más en los protocolos de acceso al medio físico y multiplexión que en codificación de la señal.

Límites Teóricos

Cualquier medio físico de transporte de señal está sujeto a ciertas restricciones, en particular que se pierde intensidad en la señal a medida que se difunde. Al enviar información binaria, se requiere transmitir una onda cuadrada por el cable. Desgraciadamente, esta seña no se adapta bien al típico cable de comunicación, donde se envían señales análogas (voltajes, radio, luz) y los cambios de valores (voltajes, frecuencia, intensidad) no son discretos (y la forma de la curva empeora con la distancia).

El número de cambios de estado de la línea por segundo se conoce como baud. Esto no corresponde a los bits/s (o bps), puesto que usualmente se codifican varios bits en cada estado (según el numero de estados diferentes de la línea: con 8 estados puedo transmitir de a tres bits a la vez).

El ancho de banda de la línea indica cuantas frecuencias soporta la línea, esto limita seriamente la capacidad de bits/s que se pueden transmitir. Por ejemplo, en una línea telefónica, solo se transmite un rango audible y generable por la voz humana, esto da

5

una frecuencia máxima de unos 3000 Hz. Usando codificación binaria normal, no hay como llegar a más de 9600 bps. Se mejora usando codificaciones muy astutas en varias frecuencias a la vez.

Medio Físico

Cable Coaxial

Fue uno de los esquemas más usado en redes locales, por la simplicidad de instalación y bajo costo. La idea es implementar un bus, es decir un cable en que todos leen los mismos datos. El cable debe estar conectado de punta a punta y en cada extremo tiene una resistencia de 50 Ohms (terminador). Internamente es un cable de datos, rodeado de una malla.

La capacidad de transmisión es buena y muy tolerante al ruido. Tasas de 10Mbps para cables de hasta 1 Km.

Sin embargo, la forma de conectar computadores al cable es delicada: una T debe insertarse en el cable y conectarlo directamente a la interfaz de comunicación. También existen los vampiros, que solo sirven para cable coaxial grueso que es menos manipulable. Esto genera múltiples problemas en instalaciones de tamaño medio, puesto que el coaxial es muy estable si no se toca, pero no soporta bien los tirones y movimientos. Un trozo de coaxial con problemas impide la comunicación en toda la red.

Par Trenzado

El Par Trenzado es el medio más usado de comunicación por el sistema telefónico. Dos cables de cobre telefónicos trenzados en forma helicoidal (para evitar que hagan de antena) permiten tasas de transferencia punto a punto de varios Mbps, dependiendo del largo, del grosor y de la calidad de los conectores. Se habla de nivel 3 cuando cumplen con la norma para telefonía y nivel 5 cuando cumplen con la norma para datos a alta velocidad. Sobre un par nivel 5 actualmente se puede transmitir a 100 Mbps en distancias inferiores a 100 metros.

Su interés es tan alto, que hoy día se usan para reemplazar cableado de coaxial, haciendo una estrella del bus (o estrellando el bus). Para esto se usa un concentrador, donde llegan todos los pares trenzados, y que repite los datos hacia todos los cables. Esto disminuye las colisiones, evita los puntos de falla globales (salvo por el concentrador mismo) y permite usar el cableado telefónico normal para redes locales.

Fibra Óptica

La luz tiene una frecuencia del orden de los MHz, lo que permite un ancho de banda enorme. El sistema de transmisión se basa en un emisor de luz, un receptor y un medio de transmisión: fibra de vidrio o de sílice.

Gracias a los coeficientes de refracción de la luz, se puede enviar luz sin perder nada de un punto al otro. Incluso, varios rayos pueden viajar al mismo tiempo usando diferentes

6

ángulos de refracción (fibras multimodo). Una fibra que es justo del largo de onda de la luz usada puede utilizarse como fibra monomodo, sin refracción, lo que permite mejores tasas de transmisión por mayor distancia (pero con equipamiento más caro).

Actualmente, 1 Gbps es normal en fibra óptica, pero en redes locales es bastante menos.

Aun no se dispone comercialmente de multiplexores en frecuencia de fibra óptica, pero ya existen en laboratorios. Esto permite pasar señas con 1 MHz cada una a la vez por una sola fibra.

Antenas directas

Varias maneras de conectar punto a punto lugares ``visibles": infrarojos, laser, UHF, microondas.

Satélites

Los satélites de comunicaciones están en órbita geo-estacionaria, a 36.000 km sobre el ecuador. Cada satélite posee varios Transponders, cada uno con capacidad de 50 Mbps (existen satélites experimentales para llegar a Gbps). Esta capacidad puede ser dividida en múltiples canales más lentos (800 64 Kbps por ejemplo).

El problema de los satélites es el retardo de la señal. La tasa de transferencia (bits/s) no indica cuánto demora la señal en llegar al otro lado. La órbita geo-estacionaria está tan lejos que en subir y bajar del satélite la onda demora unos 300 ms. Esto implica una ida y vuelta de una señal de 600 ms (aproximadamente medio segundo).

En algunos casos (envío de archivo) esto no es relevante, pero en otros (comunicación interactiva) es desastroso.

Red Telefónica

La red telefónica es probablemente el medio de transporte de datos más usado en el mundo, a pesar de que nunca fue diseñada para ello. Con sus más de 300 millones de suscriptores en el mundo, presente en todas partes, es demasiado atractiva para usarla, a pesar de sus limitaciones.

La red telefónica tradicional (que en jerga telecomunicaciones se denomina POTS: Plain Old Telephone System) provee una línea de comunicaciones punto a punto que transmite entre 0 y 3000 Hz. Además, la calidad sólo concierne que las frases sean distinguibles, por lo tanto el nivel de ruido tolerable es enorme, y no todas las frecuencias intermedias pasan con la misma calidad. Aunque ya la mayoría de las centrales telefónicas son digitales (en Chile todas lo son) y las troncales son de fibra óptica, la mayoría del ruido se genera en el par trenzado que une el teléfono con la central más cercana. La calidad de ese tendido es muy variable, y depende de la época en que fue hecho. A pesar de las apariencias, en realidad la mayoría del capital de las compañías de teléfonos está en esos cables. Se estima que el total de cables a las casas (conocido como local loop) tendidos en el mundo dan para ir y volver a la luna unas 1000 veces.

7

Curiosamente, el par trenzado debería ser capaz de soportar 2 Mbps sin problemas, y las centrales digitales mucho más. Por lo tanto, disponemos de la infraestructura para obtener datos a velocidad más que razonables desde la casa. Lo que nadie descubre aún es cómo vender ese servicio. Algunos intentos se hacen con ISDN (o RDSI: Red Digital de Servicios Integrados), que permite conexiones a 128 Kbps si ambos teléfonos tienen ese servicio. Sin embargo, mientras las estructuras de precio sean como el teléfono, y los anchos de banda sean parecidos, nadie quiere pagar más por ello. En USA, ISDN se ha vuelto una alternativa atractiva en algunos estados, en particular para accesar Internet.

Para poder transmitir datos por la línea telefónica tradicional, se usa un modem (Modulator/Demodulator). La idea es transformar la onda cuadrada digital en una onda sinusoidal análoga. Inicialmente, los modems usaban modulación en frecuencia (un tono=0 otro tono=1) y operaban a 300 bps, que obedecen a los 300 bauds de la línea. Luego, usando múltiples tonos, 1.200 y 2.400 fueron comunes. Usando modulación de fase y luego técnicas sumamente avanzadas de adaptación dinámica de frecuencias, se pasó a 4.800, 14.400 y 28.800 bps.

Técnicas de Interconexión

En redes de área amplia, donde deben interconectarse múltiples puntos unos con otros, es imposible pensar en conexiones físicas punto a punto, por lo que se utilizan varias técnicas para compartir troncales entre varias comunicaciones simultáneas.

Multiplexión

Dos técnicas clásicas se utilizan para multiplexar una línea (o sea simular varias líneas lógicas sobre una línea física): multipexión en frecuencia y multiplexión en el tiempo.

Al multiplexar en frecuencia, se asignan rangos de frecuencias a cada conexión, dividiendo la línea en segmentos reservados exclusivamente para cada canal lógico. Según la cantidad de Hz disponibles en la línea puedo multiplexar más o menos.

Al multiplexar en tiempo, los canales lógicos ocupan la línea completa (con todas las frecuencias disponibles) por turnos. Estos turnos son típicamente slots de tiempo predefinidos, garantizando entonces un ancho de banda disponible para cada canal lógico.

Estas dos técnicas pueden mezclarse, típicamente haciendo multiplexión en Frecuencia y luego cada canal lógico puede ser multiplexado en tiempo.

Ambas técnicas adolecen de un problema: reservan el ancho de banda aunque no se use. En el caso de redes de computadores esto es un gran pecado, puesto que muchas conexiones permanecen sin transmitir largos períodos de tiempo.

Conmutación

Como no existen líneas directas punto a punto entre todos los participantes de una red como la red telefónica, además de multiplexar las troncales, debe existir una forma de

8

establecer una conexión virtual punto a punto. Existen dos métodos clásicos para esto: conmutación de circuitos y conmutación de paquetes.

La conmutación de circuitos es el paradigma de las redes telefónicas. Básicamente, al pedir una conexión, se realiza una reserva de recursos desde el origen hasta el destino. Originalmente, se reservaban pares de cobre entre los switches que físicamente se iban cerrando para establecer una línea entre origen y destino. Hoy en día todo se basa en multiplexión en frecuencia y tiempo, pero la idea es la misma: una vez establecida la conexión existe un canal lógico reservado entre ambas partes. Si después no hablo durante dos minutos, el costo de recursos es el mismo que si hablo.

Una ventaja de este esquema es que nunca hay congestión en mis conexiones, puesto que los recursos están reservados: el retardo es siempre el mismo. La congestión se maneja en base a tonos ocupados al pedir realizar la llamada.

La otra alternativa es conmutación de paquetes. En este caso, no se reservan recursos par las conexiones, sino que se simulan sobre paquetes de datos (con un formato muy bien definido). Estos paquetes deben ser ruteados uno por uno hacia su destino, multiplexando en forma natural las troncales (es una multiplexión en el tiempo, pero sin slots pre-asignados). En este caso, el uso de los recursos es optimal, puesto que si no transmito, no gasto nada. Sin embargo, son de naturaleza muy dinámica, presentando problemas de congestión y de retardo variable. Por otro lado, los paquetes pueden perderse, desordenarse, etc.

Existe una guerra religiosa actualmente sobre estos puntos, que revisaremos más adelante.

MAC: Acceso al Medio

Las redes locales típicamente se organizan en base a un esquema de red de broadcast (difusión). O sea, múltiples computadores se conectan a un medio común, que permite difusión (radio en el aire, coaxial, etc).

Acá estudiaremos los protocolos utilizados para compartir este medio entre todos los participantes, conocidos como MAC (Medium Access Control).

El modelo es simple: un medio compartido donde todos pueden escribir y leer. Si dos o más computadores transmiten al mismo tiempo se produce una colisión, que es detectable (y distinta a cualquier dato posible).

Protocolos ALOHA

Este es el protocolo que dio origen a muchos en uso hoy en día. La idea es muy simple, cuando se desea transmitir se transmite. Habrán colisiones, y tanto los emisores como el resto detectarán eso. La colisión destruye los paquetes emitidos, los que deberán ser re-emitidos. Los protocolos entonces deben determinar cuándo hacerlo (por ejemplo, no sirve esperar un tiempo fijo, puesto que ambos transmitirán otra vez juntos).

Una alternativa es esperar un tiempo aleatorio antes de retransmitir.

9

Si suponemos paquetes de largo fijo a transmitir, y que cada estación transmite en cuanto tiene datos, la probabilidad de colisión en redes cargadas es muy alta puesto que basta con que el último bit de un paquete se transmita junto con el primer bit de otro para que ambos colisionen y se destruyan.

Un dato importante en estas redes compartidas es cuánto es el factor de utilización máximo que se puede lograr del medio. Es decir, si tengo un coaxial de capacidad total 10 Mbps, cuánto puedo ocupar realmente entre todos los participantes. Esto no es trivial, porque requiero que haya mucha carga de tráfico para utilizar más ancho de banda, pero al aumentar el tráfico aumentan las colisiones.

En el caso del protocolo ALOHA puro, se obtiene que el máximo factor de utilización es 18%, lo que dista mucho de ser razonable.

Una optimización al protocolo es dividir el tiempo en slots fijos sincronizados (slotted ALOHA). Un computador sólo puede transmitir en un comienzo de slot (que dura justo el tamaño de un paquete). Esto disminuye la probabilidad de colisiones, permitiendo un factor de utilización máximo de 37%.

CSMA

Una optimización importante a ALOHA puro es no transmitir si el canal está ocupado, lo que implica escuchar antes de hablar (Carrier Sense). Si el canal está ocupado, puedo quedar escuchando hasta que se desocupe y ahí transmitir (CSMA 1-persistente).

Esto no es muy bueno, porque al aumentar la carga, aumenta la probabilidad de que más de un computador esté escuchando el canal ocupado, esperando transmitir, y por lo tanto habrá una colisión cuando ambos intenten. Para evitar esto, en vez de esperar que el canal se desocupe, esperamos un tiempo aleatorio antes de volver a intentar (CSMA no persistente).

IEEE 802.3: Ethernet

Es un algoritmo CSMA/CD 1-persistente, con una tasa de 10 Mbps (ahora está de moda una nueva versión a 100 Mbps: Fast Ethernet).

Para poder escribir bytes en el cable, debemos codificarlos y encapsularlos. El encapsulamiento (framing) es típicamente tarea del MAC. En Ethernet, el paquete puede verse en la Figura 2.1.

Figure 2.1: Paquete Ethernet

El Preámbulo sirve para sincronizar los relojes del emisor y receptor. Luego viene un comienzo de paquete y la direcciones de origen y destino. Las direcciones ethernet son de 48 bits, y son asignadas centralizadamente a los fabricantes para evitar dos iguales en

10

la misma red local. Se usan direcciones de grupos (multicasts) y la dirección con todos los bits en 1, que es para todos (broadcast). Los paquetes son de tamaño variable, con máximo 1500 bytes. El campo de PAD, sirve para los paquetes de datos menores de 46 bytes, que son rellenados para dar un largo total al menos de 64 bytes, para evitar que pueda ser transmitido antes de llegar al final del cable. Al final, se agrega un checksum, que permite validar que todos los bits del paquete llegaron sin alteración.

Si se produce una colisión, el emisor espera un tiempo aleatorio antes de reintentar. El tiempo se divide en espacios (slots) de 512 bits. Con probabilidad se transmite en el slot 0 o 1. Si vuelve a ocurrir una colisión, con probabilidad se transmite en el slot 0, 1, 2 o 3. A la tercera, se espera un número aleatorio de slots entre 0 y . Luego, al ocurrir la colisión i, se sigue esperando entre 0 y . Después de 10 colisiones, se espera entre 0 y 1023 slots. Después de 16 colisiones se aborta la transmisión.

Este algoritmo se conoce como binary exponential backoff, y es muy interesante porque intenta evitar sobrecargar la red con retransmisiones una vez que la red ya está saturada.

Ethernet muestra en la práctica un factor de utilización cercano al 50%. En teoría el mejor caso es alrededor del 80%.

Token Ring

El problema con ethernet es que la distribución del acceso al medio e aleatoria, por lo que puede ser injusta, perjudicando a un computador durante un periodo de tiempo.

En algunos casos es muy importante garantizar un acceso igualitario al medio, de modo de garantizar que siempre podremos transmitir, independientementede la carga. El clásico esquema utilizado para esto es el paso de una ficha (token) entre los participantes. Quien posee la ficha, puede transmitir. Quien quiere transmitir, debe esperar a recibir la ficha.

Por razones de justicia en el acceso, típicamente estas redes se organizan en anillo, de modo de que el token pueda circular en forma natural.

Un ejemplo bastante difundido de estas redes es FDDI (Fiber Distributed Data Interface) que utiliza fibra óptica (aunque también existe sobre cobre) en un doble anillo a 100 Mbps (4 fibras multimodo).

El layer MAC usa direcciones ethernet y permite broadcast (un paquete que da la vuelta completa al anillo).

Al emitir un paquete, se espera a recibirlo por el otro lado para descartarlo. Al terminar de emitir un paquete, se escribe el token en la red para señalar que está disponible. El token es un paquete físico especial, que no debe confundirse con un paquete de datos. Ninguna estación puede retener el token por más de un tiempo dado (10 ms).

Al estar la red bajo carga, siempre habrán paquetes encolados en los computadores esperando ser enviados. El protocolo es tomar el token, transmitir un paquete y escribir

11

el token, permitiendo un acceso igualitario a la red, y un uso del ancho de banda de casi un 100%.

Para mantener el anillo funcional, el MAC layer es bastante complejo. En todo momento existe un computador que ha sido elegido como monitor de la red. Éste se encarga de revisar si el anillo está funcional, si existe un token en él, existencia de direcciones duplicadas, etc. En FDDI, es incluso capaz de recuperarse de fallas en segmentos del anillo, utilizando el anillo secundario (ver Figura 2.2).

Figure: Reconfiguración del Anillo FDDI

Redes de Banda Ancha

Está claro que el ancho de banda es siempre escaso, independientemente de cuánto haya disponible. Por lo tanto, se ha desarrollado un enorme esfuerzo de investigación en redes de Banda Ancha, con el objetivo de llegar a redes de Gigabits/s.

Dos líneas de desarrollo son las más prometedoras: la multiplexión en frecuencia de la fibra óptica y ATM. A pesar que la primera es mucho más atractiva, puesto que el ancho de banda total disponible en ese caso en una fibra es enorme, aun es demasiado caro construir un filtro que cambie dinámicamente las frecuencias del laser. Sin embargo, es probablemente la tecnología que desplazará a ATM en cinco o diez años.

ATM es la tecnología más de moda en el ambiente de redes rápidas. Es un protocolo híbrido, que toma muchos elementos aprendidos de los diversos sistemas existentes, y su objetivo es soportar muchos servicios distintos sobre el mismo medio: desde POTS, video, hasta correo electrónico y datos.

Los conceptos básicos de ATM son muy simples: todo el recorrido de la conexión va por fibra óptica (aunque un buen cableado de par trenzado puede usarse también) la que se multiplexa usando paquetes de datos. Los paquetes son minimales, y por ello se llaman celdas. Una celda es de tamaño fijo, y muy rápida para ser ruteada (ver Figura 2.3).

12

Figure 2.3: Celda ATM

Para que esto sea posible, se requiere que haya un protocolo de conexión que asigne los números de Path Virtual y Circuito Virtual en todos los switches involucrados. Esta última característica hace que ATM sea muy parecido a un sistema de conmutación de circuitos, pero con reserva minimal de recursos. El problema de ATM está en esa componente: establecer la conexión. La idea es que se quieren soportar servicios muy diferentes: Constant Bit Rate (CBR) como video, Available Bit Rate (ABR) como datos, etc. Todos ellos van a costar diferente (porque son distintas calidades de servicio) y debe entonces garantizarse que, si la conexión se establece, se obtendrá la calidad deseada. Sin embargo, los switches no quieren realmente reservar la capacidad requerida, y quieren aprovechar los momentos sin transmitir (ejemplo: silencio en las conversaciones telefónicas) para ocupar los recursos en las conexiones que sí tienen tráfico.

Existe gran controversia hoy en día si ATM realmente llegará a convertirse en una solución par dar servicios públicos a gran escala, o solo será un buen protocolo de red troncal de banda ancha.

Interconexión de Redes Físicas

Las redes tienen limitaciones de crecimiento, por restricciones físicas o por capacidad de tráfico. Frecuentemente nos vemos enfrentados a tomar decisiones para lograr crecer sin cambiar la tecnología completa. La red más usada en el mundo a nivel local actualmente es ehternet, y por ello existen múltiples esquemas que permiten pasar paquetes ehternet por tecnologías de mayor capacidad. Estudiaremos aquí las soluciones más típicas utilizadas a nivel físico.

Repeaters

Las redes tienden a tener distancias máximas y número de puntos máximos, y cuando quiero exceder esos números puedo colocar un repetidor, que es como un amplificador

13

de señal (aunque puede también cambiar la tecnología, como pasar a fibra óptica). No posee ninguna inteligencia y no filtra ningún paquete.

Bridges

Un bridge es un repetidor inteligente. La idea básica es aprender mirando los paquetes que pasan a través de él. Si conecto la red A con la red B, y viene un paquete desde la red A, enviado por la máquina X, puedo concluir que la máquina X está en la red A y no en la B. Si recuerdo eso, la próxima vez que llegue un paquete para la máquina X desde la red A, sé que no requiero pasarlo a la red B (puesto que X se encuentra al otro lado) y puedo descartarlo.

La tecnología es más compleja que eso, porque los bridges pueden interconectar las redes en múltiples puntos, generando ciclos (esto es útil para redundancia y no es posible con sólo repetidores). Para ello utilizan un algoritmo de árbol cubriente que ha sido estandarizado por la IEEE.

Por otro lado, los bridges muchas veces aprovechan de cambiar la tecnología de transporte, y me permiten pasar ethernet sobre FDDI o sobre ATM, por ejemplo. Hay que tener cuidado, sin embargo, porque los tamaños máximos de paquetes en estas tecnologías son distintos y no tengo cómo enviar paquetes grandes en una red donde no me caben. Por ello, esto sirve para conectar dos ethernets a través de una FDDI, pero no es útil para conectar máquinas en el FDDI con máquinas en una ethernet.

Switches

Al cablear las redes ethernet con par trenzado, se utiliza un hub (concentrador) que implementa el bus compartido. Al crecer el tráfico en la red, las colisiones aumentan. Por ello, se ha agregado memoria RAM a los concentradores e inteligencia para hacer de bridges. Eso es llamado típicamente un switch. Un buen switch filtra tráfico y no genera colisiones entre puertas, puesto que puede almacenar los paquetes mientras espera para transmitirlos. Son mucho más caros que los concentradores, por lo que típicamente se encuentran al centro de la red, conectando múltiples concentradores, separando los dominios de colisión (uno por cada HUB).

VLANs

Finalmente, se puede aprovechar un sólo switch para múltiples redes ethernet. Eso me separa los dominios de colisiones y los de broadcast, tal como si estuvieran en HUBs separados. La gracia es que es configurable por software y por puerta (configurando en qué ethernet está cada puerta) y utilizando técnicas de bridging puedo pasar las múltiples ethernets por cables de mayor velocidad (como ATM).

14

Capa Datos

En este nivel, suponemos que dos computadores están comunicados directamente por un "cable" bidireccional. Esto puede ser un circuito virtual, un cable serial, una conexión telefónica con modems, una ethernet, una FDDI, etc.

Suponemos que podemos hacer un 'write' de algunos bytes en ese cable y al otro lado pueden hacer un 'read'.

En el esquema OSI de layers, el nivel de datos se encarga de simular un canal de comunicaciones punto a punto libre de errores. Veremos después que, en la práctica, este no es el mejor lugar para hacer ese rol. Sin embargo, es el mejor lugar donde estudiarlo, y los protocolos y algoritmos que veremos se usan en la práctica, pero en otro nivel.

La idea es que el 'write' y el 'read' son llamadas al nivel físico para enviar los datos, pero lo podemos ver como un 'pipe' bidireccional que conecta ambas máquinas. El problema es que este 'pipe' puede tener errores: puede perder bytes y puede alterar bytes.

Par fines de este curso, vamos a suponer que el nivel físico nos entrega las siguientes primitivas:

char Pgetc();

void Pputc (char c);

void Pflush();

Las acciones son las esperadas, Pgetc lee el próximo byte del link, Pputc escribe un byte. Sin embargo, por razones de eficiencia, los bytes acumulados con Pputc no se envían realmente hasta que se llama a Pflush. Los paquetes físicos no pueden ser de más de MAX_PACK bytes, así que no puedo hacer más de MAX_PACK llamados a Pputc sin llamar a Pflush.

Encapsulamieto (Framing)

Para poder detectar errores en los datos, la técnica más usual es encapsular los datos en un formato (Frame) que permita sincronizarse y descartar los errores. La técnica tradicional es agregar bytes a los datos, en particular un checksum. Un checksum es un número calculado en base a los datos (como un dígito verificador) que se envía junto con ellos. Al recibir los datos, el checksum se recalcula, y si no calza se sabe que hubo un error, y el Frame debe ser descartado.

Dividir los datos en Frames no es tan fácil, porque todos los bytes (también los del Frame) pueden perderse. Por ejemplo, poner primero el largo y luego los bytes puede ser peligroso, porque el largo se puede perder y uso como largo un byte de datos y luego no puedo volver a sincronizarme. Por ello, el checksum debe incorporar también los datos del Frame. Sin embargo, aun así, si debo descartar un Frame (cuyo largo no es confiable) ¿cómo sé dónde empieza el siguiente?

15

Una técnica clásica es usar bytes delimitadores de comienzo ( SOH) y fin ( EOP) de Frame. Estos son bytes especiales que sirven para sincronizarme. Sin embargo, estos bytes (tengan el valor que tengan) pueden aparecer también en los datos, y no debo confundirlos con los delimitadores. Para ello, utilizo otro byte especial ( ESC) que 'escapa' el sentido mágico de estos bytes. Entonces, si aparece un SOH en los datos, envío ESC, SOH. Un nuevo problema, claro, es qué hago si aparece ESC en los datos, y en ese caso envío ESC, ESC (un escape escapado). Obviamente, todo se puede perder, desde los delimitadores, los ESC, etc. Pero el receptor siempre podrá detectarlo. El tener delimitadores permite no tener que enviar el largo, pero por razones de eficiencia (administración de memoria) muchas veces se envía el largo también. Un ejemplo de formato de Frame se puede ver en la Figura 3.1.

Figure 3.1: Ejemplo de un Frame

El determinar cuántos bytes enviar en un Frame puede ser delicado. El rango va entre 0 y MAX_PACK (incluyendo los delimitadores y los ESC necesarios). Una línea con muchos errores puede preferir Frames pequeños (la probabilidad de perderlos es menor), pero es más eficiente enviar Frames grandes (si no se pierden).

En general se considera el Framing como una sub-capa de la capa Datos.

Al usar Framing, los errores del medio físico (pérdidad de bytes y alteración de bytes) aumentan: ahora podemos perder Frames completos. Parte de la misión será ahora retransmitir los Frames perdidos, lo que también puede traernos la recepción de Frame duplicados o en desorden. Esto implica protocolos bastante complejos si se quiere ser eficiente.

Control de Errores

Existen varios algoritmos de redundancia para detección y corrección de errores en una secuencia de bits. Por ejemplo, al enviar un byte por líneas seriales, típicamente se usaban 7 bits de datos y el octavo se usaba como bit de paridad (1 si el número de bits en 1 es par, 0 si es impar, o vice-versa). Esto permite detectar todos los errores de un bit. Además, si yo supiera cual bit se alteró o perdió, podría corregirlo, deduciéndolo a partir del bit de paridad (esto se usa en los arreglos de discos redundantes RAID, porque yo sé cuál disco falló).

El problema es que tampoco queremos aumentar demasiado la información que se transmite, por lo que se usan esquemas aceptados con una buena probabilidad de detectar los errores habituales. En general, es mucho más eficiente retransmitir los Frames con errores que anexar información de corrección a todos.

Por ejemplo, CRC (cyclic redundancy code) calcula 16 bits de checksum, detecta todos los errores de uno y dos bits, todos los errores de 16 o menos bits contiguos, y el 99% de los errores de bits contiguos más largos. La idea es que los errores no son realmente aleatorios en las redes, sino que vienen acumulados en bits contiguos.

16

Cuando el cálculo se hace en software, se trata que no consuma mucha CPU. Un ejemplo de checksum tomado de IP:

short checksum(b, n)

unsigned char *b;

int n;

{

int i, cc, sum;

for( i=0, sum=0 ; i<n ; i++ )

sum += b[i];

cc = (sum >> 16) + (sum & 0x0000ffff);

cc += (cc >> 16);

return( (short) ~ (cc & 0x0000ffff) );

}

Protocolos de Retransmisión

Si queremos simular un enlace punto a punto fiable y sin errores, debemos retransmitir los frames perdidos. Intentaremos hacer un protocolo trivial primero, y veremos que no existe nada trivial con respecto a los protocolos de retransmisión.

Stop and Wait

Supongamos un layer de Framing que me permite enviar secuencias de bytes de un lado al otro. La interfaz es:

int Fread( char *buf, int size );

void Fwrite( char *buf, int size );

void Freset();

Como se pueden perder o alterar bytes, Fread me retorna size o -1 si pasa mucho tiempo sin recibir nada (timeout). Supondremos una interfaz tipo stream de bytes, no de paquetes. Es decir de qué tamaño son los frames es transparente a este nivel, Fwrite parte la secuencia en varios frames si es necesario, y Fread los pega. El tamaño del Fread y del Fwrite no tiene porqué ser el mismo, la comunicación se ve como un pipe (esto es resuelto en la capa Frame). Sin embargo, se pueden perder, alterar, etc. La función Freset permite ignorar un trozo restante de paquete, y esperar el próximo.

Un protocolo stop and wait es lo más simple que existe. La idea es transmitir un grupo de bytes y esperar que el otro lado me indique que los recibió. Sólo una vez que recibí su respuesta decido que puedo seguir enviando. Suponemos que el flujo de datos es en una sola dirección, aunque la comunicación es bi-direccional (para permitir que me indiquen que los datos llegaron). Por lo tanto hay flujo de datos y flujo de control. Los paquetes de control que usamos en este caso son muy simples: sólo indican que recibieron los datos bien. Este paquete se conoce como un Ack (de acknowledgement).

Para la aplicación, queremos darle una interfaz tipo pipe, confiable y sin errores. Para ellos usaremos la interfaz:

17

void Dread( char *buf, int size );

void Dwrite( char *buf, int size );

Pensemos cómo implementar estas funciones. Si no hubiese errores de ningún tipo, Dread llamaría a Fread y Dwrite a Fwrite y no habría más problemas.

Al haber errores, Dwrite debe enviar los datos y luego esperar el Ack. Si no llega despues de un tiempo, retransmite los datos y vuelve a esperar el Ack.

En Dread espero un paquete de datos. Cuando llega, reviso si está correcto, usando checksum. Si está bien, envío el Ack y retorno.

El nivel datos requiere un formato de empaquetamiento de los bytes (además del que ya provee el Frame) para poder entenderse y detectar los errores. Usaremos el esquema de la Figura 3.2.

Figure 3.2: Paquete de Datos

El campo tipo es para distinguir los paquetes de datos de los paquetes de control (aunque en este primer caso no se requiere). El campo largo es un short int con el tamaño del paquete y luego los datos. Al final se anexa el checksum de verificación. Cuando es un Ack, no se envía el largo, sino directamente el checksum (para evitar que un error me haga llegar un Ack inexistente).

Una primera versión del envío es:

#define TYPE 0 /* char */

#define SIZE 1 /* short */

/* Tamano del header en bytes */

#define HEADER_SIZE 3

/* Tamano de un Ack (sin checksum) */

#define ACK_SIZE 1

#define TDATA 'D'

#define TACK 'A'

Dwrite( buf, size )

char *buf;

int size;

{

short cc;

int outsize;

char InBuf[ACK_SIZE+sizeof(short)];

/* Armamos el paquete de salida en OutBuf */

OutBuf[TYPE] = TDATA;

bcopy( buf, OutBuf + HEADER_SIZE, size );

bcopy( &size, OutBuf + SIZE, sizeof(short) );

cc = checksum( OutBuf, size + HEADER_SIZE );

18

bcopy( &cc, OutBuf+size+HEADER_SIZE, sizeof(short) );

outsize = size + HEADER_SIZE + sizeof(short);

for(;;)

{

Fwrite( OutBuf, outsize );

/* Espero el ACK */

if( Fread(InBuf, ACK_SIZE + sizeof(short)) != ACK_SIZE +

sizeof(short) )

{

Freset();

continue;

}

if( InBuf[TYPE] != TACK)

{

Freset();

continue;

}

bcopy( InBuf+ACK_SIZE, &cc, sizeof(short) );

if( checksum( InBuf, ACK_SIZE ) != cc )

{

Freset();

continue;

}

return;

}

}

Notar el manejo del buffer con bcopy, lo que es clásico en estos programas.

La función de lectura de datos es más compleja por el manejo de los buffers de datos. El número de bytes que me piden al nivel datos no tiene porqué ser el mismo que recibo desde el nivel Físico, por lo que debo tener un buffer intermedio para manejar los bytes que sobran de una lectura a otra. Para esto uso una función que se encarga de retornarme un paquete de datos Dget_Packet.

Aparte de esto, el resto del código es simple, cada paquete de datos recibido correcto debe generar su Ack, y debo esperar tantos paquetes como sea necesario para completar los bytes que me piden:

char Dpbuf[MAX_DPACK];

int Dpsize = 0;

char *Dpcp = Dpbuf;

char Ack[] = { TACK, ' ' , ' ' };

Dread( buf, size )

char *buf;

int size;

{

for(;;)

{

if( Dpsize >= size )

{

19

bcopy( Dpcp, buf, size );

Dpcp += size;

Dpsize -= size;

return;

}

if( Dpsize > 0 )

{

bcopy( Dpcp, buf, Dpsize );

size -= Dpsize;

buf += Dpsize;

Dpcp = Dpbuf;

Dpsize = 0;

}

/* El resto debo leerlo */

Dpsize = Dget_packet( Dpbuf );

Dpcp = Dpbuf + HEADER_SIZE;

}

}

int Dget_packet( buf )

char *buf;

{

short size;

short cc;

/* Trato de obtener un paquete correcto */

for(;;)

{

if( Fread( buf, HEADER_SIZE ) != HEADER_SIZE )

{

Freset();

continue;

}

if( buf[TYPE] != TDATA )

{

Freset();

continue;

}

bcopy( buf+SIZE, &size, sizeof(short) );

if( size > MAX_DATA || size <= 0 )

{

Freset();

continue;

}

if( Fread(buf+HEADER_SIZE, size+sizeof(short) ) !=

size+sizeof(short))

{

Freset();

continue;

}

bcopy( buf+size+HEADER_SIZE, &cc, sizeof(short) );

if( checksum( buf, size+HEADER_SIZE ) != cc )

{

Freset();

continue;

}

20

/* Esta OK, envio Ack */

cc = checksum( Ack, ACK_SIZE );

bcopy( &cc, Ack+ACK_SIZE, sizeof(short) );

Fwrite( Ack, ACK_SIZE+sizeof(short) );

/* Retorno el paquete de datos */

return(size);

}

}

Este protocolo, que parece simple y correcto, no funciona. La retransmisión se basa en timeouts del lado del emisor. Si se pierde el paquete (o se altera, lo que es equivalente), o se pierde el Ack, el paquete será retransmitido. Si se retransmite por pérdida de paquete está bien. Pero si se retransmite por pérdida de Ack, el otro lado aceptará un paquete duplicado como correcto.

Esto se corrige agregando un número de secuencia al paquete de datos, que en este caso basta que sea un bit de modo de detectar los duplicados. El problema ahora es que los ACK's pueden confundirse (reflexionar un esquema que produzca esto). La solución es también agregar un número de secuencia a los ACK's.

El protocolo corregido implica transmitir el paquete con el bit correspondiente y esperar el Ack retransmitiendo hasta recibirlo. En el lado receptor debo recordar el bit del último paquete recibido bien, así ignoro nuevos paquetes con el mismo bit. Sólo acepto el paquete siguiente, con el bit invertido.

A pesar de ignorar estos paquetes duplicados, no puedo dejar de enviar el Ack correspondiente para que el enviador deje de retransmitirlos.

Propuesto: modifique el código anterior para tener un Stop and Wait correcto.

Este protocolo considera un emisor y un receptor. No funciona en un esquema en que se alternen los roles (por ejemplo: pedir un servicio y recibir una respuesta). Al tener datos en ambas direcciones, puedo recibir un paquete de datos al esperar un Ack y tengo que considerarlo para evitar quedar bloqueados ambos lados a la vez. Veremos en el próximo ejemplo como permitir flujo en ambas direcciones. Propuesto: Considere un diálogo trivial: envío un byte al receptor quien me contesta con otro byte igual. Encuentre un escenario, usando Dread y Dwrite, con pérdida de paquetes, que deje a ambos procesos bloqueados en Dwrite esperando un Ack.

Hay varias optimizaciones interesantes: por ejemplo en vez de esperar un timeout para retransmitir, el receptor podría ayudar enviando un Nack (Negative Acknowledgement) cuando recibe un paquete con errores, apurando la retransmisión. Pero lo más importante es eliminar los largos tiempos de bloqueo que presenta este protocolo. En general, los canales de comunicación tienen un retardo que no es despreciable (el ejemplo más típico es el satélite, donde la ida y vuelta es más de medio segundo). Por lo tanto, puedo transmitir muchos paquetes en el cable durante el tiempo que me toma llegar al otro lado y esperar el Ack de vuelta. Por supuesto, en el caso de transmitir varios paquetes antes de recibir los Acks, debo cuidar de no saturar la capacidad del receptor, lo que se denomina Control de Flujo. En Stop and Wait, esto es automático, puesto que sólo requiero almacenar un paquete cada vez.

21

En general, Stop and Wait sólo se usa si la simplicidad del algoritmo es lo principal y la eficiencia no interesa.

Protocolos de Ventana Corredera

Los protocolos de comunicación avanzados intentan sacar el mayor provecho posible del enlace, en particular en casos de retardos no despreciables, que son bastante frecuentes. La idea entonces es no esperar el Ack para transmitir el próximo paquete, sino seguir transmitiendo un tiempo. Para ello, cada paquete lleva un número de secuencia, y los Acks también de modo de saber a qué paquete corresponden. El número de paquetes que tengo derecho a transmitir sin haber recibido Ack se conoce como el tamaño de mi ventana.

Obviamente, si no hay errores, este protocolo es mucho más eficiente que Stop-and-Wait, puesto que utilizo lo más que puedo el ancho de banda disponible (si logro que el tamaño de la ventana llene el cable), no teniendo que detenerme nunca. Si hay errores, depende cómo manejemos la retransmisión, la eficiencia que podamos lograr.

El receptor también maneja una ventana. En el caso que los errores no sean demasiado frecuentes, se estila un protocolo simple que usa una ventana de recepción de tamaño 1 (Go-Back-N). Cuando se esperan tasas de errores importantes, se usa una ventana de tamaño mayor (Selective Repeat), lo que permite recibir paquetes adelantados y guardarlos dentro de la ventana hasta completar el trozo faltante.

Si la ventana del enviador y del receptor son de tamaño 1, nos encontramos con Stop and Wait.

Al tener una ventana más grande en el enviador, el hecho de transmitir paquetes en secuencia, sin esperar los Acks secuenciales, introduce un nuevo error: paquetes en desorden. Como siempre, debemos garantizar que los paquetes que entregamos a la aplicación mantienen el orden original.

Estos protocolos manejan timeouts por paquetes y almacenan múltiples paquetes que fueron transmitidos pero cuyo Ack aun no llega (por si hay que retransmitirlos). Esto produce una actividad del nivel Datos que se produce en paralelo con la aplicación. Por ejemplo, la aplicación sigue generando datos mientras el nivel Datos puede estar recibiendo timeouts o Acks.

Esto lleva a que el nivel Datos ya no puede ser una sub-rutina simple de la aplicación (como en el caso previo). Por ejemplo, si la aplicación no llama más al nivel Datos (porque ya le pasó todos los datos), igual debo terminar de procesar los Acks y retransmitir los paquetes perdidos.

Por ello, el nivel Datos se considera ahora como un proceso paralelo, que se comunica y sincroniza con la aplicación. Para hacer más simple esto, las primitivas Dread y Dwrite se mantienen, pero ahora se comunican con el proceso Dlayer quien recibe entonces tres eventos:

1. D_READY

22

Hay datos de la aplicación para ser leídos. Para obtener los datos usamos int FromUpperLayer(char *buf). Vienen máximo MAX_DATA bytes en el buffer. Estos datos fueron escritos con Dwrite por la aplicación.

2. F_READY

Hay datos del nivel físico para ser leídos usando Fread. Una vez obtenidos los datos correctamente, si corresponde, se entregan a la aplicación usando ToUpperLayer(char *buf, int size).

3. F_TIMEOUT

Pasó más del tiempo esperado.

Para manejar estos eventos, el proceso Dlayer usa la función Get_next_event(int timeout) que retorna alguno de los valores anteriores. Si se invoca con timeout &lt; 0 no hay timeout, y se bloquea hasta que exista otro evento.

La interacción entre los layers y Dlayer puede verse en la figura 3.3.

Figure 3.3: Eventos del proceso Dlayer

Go-Back-N

La idea es ir transmitiendo los paquetes de la ventana, hasta el tamaño máximo acordado. Al ir recibiendo los ACK's, la ventana se va corriendo y puedo enviar el resto de los paquetes, sin pasar el tamaño máximo.

Cada paquete tiene un timeout asociado. Cuando se cumple un timeout, decido que la transmisión de la ventana falló, y retransmito la secuencia completa. Esto me permite tener un receptor simple, con una ventana de tamaño 1. Basta con que recuerde el número de secuencia del próximo paquete que debe recibir.

Si el número de errores es bajo, este protocolo es muy eficiente con ventanas grandes. Al aumentar la tasa de errores, las ventanas deben ser más pequeñas.

En pseudo-código el receptor es:

23

for(;;)

switch(Get_next_event(-1))

{

case F_READY:

<<Leer proximos datos con Fread>>

<<Ver que esten correctos>>

if( buf[SEQN] == ExpectedSeqN )

{

AckBuf[SEQN] = ExpectedSeqN;

Fwrite(AckBuf, ACK_SIZE );

ExpectedSeqN++;

ToUpperLayer(buf+DATA, buf[size]);

}

}

El emisor es más complejo pues debe administrar su ventana de paquetes. En particular, el tamaño de la ventana es variable, y crece hasta

El emisor es más complejo pues debe administrar su ventana de paquetes. En particular, el tamaño de la ventana es variable, y crece hasta llegar a WINDOW_SIZE, donde debe estabilizarse. Para evitar recibir más paquetes de la aplicación, debemos crear una nueva primitiva DisableUpperLayer(). Para habilitar el envío, usaremos EnableUpperLayer().

Debemos manejar el número de secuencia del primer paquete cuyo ACK aun no recibimos ( ExpectedAck) y el arreglo de paquetes de nuestra ventana. En general esto se maneja como un arreglo circular y los números de secuencia van entre 0 y WINDOW_SIZE-1. En el pseudo-código simplemente incrementaremos los contadores.

Al recibir un timeout, el enviador debe retransmitir todos los paquetes de la ventana.

timeout = -1;

win_size = 0;

ExpectedAck = 0;

SeqN = 0;

for(;;)

{

switch(Get_next_event(timeout))

{

case D_READY:

size = FromUpperLayer(buf);

<<Armo el paquete Data Link, con checksum y Header (incluye

SeqN)>>

<<Pongo el paquete en window[Last], con la hora actual>>

Fwrite(window[Last].buf, window[Last].size);

SeqN++;

Last++;

win_size++;

if( win_size == WINDOW_SIZE )

DisableUpperLayer();

break;

case F_TIMEOUT:

for(i = First; i < Last; i++)

{

<<Pongo hora actual en el paquete>>

Fwrite(window[i].buf, window[i].size);

}

24

break;

case F_READY:

Fread(Ackbuf, ACK_SIZE);

if( Ackbuf[SEQN] == ExpectedAck )

{

win_size--;

First++;

ExpectedAck++;

EnableUpperLayer();

}

break;

}

if( win_size > 0 ) timeout = window[First].time + TIMEOUT;

else timeout = -1;

}

Nos quedan dos preguntas: al recibir un ACK fuera de rango, debo ignorarlo? Y en el receptor, al recibir un paquete con secuencia distinto al que espero, no debo enviar un ACK? La primera es por razones de eficiencia, la segunda puede hacer que el protocolo no funcione. Reflexionar.

Si uso un arreglo circular, los números de secuencia van a repetirse, por lo cual debo cuidar que nunca sean ambiguos. Por ejemplo, un ACK retrasado, nunca debe confundirse con un ACK que no ha llegado. Pensar cómo hacer que eso sea imposible. Básicamente, si uso números de secuencia desde cero a N, la ventana tiene un tamaño máximo N (aunque hay N+1 números de secuencia posibles).

En una implementación real, el receptor y el emisor se implementan juntos, puesto que el flujo es bi-direccional. Esto complica el código, ya que un paquete puede ser un ACK o de datos.

En general, las reglas de implementación de Go-Back-N, para los paquetes o ACKs fuera de los esperados son:

• al recibir un ACK para el paquete N, es equivalente a recibir ACK para todos los paquetes con i < N.

• al recibir un paquete con número superior al esperado, debo suponer que se perdió el que espero, por lo que puedo enviar un NACK, para apurar la retransmisión de ese pedazo de ventana. Un NACK para el paquete N, implica NACK para todo i > N, pero implica ACK para todo i < N.

Selective Repeat

En este caso, la idea es no retransmitir más que los paquetes perdidos. Para ello, el receptor también tiene una ventana, donde almacena los paquetes llegados adelantados. Esta ventana es del tamaño máximo de la ventana del enviador para asegurarse de nunca perder paquetes demás. Al recibir un paquete, el receptor revisa su número de secuencia para colocarlo en el buffer correspondiente en su ventana. Al ir llenando los primeros buffers, pueden ir pasándose a la aplicación.

25

Al recibir un timeout, se retransmite el paquete al que le corresponde, pero no el resto. En este caso, no podemos asumir los ACKs adelantados como validando los paquetes anteriores, puesto que el receptor envía los ACKs en cuanto recibe bien un paquete.

Por otro lado, debo tener más cuidado con los números de secuencia. Al tener varios buffers en el receptor, al avanzar la ventana voy a tener un overlap con los números anteriores. Supongan que recibo toda la ventana correcta y mis ACKs se pierden. El emisor me restransmite la ventana y yo ya la avancé. Como los números de secuencia se re-usan, voy a aceptar los duplicados como correctos. Debo entonces definir que el tamaño máximo de la ventana es la mitad del largo de la secuencia, para asegurar que no haya overlap entre una ventana y la siguiente.

Optimizaciones

Algunas optimizaciones son interesantes, por ejemplo usar NACKs en caso de paquetes erróneos. También, cuando hay flujo en dos direcciones, puedo poner los ACKs en un campo de los paquetes de datos (piggybacking) en vez de usar paquetes distintos.

Estos esquemas también se aplican en caso de varias aplicaciones compartiendo el nivel Datos. Basta multiplexar, anotando a cual conexión pertenece cada paquete.

26

Nivel Red

Introducción

El nivel red tiene como misiones el ruteo, el manejo de congestión y de errores. Ahora el problema radica en poder conectar una máquina con otra pasando por múltiples redes y enlaces (ver Figura 4.1), incluso cambiando la representación física de los paquetes y el encapsulamiento de una red a otra.

Figure 4.1: Nivel Red

En este capítulo nos centraremos fuertemente en IP (Internet Protocol), que es el protocolo del nivel red de TCP/IP. Los detalles corresponden a IPv4 (que es la versión actual del protocolo) pero en algunos casos mencionaremos las características relevantes de IPv6 que es la nueva versión. Los principios de ambos protocolos son idénticos, y la nueva versión sólo incluye mejoras de performance y escalabilidad en la implementación.

IP tiene dos principios básicos:

1. End-to-end Argument: La inteligencia y el manejo de las conexiones va en las puntas. Nunca hacemos algo en los nodos intermedios, si podemos hacerlo en el origen y/o en el destino. De este modo, todos los costos por saturación, estado por conexión, etc, los manejan los nodos interesados en la conexión y no el resto. Esto permite no mantener el estado de las conexiones en la red misma, sino sólo en el origen y el destino.

2. IP por sobre todas las cosas: La idea es definir un protocolo independiente de la red física, que logre pasar a través de todos los medios y no dependa de ninguno en particular.

IP se basa en el concepto de una inter-red, que es una red de redes. Es decir, no conecto un computador con otro, sino una red con otras. Esto me permite factorizar una buena cantidad de información, puesto que solo debo identificar la red destino para rutear.

El modelo de la inter-red, lo podemos ver en la Figura 4.2

27

Figure 4.2: Modelo de una Inter-red

El Router (que es un computador que conecta dos redes entre sí) se encarga del cambio de formato físico y del ruteo entre ambas redes. Para lograr hacer esto requerimos las siguientes características para IP:

1. Espacio de nombres o Únicos en toda la Inter-red o Independientes de la Red o Traducibles a direcciones físicas

2. Ruteo de los datos en base al ``nombre" IP del destino 3. Paso de los datos por los routers sin alteración

Direcciones IP

Para el nivel red, se define un espacio de direcciones de 32 bits, que serán usados en forma única para identificar cada computador conectado a la Inter-red (hoy (Marzo 1996) hay unos seis millones en Internet, pero este número se multiplica por dos cada año).

Para permitir ruteo fácil en base a la dirección IP, se dividen los bits en una dirección de red (bits superiores) y una dirección de host (bits inferiores).

La notación de las direcciones IP es en base a cuatro decimales separados por un punto (cada decimal codifica un byte: 0 - 255). Por ejemplo: 146.83.4.11

Inicialmente, las direcciones se separaron en clases A, B y C según sus bits iniciales. La idea era que la separación entre bits de red y bits de host es en un byte distinto para cada clase (ver Figura 4.3).

Al rutear, se debe separar el prefijo de red del sufijo de host. Luego, se rutea en base al prefijo. Sólo cuando ya estoy en la red correcta, utilizo el sufijo de host para encontrar la máquina destino en la red local. El número de bits del sufijo determina cuantos hosts puedo tener en la misma red: 256 para una clase C, 65.536 para una clase B y 16.777.216 para una clase A.

Los prefijos de red deben ser por lo tanto únicos en todo el mundo, por lo cual son asignados centralizadamente.

El ligar el ruteo a la dirección de la máquina implica que una máquina conectada a varias redes posee varias direcciones IP (una en cada red), lo que puede hacer que en un momento sea accesible por una dirección y no por otra.

28

Figure 4.3: Clases de direcciones IP

Sub-redes

Como obviamente 256 computadores es poco para la mayoría de las organizaciones, casi todas pedían una red clase B. Por otro lado 65.536 computadores es mucho para una sola red. Para dividir una red corporativa en múltiples redes internas, se usa el concepto de una sub-red, que considera los primeros bits de la parte host, como una extensión de la parte red, y los últimos como el verdadero host. O sea, en vez de dividir la dirección en red, host; la dividimos en red, sub-red, host. El número de bits en la sub-red es variable, y se define con una máscara de red, que es un conjunto de bits. Los bits en 1 definen el prefijo de red (red + sub-red) y los bits en 0 corresponden a la parte host. Por ejemplo, la clase B 146.83.0.0 la dividimos en redes con un máximo de 128 hosts cada una. Para esto el primer byte completo y un bit más son necesarios para el prefijo de red. La máscara es entonces: 255.255.255.128.

Esto me permite manejar la red como una sola entrada en las tablas de rutas de Internet, pero internamente sé que son múltiples redes interconectadas. La máscara de sub-red no requiere ser conocida fuera de la red local, puesto que hacia afuera somos una sola red (ver Figura 4.4).

Figure 4.4: Ejemplo de sub-redes

Para separar la parte red y la parte host, entonces, primero uso el prefijo de la clase. Si corresponde a mi red, uso la máscara para terminar de extraer el prefijo de red.

29

Super-Redes y CIDR

En el año 1992 se generó un importante proyecto de cambios en el manejo y asignación de direcciones IP en Internet. Básicamente, existían tres riesgos de muerte del Internet a mediano plazo:

1. Término de las direcciones clase B

A esa altura casi la mitad de las direcciones clase B estaban asignadas. Al ritmo de crecimiento de ese momento, iban a terminarse en un par de años. Básicamente, toda organización normal, requería de una clase B, puesto que una clase C era insuficiente.

2. Término de todas las direcciones IP

Claramente 32 bits no son suficientes para el crecimiento de Internet, y la tasa de pérdida que es parte de cualquier sistema de asignación que se utilice, dado que son prefijos de red, y las redes no están nunca totalmente utilizadas.

3. Explosión en las tablas de rutas

Los routers centrales de Internet deben mantener una tabla con una entrada para cada red conectada a Internet en el mundo entero. Actualmente, hay unas 60.000 redes, y cada vez resulta más difícil manejar ese tamaño, tanto en memoria como en ancho de banda para los protocolos de actualización.

Para resolver los problemas 1 y 3 se decidió desplegar un nuevo esquema de asignación y de manejo de las direcciones IP, de modo de disminuir el problema y darle tiempo al desarrollo de la nueva versión de IP (IP versión 6) que resolvería el punto 2.

Básicamente, una red clase A, B o C puede verse como una red con una máscara implícita. En el nuevo esquema, conocido como CIDR (Class-less IP), todas las redes se manejan con una máscara explícita para poder dividirlas en red/host y nos olvidamos de las clases. Por ello, podemos agrupar varias clases C contiguas, con una máscara común, extendiendo los bits de host hacia los de red, implementando lo que se conoce como super-redes. Por ejemplo, las clases C 200.0.0.0 y 200.0.1.0 pueden agruparse en una super-red 200.0.0.0 con máscara 255.255.254.0. Actualmente, las máscaras ya no se anotan así, y se prefiere escribir el número de bits de la parte red. En el ejemplo anterior, se habla de la red 200.0.0.0/23.

La existencia de super-redes ha permitido asignar el número de redes adecuado a cada institución sin sobrevender demasiado, disminuyendo el problema 1. Sin embargo, si las super-redes no son entendidas por los routers de Internet, exacerban el problema 3. Por ello, los nuevos protocolos de ruteo ya manejan este concepto, difundiendo redes agregadas, con un prefijo y una máscara. Esto me permite factorizar varias redes clases C agrupadas en una super-red, como una sola entrada en mi tabla de rutas.

Para poder manejar super-redes, sin embargo, se requiere un cambio mayor en la representación de las tablas de ruteo (el algoritmo de ruteo de IP se explica en la sección 4.4), porque (al contrario que en el caso de las sub-redes) ahora requiero conocer la

30

máscara de red en todas partes, incluso fuera de la red misma. En IP sin clases (CIDR) se supone que toda máquina y Router manejan tablas con el prefijo de red y la máscara asociada, de modo de poder separar la parte red y la parte host de una dirección cualquiera. Un punto importante es que aunque mi Router no hable CIDR y utilice el sistema antiguo igual funcione. Esto se logra haciendo que ese Router maneje una entrada para cada clase C de la super-red en cuestión, sin factorización. Obviamente, perdemos la ventaja de CIDR, pero al menos funciona.

Paquetes IP: Datagramas

Los datos se empaquetan en un datagrama, que es la unidad utilizada para atravesar las redes en camino. La idea básica de un datagrama es equivalente a una carta envuelta en un sobre. Los datos del sobre van en el header del paquete y el contenido va como datos. Al igual que en la carta, la idea es que al irse ruteando por la red el datagrama queda intacto, sin modificarse ni el header ni el contenido.

Cada datagrama es independiente, por lo cual pueden rutearse por caminos distintos. Por otro lado, IP provee un servicio del mejor esfuerzo, es decir no garantiza la entrega. Los paquetes pueden llegar a su destino desordenados, duplicados, alterados o incluso perderse.

Encabezamiento

El encabezamiento contiene las direcciones IP del origen y del destino, así como el largo y un checksum del header. Los datos no se validan, por lo que el nivel trasporte tendrá que encargarse de ellos. El encabezamiento puede verse en la figura 4.5.

Figure 4.5: Header del paquete IP

Todos los campos del encabezamiento se representan en forma estándar, conocida como network order. En alguna máquinas, deberemos traducir los enteros para llevarlos la representación correcta para esa arquitectura.

El header puede contener opciones que lo hagan más largo, por ello lleva un campo con el largo del header. El campo Protocol indica el tipo del protocolo de transporte (UDP, TCP, ICMP) al que pertenece este paquete. El campo tipo de servicio nunca se ha implementado realmente, pero la idea era diferenciar ruteo según requisitos de ancho de banda y/o delay.

El campo TTL sirve para limitar el tiempo que un paquete vive en la red (en particular en caso de ciclos en las rutas). Cada vez que un paquete pasa por un router debe decrementarse. Si llega a cero, el datagrama debe ser descartado.

Teóricamente, un router no debe cambiar nada en el encabezamiento, de modo de mantener el sobre y el contenido intactos hasta el destino final. El TTL es la excepción a la regla, y esto complica todo, puesto que el checksum del header debe recalcularse y cambiarse en cada router. Esto hace casi imposible hacer un ruteo eficiente de paquetes

31

IP. En IPv6 se ha rediseñado completamente el header de modo de hacerlo de tamaño fijo, sin checksum y las opciones típicamente sólo son analizadas en el destino final.

Fragmentación

Figure 4.6: Necesidad de Fragmentar

El último problema que queda es determinar el tamaño del datagrama. Obviamente quiero que sea lo más grande posible, pero debe caber en un frame físico. Por otro lado, las redes por las que transitará pueden tener tamaños de frames (MTU: Maximum Transfer Unit) distintos (ver 4.6). Si un paquete llega a un router y es muy grande para seguir su camino debo fragmentarlo en unidades más pequeñas.

Al fragmentar divido el datagrama en varios datagramas con (casi) el mismo header y los trozos de los datos en cada uno. El largo de cada datagrama es el que corresponde a cada fragmento. Cada uno de estos fragmentos será ruteado luego como un datagrama independiente.

El problema es que si el nivel transporte envía un datagrama, el receptor debe recibir también uno (y no varios más pequeños). Para esto, el receptor final ``pega" los fragmentos para reconstruir el datagrama original. En esto se usan los otros campos del encabezado (ver Figura 4.5): el identificador es un valor único por cada datagrama enviado desde un mismo host. Los fragmentos llevan todos el identificador del datagrama original, permitiendo reconocerlos. El offset del fragmento indica la posición dentro del datagrama original donde van los datos de este fragmento. (Para ahorrarse espacio en el header, el offset va anotado contando de a 8 bytes, por lo que debe multiplicarse por ocho para obtener el verdadero valor.)

Finalmente, en el campo Flags se anota que son fragmentos y no un datagrama completo. Otro bit existe ( No more fragments) para el último fragmento, de modo de saber cuándo terminar (ver 4.7).

32

Figure 4.7: Fragmentos de un Datagrama

Al recibir el primer fragmento de un datagrama, se activa un timer de modo que al transcurrir demasiado tiempo esperando armarlo lo descarto generando un error. Esto descarta el datagrama completo, aunque se hayan recibido varios fragmentos en el intertanto. Típicamente la implementación consiste en una lista enlazada de fragmentos para cada datagrama (identificado por su campo identificador), donde se van agregando los fragmentos a medida que llegan. Esto se hace así puesto que desconocemos el largo total del datagrama, hasta que no recibimos el último fragmento.

ICMP

Al detectarse un error relacionado con un datagrama, se envía un mensaje de error a la dirección IP de origen. Este mensaje va en un datagrama dirigido al layer IP propiamente tal, no a una aplicación de nivel superior. Por ello, se encapsula en un datagrama IP con valor protocolo (en el header) de ICMP. El datagrama original (que causó el error) va como dato.

Típicos paquetes de error son porque el TTL llegó a cero, porque no existen rutas a esa red, tiempo esperando fragmentos excedido, etc.

Ruteo

Ruteo Básico

Cada router y cada host mantiene una tabla de rutas, puesto que incluso un host conectado a una sola red debe saber cómo llegar a los distintos destinos. Todo el ruteo de un datagrama se hace paso a paso ( hop-by-hop), decidiendo cada vez a qué router de la red local debo entregárselo para acercarme al destino final. El ruteo se hace igual para los datagramas generados internamente por una aplicación como para uno recibido desde la red.

Un router está conectado directamente a una o más redes, cuyos prefijos de red conocemos. En esas redes pueden haber otros routers que nos permiten ir más lejos.

El algoritmo de ruteo que toda implementación de IP debe realizar, se basa en una tabla de rutas. Esa tabla consiste de una entrada para una red y el router que debo usar para ir

33

hacia ella. El router va representado por una dirección IP de una red a la cual yo estoy directamente conectado. En esa tabla también figuran todas las redes a las que estoy conectado las que se marcan con un tipo especial ( DIR).

En la figura 4.4.1 pueden ver un host conectado a una red con más de un router y su tabla de rutas.

Figure 4.8: Ejemplo de Tabla de Rutas

Para evitar el tener la tabla de todas las redes de Internet en todas las máquinas conectadas, se usa una ruta default que nos indica nuestro router usual para todas las redes que no conozco. Esta ruta se representa con la red 0.0.0.0 y se usa en todos los routers para mostrar la ruta hacia el resto de Internet. Obviamente, se requiere que algunos Routers de la red (los principales) no tengan ruta default, y efectivamente manejen la tabla completa de las redes conectadas (se llaman Routers default-less). Desde cualquier punto de Internet, la cadena de rutas default deben llevarnos a un Router default-less para que el algoritmo funcione.

El algoritmo básico de ruteo IP es:

RouteIP(dgram, table)

{

IPnet = getnet(dgram.destIP);

Route = search(IPnet, table);

if( Route.type == DIR )

sendphys(dgram, dgram.destIP);

else if (Route != NOT_FOUND)

sendphys(dgram, Route.gateway);

else

{

Route = search(default, table);

if( Route != NOT_FOUND )

sendphys(dgram, Route.gateway);

else

error(dgram, "Net Unreachable");

}

34

}

Manejo de Errores

El algoritmo anterior depende de la correctitud de las tablas de rutas utilizadas. Como estas tablas pueden contener errores, se incluyen algunos mecanismos básicos en IP para evitar daños demasiado graves.

Por ello, los datagramas incluyen el campo TTL, de modo de impedir que un ciclo en las rutas no genere datagramas permanentemente girando en la red, consumiendo ancho de banda sin llegar a ningún lado. Al llegar este contador a cero, el datagrama debe destruirse y no seguir ruteándolo. Sin embargo, es bueno generar un mensaje de error para el origen, de modo de advertirle que sus datagramas se están perdiendo. Esto va en un datagrama ICMP (Time Exceeded) al origen.

Sin embargo, es probable que (si hay un ciclo en una dirección) haya un ciclo también en la dirección del origen. Si esto ocurre, el datagrama ICMP también verá su TTL llegar a cero, y deberá ser destruido. Obviamente, si genero otro ICMP en este caso, ocurrirá lo mismo. Por ello, se define en IP que nunca se genera un mensaje ICMP para reportar errores producidos por paquetes ICMP de reportes de errores.

En el caso de recibir un paquete que debe rutearse por la misma interfaz de red por la que llegó, un Router rutea bien el paquete, pero también genera un ICMP redirect hacia el host de origen, si el origen está en la misma red.

Entrega Directa

La última etapa consiste en entregar el datagrama a su máquina destino correcta, una vez que ya llegó a la red de destino. Esto no es trivial, puesto que solo conozco su dirección IP, y lo que requiero ahora es su dirección física. Por supuesto, este problema también se aplica al tener que enviar paquetes a routers en mi propia red, para que ellos continúen el ruteo. En definitiva, nuestro problema general es enviar un datagrama cualquiera a una máquina de mi red, conociendo sólo su dirección IP.

Obviamente, conozco la interfaz de red por donde debo enviar el datagrama y también conozco la forma de encapsular el datagrama en un frame físico de este tipo de red. Pero aún así, debo encontrar la dirección física del destinatario (dirección ethernet, NSAP ATM, número de Token Ring, etc). Esto es un protocolo de traducción de direcciones, que depende de la red física en cuestión. El protocolo más usado es ARP ( Address Resolution Protocol) en medios que soportan broadcast. La idea es muy simple, hago un broadcast de un paquete con protocolo ARP (esto no es un datagrama IP), que contiene mis datos (dirección IP y física) y la dirección IP que quiero resolver. Este paquete es recibido por todas las máquinas de la red, quienes verifican si tienen esa dirección IP registrada como propia o no. Sólo quien tenga esa dirección como propia debe responder, completando la información del paquete ARP y enviándolo directamente al origen.

Al recibir la respuesta, el origen agrega a una tabla local (en memoria) el par dirección IP, dirección física. Este cache de ARP sirve para no preguntar por cada paquete en caso

35

de tráfico continuo. Sin embargo, los valores deben ir expirando en el tiempo para permitir cambios de asociaciones dinámicos.

En el caso de redes que no soportan broadcast, debemos tener tablas locales (mantenidas por personas) o un servidor ARP central a quien enviarle las preguntas (como un directorio). En este último caso, requiero que configuren a mano la dirección física de dicho servidor solamente.

ARP es uno de los problemas de IP en redes grandes, puesto que genera broadcasts que ningún filtro de nivel físico puede parar. Tormentas de broadcasts son frecuentes en redes con más de 100 máquinas.

Figure 4.9: Layers y empaquetamiento

Existen algunas redes físicas que trivializan esta operación, por ejemplo en Token Ring las estaciones están numeradas en el orden del anillo, y puedo usar el último byte de la dirección IP para almacenar el número de la estación. En este caso, puedo traducir directamente, sin ningún protocolo de red. En el caso de IPv6, cuyas direcciones IP son de 128 bits, puedo usar los últimos 48 bits para poner la dirección ethernet y no requiero ARP.

Al encapsular un paquete IP en un paquete físico, termina el proceso de empaquetamiento y finalmente es emitido el datagrama a la red (ver Figura 4.9). Si el datagrama va dirigido a un host de esta red local, la dirección IP y la dirección física serán de la misma máquina. Sin embargo, si el datagrama va dirigido a un host de otra red, la dirección física corresponderá a la del router de esta red y no estará relacionada con el destino.

36

Ruteo Interno

Hasta aquí hemos supuesto que las tablas de rutas existen, pero obviamente alguien tiene que configurarlas. En los hosts finales, típicamente es el administrador de sistemas quien configura la dirección IP de la máquina, la red local y el default gateway. Estas entradas se consideran rutas "estáticas".

Sin embargo, mantener una red de campus, donde varias redes locales se interconectan, requiere de múltiples tablas para mantener actualizadas. Al agregar o cambiar una red, debo hacer un cambio en cada router que me conecta con las redes locales (ver figura 4.4.4).

Figure: Rutas estáticas

Claramente, esto se vuelve inmanejable muy rápido. Por ello, se requiere de protocolos automáticos que difundan las tablas y permitan mantener esta información en los routers. Por otro lado, queremos aprovechar la existencia de múltiples caminos posibles, para hacer un ruteo más tolerante a fallas, que pueda encontrar rutas alternativas en casos de caidas.

Como el ruteo en todo Internet es algo complejo, se decidió tener dos familias de protocolos distintos: Ruteo Interno y Ruteo Externo. El Ruteo Interno se encarga de mantener tablas de rutas correctas en redes de tamaño mediano (país, institución, etc), que son controladas por una administración común. El Ruteo Externo se encarga de rutear en Internet global, que es una enorme colección de redes internas.

Veremos en este capítulo los protocolos internos habituales.

RIP

El protocolo más simple y antiguo es RIP, que viene provisto por el demonio routed en Unix, y fue introducido en Berkeley para mantener tablas correctas en sus redes locales.

37

Nunca fue concebido como un protocolo escalable de ruteo, a pesar que hoy se usa bastante en redes grandes.

La idea es mantener en la tabla de rutas (junto con la red y el gateway) una métrica que cuente la distancia a la que me encuentro de esa red. De esta forma, al recibir otras posibles rutas a la misma red, puedo elegir la más corta.

RIP es un protocolo de vectores de distancias, donde cada router puede verse como un nodo en un grafo, y las distancias son el número de nodos por los que debo pasar para llegar a mi destino. Cada router maneja su tabla de rutas, donde figuran todos los nodos del grafo y la distancia asociada (así como el gateway). Cada cierto tiempo, los routers envían esa tabla a todos sus vecinos. Al recibir la tabla de otro router, aprendo los caminos a redes que yo no conocía (y los agrego a mi tabla) y encuentro nuevos caminos a redes que ya conocía. Para elegir una ruta, comparo las métricas (al recibir una tabla, le sumo 1 a todas sus métricas, puesto que las redes están a un router más de distancia) y me quedo con la más pequeña. En caso de igualdad, me quedo con la ruta antigua, para evitar cambios permanentes en las rutas.

Además de las rutas aprendidas por RIP, típicamente manejamos una ruta default igual como antes, y las rutas directas a las redes a las que estoy conectado (cuyas métricas son cero).

Para encontrar a los routers y poder intercambiar con ellos las tablas, RIP utiliza un esquema de broadcast. Un router que habla RIP, difunde vía broadcast a todas las redes a las que está conectado su tabla de rutas periódicamente. Al recibir un broadcast RIP, el router compara sus entradas con las recibidas y actualiza la tabla.

Sin embargo, para poder adaptarse a fallas o caídas de routers, hay que poder borrar rutas también. Como no puedo confiar que el router caído me avise, se define un intevalo de tiempo fijo en RIP entre broadcasts (30 segundos). Al transcurrir varios intervalos sin escuchar nada de un router (180 segundos) todas las rutas que fueron recibidas desde él se invalidan.

RIP tiene varias ventajas, probablemente la principal es que funciona solo, prácticamente sin configuración o ingeniería inicial. Basta habilitar RIP en el router, y aprende y difunde todas las rutas automáticamente. Esta misma sencillez es su principal defecto, puesto que satura la red con broadcasts innecesarios, utiliza métricas que no toman en cuenta capacidades de las distintas redes, etc.

Figure 4.11: Loop en RIP

38

El principal problema de RIP es un defecto fundamental de cualquier protocolo de vector de distancias: al manejar sólo distancias, no puedo detectar los ciclos en las rutas. Al cambiar las rutas, es fácil caer en ciclos infinitos. Por ejemplo, en la Figura 4.11, podemos ver al router 1 que muere. El router 2 detecta esta muerte y elimina su ruta a la red Net1. En ese momento, recibe un broadcast del router 3, donde figura Net1 a distancia 2, por lo que agrega esa ruta (con distancia 3) apuntando al router 3. Obviamente, esto es un error, puesto que el router 3 debió haber cambiado la ruta a Net1 apuntando hacia el router 4. Este ciclo se mantendrá, pero la distancia se irá siempre incrementando.

Para evitar este efecto, en RIP se define que una métrica 16 es equivalente a infinito. Además, se implementan otras soluciones (como split horizon que no difunde por una interfaz las rutas aprendidas por esa misma). Sin embargo, estas soluciones siempre tienen efectos laterales negativos.

OSPF

La familia contraria a los vectores de distancia busca optimizar las rutas en base a una visión completa de la red interna. Cada router maneja una visión del estado actual de la red y las conexiones, y calcula en base a ella las rutas óptimas para llegar a todas las redes que la conforman.

La idea es que todos los routers manejan información completa y consistente de la red completa (son algoritmos para redes internas, por lo que se supone que no son demasiado grandes). Para adaptarse a los cambios, cada router mantiene el estado de sus enlaces y de sus routers vecinos. Cuando uno ya no le responde, o un enlace se cae, simplemente marca ese enlace caido en su estado. Cada cierto tiempo, el router envía el estado de todos sus enlaces locales a todos sus vecinos quienes a su vez lo propagan a todos sus vecinos (es un algoritmo de inundación). Estos mensajes son cortos, y el período entre un envío y el siguiente puede ser muy largo, puesto que lo cambios de estado fuerzan una transmisión también.

Al recibir un mensaje de estado de un router, todos los routers actualizan su estado y si hubo un cambio, recalculan las rutas a todos los destinos desde donde están ellos, aplicando el algoritmo de Dijkstra Shortest Path First. Como cada router sólo envía sus enlaces, los mensajes no crecen en tamaño a medida que la red crece. Claro que la memoria requerida para los routers y el tiempo de procesador requerido para recalcular rutas es mucho mayor que para RIP.

Las ventajas principales de OSPF es que es un algoritmo que converge muy rápido frente a cambios en la red, está libre de ciclos y soporta varias extensiones como autentificación, balanceo de carga y redes virtuales (simulando enlaces que pasan a través de varias redes IP).

Una desventaja importante es el consumo de CPU, y es conocido que un enlace que sube y baja muy seguido causa grandes trastornos en el funcionamiento. Para aliviar un poco este defecto, OSPF soporta el concepto de áreas, que permite subdividir la red completa en varias áreas OSPF, dentro de las cuales se aplica el algoritmo OSPF completo. Cada una de estas áreas está conectada con un backbone (área 0) que es una red de interconexión. Esto permite que cada área sólo mantiene su propio estado, y los

39

mensajes inundan sólo un área (ver Figura 4.12). La recomendación en OSPF es que un área no debe contener más de 200 routers.

Figure: Áreas OSPF

Ruteo Externo

El problema del ruteo global en Internet es un área de investigación aún hoy y es sumamente compleja, por lo que no vale la pena estudiarla en detalle acá. Sin embargo, el concepto utilizado para rutear en una red de este tamaño sigue basado en el algoritmo básico de ruteo hop-by-hop de paquetes IP. Existen varios routers en los principales puntos de interconexión, que no tienen rutas default y que manejan la tabla completa de todas las redes conectadas a Internet (que gracias a CIDR no son tantas como deberían).

Estas tablas son actualizadas para reflejar cualquier cambio en la accesibilidad de ellas, y por supuesto estos cambios son bastante frecuentes a nivel mundial. Todos los algoritmos anteriores (de ruteo interno) son imposibles de escalar a este nivel, básicamente por el nivel de detalle que manejan.

Para aliviar este problema, Internet ahora se maneja como un conjunto de Sistemas Autónomos (AS) en vez de un conjunto de redes IP. Un sistema autónomo es un conjunto grande de redes IP (típicamente todos los clientes de un proveedor) con un número único asignado por la IANA (Internet Assigned Numbers Authority) también responsable de la asignación de las direcciones IP.

El protocolo más usado hoy en día para el ruteo global es BGP-4, que soporta CIDR y sistemas autónomos. BGP-4 es un protocolo de vector de caminos, un derivado de vectores de distancias. Básicamente los routers ahora comunican por TCP (en vez de usar broadcasting) y se intercambian tablas de rutas con las distancias pero cada entrada viene con el camino que ha recorrido para llegar hasta este router. El camino es una lista de AS's, y si incluye mi AS, debo ignorar esa entrada (para evitar ciclos). Todas las entradas en mi tabla van acompañadas de su camino.

En general, se recomienda manejar por separado el ruteo interno del externo, y los routers que están en los bordes deben manejar dos tablas separadas: una para la lista de redes que deben anunciar por BGP y otra para las redes aprendidas por ruteo interno. Esto permite separar políticamente el ruteo, y evitar actuar como red de tránsito cuando no lo deseo.

El ruteo en Internet está en pleno desarrollo, y en IPv6 se espera rutear con otro protocolo (Domain Routing Protocol), manteniendo la idea de CIDR.

40

Nivel Transporte

Existen dos transportes posibles en TCP/IP: UDP y TCP. Existen algunos otros no muy utilizados, pero siempre se pueden agregar más.

UDP

UDP ( User Datagram Protocol) permite básicamente darle acceso directo a los datagramas IP a las aplicaciones de usuario.

El datagrama IP va de un computador a otro. Ahora quiero que vayan de un programa a otro (si el computador es multi-proceso, debo diferenciar los destinos/orígenes dentro de un mismo computador). Para esto, se usa un número de port. El paquete UDP es entonces un datagrama IP con un encabezamiento agregado que comprende básicamente un port de origen y un port de destino. Además se incluye un checksum que incluye los datos (puesto que en IP solo verificamos el encabezado). En IPv6 UDP se conserva igual, y solo se modifica IP, pero se vuelve obligatorio siempre implementar el checksum (antes era opcional) incluyendo encabezado y datos. Como en IPv6 no hay checksum en IP, es obvio que no podemos dejar el encabezado desprotegido. Este checksum también incluye las direcciones IP.

Figure 4.13: Paquete UDP

Tal como en el nivel red se revisa la dirección IP de destino, en el nivel UDP se revisa el port de destino para decidir a qué proceso corresponde el datagrama (ver Figura 4.14).

41

Figure 4.14: Paquete UDP dirigido a un port

Los ports son enteros de 16 bits, y los menores de 1024 se consideran oficiales, y los superiores son utilizados más libremente. TCP (el otro protocolo de transporte) también utiliza ports, pero el espacio es disjunto. Es decir un port 25 en TCP no tiene relación con el port 25 en UDP (aunque por razones de administración se trata de no asignar ports iguales a distintos servicios en ambos protocolos).

Un ejemplo de los ports UDP más conocidos (del archivo /etc/services):

echo 7/udp

discard 9/udp sink null

daytime 13/udp

chargen 19/udp ttytst source

time 37/udp timserver

rlp 39/udp resource # resource location

name 42/udp nameserver

domain 53/udp

bootps 67/udp # bootp server

bootpc 68/udp # bootp client

tftp 69/udp

sunrpc 111/udp portmapper # RPC 4.0 portmapper TCP

ntp 123/udp # Network Time Protocol

netbios-ns 137/udp nbns

netbios-dgm 138/udp nbdgm

snmp 161/udp

snmp-trap 162/udp

biff 512/udp comsat

who 513/udp whod # BSD rwhod(8)

syslog 514/udp # BSD syslogd(8)

talk 517/udp # BSD talkd(8)

ntalk 518/udp # SunOS talkd(8)

route 520/udp router routed # 521/udp too

timed 525/udp timeserver

netwall 533/udp # -for emergency

broadcasts

new-rwho 550/udp new-who # experimental

rmonitor 560/udp rmonitord # experimental

monitor 561/udp # experimental

mount 635/udp # NFS Mount Service

pcnfs 640/udp # PC-NFS DOS

Authentication

bwnfs 650/udp # BW-NFS DOS

Authentication

42

nfs 2049/udp # NFS File Service

Uno se podría preguntar ¿para qué sirve un sistema de tranporte de datagramas? Efectivamente, los datos se pueden desordenar o perder (la alteración se impide vía el checksum). Sin embargo, ofrece una ventaja importante: eficiencia.

Hay dos tipos de aplicaciones típicas de UDP: cuando la eficiencia y bajo consumo de recursos es crucial (por ejemplo: NFS) o cuando requiero soportar múltiples destinatarios: broadcast o multicast.

TCP

El método de transporte más popular en Internet es TCP (Transmission Control Protocol), ya que provee una conexión punto a punto confiable y ordenada, tipo secuencia de bytes (equivalente a un pipe de Unix bi-direccional).

Por ello, TCP requiere una operación de connect que establece la conexión entre dos procesos. Una vez realizada la conexión, puedo leer y escribir en un descriptor tal como si fuese un pipe Unix.

Al igual que en UDP, el port me indica el servicio (o el proceso) con el que quiero conectarme. Una vez establecida la conexión, se utiliza la tupla [IP orig, port orig, IP dest, port dest] para identificar la conexión. Eso me permite tener múltiples conexiones a un mismo port (por ejemplo mail) sin confundirlas.

Manteniendo la separación entre layers, TCP maneja las conexiones sin que IP sepa nada al respecto. Por ello, cuando la conexión está establecida, eso quiere decir que los dos extremos tienen recursos reservados para ella, pero entre ambos los routers no conocen su existencia. Por lo tanto, toda la inteligencia de retransmisión, descarte de duplicados, etc, debe proveerla TCP mismo. Los routers no participan, sólo los computadores origen y destino.

Ventanas de TCP

TCP es un protocolo de ventanas, orientado a los bytes, no a los paquetes o mensajes. Para enviarlos, utiliza segmentos TCP que serán encapsulados en datagramas IP. Para adaptarse a los diferentes delays en las redes y a la varianza de ellos en Internet, TCP utiliza un esquema de ventanas correderas, cuya implementación además permite control de flujo. El protocolo es básicamente Go Back N.

Las ventanas de TCP son medidas en bytes (no en segmentos), y son de tamaño variable. Como la conexión es bi-direccional, existen dos ventanas en cada extremo para los flujos en ambas direcciones. El enviador mantiene un tamaño máximo de ventana, y transmite todos los segmentos pendientes hasta completar la ventana. Los acks enviados por el receptor se anexan como piggyback a los datos que viajan en dirección contraria o como paquetes TCP con sólo el header. El número de secuencia de los segmentos y de los acks siempre referencian un número de byte (posición en la secuencia).

El receptor sabe qué número de secuencia espera y descarta todos los paquetes fuera de rango. Al recibir un paquete erróneo, siempre responde con un ack con el número de

43

secuencia esperado. Como siempre existe un timeout esperando un ack. Al ocurrir un timeout, no sabemos qué parte de la ventana se perdió. Lo usual entonces es retransmitir sólo el primer segmento de la ventana, esperando el ack. La llegada del ack indicará de donde continuar.

La ventana trata de aproximarse a un tamaño óptimo para mantener el flujo de datos sin nunca bloquearse esperando un ack. Por otro lado, TCP la utiliza para control de flujo (por ejemplo si la aplicación deja de leer y los datos siguen llegando). Junto con cada ack viaja un anuncio con el tamaño de la ventana del receptor, que indica qué tan ocupados están sus buffers (entre TCP y la aplicación). El emisor adapta su ventana a este tamaño, agrandándola o achicándola. Para achicar la ventana, TCP no puede disminuir su número de secuencia máximo ya transmitido, sino que no avanza más y espera que los acks recibidos vayan achicando la ventana por el otro lado.

Segmentos TCP

Un segmento TCP lleva un header y los datos (opcionales) como se muestra en la figura 4.15.

Figure 4.15: Segmento TCP

Los campos acknowledgement y window corresponden a datos del receptor hacia el enviador y el resto del enviador hacia el receptor. El checksum incluye las direcciones IP, los ports y el largo del paquete. Para evitar duplicar información, el largo del segmento se saca del largo del datagrama IP menos el largo del header (HLEN). Cuando se requiere enviar un ack y no hay datos pendientes, se envía un segmento vacío, sólo con el header.

El Urgent Pointer sirve para especificar datos que deben adelantar la secuencia normal para ser tratados inmediatamente por la aplicación (ej: control-C durante un telnet).

Tamaño del Segmento

Al negociar una conexión, se intenta determinar el tamaño máximo de segmento que se enviará (MSS). La idea es determinar la MTU mínima que existe entre ambos

44

computadores, y usar ese valor. Esto evita fragmentación utilizando paquetes lo más grandes posibles.

Originalmente, se utilizaba el valor 536 como máximo de segmento en Internet, y la MTU si era en la red local. Actualmente, se utiliza un algoritmo de path MTU Discovery, que envía paquetes IP grandes con la opción de No Fragmentar. Estos paquetes deben ser devueltos vía ICMP con un error si un router quiere fragmentarlos. En la nueva especificación, el router debe agregar al paquete ICMP su MTU, para que el algoritmo converja más rápido. Al recibir dichos mensajes, cambio el tamaño máximo de segmento al valor requerido, hasta llegar al destino. Durante el resto de la transmisión, TCP transmite todos sus segmentos con el bit de No Fragmentar. De este modo, aunque la ruta cambie alterando la MTU, TCP puede recuperarse y adaptar su MSS.

Timeouts

Manejar los timeouts de retransmisión es siempre complejo, porque requiere determinar el período de tiempo que demora un paquete en ir y un ack en volver. En el caso de la Internet es mucho más complejo, porque los atrasos son sumamente variables, en particular frente a congestión. Ningún protocolo con timeouts fijos sobrevive en Internet.

TCP fue revisado a fines de los '80 para optimizar su comportamiento y ha sido sintonizado para uso en Internet. Básicamente, el protocolo calcula todo el tiempo una aproximación al tiempo en ir y volver (RTT: Round Trip Time) al recibir un ack. La estimación es un promedio ponderado entre el valor anterior y la nueva estimación de modo de irse adaptando, pero no demasiado rápido (la varianza es muy alta por lo que no podemos usar sólo el último valor). La fórmula utilizada es:

RTT = (alpha * RTT) + ((1-alpha)*RTT_sample)

Actualmente, se utiliza típicamente un valor de alpha = 7/8 de modo de reaccionar lentamente a los cambios. Teniendo una estimación del tiempo promedio, normalmente se definía un timeout del doble de ese valor. Sin embargo, frente a fuerte congestión, la varianza del RTT aumenta, pudiendo incluso abarcar dentro de lo razonable el doble del RTT medio (carga sobre el 30% puede producir esto). Para evitar esto, TCP calcula también la desviación estándar del RTT (afortunadamente esto no requiere mucho cálculo) y usa la varianza multiplicada por el RTT como timeout. El código es:

DIFF = RTT_sample - RTT;

RTT = RTT - alpha * DIFF;

MDEV = MDEV + alpha * (abs(DIFF) - MDEV)

Un problema pendiente es cómo calcular RTT frente a retransmisiones, puesto que un ack puede corresponder al original o al retransmitido. El algoritmo de Karn, implementado en TCP, no actualiza RTT cuando se recibe el ack de un segmento retransmitido. Al mismo tiempo, el enviador multiplica por dos su timeout cada vez que retransmite un segmento. En general, se acepta un valor máximo de 120 segundos (lo que implica que será imposible usar las implementaciones actuales de TCP para comunicarse con Marte).

45

Control de Congestión

Otra optimización de TCP es para evitar empeorar situaciones de congestión con retransmisiones. El algoritmo se conoce como slow start y multiplicative decrease. Básicamente, el enviador maneja su ventana con dos límites: el tamaño que le informa el receptor y el tamaño de la ventana de congestión. Inicialmente, la ventana de congestión es igual a la del receptor. Pero al retransmitir un segmento, la ventana de congestión se disminuye a la mitad. En todo momento, el enviador actúa con un tamaño de ventana igual al mínimo entre la ventana del receptor y la ventana de congestión. Al seguir perdiendo datos, TCP sigue acortando la ventana y multiplicando los timeouts por dos. Eventualmente, se llega a un solo segmento y un timeout de 120 segundos. Aunque la máquina de destino haya dejado de responder, este nivel de retransmisión no contribuye a la saturación. Para evitar inestabilidades en las ventanas, la recuperación es lenta, y cada vez que se recibe un ack, la ventana de congestión se incrementa en un segmento, hasta llegar a la mitad del tamaño de la ventana del enviador. En ese nivel, TCP entra en una fase de evitar la congestión, y sólo continúa incrementando la ventana una vez que todos los segmentos en ella han recibido su ack.

Todos estos mecanismos, hacen de TCP un ``buen ciudadano" en Internet, pero también hacen que la performance se degrada mucho frente a enlaces con pérdidas de paquetes.

Interfaz de red: sockets

La interfaz estándar a TCP/IP, disponible en casi todos los sistemas operativos, son los sockets. En este capítulo nos orientaremos a Unix, pero la mayor parte de lo que diremos se aplica a todas las implementaciones.

La cantidad de parámetros a las funciones de sockets es mayor de lo necesario porque fueron diseñadas de modo de servir para múltiples protocolos y no sólo para TCP/IP. Por ello, hay que decir siempre de qué protocolo se trata y el largo de los parámetros. Por otro lado, las direcciones IP y los ports van siempre en formato de red, y deben manipularse con las funciones predefinidas para ello.

Los sockets pueden usarse tanto para TCP como para UDP,

Bind: 1 clase RPC+XDR+NFS 2 clases

Banda Ancha Multicast: 1 clase