COMO hacer Multicast sobre TCP/IP

Juan-Mariano de Goyeneche jmseyas@dit.upm.es. Traducido por Javier Fernández-Sanguino Peña jfs@computer.org.

v1.0.2, 28 Marzo 1998 (última revisión: 20 Marzo 1998)
Este COMO trata de cubrir la mayor parte de los aspectos relacionados con multicast sobre redes TCP/IP. Así pues, mucha de la información no es específica de Linux (aclaración para los que no utilizan GNU/Linux... aún). Actualmente multicast es un área de investigación contínua, y, en el momento de escribir este documento, muchos de los ``estandards'' son meramente drafts. No lo olvides mientras lees las líneas que siguen.

1. Introducción.

Intentaré darte la información más amplia, precisa y al día relacionada con mutlicast sobre redes TCP/IP que me sea posible. Cualquier ``feedback'' es bienvenido. Si encuentras cualquier error en este documento, tienes un comentario sobre sus contenidos, o una actualización o cosa a añadir, por favor envíamelo a la dirección que aparece al principio de este documento.

1.1 ¿Qué es Multicast?

Multicast es... una necesidad. Bueno, al menos en algunas ocasiones. Si tienes información (mucha información habitulamente) que debe ser transmitida a varios ordenadores (pero no a todos) en una internet, entonces la respuesta es Multicast. Una situación frecuente donde se utiliza es en la distribución de audio y video en tiempo real a un conjunto de ordenadores que se han unido a una conferencia distribuida.

Multicast es, en gran medida, como la televisión o la radio, es decir, sólo aquellos que han sintonizado sus receptores (al seleccionar una frecuencia particular que les interesa) reciben la información. Esto es: escuchas los canales que te interesan, pero no otros.

1.2 El problema con Unicast.

Unicast es cualquier cosa que no sea broadcast o multicast. Está bien, la definición no es muy brillante... pero cuando envías un paquete y sólo hay un emisor -tú- y un receptor (aquél al que envías el paquete), entonces estás haciendo unicast. TCP es, por propia naturaleza, orientado a unicast. UDP soporta muchos otros paradigmas, pero si estás enviando paquetes UDP y sólo se supone que hay un proceso que lo recibe, es también unicast.

Durante años las transmisiones unicast demostraron ser suficientes para Internet. La primera implemetación de multicast no vió la luz hasta 1993, con la versión 4.4 de BSD. Parece que nadie lo necesitaba hasta entonces. ¿Cuáles eran los nuevos problemas que trataba de arreglar multicast?

No es necesario decir que Internet ha cambiado mucho desde aquellos años. Particularmente, el nacimiento de la World Wide Web (WWW) cambió sensiblemente la situación: la gente no quería solamente conexiones a ordenadores remotos, correo electrónico y FTP. Primero querían ver las fotos de las personas, dentro de páginas personales, pero más tarde tambien querían verles y oírles.

Con la tecnología actual es posible afrontar el ``coste'' de hacer una conexión unicast con todos aquellos que desean ver su página web. Sin embargo, si quieres enviar audio y video, que necesita de un gran ancho de banda comparado con aplicaciones de web, tienes (tenías, hasta que apareció multicast) dos opciones: establecer conexiones unicast por separado con cada uno de los receptores, o usar broadcast. La primera solución no era factible: si hemos dicho que cada conexión enviando audio/video consume una gran cantidad de ancho de banda, imagina tener que establecer cientos, quizás miles, de estas conexiones. Tanto el ordenador emisor como su red se colapsarían.

Broadcast parece una solución, pero desde luego no es la solución. Si desearas que todos los ordenadores en su LAN atendieran la conferencia, podrías usar broadcast. Se enviarían los paquetes una sola vez y cada ordenador lo recibiría ya que fueron enviados a la dirección de broadcast. El problema es que quizás solo algunos de éstos y no todos estén interesados en estos paquetes. Más aún: quizás algunos ordenadores están realmente interesados en su conferencia, pero están fuera de su LAN, a varios routers de distancia. Y ya se sabe que el broadcast funciona bien dentro de una LAN, pero surgen problemas cuando haces broadcast de paquetes que deben ser enviados a través de diferentes LANs.

La mejor solución parece ser aquella en la que sólo se envían paquetes a ciertas direcciones especiales (una determinada frecuencia en transmisión de radio y televisión). Así, todos los ordenadores que han decidido unirse a la conferencia conocerán la dirección de destino, de forma que los recogerán cuando pasen por la red y los enviarán a la capa IP para que sean demultiplexados. Esto es similar al broadcast en el sentido de que sólo se envía un paquete de broadcast y todos los ordenadores en la red lo reconocen y lo leen; difiere, sin embargo, en que no todos los paquetes de multicast son leídos y procesados, sino solamente los que han sido registrados en el kernel como ``de interés''.

Estos paquetes especiales son encaminados en el nivel del kernel como cualquier paquete porque son paquetes IP. La única diferencia puede estar en el algoritmo de encaminamiento que le dice al kernel si debe o no encaminarlos.

2. Explicación del Multicast.

2.1 Direcciones Multicast.

Como probablemente sepa, el conjunto de las direcciones IP está divido en ``clases'' basado en los bits de mayor orden en una dirección IP de 32 bits.


   Bit -->  31                           0            Rango de direcciones:
           +-+----------------------------+
           |0| Direcciones de clase A     |       0.0.0.0 - 127.255.255.255
           +-+----------------------------+
           +-+-+--------------------------+
           |1 0|  Direcciones de clase B  |     128.0.0.0 - 191.255.255.255
           +-+-+--------------------------+
           +-+-+-+------------------------+
           |1 1 0| Direcciones de clase C |     192.0.0.0 - 223.255.255.255
           +-+-+-+------------------------+
           +-+-+-+-+----------------------+
           |1 1 1 0| Direcciones MULTICAST|     224.0.0.0 - 239.255.255.255
           +-+-+-+-+----------------------+
           +-+-+-+-+-+--------------------+
           |1 1 1 1 0|    Reservadas      |     240.0.0.0 - 247.255.255.255
           +-+-+-+-+-+--------------------+

La que nos interesa es la ``Clase D''. Cada datagrama de IP cuya dirección destino empieza por ``1110'' es un datagrama IP de Multicast.

Los 28 bits restantes identifican el ``grupo'' multicast al que se envía el datagrama. Siguiendo con la analogía anterior, debe sintonizar su radio para oir un programa que se transmite a una frecuencia determinada, del mismo modo debe ``sintonizar'' su kernel para que reciba paquetes destinados a un grupo de multicast específico. Cuando hace esto, se dice que el ordenador se ha unido a aquél grupo en el interfaz especificado. Esto se verá más adelante.

Hay algunos grupos especiales de multicast, o ``grupos de multicast bien conocidos'', y no debería usar ninguno de estos en una aplicación determinada dado que están destinados a un propósito en particular:

Todos estos grupos especiales de multicast son publicados cada cierto tiempo en el RFC de ``Números asignados''.

En cualquir caso, el conjunto de direcciones de la 224.0.0.0 a 224.0.0.255 están reservados localmente (para tareas administrativas y de mantenimiento) y los datagramas enviados a estos nunca se envían a los encaminadores multicast. De manera similar, el conjunto 239.0.0.0 a 239.255.255.255 ha sido reservado para ámbitos administrativos (administrative scoping, n. del t.) (para más información ver la explicación sobre el TTL más abajo).

2.2 Nivel de cumplimiento.

Los ordenadores pueden estar en tres niveles en lo que se refiere al cumplimiento de la especificación de Multicast, de acuerdo con los requisitos que cumplen.

Se está en Nivel 0 cuando no hay soporte para Multicast en IP. Un buen número de los ordenadores y los encaminadores de Internet están en este nivel, ya que el soporte de multicast no es obligatorio en IPv4 (sí lo es, sin embargo, en IPv6). No es necesaria mucha explicación aquí: los ordenadores en este nivel no pueden enviar ni recibir paquetes multicast. Deben ignorar los paquetes enviados por otros ordenador con capacidades de multicast.

En el Nivel 1 hay soporte para envío pero no para recepción de datagramas IP de multicast. Nótese, por tanto, que no es necesario unirse a un grupo multicast para enviarle datagramas. Se añaden muy pocas cosas al módulo IP para convertir un ordenador de ``Nivel 0'' a ``Nivel 1'', como muestra la sección Envío de Datagramas multicast.

El Nivel 2 es el de completo soporte a multicast en IP. Los ordenadores de nivel 2 deben ser capaces de enviar y recibir tráfico mutlicast. Tienen que saber la forma de unirse o dejar grupos multicast y de propagar ésta información a los encaminadores multicast. Es necesario incluir, por tanto, una implementación del Protocolo de Gestión de Grupos de Internet (Internet Group Management Protocol, IGMP) en su pila TCP/IP.

