Linux original socket (2)-icmp request and receipt, linux-icmp
I. Overview
The previous arp request used the original socket of the link layer. Icmp is encapsulated in ip datagram, so icmp requests can directly use the original socket at the network layer, that is, the first parameter of socket () is PF_INET. As follows:
1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
Different types of icmp packets have different formats. We use the icmp echo request and the echo Response Message format (that is, the packet type used by the ping program) as an example:
If the type is 8, the request is returned. If the value is 0, the response is returned. The Checksum must be calculated by yourself. The identifier is generally the process ID of the program. Number customization, generally starting from 1. The timestamp can be placed in the option data to calculate the time consumed by ping!
The icmp packet structure is defined in netinet/ip_icmp.h.
1 struct icmp 2 { 3 u_int8_t icmp_type; /* type of message, see below */ 4 u_int8_t icmp_code; /* type sub code */ 5 u_int16_t icmp_cksum; /* ones complement checksum of struct */ 6 union 7 { 8 u_char ih_pptr; /* ICMP_PARAMPROB */ 9 struct in_addr ih_gwaddr; /* gateway address */10 struct ih_idseq /* echo datagram */11 {12 u_int16_t icd_id;13 u_int16_t icd_seq;14 } ih_idseq;15 u_int32_t ih_void;16 17 /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */18 struct ih_pmtu19 {20 u_int16_t ipm_void;21 u_int16_t ipm_nextmtu;22 } ih_pmtu;23 24 struct ih_rtradv25 {26 u_int8_t irt_num_addrs;27 u_int8_t irt_wpa;28 u_int16_t irt_lifetime;29 } ih_rtradv;30 } icmp_hun;31 #define icmp_pptr icmp_hun.ih_pptr32 #define icmp_gwaddr icmp_hun.ih_gwaddr33 #define icmp_id icmp_hun.ih_idseq.icd_id34 #define icmp_seq icmp_hun.ih_idseq.icd_seq35 #define icmp_void icmp_hun.ih_void36 #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void37 #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu38 #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs39 #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa40 #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime41 union42 {43 struct44 {45 u_int32_t its_otime;46 u_int32_t its_rtime;47 u_int32_t its_ttime;48 } id_ts;49 struct50 {51 struct ip idi_ip;52 /* options and then 64 bits of data */53 } id_ip;54 struct icmp_ra_addr id_radv;55 u_int32_t id_mask;56 u_int8_t id_data[1];57 } icmp_dun;58 #define icmp_otime icmp_dun.id_ts.its_otime59 #define icmp_rtime icmp_dun.id_ts.its_rtime60 #define icmp_ttime icmp_dun.id_ts.its_ttime61 #define icmp_ip icmp_dun.id_ip.idi_ip62 #define icmp_radv icmp_dun.id_radv63 #define icmp_mask icmp_dun.id_mask64 #define icmp_data icmp_dun.id_data65 };
Ii. icmp request code
1/** 2 * @ file icmp_request.c 3 */4 5 # include <stdio. h> 6 # include <stdlib. h> 7 # include <string. h> 8 # include <unistd. h> 9 # include <sys/socket. h> 10 # include <arpa/inet. h> 11 # include <netinet/in. h> 12 # include <netinet/ip_icmp.h> 13 # include <sys/time. h> 14 15/* icmp packet length */16 # define ICMP_PACKET_LEN sizeof (struct icmp) 17 18 void err_exit (const char * err_msg) 19 {20 perror (err_msg); 21 exi T (1); 22} 23 24/* checksum */25 unsigned short check_sum (unsigned short * addr, int len) 26 {27 int nleft = len; 28 int sum = 0; 29 unsigned short * w = addr; 30 unsigned short answer = 0; 31 32 while (nleft> 1) 33 {34 sum + = * w ++; 35 nleft-= 2; 36} 37 if (nleft = 1) 38 {39 * (unsigned char *) (& answer) = * (unsigned char *) w; 40 sum + = answer; 41} 42 43 sum = (sum> 16) + (sum & 0 xffff); 44 sum + = (Sum> 16); 45 answer = ~ Sum; 46 47 return answer; 48} 49 50/* fill in icmp packets */51 struct icmp * fill_icmp_packet (int icmp_type, int icmp_sequ) 52 {53 struct icmp * icmp_packet; 54 55 icmp_packet = (struct icmp *) malloc (bytes); 56 icmp_packet-> icmp_type = icmp_type; 57 icmp_packet-> icmp_code = 0; 58 icmp_packet-> icmp_cksum = 0; 59 icmp_packet-> icmp_id = htons (getpid (); 60 icmp_packet-> icmp_seq = icmp_sequ; 61/* when sending */62 gettimeofday (struct timeval *) icmp_packet-> icmp_data, NULL); 63/* checksum */64 icmp_packet-> icmp_cksum = check_sum (unsigned short *) icmp_packet, ICMP_PACKET_LEN); 65 66 return icmp_packet; 67} 68 69/* Send icmp request */70 void icmp_request (const char * dst_ip, int icmp_type, int icmp_sequ) 71 {72 struct sockaddr_in dst_addr; 73 struct icmp * icmp_packet; 74 int sockfd, ret_len; 75 char buf [ICMP _ PACKET_LEN]; 76 77/* request address */78 bzero (& dst_addr, sizeof (struct sockaddr_in); 79 rows = AF_INET; 80 rows = inet_addr (dst_ip ); 81 82 if (sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP) =-1) 83 err_exit ("sockfd ()"); 84 85/* icmp packet */86 icmp_packet = fill_icmp_packet (icmp_type, icmp_sequ); 87 memcpy (buf, icmp_packet, ICMP_PACKET_LEN); 88 89/* Send request */90 re T_len = sendto (sockfd, buf, ICMP_PACKET_LEN, 0, (struct sockaddr *) & dst_addr, sizeof (struct sockaddr_in); 91 if (ret_len> 0) 92 printf ("sendto () OK !!! \ N "); 93 94 close (sockfd); 95} 96 97 int main (int argc, const char * argv []) 98 {99 if (argc! = 2) 100 {101 printf ("usage: % s dst_ip \ n", argv [0]); 102 exit (1 ); 103} 104 105/* Send icmp request */106 icmp_request (argv [1], 8, 1); 107 108 109 return 0}
Process: The target IP address of the icmp request received by the command line. The value of icmp is 8 and the serial number is 1. Then, create the network address structure through the target IP address, then create the original ICMP socket, fill in the icmp packet, and fill in the sending time to the icmp data structure.
Iii. icmp receiving code
1/** 2 * @ file icmp_recv.c 3 */4 5 # include <stdio. h> 6 # include <stdlib. h> 7 # include <string. h> 8 # include <unistd. h> 9 # include <sys/time. h> 10 # include <sys/socket. h> 11 # include <arpa/inet. h> 12 # include <netinet/in. h> 13 # include <netinet/ip. h> 14 # include <netinet/ip_icmp.h> 15 16/* IP header length */17 # define IP_HEADER_LEN sizeof (struct ip) 18/* icmp packet length */19 # define ICMP_PACKET_LEN sizeof (struct icmp) 20/* IP + ICMP length */21 # define IP_ICMP_PACKET_LEN IP_HEADER_LEN + ICMP_PACKET_LEN22 23 void err_exit (const char * err_msg) 24 {25 perror (err_msg); 26 exit (1 ); 27} 28 29/* calculating the millisecond difference between the sending time and the receiving time */30 float time_interval (struct timeval * recv_time, struct timeval * send_time) 31 {32 float msec = 0; 33 34/* if the receipt time is less subtle than the sending time */35 if (recv_time-> TV _usec <send_time-> TV _usec) 36 {37 recv_time-> TV _sec-= 1; 38 recv_time-> TV _usec + = 1000000; 39} 40 msec = (recv_time-> TV _sec-send_time-> TV _sec) * 1000.0 + (recv_time-> TV _usec-send_time-> TV _usec) /1000.0; 41 42 return msec; 43} 44 45 int main (void) 46 {47 struct ip * ip_header; 48 struct icmp * icmp_packet; 49 char buf [IP_ICMP_PACKET_LEN]; 50 struct timeval * recv_timeval, * send_timeval; 51 int sockfd, ret_len; 52 53 if (sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP) =-1) 54 err_exit ("sockfd ()"); 55 56 recv_timeval = malloc (sizeof (struct timeval); 57 while (1) 58 {59 ret_len = recv (sockfd, buf, IP_ICMP_PACKET_LEN, 0); 60 if (ret_len> 0) 61 {62/* Receipt time */63 gettimeofday (recv_timeval, NULL ); 64/* retrieve ip header */65/* retrieve icmp packet */66 ip_header = (struct ip *) buf; 67 icmp_packet = (struct icmp *) (buf + IP_HEADER_LEN ); 68/* retrieve the sending time */69 send_timeval = (struct timeval *) icmp_packet-> icmp_data; 70 printf ("===================================\ n "); 71 printf ("from ip: % s \ n", inet_ntoa (ip_header-> ip_src); 72 printf ("icmp_type: % d \ n", icmp_packet-> icmp_type ); 73 printf ("icmp_code: % d \ n", icmp_packet-> icmp_code); 74 printf ("time interval: %. 3fms \ n ", time_interval (recv_timeval, send_timeval); 75} 76} 77 78 free (recv_timeval); 79 close (sockfd); 80 return 0; 81}
Process: Create an ICMP-type original socket and receive it directly. First obtain the receiving time, and then extract the ip header, icmp packet, and then the icmp request time. Obtain the source ip address from the ip header and the type and code number of the message from the icmp message. The difference in milliseconds is calculated based on the sending time and receipt time!
Iv. Experiment
1. Open wireshark and observe it together. Run icmp_recv as root and then icmp_request
The icmp type is 0 and the code is 0. The response time is similar to that of our program.
2. Now we request an inaccessible IP address.
When the host fails, the returned icmp message type is 3 and the code is 1. Because the message structure is different, the outgoing time is not normal, so the time interval calculated here is not normal. The result in wireshark is that the local machine automatically broadcasts an arp request, but no machine answers the local machine.
Some icmp types: