Home | History | Annotate | Line # | Download | only in timed
      1 /*	$NetBSD: measure.c,v 1.17 2012/11/04 22:36:58 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1985, 1993 The Regents of the University of California.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)measure.c	8.2 (Berkeley) 3/26/95";
     36 #else
     37 __RCSID("$NetBSD: measure.c,v 1.17 2012/11/04 22:36:58 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "globals.h"
     42 #include <netinet/in_systm.h>
     43 #include <netinet/ip.h>
     44 #include <netinet/ip_icmp.h>
     45 #include <util.h>
     46 #ifdef SUPPORT_UTMPX
     47 #include <utmpx.h>
     48 #endif
     49 
     50 #define MSEC_DAY	(SECDAY*1000)
     51 
     52 #define PACKET_IN	1024
     53 
     54 #define MSGS		5		/* timestamps to average */
     55 #define TRIALS		10		/* max # of timestamps sent */
     56 
     57 extern int sock_raw;
     58 
     59 int measure_delta;
     60 
     61 extern int in_cksum(const void *, int);
     62 
     63 static n_short seqno = 0;
     64 
     65 /*
     66  * Measures the differences between machines' clocks using
     67  * ICMP timestamp messages.
     68  */
     69 int					/* status val defined in globals.h */
     70 measure(u_long maxmsec,			/* wait this many msec at most */
     71 	u_long wmsec,			/* msec to wait for an answer */
     72 	const char *hname,
     73 	const struct sockaddr_in *addr,
     74 	int printerr)			/* print complaints on stderr */
     75 {
     76 	socklen_t length;
     77 	int measure_status;
     78 	int rcvcount, trials;
     79 	int count;
     80 	struct pollfd set[1];
     81 	long sendtime, recvtime, histime1, histime2;
     82 	long idelta, odelta, total;
     83 	long min_idelta, min_odelta;
     84 	struct timeval tdone, tcur, ttrans, twait, tout;
     85 	u_char packet[PACKET_IN];
     86 	struct icmp icp;
     87 	struct icmp oicp;
     88 	struct ip ip;
     89 
     90 	min_idelta = min_odelta = 0x7fffffff;
     91 	measure_status = HOSTDOWN;
     92 	measure_delta = HOSTDOWN;
     93 	errno = 0;
     94 	trials = 0;
     95 
     96 	/* open raw socket used to measure time differences */
     97 	if (sock_raw < 0) {
     98 		sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
     99 		if (sock_raw < 0)  {
    100 			syslog(LOG_ERR, "opening raw socket: %m");
    101 			goto quit;
    102 		}
    103 	}
    104 
    105 	set[0].fd = sock_raw;
    106 	set[0].events = POLLIN;
    107 
    108 	/*
    109 	 * empty the icmp input queue
    110 	 */
    111 	for (;;) {
    112 		if (poll(set, 1, 0)) {
    113 			ssize_t ret;
    114 			length = sizeof(struct sockaddr_in);
    115 			ret = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
    116 				      0,&length);
    117 			if (ret < 0)
    118 				goto quit;
    119 			continue;
    120 		}
    121 		break;
    122 	}
    123 
    124 	/*
    125 	 * Choose the smallest transmission time in each of the two
    126 	 * directions. Use these two latter quantities to compute the delta
    127 	 * between the two clocks.
    128 	 */
    129 
    130 	oicp.icmp_type = ICMP_TSTAMP;
    131 	oicp.icmp_code = 0;
    132 	oicp.icmp_id = getpid();
    133 	oicp.icmp_rtime = 0;
    134 	oicp.icmp_ttime = 0;
    135 	oicp.icmp_seq = seqno;
    136 
    137 	(void)gettimeofday(&tdone, 0);
    138 	mstotvround(&tout, maxmsec);
    139 	timeradd(&tdone, &tout, &tdone);	/* when we give up */
    140 
    141 	mstotvround(&twait, wmsec);
    142 
    143 	tout.tv_sec = 0;
    144 	tout.tv_usec = 0;
    145 	rcvcount = 0;
    146 	while (rcvcount < MSGS) {
    147 		(void)gettimeofday(&tcur, 0);
    148 
    149 		/*
    150 		 * keep sending until we have sent the max
    151 		 */
    152 		if (trials < TRIALS) {
    153 			uint32_t otime;
    154 
    155 			trials++;
    156 			otime = (tcur.tv_sec % SECDAY) * 1000
    157                                             + tcur.tv_usec / 1000;
    158 			oicp.icmp_otime = htonl(otime);
    159 			oicp.icmp_cksum = 0;
    160 			oicp.icmp_cksum = in_cksum(&oicp, sizeof(oicp));
    161 
    162 			count = sendto(sock_raw, &oicp, sizeof(oicp), 0,
    163 				       (const struct sockaddr*)addr,
    164 				       sizeof(struct sockaddr));
    165 			if (count < 0) {
    166 				if (measure_status == HOSTDOWN)
    167 					measure_status = UNREACHABLE;
    168 				goto quit;
    169 			}
    170 			oicp.icmp_seq++;
    171 
    172 			timeradd(&tcur, &twait, &ttrans);
    173 		} else {
    174 			ttrans = tdone;
    175 		}
    176 
    177 		while (rcvcount < trials) {
    178 			ssize_t ret;
    179 
    180 			timersub(&ttrans, &tcur, &tout);
    181 			if (tout.tv_sec < 0)
    182 				tout.tv_sec = 0;
    183 
    184 			count = poll(set, 1, tout.tv_sec * 1000 + tout.tv_usec / 1000);
    185 			(void)gettimeofday(&tcur, (struct timezone *)0);
    186 			if (count <= 0)
    187 				break;
    188 
    189 			length = sizeof(struct sockaddr_in);
    190 			ret = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
    191 				      0,&length);
    192 			if (ret < 0)
    193 				goto quit;
    194 
    195 			/*
    196 			 * got something.  See if it is ours
    197 			 */
    198 
    199 			if ((size_t)ret < sizeof(ip))
    200 				continue;
    201 			memcpy(&ip, packet, sizeof(ip));
    202 			if ((size_t)ret < (size_t)ip.ip_hl << 2)
    203 				continue;
    204 			ret -= ip.ip_hl << 2;
    205 
    206 			memset(&icp, 0, sizeof(icp));
    207 			memcpy(&icp, &packet[ip.ip_hl << 2],
    208 				MIN((size_t)ret, sizeof(icp)));
    209 
    210 			if (icp.icmp_type != ICMP_TSTAMPREPLY
    211 			    || icp.icmp_id != oicp.icmp_id
    212 			    || icp.icmp_seq < seqno
    213 			    || icp.icmp_seq >= oicp.icmp_seq)
    214 				continue;
    215 
    216 			sendtime = ntohl(icp.icmp_otime);
    217 			recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
    218 				    tcur.tv_usec / 1000);
    219 
    220 			total = recvtime - sendtime;
    221 			if (total < 0)	/* do not hassle midnight */
    222 				continue;
    223 
    224 			rcvcount++;
    225 			histime1 = ntohl(icp.icmp_rtime);
    226 			histime2 = ntohl(icp.icmp_ttime);
    227 			/*
    228 			 * a host using a time format different from
    229 			 * msec. since midnight UT (as per RFC792) should
    230 			 * set the high order bit of the 32-bit time
    231 			 * value it transmits.
    232 			 */
    233 			if ((histime1 & 0x80000000) != 0) {
    234 				measure_status = NONSTDTIME;
    235 				goto quit;
    236 			}
    237 			measure_status = GOOD;
    238 
    239 			idelta = recvtime-histime2;
    240 			odelta = histime1-sendtime;
    241 
    242 			/* do not be confused by midnight */
    243 			if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
    244 			else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
    245 
    246 			if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
    247 			else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
    248 
    249 			/* save the quantization error so that we can get a
    250 			 * measurement finer than our system clock.
    251 			 */
    252 			if (total < MIN_ROUND) {
    253 				measure_delta = (odelta - idelta)/2;
    254 				goto quit;
    255 			}
    256 
    257 			if (idelta < min_idelta)
    258 				min_idelta = idelta;
    259 			if (odelta < min_odelta)
    260 				min_odelta = odelta;
    261 
    262 			measure_delta = (min_odelta - min_idelta)/2;
    263 		}
    264 
    265 		if (tcur.tv_sec > tdone.tv_sec
    266 		    || (tcur.tv_sec == tdone.tv_sec
    267 			&& tcur.tv_usec >= tdone.tv_usec))
    268 			break;
    269 	}
    270 
    271 quit:
    272 	seqno += TRIALS;		/* allocate our sequence numbers */
    273 
    274 	/*
    275 	 * If no answer is received for TRIALS consecutive times,
    276 	 * the machine is assumed to be down
    277 	 */
    278 	if (measure_status == GOOD) {
    279 		if (trace) {
    280 			fprintf(fd,
    281 				"measured delta %4d, %d trials to %-15s %s\n",
    282 			   	measure_delta, trials,
    283 				inet_ntoa(addr->sin_addr), hname);
    284 		}
    285 	} else if (printerr) {
    286 		if (errno != 0)
    287 			fprintf(stderr, "measure %s: %s\n", hname,
    288 				strerror(errno));
    289 	} else {
    290 		if (errno != 0) {
    291 			syslog(LOG_ERR, "measure %s: %m", hname);
    292 		} else {
    293 			syslog(LOG_ERR, "measure: %s did not respond", hname);
    294 		}
    295 		if (trace) {
    296 			fprintf(fd,
    297 				"measure: %s failed after %d trials\n",
    298 				hname, trials);
    299 			(void)fflush(fd);
    300 		}
    301 	}
    302 
    303 	return(measure_status);
    304 }
    305 
    306 
    307 
    308 
    309 
    310 /*
    311  * round a number of milliseconds into a struct timeval
    312  */
    313 void
    314 mstotvround(struct timeval *res, long x)
    315 {
    316 	if (x < 0)
    317 		x = -((-x + 3)/5);
    318 	else
    319 		x = (x+3)/5;
    320 	x *= 5;
    321 
    322 	res->tv_sec = x/1000;
    323 	res->tv_usec = (x-res->tv_sec*1000)*1000;
    324 	if (res->tv_usec < 0) {
    325 		res->tv_usec += 1000000;
    326 		res->tv_sec--;
    327 	}
    328 }
    329 
    330 void
    331 update_time(struct timeval *tv, const struct tsp *msg)
    332 {
    333 #ifdef SUPPORT_UTMP
    334 	logwtmp("|", "date", "");
    335 #endif
    336 #ifdef SUPPORT_UTMPX
    337 	logwtmpx("|", "date", "", 0, OLD_TIME);
    338 #endif
    339 	tv->tv_sec = msg->tsp_time.tv_sec;
    340 	tv->tv_usec = msg->tsp_time.tv_usec;
    341 	(void)settimeofday(tv, 0);
    342 #ifdef SUPPORT_UTMP
    343 	logwtmp("}", "date", "");
    344 #endif
    345 #ifdef SUPPORT_UTMPX
    346 	logwtmpx("}", "date", "", 0, NEW_TIME);
    347 #endif
    348 }
    349