6. Programmation multicast

Programmation multicast… ou comment écrire vos propres applications multicast.

Diverses extensions de l'API de programmation sont nécessaires pour utiliser le multicast. Ces extensions sont utilisables par le biais de deux appels systèmes : setsockopt() (utilisé pour envoyer des informations au noyau) et getsockopt() (utilisé pour recevoir des informations du voisinage multicast). Cela ne signifie pas que ces 2 nouveaux appels systèmes ont été ajoutés pour le fonctionnement du multicast. La paire setsockopt() / getsockopt() est là depuis des années. Depuis la BSD 4.2 au moins. L'ajout consiste en un nouveau jeu d'options (options multicast) qui est passé à ces appels systèmes et que le noyau doit comprendre.

Les deux lignes suivantes donnent le prototypage des fonctions 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);

Le premier paramètre, s, est la prise réseau (socket) à laquelle l'appel système s'applique. Pour le multicast, la prise doit être de la famille AF_INET et peut être de type SOCK_DGRAM ou SOCK_RAW. Le plus courant est l'utilisation d'une prise SOCK_DGRAM, mais si votre but est d'écrire un démon de routage ou d'en modifier un, vous aurez plutôt besoin d'une prise de type SOCK_RAW.

Le deuxième paramètre, level, identifie la couche qui capturera l'option, le message, ou la requète, ou tout ce que vous désirez appeler. De fait, SOL_SOCKET est pour la couche de la prise réseau, IPPROTO_IP est pour la couche IP, etc. Pour la programmation multicast, la valeur est toujours IPPROTO_IP.

optname identifie l'option que nous passons ou demandons. Sa valeur, soit fournie par le programme, soit retournée par le noyau, est optval. Les valeurs pour optname qui peuvent être invoquées pour la programmation multicast sont les suivantes :

  setsockopt() getsockopt()
IP_MULTICAST_LOOP oui oui
IP_MULTICAST_TTL oui oui
IP_MULTICAST_IF oui oui
IP_ADD_MEMBERSHIP oui non
IP_DROP_MEMBERSHIP oui non

optlen transporte la taille de la structure de données à laquelle optval fait référence. Notez que pour getsockopt(), c'est un résultat et non une donnée : le noyau écrit la valeur de optname dans la zone tampon pointée par optval et nous informe de la longueur de la valeur par optlen.

Aussi bien setsockopt() que getsockopt() retournent 0 en cas de succès et -1 en cas d'erreur.

Traditionnellement, l'administrateur système indique l'interface multicast par défaut sur laquelle les datagrammes sont envoyés. Le programmeur peut surcharger cela et choisir ainsi à l'aide de cette option une interface de sortie concrête pour une prise réseau donnée.

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

Ainsi, tout le trafic multicast généré par cette prise réseau sera envoyé sur l'interface choisie. Pour revenir au comportement par défaut et laisser le noyau choisir l'interface de sortie (choix basé sur la configuration de l'administrateur système), il suffit d'appeler setsockopt() avec la même option et INADDR_ANY comme valeur pour l'interface.

Pour connaitre ou sélectionner une interface de sortie, les ioctls suivants peuvent être utililisés : SIOCGIFADDR (pour obtenir les adresses des interfaces), SIOCGIFCONF (pour obtenir la liste de toutes les interfaces) et SIOCGIFFLAGS (pour obtenir les fonctionnalités d'une interface et, de fait, permet de déterminer si l'interface supporte le multicast ou non. Fonction indiquée par l'option IFF_MULTICAST).

Si un hôte a plus d'une interface et que l'option IP_MULTICAST_IF n'est pas mentionnée, alors les transmissions multicast sont émises sur l'interface par défaut, bien que les interfaces restantes puissent être utilisées pour des retransmissions multicast si l'hôte joue le rôle de routeur multicast.

Rappelez vous que vous devez informer le noyau des groupes multicast qui vous intéressent. Si aucun processus n'est intéressé par un groupe donné, les paquets provenant de ce groupe et qui arrivent sur notre hôte sont rejetés. Pour informer le noyau, des groupes qui vous intéressent, et de fait, pour devenir membre de ce groupe, vous devez d'abord remplir une structure ip_mreq qui sera passée plus tard au noyau dans le champ optval avec l'appel système setsockopt().

La structure ip_mreq (provenant de /usr/include/linux/in.h) a les membres suivants :

struct ip_mreq
{
  struct in_addr imr_multiaddr;   /* adresse IP du groupe multicast */
  struct in_addr imr_interface;   /* adresse IP de l'interface locale */
};

Note : la définition « physique » de la structure est contenue dans le fichier spécifié ci-dessus. Vous ne devez en aucun cas inclure <linux/in.h> si vous souhaitez que votre code soit portable. En lieu et place, incluez <netinet/in.h>, qui inclut à son tour <linux/in.h>.

Le premier membre, imr_multiaddr, contient l'adresse du groupe que vous désirez joindre. Souvenez-vous que l'appartenance à un groupe se fait aussi par le biais d'une interface, pas uniquement sur l'adresse du groupe. C'est la raison pour laquelle vous devez fournir une valeur au second membre : imr_interface. De cette façon, si vous êtes sur un hôte ayant plusieurs interfaces multicast, vous pouvez joindre un même groupe par le biais de différentes interfaces. Si vous ne désirez pas indiquer d'interface, vous pouvez toujours remplir ce dernier membre avec un joker (INADDR_ANY), alors le noyau choisira de lui même l'interface.

Une fois la structure remplie (définie en tant que struct ip_mreq mreq;) vous avez juste à appeler setsockopt() de cette manière :

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

Notez que vous pouvez joindre plusieurs groupes à travers la même prise réseau. Cette limite supérieure est fixée par IP_MAX_MEMBERSHIPS et, a pour la version 2.0.33 du noyau Linux, la valeur de 20.