#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); } /*校驗(yàn)和算法*/ unsigned short cal_chksum(unsigned short *addr,int len) { int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; /*把ICMP報(bào)頭二進(jìn)制數(shù)據(jù)以2字節(jié)為單位累加起來*/ while(nleft>1) { sum+=*w++; nleft-=2; } /*若ICMP報(bào)頭為奇數(shù)個(gè)字節(jié),會(huì)剩下最后一個(gè)字節(jié)。把最后一個(gè)字節(jié)視為2字節(jié)數(shù)據(jù)的高字節(jié),這個(gè)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報(bào)頭*/ 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ā)送時(shí)間*/ icmpp->icmp_cksum=cal_chksum((unsigned short*)icmpp,packsize);/*校驗(yàn)算法*/ return packsize; } /*發(fā)送3個(gè)ICMP報(bào)文*/ void send_packet() { int packetsize; while(nsend<MAX_NO_PACKETS) { nsend++; packetsize=pack(nsend);/*設(shè)置ICMP報(bào)文*/ if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr))<0) { perror("sendto error"); continue; } sleep(1);/*每隔一秒發(fā)送一個(gè)ICMP報(bào)文*/ } } /*接收所有ICMP報(bào)文*/ void recv_packet() { int n,fromlen; extern int errno; /*更多精彩內(nèi)容:http://www./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); /*記錄接收時(shí)間*/ if(unpack(recvpacket,n)==-1) continue; nreceived++; } } /*剝?nèi)CMP報(bào)頭*/ 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報(bào)頭長度,即IP報(bào)頭的長度標(biāo)志乘4*/ icmpp=(struct icmp*)(buf+iphdrlen);/*超過IP報(bào)頭,指向ICMP報(bào)頭*/ len-=iphdrlen;/*ICMP報(bào)頭及ICMP數(shù)據(jù)報(bào)的總長度*/ if(len<8)/*小于ICMP報(bào)頭長度則不合理*/ { 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ā)送的時(shí)間差*/ rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;/*以毫秒為單位計(jì)算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()); /*擴(kuò)大套接字接收緩沖區(qū)到50K,這樣做主要為了減少接收緩沖區(qū)溢出的可能性,若無意中ping一個(gè)廣播地址或多播地址,將會(huì)引來大量應(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報(bào)文*/ recv_packet();/*接收所有ICMP報(bào)文*/ statistics(SIGALRM);/*進(jìn)行統(tǒng)計(jì)*/ return 0; } |
|