2.3 Envío de Datagramas multicast.

Por ahora debe ser evidente que el tráfico multicast es manipulado en el nivel de transporte con UDP, ya que TCP provee de conexiones punto a punto que no son útiles para tráfico multicast. (Se está llevando a cabo una fuerte investigación para definir e implementar nuevos protocolos de tansporte orientados a multicast. Ver la sección Protocolos de transporte Multicast para más detalles).

En principio, una aplicación sólo necesita abrir un socket UDP y poner como dirección de destino la dirección multicast de clase D donde quiere enviar sus datos. Sin embargo, hay algunas operaciones que el proceso emisor debe ser capaz de controlar.

TTL.

El campo TTL (Time To Live, Tiempo de vida) en la cabecera IP tiene un doble significado en multicast. Como siempre, controla el tiempo de vida de un datagrama para evitar que permanezca por siempre en la red debido a errores en el encaminamiento. Los encaminadores decrementan el TTL de cada datagrama que pasa de una red a otra y cuando este valor llega a 0 el paquete se destruye.

El TTL del multicast en IPv4 tiene también el significado de ``barrera''. Su uso se hace evidente con un ejemplo: supongamos que tiene una video conferencia larga, que consume mucho ancho de banda, entre todos los ordenadores que pertenecen a su departamento. Así que quiere que esa gran cantidad de tráfico permanezca en su LAN y no salga fuera. Quizás su departamento es suficientemente grande para tener varias LANs. En este caso quieres que los ordenadores que pertenecen a cada una de sus LANs atiendan la conferencia, pero en ningún caso quiere colapsar todo Internet con su tráfico multicast. Es necesario limitar hasta dónde se expandirá el tráfico multicast entre encaminadores. Para esto se utiliza el TTL. Los encaminadores tienen una barrera TTL asignada a cada uno de sus interfaces, de forma que sólo los datagramas con un TTL mayor que la barrera del interfaz se reenvian. Sin embargo, cuando un datagrama atraviesa un encaminador con una barrera determinada, el TTL del datagrama no se ve decrementado por el valor de la barrera. Sólo se hace una comparación (como antes, el TTL se decrementa en uno cada vez que un datagrama pasa por un encaminador).

Una lista de las barreras de TTL y su ámbito asociado es la siguiente:


TTL     ámbito
----------------------------------------------------------------------
   0    Restringido al mismo ordenador. No se enviará por ningún interfaz.
   1    Restringido a la misma subred. No será reenviada por ningún encaminador.
 <32         Restringido al mismo ``sitio'', la misma organización o departamento.
 <64 Restringido a la misma región.
<128 Restringido al mismo continente.
<255 Sin ámbito restringido. Global.

Nadie sabe qué significa exactamente ``sitio'' o ``región''. Es tarea de los administradores decidir qué límite tienen estos ámbitos.

