Forum Programmation.c Raw socket (BUG)

Posté par  .
Étiquettes : aucune
0
19
jan.
2009
Bonjour à tous

J'ai un petit souci avec mon programme. Je suis en train de développer un nouveau protocole sur ethernet. J'utilise donc des RAW socket.

Tout va super bien. J'atteint 1,6 Gb/s sur le loopback avec des packets de 1500 octet (ethernet oblige, si pas de jumbo frame ^_^)

Mon problème vient de la fermeture de ladite socket. La fonction recvmsg ne me retourne jamais -1 quand je ferme la socket.

J'ai isolé le bug ans un petit programme que voici (désolé pour le format, j'ai du mal à le formater) :



#include pthread.h
#include sys/socket.h
#include netpacket/packet.h
#include net/ethernet.h
#include arpa/inet.h
#include sys/ioctl.h
#include linux/if.h
#include string.h
#include stdio.h
#include errno.h

int sock;
struct msghdr packet = {0};
struct sockaddr_ll addr = {0};
struct iovec iov = {0};
struct ifreq ifr = { 0 };
char *name="lo";
char buffer[1500] = {0};
pthread_t tid;
pthread_mutex_t mutex;
pthread_cond_t cond;
int recivelen;

static void *reciver(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
while((recivelen=recvmsg(sock,&packet,0))>0)
printf("recvmsg return : %d\n",recivelen);
}

int main()
{
int errorCondition = 0;
// overture de la socket
sock = socket ( PF_PACKET, SOCK_DGRAM, htons ( ETH_P_ALL ) );
if ( !sock )
return -1;
// attachement sur le loopback
errorCondition = setsockopt ( sock,SOL_SOCKET,SO_BINDTODEVICE,name,2);
if ( errorCondition )
return errorCondition;
// recupère l'index de l'interface. Pas utile ici mais on ne sait jamais
strcpy ( ifr.ifr_name, name);
errorCondition = ioctl ( sock, SIOCGIFINDEX, &ifr );
if ( errorCondition )
return errorCondition;
//construction du packet
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifr.ifr_ifindex;
addr.sll_halen = ETH_ALEN;
addr.sll_protocol = htons ( ETH_P_ALL );
packet.msg_name=&addr
packet.msg_namelen = sizeof ( struct sockaddr_ll );
packet.msg_iov = &iov
packet.msg_iovlen = 1;
iov.iov_base=&buffer
iov.iov_len=1500;
packet.msg_control = NULL;
packet.msg_controllen = 0;
packet.msg_flags = 0;
// initialisation du mutex pour une sychronisation
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
// démarrage du thread d'écoute
pthread_mutex_lock(&mutex);
pthread_create(&tid,NULL,&reciver,NULL);
// attente du bon démarrage, sinon ça fausse le test
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
// tentative de fermeture avec shutdown, l'erreur est normale
errorCondition = shutdown(sock,SHUT_RDWR);
if(errorCondition)
printf("shutdown fail. errno : %d\n",errno);
// fermeture avec close. Pas d'erreurs mais est-ce vrai ?
errorCondition = close(sock);
if(errorCondition)
printf("close fail. errno : %d\n",errno);
// attente de la fin du thread
pthread_join(tid,NULL); // BUG, le thread ne fini jamais, il continue d'ecouter alors que sa socket est fermé
}




a compiller avec l'option -lpthread

Pouvez vous m'aider, ou est-ce un bug du noyau linux ?

