Hacer ping en C

Requisitos previos : ICMP | Zócalo sin procesar | Suma de comprobación de Internet | DNS Ping es una necesidad para la depuración de Internet. Ping es una herramienta básica de Internet que permite a un usuario verificar que existe una dirección IP particular y puede aceptar requests, con otras facilidades. Ping envía paquetes ICMP abriendo un socket RAW , que está separado de TCP y UDP. Dado que IP no tiene ningún mecanismo incorporado para enviar mensajes de error y control. Depende del Protocolo de mensajes de control de Internet (ICMP) para proporcionar un control de errores. Se utiliza para reportar errores y consultas de gestión. Ejemplo de Ubuntu Ping

ping www.google.com
PING www.google.com (172.217.194.105) 56(84) bytes of data.
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=1 ttl=46 time=116 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=2 ttl=46 time=102 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=3 ttl=46 time=119 ms
^C
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3110ms

Mecanismo de trabajo El programa Internet Ping funciona de manera muy similar a una ubicación de eco de sonda, enviando un pequeño paquete de información que contiene un ICMP ECHO_REQUEST a una computadora específica, que luego envía un paquete ECHO_REPLY a cambio. El paquete tiene un valor TTL (tiempo de vida) que determina el número máximo de saltos del enrutador. Si el paquete no llega, se notifica al remitente con el error. Los errores son de los siguientes tipos:

  • TTL vencido en tránsito
  • Host de destino inalcanzable
  • Solicitud agotada, es decir, sin respuesta
  • Anfitrión desconocido

Implementación Los pasos que sigue un programa de ping simple son:

  1. Tome un nombre de host como entrada
  2. Haz una búsqueda de DNS

La búsqueda de DNS se puede realizar mediante gethostbyname() . La función gethostbyname() convierte un sitio web normal legible por humanos y devuelve una estructura de tipo hostent que contiene la dirección IP en forma de notación de puntos binarios y también el tipo de dirección.

  1. Algunos programas de ping como el que se proporciona con ubuntu admiten la búsqueda inversa de DNS . La búsqueda inversa de DNS se realiza mediante getnameinfo() y convierte la dirección IP de notación de puntos en nombre de host. por ejemplo, el ping de google.com frecuentemente da una dirección extraña: bom07s18-in-f14.1e100.net Esto es como resultado de una búsqueda DNS inversa.
  2. Abra un socket sin formato con SOCK_RAW con el protocolo IPPROTO_ICMP. Nota: el socket sin procesar requiere derechos de superusuario, por lo que debe ejecutar este código usando sudo
  3. Cuando se presiona crtl + C, ping da un informe. Esta interrupción es capturada por un controlador de interrupciones que simplemente establece nuestra condición de bucle de ping en falso.
  4. Aquí viene el bucle principal de envío de ping. Tenemos que:
    1. Establezca la opción ttl en un valor en el socket. El valor TTL se establece para limitar la cantidad de saltos que puede realizar un paquete.
    2. Establecer el tiempo de espera de la función recv Si no se establece el tiempo de espera, recv esperará para siempre, deteniendo el ciclo.
    3. Llene el paquete icmp de la siguiente manera:
      1. Establezca el tipo de encabezado del paquete en ICMP_ECHO.
      2. Establecer id a pid del proceso
      3. Rellene la parte del mensaje al azar.
      4. Calcule la suma de verificación y complétela en el campo de suma de verificación.
    4. enviar el paquete
    5. Espere a que se reciba . El principal problema aquí es que el paquete recibido no significa que el destino esté funcionando. La respuesta de eco significa que el destino está bien. La respuesta de eco se envía desde el kernel del sistema operativo de destino. Esta es la lista de todos los tipos y códigos. Un problema aquí es que el programa muestra el tipo 69 y el código 0 si todo va bien en lugar de 0, que significa echo_reply. 

C

// C program to Implement Ping
 
// compile as -o ping
// run as sudo ./ping <hostname>
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/ip_icmp.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
 
// Define the Packet Constants
// ping packet size
#define PING_PKT_S 64
  
// Automatic port number
#define PORT_NO 0
 
// Automatic port number
#define PING_SLEEP_RATE 1000000 x
 
// Gives the timeout delay for receiving packets
// in seconds
#define RECV_TIMEOUT 1
 
