2012-03-28 12 views
7

Obiettivo:Qual è il processo corretto per la richiesta/risposta echo ICMP su destinazioni non raggiungibili?

Devo essere in grado di eseguire il ping di un interruttore di rete per determinare se è disponibile o meno. Lo scopo è quello di dire all'utente che il cablaggio di rete è scollegato, lo switch di rete non è disponibile o che qualche altro problema si trova all'interno del percorso di comunicazione di rete. Mi rendo conto che questo non è uno strumento di diagnosi completo, ma qualcosa è meglio di niente.

Design:

Ho programmato di usare ICMP con prese prime per inviare cinque messaggi (5) ping ad un indirizzo IPv4 in dot-notazione. Installerò un filtro ICMP sul socket e non creerò la mia intestazione IP. La trasmissione dell'ICMP avverrà attraverso il metodo sendto e la ricezione attraverso il metodo recvfrom. Ciò si verifica su un singolo thread (sebbene sia possibile utilizzare un altro thread per interrompere la trasmissione e la ricezione). La ricezione di un messaggio verrà ulteriormente filtrata associando l'ID del messaggio ricevuto all'ID trasmesso. L'ID memorizzato sarà l'ID del processo in esecuzione dell'applicazione. Se viene ricevuto un messaggio ICMP_ECHOREPLY e l'ID del messaggio e l'ID memorizzato corrispondono, allora un contatore viene incrementato fino a raggiungere cinque (4) (il contatore è a base zero). Cercherò di inviare un ping, attendere la sua risposta e ripetere questo processo cinque (5) volte.

Il problema:

Dopo aver implementato il mio disegno, ogni volta che il ping di un particolare indirizzo valido di rete (ad esempio 192.168.11.15) con un partecipante rete attiva, ricevo messaggi ICMP_ECHOREPLY per ciascuno dei cinque (5) ping . Tuttavia, ogni volta che eseguo il ping di un indirizzo di rete valido (ad esempio 192.168.30.30) con partecipanti di rete inattivi (il che significa che nessun dispositivo è connesso al particolare indirizzo), ottengo uno (1) ICMP_DEST_UNREACH e quattro (4) messaggi ICMP_ECHOREPLY. L'ID nei messaggi di risposta corrisponde all'ID archiviato nel software. Ogni volta che eseguo un 'ping 192.168.30.30' dalla riga di comando, ottengo 'Da 192.168.40.50 icmp_seq = xx Host di destinazione irraggiungibile'. Non dovrei ricevere messaggi ICMP_DEST_UNREACH invece dei messaggi ICMP_ECHOREPLY?

Il Codice:

Ping.h:

#include <netinet/in.h> 
#include <linux/ip.h> 
#include <linux/ipmc.h> 
#include <arpa/inet.h> 
#include <cstdio> 
#include <cstdlib> 
#include <stdint.h> 
#include <time.h> 
#include <errno.h> 
#include <string> 
#include <cstring> 
#include <netdb.h> 

class Ping 
{ 
    public: 
     Ping(std::string host) : _host(host) {} 
     ~Ping() {} 

     void start() 
     { 
      int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
      if(sock < 0) 
      { 
       printf("Failed to create socket!\n"); 
       close(sock); 
       exit(1); 
      } 

      setuid(getuid()); 

      sockaddr_in pingaddr; 
      memset(&pingaddr, 0, sizeof(sockaddr_in)); 
      pingaddr.sin_family = AF_INET; 

      hostent *h = gethostbyname(_host.c_str()); 
      if(not h) 
      { 
       printf("Failed to get host by name!\n"); 
       close(sock); 
       exit(1); 
      } 

      memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); 

      // Set the ID of the sender (will go into the ID of the echo msg) 
      int pid = getpid(); 

      // Only want to receive the following messages 
      icmp_filter filter; 
      filter.data = ~((1<<ICMP_SOURCE_QUENCH) | 
          (1<<ICMP_DEST_UNREACH) | 
          (1<<ICMP_TIME_EXCEEDED) | 
          (1<<ICMP_REDIRECT) | 
          (1<<ICMP_ECHOREPLY)); 
      if(setsockopt(sock, SOL_RAW, ICMP_FILTER, (char *)&filter, sizeof(filter)) < 0) 
      { 
       perror("setsockopt(ICMP_FILTER)"); 
       exit(3); 
      } 

      // Number of valid echo receptions 
      int nrec = 0; 

