ICMP protocol and Ping

Source: Internet
Author: User
Tags sin
ICMP protocol

ICMP (Internet Control Message protocol,internet) is a connectionless protocol that is a child protocol of the TCP/IP protocol family and belongs to the Network layer protocol for IP host, Transfer control messages between routers (network access, host availability, routing is available, etc.). ICMP messages are automatically sent when an IP data unreachable destination is encountered, and the IP router is unable to forward packets at the current transmission rate. Although these control messages do not transmit user data, they play an important role in the transfer of user data.

The partial list of ICMP types is as follows:

TYPE CODE Description Query Error
0 0 echo reply--echoing answer (ping answer) X
3 0 Network unreachable--network can not reach X
3 1 Host unreachable--Mainframe Unreachable X
3 2 Protocol unreachable--protocol not up to X
3 3 Port unreachable--Ports not up X
3 6 Destination network unknown--Destination network unknown X
3 7 Destination host unknown--Destination hosts unknown X
8 0 echo request--echoing request (ping request) X
Original Socket

Raw sockets provide capabilities that are not provided by normal TCP and UDP sockets. Specifically as follows:

Processes can use raw sockets to read and write ICMPv4, ICMPv6, and IGMPv4 groupings.

The ping program sends an ICMP echo request using the original socket and receives an ICMP echo response.

#include <netinet/in.h>
int SOCKFD////////////////ICMPV4 socket
SOCKFD = socket (af_inet, Sock_raw, IPPROTO_ICMP);

Most cores deal only with ICMP, IGMP, TCP, and UDP datagrams, with raw sockets that the process can read and write to IPV4 datagrams that the kernel does not process its protocol fields.

When using the original socket, the process can construct the IPV4 header by itself using the IP_HDRINCL socket option.

const int on = 1;
if (setsockopt (SOCKFD, Ipproto_ip, Ip_hdrincl, &on, sizeof (ON)) < 0)
    printf ("error");

The concept of a port number does not exist for the original socket. Therefore, calling bind on the original socket simply sets the source IP address of all datagrams sent from this raw socket (without the IP_HDRINCL option turned on). Invoke connect on the original socket to specify only the destination IP address, after the call to connect can change the SendTo call to write or send call. the output rule of the original socket calls SendTo or sendmsg and specifies that the destination IP address is complete. If you successfully invoke connect, you can also call
Write, Writev, or send. When the IP_HDRINCL option is not turned on, the kernel constructs the IPV4 header based on the third parameter of the socket function and places it before data from the process. When the IP_HDRINCL option is turned on, the entire IPV4 header is constructed by the process, although the IPV4 identity field can be set to 0 for kernel setting, and the IPv4 header checksum field is always computed and stored by the kernel . In addition, the IPV4 option field is optional. The kernel performs fragmentation on the original groupings that exceed the outbound interface MTU. For IPV4, the process must be responsible for the checksum of any header checksum contained in the datagram after the IPV4 header. the input rules for the original socket receive UDP and TCP groupings that are never passed to any of the original sockets. Implementations originating from Berkeley Pass all ICMP packets that are not a callback request, a timestamp request, or an address mask request (where the three types of ICMP messages have kernel processing) to the original socket. All IGMP groupings are passed to the original socket. The kernel does not recognize that the IP datagram for its Protocol field is passed to the original socket. The kernel does not pass single or multiple fragments from the group to the original socket.

When the kernel passes an IP datagram to the original socket, the kernel examines all the original sockets on all processes because there is no concept of a port number, and carries out the following 3 tests to find the matching original socket. The Protocol field for the received datagram matches the third parameter of the socket function when the original socket was created. If BIND is already bound to a local IP address on the original socket, the destination IP address of the received datagram matches. If this original socket already has a connect call specifying the destination IP address, the received datagram's source IP address matches.

Note that if the third parameter that creates the original socket socket function is a value of 0 and no bind and connect are invoked, the original socket receives a copy of all the datagrams that the kernel passes to the original socket. In addition, the datagram that the kernel passes to the original socket must be a complete datagram that includes the IPV4 header. Ping Program

