| 검색 | ?

ICMP(Internet Control Message Protocol)

1.1. 개요

IPv6 Fundamentals: A Brief Look at ICMPv6 Neighbor Discovery
참고 영상

  • 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]http://www.iana.org/assignments/icmp-parameters[])
    Type Code Name Reference
    0 0 Echo Reply (RFC792)
    1 - Unassigned (JBP)
    2 - Unassigned (JBP)
    3 Destination Unreachable (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 Redirect (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
    6 Alternate Host Address (JBP)
    0 Alternate Address for Host
    7 - Unassigned (JBP)
    8 0 Echo (RFC792)
    9 0 Router Advertisement (RFC1256)
    10 0 Router Selection (RFC1256)
    11 Time Exceeded (RFC792)
    0 Time to Live exceeded in Transit
    1 Fragment Reassembly Time Exceeded
    12 Parameter Problem (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 이라고 부르며 Computing the Internet Checksum (RFC1071) 에 그 구현방법이 명시되어 있다.)
    #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

1.2. 예제소스

/*
 Copyright (C) JAEHYUK CHO
 All rights reserved.

 Author: JaeHyuk Cho <minzkn@minzkn.com>

 Tiny ping example source
*/

/* need for "struct addrinfo" */
#if 1L && (!defined(_POSIX_SOURCE))
# define _POSIX_SOURCE                                               (1L)
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#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 <minzkn@minzkn.com>\n");

    if(s_argc <= 1) {
        (void)fprintf(stdout, "usage: %s <host> <count>\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 */


Copyright ⓒ MINZKN.COM
All Rights Reserved.