#include<stdio.h>
#include<stdlib.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<netinet/ip_icmp.h>
#include<netdb.h>
#include<setjmp.h>
#include<errno.h>
#include<string.h>
#include<sys/time.h>
#define MAX_PACKET_SIZE 4096
static int max_packet_num = 4;
char *addr[];
char recv_packet[MAX_PACKET_SIZE];
char send_packet[MAX_PACKET_SIZE];
int sockfd = 0;
int datalen = 56;
int nsend = 0;
int nreceived = 0;
double *temp_rtt = NULL;
double all_time = 0;
double min = 0;
double max = 0;
double avg = 0;
double mdev = 0;
struct sockaddr_in dest_addr;
struct sockaddr_in from_addr;
struct timeval tvrecv;
pid_t pid;
static int pack(int pack_no);
static int unpack(char *buf, int len);
static void compute_rtt() {
double sum_avg = 0;
max = min = temp_rtt[0];
avg = all_time / nreceived;
int i;
for(i = 0; i < nreceived; i++) {
if(temp_rtt[i] < min)
min = temp_rtt[i];
if(temp_rtt[i] > max)
max = temp_rtt[i];
if(temp_rtt[i] < avg) {
sum_avg = avg - temp_rtt[i];
} else {
sum_avg = temp_rtt[i] - avg;
}
}
mdev = sum_avg / nreceived;
}
static void statistics(int sig) {
compute_rtt();
printf("\n----------%s ping statistics-------\n", addr[0]);
printf("%d packets transmitted, %d packets recevied, %d%% packets lost, time %.f ms\n",
nsend, nreceived, (nsend - nreceived) / nsend * 100, all_time);
printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f\n", min, avg, max, mdev);
close(sockfd);
exit(1);
}
static unsigned short cal_chsum(unsigned short *addr, int len) {
int nleft = len;
int sum = 0;
unsigned short check_sum = 0;
unsigned short *w = addr;
while(nleft > 1) {
sum += *w++;
nleft -= 2;
}
if(nleft == 1) {
*(unsigned char*)(&check_sum) = *(unsigned char*)w;
sum += check_sum;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
check_sum = ~sum;
return check_sum;
}
static int pack(int pack_no) {
int packet_size = 0;
struct icmp *icmp = NULL;
struct timeval *tval = NULL;
icmp = (struct icmp*)send_packet;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no;
icmp->icmp_id = pid;
packet_size = 8 + datalen;
tval = (struct timeval*)icmp->icmp_data;
gettimeofday(tval, NULL);
icmp->icmp_cksum = cal_chsum((unsigned short*)icmp, packet_size);
return packet_size;
}
void sendpacket(void) {
int packet_size = 0;
if(nsend < max_packet_num) {
nsend++;
packet_size = pack(nsend);
if(sendto(sockfd, send_packet, packet_size, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
perror("sendto error!");
}
}
}
static void tv_sub(struct timeval *recvtime, struct timeval *sendtime) {
long sec = recvtime->tv_sec - sendtime->tv_sec;
long usec = recvtime->tv_usec - sendtime->tv_usec;
if(usec >= 0) {
recvtime->tv_sec = sec;
recvtime->tv_usec = usec;
} else {
recvtime->tv_sec -= 1;
recvtime->tv_usec = -usec;
}
}
static int unpack(char *buf, int len) {
int ip_header_len = 0;
struct ip *ip = NULL;
struct icmp *icmp = NULL;
struct timeval *tvsend = NULL;
double rtt = 0;
ip = (struct ip*)buf;
ip_header_len = ip->ip_hl << 2;
icmp = (struct icmp*)(buf + ip_header_len);
len -= ip_header_len;
if(len < 8) {
printf("ICMP packet\'s length is less than 8.\n");
return -1;
}
if(icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) {
tvsend = (struct timeval*)icmp->icmp_data;
tv_sub(&tvrecv, tvsend);
rtt = tvrecv.tv_sec*1000 + tvrecv.tv_usec/1000;
temp_rtt[nreceived] = rtt;
all_time += rtt;
printf("%d bytes from %s: icmp_seq=%u,ttl=%d,time=%.1f ms\n",
len, inet_ntoa(from_addr.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
return 0;
} else {
return -1;
}
}
void recvpacket(void) {
int n = 0;
socklen_t addr_len = 0;
addr_len = sizeof(from_addr);
if(nreceived < nsend) {
n = recvfrom(sockfd, recv_packet, sizeof(recv_packet), 0, (struct sockaddr*)&from_addr, &addr_len);
if(n < 0) {
perror("recvfrom error!");
}
gettimeofday(&tvrecv, NULL);
unpack(recv_packet, n);
nreceived++;
}
}
int main(int argc, char *argv[]) {
int i = 0;
struct hostent *host = NULL;
struct protoent *protocol = NULL;
int size = 50 * 1024;
addr[0] = argv[1];
if(argc < 2) {
printf("usage: %s hostname/IP address.\n", argv[0]);
exit(-1);
}
for(i = 0; i < argc; i++) {
if(strcmp(argv[i], "-t") == 0)
sscanf(argv[i+1], "%d", &max_packet_num);
}
temp_rtt = (double*)malloc(sizeof(double) * max_packet_num);
bzero(temp_rtt, sizeof(double) * max_packet_num);
if((protocol = getprotobyname("icmp")) == NULL) {
perror("getprotobyname");
exit(-1);
}
//create raw socket need root, use sudo ./myping www.baidu.com
if((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) {
perror("socket error");
exit(-1);
}
setuid(getuid());
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
if(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 {
dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
}
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_packet_num){
sleep(1);
sendpacket();
recvpacket();
}
if(nsend == max_packet_num) {
statistics(0);
}
free(temp_rtt);
return 0;
}
测试用例: sudo ./myping www.baidu.com -t 6
目前只支持 -t这个参数,其他的还没有实现。