El truco-TTL no es siempre suficientemente flexible para todas las necesidades, especialmente cuando se trata con regiones solapadas o se desea establecer límites geográficos, topológicos o de ancho de banda de manera simultanea. Para resolver estos problemas, se definieron regiones de multicast de IPv4 con ámbito administrativo en 1994 (ver el borrador de Internet de D. Meyer: 'ádministratively Scoped IP Multicast''). Se definen los ámbitos basándose en direcciones de multicast en lugar de en TTLs. El rango 239.0.0.0 a 239.255.255.255 se reserva para estos ámbitos administrativos.

Loopback.

Cuando el ordenador emisor es de nivel 2 y también miembro del grupo al que se envían los datagramas, por defecto se envía una copia también a sí mismo, lo que se conoce con el nombre de loopback. Esto no significa que la tarjeta interfaz relea sus propias transmisiones, reconociéndolas como dirigidas al grupo al que pertenece y las recoja de nuevo de la red. Al contrario, es la capa IP la que, por defecto reconoce el datagrama que va a enviar y lo copia y encola en la cola de entrada de IP antes de enviarlo.

Este comportamiento es deseable en algunos casos, pero no en otros. Así que el proceso emisor puede activarlo o desactivarlo según desee.

Selección de interfaz.

Los ordenadores conectados a más de una red deberían ofrecer la posibilidad a las aplicaciones para que estas puedan decidir qué interfaz de red será usado para enviar las transmisiones. Si no se especifica, el kernel escogerá uno por defecto basándose en la configuración realizada por el administrador.

2.4 Recepción de datagramas Multicast.

Unirse a un grupo Multicast.

El broadcast es (en comparación) más sencillo de implementar que el multicast. No necesita que ningún proceso le dé ninguna regla al kernel para que éste sepa qué hacer con los paquetes de broadcast. El kernel ya sabe que hacer: leer y entregar todos ellos a las aplicaciones apropiadas.

Con multicast, sin embargo, es necesario avisar al kernel cuáles son los grupos multicast que nos interesan. Esto es, debemos pedir al kernel que se ``una'' a estos grupos multicast. Dependiendo del hardware que haya por debajo, los datagramas de multicast son filtrados por el hardware o por la capa IP (y en algunos casos por ambos). Y sólo aquellos con un grupo destino previamente registrado son aceptados.

Esencialmente, cuando nos unimos a un grupo le estamos diciendo al kernel: ``Vale. Sé que, por defecto, ignoras datagramas multicast, pero recuerda que estoy interesado en este grupo multicast. Así que lee y entrega (a cualquier proceso interesado en estos, no sólo a mí) cualquier datagrama que veas en este interfaz de red con este grupo de multicast como su dirección destino''.

Algunas consideraciones: en primer lugar, destacar que no sólo se une uno a un grupo. Se une a un grupo en un interfaz de red determinado. Por supuesto, es posible unirse al mismo grupo en más de un interfaz. Si no se especifica un interfaz en concreto entonces el kernel lo elegirá en base a sus tablas de encaminamiento cuando se vayan a enviar datagramas. Es también posible que más de un proceso se una al mismo grupo multicast en el mismo interfaz. Todos ellos recibirán los datagramas enviados a ese grupo vía ese interfaz.

Como se ha dicho antes, los ordenadores con capacidades multicast se unen al grupo todos los ordenadores en el arranque, así que ``pingeando'' a 224.0.0.1 nos devolverá todos los ordenadores de la red que tienen el multicast habilitado.

Finalmente, un proceso que desee recibir datagramas multicast tiene que pedir al kernel que se una al grupo y hacer un ``bind'' al puerto al que se dirigen los datagramas. La capa UDP utiliza tanto la dirección de destino como el puerto para demultiplexar los paquetes y decidir a qué socket/s entregarlos.

Abandonar un grupo multicast.

Cuando un proceso ya no está interesado en un grupo multicast, informa al kernel que él quiere abandonar este grupo. Es necesario entender que esto no significa que el kernel no acepte más datagramas multicast dirigidos a ese grupo multicast. Seguirá haciendolo si hay más procesos que hicieron una petición ``unirse en multicast'' a ese grupo y siguen interesados. En este caso el ordenador recuerda los miembros del grupo, hasta que todos los procesos deciden dejarlo.

Aún más: si abandonas el grupo, pero se permanece unido al puerto donde se recibe el tráfico multicast, y hay más procesos que se unieron al grupo, seguirás recibiendose las transmisiones multicast.

La idea es que unirse a un grupo multicast sólo dice al nivel de IP y de enlace (que en algún caso se lo dirá explicitamente al hardware) que acepten datagramas multicast para ese grupo. Quienes son realmente miembros de un grupo son los ordenadores, no los procesos que corren en esos ordenadores.

Proyección de direcciones IP Multicast sobre direcciones Ethernet/FDDI.

Tanto en Ethernet como en FDDI, las tramas tienen un campo de dirección destino de 48 bits. Para permitir un tipo de ARP multicast que proyecte las direcciones de multicast IP sobre las Ethernet/FDDI, el IANA reservó un conjunto de direcciones para multicast: cada trama Ethernet/FDDI con su dirección destino en el rango 01-00-5e-00-00-00 a 01-00-53-ff-ff-ff (hexadecimal) contiene datos para un grupo multicast. El prefijo 01-00-5e identifica la trama como de multicast, el siguiente bit es siempre 0, y así sólo se dejan 23 bits para la dirección multicast. Ya que los grupos de multicast IP son de 28 bits la proyección no puede ser uno a uno. Sólo los 23 bits menos significativos del grupo multicast IP se ponen en la trama. Los 5 bits más significativos son ignorados, dando lugar a 32 grupos de multicast que se proyectan a la misma dirección Ethernet/FDDI. Esto significa que el nivel de Ethernet actua como un filtro imperfecto, y el nivel IP tendrá que decidir si aceptar o no los datagramas que el nivel de enlace le ha entregado. El nivel IP actua como el filtro definitivo perfecto.

Los detalles de Multicasting IP sobre FDDI se dan en el RFC 1390: ``Transmission of IP and ARP over FDDI Networks''. Para más información en la proyección de direcciones multicast IP sobre Ethernet puede consultar draft-ietf-mboned-intro-multicast-03.txt: ``Introduction to IP Multicast Routing''.

Si está interesado en Multicast IP sobre redes de área local de Token-Ring mirar el RFC 1469 para más detalles.

3. Requisitos del kernel y configuración.

Linux es, por supuesto (¿lo dudaba?), completamente capaz de hacer Multicast en el Nivel 2. Cumple todos los requisitos para enviar, recibir y actuar como un encaminador (mrouter) para datagramas multicast.

Si quiere sólo enviar y recibir debe decir ``sí'' a la pregunta ``IP: multicasting'' cuando configure su kernel. Si también quiere que su ordenador con Linux pueda actuar como un encaminador de multicast (mrouter) debe activar también encaminamiento multicast en el kernel seleccionando: ``IP: forwarding/gatewaying'', ``IP: multicast routing'' y ``IP: tunneling'', este último porque las nuevas versiones de mrouted dependen de túneles sobre IP para enviar los datagramas de multicast encapsulados en datagramas de unicast. Es necesario cuando se establecen túneles entre ordenadores con multicast separados por redes y encaminadores capaces sólo de unicast (el demonio mrouted implementa el algoritmo de encaminamiento multicast -la política de encaminamiento- e indica al kernel cómo debe encaminar datagramas multicast).

Algunas versiones del kernel marcan el encaminamiento de multicast como ``EXPERIMENTAL'', así que debe activar la opción ``Prompt for development and/or incomplete code/drivers'' (Preguntar por disposivivos/código incompleto o/y en desarrollo) en la sección ``Code maturity level options'' (Opciones de madurez del código N. del T.).

Si cuando se está ejecutando mrouted, el tráfico generado en la misma red que el ordenador al que su GNU/Linux está conectado se reenvía correctamente a la otra red, pero no puedes ver el tráfico de la otra red en su red local, compruebe si está recibiendo mensajes de error de protocolo ICMP. Lo más posible es que olvidase activar los túneles sobre IP en su encaminador GNU/Linux. Es un error algo estúpido cuando lo conoces, pero, creame, lleva mucho tiempo encontrarlo cuando no es así, y no hay razón aparente que indique qué algo está funcionando mal. Un sniffer demuestra ser muy útil en estas situaciones.

(Puede ver más sobre encaminamiento multicast en la sección Políticas de encaminamiento y técnicas de reenvío; se explica también mrouted y los túneles en las secciones El Mbone y Aplicaciones Multicast).

Una vez que ha compilado e instalado su nuevo kernel, debe dar una ruta por defecto al tráfico multicast. El objetivo es añadir una ruta para la red 224.0.0.0.

El problema al que la mayor parte de la gente se enfrenta en este punto de la configuración es la elección del valor de la máscara de red. Si ha leido el excelente NET-3-HOWTO de Terry Dawson (aún no traducida, n. del t.), no te debería ser difícil adivinar el valor correcto. Como allí se explica, la máscara es un número de 32 bits con todo unos en la parte de red de su dirección IP, y con todo ceros en la parte de ordenadores. Recuerde de la sección Direcciones Multicast que una dirección multicast clase D no tiene parte de red ni ordenador. En su lugar tiene un identificador de grupo de 28 bits y un identificador de clase D de 4 bits. Pues bien, estos 4 bits son la parte de red y los restantes 28 la parte de los ordenadores. Así que la máscara buscada es 11110000000000000000000000000000 o, más fácilmente: 240.0.0.0. Entonces el comando completo debería ser:

route add 224.0.0.0 netmask 240.0.0.0 dev eth0

Dependiendo de lo viejo que sea su programa route, puedes tener que poner el flag -net después de add.

Aquí suponemos que eth0 tiene capacidades multicast y que, cuando no se indique lo contrario, queremos que se envíe el tráfico multicast por allí. Si este no es su caso, cambie el parámetro dev al correspondiente.

El sistema de ficheros /proc demuestra ser útil aquí una vez más: puede comprobar /proc/net/igmp para ver los grupos a los que su ordenador está suscrito actualmente.

4. El MBone.

El uso de una nueva tecnología generalmente acarrea tanto ventajas como desventajas. Las ventajas de multicast son (yo creo) claras. La principal desventaja es que cientos de ordenadores y, especialmente, de encaminadores no lo soportan aún. Como consecuencia, los que empezaron a trabajar en multicast, se compraron equipos nuevos, modificaron sus sistemas operativos, y construyeron islas de multicast en sus sitios locales. Entonces descubrieron que era difícil comunicarse con otras personas que hacían cosas similares porque si sólo uno de los encaminadores entre ellos no soportaba multicast no había nada que hacer...

La solución era clara: decidieron construir una red virtual de multicast sobre Internet. Esto es: los sitios con encaminadores multicast entre ellos podían comunicarse directamente. Pero los que se unían a través de routers unicast mandarían el tráfico multicast de sus ``islas'' encapsulado en paquetes unicast a otras islas multicast. Los encaminadores intermedios no tendrían problemas, ya que estarían tratando tráfico unicast. Finalmente, en el otro lado, el tráfico sería desencapsulado, y enviado a la isla en la forma original de multicast. Dos extremos que convierten de multicast a unicast y de nuevo a multicast definen lo que se llama un túnel multicast.

El MBone o Columna Multicast (Multicast Backbone, n. del t.) es esa red virtual multicast basada en islas multicast que se conectan a través de tuneles multicast.

Diariamente tienen lugar diversas actividades en el MBone, pero merece destacarse la abundancia de tele-conferencias con audio y video en tiempo real a lo largo de toda Internet. Por ejemplo, recientemente se retransmitió (en directo) la charla que dió Linus Torvalds al grupo de usuarios de Linux de Silicon Valley.

Para más información del MBone, ver

las respuestas a las preguntas comunes.

5. Aplicaciones Multicast.

La mayor parte de las personas que trabajan con multicast, más pronto o más tarde deciden conectarse al MBone, y habitualmente necesitan un mrouted. También lo necesitará si no tienes un encaminador con capacidades multicast y quiere que el tráfico multicast generado en una de sus subredes sea ``escuchado'' en otra. mrouted resuelve el problema de enviar tráfico multicast a través de encaminadores unicast -encapsula datagramas multicast en unicast (IP dentro de IP)- pero ésta no es la única función que ofrece. Más importante, indica al kernel cómo encaminar (o no encaminar) datagramas multicast basándose en su origen y destino. Así, sin tener un encaminador con capacidades multicast, mrouted puede usarse para decirle qué hacer con los datagramas (observar que digo qué, y no cómo; mrouted dice ``reenvía esto a la red conectada a aquél interfaz'', pero el reenvío real lo hace el kernel). Esta distinción entre el reenvío efectivo y el algoritmo que decide quién y cómo reenviar, es muy útil porque permite escribir código de encaminamiento sólo una vez y ponerlo en el kernel. Los algoritmos y políticas de reenvío se implementan en demonios en el espacio de usuario, así que es muy sencillo cambiar de una política a otra sin tener que recompilar el kernel.

Puede obtener una versión de mrouted portada a GNU/Linux de:

ftp://www.video.ja.net/mice/mrouted/Linux/. Este servidor tiene copias en todo el mundo. Asegúrese de leer el fichero LEAME para elegir el más cercano a vd.

A continuación nos centraremos especialmente en aplicaciones multicast escritas para conectarse al MBone, que han sido portadas a GNU/Linux. La lista está escogida de la página de Michael Esler Linux Multicast Information. Le recomiendo esa página para obtener mucha información y recursos de multicast para Linux.

Audio Conferencia

Video Conferencia

Otras Utilidades

Herramientas de Sesión

He dejado para más tarde las herramientas de sesión ya que creo que se merecen una explicación mejor. Cuando tiene lugar una conferencia, se asignan varios grupos y puertos multicast para cada servicio que quiere para la conferencia (audio, video, pizarras compartidas, etc...). Periódicamente se transmiten por multicast al MBone anuncios de las conferencias que van a tener lugar, así como la información de los grupos, puertos y programas que serán usados (vic, vat, ...). Las herramientas de sesión ``escuchan'' esta información, presentando de forma sencilla las coferencias que van a tener (o están teniendo) lugar, para que pueda decidir cuales le interesan. Asimismo, facilitan la tarea de unirse a una sesión. En lugar de lanzar cada programa que será usado indicándole el grupo/puerto al que se debe unir, generalmente no necesitará más que seleccionar la conferencia deseada y la herramienta de sesión ejecutará los programas apropiados dándoles la información necesaria para unirse a la conferencia. Las herramientas de sesión habitualmente le permiten anunciar sus propias conferencias al MBone.

6. Programación Multicast.

La programación Multicast... o cómo escribir sus propias aplicaciones multicast.

Se necesitan diversas extensiones al API de programación para poder soportar multicast. Todas ellas se manejan a través de dos llamadas al sistema: setsockopt() (usada para pasar información al kernel) y getsockopt() (para obtener información referente al comportamiento multicast). Esto no significa que se añadan dos nuevas llamadas al sistema para soportar multicast. El par setsockopt() / getsockopt() ha estado allí durante años. O por lo menos desde 4.2 BSD. Lo que se añaden son un nuevo conjunto de opciones (opciones de multicast) que se entregan a estas llamadas al sistema, y que el kernel debe entender.

Estos son los prototipos de las funciones setsockopt()/getsockopt() :

int getsockopt(int s, int level, int optname, void* optval, int* optlen);

int setsockopt(int s, int level, int optname, const void* optval, int optlen);

El primer parámetro, s, es el socket al que se aplica la llamada al sistema. Para multicast, debe ser un socket de la familia AF_INET y puede ser del tipo SOCK_DGRAM o SOCK_RAW. Se utiliza comúnmente el socket de tipo SOCK_DGRAM, pero si quiere implementar un demonio de encaminamiento, o modificar uno existente, posiblemente tenga que usar los sockets SOCK_RAW.

El segundo, level (nivel, n. del t.), identifica la capa que debe manejar la opción, mensaje, pregunta, o como quiera llamarlo. Así, SOL_SOCKET es para la capa del socket, IPPROTO_IP para la capa IP, etc... Para programación multicast, level será siempre IPPROTO_IP.

La variable optname identifica la opción que se quiere conocer/modificar. Su valor (el que entrega el programa o devuelve el kernel) es optval. Los nombres posibles para ésta variable, y que aparecen en programación multicast son los siguientes:


                        setsockopt()            getsockopt()
IP_MULTICAST_LOOP           sí                     sí
IP_MULTICAST_TTL            sí                     sí
IP_MULTICAST_IF             sí                     sí
IP_ADD_MEMBERSHIP           sí                      no
IP_DROP_MEMBERSHIP          sí                      no

La siguiente, optlen, indica el tamaño de la estructura de datos a la que apunta optval. Observe que en getsockopt() éste es un resultado devuelto más que un parámetro: el kernel escribe el valor de optname en el buffer apuntado por optval, y nos indica su tamaño a través de optlen.

Tanto setsockopt() como getsockopt() devuelven 0 en caso de éxito o -1 si ha habido un error.

6.1 IP_MULTICAST_LOOP.

Tendrá que decidir, como escritor de aplicaciones, si quiere que los datos que envía sean enviados a su máquina a través del ``loopback'' o no. Si quiere tener más de un proceso de usuario ``escuchando'', se debe activar el loopback. Por otro lado, si está enviando las imágenes que produce su cámara de vídeo, posiblemente no quiera loopback, aunque quiera verse a sí mismo en la pantalla. En este caso, su aplicación probablemente recibe las imágenes de un dispositivo conectado a su ordenador y las envíe a un socket. Ya que la aplicación ya ``tiene'' estos datos, es improbable que quiera recibirlos de nuevo en el socket. El loopback está activado por defecto.

Observe que optval es un puntero. No puede escribir:

setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, 0, 1);
para desactivar el loopback tienes que hacer:
u_char loop;
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
y poner loop a 1 para activar el loopback o a 0 para desactivarlo.

