IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Réseau C Discussion :

Problème avec IPPROTO


Sujet :

Réseau C

  1. #1
    Membre confirmé Avatar de yashiro
    Inscrit en
    Mars 2004
    Messages
    214
    Détails du profil
    Informations forums :
    Inscription : Mars 2004
    Messages : 214
    Par défaut Problème avec IPPROTO
    Voici mon problème:
    Je voudrais envoyer un paquet IP de type ICMP-EHO (un ping quoi) par le biais d'une socket raw qui me permettra de spécifier
    l'en-tête IP à utiliser (Je ne voudrais pas que le protoole IP me génère une en-tête IP). Pour cela j'ai lu dans le man SOCK_RAW qu'il faut actier l'option IP_HDRINCL pour povoir soi-même spécifier son en-tête IP et que cette option est directementactivée lorsqu'on utilise le protocole IPPROTO_RAW dans la fontion soket (sous linux). Je crée donc ma socket

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     // Création de la soket
     sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
     if(sock == -1) { printf("Erreur de création de la socket\n%s\n", strerror(errno)); return -1; }
    j'ai des structures IP_HDR et ICMP_HDR_ECHO déclarées ainsi

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    #ifndef __STRUCTURES_H__
    #define __STRUCTURES_H__
     
    typedef unsigned char u_char;
    typedef signed char s_char;
    typedef unsigned short u_short;
    typedef signed short s_short;
    typedef signed int s_int;
    typedef unsigned int u_int;
    typedef unsigned long u_long;
     
     
    // En-tête IP
    typedef struct iphdr {
     
     #if defined(__LITTLE_ENDIAN_BITFIELD)
      u_int ihl:4; // Longueur d'en-tête
      u_int version:4; // version
     #else
      u_char version:4; // version
      u_int ihl:4; // longueur d'en-tête
     #endif
     
     u_char tos:8; // Type de service
     u_short tot_len:16; // Longueur totale du datagramme
     u_short id:16; // Identificateur du datagramme
     u_short frag_off:16; // Offset du fragment
     u_char ttl:8; // Temps de vie
     u_char protocol:8; // Protocol Internet utilisé
     u_short check:16; // Champ de contrôle (checksum)
     u_int saddr:32; // Adresse source
     u_int daddr:32; // Adresse destination
     /*The options start here. */
    } IP_HDR;
     
     
    // Structure de l'en-tête ICMP
    typedef struct icmphdr_echo {
     u_char type:8; // Type de message ICMP
     u_char code:8; // Code du message ICMP
     u_short checksum:16; // Checksum
     u_short id:16; // Identificateur
     u_short sequence:16; // Numéro de séquence
     u_long  timestamp;
    } ICMP_HDR_ECHO;
     
    #endif
    Ma méthode consiste don à créer un tampon d'envoi dont la taille est la somme de la taille de l'en-tête IP et ICMP, je rempli ce tampon, je l'envoi par sendto et j'attends la réponse du destinataire. voici mon ode

    D'abord la fontion de remplissage de l'en-tête ICMP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    void MakeICMPECHO(char* sendbuf, int paquetId){
     
     ICMP_HDR_ECHO* icmp = (ICMP_HDR_ECHO*)sendbuf;
     
      // Remplissage icmphdr_echo
     memset(sendbuf, 0, sizeof(ICMP_HDR_ECHO));
     icmp->type = 8; // Requête ICMP de type echo
     icmp->code = 0;
     icmp->checksum = 0;
     icmp->id = 0;
     icmp->sequence = 0;
     icmp->timestamp = 100000;
     
     // Mise en place des Checksum
     icmp->checksum = CheckSum((unsigned short *)sendbuf, sizeof(ICMP_HDR_ECHO));
    }
    Ensuite celle de remplissge du tampon de IP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    void MakeIPICMP(char* sendbuf, int paquetId, char* s, char* d){
     
     IP_HDR* ip = (IP_HDR*)sendbuf;
     
     // Remplissage ip
     memset(sendbuf, 0, sizeof(IP_HDR));
     ip->ihl = sizeof(IP_HDR);
     ip->version = 4;
     ip->tos = 0;
     ip->tot_len = sizeof(IP_HDR) + sizeof(ICMP_HDR_ECHO);
     ip->id = paquetId;
     ip->frag_off = (u_short)(ip + ip->ihl);
     ip->check = 0;
     ip->protocol = 1;
     ip->saddr = htonl(inet_addr(s));
     ip->daddr = htonl(inet_ntoa(d));
     
     // Mise en place des Checksum
     ip->check = CheckSum((unsigned short *)sendbuf, sizeof(IP_HDR));
    }
    enfin le programme principal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
     
    int main(int argc, char* argv[]){
     
     // Déclaration des variables utiles
     int sock;  // La raw socket
     int send, recieve; // statut d'envoi et de reception
     char sendbuficmp[sizeof(ICMP_HDR_ECHO)]; // Buffer icmp
     char sendbufip[sizeof(IP_HDR)]; // Buffer IP
     char sendbuf[sizeof(IP_HDR) + sizeof(ICMP_HDR_ECHO)];
     char* ptr; // Utilisé pour la copie des en-tête dans le tampon d'enoi sendbuf
     char recvbuf[MAX_PACKET]; // Buffer de reception
     struct icmphdr_echo* icmp; // En-tête icmp de type echo
     struct iphdr* ip; // En-tête ip
     struct icmphdr_echo* ricmp; // En-tête icmp de retour
     struct iphdr* rip; // En-tête ip de retour
     char* csource = argv[1]; // @ source
     char* cdest = argv[2]; // @ destination du datagramme
     char* cproxy = argv[3]; //  du proxy
     
     struct sockaddr_in dest, from; // Adresse du destinataire
     fd_set fdsr; // Ensemle surveillé
     struct timeval tv_timeout; // Délai de sureillance
     int surv, fromlen = sizeof(from);
     
     
     // Création de la soket
     sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
     if(sock == -1) { printf("Erreur de création de la socket\n%s\n", strerror(errno)); return -1; }
     // Préparation du paquet ICMP
     MakeICMPECHO(sendbuficmp, getpid());
     MakeIPICMP(sendbufip, getpid(), csource, cdest);
     
     // Initialisation du tampon d'envoi à 0
     memset(sendbuf, 0, sizeof(IP_HDR) + sizeof(ICMP_HDR_ECHO));
     
     // ptr pointe sur sendbuf
     ptr = sendbuf;
     
     // Copie de l'en-tête IP dans ce tampon
     memcpy(ptr, sendbufip, sizeof(IP_HDR));
     
     // On va en fin d'en-tête IP
     ptr += sizeof(IP_HDR);
     
     // Copie de l'en-tête ICMP  la suite dans le tampon (en partie données du datagramme IP)
     memcpy(ptr, sendbuficmp, sizeof(ICMP_HDR_ECHO));
     
     // Paramétrage de l'adresse d'envoi direct
     dest.sin_family = AF_INET;
     dest.sin_addr.s_addr = inet_addr(cproxy);
     
     // Envoi du message
     send = sendto(sock, sendbuf, sizeof(IP_HDR) + sizeof(ICMP_HDR_ECHO), 0, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
     if(send == -1) { printf("Erreur d'envoi\n%s\n", strerror(errno)); return -1; }
     
     // Paramétrage de l'ensemble surveillé
     FD_ZERO(&fdsr);
     FD_SET(sock, &fdsr);
     tv_timeout.tv_sec = 5;
     tv_timeout.tv_usec = 0;
     
     // Appel de la fontion de surveillane
     surv = select(sock + 1, &fdsr, NULL, NULL, &tv_timeout);
     if(surv > 0 ){
      if (FD_ISSET(sock, &fdsr)){
       printf("Données en entrées sur la socket\n");
       recieve = recvfrom(sock, recvbuf, MAX_PACKET, 0, (struct sockaddr *)&from, &fromlen);
       decode_recv(recvbuf);
      }
     }
     else{
      printf("Aucune Données en entrées sur la socket\n");
     }
     return 0;
    }
    j'apelle l'éxécutable avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ./sockraw2 @source @dest @passerelle
    mais malheureusement le destinataire ne répond pas et je sais pas pourquoi. Je me demande si mes en-tête sont bonnes ou si je rempli correctement les en-têtes ou encore si le message arrive à bon port.

  2. #2
    Membre émérite Avatar de valefor
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    711
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 711
    Par défaut
    Je te conseille d'utiliser un programme de surveillance réseau :
    - wireshark (ex-ethereal) : j'aime beaucoup
    - tcpdump : j'ai pas trop utilisé

    Tu verras donc ce qui circule et tu ne te demanderas plus si tes entêtes sont bonnes.

    Ensuite, il est conseillé dans le man de socket raw d'utiliser plutôt libpcap pour des soucis de portabilité. Fait des essais avec cela en plus.

  3. #3
    Membre confirmé Avatar de yashiro
    Inscrit en
    Mars 2004
    Messages
    214
    Détails du profil
    Informations forums :
    Inscription : Mars 2004
    Messages : 214
    Par défaut
    Ca sera difficile pour moi d'utiliser un code déja fait comme wireshark ou ethereal vu que c'est dans le cadre d'un projet académique et l'encadreur veut qu'on écrive un programme capable d'envoyer nos propres paquets sur le réseaux. Donc mon problème rteste entier quant à savoir pourquoi le destinataire ne répond pas

  4. #4
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par yashiro Voir le message
    Ca sera difficile pour moi d'utiliser un code déja fait comme wireshark ou ethereal vu que c'est dans le cadre d'un projet académique et l'encadreur veut qu'on écrive un programme capable d'envoyer nos propres paquets sur le réseaux. Donc mon problème rteste entier quant à savoir pourquoi le destinataire ne répond pas
    Teste ton programme avec tcpdump pour vérifier que

    Dans un terminal tu lances "tcpdump -i lo" (bien sûr il faut être root - enfin, avoir CAP_machin_bidule).

    Si il y a déjà de l'activité sur ta machine en local, ça risque d'être verbeux et tu aura du mal à faire le trie. Dans ce cas, essaie "tcpdump -i lo icmp".

    Dans un autre terminal fais "ping localhost", "ping une-adresse-locale" pour comprendre la sortie de tcpdump.

    Après tu lances ton programme, et tu observes les paquets dans tcpdump. Si tu ne vois pas la même chose qu'avec ping, c'est que ton programme a un bug!

    Citation Envoyé par yashiro Voir le message
    Pour cela j'ai lu dans le man SOCK_RAW qu'il faut actier l'option IP_HDRINCL pour povoir soi-même spécifier son en-tête IP et que cette option est directementactivée lorsqu'on utilise le protocole IPPROTO_RAW dans la fontion soket (sous linux). Je crée donc ma socket

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     // Création de la soket
     sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    Il fallait lire le man entièrement :
    Citation Envoyé par man sock_raw
    An IPPROTO_RAW socket is send only.

  5. #5
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par yashiro Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MakeIPICMP(... char* s, char* d){
    ...
     ip->saddr = htonl(inet_addr(s));
     ip->daddr = htonl(inet_ntoa(d));
    Tu es sûr que tu as réellement ça dans ton programme?
    Citation Envoyé par yashiro Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     char* csource = argv[1]; // @ source
     char* cdest = argv[2]; // @ destination du datagramme
     char* cproxy = argv[3]; //  du proxy
    ...
     MakeIPICMP(... csource, cdest);
    ...
     // Paramétrage de l'adresse d'envoi direct
     dest.sin_family = AF_INET;
     dest.sin_addr.s_addr = inet_addr(cproxy);
     
     // Envoi du message
     send = sendto(sock, sendbuf, sizeof(IP_HDR) + sizeof(ICMP_HDR_ECHO), 0, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
    Je n'ai pas trouvé de doc et je n'ai jamais fais ça, mais intuitivement j'aurai utilisé send plutôt que sendto.

    Quel doc indique qu'il faut utiliser sendto?

    Citation Envoyé par yashiro Voir le message
    j'apelle l'éxécutable avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ./sockraw2 @source @dest @passerelle
    Je ne comprends pas cette histoire de passerelle. Pourquoi tu t'occupes de ça?

    (Aussi, attention au jeu de caractères quand tu postes : c'est de l'UTF-8?)

  6. #6
    Membre confirmé Avatar de yashiro
    Inscrit en
    Mars 2004
    Messages
    214
    Détails du profil
    Informations forums :
    Inscription : Mars 2004
    Messages : 214
    Par défaut
    Le problème c'est que sendto s'utilise avec des socket de type stream (orientées connexion) qu'on peut créer en spécifiant le type SOCK_STREAM au lieu de SOCK_RAW.

  7. #7
    Membre chevronné
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Par défaut
    Citation Envoyé par yashiro Voir le message
    Le problème c'est que [send] s'utilise avec des socket de type stream (orientées connexion) qu'on peut créer en spécifiant le type SOCK_STREAM au lieu de SOCK_RAW.
    (J'ai corrigé le "sendto"!)

    Non, en fait send s'utilise avec des socket connectées, ce qui n'est effectivement pas le cas ici.

    (J'avais tord de penser que send irait chercher l'adresse dans le message lui-même.)

    Je ne comprends toujours pas dans quel cas tu voudrais passer à sendto une adresse différente de l'adresse destination de ton message. Pourquoi parles-tu de "passerelle"?

Discussions similaires

  1. VC++ Direct3D8, problème avec LPD3DXFONT et LPD3DTEXTURE8
    Par Magus (Dave) dans le forum DirectX
    Réponses: 3
    Dernier message: 03/08/2002, 11h10
  2. Problème avec [b]struct[/b]
    Par Bouziane Abderraouf dans le forum CORBA
    Réponses: 2
    Dernier message: 17/07/2002, 10h25
  3. Problème avec le type 'Corba::Any_out'
    Par Steven dans le forum CORBA
    Réponses: 2
    Dernier message: 14/07/2002, 18h48
  4. Problème avec la mémoire virtuelle
    Par Anonymous dans le forum CORBA
    Réponses: 13
    Dernier message: 16/04/2002, 16h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo