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.
591553Srgrimes */
601553Srgrimesint					/* status val defined in globals.h */
611553Srgrimesmeasure(maxmsec, wmsec, hname, addr, print)
621553Srgrimes	u_long maxmsec;			/* wait this many msec at most */
631553Srgrimes	u_long wmsec;			/* msec to wait for an answer */
641553Srgrimes	char *hname;
651553Srgrimes	struct sockaddr_in *addr;
661553Srgrimes	int print;			/* print complaints on stderr */
671553Srgrimes{
681553Srgrimes	int length;
691553Srgrimes	int measure_status;
701553Srgrimes	int rcvcount, trials;
711553Srgrimes	int cc, count;
721553Srgrimes	fd_set ready;
731553Srgrimes	long sendtime, recvtime, histime1, histime2;
741553Srgrimes	long idelta, odelta, total;
751553Srgrimes	long min_idelta, min_odelta;
761553Srgrimes	struct timeval tdone, tcur, ttrans, twait, tout;
771553Srgrimes	u_char packet[PACKET_IN], opacket[64];
781553Srgrimes	register struct icmp *icp = (struct icmp *) packet;
791553Srgrimes	register struct icmp *oicp = (struct icmp *) opacket;
801553Srgrimes	struct ip *ip = (struct ip *) packet;
811553Srgrimes
821553Srgrimes	min_idelta = min_odelta = 0x7fffffff;
831553Srgrimes	measure_status = HOSTDOWN;
841553Srgrimes	measure_delta = HOSTDOWN;
85209344Sgavin	trials = 0;
861553Srgrimes	errno = 0;
871553Srgrimes
881553Srgrimes	/* open raw socket used to measure time differences */
891553Srgrimes	if (sock_raw < 0) {
901553Srgrimes		sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
911553Srgrimes		if (sock_raw < 0)  {
921553Srgrimes			syslog(LOG_ERR, "opening raw socket: %m");
931553Srgrimes			goto quit;
941553Srgrimes		}
951553Srgrimes	}
961553Srgrimes
978857Srgrimes
981553Srgrimes	/*
991553Srgrimes	 * empty the icmp input queue
1001553Srgrimes	 */
1011553Srgrimes	FD_ZERO(&ready);
1021553Srgrimes	for (;;) {
1031553Srgrimes		tout.tv_sec = tout.tv_usec = 0;
1041553Srgrimes		FD_SET(sock_raw, &ready);
1051553Srgrimes		if (select(sock_raw+1, &ready, 0,0, &tout)) {
1061553Srgrimes			length = sizeof(struct sockaddr_in);
1071553Srgrimes			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
1081553Srgrimes				      0,&length);
1091553Srgrimes			if (cc < 0)
1101553Srgrimes				goto quit;
1111553Srgrimes			continue;
1121553Srgrimes		}
1131553Srgrimes		break;
1141553Srgrimes	}
1151553Srgrimes
1161553Srgrimes	/*
1171553Srgrimes	 * Choose the smallest transmission time in each of the two
1181553Srgrimes	 * directions. Use these two latter quantities to compute the delta
1191553Srgrimes	 * between the two clocks.
1201553Srgrimes	 */
1211553Srgrimes
1221553Srgrimes	oicp->icmp_type = ICMP_TSTAMP;
1231553Srgrimes	oicp->icmp_code = 0;
1241553Srgrimes	oicp->icmp_id = getpid();
1251553Srgrimes	oicp->icmp_rtime = 0;
1261553Srgrimes	oicp->icmp_ttime = 0;
1271553Srgrimes	oicp->icmp_seq = seqno;
1281553Srgrimes
1291553Srgrimes	FD_ZERO(&ready);
1301553Srgrimes
1311553Srgrimes	(void)gettimeofday(&tdone, 0);
1321553Srgrimes	mstotvround(&tout, maxmsec);
1331553Srgrimes	timevaladd(&tdone, &tout);		/* when we give up */
1341553Srgrimes
1351553Srgrimes	mstotvround(&twait, wmsec);
1361553Srgrimes
1371553Srgrimes	rcvcount = 0;
1381553Srgrimes	while (rcvcount < MSGS) {
1391553Srgrimes		(void)gettimeofday(&tcur, 0);
1401553Srgrimes
1411553Srgrimes		/*
1421553Srgrimes		 * keep sending until we have sent the max
1431553Srgrimes		 */
1441553Srgrimes		if (trials < TRIALS) {
1451553Srgrimes			trials++;
14630761Scharnier			oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
1474840Sbde						 + tcur.tv_usec / 1000);
1481553Srgrimes			oicp->icmp_cksum = 0;
1491553Srgrimes			oicp->icmp_cksum = in_cksum((u_short*)oicp,
1501553Srgrimes						    sizeof(*oicp));
1511553Srgrimes
1521553Srgrimes			count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
1531553Srgrimes				       (struct sockaddr*)addr,
1541553Srgrimes				       sizeof(struct sockaddr));
1551553Srgrimes			if (count < 0) {
1561553Srgrimes				if (measure_status == HOSTDOWN)
1571553Srgrimes					measure_status = UNREACHABLE;
1581553Srgrimes				goto quit;
1591553Srgrimes			}
1601553Srgrimes			++oicp->icmp_seq;
1611553Srgrimes
1621553Srgrimes			ttrans = tcur;
1631553Srgrimes			timevaladd(&ttrans, &twait);
1641553Srgrimes		} else {
1651553Srgrimes			ttrans = tdone;
1661553Srgrimes		}
1671553Srgrimes
1681553Srgrimes		while (rcvcount < trials) {
1691553Srgrimes			timevalsub(&tout, &ttrans, &tcur);
1701553Srgrimes			if (tout.tv_sec < 0)
1711553Srgrimes				tout.tv_sec = 0;
1721553Srgrimes
1731553Srgrimes			FD_SET(sock_raw, &ready);
1741553Srgrimes			count = select(sock_raw+1, &ready, (fd_set *)0,
1751553Srgrimes				       (fd_set *)0, &tout);
1761553Srgrimes			(void)gettimeofday(&tcur, (struct timezone *)0);
1771553Srgrimes			if (count <= 0)
1781553Srgrimes				break;
1791553Srgrimes
1801553Srgrimes			length = sizeof(struct sockaddr_in);
1811553Srgrimes			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
1821553Srgrimes				      0,&length);
1831553Srgrimes			if (cc < 0)
1841553Srgrimes				goto quit;
1851553Srgrimes
1868857Srgrimes			/*
1871553Srgrimes			 * got something.  See if it is ours
1881553Srgrimes			 */
1891553Srgrimes			icp = (struct icmp *)(packet + (ip->ip_hl << 2));
1901553Srgrimes			if (cc < sizeof(*ip)
1911553Srgrimes			    || icp->icmp_type != ICMP_TSTAMPREPLY
1921553Srgrimes			    || icp->icmp_id != oicp->icmp_id
1931553Srgrimes			    || icp->icmp_seq < seqno
1941553Srgrimes			    || icp->icmp_seq >= oicp->icmp_seq)
1951553Srgrimes				continue;
1961553Srgrimes
1971553Srgrimes
1981553Srgrimes			sendtime = ntohl(icp->icmp_otime);
1991553Srgrimes			recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
2001553Srgrimes				    tcur.tv_usec / 1000);
2011553Srgrimes
2021553Srgrimes			total = recvtime-sendtime;
2031553Srgrimes			if (total < 0)	/* do not hassle midnight */
2041553Srgrimes				continue;
2051553Srgrimes
2061553Srgrimes			rcvcount++;
2071553Srgrimes			histime1 = ntohl(icp->icmp_rtime);
2081553Srgrimes			histime2 = ntohl(icp->icmp_ttime);
2091553Srgrimes			/*
2101553Srgrimes			 * a host using a time format different from
2111553Srgrimes			 * msec. since midnight UT (as per RFC792) should
2121553Srgrimes			 * set the high order bit of the 32-bit time
2131553Srgrimes			 * value it transmits.
2141553Srgrimes			 */
2151553Srgrimes			if ((histime1 & 0x80000000) != 0) {
2161553Srgrimes				measure_status = NONSTDTIME;
2171553Srgrimes				goto quit;
2181553Srgrimes			}
2191553Srgrimes			measure_status = GOOD;
2201553Srgrimes
2211553Srgrimes			idelta = recvtime-histime2;
2221553Srgrimes			odelta = histime1-sendtime;
2231553Srgrimes
2241553Srgrimes			/* do not be confused by midnight */
2251553Srgrimes			if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
2261553Srgrimes			else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
2271553Srgrimes
2281553Srgrimes			if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
2291553Srgrimes			else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
2301553Srgrimes
2311553Srgrimes			/* save the quantization error so that we can get a
2321553Srgrimes			 * measurement finer than our system clock.
2331553Srgrimes			 */
2341553Srgrimes			if (total < MIN_ROUND) {
2351553Srgrimes				measure_delta = (odelta - idelta)/2;
2361553Srgrimes				goto quit;
2371553Srgrimes			}
2381553Srgrimes
2391553Srgrimes			if (idelta < min_idelta)
2401553Srgrimes				min_idelta = idelta;
2411553Srgrimes			if (odelta < min_odelta)
2421553Srgrimes				min_odelta = odelta;
2431553Srgrimes
2441553Srgrimes			measure_delta = (min_odelta - min_idelta)/2;
2451553Srgrimes		}
2461553Srgrimes
2471553Srgrimes		if (tcur.tv_sec > tdone.tv_sec
2481553Srgrimes		    || (tcur.tv_sec == tdone.tv_sec
2491553Srgrimes			&& tcur.tv_usec >= tdone.tv_usec))
2501553Srgrimes			break;
2511553Srgrimes	}
2521553Srgrimes
2531553Srgrimesquit:
2541553Srgrimes	seqno += TRIALS;		/* allocate our sequence numbers */
2551553Srgrimes
2561553Srgrimes	/*
2571553Srgrimes	 * If no answer is received for TRIALS consecutive times,
2581553Srgrimes	 * the machine is assumed to be down
2591553Srgrimes	 */
2601553Srgrimes	if (measure_status == GOOD) {
2611553Srgrimes		if (trace) {
2621553Srgrimes			fprintf(fd,
2631553Srgrimes				"measured delta %4d, %d trials to %-15s %s\n",
2641553Srgrimes			   	measure_delta, trials,
2651553Srgrimes				inet_ntoa(addr->sin_addr), hname);
2661553Srgrimes		}
2671553Srgrimes	} else if (print) {
2681553Srgrimes		if (errno != 0)
26930642Scharnier			warn("measure %s", hname);
2701553Srgrimes	} else {
2711553Srgrimes		if (errno != 0) {
2721553Srgrimes			syslog(LOG_ERR, "measure %s: %m", hname);
2731553Srgrimes		} else {
2741553Srgrimes			syslog(LOG_ERR, "measure: %s did not respond", hname);
2751553Srgrimes		}
2761553Srgrimes		if (trace) {
2771553Srgrimes			fprintf(fd,
2781553Srgrimes				"measure: %s failed after %d trials\n",
2791553Srgrimes				hname, trials);
2801553Srgrimes			(void)fflush(fd);
2811553Srgrimes		}
2821553Srgrimes	}
2831553Srgrimes
2841553Srgrimes	return(measure_status);
2851553Srgrimes}
2861553Srgrimes
2871553Srgrimes
2881553Srgrimes
2891553Srgrimes
2901553Srgrimes
2911553Srgrimes/*
2921553Srgrimes * round a number of milliseconds into a struct timeval
2931553Srgrimes */
2941553Srgrimesvoid
2951553Srgrimesmstotvround(res, x)
2961553Srgrimes	struct timeval *res;
2971553Srgrimes	long x;
2981553Srgrimes{
2991553Srgrimes	if (x < 0)
3001553Srgrimes		x = -((-x + 3)/5);
3011553Srgrimes	else
3021553Srgrimes		x = (x+3)/5;
3031553Srgrimes	x *= 5;
3041553Srgrimes	res->tv_sec = x/1000;
3051553Srgrimes	res->tv_usec = (x-res->tv_sec*1000)*1000;
3061553Srgrimes	if (res->tv_usec < 0) {
3071553Srgrimes		res->tv_usec += 1000000;
3081553Srgrimes		res->tv_sec--;
3091553Srgrimes	}
3101553Srgrimes}
3111553Srgrimes
3121553Srgrimesvoid
3131553Srgrimestimevaladd(tv1, tv2)
3141553Srgrimes	struct timeval *tv1, *tv2;
3151553Srgrimes{
3161553Srgrimes	tv1->tv_sec += tv2->tv_sec;
3171553Srgrimes	tv1->tv_usec += tv2->tv_usec;
3181553Srgrimes	if (tv1->tv_usec >= 1000000) {
3191553Srgrimes		tv1->tv_sec++;
3201553Srgrimes		tv1->tv_usec -= 1000000;
3211553Srgrimes	}
3221553Srgrimes	if (tv1->tv_usec < 0) {
3231553Srgrimes		tv1->tv_sec--;
3241553Srgrimes		tv1->tv_usec += 1000000;
3251553Srgrimes	}
3261553Srgrimes}
3271553Srgrimes
3281553Srgrimesvoid
3291553Srgrimestimevalsub(res, tv1, tv2)
3301553Srgrimes	struct timeval *res, *tv1, *tv2;
3311553Srgrimes{
3321553Srgrimes	res->tv_sec = tv1->tv_sec - tv2->tv_sec;
3331553Srgrimes	res->tv_usec = tv1->tv_usec - tv2->tv_usec;
3341553Srgrimes	if (res->tv_usec >= 1000000) {
3351553Srgrimes		res->tv_sec++;
3361553Srgrimes		res->tv_usec -= 1000000;
3371553Srgrimes	}
3381553Srgrimes	if (res->tv_usec < 0) {
3391553Srgrimes		res->tv_sec--;
3401553Srgrimes		res->tv_usec += 1000000;
3411553Srgrimes	}
3421553Srgrimes}
343