Para saber si en un socket está activada o desactivada la opción de reenvío hacia atrás (looping back, n. del t.) use algo así:

u_char loop;
int size;

getsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &size)

6.2 IP_MULTICAST_TTL.

Si no se le especifica de otra forma, los datagramas multicast se envían con un valor por defecto de 1, para impedir que sean reenviados más allá de la red local. Para cambiar el TTL al valor deseado (de 0 a 255), debe poner ese valor en una variable (aquí la llamo ``ttl'') y escribirla en algún lugar de su programa:

u_char ttl;
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));

El comportamiento con getsockopt() es similar al visto en IP_MULTICAST_LOOP.

6.3 IP_MULTICAST_IF.

Generalmente, el administrador del sistema especifica el interfaz por el que se envian, por defecto, los datagramas multicast. El programador puede modificar esto y elegir un interfaz concreto para un socket determinado con esta opción.

struct in_addr interface_addr;
setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr));

De ahora en adelante, todo el tráfico multicast generado en este socket, será enviado a través del interfaz generado. Para volver al comportamiento original y permitir al kernel elegir el interfaz de salida basado en la configuración del administrador del sistema, es suficiente llamar a setsockopt() con esta misma opción e INADDR_ANY en el campo de interfaz.

Para elegir el interfaz de salida, las siguientes ioctls pueden ser útiles: SIOCGIFADDR (para obtener la dirección de un interfaz), SIOCGIFCONF (para obtener una lista con todos los interfaces) y SIOCGIFFLAGS (para obtener las flags de un interfaz, y, por tanto determinar si el interfaz tiene capacidades multicast -el flag IFF_MULTICAST-).

Si el ordenador tiene más de un interfaz y la opción IP_MULTICAST_IF no está fijada, las transmisiones multicast se envían a través del interfaz por defecto, aunque los intefaces restantes pueden usarse para reenvío multicast si el ordenador está actuando como un encaminador multicast.

6.4 IP_ADD_MEMBERSHIP.

Recordemos que se necesita decir al kernel en qué grupos multicast está uno interesado. Si ningún proceso está interesado en un grupo, los paquetes destinados a éste que llegan al ordenador son descartados. Para informar al kernel de sus intereses y, por tanto, convertirse en miembro de ese grupo, debe primero llenar una estructura ip_mreq que se pasa más tarde al kernel en el campo optval de la llamada al sistema setsockopt().

La estructura ip_mreq (obtenido de /usr/include/linux/in.h) tiene los siguientes miembros:

struct ip_mreq
{
        struct in_addr imr_multiaddr;   /* Direccion multicast IP del grupo */
        struct in_addr imr_interface;   /* Direccion IP local del interfaz */
};

(Observación: la definición ``física'' de la estructura está en el fichero arriba indicado. Sin embargo, no debería incluir <linux/in.h> si quiere que su código sea portable. En su lugar incluya <netinet/in.h> que incluye a su vez a <linux/in.h>).

El primer miembro, imr_multiaddr, mantiene la dirección del grupo al que deseas unirse. Recuerde que las asociaciones también están relacionadas con el interfaz, no sólo con los grupos. Esta es la razón por la que tienes que dar un valor al segundo miembro: imr_interface. De esta forma, si está en un ordenador ``multihomed'', puede unirse al mismo grupo en distintos interfaces. Siempre puede llenar el último miembro con la dirección comodín (INADRR_ANY) y entonces el kernel se encargará de elegir el interfaz apropiado.

Con esta estructura rellena (supongamos que la ha definido como struct ip_mreq mreq;) sólo tienes que llamar a setsockopt() de la siguiente manera:

setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

Observe que puede unirse a distintos grupos en el mismo socket, no sólo a uno. El límite a esto es IP_MAX_MEMBERSHIPS y, en la versión 2.0.33, tiene el valor de 20.

6.5 IP_DROP_MEMBERSHIP.

El proceso es muy similar al de unirse a un grupo:

struct ip_mreq mreq;
setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));

Donde mreq es la misma estructura con los mismos datos usados para unirse al grupo. Si el imr_interface está rellenado con INADRR_ANY, el primer grupo que encaje es abandonado.

Si se ha unido a muchos grupos en el mismo socket, no necesita abandonar todos los grupos para terminar. Cuando cierra un socket, todas las asociaciones de éste son abandonadas por el kernel. Lo mismo ocurre si el proceso que abrió el socket muere.

Finalmente, recuerde que porque un proceso abandone un grupo eso no significa que el ordenador deje de recibir datagramas para ese grupo. Si otro socket se unió al grupo en el mismo interfaz previamente a este IP_DROP_MEMBERSHIP, entonces el ordenador seguirá siendo un miembro del grupo.

Tanto ADD_MEMBERSHIP como DROP_MEMBERSHIP son operaciones no bloqueantes. Deberían retornar inmediatamente indicando éxito o fallo.

7. Por dentro.

El objetivo de esta sección es el de dar alguna información, que no es necesaria para un entendimiento básico de cómo funciona multicast ni para escribir programas multicast, pero que sí es muy interesante, indicando como funcionan los protocolos e implementaciones de multicast que hay por debajo, y que puede ser útil para evitar errores y malentendidos comunes.

