Most people use the ping command just a simple way to see if the network connection of another system is normal. In this article, the author will introduce how to write a program that simulates the ping command function with the C language. The PING command is used to see if a network connection of another host system on the network is normal. The working principle of the ping command is to send ICMP packets to another host system on the network. If the specified system gets a message, it will transmit the message to the sender, which is a bit like submarine The sound device used is used. For example, executing the ping localhost command on the Linux terminal will see the following results: ping localhost.localdomain (127.0.0.1) from 127.0.0.1: 56 (84) bytes of data. 64 bytes from localhost.locAlDomain (127.0.0.1) : icmp_seq = 0 ttl = 255 time = 112 usec 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq = 1 ttl = 255 time = 79 usec 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq = 2 ttl = 255 time = 78 usec 64 bytes from localhost.localdomain (127.0.0.1): icmp_seq = 3 ttl = 255 time = 82 usec --- localhost.localdomain ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss Round-trip min / avg / max / mdev = 0.078 / 0.087 / 0.112 / 0.018 MS The above execution result can be seen that the ping command is executed after the test system hostname and the corresponding IP address, return to the current host ICMP report Wenshun serial number, TTL survival time and round trip time RTT (unit is millisecond, ie one third of the second). To write an analog ping command, this information has a revelation. To truly understand the principle of ping command implementation, you must understand the TCP / IP protocol used by the ping command. ICMP (Internet Control Message, internet protocol) is an error control mechanism provided for gateways and target hosts, enabling them to report the error to the packet source when encountered errors. The ICMP protocol is an IP layer protocol, but since the error report may also pass a few subnets when sending to the packet source, the ICMP message is involved in routing, so ICMP packets are sent via the IP protocol. Two level packages need to be added before the data transmission of ICMP datagram: First add ICMP header to form ICMP packets, add IP header to form an IP datagram. If the IP header ICMP header ICMP datagram, IP header format, the IP layer protocol is a point-to-point protocol, not the end-to-end protocol, which provides connectionless datagram, no port concept, so few use Bind () and connect () functions, if you use it, just use to set IP addresses. Sending data Use the sendto () function to receive data using the RECVFROM () function.
The IP header format is as follows: In Linux, the IP header format data structure () is defined as follows: struct ip {#IF __BYTE_ETE_ORDER == __Little_endian unsigned int ip_hl: 4; / * header length * / unsigned int ip_v: 4; / * Version * / #ndif #if __BYTE_ORDER == __BIG_ENDIAN UNSIGNED INT IP_V: 4; / * Version * / unsigned int ip_hl: 4; / * header length * / #ndif u_int8_t ip_tos; / * type of service * / u_short ip_len; / * total length * / u_short ip_id; / * identification * / u_short ip_off; / * fragment offset field * / #define IP_RF 0x8000 / * reserved fragment flag * / #define IP_DF 0x4000 / * dont fragment flag * / #define IP_MF 0x2000 / * more fragments flag * / #define IP_OFFMASK 0x1fff / * mask for fragmenting bits * / u_int8_t ip_ttl; / * time to live * / u_int8_t ip_p; / * protocol * / u_short ip_sum; / * checksum * / struct in_addr ip_src, ip_dst; / * Source and dest address * /}; where the PING program uses only the following data: IP header length IHL (Internet Header Length) - The length of the IP header is recorded in 4 bytes, which is the IP_HL variable of the above IP data structure. . Survival time TTL (Time to Live) - in seconds, pointing to the maximum time of IP data taps to stay on the network, its value is set by the sender, and minus one when the routing is When this value is 0, the datagram will be discarded, which is the IP_TTL variable of the above IP data structure. ICMP header format ICMP packets are divided into two, one is the error report message, and the other is the query message. Each ICMP header contains type, encoding, and checksum, with a length of 8 bits, 8-bit, and 16th, and the remaining options are different from ICMP features. The ping command only uses two of the many ICMP packets: "Request to return" (ICMP_ECHO) and "request response" (ICMP_ECHOREPLY).
Define below Linux: #define icmp_echo 0 #define ICMP_ECHOREPLY 8 These two ICMP type header formats are as follows: The ICMP data structure in Linux is defined as follows: struct ICMP {u_int8_t icmp_type; / * type of message, see beelow * / u_int8_t icmp_code; / * type sub code * / u_int16_t icmp_cksum; / * ones complement checksum of struct * / union {u_char ih_pptr; / * ICMP_PARAMPROB * / struct in_addr ih_gwaddr; / * gateway address * / struct ih_idseq / * echo datagram * / {u_int16_t icd_id; u_int16_t icd_seq;} ih_idseq; u_int32_t ih_void; / * ICMP_UNREACH_NEEDFRAG - Path MTU Discovery (RFC1191) * / struct ih_pmtu {u_int16_t ipm_void; u_int16_t ipm_nextmtu;} ih_pmtu; struct ih_rtradv {u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv;} icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define ICMP_NEXTMTU ICMP_HUN.IH_PMTU.IPM_NEXTMTU #d efine icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union {struct {u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime;} id_ts; struct {struct ip idi_ip; / * options and then 64 bits of data * /} id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; u_int8_t id_data [1];} icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime # define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_radv #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data}; macros used to make expression more concise, wherein the ICMP header is 8 bytes, the data packets up to 64K words of length Section.
Calibration and algorithm - This algorithm is called an internet check and algorithm, and the 16 digits of the verified data are accumulated, then take the reverse code, if the data byte length is odd, the data tail is added to one byte 0 To make up even. This algorithm applies to IPv4, ICMPv4, IGMPv4, ICMPv6, UDP, and TCP checksums. For more detailed information, please refer to the RFC 1071, the checksum field for the ICMP_CKSUM variable of the above ICMP data structure. Identifier - For unique identity ICMP packets, the variables referred to in the ICMP_ID macro of the above ICMP data structure. The sequence number -pring command ICMP_SEQ is read here, representing the parameter of the ICMP data structure of the ICMP data structure, represents the variable of the ICMP data structure of the ICMP data structure. ICMP datagram ping commands need to be displayed, including ICMP_SEQ and TTLs have implemented methods, but it is also deficient to RTT round trip time. In order to achieve this, ICMP datagram can be used to bring a timestamp. Use the following function to generate the timestamp: #include int gettimeofDay (Struct Timeval * TP, VOID * TZP) where the TimeVal structure is as follows: struct timeval {long TV_sec; long TV_usec;} where tv_sec is second, TV_USEC microseconds. Generate two TIMEVAL structures by getTimeOfDay when sending and receiving packets, the difference between the two, that is, the time difference of the ICMP packet transmission and reception, and the TIMEVAL structure is carried by the ICMP datagram, and the TZP pointer represents the time zone, generally Not used, assign a null value. The ping command comes with the data statistics system When it passes all ICMP packets, all transmission and all received ICMP packets are statistically calculate the ratio of ICMP packets. To reach the purpose, two global variables are defined: the receiving counter and send counters are used to record ICMP packet acceptance and transmission. Loss Number = Total Send Total - Total Number of Receives, Loss Ratio = Loss Number / Total Number.
The code for the simulation ping program is now as follows: / ********************************************* ********************* * author: Liang Junhui * * Date: October 2001 * * name: myping.c * * Note: this program is used to demonstrate ping The principle of orders * ***************************************************** ************** / #include #include #include #include #include #include #include #include #include #include #include #include #define PACKET_SIZE 4096 #define MAX_WAIT_TIME 5 #define MAX_NO_PACKETS 3 char sendpacket [PACKET_SIZE]; char recvpacket [PACKET_SIZE]; int sockfd, datalen = 56; int nsend = 0, nreceived = 0; struct sockaddr_in dest_addr; pid_t pid; struct sockaddr_in from; struct timeval tvrecv; void statistics (int signo) ; unsigned short cal_chksum (unsigned short * addr, int len); int pack (int pack_no); void send_packet (void); void recv_packet (void); int unpack (char * buf, int len); void tv_sub (struct timeval * OUT, STRUCT TIMEVAL * IN); void statistics (int Signo) {printf ("/ n ------------------ ping statistics -------- --------- / N "); Printf ("% D Packets Transmitted,% D Received, %%% D Lost / N ", NSEND, NRECEI VED, (Nsend-Nreceived) / NSEND * 100); Close (SockFD); EXIT (1);} / * Calibration and Algorithm * / unsigned short cal_chksum (unsigned short * addr, int LEN) {Int NLEFT = LEN; INT SUM = 0; unsigned short * w = addr; unsigned short answer = 0; / * Tired of ICMP header binary data in 2 bytes * / while (NLEFT> 1) {SUM = * W ; NLEFT- = 2;} / * If the ICMP header is an odd byte, the last byte is left.
Conference the last byte as a high byte of a 2-byte data, the low byte of this 2 byte data is 0, continue to accumulate * / if (NLEFT == 1) {* (unsigned char *) (& Answer) = * (unsigned char *) W; SUM = Answer;} SUM = (SUM >> 16) (SUM & 0xFFFF); SUM = (SUM >> 16); Answer = ~ Sum; Return Answer;} / * Setting ICMP header * / int pack (int pack_no) {int i, packsize; struct icmp * icmp; struct timeval * tval; icmp = (struct icmp *) sendpacket; icmp-> icmp_type = ICMP_ECHO; icmp-> icmp_code = 0; icmp-> ICMP_CKSUM = 0; ICMP-> ICMP_SEQ = PACK_NO; ICMP-> ICMP_ID = PID; packsize = 8 Datalen; TVAL = (struct timeval *) ICMP-> ICMP_DATA; GetTimeOfDay (tval, null); / * Record delivery time * / ICMP-> ICMP_CKSUM = CAL_CHKSUM ((unsigned short *) ICMP, PACKSIZE); / * Calibration Algorithm * / Return Packsize;} / * Send three ICMP packets * / void send_packet () {int packetsize; while (nsend { Nsend ; packetsize = pack (nsend); / * Set ICMP header * / if (SENDTO (SOCKFD, Sendpacket, PacketSize, 0, (Struct SockAddr *) & dest_addr, sizeof (dest_addr)) <0) {PERROR ("Sendto Error" ); Continue;} Sleep (1); / * Send an ICMP packet every other second * /}} / * Receive all ICMP packets * / void recv_packet () {Int n, fromlen; extern int errno; Signal SigalRM, Statistics; From LEN = SizeOf (from); while (NReceived {alarm (max_wait_time); if ((n = recvfrom (sockfd, recvpacket, sizeof (recvpacket), 0, (Struct SockAddr *) & from, & fromlease) <0) {IF ( Errno == Eintr) Continue; PERROR ("Recvfrom Error"); Continue;} getTimeOfDay (& TVRecv, null); / * Record time * / if (unpack (recapacket, n) == - 1) Continue; NRECeived ;} } / * Ripped ICMP header * / int unpack (char * buf, int LEN) {INT I, IPHDRLEN; STRUCT IP * IP; Struct ICMP * ICMP; Struct Timeval * Tvsend; Double RTT; IP = (struct IP *) BUF; iphdrlen = ip-> ip_hl << 2;
/ * Ask the IP header length, that is, the length flag of the IP header multiplied by 4 * / ICMP = (buf iPhdrlen); / * Crossing the IP header, pointing to ICMP header * / len- = iphdrlen; / * ICMP header And the total length of the ICMP datagram * / if (len <8) / * is less than the ICMP header length is unreasonable * / {printf ("ICMP Packets / 'S Length Is Less Than 8 / N"); Return -1;} / * Make sure the received ICMP response * / if ((iCMP-> ICMP_TYPE == ICMP_ECHOREPLY) && (ICMP-> ICMP_ID == PID) {tvsend = (Struct Timeval *) ICMP-> ICMP_DATA; TV_SUB (& TVRecv, TVSend); / * Receive and Send time difference * / RTT = TVRecv.tv_sec * 1000 TVRecv.tv_usec / 1000; / * calculate rtt * / / * in milliseconds * / / / * Display related information * / printf ("% D Byte from% S: ICMP_SEQ =% U TTL =% D =%. 3F ms / n", len, inet_ntoa (from.sin_addr), ICMP-> ICMP_SEQ, IP-> IP_TTL, RTT);} else return -1;} main (int argc, char * argv []) {struct hostent * host; struct protoent * protocol; unsigned long inaddr = 0l; int waittime = MAX_WAIT_TIME; int size = 50 * 1024; if (argc < 2) {Printf ("USAGE:% s hostname / ip address / n", argv [0]); exit (1);} if ((protocol = getProtobyname ("ICMP")) == null) {PERROR (" GetProtobyname "); EXIT (1);} / * Generate the original socket using ICMP, this socket only root can generate * / if ((sockfd = socket (AF_INET, SOCK_RAW, Protocol-> p_proto) <0) {Perror ("Socket Error"); EXIT (1);} / * Recycle root privileges, set current user rights * / setuid (GetUID ()); / * Expand socket reception buffer to 50K Do the main possibility to reduce the spillover of the receiving buffer, if there is no intention to ping a broadcast address or multicast address, will attract a large number of answers * / setsockopt (SockFD, SOL_Socket, S /RCVBUF, & Size, SIZEOF (SIZE)); Bzero (& DEST_ADDR, SIZEOF (DEST_ADDR)); dest_addr.sin_family = ADDR.SIN_FAMILY = AD_INET; / * Judges Is the host name or IP address * / if (INADDR = INET_ADDR (Argv [1]) == INADDR_NONE) {IF ((Host = gethostbyname) Argv [1])) == NULL) / * is hostname * / {Perror ("gethostbyname error"); exit (1);} Memcpy ((char *) & dest_addr.sin_addr, host-> h_addr, host-> h_length);