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