2012-03-28 11 views
7

Ziel:Was ist der richtige Prozess für ICMP Echo Request/Reply auf nicht erreichbare Ziele?

Ich muss in der Lage sein, einen Netzwerkschalter zu pingen, um zu bestimmen, ob es verfügbar ist oder nicht. Dies soll dem Benutzer sagen, dass entweder die Netzwerkverkabelung nicht angeschlossen ist, der Netzwerkschalter nicht verfügbar ist oder ein anderes Problem innerhalb des Netzwerkkommunikationsweges liegt. Mir ist klar, dass dies kein umfassendes Diagnosewerkzeug ist, aber etwas ist besser als nichts.

Entwurf:

ich mit ICMP geplant mit Raw Sockets fünf (5) Ping-Nachrichten an eine bestimmte Adresse in IPv4 Punktnotation zu senden. Ich werde einen ICMP-Filter für den Socket einrichten und werde keinen eigenen IP-Header erstellen. Die Übertragung des ICMP erfolgt über die sendto-Methode und den Empfang über die recvfrom-Methode. Dies tritt bei einem einzelnen Thread auf (obwohl ein anderer Thread verwendet werden kann, um Übertragung und Empfang auseinander zu brechen). Der Empfang einer Nachricht wird weiter gefiltert, indem die ID der empfangenen Nachricht mit der ID abgeglichen wird, die übertragen wurde. Die gespeicherte ID ist die laufende Prozess-ID der Anwendung. Wenn eine ICMP_ECHOREPLY-Nachricht empfangen wird und die ID der Nachricht und die gespeicherte ID übereinstimmen, wird ein Zähler inkrementiert, bis fünf (4) erreicht sind (der Zähler ist nullbasiert). Ich werde versuchen, einen Ping zu senden, auf seine Antwort zu warten und diesen Vorgang fünf (5) mal wiederholen.

Das Problem:

Nach meinem Design umgesetzt haben, wann immer ich eine bestimmte gültige Netzwerkadresse ping (zB 192.168.11.15) mit einem aktiven Netzwerk-Teilnehmer, erhalte ich ICMP_ECHOREPLY Nachrichten für jeden der fünf (5) Pings . Wenn ich jedoch mit inaktiven Netzwerkteilnehmern eine gültige Netzwerkadresse (zB 192.168.30.30) anpinge (was bedeutet, dass kein Gerät mit der bestimmten Adresse verbunden ist), erhalte ich einen (1) ICMP_DEST_UNREACH und vier (4) ICMP_ECHOREPLY-Nachrichten. Die ID in den Antwortnachrichten stimmt mit der in der Software gespeicherten ID überein. Immer wenn ich einen 'ping 192.168.30.30' von der Kommandozeile aus führe, bekomme ich 'Von 192.168.40.50 icmp_seq = xx Zielhost nicht erreichbar'. Soll ich nicht ICMP_DEST_UNREACH-Nachrichten anstelle von ICMP_ECHOREPLY-Nachrichten empfangen?

Der Code:

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); 
} 

Um in zu kompilieren, geben Sie einfach 'g ++ main.cpp -o ping' die Befehlszeile einer Linux-Box, und es sollte kompilieren (das heißt, wenn der gesamte Quellcode installiert ist).

Fazit:

Kann mir jemand sagen, warum ich ein empfange (1) ICMP_DEST_UNREACH und vier (4) ICMP_ECHOREPLY Nachrichten von einem Gerät, das nicht auf dieser bestimmten Netzwerkadresse?

HINWEIS: Sie können die Netzwerk-IP-Adresse aus der main.cpp-Datei ändern. Ändern Sie einfach die IP-Adresse zu einem Gerät, das tatsächlich in Ihrem Netzwerk vorhanden ist, oder zu einem Gerät, das in Ihrem Netzwerk nicht vorhanden ist.

Ich bin auch nicht interessiert an Kritik am Coding-Stil. Ich weiß, dass es nicht schön ist, hat C-Style-Casting gemischt mit C++ - Casts, hat schlechte Speicherverwaltung, usw., aber das ist nur Prototyp-Code. Es soll nicht hübsch sein.

+0

Hat die Struktur ICMP_ECHO_REPLY die gleiche Adresse wie die Zieladresse? Oder hat es den Zwischenrouter? – Beached

+0

Wenn Sie '/ bin/ping -c5 192.168.30.30' verwenden, was bekommen Sie? Das Drucken des Inhalts des Antwortpakets kann weitere Informationen bereitstellen. Wenn Sie das Problem selbst gelöst haben, schreiben Sie bitte, was Sie gelernt haben. –

+0

Das klingt eher nach einem Netzwerkproblem als nach einer Programmierfrage. – cxxl

Antwort

4

Ok ich habe den Fehler gefunden. Schau dir diese zwei Zeilen an.

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); 

beide Funktionen verwendet pingaddr Zeiger als Parameter, aber das sollte die Ziel-IP des ICMP-Paket zu zeigen, sondern in der recvfrom() wird verwendet, um wieder die IP-Adresse des Host vermieden, weil in der sendto() Funktion verwendet, die ist antworten.

Nehmen wir an, ist mit einer IP nicht erreichbar eingestellt. Nach Ihrem ersten ICMP_REQUEST antwortet Ihnen das erste Gateway mit einer ICMP_DEST_UNREACH und ... hier kommt der Fehler ... wenn recvfrom aufgerufen wird, wird die pingaddr Struktur mit der IP des Gateways überschrieben.

SO ... vom zweiten Ping werden Sie auf die Gateway-IP, die offensichtlich existiert, und wird mit einer ICMP_ECHOREPLY antworten.

LÖSUNG:

vermeiden passieren die gleiche Struktur sockaddr_in Zeiger sowohl sendto() und recvfrom().

Verwandte Themen