In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)06/01 Report--
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, the last byte is left. Treat the last 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
Know how to package the package, then unpack it is not difficult, note that we received an ICMP package, we do not think that this package is we sent out the ICMP echo reply package, the new channel IELTS training we need to add a layer of code to determine whether the ICMP message id and seq fields 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 time when the first ping package was sent. In addition, we also need a global variable to record how many ping packets we 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 when the first ping packet was 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包的循环停止而结束程序。 void icmp_sigint(int signo){ alive = 0; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time);}signal(SIGINT, icmp_sigint); 五、总体实现 各模块介绍完了,现在贴出完整代码。 1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 14 15 #define PACKET_SEND_MAX_NUM 64 16 17 typedef struct ping_packet_status 18 { 19 struct timeval begin_time; 20 struct timeval end_time; 21 int flag; //发送标志,1为已发送 22 int seq; //包的序列号 23 }ping_packet_status; 24 25 26 27 ping_packet_status ping_packet[PACKET_SEND_MAX_NUM]; 28 29 int alive; 30 int rawsock; 31 int send_count; 32 int recv_count; 33 pid_t pid; 34 struct sockaddr_in dest; 35 struct timeval start_time; 36 struct timeval end_time; 37 struct timeval time_interval; 38 39 /*校验和算法*/ 40 unsigned short cal_chksum(unsigned short *addr,int len) 41 { int nleft=len; 42 int sum=0; 43 unsigned short *w=addr; 44 unsigned short answer=0; 45 46 /*把ICMP报头二进制数据以2字节为单位累加起来*/ 47 while(nleft>1) 48 {49 sum+=*w++; 50 nleft-=2; 51} 52 / * if the ICMP header is odd, the last byte is left. Treat the last byte as a high byte of 2-byte data, and the low byte of this 2-byte data is 0. Continue to accumulate * / 53 if (nleft==1) 54 {55 * (unsigned char *) (& answer) = * (unsigned char *) w; 56 sum+=answer; 57} 58 sum= (sum > > 16) + (sum&0xffff); 59 sum+= (sum > > 16) 60 answer=~sum; 61 return answer; 62} 63 64 struct timeval cal_time_offset (struct timeval begin, struct timeval end) 65 {66 struct timeval ans; 67 ans.tv_sec = end.tv_sec-begin.tv_sec; 68 ans.tv_usec = end.tv_usec-begin.tv_usec; 69 if (ans.tv_usec)
< 0) //如果接收时间的usec小于发送时间的usec,则向sec域借位 70 { 71 ans.tv_sec--; 72 ans.tv_usec+=1000000; 73 } 74 return ans; 75 } 76 77 void icmp_pack(struct icmp* icmphdr, int seq, int length) 78 { 79 int i = 0; 80 81 icmphdr->Icmp_type = ICMP_ECHO; 82 icmphdr- > icmp_code = 0; 83 icmphdr- > icmp_cksum = 0; 84 icmphdr- > icmp_seq = seq; 85 icmphdr- > icmp_id = pid & 0xfff; 86 for [I] = I; 89} 90 91 icmphdr- > icmp_cksum = cal_chksum ((unsigned short*) icmphdr, length); 92} 93 int icmp_unpack (char* buf, int len) 95 {96 int iphdr_len 97 struct timeval begin_time, recv_time, offset_time; 98 int rtt; / / round trip time 99 100 struct ip* ip_hdr = (struct ip*) buf;101 iphdr_len = ip_hdr- > ip_hl*4;102 struct icmp* icmp = (struct icmp*) (buf+iphdr_len); 103 len-=iphdr_len; / / icmp packet length 104 if (len
< 8) //判断长度是否为ICMP包长度105 {106 fprintf(stderr, "Invalid icmp packet.Its length is less than 8\n");107 return -1;108 }109 110 //判断该包是ICMP回送回答包且该包是我们发出去的111 if((icmp->Icmp_type = = ICMP_ECHOREPLY) & & (icmp- > icmp_id = = (pid & 0xffff)) 112 {113 if ((icmp- > icmp_seq)
< 0) || (icmp->Icmp_seq > PACKET_SEND_MAX_NUM) 114 {115 fprintf (stderr, "icmp packet seq is out of range!\ n"); 116 return-1117} 118119 ping_ packet120 begin_time = ping_ packet120 begin_time = ping_ packet.begin _ time;121 gettimeofday (& recv_time, NULL) 122123 offset_time = cal_time_offset (begin_time, recv_time); 1224rtt= offset_time.tv_sec*1000 + offset_time.tv_usec/1000; / / Ms in 125,126 printf ("% d byte from% s: icmp_seq=%u ttl=%d rtt=%d ms\ n", 127len, inet_ntoa (ip_hdr- > ip_src), icmp- > icmp_seq, ip_hdr- > ip_ttl, rtt) 128 129} 130 else131 {132 fprintf (stderr, "Invalid ICMP packet! Its id is not matched!\ n "); 133 return-1 gettimeofday 134} 135 return 0136} 137138 void ping_send () 139 {140 char send_buf; 141 memset (send_buf, 0, sizeof (send_buf)); 142 gettimeofday (& start_time, NULL); / / record the time when the first ping packet was sent 143while (alive) 144{ 145int size = 0 146 gettimeofday (& (ping_ packet [send _ count] .begin _ time), NULL); 147 ping_ packet [send _ count]. Flag = 1; / / set this to set to 148149 icmp_pack ((struct icmp*) send_buf, send_count, 64); / / encapsulate the icmp packet size = sendto (rawsock, send_buf, 64,0, (struct sockaddr*) & dest, sizeof (dest)) 151if / / record the number of ping packets sent (size
< 0)153 {154 fprintf(stderr, "send icmp packet fail!\n");155 continue;156 }157 158 sleep(1);159 }160 }161 162 void ping_recv()163 {164 struct timeval tv;165 tv.tv_usec = 200; //设置select函数的超时时间为200us166 tv.tv_sec = 0;167 fd_set read_fd;168 char recv_buf[512];169 memset(recv_buf, 0 ,sizeof(recv_buf));170 while(alive)171 {172 int ret = 0;173 FD_ZERO(&read_fd);174 FD_SET(rawsock, &read_fd);175 ret = select(rawsock+1, &read_fd, NULL, NULL, &tv);176 switch(ret)177 {178 case -1:179 fprintf(stderr,"fail to select!\n");180 break;181 case 0:182 break;183 default:184 {185 int size = recv(rawsock, recv_buf, sizeof(recv_buf), 0);186 if(size < 0)187 {188 fprintf(stderr,"recv data fail!\n");189 continue;190 }191 192 ret = icmp_unpack(recv_buf, size); //对接收的包进行解封193 if(ret == -1) //不是属于自己的icmp包,丢弃不处理194 {195 continue;196 }197 recv_count++; //接收包计数198 }199 break;200 }201 202 }203 }204 205 void icmp_sigint(int signo)206 {207 alive = 0;208 gettimeofday(&end_time, NULL);209 time_interval = cal_time_offset(start_time, end_time);210 }211 212 void ping_stats_show()213 {214 long time = time_interval.tv_sec*1000+time_interval.tv_usec/1000;215 /*注意除数不能为零,这里send_count有可能为零,所以运行时提示错误*/216 printf("%d packets transmitted, %d recieved, %d%c packet loss, time %ldms\n",217 send_count, recv_count, (send_count-recv_count)*100/send_count, '%', time);218 }219 220 221 int main(int argc, char* argv[])222 {223 int size = 128*1024;//128k224 struct protoent* protocol = NULL;225 char dest_addr_str[80];226 memset(dest_addr_str, 0, 80);227 unsigned int inaddr = 1;228 struct hostent* host = NULL;229 230 pthread_t send_id,recv_id;231 232 if(argc < 2)233 {234 printf("Invalid IP ADDRESS!\n");235 return -1;236 }237 238 protocol = getprotobyname("icmp"); //获取协议类型ICMP239 if(protocol == NULL)240 {241 printf("Fail to getprotobyname!\n");242 return -1;243 }244 245 memcpy(dest_addr_str, argv[1], strlen(argv[1])+1);246 247 rawsock = socket(AF_INET,SOCK_RAW,protocol->P_proto); 248if (rawsock
< 0)249 {250 printf("Fail to create socket!\n");251 return -1;252 }253 254 pid = getpid();255 256 setsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); //增大接收缓冲区至128K257 258 bzero(&dest,sizeof(dest));259 260 dest.sin_family = AF_INET;261 262 inaddr = inet_addr(argv[1]);263 if(inaddr == INADDR_NONE) //判断用户输入的是否为IP地址还是域名264 {265 //输入的是域名地址266 host = gethostbyname(argv[1]);267 if(host == NULL)268 {269 printf("Fail to gethostbyname!\n");270 return -1;271 }272 273 memcpy((char*)&dest.sin_addr, host->H_addr, host- > h_length); 274} 275 else276 {277 memcpy ((char*) & dest.sin_addr, & inaddr, sizeof (inaddr)); / / enter IP address 278} 279 inaddr = dest.sin_addr.s_addr 280printf ("PING% s, (% d.%d.%d.%d) 56 (84) bytes of data.\ n", dest_addr_str,281 (inaddr&0x000000ff), (inaddr&0x0000ff00) > > 8282 (inaddr&0x00ff0000) > > 16, (inaddr&0xff000000) > > 24); 283,284 alive = 1; / / controls the sending and receiving of ping 285286 signal (SIGINT, icmp_sigint) 287 288 if (pthread_create (& send_id, NULL, (void*) ping_send, NULL)) 289 {290 printf ("Fail to create ping send thread!\ n"); 291 return-1 * 292} 293 294 if (pthread_create (& recv_id, NULL, (void*) ping_recv, NULL)) 295 {296 printf ("Fail to create ping recv thread!\ n"); 297 return-1 298} 299 300 pthread_join (send_id, NULL); / / wait for the end of the send ping thread before the process ends 301 pthread_join (recv_id, NULL); / / wait for the end of the recv ping thread before the process ends 302 303 ping_stats_show (); 304,305 close (rawsock); 306 return 0th 307 308}
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.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.