Merci a vous
  • # Threads

    Posté par  (site web personnel) . Évalué à 3.

    Commençons par les threads : tu ferme la socket pendant sont utilisation par un autre thread. Il te faudrait un mutex dessus pour faire propre.

    je pense que ton problème est l'utilisation de shutdown. C'est une fonction servant à fermer une connexion, or les socket raw sont des socket non connectées.

    Ce que tu peux faire c'est de la fermer avec un close, auquel cas recvmsg te retournera une erreur, ce qui est normal.
    • [^] # Re: Threads

      Posté par  . Évalué à 1.


      Commençons par les threads : tu ferme la socket pendant sont utilisation par un autre thread. Il te faudrait un mutex dessus pour faire propre.

      Oui, c'est le but. Car recvmsg est un appel bloquant, donc pour l'annuler, un close sur la socket me semblait un bonne idée.


      je pense que ton problème est l'utilisation de shutdown. C'est une fonction servant à fermer une connexion, or les socket raw sont des socket non connectées.


      Comme mis en commentaire dans mon source, c'est normal qu'il ne marche pas.



      Ce que tu peux faire c'est de la fermer avec un close, auquel cas recvmsg te retournera une erreur, ce qui est normal.


      C'est là ou ça ne marche plus. Le recvmsg ne termine pas !

      Comment faire ?
      • [^] # Re: Threads

        Posté par  (site web personnel) . Évalué à 1.

        C'est là ou ça ne marche plus. Le recvmsg ne termine pas !
        OK, j'avais mal lu.

        Oui, c'est le but.
        Non, tu ne protège pas l'accès à ta socket dans ton cas, je ne connais pas assez bien le noyau pour savoir si c'est sensé fonctionner comme tu veux.

        Car recvmsg est un appel bloquant, donc pour l'annuler, un close sur la socket me semblait un bonne idée.

        Tu ne peux pas arrêter un appel bloquant avec un close. Il te faut un signal pour ça (et encore pour recvmsg je ne sais pas).

        En fait tu est sensé faire un select dans ton while avant d'appeler recvmsg pour détecter les conditions d'arrêt.
  • # ben chez moi

    Posté par  . Évalué à 1.

    ça marche, avec même un errno bien rempli
    • [^] # Re: ben chez moi

      Posté par  . Évalué à 1.

      Ah bon ?

      Tu doit avoir du trafic sur le loopback car le programme écrit reçoit qu'un packet et de n'importe quel type (P_ETH_ALL).

      Coupe tout ce qui utilise le loopback avant de lancer le programme.
      • [^] # Re: ben chez moi

        Posté par  . Évalué à 1.

        euh cest possible, mais c'était le problème ?
        en fait c'est juste que la socket me semblait bien fermer vu que le recv me sort un beau errno (ENOTSOCK je crois)
        je retesterai
        • [^] # Re: ben chez moi

          Posté par  . Évalué à 1.

          euh autant pour moi
          juste en relisant le code, il est évidant qu'en recevant des infos par lo, le prochain recv se fera après la fermeture de la socket

          bon, si vous pouviez moinser...

          sinon c'est vrai que la solution est surement un select ou un poll
        • [^] # Re: ben chez moi

          Posté par  . Évalué à 1.

          Le but est de vérifier que la fonction recvmsg doit se couper si on ferme la socket.
          Dans le morceau de code donné, je ne boucle pas si la socket reçoit un message. Donc ça se termine bien.
          Par contre, si tu me dit que tu n'as pas de trafic et que la fonction recvmsg se termine en erreur (c'est le but), c'est que j'ai un bug avec mon noyau.

          Pourrais tu faire le test à nouveau ?

          Merci
  • # socket non bloquant...

    Posté par  . Évalué à 2.

    Peut etre que tu devrais definir ton socket non bloquant (O_NONBLOCK).
    select() est aussi ton ami.

    ---
    • [^] # Re: socket non bloquant...

      Posté par  . Évalué à 1.

      Tu lock pas deux fois ton mutex là ?
      • [^] # Re: socket non bloquant...

        Posté par  . Évalué à 1.

        Pour le mutex => non, car il est délocké avec le pthread_cond_wait

        Pour faire un recvmsg non-bloquant => non car le programme bouclerait et donc 100% de cpu. Pas bien

        Je vais regarder du coté de select. C'est une bonne alternative.
        L'inconvénient c'est que je suit dans l'obligation d'attendre le timeout lors de la fin du programme.
        C'est pour cela que je n'ai pas pensé tout de suite à l'utiliser.

        Si quelqu'un a une solution autre que select, je prend, c'est peut-être une combinaison de chaque qui fera l'affaire
  • # [ RESOLU ]

    Posté par  . Évalué à 3.

    Merci pour vos réponses. Pour ceux que cela interreserait, je poste la solution au problème.
    
    #include pthread.h
    #include sys/socket.h
    #include netpacket/packet.h
    #include net/ethernet.h
    #include arpa/inet.h
    #include sys/ioctl.h
    #include linux/if.h
    #include string.h
    #include stdio.h
    #include errno.h
    
    int sock;
    struct msghdr packet = {0};
    struct sockaddr_ll addr = {0};
    struct iovec iov = {0};
    struct ifreq ifr = { 0 };
    char *name="lo";
    char buffer[1500] = {0};
    pthread_t tid;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int recivelen;
    
    fd_set readset;
    struct timeval tv;
    
    static void *reciver(void *arg)
    {
    	int datapresent;
    
    	//synchronisation
    	pthread_mutex_lock(&mutex);
    	pthread_cond_signal(&cond);
    	pthread_mutex_unlock(&mutex);
    
    	//construction du select
    	tv.tv_sec = 1;
    	tv.tv_usec = 0;
    	FD_ZERO(&readset);
    	FD_SET(sock,&readset);
    
    	// données présentes ?
    	datapresent=select(sock+1, &readset,NULL,NULL,&tv);
    
    	if(datapresent<=0)
    	{
    
    		// non
    		printf("timeout\n");
    		return NULL;
    	}
    
    		//oui
    	recivelen=recvmsg(sock,&packet,MSG_DONTWAIT);
    	printf("recvmsg return : %d\n",recivelen);
    	return NULL;
    }
    
    int main()
    {
    	int errorCondition = 0;
    
    	// overture de la socket
    	sock = socket ( PF_PACKET, SOCK_DGRAM, htons ( ETH_P_ALL ) );
    	if ( !sock )
    		return -1;
    
    	// attachement sur le loopback
    	errorCondition = setsockopt ( sock,SOL_SOCKET,SO_BINDTODEVICE,name,2);
    	if ( errorCondition )
    		return errorCondition;
    
    	// recupère l'index de l'interface. Pas utile ici mais on ne sait jamais
    	strcpy ( ifr.ifr_name, name);
    	errorCondition = ioctl ( sock, SIOCGIFINDEX, &ifr );
    	if ( errorCondition )
    		return errorCondition;
    
    	//construction du packet
    	addr.sll_family = AF_PACKET;
    	addr.sll_ifindex = ifr.ifr_ifindex;
    	addr.sll_halen = ETH_ALEN;
    	addr.sll_protocol = htons ( ETH_P_ALL );
    	packet.msg_name=&addr;
    	packet.msg_namelen = sizeof ( struct sockaddr_ll );
    	packet.msg_iov = &iov;
    	packet.msg_iovlen = 1;
    	iov.iov_base=&buffer;
    	iov.iov_len=1500;
    	packet.msg_control = NULL;
    	packet.msg_controllen = 0;
    	packet.msg_flags = 0;
    
    	// initialisation du mutex pour une sychronisation
    	pthread_mutex_init(&mutex,NULL);
    	pthread_cond_init(&cond,NULL);
    
    	// démarrage du thread d'écoute
    	pthread_mutex_lock(&mutex);
    	pthread_create(&tid,NULL,&reciver,NULL);
    
    	// attente du bon démarrage, sinon ça fausse le test
    	pthread_cond_wait(&cond,&mutex);
    	pthread_mutex_unlock(&mutex);
    
    	// attente de la fin du thread 
    	pthread_join(tid,NULL);
    
    	// fermeture avec close.
    	errorCondition = close(sock);
    	if(errorCondition)
    		printf("close fail. errno : %d\n",errno);
    }
    
    
    

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.