      // Send the packet 
      for(int i = 0; i < 5; ++i) 
      { 
       char packet[sizeof(icmphdr)]; 
       memset(packet, 0, sizeof(packet)); 

       icmphdr *pkt = (icmphdr *)packet; 
       pkt->type = ICMP_ECHO; 
       pkt->code = 0; 
       pkt->checksum = 0; 
       pkt->un.echo.id = htons(pid & 0xFFFF); 
       pkt->un.echo.sequence = i; 
       pkt->checksum = checksum((uint16_t *)pkt, sizeof(packet)); 

       int bytes = sendto(sock, packet, sizeof(packet), 0, (sockaddr *)&pingaddr, sizeof(sockaddr_in)); 
       if(bytes < 0) 
       { 
        printf("Failed to send to receiver\n"); 
        close(sock); 
        exit(1); 
       } 
       else if(bytes != sizeof(packet)) 
       { 
        printf("Failed to write the whole packet --- bytes: %d, sizeof(packet): %d\n", bytes, sizeof(packet)); 
        close(sock); 
        exit(1); 
       } 

       while(1) 
       { 
        char inbuf[192]; 
        memset(inbuf, 0, sizeof(inbuf)); 

        int addrlen = sizeof(sockaddr_in); 
        bytes = recvfrom(sock, inbuf, sizeof(inbuf), 0, (sockaddr *)&pingaddr, (socklen_t *)&addrlen); 
        if(bytes < 0) 
        { 
         printf("Error on recvfrom\n"); 
         exit(1); 
        } 
        else 
        { 
         if(bytes < sizeof(iphdr) + sizeof(icmphdr)) 
         { 
          printf("Incorrect read bytes!\n"); 
          continue; 
         } 

         iphdr *iph = (iphdr *)inbuf; 
         int hlen = (iph->ihl << 2); 
         bytes -= hlen; 

         pkt = (icmphdr *)(inbuf + hlen); 
         int id = ntohs(pkt->un.echo.id); 
         if(pkt->type == ICMP_ECHOREPLY) 
         { 
          printf(" ICMP_ECHOREPLY\n"); 
          if(id == pid) 
          { 
           nrec++; 
           if(i < 5) break; 
          } 
         } 
         else if(pkt->type == ICMP_DEST_UNREACH) 
         { 
          printf(" ICMP_DEST_UNREACH\n"); 
          // Extract the original data out of the received message 
          int offset = sizeof(iphdr) + sizeof(icmphdr) + sizeof(iphdr); 
          if(((bytes + hlen) - offset) == sizeof(icmphdr)) 
          { 
           icmphdr *p = reinterpret_cast<icmphdr *>(inbuf + offset); 
           id = ntohs(p->un.echo.id); 
           if(origid == pid) 
           { 
            printf("  IDs match!\n"); 
            break; 
           } 
          } 
         } 
        } 
       } 
      } 

      printf("nrec: %d\n", nrec); 
     } 

    private: 
     int32_t checksum(uint16_t *buf, int32_t len) 
     { 
      int32_t nleft = len; 
      int32_t sum = 0; 
      uint16_t *w = buf; 
      uint16_t answer = 0; 

      while(nleft > 1) 
      { 
       sum += *w++; 
       nleft -= 2; 
      } 

      if(nleft == 1) 
      { 
       *(uint16_t *)(&answer) = *(uint8_t *)w; 
       sum += answer; 
      } 

      sum = (sum >> 16) + (sum & 0xFFFF); 
      sum += (sum >> 16); 
      answer = ~sum; 

      return answer; 
     } 

     std::string _host; 
}; 

main.cpp:

#include "Ping.h" 

int main() 
{ 
//  Ping ping("192.168.11.15"); 
    Ping ping("192.168.30.30"); 
    ping.start(); 

    while(1) sleep(10); 
} 

Per compilare, basta digitare 'g ++ -o main.cpp ping' in la riga di comando di una scatola Linux, e dovrebbe compilare (cioè se tutto il codice sorgente è installato).

Conclusione:

Qualcuno può dirmi il motivo per cui sto ricevendo un (1) ICMP_DEST_UNREACH e quattro (4) i messaggi ICMP_ECHOREPLY da un dispositivo che non è su quel particolare indirizzo di rete?

NOTA: è possibile modificare l'indirizzo IP di rete dal file main.cpp. Basta modificare l'IP su un dispositivo effettivamente esistente sulla rete o su un dispositivo che non esiste sulla rete.

Inoltre, non mi interessano le critiche sullo stile di codifica. So che non è bello, ha un casting in stile "C" misto a cast di C++, ha una scarsa gestione della memoria, ecc., Ma questo è solo un codice prototipo. Non è pensato per essere carino.

+0

La struttura ICMP_ECHO_REPLY ha lo stesso indirizzo dell'indirizzo di destinazione? O ha il router intermedio? – Beached

+0

Se si utilizza '/ bin/ping -c5 192.168.30.30', cosa ottieni? La stampa del contenuto del pacchetto di risposta può fornire ulteriori informazioni. Se hai risolto il problema da solo, per favore pubblica ciò che hai imparato. –

+0

Sembra più un problema di rete che una domanda di programmazione. – cxxl

risposta

4

Ok ho trovato l'errore. Guarda queste due righe.

int bytes = sendto(sock, packet, sizeof(packet), 0, (sockaddr *)&pingaddr, sizeof(sockaddr_in)); 

bytes = recvfrom(sock, inbuf, sizeof(inbuf), 0, (sockaddr *)&pingaddr, (socklen_t *)&addrlen); 

entrambe le funzioni utilizza pingaddr puntatore come parametro, ma questo dovrebbe evitare perché nella funzione sendto() viene utilizzato per indicare l'IP di destinazione del pacchetto ICMP ma nel recvfrom() è usato per recuperare l'IP dell'host che è rispondere.

Diciamo che pingaddr è impostato con un IP non raggiungibile. Dopo il primo ICMP_REQUEST il primo gateway risponderà con un ICMP_DEST_UNREACH e ... ecco che arriva l'errore ... quando viene chiamato recvfrom, la struttura di pingaddr verrà sovrascritta con l'IP del gateway.

SO ... dal secondo ping si indicherà l'IP del gateway che, ovviamente, esiste e risponderà con un ICMP_ECHOREPLY.

SOLUZIONE:

evita passare lo stesso puntatore sockaddr_in struttura sia sendto() e recvfrom().

Problemi correlati