002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)
生活随笔
收集整理的這篇文章主要介紹了
002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一.大致流程:
??? 將ICMP頭和時間數據設置好后,通過創建好的原始套接字socket發出去。目的主機計算效驗和后會將數據原樣返回,用當前時間和返回的數據結算時間差,計算出rtt。
?
二.數據結構:
ICMP時間戳請求與應答報文格式:
????????????????????????????????????
ICMP頭部數據結構:
struct icmp //占8字節,在<netinet/ip_icmp.h>頭文件中定義 {u_int8_t icmp_type; //類型u_int8_t icmp_code; //代碼u_int16_t icmp_cksum; //檢驗和u_int16_t icd_id; //標識符u_int16_t icd_seq; //序列號 }注:對于icmp時間戳請求與應答是這樣的,我將不必要的數據定義省略了。
struct timeval時間結構體:
struct timeval //該結構在<time.h>中定義 {__time_t tv_sec; // 秒,實際上為long int類型__suseconds_t tv_usec; // 微秒(百萬分之一秒),實際上為long int類型 };?
三.說明:
??? 1.我們以ping www.baidu.com為例,但我們并沒有計算平均rtt
??? 2.我們并沒有手動創建IP頭,而是交給了內核去幫我們處理
??? 3.中間設計到的函數需要查相關資料
?
四.代碼實現:
1 /* 2 ============================================================================ 3 Name : myping.c 4 Author : huh 5 Version : 0.01 6 Copyright : Your copyright notice 7 Description : Hello World in C, Ansi-style 8 ============================================================================ 9 */ 10 11 #include <sys/types.h> 12 #include <sys/select.h> 13 #include <stdio.h> 14 #include <time.h> 15 #include <string.h> 16 #include <netdb.h> 17 #include <arpa/inet.h> 18 #include <unistd.h> 19 #include <signal.h> 20 #include <sys/time.h> 21 #include <sys/socket.h> 22 #include <netinet/ip_icmp.h> 23 24 #define PACKET_SIZE 1024*4 25 26 int pid; 27 int sockfd; 28 int datalen = 56; 29 int nsent = 1, nrecv = 1; 30 char sendbuf[PACKET_SIZE]; 31 char recvbuf[PACKET_SIZE]; 32 struct sockaddr_in dest_addr; //socket目的地址 33 struct sockaddr_in src_addr; 34 struct timeval tvrecv; 35 36 void send_packet(); 37 void un_packet(int); 38 void tv_sub(struct timeval *out, struct timeval *in); 39 unsigned short in_chksum(unsigned short *addr, int len); 40 41 int main() 42 { 43 pid = getpid(); 44 char str[20]; 45 unsigned int inaddr; 46 struct hostent *host; 47 int size = 1024 * 25; 48 strcpy(str,"www.baidu.com"); 49 //IP地址(域名)到無符號整數的轉換 50 inaddr = inet_addr(str); 51 if (inaddr == INADDR_NONE) 52 { 53 host = gethostbyname(str); 54 if (host == NULL) 55 { 56 printf("參數格式不正確,請重新輸入!\n"); 57 return 0; 58 } 59 memcpy((char*) &inaddr, host->h_addr, sizeof(dest_addr.sin_addr)); 60 } 61 //設置套接字地址 62 dest_addr.sin_family = AF_INET; 63 memcpy((char *)&dest_addr.sin_addr, (char *)&inaddr,sizeof(inaddr)); 64 //創建套接字 65 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 66 //改變socket緩沖區大小 67 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); 68 69 printf("PING %s (%s) %d(84) bytes of data.\n",str, inet_ntoa(dest_addr.sin_addr), datalen); 70 //不停的發送和接受ICMP數據包 71 while (1) 72 { 73 send_packet(); 74 int src_addr_len = sizeof(struct sockaddr_in); 75 //接收數據包,一直阻塞到有數據包到達為止 76 int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &src_addr, (socklen_t *) &src_addr_len); 77 if(len < 0) 78 printf("recvfrom error!\n"); 79 un_packet(len); 80 sleep(1); //間隔一秒,后面會用信號實現每秒發送一個ICMP包。 81 } 82 return 0; 83 } 84 85 //解包 86 void un_packet(int len) 87 { 88 int hlen1; 89 double rtt; 90 struct ip *ip; 91 struct icmp *icmp; 92 struct timeval *tvsend; 93 94 ip = (struct ip *) recvbuf; 95 hlen1 = ip->ip_hl << 2; 96 if (ip->ip_p != IPPROTO_ICMP) 97 return ; 98 99 icmp = (struct icmp *) (recvbuf + hlen1); 100 len -= hlen1; 101 102 if ((icmp->icmp_type == ICMP_ECHOREPLY)) 103 { 104 if (icmp->icmp_id != pid) 105 return; 106 tvsend = (struct timeval *) icmp->icmp_data; //發送時間 107 gettimeofday(&tvrecv, NULL); //得到當前時間 108 109 tv_sub(&tvrecv, tvsend); //計算接收和發送的時間差 110 rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0; //以毫秒單位計算rtt 111 printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3fms\n", len, inet_ntoa(src_addr.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt); 112 nrecv++; 113 } 114 } 115 116 //手動構建數據包,并通過原始套接字發送 117 void send_packet() 118 { 119 int len; 120 struct icmp *icmp; 121 icmp = (struct icmp *) (sendbuf); 122 icmp->icmp_type = ICMP_ECHO; //拼接icmp 123 icmp->icmp_code = 0; 124 icmp->icmp_id = pid; //2字節 125 icmp->icmp_seq = nsent++; //2字節 126 memset(icmp->icmp_data, 0xa5, datalen); 127 gettimeofday((struct timeval *) icmp->icmp_data, NULL); //將發送時間作為數據傳遞過去 128 129 len = datalen + 8; 130 icmp->icmp_cksum = 0; //校驗和需要先置0 131 icmp->icmp_cksum = in_chksum((unsigned short *) icmp, len); //計算效驗和 132 133 sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &dest_addr, 134 sizeof(dest_addr)); //將包發出去 135 //printf("package have sent!\n"); 136 } 137 138 //計算效驗和 139 unsigned short in_chksum(unsigned short *addr, int len) 140 { 141 int nleft = len; 142 int sum = 0; 143 unsigned short *w = addr; 144 unsigned short answer = 0; 145 //把ICMP報頭二進制數據以2字節為單位累加起來 146 while (nleft > 1) 147 { 148 sum += *w++; 149 nleft -= 2; 150 } 151 if (nleft == 1) 152 { 153 *(unsigned char *) (&answer) = *(unsigned char *) w; 154 sum += answer; 155 } 156 sum = (sum >> 16) + (sum & 0xffff); 157 sum += (sum >> 16); 158 answer = ~sum; 159 return answer; 160 } 161 162 //計算時間差 163 void tv_sub(struct timeval *out, struct timeval *in) 164 { 165 if ((out->tv_usec -= in->tv_usec) < 0) 166 { 167 --out->tv_sec; 168 out->tv_usec += 1000000; 169 } 170 out->tv_sec -= in->tv_sec; 171 } myping?
轉載于:https://www.cnblogs.com/ruo-yu/p/4978968.html
總結
以上是生活随笔為你收集整理的002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node.js学习笔记14—微型社交网站
- 下一篇: 第二百三十一天 how can I 坚