7.1 IGMP.

Cuando hablamos de IP_ADD_MEMBERSHIP y IP_DROP_MEMBERSHIP, dijimos que la información ofrecida por estos ``comandos'' era usada por el kernel para elegir qué datagramas aceptar o descartar. Esto es cierto, pero no es toda la verdad. Una simplificación así indicaría que los datagramas para todos los grupos multicast en el mundo serían recibidos por nuestro ordenador, el cual comprobaría en función de lo indicado por los procesos que se estuvieran ejecutando, si pasar el tráfico a estos o descartarlo. Como puedes imaginar, esto sería un completo desaprovechamiento de ancho de banda.

Lo que pasa realmente es que los ordenadores indican a sus encaminadores indicándoles cuales son los grupos multicast en los que están interesados; entonces, todos los encaminadores indican a su vez a los encaminadore de mayor nivel que desean recibir este tráfico, y así continuamente. Los algoritmos empleados para tomar la decision de cuándo pedir el tráfico de un grupo o decir que no se desea más, varían mucho. Hay algo que nunca cambia: cómo se transmite esta información. Se utiliza IGMP para esto. Significa Protocolo de Gestión de Grupos de Internet (Internet Group Management Protocol). Se trata de un nuevo protocolo, parecido en muchos aspectos a ICMP, con un número de protocolo de 2, cuyos mensjaes se envían en datagramas IP, y que debe estar implementado en todos los ordenadores de nivel 2.

Como se ha dicho antes, se utiliza tanto para que los ordenadores den información a los encaminadores, como para que los encaminadores hablen entre sí. De aquí en adelante sólo cubriré las relaciones ordenadores-encamindaores, principalmente porque no he sido capaz de encontrar información describiendo la comunicación encaminador a encaminador aparte del código fuente de mrouted (el RFC 1075 que describe el Protocolo de Encaminamiento Multicast basado en vector de distancias -Distance Vector Multicast Routing Protocol, DVMRP- está ya obsoleto, y mrouted implementa un DVMRP que no ha sido aún documentado).

El RFC 988 especifica IGMP versión 0, que ahora está obsoleto. Casi nadie utiliza la versión 0.

El RFC 1112 describe la versión 1 de IGMP y, aunque se ha actualizado con el RFC 2236 (IGMP versión 2), aún se utiliza ampliamente. El kernel Linux (2.0.33) implementa la versión 1 por completo y parte de los requisitos de la versión 2, pero no todos.

Intentaré ahora dar una descripción informal del protocolo. Puede comprobar el RFC 2236 para una descripción formal, con gran número de diagramas de estado y fronteras de ``timeout''.

Todos los mensajes IGMP tienen la siguiente estructura:


    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Tipo     |Max Tiempo Resp|           Checksum            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         Dirección de Grupo                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

La versión 1 de IGMP (de aquí en adelante IGMPv1) marca el campo ``Max Tiempo Resp'' como ``No usado'', lo pone a cero cuando lo envía, y lo ignora cuando lo recibe. Asimismo parte el campo ``Tipo'' en dos campos de 4 bits: ``Versión'' y ``Tipo''. Dado que IGMPv1 marca una ``pregunta de miembros'' como mensaje 0x11 (versión 1, tipo 1) y IGMPv2 también lo hace como 0x11, los 8 bits tienen la misma interpretación en ambos.

Creo que es más intructivo dar primero la descripción de IGMPv1 y después destacar lo que añade IGMPv2, ya que son fundamentalmente eso, cosas adicionales.

Para lo siguiente es importante recordar que los encaminadores de multicast reciben todos los datagramas de multicast IP.

IGMP versión 1.

Los encaminadores envían periódicamente Preguntas IGMP de Pertenencia de Ordenadores (IGMP Ordenador Membership Queries, n. del t.) al grupo todos-los-ordenadores (224.0.0.1) con un TTL de 1 (cada minuto más o menos). Todos los ordenadores con capacidades multicast lo ``escuchan'', pero no responden inmediatamente para evitar una tormenta de respuestas. En lugar de esto, ponen un temporizador con un valor aleatorio para cada grupo al que pertenecen en el interfaz donde recibieron la pregunta.

Más pronto o más tarde, el temporizador llega a cero en uno de los ordenadores, y éste envia un paquete IGMP Respuesta de Pertenencia (Ordenador Membership Report, n. del t.) (también con TTL 1) a la dirección multicast del grupo sobre el que responde. Ya que se envía al grupo, todos los ordenadores que se unieron a ese grupo (y que están esperando que su propio temporizador se ponga a cero) también lo reciben. Entonces, paran sus temporizadores y no generan otra respuesta. Solo una es generada (por el ordenador que obtiene el menor valor aleatorio), y esto es suficiente para el encaminador. Sólo necesita saber que hay miembros para este grupo en la subred, no quiénes ni cuántos.

Cuando no se recibe ninguna respuesta para un grupo determinado después de algún número de preguntas, el encaminador asume que no queda ningún miembro, y por tanto no tiene que encaminar el tráfico para ese grupo en esa subred. Obsérvese que en IGMPv1 no hay ningún mensaje de ``Abandono de Grupo''.

Cuando un ordenador se une a un nuevo grupo, el kernel envía un aviso para ese grupo, de forma que los procesos respectivos no tienen que esperar uno o dos minutos hasta que se reciba una pregunta con respecto a ese grupo. Este paquete IGMP se genera por el kernel como respuesta al comando IP_ADD_MEMBERSHIP,visto en la sección IP_ADD_MEMBERSHIP.

Notese el énfasis en el adjetivo ``nuevo'': si un proceso solicita un IP_ADD_MEMBERSHIP para un grupo del cual el ordenadores ya es miembro, no se envía ningún paquete IGMP dado que ya se debe estar recibiendo tráfico para este grupo; en su lugar, se incrementa un contador de miembros para ese grupo. IP_DROP_MEMBERSHIP no genera ningún datagrama en IGMPv1.

Las preguntas de pertenencia de ordenadores se identifican con el Tipo 0x11, y las respuestas con el Tipo 0x12.

No se envían nunca respuestas para el grupo todo-ordenadores. La pertenencia a este grupo es permanente.

IGMP versión 2.

Una adición importante a lo anterior es la inclusión de un mensaje Abandonar Grupo (Tipo 0x17). La razón para esta inclusión es la de reducir el gasto de ancho de banda entre el tiempo que el último ordenador de una subred abandona un grupo y el tiempo que el encaminador abandona todas sus preguntas y decide que no hay más miembros presentes para ese grupo. Los mensajes de Abandonar Grupo deben dirigirse al grupo todo-encaminadores (224.0.0.2) en lugar de al grupo que se deja, dado que esta información no tiene ninguna utilidad a otro miembros (las versiones del kernel hasta la 2.0.33 los envían a este grupo; aunque no hace ningún daño a los otros ordenadores, es un gasto de tiempo ya que tienen que procesarlo, y sin embargo no obtienen información útil). Hay ciertos detalles sutiles respecto a cuándo o cuándo no enviar estos mensajes; si le interesa, consulte el RFC.

Cuando un encaminador IGMPv2 recibe un mensaje de abandono para un grupo, envía Preguntas específicas de Grupo (Group-Specific Queries , n. del t.) al grupo que se deja. Esto es otra novedad. IGMPv1 no tiene ningún tipo de preguntas específicas para grupos. Todas las preguntas se envían al grupo todo-ordenadores. El Tipo en la cabecera IGMP no varía (0x11 como antes), pero la ``Dirección de grupo'' se rellena con la del grupo multicast que se abandona.

El campo ``Max Tiempo Resp'', que se ponía a 0 en transmisión y que se ignoraba en recepción en IGMPv1, es significativo sólo en mensajes del tipo ``Pregunta de pertenencia''. Se da el máximo tiempo permitido antes de enviar una respuesta, en unidades de 1/10 segundo. Se utiliza como mecanismo de ajuste.

IGMPv2 añade otro tipo de mensaje: 0x16. Es una ``Respuesta de Pertenencia Versión 2'' enviada por los ordenadores IGMPv2 si detectan un encaminador IGMPv2 (un ordenador IGMPv2 sabe que hay un encaminador IGMPv1 cuando recibe una pregunta con el campo ``Max Respuesta'' a 0).

Cuando más de un encaminador desea actuar como entrevistador, IGMPv2 provee un mecanismo para evitar ``discusiones'': el encaminador con la dirección IP más baja se elige como entrevistador. Los otro encaminadores activan un temporizador. Si el encaminador con la dirección IP más baja se cae o se apaga, la decisión de quién va a ser el entrevistador se vuelve a tomar cuando los temporizadores expiran.

7.2 El rincón del Kernel.

