标签:
1 #include <stdio.h> 2 #include <signal.h> 3 #include <arpa/inet.h> 4 #include <sys/types.h> 5 #include <sys/socket.h> 6 #include <unistd.h> 7 #include <netinet/in.h> 8 #include <netinet/ip.h> 9 #include <netinet/ip_icmp.h> 10 #include <netdb.h> 11 #include <setjmp.h> 12 #include <errno.h> 13 #define PACKET_SIZE 4096 14 #define MAX_WAIT_TIME 5 15 #define MAX_NO_PACKETS 3 16 char sendpacket[PACKET_SIZE]; 17 char recvpacket[PACKET_SIZE]; 18 int sockfd,datalen=56; 19 int nsend=0,nreceived=0; 20 struct sockaddr_in dest_addr; 21 pid_t pid; 22 struct sockaddr_in from; 23 struct timeval tvrecv; 24 void statistics(int signo); 25 unsigned short cal_chksum(unsigned short *addr,int len); 26 int pack(int pack_no); 27 void send_packet(void); 28 void recv_packet(void); 29 int unpack(char *buf,int len); 30 void tv_sub(struct timeval *out,struct timeval *in); 31 void statistics(int signo) 32 { printf("\n--------------------PING statistics-------------------\n"); 33 printf("%d packets transmitted, %d received , %%%d lost\n",nsend,nreceived, 34 (nsend-nreceived)/nsend*100); 35 close(sockfd); 36 exit(1); 37 } 38 /*校验和算法*/ 39 unsigned short cal_chksum(unsigned short *addr,int len) 40 { int nleft=len; 41 int sum=0; 42 unsigned short *w=addr; 43 unsigned short answer=0; 44 45 /*把ICMP报头二进制数据以2字节为单位累加起来*/ 46 while(nleft>1) 47 { sum+=*w++; 48 nleft-=2; 49 } 50 /*若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/ 51 if( nleft==1) 52 { *(unsigned char *)(&answer)=*(unsigned char *)w; 53 sum+=answer; 54 } 55 sum=(sum>>16)+(sum&0xffff); 56 sum+=(sum>>16); 57 answer=~sum; 58 return answer; 59 } 60 /*设置ICMP报头*/ 61 int pack(int pack_no) 62 { int i,packsize; 63 struct icmp *icmp; 64 struct timeval *tval; 65 icmp=(struct icmp*)sendpacket; 66 icmp->icmp_type=ICMP_ECHO; 67 icmp->icmp_code=0; 68 icmp->icmp_cksum=0; 69 icmp->icmp_seq=pack_no; 70 icmp->icmp_id=pid; 71 packsize=8+datalen; 72 tval= (struct timeval *)icmp->icmp_data; 73 gettimeofday(tval,NULL); /*记录发送时间*/ 74 icmp->icmp_cksum=cal_chksum( (unsigned short *)icmp,packsize); /*校验算法*/ 75 return packsize; 76 } 77 /*发送三个ICMP报文*/ 78 void send_packet() 79 { int packetsize; 80 while( nsend<MAX_NO_PACKETS) 81 { nsend++; 82 packetsize=pack(nsend); /*设置ICMP报头*/ 83 if( sendto(sockfd,sendpacket,packetsize,0, 84 (struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0 ) 85 { perror("sendto error"); 86 continue; 87 } 88 sleep(1); /*每隔一秒发送一个ICMP报文*/ 89 } 90 } 91 /*接收所有ICMP报文*/ 92 void recv_packet() 93 { int n,fromlen; 94 extern int errno; 95 signal(SIGALRM,statistics); 96 fromlen=sizeof(from); 97 while( nreceived<nsend) 98 { alarm(MAX_WAIT_TIME); 99 if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0, 100 (struct sockaddr *)&from,&fromlen)) <0) 101 { if(errno==EINTR)continue; 102 perror("recvfrom error"); 103 continue; 104 } 105 gettimeofday(&tvrecv,NULL); /*记录接收时间*/ 106 if(unpack(recvpacket,n)==-1)continue; 107 nreceived++; 108 } 109 } 110 /*剥去ICMP报头*/ 111 int unpack(char *buf,int len) 112 { int i,iphdrlen; 113 struct ip *ip; 114 struct icmp *icmp; 115 struct timeval *tvsend; 116 double rtt; 117 ip=(struct ip *)buf; 118 iphdrlen=ip->ip_hl<<2; /*求ip报头长度,即ip报头的长度标志乘4*/ 119 icmp=(struct icmp *)(buf+iphdrlen); /*越过ip报头,指向ICMP报头*/ 120 len-=iphdrlen; /*ICMP报头及ICMP数据报的总长度*/ 121 if( len<8) /*小于ICMP报头长度则不合理*/ 122 { printf("ICMP packets\‘s length is less than 8\n"); 123 return -1; 124 } 125 /*确保所接收的是我所发的的ICMP的回应*/ 126 if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) ) 127 { tvsend=(struct timeval *)icmp->icmp_data; 128 tv_sub(&tvrecv,tvsend); /*接收和发送的时间差*/ 129 rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000; /*以毫秒为单位计算rtt*/ 130 /*显示相关信息*/ 131 printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n", 132 len, 133 inet_ntoa(from.sin_addr), 134 icmp->icmp_seq, 135 ip->ip_ttl, 136 rtt); 137 } 138 else return -1; 139 } 140 main(int argc,char *argv[]) 141 { struct hostent *host; 142 struct protoent *protocol; 143 unsigned long inaddr=0l; 144 int waittime=MAX_WAIT_TIME; 145 int size=50*1024; 146 if(argc<2) 147 { printf("usage:%s hostname/IP address\n",argv[0]); 148 exit(1); 149 } 150 if( (protocol=getprotobyname("icmp") )==NULL) 151 { perror("getprotobyname"); 152 exit(1); 153 } 154 /*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/ 155 if( (sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) )<0) 156 { perror("socket error"); 157 exit(1); 158 } 159 /* 回收root权限,设置当前用户权限*/ 160 setuid(getuid()); 161 /*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的 162 的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/ 163 setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size) ); 164 bzero(&dest_addr,sizeof(dest_addr)); 165 dest_addr.sin_family=AF_INET; 166 /*判断是主机名还是ip地址*/ 167 if( inaddr=inet_addr(argv[1])==INADDR_NONE) 168 { if((host=gethostbyname(argv[1]) )==NULL) /*是主机名*/ 169 { perror("gethostbyname error"); 170 exit(1); 171 } 172 memcpy( (char *)&dest_addr.sin_addr,host->h_addr,host->h_length); 173 } 174 else /*是ip地址*/ 175 memcpy( (char *)&dest_addr,(char *)&inaddr,host->h_length); 176 /*获取main的进程id,用于设置ICMP的标志符*/ 177 pid=getpid(); 178 printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1], 179 inet_ntoa(dest_addr.sin_addr),datalen); 180 send_packet(); /*发送所有ICMP报文*/ 181 recv_packet(); /*接收所有ICMP报文*/ 182 statistics(SIGALRM); /*进行统计*/ 183 return 0; 184 } 185 /*两个timeval结构相减*/ 186 void tv_sub(struct timeval *out,struct timeval *in) 187 { if( (out->tv_usec-=in->tv_usec)<0) 188 { --out->tv_sec; 189 out->tv_usec+=1000000; 190 } 191 out->tv_sec-=in->tv_sec; 192 } 193 /*------------- The End -----------*/
程序编译:
1 sudo gcc myping.c -o myping 2 sudo chmod u+s myping 3 ./myping www.baidu.com
程序运行结果:
1 PING www.baidu.com(180.97.33.107): 56 bytes data in ICMP packets. 2 64 byte from 180.97.33.107: icmp_seq=1 ttl=128 rtt=3003.000 ms 3 64 byte from 180.97.33.107: icmp_seq=2 ttl=128 rtt=2002.000 ms 4 64 byte from 180.97.33.107: icmp_seq=3 ttl=128 rtt=1001.000 ms 5 6 --------------------PING statistics------------------- 7 3 packets transmitted, 3 received , %0 lost
标签:
原文地址:http://www.cnblogs.com/wireless-dragon/p/5187492.html