11553Srgrimes/*-
21553Srgrimes * Copyright (c) 1985, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 4. Neither the name of the University nor the names of its contributors
141553Srgrimes *    may be used to endorse or promote products derived from this software
151553Srgrimes *    without specific prior written permission.
161553Srgrimes *
171553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271553Srgrimes * SUCH DAMAGE.
281553Srgrimes */
291553Srgrimes
301553Srgrimes#ifndef lint
3130642Scharnier#if 0
321553Srgrimesstatic char sccsid[] = "@(#)measure.c	8.1 (Berkeley) 6/6/93";
3330642Scharnier#endif
3430642Scharnierstatic const char rcsid[] =
3550479Speter  "$FreeBSD$";
361553Srgrimes#endif /* not lint */
371553Srgrimes
381553Srgrimes#include "globals.h"
391553Srgrimes#include <netinet/in_systm.h>
401553Srgrimes#include <netinet/ip.h>
411553Srgrimes#include <netinet/ip_icmp.h>
421553Srgrimes
431553Srgrimes#define MSEC_DAY	(SECDAY*1000)
441553Srgrimes
451553Srgrimes#define PACKET_IN	1024
461553Srgrimes
471553Srgrimes#define MSGS		5		/* timestamps to average */
481553Srgrimes#define TRIALS		10		/* max # of timestamps sent */
491553Srgrimes
501553Srgrimesextern int sock_raw;
511553Srgrimes
521553Srgrimesint measure_delta;
531553Srgrimes
541553Srgrimesstatic n_short seqno = 0;
551553Srgrimes
561553Srgrimes/*
571553Srgrimes * Measures the differences between machines' clocks using
581553Srgrimes * ICMP timestamp messages.
59246209Scharnier * maxmsec	wait this many msec at most
60246209Scharnier * wmsec	msec to wait for an answer
61246209Scharnier * print	print complaints on stderr
621553Srgrimes */
631553Srgrimesint					/* status val defined in globals.h */
64246209Scharniermeasure(u_long maxmsec, u_long wmsec, char *hname, struct sockaddr_in *addr, int print)
651553Srgrimes{
661553Srgrimes	int length;
671553Srgrimes	int measure_status;
681553Srgrimes	int rcvcount, trials;
691553Srgrimes	int cc, count;
701553Srgrimes	fd_set ready;
711553Srgrimes	long sendtime, recvtime, histime1, histime2;
721553Srgrimes	long idelta, odelta, total;
731553Srgrimes	long min_idelta, min_odelta;
741553Srgrimes	struct timeval tdone, tcur, ttrans, twait, tout;
751553Srgrimes	u_char packet[PACKET_IN], opacket[64];
761553Srgrimes	register struct icmp *icp = (struct icmp *) packet;
771553Srgrimes	register struct icmp *oicp = (struct icmp *) opacket;
781553Srgrimes	struct ip *ip = (struct ip *) packet;
791553Srgrimes
801553Srgrimes	min_idelta = min_odelta = 0x7fffffff;
811553Srgrimes	measure_status = HOSTDOWN;
821553Srgrimes	measure_delta = HOSTDOWN;
83209344Sgavin	trials = 0;
841553Srgrimes	errno = 0;
851553Srgrimes
861553Srgrimes	/* open raw socket used to measure time differences */
871553Srgrimes	if (sock_raw < 0) {
881553Srgrimes		sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
891553Srgrimes		if (sock_raw < 0)  {
901553Srgrimes			syslog(LOG_ERR, "opening raw socket: %m");
911553Srgrimes			goto quit;
921553Srgrimes		}
931553Srgrimes	}
941553Srgrimes
958857Srgrimes
961553Srgrimes	/*
971553Srgrimes	 * empty the icmp input queue
981553Srgrimes	 */
991553Srgrimes	FD_ZERO(&ready);
1001553Srgrimes	for (;;) {
1011553Srgrimes		tout.tv_sec = tout.tv_usec = 0;
1021553Srgrimes		FD_SET(sock_raw, &ready);
1031553Srgrimes		if (select(sock_raw+1, &ready, 0,0, &tout)) {
1041553Srgrimes			length = sizeof(struct sockaddr_in);
1051553Srgrimes			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
1061553Srgrimes				      0,&length);
1071553Srgrimes			if (cc < 0)
1081553Srgrimes				goto quit;
1091553Srgrimes			continue;
1101553Srgrimes		}
1111553Srgrimes		break;
1121553Srgrimes	}
1131553Srgrimes
1141553Srgrimes	/*
1151553Srgrimes	 * Choose the smallest transmission time in each of the two
1161553Srgrimes	 * directions. Use these two latter quantities to compute the delta
1171553Srgrimes	 * between the two clocks.
1181553Srgrimes	 */
1191553Srgrimes
1201553Srgrimes	oicp->icmp_type = ICMP_TSTAMP;
1211553Srgrimes	oicp->icmp_code = 0;
1221553Srgrimes	oicp->icmp_id = getpid();
1231553Srgrimes	oicp->icmp_rtime = 0;
1241553Srgrimes	oicp->icmp_ttime = 0;
1251553Srgrimes	oicp->icmp_seq = seqno;
1261553Srgrimes
1271553Srgrimes	FD_ZERO(&ready);
1281553Srgrimes
129239991Sed	(void)gettimeofday(&tdone, NULL);
1301553Srgrimes	mstotvround(&tout, maxmsec);
1311553Srgrimes	timevaladd(&tdone, &tout);		/* when we give up */
1321553Srgrimes
1331553Srgrimes	mstotvround(&twait, wmsec);
1341553Srgrimes
1351553Srgrimes	rcvcount = 0;
1361553Srgrimes	while (rcvcount < MSGS) {
137239991Sed		(void)gettimeofday(&tcur, NULL);
1381553Srgrimes
1391553Srgrimes		/*
1401553Srgrimes		 * keep sending until we have sent the max
1411553Srgrimes		 */
1421553Srgrimes		if (trials < TRIALS) {
1431553Srgrimes			trials++;
14430761Scharnier			oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
1454840Sbde						 + tcur.tv_usec / 1000);
1461553Srgrimes			oicp->icmp_cksum = 0;
1471553Srgrimes			oicp->icmp_cksum = in_cksum((u_short*)oicp,
1481553Srgrimes						    sizeof(*oicp));
1491553Srgrimes
1501553Srgrimes			count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
1511553Srgrimes				       (struct sockaddr*)addr,
1521553Srgrimes				       sizeof(struct sockaddr));
1531553Srgrimes			if (count < 0) {
1541553Srgrimes				if (measure_status == HOSTDOWN)
1551553Srgrimes					measure_status = UNREACHABLE;
1561553Srgrimes				goto quit;
1571553Srgrimes			}
1581553Srgrimes			++oicp->icmp_seq;
1591553Srgrimes
1601553Srgrimes			ttrans = tcur;
1611553Srgrimes			timevaladd(&ttrans, &twait);
1621553Srgrimes		} else {
1631553Srgrimes			ttrans = tdone;
1641553Srgrimes		}
1651553Srgrimes
1661553Srgrimes		while (rcvcount < trials) {
1671553Srgrimes			timevalsub(&tout, &ttrans, &tcur);
1681553Srgrimes			if (tout.tv_sec < 0)
1691553Srgrimes				tout.tv_sec = 0;
1701553Srgrimes
1711553Srgrimes			FD_SET(sock_raw, &ready);
1721553Srgrimes			count = select(sock_raw+1, &ready, (fd_set *)0,
1731553Srgrimes				       (fd_set *)0, &tout);
174239991Sed			(void)gettimeofday(&tcur, NULL);
1751553Srgrimes			if (count <= 0)
1761553Srgrimes				break;
1771553Srgrimes
1781553Srgrimes			length = sizeof(struct sockaddr_in);
1791553Srgrimes			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
1801553Srgrimes				      0,&length);
1811553Srgrimes			if (cc < 0)
1821553Srgrimes				goto quit;
1831553Srgrimes
1848857Srgrimes			/*
1851553Srgrimes			 * got something.  See if it is ours
1861553Srgrimes			 */
1871553Srgrimes			icp = (struct icmp *)(packet + (ip->ip_hl << 2));
1881553Srgrimes			if (cc < sizeof(*ip)
1891553Srgrimes			    || icp->icmp_type != ICMP_TSTAMPREPLY
1901553Srgrimes			    || icp->icmp_id != oicp->icmp_id
1911553Srgrimes			    || icp->icmp_seq < seqno
1921553Srgrimes			    || icp->icmp_seq >= oicp->icmp_seq)
1931553Srgrimes				continue;
1941553Srgrimes
1951553Srgrimes
1961553Srgrimes			sendtime = ntohl(icp->icmp_otime);
1971553Srgrimes			recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
1981553Srgrimes				    tcur.tv_usec / 1000);
1991553Srgrimes
2001553Srgrimes			total = recvtime-sendtime;
2011553Srgrimes			if (total < 0)	/* do not hassle midnight */
2021553Srgrimes				continue;
2031553Srgrimes
2041553Srgrimes			rcvcount++;
2051553Srgrimes			histime1 = ntohl(icp->icmp_rtime);
2061553Srgrimes			histime2 = ntohl(icp->icmp_ttime);
2071553Srgrimes			/*
2081553Srgrimes			 * a host using a time format different from
2091553Srgrimes			 * msec. since midnight UT (as per RFC792) should
2101553Srgrimes			 * set the high order bit of the 32-bit time
2111553Srgrimes			 * value it transmits.
2121553Srgrimes			 */
2131553Srgrimes			if ((histime1 & 0x80000000) != 0) {
2141553Srgrimes				measure_status = NONSTDTIME;
2151553Srgrimes				goto quit;
2161553Srgrimes			}
2171553Srgrimes			measure_status = GOOD;
2181553Srgrimes
2191553Srgrimes			idelta = recvtime-histime2;
2201553Srgrimes			odelta = histime1-sendtime;
2211553Srgrimes
2221553Srgrimes			/* do not be confused by midnight */
2231553Srgrimes			if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
2241553Srgrimes			else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
2251553Srgrimes
2261553Srgrimes			if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
2271553Srgrimes			else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
2281553Srgrimes
2291553Srgrimes			/* save the quantization error so that we can get a
2301553Srgrimes			 * measurement finer than our system clock.
2311553Srgrimes			 */
2321553Srgrimes			if (total < MIN_ROUND) {
2331553Srgrimes				measure_delta = (odelta - idelta)/2;
2341553Srgrimes				goto quit;
2351553Srgrimes			}
2361553Srgrimes
2371553Srgrimes			if (idelta < min_idelta)
2381553Srgrimes				min_idelta = idelta;
2391553Srgrimes			if (odelta < min_odelta)
2401553Srgrimes				min_odelta = odelta;
2411553Srgrimes
2421553Srgrimes			measure_delta = (min_odelta - min_idelta)/2;
2431553Srgrimes		}
2441553Srgrimes
2451553Srgrimes		if (tcur.tv_sec > tdone.tv_sec
2461553Srgrimes		    || (tcur.tv_sec == tdone.tv_sec
2471553Srgrimes			&& tcur.tv_usec >= tdone.tv_usec))
2481553Srgrimes			break;
2491553Srgrimes	}
2501553Srgrimes
2511553Srgrimesquit:
2521553Srgrimes	seqno += TRIALS;		/* allocate our sequence numbers */
2531553Srgrimes
2541553Srgrimes	/*
2551553Srgrimes	 * If no answer is received for TRIALS consecutive times,
2561553Srgrimes	 * the machine is assumed to be down
2571553Srgrimes	 */
2581553Srgrimes	if (measure_status == GOOD) {
2591553Srgrimes		if (trace) {
2601553Srgrimes			fprintf(fd,
2611553Srgrimes				"measured delta %4d, %d trials to %-15s %s\n",
2621553Srgrimes			   	measure_delta, trials,
2631553Srgrimes				inet_ntoa(addr->sin_addr), hname);
2641553Srgrimes		}
2651553Srgrimes	} else if (print) {
2661553Srgrimes		if (errno != 0)
26730642Scharnier			warn("measure %s", hname);
2681553Srgrimes	} else {
2691553Srgrimes		if (errno != 0) {
2701553Srgrimes			syslog(LOG_ERR, "measure %s: %m", hname);
2711553Srgrimes		} else {
2721553Srgrimes			syslog(LOG_ERR, "measure: %s did not respond", hname);
2731553Srgrimes		}
2741553Srgrimes		if (trace) {
2751553Srgrimes			fprintf(fd,
2761553Srgrimes				"measure: %s failed after %d trials\n",
2771553Srgrimes				hname, trials);
2781553Srgrimes			(void)fflush(fd);
2791553Srgrimes		}
2801553Srgrimes	}
2811553Srgrimes
2821553Srgrimes	return(measure_status);
2831553Srgrimes}
2841553Srgrimes
2851553Srgrimes
2861553Srgrimes
2871553Srgrimes
2881553Srgrimes
2891553Srgrimes/*
2901553Srgrimes * round a number of milliseconds into a struct timeval
2911553Srgrimes */
2921553Srgrimesvoid
293246209Scharniermstotvround(struct timeval *res, long x)
2941553Srgrimes{
2951553Srgrimes	if (x < 0)
2961553Srgrimes		x = -((-x + 3)/5);
2971553Srgrimes	else
2981553Srgrimes		x = (x+3)/5;
2991553Srgrimes	x *= 5;
3001553Srgrimes	res->tv_sec = x/1000;
3011553Srgrimes	res->tv_usec = (x-res->tv_sec*1000)*1000;
3021553Srgrimes	if (res->tv_usec < 0) {
3031553Srgrimes		res->tv_usec += 1000000;
3041553Srgrimes		res->tv_sec--;
3051553Srgrimes	}
3061553Srgrimes}
3071553Srgrimes
3081553Srgrimesvoid
309246209Scharniertimevaladd(struct timeval *tv1, struct timeval *tv2)
3101553Srgrimes{
3111553Srgrimes	tv1->tv_sec += tv2->tv_sec;
3121553Srgrimes	tv1->tv_usec += tv2->tv_usec;
3131553Srgrimes	if (tv1->tv_usec >= 1000000) {
3141553Srgrimes		tv1->tv_sec++;
3151553Srgrimes		tv1->tv_usec -= 1000000;
3161553Srgrimes	}
3171553Srgrimes	if (tv1->tv_usec < 0) {
3181553Srgrimes		tv1->tv_sec--;
3191553Srgrimes		tv1->tv_usec += 1000000;
3201553Srgrimes	}
3211553Srgrimes}
3221553Srgrimes
3231553Srgrimesvoid
324246209Scharniertimevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2)
3251553Srgrimes{
3261553Srgrimes	res->tv_sec = tv1->tv_sec - tv2->tv_sec;
3271553Srgrimes	res->tv_usec = tv1->tv_usec - tv2->tv_usec;
3281553Srgrimes	if (res->tv_usec >= 1000000) {
3291553Srgrimes		res->tv_sec++;
3301553Srgrimes		res->tv_usec -= 1000000;
3311553Srgrimes	}
3321553Srgrimes	if (res->tv_usec < 0) {
3331553Srgrimes		res->tv_sec--;
3341553Srgrimes		res->tv_usec += 1000000;
3351553Srgrimes	}
3361553Srgrimes}
337