Esta subsección da algunos puntos de entrada para estudiar la implementación de multicast en el kernel de Linux. No explica esta implementación. Sólo dice dónde encontrar las cosas.

El estudio fue realizado sobre la versión 2.0.32, así que puede haber cambiado para cuando leas esto (el código fuente de red parece haber cambiado MUCHO en las versiones 2.1.x, por ejemplo).

El código fuente multicast en el kernel de Linux siempre está rodeado por pares #ifdef CONFIG_IP_MULTICAST / #endif , para que pueda incluir/excluirlo de su kernel según sus intereses (esta inclusión/exclusión se realiza en el momento de compilar, como probablemente sepa si lee esta sección...). Los #ifdefs son manejados por el preprocesador. La decisión se realiza basándose en lo que elegió cuando hizo, bien make config, make menuconfig o make xconfig).

Quizás quiera las capacidades multicast, pero si su ordenador Linux no va a trabajar como encaminador posiblemente no quiera las funciones incluidas en su nuevo kernel. Por esto el código de encaminamiento multicast esta rodeado por pares #ifdef CONFIG_IP_MROUTE / #endif.

El código fuente del kernel generalmente reside en /usr/src/linux. Sin embargo, el lugar puede variar, así que para mayor exactitud y brevedad, me referiré al directorio raíz del código del kernel como simplemente LINUX. Así, algo como LINUX/net/ipv4/udp.c es equivalente a /usr/src/linux/net/ipv4/udp.c si desempaquetó el código fuente en el directorio /usr/src/linux.

Todas las comunicaciones multicast entre los programas de usuario mostrados en la sección dedica a la programación multicast y el kernel se realizaron a través de las llamadas al sistema setsockopt()/getsockopt(). Ambas son implementadas a través de funciones que hacen algunas pruebas para verificar los parámetros que se le entregan y las cuales, a su vez, llaman a otras funciones que hacen comprobaciones adicionales, demultiplexan la llamada basándose en el parámetro nivel a otra llamada al sistema, y después llaman a otra función que ... (si le interesan todos estos saltos, puede seguirlos en LINUX/net/socket.c -funciones sys_socketcall() y sys_setsockopt() -, LINUX/net/ipv4/af_inet.c -función inet_setsockopt()- y LINUX/net/ipv4/ip_sockglue.c -función ip_setsockopt() ).

El fichero que nos interesa es LINUX/net/ipv4/ip_sockglue.c, donde encontraremos ip_setsockopt() y ip_getsockopt() que son básicamente un switch (después de alguna comprobación de errores) que verifican todos los posibles valores para optname. Aquí se manejan las opciones unicast, y también las multicast: IP_MULTICAST_TTL, IP_MULTICAST_LOOP, IP_MULTICAST_IF, IP_ADD_MEMBERSHIP y IP_DROP_MEMBERSHIP. Antes del switch, se realiza una comprobación para determinar si las opciones son específicas de un encaminador de multicast, y, de ser así, son reenviada a las funciones ip_mroute_setsockopt() y ip_mroute_getsockopt() (fichero LINUX/net/ipv4/ipmr.c).

En LINUX/net/ipv4/af_inet.c podemos ver los valores por defecto de los que hablamos en secciones anteriores (loopback habilitado, TTL=1) dadas cuando el socket se crea (obtenido de la función inet_create() en este fichero):


 
#ifdef CONFIG_IP_MULTICAST
        sk->ip_mc_loop=1;
        sk->ip_mc_ttl=1;
        *sk->ip_mc_name=0;
        sk->ip_mc_list=NULL;
#endif

Asimismo la aseveración de ``cerrar el socket hace que el kernel abandone todas las asociaciones que este socket tenía'' se corroboran por:


#ifdef CONFIG_IP_MULTICAST
                /* Las aplicaciones se olvidan de abandonar los grupos
                antes de salir */
                ip_mc_drop_socket(sk);
#endif

Obtenido de inet_release(), en el mismo fichero que el anterior.

Las operaciones independientes de dispositivo para la capa de Enlace se guardan en LINUX/net/core/dev_mcast.c.

Dos funciones importantes siguen faltando: el proceso de la entrada y salida de datagramas multicast. Como los demás datagramas, los datagramas entrantes son pasados de los controladores de dispositivos a la función ip_rcv() (LINUX/net/ipv4/ip_input.c). En esta función es donde se aplica el filtrado perfecto a los paquetes multicast que han pasado la capa de dispositivo (observe que las capas inferiores sólo hacen un filtro de mayor esfuerzo y es IP quien sabe al 100% si estamos interesados o no en un grupo multicast). Si el ordenador está actuando como encaminador multicast, esta función decide también si los datagramas deberían ser reenviados y llama a la función ipmr_forward() adecuadamente. (ipmr_forward() está implementada en LINUX/net/ipv4/ipmr.c).

El código que se encarga de los paquetes salientes se gurada en LINUX/net/ipv4/ip_output.c. Aquí es donde la opción IP_MULTICAST_LOOP entra en juego, ya que se comprueba para ver si los paquetes deben ser enviados a uno mismo o no (función ip_queue_xmit()). Asimismo el TTL de los paquetes salientes se elige en base a si es multicast o unicast. En este último caso, se utiliza el argumento dado a la opción IP_MULTICAST_TTL (función (ip_build_xmit()).

Mientras trabajabamos con mrouted (un programa que le da al kernel información de como encaminar los datagramas multicast), detectamos que todos los paquetes multicast originados en la red local eran encaminados adecuandamente... ¡¡excepto aquellos del propio ordenador GNU/Linux que estaba actuando como encaminador multicast!! ip_input.c estaba funcionando bien, pero parecía que ip_output.c no. Leyendo el código fuente de las funciones de salida, encontramos que los datagramas de salida no eran pasados a impr_forward(), la función que debía decidir si eran encaminados o no. Los paquetes salían a la red local pero, como generalmente las tarjetas de red son incapaces de leer sus propias transmisiones, estos datagramas nunca se encaminaban. Añadimos el código necesario a la función ip_build_xmit() y todo funcionó bien (tener las fuentes de tu kernel no es un lujo ni una pedantería; es una necesidad!)

Se ha mencionado varias veces ipmr_forward(). Se trata de una función importante porque resuelve un malentendido que parece haberse extendido mucho. Cuando se encamina el tráfico multicast, no es mrouted quien hace copias y las manda a los receptores adecuados. mrouted recibe todo el tráfico multicast y, en función de esta información, realiza las tablas de encaminamiento multicast y le dice al kernel cómo encaminar: ``datagramas para este grupo viniendo de este interfaz deben ser reenviados por estos interfaces''. Esta información es enviada al kernel a través de llamadas a setsockopt() en un socket ``raw'' abierto por el demonio mrouted (el protocolo especificado cuando se creó el socket debe ser IPPROTO_IGMP). Estas opciones son manejadas por la función ip_mroute_setsockopt() de LINUX/net/ipv4/ipmr.c. La primera opción (sería más apropiado llamarlos comandos en vez de opciones) dada en este socket debe ser MRT_INIT. Todos los demas comandos son ignorados (devolviendo -EACCES) si no se envía primero MRT_INIT. Sólo puede estar ejecutándose una instancia de mrouted en el mismo ordenador. Para conseguir esto, cuando se envía el primer MRT_INIT, una importante variable, struct sock* mroute_socket, apunta al socket donde se recibió MRT_INIT. Si mroute_socket no es NULL cuando atiende un MRT_INIT significa que hay otro mrouted funcionando y se devuelve un -EADRRINUSE. Todos los comandos restantes (MRT_DONE, MRT_ADD_VIF, MRT_DEL_VIF, MRT_ADD_MFC, MRT_DEL_MFC y MRT_ASSERT) devuelven -EACCES si vienen de un socket distinto al mroute_socket.

Dado que los datagramas multicast encaminados pueden ser recibidos/enviados tanto a través de interfaces físicos como de túneles, se definió una abstracción común para ambos: IFV's, InterFaces Virtuales (Virtual InterFaces, n. del t.). mrouted pasa estructuras ifv al kernel, indicando interfaces físicos o túneles que deben ser añadidos a sus tablas de encaminamiento, y entradas en el reenvío multicast diciendo dónde deben ser reenviados.

Los IVFs son añadidos con MRT_ADD_VIF y borrados con MRT_DEL_VIF. Ambos pasan un struct vifctl al kernel (definido en /usr/include/linux/mroute.h) con la siguiente información:


struct vifctl {
        vifi_t  vifc_vifi;              /* Indice de IFV */
        unsigned char vifc_flags;       /* VIFF_ flags */
        unsigned char vifc_threshold;   /* Limite ttl  */
        unsigned int vifc_rate_limit;   /* Rate limiter values (NI) */
        struct in_addr vifc_lcl_addr;   /* Nuestra direccion */
        struct in_addr vifc_rmt_addr;   /* direccion del tunel IPIP  */
};

Con esta información se construye una estructura vif_device:


struct vif_device
{
        struct device   *dev;                   /* Dispositivo que estamos usando  */
        struct route    *rt_cache;              /* Tunnel route cache */
        unsigned long   bytes_in,bytes_out;
        unsigned long   pkt_in,pkt_out;         /* Estadisticas  */
        unsigned long   rate_limit;             /* Traffic shaping (NI) */
        unsigned char   threshold;              /* Barrera TTL  */
        unsigned short  flags;                  /* Flags de Control */
        unsigned long   local,remote;           /* Direcciones(remotas para tuneles)*/
};

Observe el campo dev en la estructura. La estructura device está definida en el fichero /usr/include/linux/netdevice.h. Es una estructura grande pero el campo que nos interesa es:


  struct ip_mc_list*    ip_mc_list;  /* cadena de filtro IP multicast */

La estructura ip_mc_list - definida en /usr/include/linux/igmp.h - es como sigue:


struct ip_mc_list
{
        struct device *interface;
        unsigned long multiaddr;
        struct ip_mc_list *next;
        struct timer_list timer;
        short tm_running;
        short reporter;
        int users;
};

Por tanto, el miembro ip_mc_list de la estructura dev es un puntero a una lista enlazada de estructuras ip_mc_list, y cada una contiene una entrada para cada grupo multicast del cual es miembro el interfaz de red. Una vez más vemos aquí que los miembros de un grupo son los interfaces. LINUX/net/ipv4/ip_input.c recorre esta lista enlazada para decidir si el datagrama recibido está destinados a algún grupo al que pertenezca el interfaz que recibió el datagrama.


#ifdef CONFIG_IP_MULTICAST
                if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST 
                   && iph->daddr!=IGMP_ALL_ORDENADORES 
                   && !(dev->flags&IFF_LOOPBACK))
                {
                        /*
                         *      Comprueba que es para uno de nuestros grupos
                         */
                        struct ip_mc_list *ip_mc=dev->ip_mc_list;
                        do
                        {
                                if(ip_mc==NULL)
                                {
                                        kfree_skb(skb, FREE_WRITE);
                                        return 0;
                                }
                                if(ip_mc->multiaddr==iph->daddr)
                                        break;
                                ip_mc=ip_mc->next;
                        }
                        while(1);
                }
