Sophie

Sophie

distrib > * > 2010.0 > * > by-pkgid > a412ceb851151854794ced2a242192bb > files > 2412

howto-html-fr-20080722-1mdv2010.0.noarch.rpm

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>7. Fonctionnement interne</title>
<link rel="stylesheet" href="style.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.64.1">
<link rel="home" href="index.html" title="Guide pratique du multicast sur les réseaux TCP/IP">
<link rel="up" href="index.html" title="Guide pratique du multicast sur les réseaux TCP/IP">
<link rel="previous" href="ar01s06.html" title="6. Programmation multicast">
<link rel="next" href="ar01s08.html" title="8. Politiques de routage et techniques de retransmission">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">7. Fonctionnement interne</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="ar01s06.html">Précédent</a> </td>
<th width="60%" align="center"> </th>
<td width="20%" align="right"> <a accesskey="n" href="ar01s08.html">Suivant</a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1" lang="fr">
<div class="titlepage">
<div><div><h2 class="title" style="clear: both">
<a name="id2514518"></a>7. Fonctionnement interne</h2></div></div>
<div></div>
</div>
<p>
Ce chapitre délivre des informations, qui ne sont ni nécessaires à la compréhension du fonctionnement du multicast, ni nécessaires à l'écriture de programmes multicast, mais qui deumeurent cependant très intéressantes car elles permettent une compréhension des concepts sur lesquels s'appuient le multicast et ses implémentations. Cela permet ainsi de contourner les erreurs les plus fréquentes et de nombreuses incompréhensions.
  </p>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2514538"></a>7.1. IGMP</h3></div></div>
<div></div>
</div>
<p>
Lorsque nous avons parlé de <span class="emphasis"><em>IP_ADD_MEMBERSHIP</em></span> et de <span class="emphasis"><em>IP_DROP_MEMBERSHIP</em></span>, nous avions dit que l'information délivrée était utilisée par le noyau pour accepter ou refuser les datagrammes multicast. Cela est vrai mais pas complêtement. Une telle simplification impliquerait que les datagrammes multicast de tous les groupes multicast à travers le monde seraient reçus par notre hôte, et alors il vérifierait les souscriptions des processus s'exécutant sur lui et déciderait s'il délivre l'information ou non. Comme vous pouvez l'imaginer, cela serait du gaspillage de bande passante.
   </p>
<p>
De fait actuellement, un hôte informe son routeur des groupes multicasts qui l'intéressent ; alors, ce routeur informe à son tour les routeurs en amont qu'il souhaite recevoir le trafic en question, et ainsi de suite. Les algorithmes employés servant à demander le trafic d'un groupe donné, ou à l'inverse ceux pour terminer une souscription, peuvent varier. Cependant, il y a quelque chose qui ne change jamais : la manière dont cette information est transmisse. IGMP est utilisé pour cela. IGMP signifie Internet Group Management Protocol. C'est un protocole similaire par de nombreux aspects à ICMP. Il porte un numéro de protocole égal à 2 et ses messages sont transportés dans des datagrammes IP. Tous les hôtes compatibles multicast niveau 2 doivent implémenter ce protocole.
   </p>
<p>
Comme dit précédemment, IGMP est utilisé aussi bien par les hôtes pour donner des informations de souscription à ses routeurs, que par les routeurs pour communiquer entre eux. Par la suite, il ne sera question que des relations entre les hôtes et les routeurs, principalement parce que les informations décrivant les communications entre routeurs (autres que celles provenant du source code de mrouted, RFC-1075) et décrivant le protocole de routage multicast « distance-vecteur » sont maintenant obsolètes. mrouted implémente quant à lui une version de ce protocole non encore documentée.
   </p>
<p>
La version 0 d'IGMP est spécifiée dans le RFC-988 qui est maintenant obsolète. Plus personne n'utilise la version 0 de nos jours.
   </p>
<p>
La version 1 d'IGMP est décrite dans le RFC-1112 et, bien qu'elle soit mise-à-jour par le RFC-2236 (IGMP version 2), elle est encore largement utilisée.
   </p>
<p>
Le noyau Linux implémente complètement IGMP version 1 et en partie la version 2.
   </p>
<p>
Vous trouverez ci-dessous une description informelle du protocole. Vous pouvez consulter le RFC-2236 pour une description formelle, avec beaucoup de diagrammes d'états et d'expiration de délais.
   </p>
