xref: /netbsd-src/usr.sbin/timed/timed/measure.c (revision 39a3c792a3a21f9bb762211397f58d82bd652902)
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 */
measure(u_long maxmsec,u_long wmsec,const char * hname,const struct sockaddr_in * addr,int printerr)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
mstotvround(struct timeval * res,long x)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
update_time(struct timeval * tv,const struct tsp * msg)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