Difference between revisions of "Utilisation du multicast"

From Livre IPv6

 
 
(13 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 +
{{suivi| mini-ping | mini-ping | Programmation avancée | Programmation avancée }}
 
La programmation avec les groupes multicast n'est pas standardisée en IPv4. La nouvelle API "socket" propose un ensemble de structures et appels systèmes pour étendre l'interface de programmation sockets aux applications utilisant le multicast. Cet exemple va illustrer ce point.
 
La programmation avec les groupes multicast n'est pas standardisée en IPv4. La nouvelle API "socket" propose un ensemble de structures et appels systèmes pour étendre l'interface de programmation sockets aux applications utilisant le multicast. Cet exemple va illustrer ce point.
  
Le but des deux programmes est d'échanger des données par multicast. Le programme <tt>in2multi6</tt> diffuse les données lues en entrée ("standard input") vers un groupe multicast donné. Le programme <tt>multi2out6</tt> écoute les paquets transmis dans ce groupe et les copie sur la sortie standard ("stdout").
+
Le but des deux programmes est d'échanger des données par multicast. Le programme <tt>[http://livre.point6.net/listings/in2multi6.c in2multi6]</tt> diffuse les données lues en entrée ("standard input") vers un groupe multicast donné. Le programme <tt>[http://livre.point6.net/listings/multi2out6.c multi2out6]</tt> écoute les paquets transmis dans ce groupe et les copie sur la sortie standard ("stdout").
  
Pour ce faire <tt>multi2out6</tt> va faire des appels systèmes qui vont produire des paquets d'abonnement (et de désabonnement) à un groupe multicast. L'abonnement sera réalisé grâce à l'envoi de deux messages ICMPv6 successifs de type 131, c'est-à-dire des "rapports d'abonnement". Puis les messages émis dans le groupe sont reçus par le programme. Lorsque le programme s'arrête, le code de l'interface va automatiquement provoquer l'émission d'un message de réduction d'un groupe de multicast (132).
+
==[http://livre.point6.net/listings/multi2out6.c multi2out6]==
  
Le programme multi2out6 est appelé de la manière suivante :
+
Pour ce faire <tt>[http://livre.point6.net/listings/multi2out6.c multi2out6]</tt> va faire des appels systèmes qui vont produire des paquets d'abonnement (et de désabonnement) à un groupe multicast. L'abonnement sera réalisé grâce à l'envoi de deux messages ICMPv6 successifs de type 131, c'est-à-dire des "rapports d'abonnement". Puis les messages émis dans le groupe sont reçus par le programme. Lorsque le programme s'arrête, le code de l'interface va automatiquement provoquer l'émission d'un message de réduction d'un groupe de multicast (132).
 +
 
 +
Le programme [http://livre.point6.net/listings/multi2out6.c multi2out6] est appelé de la manière suivante :
  
 
  '''multi2out6 [-i interface] <adresse de groupe multicast>'''
 
  '''multi2out6 [-i interface] <adresse de groupe multicast>'''
  
Voici le code complet du programme. Le port utilisé (ligne [[See]] ) est quelconque mais ne doit bien sûr pas correspondre à un service déjà existant.
+
Voici le code complet du programme. Le port utilisé (ligne 15) est quelconque mais ne doit bien sûr pas correspondre à un service déjà existant.
+
 
 
#include <sys/types.h>
+
  1| #include <sys/types.h>
#include <sys/socket.h>
+
  2| #include <sys/socket.h>
#include <netinet/in.h>
+
  3| #include <netinet/in.h>
#include <arpa/inet.h>
+
  4| #include <arpa/inet.h>
#include <stdio.h>
+
  5| #include <stdio.h>
#include <signal.h>
+
  6| #include <signal.h>
#include <unistd.h>
+
  7| #include <unistd.h>
    
+
   8|  
  #ifndef SO_REUSEPORT
+
  9| #ifndef SO_REUSEPORT
  #define SO_REUSEPORT SO_REUSEADDR
+
  10| #define SO_REUSEPORT SO_REUSEADDR
  #endif
+
  11| #endif
    
+
12|    
  struct sockaddr_in6 sin6;
+
  13| struct sockaddr_in6 sin6;
 
+
14| 
  #define IPPORT 54321
+
  15| #define IPPORT 54321
 
+
16| 
  void Perror(const char *c)
+
  17| void Perror(const char *c)
  {
+
  18| {
    perror(c);
+
19|    perror(c);
    exit(1);
+
20|    exit(1);
  }
+
  21| }
 
+
22| 
  void Usage ()
+
  23| void Usage ()
  {
+
  24| {
    fprintf(stderr, "%s\n", "Usage: multi2out6 [-i interface] addr");
+
25|    fprintf(stderr, "%s\n", "Usage: multi2out6 [-i interface] addr");
    exit(1);
+
26|    exit(1);
  }
+
  27| }
 
+
28| 
  void BrokenPipe(int Signal)
+
  29| void BrokenPipe(int Signal)
  {
+
  30| {
    signal(SIGPIPE, BrokenPipe);
+
31|    signal(SIGPIPE, BrokenPipe);
    return;
+
32|    return;
  }
+
  33| }
 +
34|
 
    
 
    
 
La partie principale du programme traite les options éventuelles. En fait, il n'y en a qu'une, le choix de l'interface à abonner au groupe multicast. Une fois ces operations effectuées, il faut créer et attacher une socket à une adresse. Ces opérations sont réalisées par l'utilisation des fonctions classiques socket et bind.
 
La partie principale du programme traite les options éventuelles. En fait, il n'y en a qu'une, le choix de l'interface à abonner au groupe multicast. Une fois ces operations effectuées, il faut créer et attacher une socket à une adresse. Ces opérations sont réalisées par l'utilisation des fonctions classiques socket et bind.
  
On utilise une structure de données spéciale pour stocker l'adresse multicast du groupe (définie dans netinet/in.h) :
+
On utilise une structure de données spéciale pour stocker l'adresse multicast du groupe (définie dans <tt>netinet/in.h</tt>) :
  
struct ipv6_mreq {
+
struct ipv6_mreq {
 
+
struct in6_addr ipv6mr_multiaddr; /* IPv6 mcast address of group */
struct in6_addr ipv6mr_multiaddr; /* IPv6 mcast address of group */
+
unsigned int ipv6mr_interface; /* local IPv6 address of interface */
 
+
};
unsigned int ipv6mr_interface; /* local IPv6 address of interface */
+
 
+
};
+
  
 
   
 
   
 
   
 
   
 
main(int argc, char **argv)
 
 
 
{
 
 
 
struct ipv6_mreq mreq;
 
 
 
int cc, ccb, ch, s;
 
 
 
char buf[10240];
 
 
 
u_int one = 1;
 
 
 
u_int ifi = 0;
 
 
 
 
 
 
signal(SIGPIPE, BrokenPipe);
 
 
 
while ((ch = getopt(argc, argv, "i:")) != -1)
 
 
 
switch(ch) {
 
 
 
case 'i':
 
 
 
if (sscanf(optarg, "%u\0", &ifi) != 1 &&
 
 
 
(ifi = if_nametoindex(optarg)) == 0)
 
 
 
Usage();
 
 
 
break;
 
 
 
default:
 
 
 
Usage();
 
 
 
}
 
 
 
argc -= optind;
 
 
 
argv += optind;
 
 
 
if (argc != 1)
 
 
 
Usage();
 
 
 
 
 
 
if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 
 
 
Perror("socket");
 
 
 
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
 
 
 
 
 
 
#ifdef SIN6_LEN
 
 
 
sin6.sin6_len = sizeof(sin6);
 
 
 
#endif
 
 
 
sin6.sin6_family = AF_INET6;
 
 
 
sin6.sin6_port = htons(IPPORT);
 
 
 
if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
 
 
 
Perror("bind");
 
  
La fonction inet_pton va permettre la conversion du nom de groupe passé en option sous une forme textuelle (par exemple ff12::1234:5678) en forme numérique. Le résultat est directement stocké dans la variable mreq qui sera utilisée par la commande setsockop. On passe en paramètre à cette fonction l'option IPV6_JOIN_GROUP avec la variable mreq. À partir de ce moment, il y a émission de deux messages d'abonnement. La boucle qui suit va permettre la lecture des informations envoyées sur le groupe auquel on vient de s'abonner et les afficher sur la sortie standard ainsi que leur longueur sur la sortie erreur standard.
+
35| int main(int argc, char **argv)
 +
36| {
 +
37| struct ipv6_mreq mreq;
 +
38| int cc, ccb, ch, s;
 +
39| char buf[10240];
 +
40| u_int one = 1;
 +
41| u_int ifi = 0;
 +
42| 
 +
43|    signal(SIGPIPE, BrokenPipe);
 +
44|    while ((ch = getopt(argc, argv, "i:")) != -1)
 +
45|      switch(ch) {
 +
46|      case 'i':
 +
47|          if (sscanf(optarg, "%u\0", &ifi) != 1 &&
 +
48|                      (ifi = if_nametoindex(optarg)) == 0)
 +
49|            Usage();
 +
50|          break;
 +
51|      default:
 +
52|          Usage();
 +
53|      }
 +
54|    argc -= optind;
 +
55|    argv += optind;
 +
56|    if (argc != 1)
 +
57|      Usage();
 +
58| 
 +
59|    if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 +
60|      Perror("socket");
 +
61|    setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
 +
62| 
 +
63| #ifdef SIN6_LEN
 +
64|    sin6.sin6_len = sizeof(sin6);
 +
65| #endif
 +
66|    sin6.sin6_family = AF_INET6;
 +
67|    sin6.sin6_port = htons(IPPORT);
 +
68|    if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
 +
69|      Perror("bind");
 
   
 
   
+
La fonction <tt>inet_pton</tt> va permettre la conversion du nom de groupe passé en option sous une forme textuelle (par exemple <tt>ff12::1234:5678</tt>) en forme numérique. Le résultat est directement stocké dans la variable <tt>mreq</tt> qui sera utilisée par la commande <tt>setsockop</tt>. On passe en paramètre à cette fonction l'option <tt>IPV6_JOIN_GROUP</tt> avec la variable <tt>mreq</tt>. À partir de ce moment, il y a émission de deux messages d'abonnement. La boucle qui suit va permettre la lecture des informations envoyées sur le groupe auquel on vient de s'abonner et les afficher sur la sortie standard ainsi que leur longueur sur la sortie erreur standard.
if (inet_pton(AF_INET6, *argv, &mreq.ipv6mr_multiaddr) != 1)
+
 
   
 
   
 
 
Usage();
+
70|    if (inet_pton(AF_INET6, *argv, &mreq.ipv6mr_multiaddr) != 1)
   
+
71|      Usage();
+
  72|    mreq.ipv6mr_interface = ifi;
mreq.ipv6mr_interface = ifi;
+
  73|    if (setsockopt(s,IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
   
+
  74|      Perror("setsockopt IPV6_JOIN_GROUP");
+
  75|    for (;;) {
if (setsockopt(s,IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
+
  76|      cc = read(s, buf, 10240);
   
+
  77|      if (cc < 0)
+
  78|          Perror("read socket");
Perror("setsockopt IPV6_JOIN_GROUP");
+
  79|      if (cc == 0) {
   
+
  80|          fprintf(stderr, "..\n");
+
  81|          exit (0);
for (;;) {
+
  82|      }
   
+
  83|      ccb = write(1, buf, cc);
+
  84|      if (ccb != cc)
cc = read(s, buf, 10240);
+
  85|          Perror("write file");
   
+
  86|      fprintf(stderr, "<-%d-\n", cc);
+
  87|    }
if (cc < 0)
+
  88| }
   
+
 
+
Lorsque le programme s'arrête, un <tt>close(s)</tt> implicite a lieu, et le code de l'interface va envoyer un message de réduction de groupe si elle est la dernière à avoir envoyé un rapport d'abonnement au groupe.
Perror("read socket");
+
   
+
+
if (cc == 0) {
+
   
+
+
fprintf(stderr, "..\n");
+
   
+
+
exit (0);
+
   
+
+
}
+
   
+
+
ccb = write(1, buf, cc);
+
   
+
+
if (ccb != cc)
+
   
+
+
Perror("write file");
+
   
+
+
fprintf(stderr, "<-%d-\n", cc);
+
   
+
+
}
+
   
+
+
}
+
  
Lorsque le programme s'arrête, un close(s) implicite a lieu, et le code de l'interface va envoyer un message de réduction de groupe si elle est la dernière à avoir envoyé un rapport d'abonnement au groupe.
+
==[http://livre.point6.net/listings/in2multi6.c in2multi6]==
  
 
Le programme est appelé de la manière suivante :
 
Le programme est appelé de la manière suivante :
  
in2multi6 [-i interface][-h max-hop-count][-l loop] <adresse de groupe multicast>
+
'''in2multi6 [-i interface][-h max-hop-count][-l loop] <adresse de groupe multicast>'''
  
 
Le code est relativement simple, principalement une analyse des arguments, le positionnement d'option et une boucle lecture--émission. En effet il n'est pas nécessaire de s'abonner pour faire de l'émission multicast.
 
Le code est relativement simple, principalement une analyse des arguments, le positionnement d'option et une boucle lecture--émission. En effet il n'est pas nécessaire de s'abonner pour faire de l'émission multicast.
  
Il y a quatre arguments, trois optionnels qui sont l'interface d'émission (nom ou index numérique), le "ttl" mis dans les paquets multicast51, et un drapeau qui sert à dire si la machine émettrice reçoit ou non les paquet émis. Le dernier argument est l'adresse du groups sous forme numérique.
+
Il y a quatre arguments, trois optionnels qui sont l'interface d'émission (nom ou index numérique), le "ttl" mis dans les paquets multicast (voir le manuel de la primitive <tt>readv</tt>), et un drapeau qui sert à dire si la machine émettrice reçoit ou non les paquet émis. Le dernier argument est l'adresse du groups sous forme numérique.
  
Voici le code complet du programme. Le port utilisé (ligne See ) est naturellement celui de in2multi6.
+
Voici le code complet du programme. Le port utilisé (ligne 10) est naturellement celui de <tt>in2multi6</tt>.
+
+
#include <sys/types.h>
+
+
+
#include <sys/socket.h>
+
+
+
#include <netinet/in.h>
+
+
+
#include <arpa/inet.h>
+
+
+
#include <stdio.h>
+
+
+
#include <unistd.h>
+
+
+
+
+
+
struct sockaddr_in6 sin6;
+
+
+
+
+
+
#define IPPORT 54321
+
+
+
+
+
+
void Perror(const char *c)
+
+
+
{
+
+
+
perror(c);
+
+
+
exit(1);
+
+
+
}
+
+
+
+
+
+
void Usage ()
+
+
+
{
+
+
+
fprintf(stderr, "%s\n", "Usage: in2multi6 [-i interface][-h hop][-l loop] addr");
+
+
+
exit(1);
+
+
+
}
+
+
+
+
+
+
main(int argc, char **argv)
+
+
+
{
+
+
+
u_int hops = 1, /* as defined in rfc2553 */
+
+
+
loop = 1, /* as defined in rfc2553 */
+
+
+
ifi = 0;
+
+
+
int s, cc, ch;
+
+
+
char buf[1024];
+
+
+
struct in6_addr addr6;
+
+
+
extern char *optarg;
+
+
+
extern int optind;
+
+
+
+
+
+
addr6 = in6addr_any;
+
+
+
if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+
+
+
Perror("socket");
+
+
+
while ((ch = getopt(argc, argv, "h:t:l:i:")) != -1)
+
+
+
switch(ch) {
+
+
+
case 'h':
+
+
+
case 't':
+
+
+
hops = atoi(optarg);
+
+
+
break;
+
+
+
case 'l':
+
+
+
loop = atoi(optarg);
+
+
+
break;
+
+
+
case 'i':
+
+
+
if (sscanf(optarg, "%u\0", &ifi) != 1) {
+
+
+
ifi = if_nametoindex(optarg);
+
+
+
if (ifi == 0)
+
+
+
Usage();
+
+
+
}
+
+
+
break;
+
+
+
default:
+
+
+
Usage();
+
+
+
}
+
+
+
argc -= optind;
+
+
+
argv += optind;
+
+
+
if (argc != 1 || inet_pton(AF_INET6, *argv, &addr6) <= 0)
+
+
+
Usage();
+
+
+
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+
+
+
&hops, sizeof(hops)) < 0)
+
+
+
Perror("setsockopt IPV6_MULTICAST_HOPS");
+
+
+
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+
+
+
&loop, sizeof(loop)) < 0)
+
+
+
Perror("setsockopt IPV6_MULTICAST_LOOP");
+
+
+
if (ifi && (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+
+
+
&ifi, sizeof(u_int)) < 0))
+
+
+
Perror("setsockopt IPV6_MULTICAST_IF");
+
+
+
+
+
+
#ifdef SIN6_LEN
+
+
+
sin6.sin6_len = sizeof(sin6);
+
+
+
#endif
+
+
+
sin6.sin6_family = AF_INET6;
+
+
+
sin6.sin6_addr = addr6;
+
+
+
sin6.sin6_port = htons(54321);
+
+
+
+
+
+
for (;;) {
+
+
+
cc = read(0, buf, 1024);
+
+
+
if (cc < 0)
+
+
+
Perror("read file");
+
+
+
if (cc == 0) {
+
+
+
fprintf(stderr, ".\n", cc);
+
+
+
exit (0);
+
+
+
}
+
+
+
if (sendto(s, buf, cc, 0,
+
+
+
(struct sockaddr *)&sin6, sizeof(sin6)) < 0)
+
+
+
Perror("sendto");
+
+
+
fprintf(stderr, "-%d->\n", cc);
+
+
+
}
+
 
   
 
   
 
 
}
+
  1| #include <sys/types.h>
 +
  2| #include <sys/socket.h>
 +
  3| #include <netinet/in.h>
 +
  4| #include <arpa/inet.h>
 +
  5| #include <stdio.h>
 +
  6| #include <unistd.h>
 +
  7| 
 +
  8| struct sockaddr_in6 sin6;
 +
  9| 
 +
10| #define IPPORT 54321
 +
11| 
 +
12| void Perror(const char *c)
 +
13| {
 +
14|    perror(c);
 +
15|    exit(1);
 +
16| }
 +
17| 
 +
18| void Usage ()
 +
19| {
 +
20|    fprintf(stderr, "%s\n", "Usage: in2multi6 [-i interface][-h hop][-l loop] addr");
 +
21|    exit(1);
 +
22| }
 +
23| 
 +
24| int main(int argc, char **argv)
 +
25| {
 +
26| u_int hops = 1,      /* as defined in rfc2553 */
 +
27|      loop = 1,      /* as defined in rfc2553 */
 +
28|      ifi = 0;
 +
29| int s, cc, ch;
 +
30| char buf[1024];
 +
31| struct in6_addr addr6;
 +
32| extern char *optarg;
 +
33| extern int optind;
 +
34| 
 +
35|    addr6 = in6addr_any;
 +
36|    if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
 +
37|      Perror("socket");
 +
38|    while ((ch = getopt(argc, argv, "h:t:l:i:")) != -1)
 +
39|      switch(ch) {
 +
40|      case 'h':
 +
41|      case 't':
 +
42|          hops = atoi(optarg);
 +
43|          break;
 +
44|      case 'l':
 +
45|          loop = atoi(optarg);
 +
46|          break;
 +
47|      case 'i':
 +
48|          if (sscanf(optarg, "%u\0", &ifi) != 1) {
 +
49|            ifi = if_nametoindex(optarg);
 +
50|            if (ifi == 0)
 +
51|                Usage();
 +
52|          }
 +
53|          break;
 +
54|      default:
 +
55|          Usage();
 +
56|    }
 +
57|    argc -= optind;
 +
58|    argv += optind;
 +
59|    if (argc != 1 || inet_pton(AF_INET6, *argv, &addr6) <= 0)
 +
60|      Usage();
 +
61|    if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
 +
62|                  &hops, sizeof(hops)) < 0)
 +
63|      Perror("setsockopt IPV6_MULTICAST_HOPS");
 +
64|    if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
 +
65|                  &loop, sizeof(loop)) < 0)
 +
66|      Perror("setsockopt IPV6_MULTICAST_LOOP");
 +
67|    if (ifi && (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF,
 +
68|                          &ifi, sizeof(u_int)) < 0))
 +