<p>
Tous les messages IGMP ont la structure suivante :
   </p>
<p>
    </p>
<div class="informaltable"><table border="1">
<colgroup>
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
<col align="center">
</colgroup>
<tbody>
<tr>
<td align="center" valign="top"><p>0</p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p>1</p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p>2</p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p></p></td>
<td align="center" valign="top"><p>3</p></td>
<td align="center" valign="top"><p></p></td>
</tr>
<tr>
<td align="center" valign="top"><p>0</p></td>
<td align="center" valign="top"><p>1</p></td>
<td align="center" valign="top"><p>2</p></td>
<td align="center" valign="top"><p>3</p></td>
<td align="center" valign="top"><p>4</p></td>
<td align="center" valign="top"><p>5</p></td>
<td align="center" valign="top"><p>6</p></td>
<td align="center" valign="top"><p>7</p></td>
<td align="center" valign="top"><p>8</p></td>
<td align="center" valign="top"><p>9</p></td>
<td align="center" valign="top"><p>0</p></td>
<td align="center" valign="top"><p>1</p></td>
<td align="center" valign="top"><p>2</p></td>
<td align="center" valign="top"><p>3</p></td>
<td align="center" valign="top"><p>4</p></td>
<td align="center" valign="top"><p>5</p></td>
<td align="center" valign="top"><p>6</p></td>
<td align="center" valign="top"><p>7</p></td>
<td align="center" valign="top"><p>8</p></td>
<td align="center" valign="top"><p>9</p></td>
<td align="center" valign="top"><p>0</p></td>
<td align="center" valign="top"><p>1</p></td>
<td align="center" valign="top"><p>2</p></td>
<td align="center" valign="top"><p>3</p></td>
<td align="center" valign="top"><p>4</p></td>
<td align="center" valign="top"><p>5</p></td>
<td align="center" valign="top"><p>6</p></td>
<td align="center" valign="top"><p>7</p></td>
<td align="center" valign="top"><p>8</p></td>
<td align="center" valign="top"><p>9</p></td>
<td align="center" valign="top"><p>0</p></td>
<td align="center" valign="top"><p>1</p></td>
</tr>
<tr>
<td colspan="8" align="center" valign="top">
	 Type
	</td>
<td colspan="8" align="center" valign="top">
	 Temp max. de réponse
	</td>
<td colspan="16" align="center" valign="top">
	 contrôle d'intégrité
	</td>
</tr>
<tr><td colspan="32" align="center" valign="top">
	 Adresse de groupe
	</td></tr>
</tbody>
</table></div>
<p>
   </p>
<p>
Dans le cas d'IGMP version 1 (appelé ci-après IGMPv1) le champ « temp maximum de réponse » est marqué comme « inutilisé ». Il est rempli de zéros lors d'une émission et est ignoré à la réception. De plus, cette version d'IGMP coupe le champ « type » en deux sous champs de longueur 4 bits : « version » et « type ». Comme IGMPv1 identifie un message d'<span class="emphasis"><em>interrogation de souscription</em></span> comme étant 0x11 (version 1, type 1) et IGMPv2 aussi, les 8 bits ont la même signification.
   </p>
<p>
Je pense qu'il est plus instructif de donner d'abord une description d'IGMPv1 et de pointer ensuite les additions faites par IGMPv2.
   </p>
<p>
À la lecture de la description faite ci-dessous, gardez à l'esprit qu'un routeur multicast reçoit <span class="emphasis"><em>tous</em></span> les datagrammes IP multicast.
   </p>
<div class="sect3" lang="fr">
<div class="titlepage">
<div><div><h4 class="title">
<a name="IGMP-version-1"></a>7.1.1. IGMP version 1</h4></div></div>
<div></div>
</div>
<p>
Les routeurs émettent périodiquement des requêtes IGMP sur les souscriptions des hôtes (IGMP Host Membership Queries). Ces requêtes sont faites sur le groupe <span class="emphasis"><em>all-hosts</em></span> (224.0.0.1) avec un TTL de 1, ceci toutes les 1 ou 2 minutes. Tous les hôtes supportant le multicast entendent cela, mais ne répondent pas immédiatement pour éviter une tempête de réponses. Au lieu de cela, ils déclenchent un compte à rebours pour chaque groupe auquel ils appartiennent, et cela sur l'interface sur laquelle ils ont reçu la demande.
    </p>
