#include <stdio.h>#include <sys/time.h>#include <signal.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <netinet/in.h>#include <netinet/ip.h>#include <netdb.h>#include <setjmp.h>#include <errno.h>#include <netinet/ip_icmp.h>#include <string.h>#include <stdlib.h>#define PACKET_SIZE 4096 /* */#define MAX_WAIT_TIME 5#define MAX_NO_PACKETS 3 char sendpacket[PACKET_SIZE];char recvpacket[PACKET_SIZE];int sockfd,datalen=56;int nsend=0,nreceived=0;struct sockaddr_in dest_addr;pid_t pid;struct sockaddr_in from;struct timeval tvrecv;void statistics(int signo);unsigned short cal_chksum(unsigned short *addr,int len);int pack(int pack_no);void send_packet(void);void recv_packet(void);int unpack(char* buf,int len);void tv_sub(struct timeval *out,struct timeval* in); void statistics(int signo){ printf("\n-----------------PING statistics----------------\n"); printf("%d packet transmitted,%d received,%%%d lost \n",nsend,nreceived,(nsend-nreceived)/nsend*100); close(sockfd); exit(1);}/*校驗和算法*/unsigned short cal_chksum(unsigned short *addr,int len){ int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; /*把ICMP報頭二進制數(shù)據(jù)以2字節(jié)為單位累加起來*/ while(nleft>1) { sum+=*w++; nleft-=2; } /*若ICMP報頭為奇數(shù)個字節(jié),會剩下最后一個字節(jié)。把最后一個字節(jié)視為2字節(jié)數(shù)據(jù)的高字節(jié),這個2字節(jié)數(shù)據(jù)的低字節(jié)為0,繼續(xù)累加*/ if(nleft==1) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); answer=~sum; return answer;}/*設(shè)置ICMP報頭*/int pack(int pack_no){ int packsize; struct icmp *icmpp; struct timeval *tval; icmpp=(struct icmp *)sendpacket; icmpp->icmp_type=ICMP_ECHO; icmpp->icmp_code=0; icmpp->icmp_cksum=0; icmpp->icmp_seq=pack_no; icmpp->icmp_id=pid; packsize=8+datalen; tval=(struct timeval *)icmpp->icmp_data; gettimeofday(tval,NULL);/*記錄發(fā)送時間*/ icmpp->icmp_cksum=cal_chksum((unsigned short*)icmpp,packsize);/*校驗算法*/ return packsize;}/*發(fā)送3個ICMP報文*/void send_packet(){ int packetsize; while(nsend<MAX_NO_PACKETS) { nsend++; packetsize=pack(nsend);/*設(shè)置ICMP報文*/ if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr))<0) { perror("sendto error"); continue; } sleep(1);/*每隔一秒發(fā)送一個ICMP報文*/ }}/*接收所有ICMP報文*/void recv_packet(){ int n,fromlen; extern int errno; /*更多精彩內(nèi)容:http://www.bianceng.cn/Programming/cplus/*/ signal(SIGALRM,statistics); fromlen=sizeof(from); while(nreceived<10) { alarm(MAX_WAIT_TIME); if((n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen))<0) { if(errno==EINTR) { continue; } perror("recvfrom error\n"); continue; } gettimeofday(&tvrecv,NULL); /*記錄接收時間*/ if(unpack(recvpacket,n)==-1) continue; nreceived++; }}/*剝?nèi)CMP報頭*/int unpack(char *buf,int len){ int iphdrlen; struct ip *ipp; struct icmp *icmpp; struct timeval *tvsend; int rtt; ipp=(struct ip *)buf; iphdrlen=(ipp->ip_hl)*4;/*求IP報頭長度,即IP報頭的長度標(biāo)志乘4*/ icmpp=(struct icmp*)(buf+iphdrlen);/*超過IP報頭,指向ICMP報頭*/ len-=iphdrlen;/*ICMP報頭及ICMP數(shù)據(jù)報的總長度*/ if(len<8)/*小于ICMP報頭長度則不合理*/ { printf("ICMP packets\'s length is less than 8\n"); return -1; } /*確保所接收的是用戶所發(fā)的ICMP的回應(yīng)*/ if((icmpp->icmp_type==ICMP_ECHOREPLY)&&(icmpp->icmp_id==pid)) { tvsend=(struct timeval *)icmpp->icmp_data; tv_sub(&tvrecv,tvsend); /*接收和發(fā)送的時間差*/ rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;/*以毫秒為單位計算rtt*/ /*顯示相關(guān)信息*/ printf("%d byte from %s:icmp_seq=%u ttl=%d rtt=%d ms\n", len,inet_ntoa(from.sin_addr),icmpp->icmp_seq,ipp->ip_ttl,rtt); } else return -1;}void tv_sub(struct timeval *out,struct timeval *in){ if((out->tv_usec-=in->tv_usec)<0) { out->tv_sec-=1; out->tv_usec+=1000000; } out->tv_sec-=in->tv_sec;}int main(int argc,char **argv){ struct hostent *host; struct protoent *protocol; int size=50*1024; if(argc<2) { printf("usage : %s hostname/IP address \n",argv[0]); exit(1); } if((protocol=getprotobyname("icmp"))==NULL) { perror("getprotobyname\n"); exit(1); } /*生成使用ICMP的原始套接字,這種套接字只是root才能生成*/ if((sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0) { perror("socket error\n"); exit(1); } /*回收root權(quán)限,設(shè)置當(dāng)前用戶權(quán)限*/ setuid(getuid()); /*擴大套接字接收緩沖區(qū)到50K,這樣做主要為了減少接收緩沖區(qū)溢出的可能性,若無意中ping一個廣播地址或多播地址,將會引來大量應(yīng)答*/ setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size)); bzero(&dest_addr,sizeof(dest_addr)); dest_addr.sin_family=AF_INET; if((host=gethostbyname(argv[1]))==NULL) { perror("gethostbyname error\n"); exit(1); } dest_addr.sin_addr=*((struct in_addr *)host->h_addr); pid=getpid(); printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1],inet_ntoa(dest_addr.sin_addr),datalen); send_packet();/*發(fā)送所有ICMP報文*/ recv_packet();/*接收所有ICMP報文*/ statistics(SIGALRM);/*進行統(tǒng)計*/ return 0;}