#endif

El campo users en la estructura ip_mc_list se utiliza para implementar lo indicado en la sección IGMP versión 1: si un proceso se une a un grupo y el interfaz ya es miembro de este grupo (esto es, otro proceso se unió a este mismo grupo en el mismo intefaz anteriormente) sólo se incrementa el contador (users). No se envían mensajes IGMP, como puede ver en el siguiente código (sacado de ip_mc_inc_group(), llamado por ip_mc_join_group(), ambos están en LINUX/net/ipv4/igmp.c):


        for(i=dev->ip_mc_list;i!=NULL;i=i->next)
        {
                if(i->multiaddr==addr)
                {
                        i->users++;
                        return;
                }
        }

Cuando se abandona un grupo, el contador se decrementa y se hacen operaciones adicionales sólo si la cuenta ha llegado a 0 (ip_mc_dec_group()).

MRT_ADD_MFC y MRT_DEL_MFC añaden o borran entradas de encaminamineto en las tablas de encaminamiento multicast. Ambas pasan una estructura struct mfcctl al kernel (también definida en /usr/include/linux/mroute.h) con esta información:


struct mfcctl
{
        struct in_addr mfcc_origin;             /* Origen de mcast      */
        struct in_addr mfcc_mcastgrp;           /* Grupo en cuestión */
        vifi_t  mfcc_parent;                    /* Donde llegó */
        unsigned char mfcc_ttls(MAXVIFS);       /* A donde se dirige    */
};

Con esta información en la mano, ipmr_forward() ``recorre'' los IVFs, y si se encuentra uno que case él duplica el datagrama y llama a ipmr_queue_xmit() el cual, a su vez, utiliza el dispositivo de salida especificado por la tabla de encaminamiento y el destino apropiado si el paquete debe ser enviado por un túnel (esto es, la dirección de destion unicast del otro extremo del túnel).

La función ip_rt_event() (no directamente relacionada con el envío, pero que también está en ip_output.c) recibe los eventos relacionados con un dispositvo de red, como que un dispositivo se está activando. Esta función asegura que el dispositivo se une al grupo multicast TODO-ORDENADORES.

Las funciones IGMP se implementan en LINUX/net/ipv4/igmp.c. Hay importante información para estas funciones en /usr/include/linux/igmp.h y /usr/include/linux/mroute.h.

La entrada IGMP en el directorio /proc/net se crea con ip_init() en LINUX/net/ipv4/ip_output.c.

8. Políticas de encaminamiento y técnicas de reenvío.

Un algoritmo trivial para hacer disponible el tráfico multicast mundial en todos los sitios sería enviarlo... a todo el mundo, sin tener en cuenta si se quiere o no. Dado que este no parece muy optimizado, se han implementado varios algoritmos de encaminamiento y reenvío.

DVMRP (Distance Vector Multicast Routing Protocol) (Protocolo de Encaminamiento Multicast de Vector de Distancias, n. del t.), es, quizás, el que más utilizan los encaminadores de multicast hoy en día. Se trata de un protocolo de encaminamiento de modo denso, esto es, funciona bien en entornos con alto ancho de banda y miembros distribuidos con alta densidad. Sin embargo, en escenarios de modo disperso, sufre problemas de escalabilidad.

Junto con DVMRP podemos encontrar otro procolo de encaminamiento de modo denso: MOSPF (Multicast Extensions to OSPF -Open Shortest Path First-) (Extensiones Multicast a OSPF- n. del t.), y PIM-DM (Protocol-Independent Multicast Dense Mode) (Multicast Independiente de Protocolo Modo Denso - n. del t.).

Para hacer encaminamiento en entornos disperso, tenemos PIM-SM (Protocol-Independent Multicast Sparse Mode) (Multicast Independiente de Procolo modo Disperso - n. del t.) y CBT (Core Based Trees) (Arboles basados en Núcleo - n. del t.).

El RFC 1583 explica OSPF versión 2, y MOSPF se explica en el RFC 1584. Las especificaciones de PIM-SM y CBT se pueden encontrar en el RF 2117 y 2201, respectivamente.

Todos estos protocolos de encaminamiento hacen uso de alguún tipo de reenvío multicast, como inundación, Reverse Path Broadcasting (RPB), Truncated Reverse Path Broadcasting (TRPB), Reverse Path Multicasting (RPM) o Shared Trees.

Sería muy largo explicar todos aquí, y dado que hay descripciones cortas disponibles, recomendaré solamente leer el texto draft-ietf-mboned-in.txt . Puedes encontrarlo en los mismmos lugares donde estén los RFCs, y explica en algún detalle todas las técnicas y políticas comentadas.

9. Protocolos de Transporte Multicast.

Hasta ahora hemos estado hablando sobre transimisiones multicast usando UDP. Esta es la práctica habitual, ya que es imposible hacerlo usando TCP. Sin embargo, desde hace un par de años se están realizando intensas investigaciones para diseñar algunos protocolos de transporte multicast nuevos.

Algunos de estos protocolos han sido implementados y se ya están probando. Una buena lección que se puede extraer de ellos es que parece que no hay ningún protocolo de transporte multicast que sea suficientemente bueno y general para todas las aplicaciones multicast.

Si los protocolos de transporte son complejos y difíciles de sintonizar, imagínese teniendo que ajustar los retardos (en conferencias multimedia), perdidas de datos, ordenaciones, retransmisiones, control de flujo y congestión, gestión de grupos, etc.. cuando el receptor no es uno, sino quizás cientos o miles de ordenadores dispersos. Aquí es importante la escalabilidad, y se están implementando nuevas técnicas, como no transmitir asentimientos para cada paquete recibido y en su lugar, enviar asentimientos negativos (NACKs) para los datos que no se han recibido. El RFC 1458 detalla los requisitos propuestos para los protocolos multicast.