<p>
Tôt ou tard, le compte à rebours expirera sur un des hôtes, ce dernier envoiera alors un <span class="emphasis"><em>IGMP Host Membership Report</em></span> (résultat d'appartenance) avec un TTL de 1 à l'adresse multicast du groupe devant être rapporté. Comme le résultat est envoyé au groupe, tous les hôtes qui ont rejoint ce dernier -et qui sont actuellement en train d'attendre que leur propre délai expire- le recoive aussi. Alors, ces hôtes stoppent leur compte à rebours, et ne génèrent pas d'autres rapports. Un seul rapport est généré - par l'hôte ayant le délai le plus court-, et cela est suffisant pour le routeur. Celui-ci doit seulement savoir s'il y a des membres de ce groupe dans le sous réseau, pas qui et combien ils sont.
    </p>
<p>
Lorsqu'aucune réponse n'est reçue pour un groupe donné après un certain nombre de requêtes, le routeur déduit qu'il n'y a pas de membre de ce groupe sur ce sous réseau, et de ce fait n'a pas à transmettre le trafic. Notez que pour IGMPv1 il n'y a pas de message de fin d'appartenance de groupe.
    </p>
<p>
Lorsqu'un hôte rejoint un <span class="emphasis"><em>nouveau</em></span> groupe, le noyau
envoie un rapport concernant ce groupe, ainsi il n'est pas nécessaire
d'attendre une ou deux minutes jusqu'a ce que la demande d'appartenance
suivante soit formulée. Comme vous pouvez le constater le paquet IGMP
est généré par le noyau comme étant une réponse à la commande
<span class="emphasis"><em>IP_ADD_MEMBERSHIP</em></span>, comme vu dans la <a href="ar01s06.html#IP-ADD-MEMBERSHIP">section IP_ADD_MEMBERSHIP</a>. Notez l'importance de l'adjectif
<span class="emphasis"><em>nouveau</em></span> : si la commande
<span class="emphasis"><em>IP_ADD_MEMBERSHIP</em></span> est effectuée sur un hôte déjà
membre de ce groupe, aucun paquet IGMP n'est envoyé car nous recevons
déjà le trafic pour ce groupe ; à la place, un compteur sur le
nombre d'utilisateurs de ce groupe est incrémenté.
<span class="emphasis"><em>IP_DROP_MEMBERSHIP</em></span> ne génère aucun datagramme dans
IGMPv1.
    </p>
<p>
Les requêtes sur les souscriptions des hôtes sont identifiées par le type 0x11, et les rapports d'appartenance par le type 0x12.
    </p>
<p>
Aucun rapport n'est envoyé pour le groupe <span class="emphasis"><em>all-hosts</em></span>. L'appartenance à ce dernier groupe est permanente.
    </p>
</div>
<div class="sect3" lang="fr">
<div class="titlepage">
<div><div><h4 class="title">
<a name="id2515640"></a>7.1.2. IGMP version 2</h4></div></div>
<div></div>
</div>
<p>
L'ajout majeur dans cette version du protocole est l'inclusion de messages de fin d'appartenance de groupe (<span class="emphasis"><em>Leave Group message</em></span>) noté 0x17. La raison de cet ajout est d'éviter un gaspillage de bande passante entre le moment où le dernier hôte du sous-réseau quitte ses souscriptions et le moment où le routeur s'aperçoit qu'il n'y a plus de membre de ce groupe présent dans ce sous réseau (<span class="emphasis"><em>leave latency</em></span>). Les messages de fin d'appartenance doivent être adressés au groupe <span class="emphasis"><em>all-routers</em></span> (224.0.0.2) plus qu'au groupe que l'on vient de quitter, car l'information n'a pas d'utilité pour les autres membres de ce groupe (les versions du noyau ultérieures à 2.0.33 envoyent l'information au groupe ; et bien que cela ne porte pas préjudice aux hôtes, cela constitue une perte de temps car ces messages doivent êtres traités, sans pour autant donner d'information utile). Il est à noter qu'il existe certains détails subtiles à propos du moment où il est approprié ou non d'envoyer des Messages de fin d'appartenance ; si cela vous intéresse, reportez-vous au RFC.
    </p>
<p>
Quant un router IGMPv2 reçoit un message de fin d'appartenance de groupe, il envoie une requête « spécifique de groupe » (<span class="emphasis"><em>Group-Specific Queries</em></span>) au groupe à quitter. Cela contitue un autre ajout. IGMPv1 n'a pas ces requêtes spécifiques de groupes. Toutes ces requêtes sont envoyées au groupe <span class="emphasis"><em>all-hosts</em></span>. Le champ <span class="emphasis"><em>type</em></span> de l'entête IGMP n'est pas amené à changer (0x11 comme précédemment), mais le champ « adresse du groupe » est rempli avec l'adresse du groupe multicast à quitter.
    </p>
<p>
Le champ <span class="emphasis"><em>Temps Maximum de Réponse</em></span>, qui dans IGMPv1 était mis à 0 pour la transmission et ignoré à la réception, devient significatif seulement dans les messages de requêtes en appartenance (ou Membership Query). Ce champ informe du temps maximum alloué en 1/10 de secondes avant l'envoi d'un rapport. Il est donc utilisé comme mécanisme d'écoute.
    </p>
<p>
IGMPv2 introduit un nouveau type de message : 0x16. Il s'agit d'un « rapport d'appartence IGMPv2 » envoyé par les hôtes IGMPv2 si ceux-ci détectent qu'un routeur IGMPv2 est présent (un hôte IGMPv2 sait qu'un routeur IGMPv1 est présent s'il reçoit une requête avec le champ « Réponse Max » à 0).
    </p>
<p>
Lorsque plus d'un routeur se réclame comme agissant en interrogateur, IGMPv2 fournit un mécanisme pour écourter les discussions : le routeur possédant la plus petite adresse IP est désigné comme étant l'interrogateur. Les autres routeurs restent à l'écoute d'éventuels dépassements de temps de réponse. Ainsi, si le routeur ayant la plus petite adresse IP « tombe » ou est éteint, la décision pour connaitre le nouvel interrogateur est prise après que les délais aient expiré.
    </p>
</div>
</div>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2515752"></a>7.2. Aspects concernant le noyau</h3></div></div>
<div></div>
</div>
<p>
Cette sous-section donne le point de départ de l'étude de l'implémentation du multicast dans le noyau Linux. Cela n'explique pas l'implémentation faite. Elle indique juste où trouver les éléments.
   </p>
<p>
L'étude a été menée sur la version 2.0.32, de ce fait il se peut qu'elle soit dépassée au moment où vous allez la lire (le code réseau du noyau semble avoir beaucoup changé dans les versions 2.1.x).
   </p>
<p>
Le code multicast dans les noyaux Linux est toujours entouré par une paire de balises #ifdef CONFIG_IP_MULTICAST / #endif, de cette façon vous pouvez l'inclure ou l'exclure de votre noyau selon vos besoins (cette inclusion/exclusion est faite à l'étape de compilation, comme vous le savez probablement… Les #ifdefs sont interprétés par le pré-processeur. La décision est prise selon les choix que vous avez effectués lors du make config, make menuconfig ou make xconfig).
    </p>
<p>
Vous désirez probablement activer les fonctionnalités multicast, mais si votre machine Linux n'a pas à se comporter comme un routeur multicast, vous n'aurez sûrement pas besoin d'activer le routage multicast du noyau. Le code concernant le routage multicast est contenu entre la paire de balises #ifdef CONFIG_IP_MROUTE / #endif.
    </p>
<p>
Les sources du noyau sont couramment placées dans le répertoire /usr/src/linux. Cependant, l'emplacement peut changer, de ce fait l'emplacement du répertoire de base des sources du noyau sera designé ici comme étant LINUX. De ce fait, LINUX/net/ipv4/udp.c correspond à /usr/src/linux/net/ipv4/udp.c si vous avez extrait les sources du noyau dans le répertoire /usr/src/linux.
   </p>
<p>
Toutes les interfaces multicast désignées dans cette section et dédiées à la programmation d'applications multicast, s'utilisent au travers des appels systèmes <span class="emphasis"><em>setsockopt() / getsockopt()</em></span>.
   </p>
<p>
L'un comme l'autre est implémenté au moyen de fonctions qui effectuent quelques tests pour vérifier les paramètres qui leurs sont passés et qui, à tour de rôle, appellent une autre fonction qui effectue quelques tests complémentaires, démultiplexant l'appel passé dans le paramètre <span class="emphasis"><em>level</em></span> pour chaque appel système, et appelle alors une autre fonction qui… (si vous êtes intéressé par tous ces sauts, vous pouvez les suivre dans LINUX/net/socket.c (fonctions <span class="emphasis"><em>sys_socketcall()</em></span> et <span class="emphasis"><em>sys_setsockopt()</em></span>), LINUX/net/ipv4/af_inet.c (fonction <span class="emphasis"><em>inet_setsockopt()</em></span>) et LINUX/net/ipv4/ip_sockglue.c (fonction <span class="emphasis"><em>ip_setsockopt()</em></span>)).
    </p>
<p>
Le fichier qui nous intéresse ici est LINUX/net/ipv4/ip_sockglue.c. Nous y trouvons <span class="emphasis"><em>ip_setsockopt()</em></span> et <span class="emphasis"><em>ip_getsockopt()</em></span> qui sont essentiellement des switchs (après quelques vérifications d'erreurs) vérifiant chaque valeur possible pour <span class="emphasis"><em>optname</em></span>. Tout comme les options unicast, les options multicast sont capturées : <span class="emphasis"><em>IP_MULTICAST_TTL</em></span>, <span class="emphasis"><em>IP_MULTICAST_LOOP</em></span>, <span class="emphasis"><em>IP_MULTICAST_IF</em></span>, <span class="emphasis"><em>IP_ADD_MEMBERSHIP</em></span> et <span class="emphasis"><em>IP_DROP_MEMBERSHIP</em></span>. Avant le switch, un test est fait pour savoir si les options sont spécifiques au routeur multicast, et dans ce cas, elles sont redirigées vers les fonctions <span class="emphasis"><em>ip_mroute_setsockopt()</em></span> et <span class="emphasis"><em>ip_mroute_getsockopt()</em></span> (fichier LINUX/net/ipv4/ipmr.c).
     </p>
<p>
Dans le fichier LINUX/net/ipv4/af_inet.c nous pouvons voir les valeurs par défaut -dont nous parlions dans les sections précédentes (loopback activé, TTL=1)- données à la prise réseau lorsqu'elle est crée (prises depuis la fonction <span class="emphasis"><em>inet_create()</em></span> de ce fichier) :
     </p>
<pre class="programlisting">
#ifdef CONFIG_IP_MULTICAST
  sk-&gt;ip_mc_loop=1;
  sk-&gt;ip_mc_ttl=1;
 *sk-&gt;ip_mc_name=0;
  sk-&gt;ip_mc_list=NULL;
#endif
</pre>
<p>
L'assertion stipulant que « la fermeture d'une prise réseau implique que le noyau n'accepte plus les souscriptions faites par cette prise » est corroborée par :
    </p>
<pre class="programlisting">
#ifdef CONFIG_IP_MULTICAST
/* Les applications oublient de quitter les groupes avant de partir */
  ip_mc_drop_socket(sk);
#endif
</pre>
<p>
Extrait de la fonction <span class="emphasis"><em>inet_release()</em></span>, dans le même fichier que précédemment.
   </p>
<p>
Les opérations indépendantes de la couche de liaison et relatives aux périphériques sont gardées dans le fichier LINUX/net/core/dev_mcast.c.
   </p>
<p>
Deux fonctions sont encore manquantes : les fonctions concernant le traîtement des paquets multicast en entrée et en sortie. Comme tous les autres paquets, les paquets entrants sont transmis depuis les pilotes de périphériques à la fonction <span class="emphasis"><em>ip_rcv()</em></span> (LINUX/net/ipv4/ip_input.c). Cette fonction contient le filtrage parfait appliqué aux paquets multicasts qui ont passé la couche du périphérique (souvenez-vous que les couches les plus basses ne fournissent qu'un filtrage approximatif et ce n'est que la couche IP qui sait à 100% si nous sommes intéressés ou non par ce groupe multicast). Si l'hôte fonctionne comme un routeur multicast, alors la fonction décide aussi si le paquet doit être transmis. Il appelle alors <span class="emphasis"><em>ipmr_forward()</em></span>. Cette dernière fonction est implémentée dans le fichier LINUX/net/ipv4/ipmr.c.
   </p>
<p>
Le code se chargeant des paquets émis en sortie est contenu dans le fichier LINUX/net/ipv4/ip_output.c. On y trouve l'effet possible de l'option des <span class="emphasis"><em>IP_MULTICAST_LOOP</em></span>, selon que l'on souhaite ou non faire une boucle locale (fonction <span class="emphasis"><em>ip_queue_xmit()</em></span>). Ainsi la valeur du <span class="emphasis"><em>TTL</em></span> du paquet sortant est donné selon qu'il s'agit d'un paquet unicast ou multicast. Dans ce dernier cas, la valeur de l'argument <span class="emphasis"><em>IP_MULTICAST_TTL</em></span> passé en option est utilisée (fonction <span class="emphasis"><em>ip_build_xmit()</em></span>).
   </p>
<p>
Durant un travail à l'aide de mrouted (un programme fournissant des informations liées au noyau sur la façon de router des datagrammes multicast), nous avons détecté que les paquets multicast provenant du réseau local sont routés correctement exceptés ceux venant de la machine Linux agissant en tant que routeur multicast ! ip_input.c fonctionne bien, mais il semble que ce n'est pas le cas de ip_output.c. En lisant le code source des fonctions de sorties, nous avons trouvé que les paquets sortant n'étaient pas passés à <span class="emphasis"><em>ipmr_forward()</em></span>, fonction décidant si les paquets doivent être routés ou non. Les paquets sont sortis sur le réseau local mais, comme les cartes réseaux sont le plus souvent incapables de lire les données transmises, ces datagrammes ne sont alors jamais routés. Nous avons ajouté le code nécessaire à la fonction ip_build_xmit() et le tour était joué. (Disposer du code source du noyau n'est pas un luxe, mais une nécessité !)
   </p>
<p>
<span class="emphasis"><em>ipmr_forward()</em></span> a déjà été mentionné un certain nombre de fois. C'est une fonction importante car elle permet de résoudre un important problème de compréhension nécessitant d'être plus largement expliqué. En routant du trafic multicast, ce n'est pas mrouted qui fabrique les copies puis les expédie à ses propres destinataires. mrouted reçoit tout le trafic multicast et, basé sur cette information, calcule les tables de routages multicast puis informe le noyau de la manière de router : « les datagrammes pour ce groupe provenant de cette interface doivent être transférés à ces interfaces ». Cette information est transmise au noyau par l'appel de <span class="emphasis"><em>setsockopt()</em></span> sur la file d'une prise réseau ouverte par le daemon mrouted (le protocole spécifié lors de la création de la file de la prise réseau doit être <span class="emphasis"><em>IPPROTO_IGMP</em></span>). La prise en compte d'options s'effectue par l'appel à la fonction <span class="emphasis"><em>ip_mroute_setsockopt()</em></span> de LINUX/net/ipv4/ipmr.c. Cette première option (il est préférable de les appeler commandes plutôt qu'options) provenant de cette prise réseau doit être <span class="emphasis"><em>MRT_INIT</em></span>. Toutes les autres commandes sont ignorées (retournant <span class="emphasis"><em>-EACCES</em></span>) si <span class="emphasis"><em>MRT_INIT</em></span> n'est pas précisé en premier. Seulement une seule instance de mrouted peut être exécutée à la fois sur un même hôte. Pour garder une trace de cela, lorsque le premier <span class="emphasis"><em>MRT_INIT</em></span> est reçu, une variable importante, <span class="emphasis"><em>struct sock* mroute_socket</em></span>, est pointée sur la prise réseau ou le <span class="emphasis"><em>MRT_INIT</em></span> a été reçu. Si <span class="emphasis"><em>mroute_socket</em></span> n'est pas nul lors de l'attente d'un <span class="emphasis"><em>MRT_INIT</em></span> cela signifie qu'un autre mrouted est en cours d'exécution, un <span class="emphasis"><em>-EADDRINUSE</em></span> est alors renvoyé. Toutes les commandes s'appuyant sur le même principe (<span class="emphasis"><em>MRT_DONE</em></span>, <span class="emphasis"><em>MRT_ADD_VIF</em></span>, <span class="emphasis"><em>MRT_DEL_VIF</em></span>, <span class="emphasis"><em>MRT_ADD_MFC</em></span>, <span class="emphasis"><em>MRT_DEL_MFC</em></span> et <span class="emphasis"><em>MRT_ASSERT</em></span>) retournent <span class="emphasis"><em>-EACCES</em></span> si elles proviennent d'une prise réseau différente de <span class="emphasis"><em>mroute_socket</em></span>.
   </p>
<p>
Comme les datagrammes multicast routés peuvent être reçus/envoyés au travers d'interfaces physiques ou de tunnels, une abstraction commune a été définie : les VIFs, InterFaces Virtuelles (ou Virtual InterFaces). mrouted communique les structures VIFs au noyau, en indiquant les interfaces physiques ou tunnels pour qu'il les ajoute à sa table de routage. Les entrées de retransmissions multicast indiquent où transmettre les datagrammes.
   </p>
<p>
Les VIFs sont ajoutées à l'aide de <span class="emphasis"><em>MRT_ADD_VIF</em></span> et supprimées avec <span class="emphasis"><em>MRT_DEL_VIF</em></span>. L'un comme l'autre passent un struct vifctl au noyau (défini dans /usr/include/linux/mroute.h) avec les informations suivantes :
   </p>
<pre class="programlisting">
struct vifctl {
  vifi_t  vifc_vifi;             /* Index du VIF */
  unsigned char vifc_flags;      /* Attributs VIFF_ */
  unsigned char vifc_threshold;  /* Seuil du ttl */
  unsigned int vifc_rate_limit;  /* Valeur de débit limite : non implémenté */
  struct in_addr vifc_lcl_addr;  /* Notre adresse */
  struct in_addr vifc_rmt_addr;  /* Adresse du tunnel IPIP */
};
</pre>
<p>
Avec cette information une structure <span class="emphasis"><em>vif_device</em></span> est construite :
   </p>
<pre class="programlisting">
struct vif_device
{
  struct device   *dev;                   /* le périphérique que nous utilisons */
  struct route    *rt_cache;              /* Tunnel route cache */
  unsigned long   bytes_in,bytes_out;
  unsigned long   pkt_in,pkt_out;         /* Statistiques */
  unsigned long   rate_limit;             /* Forme du trafic ; non implémenté */
  unsigned char   threshold;              /* seuil du TTL */
  unsigned short  flags;                  /* attributs de contrôle */
  unsigned long   local,remote;           /* Adresses(distantes pour les tunnels)*/
};
</pre>
<p>
Notez l'entrée <span class="emphasis"><em>dev</em></span> dans la structure. La structure <span class="emphasis"><em>device</em></span> est définie dans le fichier /usr/include/linux/netdevice.h. C'est une grosse structure, mais le champ qui nous intéresse est :
   </p>
<pre class="programlisting">
struct ip_mc_list*    ip_mc_list;   /* chaîne des filtres IP multicast */
</pre>
<p>
La structure <span class="emphasis"><em>ip_mc_list</em></span> -définie dans /usr/include/linux/igmp.h- est comme suit :
   </p>
<pre class="programlisting">
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;
};
</pre>
<p>
Ainsi, le membre <span class="emphasis"><em>ip_mc_list</em></span> de la structure <span class="emphasis"><em>dev</em></span> est un pointeur sur une liste chaînée de structures <span class="emphasis"><em>ip_mc_list</em></span>, chacune contenant une entrée pour chaque groupe multicast pour laquelle l'interface est membre. Ici nous voyons une fois de plus que les appartenances sont associées à des interfaces. LINUX/net/ipv4/ip_input.c parcourt cette liste chaînée pour décider si le datagramme reçu est destiné à un groupe auquel l'interface appartient :
   </p>
<pre class="programlisting">
#ifdef CONFIG_IP_MULTICAST
  if(!(dev-&gt;flags&amp;IFF_ALLMULTI) &amp;&amp; brd==IS_MULTICAST
     &amp;&amp; iph-&gt;daddr!=IGMP_ALL_HOSTS
     &amp;&amp; !(dev-&gt;flags&amp;IFF_LOOPBACK))
  {
    /*
     * Verification de l'appartenance à un de nos groupes
     */
     struct ip_mc_list *ip_mc=dev-&gt;ip_mc_list;
     do
     {
       if(ip_mc==NULL)
       {
         kfree_skb(skb, FREE_WRITE);
         return 0;
       }
       if(ip_mc-&gt;multiaddr==iph-&gt;daddr)
         break;
       ip_mc=ip_mc-&gt;next;
     }
     while(1);
  }
#endif
</pre>
<p>
Le champ <span class="emphasis"><em>users</em></span> de la strucuture
<span class="emphasis"><em>ip_mc_list</em></span> est utilisé pour implémenter ce que nous
avons dit dans la <a href="ar01s07.html#IGMP-version-1">section IGMP version 1</a> : si un processus
joint un groupe et que l'interface est déjà membre de ce groupe (ie, un
autre processus joint le même groupe sur la même interface que
précédemment) seul le compteur du nombre de membres
(<span class="emphasis"><em>users</em></span>) est incrémenté. Aucun message IGMP n'est
envoyé, comme vous pouvez le voir dans le code suivant (extrait de
<span class="emphasis"><em>ip_mc_inc_group()</em></span>, appelé par
<span class="emphasis"><em>ip_mc_join_group()</em></span>, provenant du fichier
LINUX/net/ipv4/igmp.c) :
   </p>
