0

I'm trying to code a simple version of the ping command.

After I successfully resolve the address of the destination (www.googlg.com in my case), I send an ICMP_ECHO request to the destination using sendto(2) but in the receiving process the function recvmsg(2) hangs and after the wait time, the function return EAGAIN error.

I'm not sure if the icmp header is not set properly, or the parameters of the recvmsg funtions are wrong. This issue only happens for google.com but other websites works fine.

This is the code I use for sending the ICMP_ECHO request:

void send_v4()
{
  char buff[64];
  struct icmp *icmp;

  // Setup the ICMP packet header
  icmp = (struct icmp *)buff;
  icmp->icmp_id    = getpid();
  icmp->icmp_type  = ICMP_ECHO;
  icmp->icmp_code  = 0;
  icmp->icmp_seq   = 1;
  memset(icmp->icmp_data, 0x00, 56);

  // Setup data section
  gettimeofday((struct timeval *)(icmp->icmp_data), NULL);

  // Send full packet
  int ret = sendto(proto_v4.sockfd, buff, sizeof(buff), 0, proto_v4.dst_sa, proto_v4.dst_ai->ai_addrlen);
  if (ret == -1)
  printf("sendto failed with error code: %d\n", ret);
}

And this is code I use to receive the response:

void proc_v4()
{
        char buff[256];
        ssize_t received;
        struct iovec iov;
        struct msghdr msghdr;
        struct ip *ip;
        struct icmp *icmp;
        struct timeval tvcurr;
        struct timeval tvrecv;
        double time;

        memset(buff, 0x00, 256);

        iov.iov_base = buff;
        iov.iov_len = sizeof(buff);

        msghdr.msg_iov = &iov;
        msghdr.msg_iovlen = 1;

        received = recvmsg(proto_v4.sockfd, &msghdr, 0);
        if (received == -1)
                perror("recvmsg");

        ip = (struct ip *)(iov.iov_base);
        if (ip->ip_p == IPPROTO_ICMP)
                printf("IPPROTO_ICMP\n");

        icmp = (struct icmp *)(iov.iov_base + (ip->ip_hl << 2));
        if (icmp->icmp_type == ICMP_ECHOREPLY)
                printf("ICMP_ECHOREPLY %d\n", icmp->icmp_id);

        char buf[256];
        inet_ntop(
                proto_v4.dst_ai->ai_family,
                &((struct sockaddr_in *)proto_v4.dst_sa)->sin_addr,
                buf,
                256
        );

        tvrecv = *(struct timeval *)(icmp->icmp_data);

        gettimeofday(&tvcurr, NULL);
        time  = (tvcurr.tv_sec  - tvrecv.tv_sec)  * 1000;
        time += (tvcurr.tv_usec - tvrecv.tv_usec) / 1000.0;
        printf("%ld %ld\n", (tvcurr.tv_usec - tvrecv.tv_usec), (tvcurr.tv_usec - tvrecv.tv_usec) % 1000);
        printf("%d bytes from %s (%s): icmp_seq=%d ttl=%d time=%.2lf ms\n", 64, buf, buf, icmp->icmp_seq, ip->ip_ttl, time);
}
Glitch
  • 155
  • 1
  • 9
  • Did you check with a packet sniffer, like Wireshark, that the ECHO request is well-formed and actually being transmitted, and that a response is actually coming back? Also, just note that not all servers respond to pings (Google does, though). – Remy Lebeau Jan 23 '23 at 18:00
  • Yes, I did check with wireshark and the request is indeed sent and received for facebook.com but for google.com or github.com for example it was not successeful; the program just hangs at `recvmsg`. I have to say though that the checksum for the ICMP is not calculated as it should, as wireshark always keeps saying it is not correct. – Glitch Jan 30 '23 at 12:51
  • Correct me if I'm wrong please, is it always necessary to fill the ICMP packet and then wrapped with handmade IP packet and send the latter instead of sending the ICMP packet only? The reason I'm asking is because I've read some somewhere that some servers need to know the type of the request from the IP layer to prevent ICMP flood or for some security reasons. – Glitch Jan 30 '23 at 12:57

0 Answers0