// Define the Ping Loop
int pingloop=1;
 
 
// ping packet structure
struct ping_pkt
{
    struct icmphdr hdr;
    char msg[PING_PKT_S-sizeof(struct icmphdr)];
};
 
// Calculating the Check Sum
unsigned short checksum(void *b, int len)
{    unsigned short *buf = b;
    unsigned int sum=0;
    unsigned short result;
 
    for ( sum = 0; len > 1; len -= 2 )
        sum += *buf++;
    if ( len == 1 )
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
}
 
 
// Interrupt handler
void intHandler(int dummy)
{
    pingloop=0;
}
 
// Performs a DNS lookup
char *dns_lookup(char *addr_host, struct sockaddr_in *addr_con)
{
    printf("\nResolving DNS..\n");
    struct hostent *host_entity;
    char *ip=(char*)malloc(NI_MAXHOST*sizeof(char));
    int i;
 
    if ((host_entity = gethostbyname(addr_host)) == NULL)
    {
        // No ip found for hostname
        return NULL;
    }
     
    //filling up address structure
    strcpy(ip, inet_ntoa(*(struct in_addr *)
                          host_entity->h_addr));
 
    (*addr_con).sin_family = host_entity->h_addrtype;
    (*addr_con).sin_port = htons (PORT_NO);
    (*addr_con).sin_addr.s_addr  = *(long*)host_entity->h_addr;
 
    return ip;
     
}
 
// Resolves the reverse lookup of the hostname
char* reverse_dns_lookup(char *ip_addr)
{
    struct sockaddr_in temp_addr;   
    socklen_t len;
    char buf[NI_MAXHOST], *ret_buf;
 
    temp_addr.sin_family = AF_INET;
    temp_addr.sin_addr.s_addr = inet_addr(ip_addr);
    len = sizeof(struct sockaddr_in);
 
    if (getnameinfo((struct sockaddr *) &temp_addr, len, buf,
                    sizeof(buf), NULL, 0, NI_NAMEREQD))
    {
        printf("Could not resolve reverse lookup of hostname\n");
        return NULL;
    }
    ret_buf = (char*)malloc((strlen(buf) +1)*sizeof(char) );
    strcpy(ret_buf, buf);
    return ret_buf;
}
 