This ping only supports a-V command-line option to print verbose output. For the sake of simplification, only IPV4 is supported. The ping program is very simple to send an ICMP echo request to an IP address, and the node responds with an ICMP echo response.

#include <errno.h> #include <netdb.h> #include <signal.h> #include <stdio.h> #include < stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in_ systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h>/* C99 that a macro can have a variable parameter like a function/#define ERROR_EXIT (format, ...) \ Do {FPR intf (stderr, format "\ n", # #__VA_ARGS__); Exit (Exit_failure);
while (0) #define BUFSIZE 1500 char sendbuf[bufsize];      int datalen = 56;              The length of the "optional data" char *host;             Host name or IP address number of serial int nsent;               Send out the packet "serial number" pid_t pid;            Process ID int sockfd;           The original socket descriptor int verbose;
  The "-V" command-line option is marked struct proto {void (*fproc) (char *, ssize_t, struct msghdr *, struct timeval *);
  void (*fsend) (void);  struct SOCKADDR *sasend; sockaddr{} for Send, from getaddrinfo struct sockaddr *sarecv;          sockaddr{} for receiving socklen_t Salen;            Length of sockaddr{}s int icmpproto;

Ipproto_xxx value for ICMP} *PR;

    void* Signal (int signo, void (*func) (int)) {struct Sigaction Act, oact; 
    Act.sa_handler = func; 
    Sigemptyset (&act.sa_mask);
act.sa_flags = 0;
#ifdef Sa_interrupt if (Signo = = sigalrm) act.sa_flags |= sa_interrupt; 
#endif #ifdef Sa_restart if (signo!= sigalrm) act.sa_flags |=;
    #endif if (sigaction (Signo, &act, &oact) < 0) return sig_err; return oact.sa_handler;
    Returns the old behavior of the signal} struct addrinfo* host_serv (const char *host, const char *serv, int family, int socktype) {int n;

    struct addrinfo hints, *res;
    Bzero (&hints, sizeof (struct addrinfo));  Hints.ai_flags = Ai_canonname;       Always return canonical name hints.ai_family = family; 0, Af_inet, af_inet6, etc Hints.ai_socktype = sockType 0, Sock_stream, SOCK_DGRAM, etc if ((n = getaddrinfo (host, serv, &hints, &res))!= 0) {error_exit ( "Host_serv error for%s,%s:%s", (host = NULL)? "(No hostname)": Host, (serv = = NULL)?            
    "(No Service Name)": Serv, Gai_strerror (n));  } return (res); Return pointer to the linked list} char* sock_ntop_host (const struct SOCKADDR, *sa socklen_t) {Salen   c Char str[128]; Unix domain is largest switch (sa->sa_family) {case af_inet: {struct sockaddr_in *sin =
            (struct sockaddr_in *) sa;
            if (Inet_ntop (af_inet, &sin->sin_addr, str, sizeof (str)) = = null) return (NULL);            
        return (str); default:snprintf (str, sizeof (STR), "Sock_ntop_host:unknown af_xxx:%d, len%d", sa->sa_family,
            Salen);
    return (str);
return (NULL); /* Subtract two timeval time, the result is put in out
static void Tv_sub (struct timeval *out, struct timeval *in) {if (out->tv_usec-= In->tv_usec) < 0) {//OU
        T-= in--out->tv_sec;
    Out->tv_usec + 1000000;
} out->tv_sec-= in->tv_sec;
 /* Process all received ICMPV4 messages.
 * (When a ICMPV4 message is charged by the process on the original socket, the kernel has verified the validity of the basic field in its IPV4 header and ICMPV4 header) * * |--------------------len-------------------|
 * |--------hlen1--------|-------icmplen------|  * ++++++++++++++++++++++++++++++++++++++++++++ * + IPv4 + IPv4 + ICMPv4 + ICMP + * + header + OPTION +
 Header + Data + * ++++++++++++++++++++++++++++++++++++++++++++ * |--20b--|----0~40b----|---8B---|-----------| * [IP] [ICMP] * * (figure: Process ICMPv4 answer involves header, pointer, and length) ************************************************/void P
    Roc_v4 (char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv) {int hlen1, Icmplen;
    Double RTT;
    struct IP *ip;
    struct ICMP *icmp;

    struct Timeval *tvsend; ip = (struct IP *) ptr;        Start of IP Header hlen1 = Ip->ip_hl << 2; Length of IP header if (ip->ip_p!= ipproto_icmp)//Check ICMP identifier field return;  Not ICMP ICMP = (struct ICMP *) (PTR + hlen1); The start of ICMP header if ((Icmplen = Len-hlen1) < 8)//Is full ICMP data return;            Malformed packet if (Icmp->icmp_type = = icmp_echoreply) {//ICMP message ' type ' if (icmp->icmp_id!= pid) "identifier" return for ICMP messages; Not a response to We echo_request if (Icmplen <) return; Not enough data to use Tvsend = (struct Timeval *) icmp->icmp_data;
        "Optional Data" tv_sub (TVRECV, tvsend) for ICMP messages; RTT = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec/1000.0; Calculate RTT time printf ("%d bytes from%s:seq=%u, ttl=%d, rtt=%.3f ms\n", Icmplen, Sock_ntop_host (PR-&G
    T;SARECV, Pr->salen), Icmp->icmp_seq, Ip->ip_ttl, RTT); } 

    /* If specified-V (verbose output) displays all incoming ICMP messages/if (verbose) {printf ("%d bytes from%s:type =%d, code =%d\n",
    Icmplen, Sock_ntop_host (PR-&GT;SARECV, Pr->salen), Icmp->icmp_type, Icmp->icmp_code);
    }/* Compute the Internet and (specifically, the binary inverse code of each of the 16-bit values being validated)/static uint16_t in_cksum (uint16_t *addr, int len) {int nleft = Len;
    uint32_t sum = 0;
    uint16_t *w = addr;

    uint16_t answer = 0;  /* Our algorithm are simple, using a bit accumulator (sum), we add * sequential bit words to it, and on the end, Fold back all of the * carry bits from the top bits into the lower bits.
        */while (Nleft > 1) {sum + = *w++;
    Nleft-= 2;
        } if (nleft = = 1) {* (unsigned char *) (&answer) = * (unsigned char *) W;
    sum + + answer; }/* Add back carry outs from top bits to low bits/sum = (sum >>) + (sum & 0xFFFF);     Sum's high 16-bit and the low 16-bit for the first time added sum + = (sum >> 16);            The upper 16-bit can be produced by the last step again with the lower 16-bit cumulative answer = ~sum;
Truncate to bits return (answer); 
 /* Send ICMPv4 Echo request message.
 * * 0_______7.8_____15.16_____________31 * |__ type __|__ code __|_____ checksum _____|
 * |______ identifier _____|_____ serial number _____|                                  * |
 |             * .
 Optional data.
 * |__________________________________|
    * * (Figure: ICMPV4 message format) ************************************************/void send_v4 (void) {int len;

    struct ICMP *icmp;
    /* Constructs ICMPV4 message * * ICMP = (struct ICMP *) sendbuf;    Icmp->icmp_type = Icmp_echo;            "Type" of the message Icmp->icmp_code = 0;            The "code" value for the message is 0 icmp->icmp_id = pid;       The "identifier" for the message uses the ping process's pid icmp->icmp_seq = nsent++;                The "serial number" of the message is incremented memset (Icmp->icmp_data, 0xa5, datalen); The "optional data" for the message is populated 0xa5 gettimeofday (struct timeval *) icmp->icmp_data, NULL); The "optional data" at the beginning of the message holds the 8-byte timestamp of the sending moment/* COMPUTE ICMP checksum */len = 8 + dataLen           The length and icmp->icmp_cksum of ICMP "header" and "optional data" = 0;

    Before calculating the checksum, the officers transferred Guevara and field are set to 0 icmp->icmp_cksum = In_cksum ((u_short *) ICMP, len); /* Send datagrams (because the IP_HDRINCL option is not turned on, the kernel constructs the IPV4 header and places it before the ICMPV4 message buffer above) * * SendTo (SOCKFD, SendBuf, Len, 0, Pr->sasend, pr->s
Alen);

    * * Send an ICMP echo request/static void Sig_alrm (int signo) {(*pr->fsend) () every 1s. 
    Alarm (1);
Return
    } void Readloop (void) {int size;
    Char Recvbuf[bufsize];
    Char Controlbuf[bufsize];
    struct MSGHDR msg;
    struct Iovec Iov;
    ssize_t N;

    struct Timeval tval; /* Create original socket (super User privilege required)/SOCKFD = socket (pr->sasend->sa_family, Sock_raw, Pr->icmpproto); The process must have superuser privileges to create the original socket Setuid (Getuid ()); 
    Set the valid user ID of the process to the actual user ID, causing the process to discard the ownership (anti-attack) of Superuser privileges, set the size of the socket receive buffer, prevent receiving buffer overflow/size = 60 * 1024;

    SetSockOpt (SOCKFD, Sol_socket, So_rcvbuf, &size, sizeof (size));  

    /* Start the echo request, driven by SIGALRM signal per second/SIG_ALRM (SIGALRM); /* Read each of the received points on a raw socketGroup, show ICMP echo response/iov.iov_base = RECVBUF;
    Iov.iov_len = sizeof (RECVBUF);
    Msg.msg_name = pr->sarecv;
    Msg.msg_iov = &iov;
    Msg.msg_iovlen = 1;
    Msg.msg_control = Controlbuf; for (;;)
        {Msg.msg_namelen = pr->salen;
        Msg.msg_controllen = sizeof (CONTROLBUF);  n = recvmsg (sockfd, &msg, 0);
            Read back to the original ICMP socket for each grouping if (N < 0) {if (errno = eintr) continue;
        else Error_exit ("recvmsg (%d) =%d", SOCKFD, N);    } gettimeofday (&tval, NULL);
    Record packet collection time for calculating RTT (*pr->fproc) (Recvbuf, N, &msg, &tval);  int main (int argc, char **argv) {struct Proto proto_v4 = {proc_v4, send_v4, NULL, NULL, 0, ipproto_icmp};
    IPV4 proto structure int c;
    struct Addrinfo *ai;

    Char *h; /* getopt is used to resolve command-line option parameters.
     Call once, returning an option.   * extern int opterr;   When opterr=0, getopt does not output error message to STDERR * extern int optopt; The unknown option is stored in optopt, and the GetOpt back '? '   * extern int optind; Getopt the index * extern char *optarg of the next character option in the argv to be processed; Parameter pointer * For an option getopt returns-1 when no longer checks the included option, while Optind stores the index of the first command-line argument that does not contain an option. 
    * * Opterr = 0;
                while ((c = getopt (argc, argv, "V"))!=-1) {//This program has only the-v option switch (c) {case ' V '://-v option
                verbose++;
            Break
        Case '? '://unknown option error_exit ("Unrecognized option:%c", optopt);    

    } if (Optind!= argc-1) error_exit ("usage:ping [-v]  

Test:

: ~$ sudo./ping www.baidu.com
Ping www.baidu.com (123.125.114.144): Data bytes The bytes from
123.125.114.144:s Eq=0, ttl=52, rtt=37.399 ms bytes from 123.125.114.144:seq=1, ttl=52, rtt=36.838 ms-bytes from
123.125.114. 144:seq=2, ttl=52, rtt=62.450 ms

: ~$ sudo/ping-v www.baidu.com
ping www.baidu.com (123.125.114.144): Data b Ytes
bytes from 123.125.114.144:seq=0, ttl=52, rtt=52.391 ms-bytes from
  123.125.114.144:type = 0, code = 0
bytes from 123.125.114.144:seq=1, ttl=52, rtt=37.155 ms-bytes from
  123.125.114.144:type = 0, code = 0
  64 bytes from 123.125.114.144:seq=2, ttl=52, rtt=36.360 ms-bytes from
  123.125.114.144:type = 0, code = 0

: ~$ sudo./ping-v 123.3.4.5
Ping 123.3.4.5 (123.3.4.5): bytes bytes
  from 203.134.25.118:type = 3, C Ode = 1
  bytes from 203.134.25.118:type = 3, code = 1

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.