Send ICMP ECHO_REQUEST to network hosts
- Ping works by sending a ICMP echo request packet to a target host and waiting for the ICMP echo reply
- The Internet Control Message Protocol (ICMP) is a network layer protocol used by network devices to diagnose network communication issues
- Internet Control Message Protocol (ICMP) is a stand-alone protocol
- ICMP is not associated with any transport layer protocol, such as Transmission Control Protocol (TCP) or User Datagram Protocol (UDP).
- It is a connectionless protocol, meaning a device does not need to open a connection with the target device before sending a message.
- ICMP packets are described within this structure
struct s_packet
{
struct icmphdr hdr {
u_int8_t type; /* Message type (echo, echo reply, etc...) */
u_int8_t code; /* Error codes (Host unreachable, TTL exceeded...) */
u_int16_t checksum; /* 16-bit one's complement of the one's complement sum of the ICMP message */
u_int16_t echo.id; /* PID of the program */
u_int16_t echo.sequence; /* Sequence number, is incremented by 1 for each packet */
u_int32_t gateway; /* Used for ICMP redirect type */
[...] /* Unused */
}
char *msg;
}
- ICMP packets are encapsulated in IP packets, it relies on IP to transmit its messages.
- Internet Protocol (IP) is a protocol for connectionless services.
- A user protocol may receive ICMP packets for all local sockets by opening a raw socket with the protocol IPPROTO_ICMP
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
- Convert domain names, hostnames, and IP addresses to structured binary formats for the operating system's networking API
getaddrinfo(host, NULL, &hints, &res)
- Convert IPv4 and IPv6 addresses from binary to text form
inet_ntop(AF_INET, &((const struct sockaddr_in *)ret->ai_addr)->sin_addr, g_data.ipv4, sizeof(g_data.ipv4))
- Since ICMP packets are encapsulated in IP packets, we use IPROTO_IP in the socket option, by doing this the kernel will manage the IP header by himself.
- TTL option
setsockopt(g_data.sockfd, IPPROTO_IP, IP_TTL, &g_data.ttl, sizeof(g_data.ttl))
- Timeout option
setsockopt(g_data.sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout))
- Wait time is handled with SIG_ALRM, a signal is raised every X sec. (subject's constraint)
signal(SIGALRM, alarmhandler);
- Format packet
ft_memset(&packet, 0, sizeof(packet));
packet.hdr.type = ICMP_ECHO;
packet.hdr.un.echo.id = getpid();
packet.hdr.un.echo.sequence = g_data.seq++;
packet.hdr.checksum = checksum(&packet, sizeof(packet));
- Send packet
sendto(g_data.sockfd, &packet, sizeof(packet), 0, g_data.host_addr, sizeof(*(g_data.host_addr))
- Wait for reply
recvmsg(g_data.sockfd, &msg, 0)
- Check validity of the packet
if (ret.hdr.type == ICMP_ECHOREPLY)
- Print the packet
print_packet(ret, g_data.seq, start_time);
- Print summary
print_end();
- Free host infos
freeaddrinfo(g_data.host_info);
Implemented options:
-c <count> stop after <count> replies.
-f flood ping.
-h print help and exit.
-i <interval> seconds between sending each packet.
-q quiet output.
-t <ttl> set time to live.
-v verbose output.
RTT round trip time, min, max, average.
Given Makefile contains all standard rules
- all
- clean
- fclean
- re
$ make
~ | Compiled libft
~ | Compiled ft_ping
$ sudo ./ft_ping localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.013 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.034 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.021 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.021 ms
64 bytes from localhost (127.0.0.1): icmp_seq=5 ttl=64 time=0.021 ms
^C
--- localhost ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4411ms
rtt min/avg/max = 0.013/0.022/0.034 ms