Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use PING in Linux

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly shows you "how to use PING in Linux", the content is easy to understand, clear, hope to help you solve doubts, the following let the editor lead you to study and learn "how to use PING in Linux" this article.

PING (Packet InterNet Groper), known as Internet packet Explorer in Chinese, is a tool used to check whether the network connection of another host system on the network is normal. The ping command works by sending an ICMP message to another host system on the network. If the designated system gets the message, it will send the reply message back to the sender, which is a bit like the sound device used in the submarine sonar system. Therefore, we want to know if my host can communicate with another one. The first thing we need to confirm is whether the network between our two hosts is connected, that is, whether what I said can be transmitted to you. This is the premise of communication between the two sides. The methods and phenomena of using the instruction ping under Linux are as follows:

The implementation of PING doesn't look complicated. I want to write my own code to implement this function. What kind of knowledge storage do I need? I made a brief list:

Understanding of ICMP Protocol

RAW socket

Network packet and unpacking skills

The steps to build such a ping program are as follows:

Encapsulation and unencapsulation of ICMP packet

Create a thread for sending ICMP packets

Create a thread for receiving ICMP packets

Original socket programming

The process of PING is as follows:

1. Encapsulation and unpacking of ICMP packets

(1) understanding of ICMP protocol

To develop PING, we first need to know that the implementation of PING is based on the ICMP protocol. To encapsulate and unpack ICMP packets, we first need to understand the ICMP protocol. ICMP is located at the network layer and allows hosts or routers to report errors and provide reports on anomalies. ICMP message is encapsulated in IP Datagram as the data part of it. As the data of the IP layer Datagram, the ICMP message is composed of the IP Datagram and sent out. The format of ICMP message is as follows:

There are two types of ICMP messages, namely, ICMP error report message and ICMP inquiry message. The type of ICMP message used by PING program is ICMP query message. Notice the "type" field in the ICMP message format mentioned above. We can fill in different values into this field when grouping packets to calibrate the type of the ICMP message. Listed below are several commonly used ICMP message types.

The type of ICMP that our PING program needs to use is an echo request (8).

Because the specific format of the ICMP message varies depending on the type of ICMP message, the format of our ping packet is as follows:

(2) Assembly of ICMP packets

Compared with the above ping package format, the code that encapsulates the ping package can be written as follows:

