
 
/*
	Uses the UDP echo service to try to get exact RTTs.

*/

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>

/* forward */

int usage();
int ctl_c();
void do_ping();

int	sent, received;

/* Stored in usec */
unsigned int	worst_time;
unsigned int	best_time;

/* Stored in tenths of milliseconds, to try to avoid overflows. */
unsigned int	total_time;

char buff[4096];
char inbuff[4096];

main(ac, av)
int	ac;
char	*av[];
{
	unsigned int	host;
	int		size = 64;
	int		count = -1;


	switch(ac) {
	case 2:
		count = -1;
		break;
	case 4:
		size = atoi(av[3]);
		/* Fall through */
	case 3:
		count = atoi(av[2]);
		break;
	default:
		exit(usage(av[0]));
		break;
	}

	signal(SIGINT, ctl_c);

	host = inet_addr(av[1]);

	best_time = 9999999999;
	worst_time = 0;
	total_time = 0;

	sent = received = 0;

	do_ping(host, (size > 4096 ? 4096 : size), count);

	ctl_c();
}

int
ctl_c()
{
	unsigned int avg;


	printf("\nSent %d packets, received %d.\n", sent, received);
	if(received > 0) {
		avg = total_time / received;
		printf("Best/Avg/Worst %d.%03d/%d.%d/%d.%03d\n",
			best_time / 1000, best_time % 1000,
			avg / 10, avg % 10,
			worst_time / 1000, worst_time % 1000
		);
	}

	exit(-1);
}

void
do_ping(host, size, count)
unsigned int	host;
int		size;
int		count;
{
	struct servent		*echo_service;
	struct sockaddr_in	sock;
	struct sockaddr_in	destsock;
	struct timeval		sent_tm, recvd_tm;
	struct timeval		wait_tm;
	unsigned short		echo_port;
	unsigned int		jiffies; /* 10ths of millseconds RTT */
	int			fd;
	int			i;
	int			socklen;
	int			secs, usecs;
	int			red, rdy;
	int			minsize;
	int			sent_seq, recvd_seq;
	fd_set			readfds;
	char			*p;

	sent_seq = 0;
	minsize = sizeof(sent_tm) + sizeof(sent_seq);
	size = (size < minsize ? minsize : size);

	echo_service = getservbyname("echo", "udp");
	if(echo_service == NULL) {
		echo_port = 7;
	} else {
		echo_port = echo_service->s_port;
	}

	sock.sin_family = AF_INET;
	sock.sin_port = 0;
	sock.sin_addr.s_addr = INADDR_ANY;

	fd = socket(PF_INET, SOCK_DGRAM, 0);
	if(fd < 0) {
		perror("socket");
		exit(-1);
	}

	if(bind(fd, &sock, sizeof(sock)) < 0) {
		perror("bind");
		exit(-1);
	}

	for(i = 0; i < size; i++) {
		buff[i] = random();
	}
	destsock.sin_addr.s_addr = host;
	printf("Sending %d bytes to %s\n", size, inet_ntoa(destsock.sin_addr));

	while(1) {
		destsock.sin_family = AF_INET;
		destsock.sin_port = echo_port;
		destsock.sin_addr.s_addr = host;

		FD_ZERO(&readfds);
		FD_SET(fd, &readfds);
		wait_tm.tv_sec = 1;
		wait_tm.tv_usec = 0;

		p = buff;
		bcopy((char *)&sent_seq, p, sizeof(sent_seq));
		p += sizeof(sent_seq);
		sent_seq++;

		(void) gettimeofday(&sent_tm, NULL);
		bcopy((char *)&sent_tm, p, sizeof(sent_tm));

		if(sendto(fd, buff, size, 0, &destsock, sizeof(destsock)) < 0) {
			perror("sendto");
			exit(-1);
		}
		sent++;

		/* Now wait for a reply */

		rdy = select(fd+1, &readfds, NULL, NULL, &wait_tm);
		if(rdy < 0) {
			perror("select");
			exit(-1);
		}
		if(rdy != 1) {
			continue;
		}

		red = recvfrom(fd, inbuff, 4096, 0, &destsock, &socklen);
		if(red < 0) {
			perror("recvfrom");
			exit(-1);
		}
		received++;

		(void) gettimeofday(&recvd_tm, NULL);

		p = inbuff;
		bcopy(p, (char *) &recvd_seq, sizeof(recvd_seq));
		p += sizeof(recvd_seq);
		bcopy(p, (char *) &sent_tm, sizeof(sent_tm));
		usecs = sent_tm.tv_usec;
		secs = sent_tm.tv_sec;
		usecs = (recvd_tm.tv_usec - usecs);
		secs = (recvd_tm.tv_sec - secs);

		while(usecs < 0) {
			secs--;
			usecs += 1000000;
		}


		printf("%d bytes from %s seq=%d in %d.%03dms\n", red,
			inet_ntoa(destsock.sin_addr), recvd_seq,
				(1000 * secs) + (usecs / 1000),
				usecs % 1000);

		usecs += (1000000 * secs);
		jiffies = usecs / 100;
		if(usecs > worst_time) {
			worst_time = usecs;
		}
		if(usecs < best_time) {
			best_time = usecs;
		}
		total_time += jiffies;

		for(i = minsize; i < red; i++) {
			if(buff[i] != inbuff[i]) {
				printf("Reply mismatch at byte %d\n", i);
				break;
			}
		}

		if(count != -1) {
			if(count == sent) {
				break;
			}
		}


		/* Compute remaining time to sleep off */

		usecs += (secs * 1000);
		usecs = 1000000 - usecs;

		if(usecs > 0) {
			usleep(usecs);
		}
	}
}

usage(nam)
char	*nam;
{
	fprintf(stderr, "usage: %s <ip address> [[count] size]\n", nam);
	return -1;
}
