2012-03-28 12 views
7

Objetivo:¿Cuál es el proceso adecuado para la solicitud/respuesta de eco ICMP en destinos inalcanzables?

Necesito poder hacer ping a un conmutador de red para determinar si está disponible o no. Esto está destinado a decirle al usuario que el cableado de la red está desenchufado, que el interruptor de red no está disponible o que existe algún otro problema dentro de la ruta de comunicación de la red. Me doy cuenta de que esta no es una herramienta de diagnóstico integral, pero algo es mejor que nada.

Diseño:

que pensaban utilizar ICMP con conectores directos para enviar mensajes de cinco (5) de ping a una dirección IPv4 en la notación de puntos. Configuraré un filtro ICMP en el socket y no crearé mi propio encabezado IP. La transmisión del ICMP se realizará a través del método sendto y la recepción a través del método recvfrom. Esto ocurrirá en un solo hilo (aunque otro hilo se puede usar para separar la transmisión y la recepción). La recepción de un mensaje se filtrará además haciendo coincidir la ID del mensaje recibido con la ID que se transmitió. El ID almacenado será el ID del proceso en ejecución de la aplicación. Si se recibe un mensaje ICMP_ECHOREPLY y la ID del mensaje y la ID almacenada coinciden, entonces un contador se incrementa hasta que se hayan alcanzado cinco (4) (el contador está basado en cero). Intentaré enviar un ping, esperar su respuesta y repetir este proceso cinco (5) veces.

El problema:

Después de haber implementado mi diseño, cada vez que ping a una dirección particular válida de red (por ejemplo 192.168.11.15) con un participante de red activa, recibo mensajes ICMP_ECHOREPLY para cada uno de los cinco (5) pings . Sin embargo, siempre que hago ping a una dirección de red válida (digamos 192.168.30.30) con participantes inactivos de la red (es decir, ningún dispositivo está conectado a la dirección particular), recibo un (1) mensaje ICMP_DEST_UNREACH y cuatro (4) mensajes ICMP_ECHOREPLY. La ID en los mensajes de respuesta coincide con la ID almacenada en el software. Cada vez que realizo un 'ping 192.168.30.30' desde la línea de comando, obtengo 'De 192.168.40.50 icmp_seq = xx Host de destino inalcanzable'. ¿No se supone que debo recibir mensajes ICMP_DEST_UNREACH en lugar de mensajes ICMP_ECHOREPLY?

el código:

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

Para compilar, sólo tiene que escribir 'g ++ main.cpp -o ping' en la línea de comandos de un cuadro Linux, y debería compilarse (es decir, si todo el código fuente está instalado).

Conclusión:

Puede alguien decirme por qué estoy recibiendo un (1) ICMP_DEST_UNREACH y cuatro (4) mensajes ICMP_ECHOREPLY desde un dispositivo que no está en esa dirección de red en particular?

NOTA: Puede cambiar la dirección IP de red del archivo main.cpp. Simplemente cambie la IP a un dispositivo que realmente exista en su red o un dispositivo que no exista en su red.

Tampoco me interesan las críticas sobre el estilo de codificación. Sé que no es bonito, tiene una mezcla de estilo 'C' mezclada con moldes de C++, tiene mala administración de memoria, etc., pero esto es solo código de prototipo. No es para ser bonita.

+0

¿La estructura ICMP_ECHO_REPLY tiene la misma dirección que la dirección de destino? ¿O tiene el enrutador intermedio? – Beached

+0

Si usa '/ bin/ping -c5 192.168.30.30', ¿qué obtiene? La impresión del contenido del paquete de respuesta puede proporcionar más información. Si resuelves el problema tú mismo, publica lo que has aprendido. –

+0

Esto suena más como un problema de red que una pregunta de programación. – cxxl

Respuesta

4

Ok, he encontrado el error. Mira estas dos líneas.

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

ambas funciones utiliza pingaddr puntero como parámetro, pero esto debe evitarse ya que en la función sendto() se utiliza para indicar la IP de destino del paquete ICMP pero en el recvfrom() se utiliza para recuperar la IP de la máquina que está respondiendo

Digamos pingaddr se establece con un IP no alcanzable. Después de su primer ICMP_REQUEST, la primera puerta de enlace le responderá con un ICMP_DEST_UNREACH y ... aquí viene el error ... cuando se llama a recvfrom, la estructura de pingaddr se sobrescribirá con la IP de la puerta de enlace.

SO ... desde el segundo ping estarás apuntando a la puerta de enlace IP que, obviamente, existe y responderá con un ICMP_ECHOREPLY.

SOLUCIÓN:

evitar pasar el mismo puntero sockaddr_in estructura tanto sendto() y recvfrom().

Cuestiones relacionadas