Void icmp_pack (struct icmp* icmphdr, int seq, int length) {int I = 0; icmphdr- > icmp_type = ICMP_ECHO; / / type return request icmphdr- > icmp_code = 0; icmphdr- > icmp_cksum = 0; / / Note, enter 0 first, which is very important! Icmphdr- > icmp_seq = seq; / / for the serial number here, let's fill in 1, 2, 3, 4.... Icmphdr- > icmp_id = pid & 0xfffft; / / We use pid as the icmp_id,icmp_id is only 2 bytes, while pid has 4 bytes of for (iDeposit iicmp _ data [I] = I; / / fill the data segment so that the ICMP message is larger than 64B} icmphdr- > icmp_cksum = cal_chksum ((unsigned short*) icmphdr, length); / / checksum calculation}

I would like to remind you again and again that icmp_cksum must first enter 0 before performing checksum algorithm calculation, otherwise the other host will discard the request packet due to incorrect checksum calculation during ping, resulting in the failure of ping. One of my colleagues once investigated for such a mistake for a long time. Please bear in mind the lesson of blood.

Here is a brief introduction to checksum (checksum).

In computer network communication, in order to check whether there is an error in the process of data transmission, the data is usually transmitted together with the checksum. When the receiver receives the data, it will calculate the checksum again. If it is different from the original checksum, it will be regarded as an error, discard the data packet and return the icmp message.

The basic idea of the algorithm:

The checksum algorithms of IP/ICMP/IGMP/TCP/UDP and other protocols are all the same, and they all use the data stream as a 16-bit integer stream for repeated superposition calculation. In order to calculate the check sum, first set the check sum field to 0. Then, the binary inverse code is summed for each 16 bits in the valid data range, and the result is stored in the check sum field, and if the data length is odd, add a byte 0. When the data is received, the sum of binary inverse codes is also carried out for each 16-bit number in the valid data range. Since the receiver includes the check sum in the header of the sender in the process of calculation, if there is no error in the header during transmission, the result of the receiver's calculation should be all zero or all one (see implementation, the essence is the same). If the result is not all zero or all one, then the data is wrong.

/ * checksum algorithm * / unsigned short cal_chksum (unsigned short * addr,int len) {int nleft=len; int sum=0; unsigned short * wattaddr; unsigned short answer=0; / * accumulate the ICMP header binary data in 2 bytes * / while (nleft > 1) {sum+=*w++; nleft-=2 } / * if the ICMP header is odd, there will be one byte left. Treat * * a byte as a high byte of 2-byte data, and 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;}

(3) unpacking of ICMP packet

If you know how to package the package, it is not difficult to unpack it. Note that when we receive an ICMP packet, we do not just think that this package is the ICMP echo reply packet we sent out. We need to add a layer of code to determine whether the id and seq fields of the ICMP message are in line with the settings of the ICMP message we sent, to verify the correctness of the ICMP reply package.

Int icmp_unpack (char* buf, int len) {int iphdr_len; struct timeval begin_time, recv_time, offset_time; int rtt; / / round trip time struct ip* ip_hdr = (struct ip*) buf; iphdr_len = ip_hdr- > ip_hl*4; struct icmp* icmp = (struct icmp*) (buf+iphdr_len); / / skip the IP header to len-=iphdr_len; / / icmp packet length if (len)

< 8) //判断长度是否为ICMP包长度 { fprintf(stderr, "Invalid icmp packet.Its length is less than 8\n"); return -1; } //判断该包是ICMP回送回答包且该包是我们发出去的 if((icmp->

Icmp_type = = ICMP_ECHOREPLY) & & (icmp- > icmp_id = = (pid & 0xffff)) {if ((icmp- > icmp_seq)

< 0) || (icmp->

Icmp_seq > PACKET_SEND_MAX_NUM) {fprintf (stderr, "icmp packet seq is out of range!\ n"); return-1;} ping_ packet [ICMP-> icmp_seq]. Flag = 0; begin_time = ping_ packet [ICMP-> icmp_seq] .begin _ time; / / remove the issue time of the packet gettimeofday (& recv_time, NULL) Offset_time = cal_time_offset (begin_time, recv_time); rtt= offset_time.tv_sec*1000 + offset_time.tv_usec/1000; / / millisecond in printf ("% d byte from% s: icmp_seq=%u ttl=%d rtt=%d ms\ n", len, inet_ntoa (ip_hdr- > ip_src), icmp- > icmp_seq, ip_hdr- > ip_ttl, rtt) } else {fprintf (stderr, "Invalid ICMP packet! Its id is not matched!\ n "); return-1;} return 0;}

Second, the construction of the sending thread

According to the framework of the PING program, we need to set up a thread for sending ping packets. My idea is this: we use sendto to send packets, and we keep the sending rate at 1 second. We need a global variable to record the sending time of * ping packets. In addition, we also need a global variable to record how many ping packets we have sent. These two variables are used to calculate the data after receiving a reply from the ping packet.

