multicast et linux

multicast et linux - C - Programmation

Marsh Posté le 18-06-2013 à 15:49:14    

Bonjour,
 
J'ai un petit souci lors d'envoi de trame multicast sous linux. Voici un code exemple pour illustrer le problème :
 

Code :
  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <sys/types.h>
  4. #include <string.h>
  5. #include <netinet/in.h>
  6. #include <netinet/ip.h>
  7. #include <errno.h>
  8. #include <arpa/inet.h>
  9. static const char multicast_group_sz[] = "224.1.2.3";
  10. static const short port = 65007;
  11. int main()
  12. {
  13. int sock = socket( AF_INET , SOCK_DGRAM , IPPROTO_UDP );
  14. unsigned long hote_l = inet_addr( multicast_group_sz );
  15. struct sockaddr_in addr;
  16. int ret;
  17. memset( &addr , 0 , sizeof(addr ));
  18. addr.sin_family = AF_INET;
  19. addr.sin_addr.s_addr = hote_l;
  20. addr.sin_port = htons( port );
  21. struct in_addr iface_addr;
  22. iface_addr.s_addr = inet_addr("172.17.17.17" );
  23. ret = setsockopt( sock , IPPROTO_IP , IP_MULTICAST_IF , (char *)&iface_addr , sizeof ( iface_addr ) );
  24. if ( ret < 0 )
  25. {
  26.  printf("Error setsockopt %d\n", errno );
  27. }
  28. while ( 1 )
  29. {
  30.  char buffer[1024];
  31.  fgets( buffer,  1024 , stdin );
  32.  int len = strlen( buffer);
  33.  buffer[len - 1] = 0; // virer le \n et remplacer par un fin de chaine
  34.  ret = sendto( sock , buffer , len , 0 ,(struct sockaddr *) &addr, sizeof ( addr ) );
  35.  printf("sendto : %d %d\n" , ret , errno );
  36.  if ( strcmp( buffer , "QUIT" ) == 0 )
  37.  {
  38.   break;
  39.  }
  40. }
  41. close ( sock );
  42. return 0;
  43. }


 
si je commente les lignes  

Code :
  1. struct in_addr iface_addr;
  2. iface_addr.s_addr = inet_addr("172.17.17.17" );
  3. ret = setsockopt( sock , IPPROTO_IP , IP_MULTICAST_IF , (char *)&iface_addr , sizeof ( iface_addr ) );


 
mon sendto renvoie une erreur. On va dire que c'est normal, je ne peux pas envoyer en multicast sur une socket, si je ne lui ai pas précisé préalablement qu'elle doit fonctionner en multicast.
 
 
Si maintenant je remplace ces mêmes lignes par :  

Code :
  1. struct in_addr iface_addr;
  2. iface_addr.s_addr = INADDR_ANY;
  3. ret = setsockopt( sock , IPPROTO_IP , IP_MULTICAST_IF , (char *)&iface_addr , sizeof ( iface_addr ) );


 
setsockopt ne me retourne pas d'erreur ( donc on peut suppose que ca s'est bien passé ) , par contre mon sendto me renvoie également une erreur ( network unreachable me semble-t-il ).
 
Si maintenant je mets  

Code :
  1. struct in_addr iface_addr;
  2. iface_addr.s_addr = inet_addr("172.17.17.17" );
  3. ret = setsockopt( sock , IPPROTO_IP , IP_MULTICAST_IF , (char *)&iface_addr , sizeof ( iface_addr ) );


 
Comme dans le code exemple, ici 172.17.17.17 étant l'adresse IP de l'interface sur laquelle je veux dialoguer ( eth0 ) , mon sendto fonctionne bien et je vois bien le paquet UDP partir avec wireshark.
 
 
Ma question est : pourquoi cela ne marche pas si je fais un setsockopt sur INADDR_ANY, y a-t-il une option supplémentaire pour autoriser ce genre de chose ?
 
Je précise que sous windows, ce code fonctionne bien, et c'est un besoin que j'ai car je ne connais pas forcément l'adresse IP de l'interface sur laquelle je veux dialoguer.
 
Merci d'avance  :jap:

Reply

Marsh Posté le 18-06-2013 à 15:49:14   

Reply

Marsh Posté le 18-06-2013 à 16:07:55    

Bonjour ! Avez-vous défini une route pour le multicast ?

Code :
  1. route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 (en tant que root)


Si oui, il ne doit pas être nécessaire de dire quelle interface utiliser dans le programme, dans le cas contraire, il parait logique d'indiquer au système sur quelle interface réseau (eth0, lo) envoyer les trames.
 
De mémoire, sous Windows il n'est pas nécessaire d'ajouter cette route pour que cela marche (est-elle ajoutée automatiquement ?), ce qui explique la différence de fonctionnement, que j'ai en effet souvent remarquée entre les deux OS.
 
Bonne continuation !
 
PS : Avec la gestion des routes, vous pouvez diriger finement vos trames multicast vers l'une ou l'autre de vos interfaces, en faisant varier l'IP et le masque.

Reply

Marsh Posté le 18-06-2013 à 16:14:58    

super, c'est exactement ca le probleme.
 
Sous windows, les routes multicast sont définies pour toutes les interfaces par défaut, donc si je fais un send sous windows, il envoie mon paquet sur toutes les interfaces.
 
Sous linux, par défaut, aucune route n'est définie, donc si je ne précise pas l'interface, il ne sait pas où aller ( même si je n'ai qu'une interface réseau ). En ajoutant la route comme tu le précises, cela fonctionne.
 
 
Je viens de tester, ca marche. Merci pour l'info.
 
 

Reply

Marsh Posté le 18-06-2013 à 16:18:08    

J'en profite pour glisser une info supplémentaire, sur la réception cette fois-ci.
 
Le setsockopt ( ... IP_ADD_MEMBERSHIP ... ) doit se faire AVANT le bind sous linux, alors que sous windows , il faut le faire APRES le bind.
 
Je n'ai pas d'explication  à propos de cette différence d'implémentation.
 
 
 
next step  : ios  :jap:

Reply

Marsh Posté le 18-06-2013 à 16:59:33    

xilebo a écrit :

... ( même si je n'ai qu'une interface réseau ) ...


 
Justement, il y a deux interfaces sous Linux : "eth0" et la boucle locale "lo", d'où, sans doute, la confusion du système (je n'ai jamais essayé d'en désactiver une voir si cela marchait sur la seule restante).

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed