#keywords ICMP,ICMPv6,IPPROTO_ICMP,IPPROTO_ICMPV6,echo,request,reply,check,ping,ping6,raw,socket #title ICMP(Internet Control Message Protocol) [wiki:Home 대문] / [wiki:CategoryNetwork 네트웍], [wiki:CategoryProgramming 프로그래밍] / [wiki:ICMP ICMP(Internet Control Message Protocol)] ---- == [wiki:ICMP ICMP(Internet Control Message Protocol)] == * 작성자 조재혁 ([mailto:minzkn@minzkn.com]) * 고친과정 2008년 9월 1일 : 처음씀[[br]] [[TableOfContents]] === 개요 === |IPv6 Fundamentals: A Brief Look at ICMPv6 Neighbor Discovery| 참고 영상 || || [[Play(https://youtu.be/MGzl2TkGsSg)]] || * ICMP는 다음과 같이 IP datagram 과 함께 구성된 모습을 띄고 있다. |||| <- IP datagram -> || || IP header || ICMP message || * ICMP message || '''0''' || '''1''' || '''2''' || '''3''' || '''4''' || '''5''' || '''6''' || '''7''' || '''8''' || '''9''' || '''10''' || '''11''' || '''12''' || '''13''' || '''14''' || '''15''' || '''16''' || '''17''' || '''18''' || '''19''' || '''20''' || '''21''' || '''22''' || '''23''' || '''24''' || '''25''' || '''26''' || '''27''' || '''28''' || '''29''' || '''30''' || '''31''' || |||||||||||||||| 8bit Type |||||||||||||||| 8bit Code |||||||||||||||||||||||||||||||| 16bit Checksum || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ''(Type과 Code에 따라서 다른 형식을 갖는 부분)'' || * ICMP message ''Type'' and ''Code'' (참고: [^http://www.iana.org/assignments/icmp-parameters]) || '''Type''' || '''Code''' || '''Name''' || '''Reference''' || || 0 || 0 ||Echo Reply ||(RFC792) || || 1 || - ||Unassigned ||(JBP) || || 2 || - ||Unassigned ||(JBP) || ||<|14> 3 ||||Destination Unreachable ||<|14>(RFC792) || || 0 ||Net Unreachable || || 1 ||Host Unreachable || || 2 ||Protocol Unreachable || || 3 ||Port Unreachable || || 4 ||Fragmentation Needed and Don't Fragment was Set || || 5 ||Source Route Failed || || 6 ||Destination Network Unknown || || 7 ||Destination Host Unknown || || 8 ||Source Host Isolated || || 9 ||Communication with Destination Network is Administratively Prohibited || || 10 ||Communication with Destination Host is Administratively Prohibited || || 11 ||Destination Network Unreachable for Type of Service || || 12 ||Destination Host Unreachable for Type of Service || || 4 || 0 ||Source Quench ||(RFC792) || ||<|5> 5 ||||Redirect ||<|5>(RFC792) || || 0 ||Redirect Datagram for the Network (or subnet) || || 1 ||Redirect Datagram for the Host || || 2 ||Redirect Datagram for the Type of Service and Network || || 3 ||Redirect Datagram for the Type of Service and Host || ||<|2> 6 ||||Alternate Host Address ||<|2>(JBP) || || 0 ||Alternate Address for Host || || 7 || - ||Unassigned ||(JBP) || || 8 || 0 ||Echo ||(RFC792) || || 9 || 0 ||Router Advertisement ||(RFC1256) || || 10 || 0 ||Router Selection ||(RFC1256) || ||<|3> 11 ||||Time Exceeded ||<|3>(RFC792) || || 0 ||Time to Live exceeded in Transit || || 1 ||Fragment Reassembly Time Exceeded || ||<|4> 12 ||||Parameter Problem ||<|4>(RFC792) || || 0 ||Pointer indicates the error || || 1 ||Missing a Required Option || || 2 ||Bad Length || || 13 || 0 ||Timestamp ||(RFC792) || || 14 || 0 ||Timestamp Reply ||(RFC792) || || 15 || 0 ||Information Request ||(RFC792) || || 16 || 0 ||Information Reply ||(RFC792) || || 17 || 0 ||Address Mask Request ||(RFC950) || || 18 || 0 ||Address Mask Reply ||(RFC950) || || 19 || - ||Reserved (for Security) ||(Solo) || || 20-29 || - ||Reserved (for Robustness Experiment) ||(ZSu) || || 30 || - ||Traceroute ||(RFC1393) || || 31 || - ||Datagram Conversion Error ||(RFC1475) || || 32 || - ||Mobile Host Redirect ||(David Johnson) || || 33 || - ||IPv6 Where-Are-You ||(Bill Simpson) || || 34 || - ||IPv6 I-Am-Here ||(Bill Simpson) || || 35 || - ||Mobile Registration Request ||(Bill Simpson) || || 36 || - ||Mobile Registration Reply ||(Bill Simpson) || || 37-255 || - ||Reserved ||(JBP) || * ''Type'' 에 따라서 일부는 ''Code''를 추가적으로 필요하거나 필요하지 않다. * ICMP checksum (엄밀히는 Internet checksum 이라고 부르며 [wiki:rfc1071checksum Computing the Internet Checksum (RFC1071)] 에 그 구현방법이 명시되어 있다.) {{{#!enscript c #define def_mzping_icmp_optimize (1) /* 0=이론적구현, 1=최적화구현 */ static unsigned int mzping_icmp_checksum(const void *s_data, size_t s_size) { /* s_result의 변수형이 unsigned int로 사용하였지만 엄격하게는 32bit 정수형을 사용해야 한다. 만약 unsigned int가 32bit 정수형이 아닌 경우 이는 잘못된 구현이 됨을 주의해야 한다. */ register unsigned int s_result = 0u; while(s_size > ((size_t)1)) { #if def_mzping_icmp_optimize == (0) s_result += (unsigned int)(ntohs(*((const unsigned short int *)s_data))); #else s_result += (unsigned int)(*((const unsigned short int *)s_data)); #endif s_data = ((const unsigned short int *)s_data) + ((size_t)1); s_size -= (size_t)2; } if(s_size > ((size_t)0)) { s_result += (unsigned int)(*((const unsigned char *)s_data)); } #if def_mzping_icmp_optimize == (0) while(s_result > 0xffffu) { s_result = (s_result >> 16) + (s_result & 0xffffu); } #else s_result = (s_result >> 16) + (s_result & 0xffffu); s_result += s_result >> 16; #endif #if def_mzping_icmp_optimize == (0) return(htons((~s_result) & 0xffffu)); #else return((~s_result) & 0xffffu); #endif } }}} * ICMP echo request and reply || '''0''' || '''1''' || '''2''' || '''3''' || '''4''' || '''5''' || '''6''' || '''7''' || '''8''' || '''9''' || '''10''' || '''11''' || '''12''' || '''13''' || '''14''' || '''15''' || '''16''' || '''17''' || '''18''' || '''19''' || '''20''' || '''21''' || '''22''' || '''23''' || '''24''' || '''25''' || '''26''' || '''27''' || '''28''' || '''29''' || '''30''' || '''31''' || |||||||||||||||| 8bit Type (8 or 0) |||||||||||||||| 8bit Code |||||||||||||||||||||||||||||||| 16bit Checksum || |||||||||||||||||||||||||||||||| Identifier |||||||||||||||||||||||||||||||| Sequence number || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| Variable data || * ICMP address mask request and replay || '''0''' || '''1''' || '''2''' || '''3''' || '''4''' || '''5''' || '''6''' || '''7''' || '''8''' || '''9''' || '''10''' || '''11''' || '''12''' || '''13''' || '''14''' || '''15''' || '''16''' || '''17''' || '''18''' || '''19''' || '''20''' || '''21''' || '''22''' || '''23''' || '''24''' || '''25''' || '''26''' || '''27''' || '''28''' || '''29''' || '''30''' || '''31''' || |||||||||||||||| 8bit Type (17 or 18) |||||||||||||||| 8bit Code |||||||||||||||||||||||||||||||| 16bit Checksum || |||||||||||||||||||||||||||||||| Identifier |||||||||||||||||||||||||||||||| Sequence number || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 32bit subnet mask || * ICMP timestamp request and replay || '''0''' || '''1''' || '''2''' || '''3''' || '''4''' || '''5''' || '''6''' || '''7''' || '''8''' || '''9''' || '''10''' || '''11''' || '''12''' || '''13''' || '''14''' || '''15''' || '''16''' || '''17''' || '''18''' || '''19''' || '''20''' || '''21''' || '''22''' || '''23''' || '''24''' || '''25''' || '''26''' || '''27''' || '''28''' || '''29''' || '''30''' || '''31''' || |||||||||||||||| 8bit Type (13 or 14) |||||||||||||||| 8bit Code |||||||||||||||||||||||||||||||| 16bit Checksum || |||||||||||||||||||||||||||||||| Identifier |||||||||||||||||||||||||||||||| Sequence number || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 32bit originate timestamp || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 32bit receive timestamp || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 32bit transmit timestamp || === 예제소스 === {{{#!enscript c /* Copyright (C) JAEHYUK CHO All rights reserved. Author: JaeHyuk Cho Tiny ping example source */ /* need for "struct addrinfo" */ #if 1L && (!defined(_POSIX_SOURCE)) # define _POSIX_SOURCE (1L) #endif #include #include #include #include #include #include #include #include #include #include #define def_mzping_icmp_optimize (1) #define def_mzping_icmp_min_packet_size (1 + 1 + 2 + 2 + 2) /* type(1) + code(1) + checksum(2) + identifier(2) + sequence_number(2) */ static unsigned int mzping_icmp_checksum(const void *s_data, size_t s_size); static unsigned int mzping_ts32_us(void); static int mzping_icmp_v4(int s_socket, const char *s_hostname, const char *s_address_string, struct addrinfo *s_addrinfo, int s_sequence_number, int s_timeout); static int mzping_icmp_v6(int s_socket, const char *s_hostname, const char *s_address_string, struct addrinfo *s_addrinfo, int s_sequence_number, int s_timeout); int mzping(const char *s_hostname, int s_count); int main(int s_argc, char **s_argv); static unsigned int mzping_icmp_checksum(const void *s_data, size_t s_size) { register unsigned int s_result = 0u; while(s_size > ((size_t)1)) { #if def_mzping_icmp_optimize == (0) s_result += (unsigned int)(ntohs(*((const unsigned short int *)s_data))); #else s_result += (unsigned int)(*((const unsigned short int *)s_data)); #endif s_data = ((const unsigned short int *)s_data) + ((size_t)1); s_size -= (size_t)2; } if(s_size > ((size_t)0)) { s_result += (unsigned int)(*((const unsigned char *)s_data)); } #if def_mzping_icmp_optimize == (0) while(s_result > 0xffffu) { s_result = (s_result >> 16) + (s_result & 0xffffu); } #else s_result = (s_result >> 16) + (s_result & 0xffffu); s_result += s_result >> 16; #endif #if def_mzping_icmp_optimize == (0) return(htons((~s_result) & 0xffffu)); #else return((~s_result) & 0xffffu); #endif } static unsigned int mzping_ts32_us(void) { struct timeval s_timeval; (void)gettimeofday((struct timeval *)(&s_timeval), (void *)0); return((s_timeval.tv_sec * 1000000u) + s_timeval.tv_usec); } static int mzping_icmp_v4(int s_socket, const char *s_hostname, const char *s_address_string, struct addrinfo *s_addrinfo, int s_sequence_number, int s_timeout) { int s_result, s_check, s_myid; unsigned char s_packet[ (20 + 40) + (def_mzping_icmp_min_packet_size + 4) ]; size_t s_packet_size; ssize_t s_send_bytes; size_t s_ip_header_size; fd_set s_fd_rx; struct timeval s_timeval; ssize_t s_recv_bytes; socklen_t s_socklen_in; struct sockaddr_in s_sockaddr_in; s_result = (-1); s_myid = (int)(getpid() & 0xffff); s_packet_size = (size_t)0; s_packet[s_packet_size] = 8u; /* ICMP_ECHO */ s_packet_size += (size_t)1; s_packet[s_packet_size] = 0u; /* code */ s_packet_size += (size_t)1; *((unsigned short int *)(&s_packet[s_packet_size])) = 0u; /* checksum */ s_packet_size += (size_t)2; *((unsigned short int *)(&s_packet[s_packet_size])) = htons(s_myid); /* identifier */ s_packet_size += (size_t)2; *((unsigned short int *)(&s_packet[s_packet_size])) = htons(s_sequence_number); /* sequence number */ s_packet_size += (size_t)2; #if def_mzping_icmp_optimize == (0) *((unsigned int *)(&s_packet[8])) = htonl(mzping_ts32_us()); /* optional: time stamp */ #else *((unsigned int *)(&s_packet[8])) = (unsigned int)mzping_ts32_us(); /* optional: time stamp */ #endif s_packet_size += (size_t)4; /* do checksum */ *((unsigned short int *)(&s_packet[2])) = mzping_icmp_checksum((const void *)(&s_packet[0]), s_packet_size); /* checksum */ s_send_bytes = sendto(s_socket, (const void *)(&s_packet[0]), s_packet_size, MSG_NOSIGNAL, (struct sockaddr *)s_addrinfo->ai_addr, s_addrinfo->ai_addrlen); if(s_send_bytes != ((ssize_t)s_packet_size)) { (void)fprintf(stderr, "send: can not send %ld/%lu\n", (long)s_send_bytes, (unsigned long)sizeof(s_packet)); return(-1); } l_need_echoreply:; FD_ZERO(&s_fd_rx); FD_SET(s_socket, &s_fd_rx); s_timeval.tv_sec = s_timeout / 1000; s_timeval.tv_usec = (s_timeout % 1000) * 1000; s_check = select(s_socket + 1, (fd_set *)(&s_fd_rx), (fd_set *)0, (fd_set *)0, (struct timeval *)(&s_timeval)); if(s_check == (-1)) { perror("select"); return(-1); } if(s_check == 0) { (void)fprintf(stderr, "select: timeout\n"); return(-1); } if(FD_ISSET(s_socket, &s_fd_rx) == 0) { (void)fprintf(stderr, "select: is not set\n"); return(-1); } s_socklen_in = (socklen_t)sizeof(s_sockaddr_in); s_recv_bytes = recvfrom(s_socket, (void *)(&s_packet[0]), sizeof(s_packet), MSG_NOSIGNAL, (struct sockaddr *)(&s_sockaddr_in), (socklen_t *)(&s_socklen_in)); if(s_recv_bytes == ((ssize_t)(-1))) { perror("recvfrom"); return(-1); } s_ip_header_size = ((size_t)((s_packet[0] >> 0) & 0x0fu)) << 2; if(s_recv_bytes < (s_ip_header_size + s_packet_size)) { /* (void)fprintf(stderr, "too small packet\n"); */ goto l_need_echoreply; } if(ntohs(*((unsigned short int *)(&s_packet[s_ip_header_size + 4]))) != s_myid) { /* (void)fprintf(stderr, "not my ping\n"); */ goto l_need_echoreply; } if(s_packet[s_ip_header_size] == 8u /* ICMP_ECHO */) { /* maybe localhost loopback case */ goto l_need_echoreply; } if(s_packet[s_ip_header_size] == 0u /* ICMP_ECHOREPLY */) { unsigned int s_trip_time; s_result = (int)ntohs(*((unsigned short int *)(&s_packet[s_ip_header_size + 1 + 1 + 2 + 2]))); #if def_mzping_icmp_optimize == (0) s_trip_time = mzping_ts32_us() - ntohl(*((unsigned int *)(&s_packet[s_ip_header_size + def_mzping_icmp_min_packet_size]))); #else s_trip_time = mzping_ts32_us() - (*((unsigned int *)(&s_packet[s_ip_header_size + def_mzping_icmp_min_packet_size]))); #endif (void)fprintf(stdout, "%ld bytes from %s (%s): icmp_seq=%d ttl=%u time=%u.%03u ms;\n", (unsigned long)(s_recv_bytes - (s_ip_header_size)), s_hostname, s_address_string, s_result, (unsigned int)s_packet[8], s_trip_time / 1000u, s_trip_time % 1000u); } return(s_result); } static int mzping_icmp_v6(int s_socket, const char *s_hostname, const char *s_address_string, struct addrinfo *s_addrinfo, int s_sequence_number, int s_timeout) { int s_result, s_check, s_myid; unsigned char s_packet[ (def_mzping_icmp_min_packet_size + 4) ]; size_t s_packet_size; ssize_t s_send_bytes; fd_set s_fd_rx; struct timeval s_timeval; ssize_t s_recv_bytes; socklen_t s_socklen_in; struct sockaddr_in s_sockaddr_in; s_result = (-1); s_myid = (int)(getpid() & 0xffff); s_packet_size = (size_t)0; s_packet[s_packet_size] = 128u; /* ICMP6_ECHO_REQUEST */ s_packet_size += (size_t)1; s_packet[s_packet_size] = 0u; /* code */ s_packet_size += (size_t)1; *((unsigned short int *)(&s_packet[s_packet_size])) = 0u; /* checksum */ s_packet_size += (size_t)2; *((unsigned short int *)(&s_packet[s_packet_size])) = htons(s_myid); /* identifier */ s_packet_size += (size_t)2; *((unsigned short int *)(&s_packet[s_packet_size])) = htons(s_sequence_number); /* sequence number */ s_packet_size += (size_t)2; #if def_mzping_icmp_optimize == (0) *((unsigned int *)(&s_packet[8])) = htonl(mzping_ts32_us()); /* optional: time stamp */ #else *((unsigned int *)(&s_packet[8])) = (unsigned int)mzping_ts32_us(); /* optional: time stamp */ #endif s_packet_size += (size_t)4; /* do checksum */ *((unsigned short int *)(&s_packet[2])) = mzping_icmp_checksum((const void *)(&s_packet[0]), s_packet_size); /* checksum */ s_send_bytes = sendto(s_socket, (const void *)(&s_packet[0]), s_packet_size, MSG_NOSIGNAL, (struct sockaddr *)s_addrinfo->ai_addr, s_addrinfo->ai_addrlen); if(s_send_bytes != ((ssize_t)s_packet_size)) { (void)fprintf(stderr, "send: can not send %ld/%lu\n", (long)s_send_bytes, (unsigned long)sizeof(s_packet)); return(-1); } l_need_echoreply:; FD_ZERO(&s_fd_rx); FD_SET(s_socket, &s_fd_rx); s_timeval.tv_sec = s_timeout / 1000; s_timeval.tv_usec = (s_timeout % 1000) * 1000; s_check = select(s_socket + 1, (fd_set *)(&s_fd_rx), (fd_set *)0, (fd_set *)0, (struct timeval *)(&s_timeval)); if(s_check == (-1)) { perror("select"); return(-1); } if(s_check == 0) { (void)fprintf(stderr, "select: timeout\n"); return(-1); } if(FD_ISSET(s_socket, &s_fd_rx) == 0) { (void)fprintf(stderr, "select: is not set\n"); return(-1); } s_socklen_in = (socklen_t)sizeof(s_sockaddr_in); s_recv_bytes = recvfrom(s_socket, (void *)(&s_packet[0]), sizeof(s_packet), MSG_NOSIGNAL, (struct sockaddr *)(&s_sockaddr_in), (socklen_t *)(&s_socklen_in)); if(s_recv_bytes == ((ssize_t)(-1))) { perror("recvfrom"); return(-1); } if(s_recv_bytes < s_packet_size) { /* (void)fprintf(stderr, "too small packet\n"); */ goto l_need_echoreply; } if(ntohs(*((unsigned short int *)(&s_packet[4]))) != s_myid) { /* (void)fprintf(stderr, "not my ping\n"); */ goto l_need_echoreply; } if(s_packet[0] == 128u /* ICMP6_ECHO_REQUEST */) { goto l_need_echoreply; } if(s_packet[0] == 129u /* ICMP6_ECHO_REPLY */) { unsigned int s_trip_time; s_result = (int)ntohs(*((unsigned short int *)(&s_packet[1 + 1 + 2 + 2]))); #if def_mzping_icmp_optimize == (0) s_trip_time = mzping_ts32_us() - ntohl(*((unsigned int *)(&s_packet[def_mzping_icmp_min_packet_size]))); #else s_trip_time = mzping_ts32_us() - (*((unsigned int *)(&s_packet[def_mzping_icmp_min_packet_size]))); #endif (void)fprintf(stdout, "%ld bytes from %s (%s): icmp_seq=%d ttl=%u time=%u.%03u ms;\n", (unsigned long)s_recv_bytes, s_hostname, s_address_string, s_result, (unsigned int)0u /* TODO: Hops limit here */, s_trip_time / 1000u, s_trip_time % 1000u); } return(s_result); } int mzping(const char *s_hostname, int s_count) { int s_sequence_number = 0, s_check, s_socket; struct addrinfo *s_addrinfo_result; struct addrinfo s_addrinfo_hints; struct addrinfo *s_addrinfo; char s_address_string[ 64 ]; /* resolv name */ (void)memset((void *)(&s_addrinfo_hints), 0, sizeof(s_addrinfo_hints)); s_addrinfo_hints.ai_socktype = SOCK_RAW; s_addrinfo_hints.ai_family = AF_UNSPEC; s_check = getaddrinfo(s_hostname, (const char *)0, (const struct addrinfo *)(&s_addrinfo_hints), (struct addrinfo **)(&s_addrinfo_result)); if(s_check != 0) { (void)fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(s_check)); return(-1); } do { s_sequence_number++; for(s_addrinfo = s_addrinfo_result;s_addrinfo != ((struct addrinfo *)0);s_addrinfo = s_addrinfo->ai_next) { if(s_addrinfo->ai_family == AF_INET) { /* ICMP */ struct sockaddr_in *s_in; s_socket = socket(s_addrinfo->ai_family, SOCK_RAW, IPPROTO_ICMP); if(s_socket == (-1)) { perror("socket"); continue; } s_in = (struct sockaddr_in *)s_addrinfo->ai_addr; inet_ntop(s_addrinfo->ai_family, (const void *)(&s_in->sin_addr), (char *)(&s_address_string[0]), (socklen_t)sizeof(s_address_string)); s_check = mzping_icmp_v4(s_socket, s_hostname, (const char *)(&s_address_string[0]), s_addrinfo, s_sequence_number, 20000); close(s_socket); } else if(s_addrinfo->ai_family == AF_INET6) { /* ICMPv6 */ struct sockaddr_in6 *s_in6; s_socket = socket(s_addrinfo->ai_family, SOCK_RAW, IPPROTO_ICMPV6); if(s_socket == (-1)) { perror("socket"); continue; } s_in6 = (struct sockaddr_in6 *)s_addrinfo->ai_addr; inet_ntop(s_addrinfo->ai_family, (const void *)(&s_in6->sin6_addr), (char *)(&s_address_string[0]), (socklen_t)sizeof(s_address_string)); s_check = mzping_icmp_v6(s_socket, s_hostname, (const char *)(&s_address_string[0]), s_addrinfo, s_sequence_number, 20000); close(s_socket); } } (void)sleep(1); }while(s_sequence_number < s_count); freeaddrinfo((struct addrinfo *)s_addrinfo_result); return(1); } int main(int s_argc, char **s_argv) { int s_count = 4; (void)fprintf(stdout, "mzping v0.0.2 - Code by JaeHyuk Cho \n"); if(s_argc <= 1) { (void)fprintf(stdout, "usage: %s \n", (char *)s_argv[0]); /* (void)mzping("localhost", s_count); */ return(EXIT_SUCCESS); } if(s_argc >= 3) { if(sscanf(s_argv[2], "%i", &s_count) != 1) { perror("count"); return(EXIT_FAILURE); } } (void)setuid(getuid()); (void)mzping(s_argv[1], s_count); return(EXIT_SUCCESS); } /* vim: set expandtab: */ /* End of source */ }}} === 참고자료 === * [^http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml Internet Control Message Protocol (ICMP) Parameters - IANA] * [^https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml Internet Control Message Protocol version 6 (ICMPv6) Parameters - IANA] * [wiki:OSI_7LayerModel OSI 7 계층모델] * [wiki:rfc1071checksum Computing the Internet Checksum (RFC1071)] * [wiki:TCP TCP(Transmission Control Protocol)] * [^https://tools.ietf.org/rfc/rfc792.txt RFC792 - INTERNET CONTROL MESSAGE PROTOCOL] * [^https://tools.ietf.org/rfc/rfc2894.txt RFC2894 - Router Renumbering for IPv6] * [^https://tools.ietf.org/rfc/rfc3315.txt RFC3315 - Dynamic Host Configuration Protocol for IPv6 (DHCPv6)] * [^https://tools.ietf.org/rfc/rfc4443.txt RFC4443 - Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification] * [wiki:mzping mzping] * [^http://www.networksorcery.com/enp/protocol/icmp.htm ICMP, Internet Control Message Protocol - RFC sourcebook] * [^https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol_for_IPv6] * [^https://www.youtube.com/watch?v=MGzl2TkGsSg]