Void ping_send () {char send_buf; memset (send_buf, 0, sizeof (send_buf)); gettimeofday (& start_time, NULL); / / record the time that * ping packets were sent while (alive) {int size = 0; gettimeofday (& (ping_ packet [send _ count] .begin _ time), NULL); ping_ packet [send _ count]. Flag = 1 / / set the packet to be icmp_pack ((struct icmp*) send_buf, send_count, 64); / / encapsulate the icmp package size = sendto (rawsock, send_buf, 64,0, (struct sockaddr*) & dest, sizeof (dest)); send_count++; / / record the number of ping packets sent (size

< 0) { fprintf(stderr, "send icmp packet fail!\n"); continue; } sleep(1); } } 三、收包线程的搭建 我们同样建立一个接收包的线程,这里我们采用select函数进行收包,并为select函数设置超时时间为200us,若发生超时,则进行下一个循环。同样地,我们也需要一个全局变量来记录成功接收到的ping回复包的数量。 void ping_recv() { struct timeval tv; tv.tv_usec = 200; //设置select函数的超时时间为200us tv.tv_sec = 0; fd_set read_fd; char recv_buf[512]; memset(recv_buf, 0 ,sizeof(recv_buf)); while(alive) { int ret = 0; FD_ZERO(&read_fd); FD_SET(rawsock, &read_fd); ret = select(rawsock+1, &read_fd, NULL, NULL, &tv); switch(ret) { case -1: fprintf(stderr,"fail to select!\n"); break; case 0: break; default: { int size = recv(rawsock, recv_buf, sizeof(recv_buf), 0); if(size < 0) { fprintf(stderr,"recv data fail!\n"); continue; } ret = icmp_unpack(recv_buf, size); //对接收的包进行解封 if(ret == -1) //不是属于自己的icmp包,丢弃不处理 { continue; } recv_count++; //接收包计数 } break; } } } 四、中断处理 我们规定了一次ping发送的包的***值为64个,若超出该数值就停止发送。作为PING的使用者,我们一般只会发送若干个包,若有这几个包顺利返回,我们就crtl+c中断ping。这里的代码主要是为中断信号写一个中断处理函数,将alive这个全局变量设置为0,进而使发送ping包的循环停止而结束程序。 oid icmp_sigint(int signo) { alive = 0; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time); } signal(SIGINT, icmp_sigint); 五、总体实现 各模块介绍完了,现在贴出完整代码。 #include #include #include #include #include #include #include #include #include #include #include #include #define PACKET_SEND_MAX_NUM 64 typedef struct ping_packet_status { struct timeval begin_time; struct timeval end_time; int flag; //发送标志,1为已发送 int seq; //包的序列号 }ping_packet_status; ping_packet_status ping_packet[PACKET_SEND_MAX_NUM]; int alive; int rawsock; int send_count; int recv_count; pid_t pid; struct sockaddr_in dest; struct timeval start_time; struct timeval end_time; struct timeval time_interval; /*校验和算法*/ unsigned short cal_chksum(unsigned short *addr,int len) { int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; /*把ICMP报头二进制数据以2字节为单位累加起来*/ while(nleft>

1) {sum+=*w++; nleft-=2;} / * if the ICMP header is odd, there will be one byte left. Treat * * a byte as a high byte of 2-byte data, and 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;} struct timeval cal_time_offset (struct timeval begin, struct timeval end) {struct timeval ans; ans.tv_sec = end.tv_sec-begin.tv_sec; ans.tv_usec = end.tv_usec-begin.tv_usec; if (ans.tv_usec

< 0) //如果接收时间的usec小于发送时间的usec,则向sec域借位 { ans.tv_sec--; ans.tv_usec+=1000000; } return ans; } void icmp_pack(struct icmp* icmphdr, int seq, int length) { int i = 0; icmphdr->

Icmp_type = ICMP_ECHO; icmphdr- > icmp_code = 0; icmphdr- > icmp_cksum = 0; icmphdr- > icmp_seq = seq; icmphdr- > icmp_id = pid & 0xfff; for (icmphdr- > icmp_cksum = cal_chksum ((unsigned short*) icmphdr, length) } int icmp_unpack (char* buf, int len) {int iphdr_len; struct timeval begin_time, recv_time, offset_time; int rtt; / / round trip time struct ip* ip_hdr = (struct ip*) buf; iphdr_len = ip_hdr- > ip_hl*4; struct icmp* icmp = (struct icmp*) (buf+iphdr_len); len-=iphdr_len / / icmp packet length if (len

< 8) //判断长度是否为ICMP包长度 { fprintf(stderr, "Invalid icmp packet.Its length is less than 8\n"); return -1; } //判断该包是ICMP回送回答包且该包是我们发出去的 if((icmp->

Icmp_type = = ICMP_ECHOREPLY) & & (icmp- > icmp_id = = (pid & 0xffff)) {if ((icmp- > icmp_seq)

< 0) || (icmp->

Icmp_seq > PACKET_SEND_MAX_NUM) {fprintf (stderr, "icmp packet seq is out of range!\ n"); return-1;} ping_ packet [ICMP-> icmp_seq]. Flag = 0; begin_time = ping_ packet [ICMP-> icmp_seq] .begin _ time; gettimeofday (& recv_time, NULL) Offset_time = cal_time_offset (begin_time, recv_time); rtt= offset_time.tv_sec*1000 + offset_time.tv_usec/1000; / / millisecond in printf ("% d byte from% s: icmp_seq=%u ttl=%d rtt=%d ms\ n", len, inet_ntoa (ip_hdr- > ip_src), icmp- > icmp_seq, ip_hdr- > ip_ttl, rtt) } else {fprintf (stderr, "Invalid ICMP packet! Its id is not matched!\ n "); return-1;} return 0;} void ping_send () {char send_buf [128]; memset (send_buf, 0, sizeof (send_buf)); gettimeofday (& start_time, NULL); / / record the time when * ping packets were sent while (alive) {int size = 0 Gettimeofday (& (ping_ packet [send _ count] .begin _ time), NULL); ping_ packet [send _ count]. Flag = 1; / / mark this as if the packet has been sent icmp_pack ((struct icmp*) send_buf, send_count, 64); / / encapsulates the icmp package size = sendto (rawsock, send_buf, 64,0, (struct sockaddr*) & dest, sizeof (dest)) Send_count++; / / record the number of ping packets sent if (size

< 0) { fprintf(stderr, "send icmp packet fail!\n"); continue; } sleep(1); } } void ping_recv() { struct timeval tv; tv.tv_usec = 200; //设置select函数的超时时间为200us tv.tv_sec = 0; fd_set read_fd; char recv_buf[512]; memset(recv_buf, 0 ,sizeof(recv_buf)); while(alive) { int ret = 0; FD_ZERO(&read_fd); FD_SET(rawsock, &read_fd); ret = select(rawsock+1, &read_fd, NULL, NULL, &tv); switch(ret) { case -1: fprintf(stderr,"fail to select!\n"); break; case 0: break; default: { int size = recv(rawsock, recv_buf, sizeof(recv_buf), 0); if(size < 0) { fprintf(stderr,"recv data fail!\n"); continue; } ret = icmp_unpack(recv_buf, size); //对接收的包进行解封 if(ret == -1) //不是属于自己的icmp包,丢弃不处理 { continue; } recv_count++; //接收包计数 } break; } } } void icmp_sigint(int signo) { alive = 0; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time); } void ping_stats_show() { long time = time_interval.tv_sec*1000+time_interval.tv_usec/1000; /*注意除数不能为零,这里send_count有可能为零,所以运行时提示错误*/ printf("%d packets transmitted, %d recieved, %d%c packet loss, time %ldms\n", send_count, recv_count, (send_count-recv_count)*100/send_count, '%', time); } int main(int argc, char* argv[]) { int size = 128*1024;//128k struct protoent* protocol = NULL; char dest_addr_str[80]; memset(dest_addr_str, 0, 80); unsigned int inaddr = 1; struct hostent* host = NULL; pthread_t send_id,recv_id; if(argc < 2) { printf("Invalid IP ADDRESS!\n"); return -1; } protocol = getprotobyname("icmp"); //获取协议类型ICMP if(protocol == NULL) { printf("Fail to getprotobyname!\n"); return -1; } memcpy(dest_addr_str, argv[1], strlen(argv[1])+1); rawsock = socket(AF_INET,SOCK_RAW,protocol->

P_proto); if (rawsock

< 0) { printf("Fail to create socket!\n"); return -1; } pid = getpid(); setsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); //增大接收缓冲区至128K bzero(&dest,sizeof(dest)); dest.sin_family = AF_INET; inaddr = inet_addr(argv[1]); if(inaddr == INADDR_NONE) //判断用户输入的是否为IP地址还是域名 { //输入的是域名地址 host = gethostbyname(argv[1]); if(host == NULL) { printf("Fail to gethostbyname!\n"); return -1; } memcpy((char*)&dest.sin_addr, host->