<pre class="programlisting">
for(i=dev-&gt;ip_mc_list;i!=NULL;i=i-&gt;next)
  {
    if(i-&gt;multiaddr==addr)
    {
      i-&gt;users++;
      return;
    }
  }
</pre>
<p>
À chaque suppression d'une souscription de groupe, le compteur est décrémenté. Des opérations supplémentaires sont appliquées seulement si le compteur est devient égal à 0 (fonction <span class="emphasis"><em>ip_mc_dec_group()</em></span>).
   </p>
<p>
<span class="emphasis"><em>MRT_ADD_MFC</em></span> et <span class="emphasis"><em>MRT_DEL_MFC</em></span> ajoutent ou suppriment des entrées de retransmission dans la table de routage multicast. L'un comme l'autre passent un <span class="emphasis"><em>struct mfcctl</em></span> au noyau (défini dans /usr/include/linux/mroute.h) avec cette information :
   </p>
<pre class="programlisting">
struct mfcctl
{
  struct in_addr mfcc_origin;       /* Origine du mcast      */
  struct in_addr mfcc_mcastgrp;     /* Groupe en question    */
  vifi_t  mfcc_parent;              /* Où cela arrive     */
  unsigned char mfcc_ttls[MAXVIFS]; /* Où cela va t-il    */
};
</pre>
<p>
Avec toute cette information en main, <span class="emphasis"><em>ipmr_forward()</em></span> parcourt les VIFs, et si une correspondance est trouvée, on duplique le datagramme et on appelle <span class="emphasis"><em>ipmr_queue_xmit()</em></span> qui, à tour de rôle, utilise le périphérique de sortie indiqué par la table de routage et une adresse de destination propre si le paquet est à envoyer au travers d'un tunnel (il s'agit en fait de l'adresse de destination unicast de l'autre coté du tunnel).
   </p>