69|      Perror("setsockopt IPV6_MULTICAST_IF");
 +
70| 
 +
71| #ifdef SIN6_LEN
 +
72|    sin6.sin6_len = sizeof(sin6);
 +
73| #endif
 +
74|    sin6.sin6_family = AF_INET6;
 +
75|    sin6.sin6_addr = addr6;
 +
76|    sin6.sin6_port = htons(54321);
 +
77| 
 +
78|    for (;;) {
 +
79|      cc = read(0, buf, 1024);
 +
80|      if (cc < 0)
 +
81|          Perror("read file");
 +
82|      if (cc == 0) {
 +
83|          fprintf(stderr, ".\n", cc);
 +
84|          exit (0);
 +
85|      }
 +
86|      if (sendto(s, buf, cc, 0,
 +
87|                  (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
 +
88|          Perror("sendto");
 +
89|      fprintf(stderr, "-%d->\n", cc);
 +
90|    }
 +
91| }
 +
{{suivi| mini-ping | mini-ping | Programmation avancée | Programmation avancée }}

Latest revision as of 10:47, 13 April 2006

mini-ping Table des matières Programmation avancée

La programmation avec les groupes multicast n'est pas standardisée en IPv4. La nouvelle API "socket" propose un ensemble de structures et appels systèmes pour étendre l'interface de programmation sockets aux applications utilisant le multicast. Cet exemple va illustrer ce point.

Le but des deux programmes est d'échanger des données par multicast. Le programme in2multi6 diffuse les données lues en entrée ("standard input") vers un groupe multicast donné. Le programme multi2out6 écoute les paquets transmis dans ce groupe et les copie sur la sortie standard ("stdout").

multi2out6

Pour ce faire multi2out6 va faire des appels systèmes qui vont produire des paquets d'abonnement (et de désabonnement) à un groupe multicast. L'abonnement sera réalisé grâce à l'envoi de deux messages ICMPv6 successifs de type 131, c'est-à-dire des "rapports d'abonnement". Puis les messages émis dans le groupe sont reçus par le programme. Lorsque le programme s'arrête, le code de l'interface va automatiquement provoquer l'émission d'un message de réduction d'un groupe de multicast (132).

Le programme multi2out6 est appelé de la manière suivante :

multi2out6 [-i interface] <adresse de groupe multicast>

Voici le code complet du programme. Le port utilisé (ligne 15) est quelconque mais ne doit bien sûr pas correspondre à un service déjà existant.

 1| #include <sys/types.h>
 2| #include <sys/socket.h>
 3| #include <netinet/in.h>
 4| #include <arpa/inet.h>
 5| #include <stdio.h>
 6| #include <signal.h>
 7| #include <unistd.h>
 8|  
 9| #ifndef SO_REUSEPORT
10| #define SO_REUSEPORT SO_REUSEADDR
11| #endif
12|   
13| struct sockaddr_in6 sin6;
14|  
15| #define IPPORT 54321
16|  
17| void Perror(const char *c)
18| {
19|    perror(c);
20|    exit(1);
21| }
22|  
23| void Usage ()
24| {
25|    fprintf(stderr, "%s\n", "Usage: multi2out6 [-i interface] addr");
26|    exit(1);
27| }
28|  
29| void BrokenPipe(int Signal)
30| {
31|    signal(SIGPIPE, BrokenPipe);
32|    return;
33| }
34| 
 

La partie principale du programme traite les options éventuelles. En fait, il n'y en a qu'une, le choix de l'interface à abonner au groupe multicast. Une fois ces operations effectuées, il faut créer et attacher une socket à une adresse. Ces opérations sont réalisées par l'utilisation des fonctions classiques socket et bind.

On utilise une structure de données spéciale pour stocker l'adresse multicast du groupe (définie dans netinet/in.h) :

struct ipv6_mreq {
struct in6_addr ipv6mr_multiaddr; /* IPv6 mcast address of group */
unsigned int ipv6mr_interface; /* local IPv6 address of interface */
};



35| int main(int argc, char **argv)
36| {
37| struct ipv6_mreq mreq;
38| int cc, ccb, ch, s;
39| char buf[10240];
40| u_int one = 1;
41| u_int ifi = 0;
42|  
43|    signal(SIGPIPE, BrokenPipe);
44|    while ((ch = getopt(argc, argv, "i:")) != -1)
45|       switch(ch) {
46|       case 'i':
47|          if (sscanf(optarg, "%u\0", &ifi) != 1 &&
48|                       (ifi = if_nametoindex(optarg)) == 0)
49|             Usage();
50|          break;
51|       default:
52|          Usage();
53|       }
54|    argc -= optind;
55|    argv += optind;
56|    if (argc != 1)
57|       Usage();
58|  
59|    if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
60|       Perror("socket");
61|    setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
62|  
63| #ifdef SIN6_LEN
64|    sin6.sin6_len = sizeof(sin6);
65| #endif
66|    sin6.sin6_family = AF_INET6;
67|    sin6.sin6_port = htons(IPPORT);
68|    if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
69|       Perror("bind");

La fonction inet_pton va permettre la conversion du nom de groupe passé en option sous une forme textuelle (par exemple ff12::1234:5678) en forme numérique. Le résultat est directement stocké dans la variable mreq qui sera utilisée par la commande setsockop. On passe en paramètre à cette fonction l'option IPV6_JOIN_GROUP avec la variable mreq. À partir de ce moment, il y a émission de deux messages d'abonnement. La boucle qui suit va permettre la lecture des informations envoyées sur le groupe auquel on vient de s'abonner et les afficher sur la sortie standard ainsi que leur longueur sur la sortie erreur standard.


70|    if (inet_pton(AF_INET6, *argv, &mreq.ipv6mr_multiaddr) != 1)
71|       Usage();
72|    mreq.ipv6mr_interface = ifi;
73|    if (setsockopt(s,IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
74|       Perror("setsockopt IPV6_JOIN_GROUP");
75|    for (;;) {
76|       cc = read(s, buf, 10240);
77|       if (cc < 0)
78|          Perror("read socket");
79|       if (cc == 0) {
80|          fprintf(stderr, "..\n");
81|          exit (0);
82|       }
83|       ccb = write(1, buf, cc);
84|       if (ccb != cc)
85|          Perror("write file");
86|       fprintf(stderr, "<-%d-\n", cc);
87|    }
88| }

Lorsque le programme s'arrête, un close(s) implicite a lieu, et le code de l'interface va envoyer un message de réduction de groupe si elle est la dernière à avoir envoyé un rapport d'abonnement au groupe.

in2multi6

Le programme est appelé de la manière suivante :

in2multi6 [-i interface][-h max-hop-count][-l loop] <adresse de groupe multicast>

Le code est relativement simple, principalement une analyse des arguments, le positionnement d'option et une boucle lecture--émission. En effet il n'est pas nécessaire de s'abonner pour faire de l'émission multicast.

Il y a quatre arguments, trois optionnels qui sont l'interface d'émission (nom ou index numérique), le "ttl" mis dans les paquets multicast (voir le manuel de la primitive readv), et un drapeau qui sert à dire si la machine émettrice reçoit ou non les paquet émis. Le dernier argument est l'adresse du groups sous forme numérique.

Voici le code complet du programme. Le port utilisé (ligne 10) est naturellement celui de in2multi6.


 1| #include <sys/types.h>
 2| #include <sys/socket.h>
 3| #include <netinet/in.h>
 4| #include <arpa/inet.h>
 5| #include <stdio.h>
 6| #include <unistd.h>
 7|  
 8| struct sockaddr_in6 sin6;
 9|  
10| #define IPPORT 54321
11|  
12| void Perror(const char *c)
13| {
14|    perror(c);
15|    exit(1);
16| }
17|  
18| void Usage ()
19| {
20|    fprintf(stderr, "%s\n", "Usage: in2multi6 [-i interface][-h hop][-l loop] addr");
21|    exit(1);
22| }
23|  
24| int main(int argc, char **argv)
25| {
26| u_int hops = 1,       /* as defined in rfc2553 */
27|       loop = 1,       /* as defined in rfc2553 */
28|       ifi = 0;
29| int s, cc, ch;
30| char buf[1024];
31| struct in6_addr addr6;
32| extern char *optarg;
33| extern int optind;
34|  
35|    addr6 = in6addr_any;
36|    if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
37|       Perror("socket");
38|    while ((ch = getopt(argc, argv, "h:t:l:i:")) != -1)
39|       switch(ch) {
40|       case 'h':
41|       case 't':
42|          hops = atoi(optarg);
43|          break;
44|       case 'l':
45|          loop = atoi(optarg);
46|          break;
47|       case 'i':
48|          if (sscanf(optarg, "%u\0", &ifi) != 1) {
49|             ifi = if_nametoindex(optarg);
50|             if (ifi == 0)
51|                Usage();
52|          }
53|          break;
54|       default:
55|          Usage();
56|    }
57|    argc -= optind;
58|    argv += optind;
59|    if (argc != 1 || inet_pton(AF_INET6, *argv, &addr6) <= 0)
60|       Usage();
61|    if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
62|                   &hops, sizeof(hops)) < 0)
63|       Perror("setsockopt IPV6_MULTICAST_HOPS");
64|    if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
65|                   &loop, sizeof(loop)) < 0)
66|       Perror("setsockopt IPV6_MULTICAST_LOOP");
67|    if (ifi && (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF,
68|                           &ifi, sizeof(u_int)) < 0))
69|       Perror("setsockopt IPV6_MULTICAST_IF");
70|  
71| #ifdef SIN6_LEN
72|    sin6.sin6_len = sizeof(sin6);
73| #endif
74|    sin6.sin6_family = AF_INET6;
75|    sin6.sin6_addr = addr6;
76|    sin6.sin6_port = htons(54321);
77|  
78|    for (;;) {
79|       cc = read(0, buf, 1024);
80|       if (cc < 0)
81|          Perror("read file");
82|       if (cc == 0) {
83|          fprintf(stderr, ".\n", cc);
84|          exit (0);
85|       }
86|       if (sendto(s, buf, cc, 0,
87|                  (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
88|          Perror("sendto");
89|       fprintf(stderr, "-%d->\n", cc);
90|    }
91| }
mini-ping Table des matières Programmation avancée
Personal tools