H_addr, host- > h_length);} else {memcpy ((char*) & dest.sin_addr, & inaddr, sizeof (inaddr)); / / enter IP address} inaddr = dest.sin_addr.s_addr Printf ("PING% s, (% d.%d.%d.%d) 56 (84) bytes of data.\ n", dest_addr_str, (inaddr&0x000000ff), (inaddr&0x0000ff00) > > 8, (inaddr&0x00ff0000) > > 16, (inaddr&0xff000000) > > 24); alive = 1; / / controls the sending and receiving of ping signal (SIGINT, icmp_sigint) If (pthread_create (& send_id, NULL, (void*) ping_send, NULL) {printf ("Fail to create ping send thread!\ n"); return-1;} if (pthread_create (& recv_id, NULL, (void*) ping_recv, NULL)) {printf ("Fail to create ping recv thread!\ n"); return-1 } pthread_join (send_id, NULL); / / wait for the send ping thread to end before the process ends pthread_join (recv_id, NULL); / / wait for the recv ping thread to end before the process ends ping_stats_show (); close (rawsock); return 0;}

The compilation and experimental phenomena are as follows:

My experimental environment is two servers, the host that initiates ping is 172.0.5.183, and the host that is ping is 172.0.5.182. Here are my two experimental phenomena (ping IP and ping domain name).

Pay special attention to:

Only root users can use the socket () function to generate the original socket. To enable the general user of Linux to execute the above program, you need to do the following special operations: log in with root and compile the above program gcc-lpthread-o ping ping.c.

As can be seen from the experimental phenomenon, PING is successful, indicating that the network between the two hosts is connected, and all the ping packets sent have received a reply.

The following is the PING program that comes with the Linux system. We can compare the difference between the PING program we designed and the PING program that comes with the system.

The above is all the contents of the article "how to use PING in Linux". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report