Dar descripciones de esos protocolos multicast está fuera del ámbito de esta sección. En su lugar daré los nombres de algunos de ellos y apuntaré a algunas fuentes de información: Real-Time Transport Protocol (RTP) se ocupa de conferencias multimedia entre varias personas, Scalable Reliable Multicast (SRM) se utiliza por el wb (la herramienta de Pizarra distribuida, ver la sección Multicast applications), Uniform Reliable Group Communication Protocol (URGC) fomenta transacciones fiables y ordenadas basadas en un protocolo centralizado, Muse fue desarollado como un protocolo específico de aplicación: el de transmitir los artículos de noticias via multicast sobre el MBone, el Multicast File Transfer Protocol (MFTP) es suficientemente descriptivo por sí mismo y la gente se ``une'' a transmisiones de ficheros (previamente anunciadas) de igual manera que se unirían a una conferencia, Log-Based Receiver-reliable Multicast (LBRM) es un protocolo curioso ya que guarda un registro de todos los paquetes enviados en un ordenador que indica al emisor cuándo tiene que retransmitir los datos o puede descartarlos ya que los receptores los han recibido. Un protocolo con un nombre divertido -especialmenta para un protocolo multicast- es STORM (STructure-Oriented Resilient Multicast) (``storm'' en inglés significa ``tormenta''. De ahí la gracia del nombre para un protocolo multicast, ya que con el apelativo de ``tormentas multicast'' se suele bautizar el efecto de cientos de ordenadores transmitiendo respuestas a la vez e inundando la red; un efecto que todo protocolo multicast debe encontrar la forma de evitar. N. del A. para la T.) Se pueden encontrar muchos protocolos multicast buscando la Web, junto con trabajos interesantes proponiendo nuevas actividades para multicast (por ejemplo, distribución de páginas www usando multicast).

Una buena página que da comparativas entre protocolos fiables multicast es: http://www.tascnets.com/mist/doc/mcpCompare.html.

Un servidor muy bueno y actualizado, con un buen número de enlaces interesantes (borradores de Internet, RFC's, trabajos, enlaces a otros servidores) es:

http://research.ivv.nasa.gov/RMP/links.html.

http://hill.lut.ac.uk/DS-Archive/MTP.html también es una buena fuente de información sobre el tema.

El articulo de Katia Obraczka ``Multicast Transport Protocols: A Survey and Taxonomy'' da una breve descripción para cada protocolo e intenta clasificarlos de acuerdo con distintas características. Puedes leerlo en la revista IEEE Communications, Enero 1998, vol. 36, No. 1.

10. Referencias.

10.1 RFCs.

10.2 Borradores de Internet.

10.3 Páginas de Web.

10.4 Libros.

11. Copyright and Disclaimer.

Copyright 1998 Juan-Mariano de Goyeneche.

This HOWTO is free documentation; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This document is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the GNU General Public License for more details.

You can obtain a copy of the GNU General Public License by writing to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

If you publish this document on a CD-ROM or in hardcopy form, a complimentary copy would be appreciated; mail me for my postal address. Also consider making a donation to the Linux Documentation Project or the Free Software Foundation to help support free documentation for GNU/Linux. Contact the Linux HOWTO coordinator, Greg Hankins gregh@sunsite.unc.edu, for more information. (n. del T., del original)

Esta traducción es Copyright 1998, Javier Fernández-Sanguino Peña.

Las condiciones de distribución de este documento traducido son las mismas que las establecidas por el autor original (ver arriba).

12. Agradecimientos.

Ésta es la mejor oportunidad que he tenido nunca para dar las gracias a tanta gente a la que me siento agradecido. Así que, me temo que esta va a ser una sección larga... Es, en cualquier caso, la más importante de este texto (para mí, por lo menos...).

En primer lugar quiero agradecer a Elena Apolinario Fernández de Sousa (sí: Elena es su nombre, el RESTO es EL apellido ;-) ). Intenté reflejar en este COMO todo el conocimiento que recogí mientras trabajaba con ella conectando nuestro Departamento al MBone a través de túneles multicast, y depurando los problemas de software CSCW generado localmente. Ella fue de ayuda incalculable para encontrar y corregir problemas en la red, descubriendo y arreglando errores en el kernel que nos tuvieron perplejos durante días, .... y manteniendo vivo el sentido del humor cuando los problemas no dejaban de aparecer, pero las soluciones se resistían. Ella también leyó y corregió los borradores para este documento y ofreció ideas y sugerencias importantes. Si este COMO está aquí y es útil a alguien, será, en muchos aspectos, gracias a ella. ¡Gracias, Elena!

Hay algo que he tenido la suerte de encontrar en toda mi (todavía no muy larga) vida, y que, a pesar de ser repetivo, nunca ha dejado de sorprenderme. Estoy hablando de esas personas que altruístamente dedican parte de su tiempo y/o recursos a ayudar a otras personas a aprender nuevas cosas y que, lo que es mejor, disfrutan haciéndolo. No me refiero sólamente (aunque también) a explicar las cosas que ya saben, sino también a prestar sus libros, dar acceso a sus fuentes e incluso facilitarte el camino para aprender todas las cosas que ellos ya saben; a veces, incluso más de lo que ellos saben... Conozco a bastantes personas asi', y me gustaría agradecerles su ayuda.

Pablo Basterrechea fue mi primera ``fuente de documentación'' mientras estaba en mi etapa pre-Internet. Aprendí ensamblador y programación estructurada avanzada directamente con sus libros (bueno, lo último también con sus programas...). Gracias por todo, Pablo.

Mi primer año en la Universidad Pepe Mañas ocupó el lugar de esta ``fuente de documentación''. Él era entonces profesor de Programación, y pronto me convertí en adicto a su biblioteca. Me prestó sus libros montones de veces sin siquiera exigir un signo que le asegurara que iba a devolvérselos, ¡ni siquiera mi nombre! Mi primer acercamiento al protocolo TCP/IP fué también por su mano: me prestó el libro de Comer ``Internetworking with TCP/IP, Volume 1'' para todo el verano. Ni siquiera sabía mi nombre entonces pero me prestó el libro...

Si hay dos personas a las que debo agradecer mucho, son (en orden alfabético ;-) ), José Manuel y Paco Moya. A nadie he preguntado más cosas y ma's veces (C, C++, Linux, seguridad, Web, OSs, señales & sistemas, electrónica, ... ¡cualquier cosa !) y, a pesar de mi persistencia, siempre conseguí respuestas amistosas y ayuda. Si ahora uso GNU/Linux es, de nuevo, gracias a ellos. Me siento afortunado con amigos como ellos. GRACIAS.

Iñigo Mascaraque también me ayudó (de él conseguí mi primer libro de Administración de Sistemas) y me dió ánimos en mis principios, pero nunca dejó de recordarme que, aunque esto fuera un mundo fascinante y una parte importante de mi carrera, no debía olvidar las otras partes, menos interesantes. (No lo olvido, ¡I$!).

Y ya que estoy en ello, me gustaría dar las gracias a mis padres también, que siempre han procurado que tuviera a mi alcance las mejores oportunidades. Muchas gracias por todo.

También me siento en deuda con Joaquín Seoane, el primero que confió lo suficiente en mí para darme una password de root cuando estaba aprendiendo administración por mi cuenta, y Santiago Pavón, el que me dió mi primera oportunidad en el DIT.

Los libros de W. Richard Stevens han sido una verdadera revelación para mí (es una lástima que sean tan caros...). Si alguna vez lee esto, me gustaría darle las gracias por ellos, y animarle a que siga escribiendo. Cualquier cosa que salga de su pluma será -indudablemente- buena para todos nosotros.

Finalmente me gustaría agradecer a Richard Stallman, Linus Torvalds, Alan Cox y todos los que han contribuido al kernel Linux y al software libre en general, por darnos un sistema operativo tan bueno.

Seguramente me estoy olvidando a alguien... lo siento. Estoy convencido de que los que esté olvidando saben que también les estoy agradecido, asi' que si me dicen que no les he incluido, todo el mundo sabrá que realmente lo estoy... :-)

NOTA A LA TRADUCCIóN AL ESPAñOL: quiero también expresar mi profundo agradecimiento a Javier Fernández-Sanguino Peña por esta traducción. Realmente sentía el que este COMO estuviera en inglés y no es español, que es mi lengua materna. Pero problemas de tiempo y trabajo me impedían hacerlo. Javi se ofreció enseguida a llevarla a cabo, y en un tiempo record tuvo terminado este estupendo trabajo. En un nuevo rasgo de amabilidad quiso que yo le diera el visto bueno a la traducción antes de mandarla, para estar seguro de que estaba de acuerdo con todo lo que él había hecho y que su traducción transmitía fielmente lo que yo había querido comunicar. De nuevo mis problemas de tiempo me han hecho retrasar esa revisión hasta ahora, por lo que creo que debo pedir disculpas. En primer lugar a él, que durante meses (sí, meses) vió cómo su intenso trabajo no veía la luz porque yo no encontraba un momento para revisarlo. Y por supuesto también a todos los potenciales lectores de habla hispana que podían haberse beneficiado del extraordinario trabajo de Javi si yo me hubiera dado más prisa.

A todos ellos, lo siento. Espero que sepan entenderlo...