/*
 * authspeed - figure out how long it takes to do an NTP encryption
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "ntp_fp.h"

#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)

#define DEFLOOPS	-1

#define	DEFDELAYLOOPS	20000
#define	DEFCOSTLOOPS	2000

char *progname;
int debug;

struct timeval tstart, tend;
struct rusage rstart, rend;

l_fp dummy1, dummy2;
u_long dummy3;

u_long pkt[15];

int totalcost = 0;

/*
 * main - parse arguments and handle options
 */
main(argc, argv)
int argc;
char *argv[];
{
	int c;
	int loops;
	int i;
	int errflg = 0;
	extern int optind;
	extern char *optarg;
	double rtime;
	double vtime;

	progname = argv[0];
	loops = DEFLOOPS;
	while ((c = getopt(argc, argv, "cdn:")) != EOF)
		switch (c) {
		case 'c':
			totalcost++;
			break;
		case 'd':
			++debug;
			break;
		case 'n':
			loops = atoi(optarg);
			if (loops <= 0) {
				(void) fprintf(stderr, 
			"%s: %s is unlikely to be a useful number of loops\n",
				    progname, optarg);
				errflg++;
			}
			break;
		default:
			errflg++;
			break;
		}
	if (errflg || optind == argc) {
		(void) fprintf(stderr,
		    "usage: %s [-d] [-n loops] [ -c ] auth.samplekeys\n",
		    progname);
		exit(2);
	}
	
	init_auth();
	authreadkeys(argv[optind]);
	for (i = 0; i < 16; i++) {
		if (!auth_havekey(i)) {
			errflg++;
			(void) fprintf(stderr, "%s: key %d missing\n",
			    progname, i);
		}
	}

	if (errflg) {
		(void) fprintf(stderr,
	"%s: check syslog for errors, or use file with complete set of keys\n",
		    progname);
		exit(1);
	}

	if (loops == DEFLOOPS) {
		if (totalcost)
			loops = DEFCOSTLOOPS;
		else
			loops = DEFDELAYLOOPS;
	}

	dummy1.l_ui = 0x80808080;
	dummy1.l_uf = 0xffffff00;
	dummy3 = 0x0aaaaaaa;

	for (i = 0; i < 12; i++)
		pkt[i] = i * 0x22222;

	if (totalcost) {
		if (totalcost > 1)
			docheap(loops);
		else
			docost(loops);
	} else {
		dodelay(loops);
	}

	subtime(&tstart, &tend, &rtime);
	subtime(&rstart.ru_utime, &rend.ru_utime, &vtime);

	printf("total real time: %.3f\n", rtime);
	printf("total CPU time: %.3f\n", vtime);
	if (totalcost) {
		printf("real cost (in seconds): %.6f\n",
		    rtime/(double)loops);
		printf("CPU cost (in seconds): %.6f\n",
		    vtime/(double)loops);
		printf("\nThis includes the cost of a decryption plus the\n");
		printf("the cost of an encryption, i.e. the cost to process\n");
		printf("a single authenticated packet.\n");
	} else {
		printf("authdelay in the configuration file\n");
		printf("real authentication delay: %.6f\n",
		    rtime/(double)loops);
		printf("authentication delay in CPU time: %.6f\n",
		    vtime/(double)loops);
		printf("\nThe CPU delay is probably the best bet for\n");
		printf("authdelay in the configuration file\n");
	}
	exit(0);
}


/*
 * dodelay - do the delay measurement
 */
dodelay(loops)
	register int loops;
{

	(void) gettimeofday(&tstart, (struct timezone *)0);
	(void) getrusage(RUSAGE_SELF, &rstart);

	while (loops-- > 0) {
		L_ADDUF(&dummy1, dummy3);
		auth2crypt((loops & 0xf), pkt, 48);
	}

	(void) getrusage(RUSAGE_SELF, &rend);
	(void) gettimeofday(&tend, (struct timezone *)0);
}


/*
 * docheap - do the cost measurement the cheap way
 */
docheap(loops)
	register int loops;
{

	(void) authhavekey(3);

	(void) gettimeofday(&tstart, (struct timezone *)0);
	(void) getrusage(RUSAGE_SELF, &rstart);

	while (loops-- > 0) {
		auth1crypt(3, pkt, 48);
		L_ADDUF(&dummy1, dummy3);
		auth2crypt(3, pkt, 48);
		(void) authdecrypt(3, pkt, 48);
	}

	(void) getrusage(RUSAGE_SELF, &rend);
	(void) gettimeofday(&tend, (struct timezone *)0);
}


/*
 * docost - do the cost measurement
 */
docost(loops)
	register int loops;
{

	(void) gettimeofday(&tstart, (struct timezone *)0);
	(void) getrusage(RUSAGE_SELF, &rstart);

	while (loops-- > 0) {
		auth1crypt((loops & 0xf), pkt, 48);
		L_ADDUF(&dummy1, dummy3);
		auth2crypt((loops & 0xf), pkt, 48);
		(void) authdecrypt(((loops+1) & 0xf), pkt, 48);
	}

	(void) getrusage(RUSAGE_SELF, &rend);
	(void) gettimeofday(&tend, (struct timezone *)0);
}


/*
 * subtime - subtrace two struct timevals, return double result
 */
subtime(tvs, tve, res)
	struct timeval *tvs, *tve;
	double *res;
{
	long sec;
	long usec;

	sec = tve->tv_sec - tvs->tv_sec;
	usec = tve->tv_usec - tvs->tv_usec;

	if (usec < 0) {
		usec += 1000000;
		sec--;
	}

	*res = (double)sec + (double)usec/1000000.;
	return;
}