// make a ping request
void send_ping(int ping_sockfd, struct sockaddr_in *ping_addr,
                char *ping_dom, char *ping_ip, char *rev_host)
{
    int ttl_val=64, msg_count=0, i, addr_len, flag=1,
               msg_received_count=0;
     
    struct ping_pkt pckt;
    struct sockaddr_in r_addr;
    struct timespec time_start, time_end, tfs, tfe;
    long double rtt_msec=0, total_msec=0;
    struct timeval tv_out;
    tv_out.tv_sec = RECV_TIMEOUT;
    tv_out.tv_usec = 0;
 
    clock_gettime(CLOCK_MONOTONIC, &tfs);
 
     
    // set socket options at ip to TTL and value to 64,
    // change to what you want by setting ttl_val
    if (setsockopt(ping_sockfd, SOL_IP, IP_TTL,
               &ttl_val, sizeof(ttl_val)) != 0)
    {
        printf("\nSetting socket options
                 to TTL failed!\n");
        return;
    }
 
    else
    {
        printf("\nSocket set to TTL..\n");
    }
 
    // setting timeout of recv setting
    setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO,
                   (const char*)&tv_out, sizeof tv_out);
 
    // send icmp packet in an infinite loop
    while(pingloop)
    {
        // flag is whether packet was sent or not
        flag=1;
      
        //filling packet
        bzero(&pckt, sizeof(pckt));
         
        pckt.hdr.type = ICMP_ECHO;
        pckt.hdr.un.echo.id = getpid();
         
        for ( i = 0; i < sizeof(pckt.msg)-1; i++ )
            pckt.msg[i] = i+'0';
         
        pckt.msg[i] = 0;
        pckt.hdr.un.echo.sequence = msg_count++;
        pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
 
 
        usleep(PING_SLEEP_RATE);
 
        //send packet
        clock_gettime(CLOCK_MONOTONIC, &time_start);
        if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0,
           (struct sockaddr*) ping_addr,
            sizeof(*ping_addr)) <= 0)
        {
            printf("\nPacket Sending Failed!\n");
            flag=0;
        }
 
        //receive packet
        addr_len=sizeof(r_addr);
 
        if ( recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0,
             (struct sockaddr*)&r_addr, &addr_len) <= 0
              && msg_count>1)
        {
            printf("\nPacket receive failed!\n");
        }
 
        else
        {
            clock_gettime(CLOCK_MONOTONIC, &time_end);
             
            double timeElapsed = ((double)(time_end.tv_nsec -
                                 time_start.tv_nsec))/1000000.0
            rtt_msec = (time_end.tv_sec-
                          time_start.tv_sec) * 1000.0
                        + timeElapsed;
             
            // if packet was not sent, don't receive
            if(flag)
            {
                if(!(pckt.hdr.type ==69 && pckt.hdr.code==0))
                {
                    printf("Error..Packet received with ICMP
                           type %d code %d\n",
                           pckt.hdr.type, pckt.hdr.code);
                }
                else
                {
                    printf("%d bytes from %s (h: %s)
                          (%s) msg_seq=%d ttl=%d
                          rtt = %Lf ms.\n",
                          PING_PKT_S, ping_dom, rev_host,
                          ping_ip, msg_count,
                          ttl_val, rtt_msec);
 
                    msg_received_count++;
                }
            }
        }   
    }
    clock_gettime(CLOCK_MONOTONIC, &tfe);
    double timeElapsed = ((double)(tfe.tv_nsec -
                          tfs.tv_nsec))/1000000.0;
     
    total_msec = (tfe.tv_sec-tfs.tv_sec)*1000.0+
                          timeElapsed
                    
    printf("\n===%s ping statistics===\n", ping_ip);
    printf("\n%d packets sent, %d packets received, %f percent
           packet loss. Total time: %Lf ms.\n\n",
           msg_count, msg_received_count,
           ((msg_count - msg_received_count)/msg_count) * 100.0,
          total_msec);
}
 
// Driver Code
int main(int argc, char *argv[])
{
    int sockfd;
    char *ip_addr, *reverse_hostname;
    struct sockaddr_in addr_con;
    int addrlen = sizeof(addr_con);
    char net_buf[NI_MAXHOST];
 
    if(argc!=2)
    {
        printf("\nFormat %s <address>\n", argv[0]);
        return 0;
    }
 
    ip_addr = dns_lookup(argv[1], &addr_con);
    if(ip_addr==NULL)
    {
        printf("\nDNS lookup failed! Could
                   not resolve hostname!\n");
        return 0;
    }
 
    reverse_hostname = reverse_dns_lookup(ip_addr);
    printf("\nTrying to connect to '%s' IP: %s\n",
                                       argv[1], ip_addr);
    printf("\nReverse Lookup domain: %s",
                           reverse_hostname);
 
    //socket()
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if(sockfd<0)
    {
        printf("\nSocket file descriptor not received!!\n");
        return 0;
    }
    else
        printf("\nSocket file descriptor %d received\n", sockfd);
 
    signal(SIGINT, intHandler);//catching interrupt
 
    //send pings continuously
    send_ping(sockfd, &addr_con, reverse_hostname,
                                 ip_addr, argv[1]);
     
    return 0;
}
  1. Un resultado de ejemplo: Ejecute sudo ./ping google.com
Resolving DNS..

Trying to connect to 'google.com' IP: 172.217.27.206

Reverse Lookup domain: bom07s15-in-f14.1e100.net
Socket file descriptor 3 received

Socket set to TTL..
64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                msg_seq=1 ttl=64 rtt = 57.320584 ms.

64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                msg_seq=2 ttl=64 rtt = 58.666775 ms.

64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                msg_seq=3 ttl=64 rtt = 58.081148 ms.

64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                msg_seq=4 ttl=64 rtt = 58.700630 ms.

64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                msg_seq=5 ttl=64 rtt = 58.281802 ms.

64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                msg_seq=6 ttl=64 rtt = 58.360916 ms.

===172.217.27.206 ping statistics===

6 packets sent, 6 packets received, 0.000000 percent packet loss. 
Total time: 6295.187804 ms.

Publicación traducida automáticamente

Artículo escrito por SuprotikDey y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *