What is the proper process for requesting / responding an ICMP echo to inaccessible destinations?

Purpose:

I need to be able to ping a network switch to determine if it is available. This means that the user indicates that the network cable is disconnected, the network switch is unavailable, or some other problem lies in the way of network communication. I understand that this is not a comprehensive diagnostic tool, but something is better than nothing.

Design:

I planned to use ICMP with raw sockets to send five (5) ping messages to a specific address in IPv4 notation. I will configure the ICMP filter on the socket and will not create my own IP header. ICMP is transmitted through the sendto method and received through the recvfrom method. This will happen in one thread (although another thread can be used to break the transmission and reception). Reception of the message will be further filtered by matching the identifier of the received message with the identifier that was transmitted. The identifier that will be saved will be the identifier of the application process in progress. If an ICMP_ECHOREPLY message is received and the message identifier and stored identifier are the same, the counter is incremented until five (4) is reached (the counter is zero). I will try to send ping, wait for its reply and repeat this process five (5) times.

Problem:

After I completed my project, whenever I send a specific valid network address (say, 192.168.11.15) with an active network member, I get ICMP_ECHOREPLY messages for each of the five (5) pings. However, whenever I write a valid network address (say, 192.168.30.30) with inactive network participants (this means that no device is connected to a specific address), I receive one (1) ICMP_DEST_UNREACH and four (4) messages ICMP_ECHOREPLY. The identifier in the response messages corresponds to the identifier stored in the software. Whenever I execute "ping 192.168.30.30" from the command line, I get "From 192.168.40.50 icmp_seq = xx Destination Host Unreachable". Shouldn't I receive ICMP_DEST_UNREACH messages instead of ICMP_ECHOREPLY messages?

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

To compile, simply enter "g ++ main.cpp -o ping" at the Linux command line and it should compile (that is, if all source code is installed).

Output:

Can someone tell me why I get one (1) ICMP_DEST_UNREACH and four (4) ICMP_ECHOREPLY messages from a device that is not located on this particular network address?

NOTE. You can change the network IP address from the main.cpp file. Just change the IP to a device that really exists on your network, or a device that does not exist on your network.

I am also not interested in criticizing the coding style. I know that this is ugly, it has a C style mixed with C ++ castes, poor memory management, etc., but this is just a prototype code. It does not have to be beautiful.

+8
c ++ tcp icmp
source share
1 answer

Ok, I found a mistake. Look at the two lines.

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

both functions use the pingaddr pointer as a parameter, but this should be avoided because the sendto() function is used to specify the destination IP address of the icmp packet, but in recvfrom() used to return the IP address of the host that responds.

Say pingaddr installed with an inaccessible IP. After the first ICMP_REQUEST first gateway, it will respond with ICMP_DEST_UNREACH and ... an error appears here ... when recvfrom is called, the pingaddr structure will be overwritten by the IP address of the gateway.

SO ... from the second ping, you will point to the IP address of the gateway, which obviously exists and will respond using ICMP_ECHOREPLY .

DECISION:

avoid skipping the same pointer to the sockaddr_in structure for both sendto() and recvfrom() .

+5
source share

All Articles