#include#include#include#include#include#include#include#include#include#include#include#include#definePACKET_SIZE 4096#defineMAX_WAIT_TIME 5#defineMAX_NO_PACKETS 3charsendpacket[PACKET_SIZE];charrecvpacket[PACKET_SIZE];intsockfd,datalen=56;intnsend=0,nreceived=0;structsockaddr_in dest_addr;
pid_t pid;structsockaddr_in from;structtimeval tvrecv;voidstatistics(intsigno);
unsignedshortcal_chksum(unsignedshort*addr,intlen);intpack(intpack_no);voidsend_packet(void);voidrecv_packet(void);intunpack(char*buf,intlen);voidtv_sub(structtimeval*out,structtimeval*in);voidstatistics(intsigno)
{ printf("\n--------------------PING statistics-------------------\n");
printf("%d packets transmitted, %d received , %%%d lost\n",nsend,nreceived,
(nsend-nreceived)/nsend*100);
close(sockfd);
exit(1);
}/*校验和算法*/unsignedshortcal_chksum(unsignedshort*addr,intlen)
{intnleft=len;intsum=0;
unsignedshort*w=addr;
unsignedshortanswer=0;/*把ICMP报头二进制数据以2字节为单位累加起来*/while(nleft>1)
{ sum+=*w++;
nleft-=2;
}/*若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/if( nleft==1)
{*(unsignedchar*)(&answer)=*(unsignedchar*)w;
sum+=answer;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
answer=~sum;returnanswer;
}/*设置ICMP报头*/intpack(intpack_no)
{inti,packsize;structicmp*icmp;structtimeval*tval;
icmp=(structicmp*)sendpacket;
icmp->icmp_type=ICMP_ECHO;
icmp->icmp_code=0;
icmp->icmp_cksum=0;
icmp->icmp_seq=pack_no;
icmp->icmp_id=pid;
packsize=8+datalen;
tval=(structtimeval*)icmp->icmp_data;
gettimeofday(tval,NULL);/*记录发送时间*/icmp->icmp_cksum=cal_chksum( (unsignedshort*)icmp,packsize);/*校验算法*/returnpacksize;
}/*发送三个ICMP报文*/voidsend_packet()
{intpacketsize;while( nsend
{ nsend++;
packetsize=pack(nsend);/*设置ICMP报头*/if( sendto(sockfd,sendpacket,packetsize,0,
(structsockaddr*)&dest_addr,sizeof(dest_addr) )<0)
{ perror("sendto error");continue;
}
sleep(1);/*每隔一秒发送一个ICMP报文*/}
}/*接收所有ICMP报文*/voidrecv_packet()
{intn,fromlen;externinterrno;
signal(SIGALRM,statistics);
fromlen=sizeof(from);while( nreceived
{ alarm(MAX_WAIT_TIME);if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,
(structsockaddr*)&from,&fromlen))<0)
{if(errno==EINTR)continue;
perror("recvfrom error");continue;
}
gettimeofday(&tvrecv,NULL);/*记录接收时间*/if(unpack(recvpacket,n)==-1)continue;
nreceived++;
}
}/*剥去ICMP报头*/intunpack(char*buf,intlen)
{inti,iphdrlen;structip*ip;structicmp*icmp;structtimeval*tvsend;doublertt;
ip=(structip*)buf;
iphdrlen=ip->ip_hl<<2;/*求ip报头长度,即ip报头的长度标志乘4*/icmp=(structicmp*)(buf+iphdrlen);/*越过ip报头,指向ICMP报头*/len-=iphdrlen;/*ICMP报头及ICMP数据报的总长度*/if( len<8)/*小于ICMP报头长度则不合理*/{ printf("ICMP packets\'s length is less than 8\n");return-1;
}/*确保所接收的是我所发的的ICMP的回应*/if( (icmp->icmp_type==ICMP_ECHOREPLY)&&(icmp->icmp_id==pid) )
{ tvsend=(structtimeval*)icmp->icmp_data;
tv_sub(&tvrecv,tvsend);/*接收和发送的时间差*/rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;/*以毫秒为单位计算rtt*//*显示相关信息*/printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",
len,
inet_ntoa(from.sin_addr),
icmp->icmp_seq,
ip->ip_ttl,
rtt);
}elsereturn-1;
}
main(intargc,char*argv[])
{structhostent*host;structprotoent*protocol;
unsignedlonginaddr=0l;intwaittime=MAX_WAIT_TIME;intsize=50*1024;if(argc<2)
{ printf("usage:%s hostname/IP address\n",argv[0]);
exit(1);
}if( (protocol=getprotobyname("icmp") )==NULL)
{ perror("getprotobyname");
exit(1);
}/*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/if( (sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) )<0)
{ perror("socket error");
exit(1);
}/*回收root权限,设置当前用户权限*/setuid(getuid());/*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的
的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size) );
bzero(&dest_addr,sizeof(dest_addr));
dest_addr.sin_family=AF_INET;/*判断是主机名还是ip地址*/if( inaddr=inet_addr(argv[1])==INADDR_NONE)
{if((host=gethostbyname(argv[1]) )==NULL)/*是主机名*/{ perror("gethostbyname error");
exit(1);
}
memcpy( (char*)&dest_addr.sin_addr,host->h_addr,host->h_length);
}else/*是ip地址*/memcpy( (char*)&dest_addr,(char*)&inaddr,host->h_length);/*获取main的进程id,用于设置ICMP的标志符*/pid=getpid();
printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1],
inet_ntoa(dest_addr.sin_addr),datalen);
send_packet();/*发送所有ICMP报文*/recv_packet();/*接收所有ICMP报文*/statistics(SIGALRM);/*进行统计*/return0;
}/*两个timeval结构相减*/voidtv_sub(structtimeval*out,structtimeval*in)
{if( (out->tv_usec-=in->tv_usec)<0)
{--out->tv_sec;out->tv_usec+=1000000;
}out->tv_sec-=in->tv_sec;
}/*------------- The End -----------*/