<p>
La fonction <span class="emphasis"><em>ip_rt_event()</em></span> (qui n'est pas directement apparentée à la sortie des datagrammes, mais qui se trouve pourtant dans ip_output.c) reçoit les évènements relatifs aux périphériques réseaux, tel que le branchement du périphérique par exemple. Cette fonction assure que le périphérique joint bien le groupe multicast <span class="emphasis"><em>ALL-HOSTS</em></span>.
   </p>
<p>
Les fonctions IGMP sont implémentées dans le fichier LINUX/net/ipv4/igmp.c. Les informations importantes concernant ces fonctions se trouvent dans /usr/include/linux/igmp.h et /usr/include/linux/mroute.h. L'entrée IGMP dans le répertoire /proc/net est créée à l'aide de la fonction <span class="emphasis"><em>ip_init()</em></span> contenue dans le fichier LINUX/net/ipv4/ip_output.c.
   </p>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="ar01s06.html">Précédent</a> </td>
<td width="20%" align="center"><a accesskey="u" href="index.html">Niveau supérieur</a></td>
<td width="40%" align="right"> <a accesskey="n" href="ar01s08.html">Suivant</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">6. Programmation multicast </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">Sommaire</a></td>
<td width="40%" align="right" valign="top"> 8. Politiques de routage et techniques de retransmission</td>
</tr>
</table>
</div>
</body>
</html>