#include<unistd.h>
#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netdb.h>
#include<signal.h>
#include<arpa/inet.h>
#include<netinet/ip.h>
#include<netinet/in.h>
#include<netinet/ip_icmp.h>
#include<setjmp.h>
#include<error.h>
#include<string.h>
#include<sys/types.h>
#define MAX_NO_PACKETS 100000 /*最多发送的报文数*/
#define PACKET_SIZE 4096 /*发送和接受端的缓存大小*/
char send_buf[PACKET_SIZE] ; /* 发送 */
char recv_buf[PACKET_SIZE] ; /* 接收 */
char addr[200] ;
double temp_rtt[MAX_NO_PACKETS] ;
double rtt ,all_time;
double min ,max ,avg ,mdev ;
int size = 50 * 1024 ;
int sockfd , datalen = 56 ;
int nsend = 0 ,nreceived = 0 ;
pid_t pid ;
struct sockaddr_in dest_addr ;
struct sockaddr_in from ;
struct timeval tvrecv,cost ;
void computer_rtt()
{
double sum_avg = 0 ;
int i ;
min = max = temp_rtt[0] ;
avg = all_time / nreceived ;
for(i=0;i<nreceived;++i)
{
if(min > temp_rtt[i])
min = temp_rtt[i] ;
if(max < temp_rtt[i])
max = temp_rtt[i] ;
if(temp_rtt[i] > avg)
sum_avg += (temp_rtt[i] - avg) ;
if(temp_rtt[i] < avg)
sum_avg += (avg - temp_rtt[i]) ;
}
mdev = sum_avg / nreceived ;
}
void statistics(int sig)
{
computer_rtt();
printf("\n---------%s ping statistics--------\n",addr);
printf("%d packets transmitted,%d received,%d%% packet loss,time %.f ms\n",
nsend,nreceived,(nsend-nreceived)/nsend*100,all_time);
printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n",min,avg,max,mdev);
close(sockfd);
exit(1);
}
unsigned short cal_chksum(unsigned short *addr ,int len)
{
int sum = 0 ;
int nleft = len ;
unsigned short *w = addr ;
unsigned short check_sum = 0 ;
while(nleft > 1)
{
sum += *w ;
nleft -= 2 ;
w ++ ;
}
if(nleft == 1)
{
sum += *(unsigned char *)(w) ;
}
sum = (sum >> 16 ) + (sum & 0xFFFF) ;
sum += (sum >> 16) ;
check_sum = ~sum ;
return check_sum ;
}
int pack(int pack_no)
{
int packsize ;
struct icmp *icmp ;
struct timeval *tval ;
icmp = (struct icmp *)send_buf ;
icmp->icmp_type = ICMP_ECHO ;
icmp->icmp_code = 0 ;
icmp->icmp_cksum = 0 ;
icmp->icmp_seq = pack_no ;
icmp->icmp_id = pid ;
packsize = 64 ;
tval = (struct timeval *)icmp->icmp_data ;
gettimeofday(tval ,NULL);
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp , packsize);
return packsize ;
}
void send_packet()
{
int packsize ;
if(nsend < MAX_NO_PACKETS)
{
nsend++ ;
packsize = pack(nsend);
if(sendto(sockfd , send_buf,packsize ,0 ,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0)
{
printf("sendto error.\n");
exit(1) ;
}
}
}
void tv_sub(struct timeval *send ,struct timeval *recv , struct timeval *cost)
{
long sec;
long usec ;
sec = recv->tv_sec - send->tv_sec ;
usec = recv->tv_usec - send->tv_usec ;
if(usec >= 0)
{
cost->tv_sec = sec ;
cost->tv_usec = usec ;
}
else{
cost->tv_sec = sec - 1 ;
cost->tv_usec = usec + 1e6 ;
}
}
void unpack(char *buf, int len)
{
int iphdrlen ;
struct ip *ip ;
struct icmp *icmp ;
struct timeval *tvsend ;
ip = (struct ip *)buf ;
iphdrlen = ip->ip_hl << 2 ;
icmp = (struct icmp *)(buf + iphdrlen) ;
len -= iphdrlen ;
if(len < 8)
{
printf("ICMP packet's length is less than 8.\n");
return ;
}
if( (icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
{
tvsend = (struct timeval *)(icmp->icmp_data);
tv_sub(tvsend,&tvrecv,&cost);
rtt = cost.tv_sec * 1000 + cost.tv_usec/1000 ; /*以ms为单位。*/
temp_rtt[nreceived] = rtt ;
all_time += rtt ;
printf("%d bytes from %s: icmp_seq=%u ttl=%d time=%.1lf ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq,ip->ip_ttl,rtt);
}
}
void recv_packet()
{
int n;
int fromlen = sizeof(from) ;
if(nreceived < nsend)
{
if( (n=recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&from,&fromlen)) < 0)
{
printf("recvfrom error.\n");
exit(1) ;
}
gettimeofday(&tvrecv,NULL);
unpack(recv_buf,n);
nreceived ++ ;
}
}
int main(int argc,char* argv[]){
struct protoent *protocol ;
struct hostent *hp ;
memcpy(addr,argv[0],sizeof(argv[0]));
if(argc < 2)
{
printf("usage:%s hostname/IP\n",argv[0]);
exit(1) ;
}
if( ( protocol = getprotobyname("icmp") ) == NULL )
{
printf("getprotobyname error.\n");
exit(1) ;
}
if( ( sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) ) < 0)
{
printf("socket error.\n");
exit(1) ;
}
setuid(getpid());
setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size, sizeof(size));
memset(&dest_addr, 0 , sizeof(&dest_addr));
dest_addr.sin_family = AF_INET ;
if( !inet_aton(argv[1] , &dest_addr.sin_addr) )
{
hp = gethostbyname(argv[1]) ;
if( hp == NULL )
{
printf("gethostbyname error.\n");
exit(1) ;
}
dest_addr.sin_addr = *(struct in_addr *)hp->h_addr ;
}
pid = getpid() ;
printf("PING %s(%s):%d bytes of data.\n",argv[1],inet_ntoa(dest_addr.sin_addr),datalen);
signal(SIGINT,statistics);
while(nsend < MAX_NO_PACKETS)
{
sleep(1) ;
send_packet() ;
recv_packet() ;
}
return 0;
}