xref: /original-bsd/usr.sbin/timed/timed/measure.c (revision 0842ddeb)
1 /*-
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)measure.c	8.2 (Berkeley) 03/26/95";
10 #endif /* not lint */
11 
12 #ifdef sgi
13 #ident "$Revision: 1.8 $"
14 #endif
15 
16 #include "globals.h"
17 #include <netinet/in_systm.h>
18 #include <netinet/ip.h>
19 #include <netinet/ip_icmp.h>
20 
21 #define MSEC_DAY	(SECDAY*1000)
22 
23 #define PACKET_IN	1024
24 
25 #define MSGS		5		/* timestamps to average */
26 #define TRIALS		10		/* max # of timestamps sent */
27 
28 extern int sock_raw;
29 
30 int measure_delta;
31 
32 static n_short seqno = 0;
33 
34 /*
35  * Measures the differences between machines' clocks using
36  * ICMP timestamp messages.
37  */
38 int					/* status val defined in globals.h */
39 measure(maxmsec, wmsec, hname, addr, print)
40 	u_long maxmsec;			/* wait this many msec at most */
41 	u_long wmsec;			/* msec to wait for an answer */
42 	char *hname;
43 	struct sockaddr_in *addr;
44 	int print;			/* print complaints on stderr */
45 {
46 	int length;
47 	int measure_status;
48 	int rcvcount, trials;
49 	int cc, count;
50 	fd_set ready;
51 	long sendtime, recvtime, histime1, histime2;
52 	long idelta, odelta, total;
53 	long min_idelta, min_odelta;
54 	struct timeval tdone, tcur, ttrans, twait, tout;
55 	u_char packet[PACKET_IN], opacket[64];
56 	register struct icmp *icp = (struct icmp *) packet;
57 	register struct icmp *oicp = (struct icmp *) opacket;
58 	struct ip *ip = (struct ip *) packet;
59 
60 	min_idelta = min_odelta = 0x7fffffff;
61 	measure_status = HOSTDOWN;
62 	measure_delta = HOSTDOWN;
63 	errno = 0;
64 
65 	/* open raw socket used to measure time differences */
66 	if (sock_raw < 0) {
67 		sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
68 		if (sock_raw < 0)  {
69 			syslog(LOG_ERR, "opening raw socket: %m");
70 			goto quit;
71 		}
72 	}
73 
74 
75 	/*
76 	 * empty the icmp input queue
77 	 */
78 	FD_ZERO(&ready);
79 	for (;;) {
80 		tout.tv_sec = tout.tv_usec = 0;
81 		FD_SET(sock_raw, &ready);
82 		if (select(sock_raw+1, &ready, 0,0, &tout)) {
83 			length = sizeof(struct sockaddr_in);
84 			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
85 				      0,&length);
86 			if (cc < 0)
87 				goto quit;
88 			continue;
89 		}
90 		break;
91 	}
92 
93 	/*
94 	 * Choose the smallest transmission time in each of the two
95 	 * directions. Use these two latter quantities to compute the delta
96 	 * between the two clocks.
97 	 */
98 
99 	oicp->icmp_type = ICMP_TSTAMP;
100 	oicp->icmp_code = 0;
101 	oicp->icmp_id = getpid();
102 	oicp->icmp_rtime = 0;
103 	oicp->icmp_ttime = 0;
104 	oicp->icmp_seq = seqno;
105 
106 	FD_ZERO(&ready);
107 
108 #ifdef sgi
109 	sginap(1);			/* start at a clock tick */
110 #endif /* sgi */
111 
112 	(void)gettimeofday(&tdone, 0);
113 	mstotvround(&tout, maxmsec);
114 	timevaladd(&tdone, &tout);		/* when we give up */
115 
116 	mstotvround(&twait, wmsec);
117 
118 	rcvcount = 0;
119 	trials = 0;
120 	while (rcvcount < MSGS) {
121 		(void)gettimeofday(&tcur, 0);
122 
123 		/*
124 		 * keep sending until we have sent the max
125 		 */
126 		if (trials < TRIALS) {
127 			trials++;
128 			oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
129 						 + tcur.tv_usec / 1000);
130 			oicp->icmp_cksum = 0;
131 			oicp->icmp_cksum = in_cksum((u_short*)oicp,
132 						    sizeof(*oicp));
133 
134 			count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
135 				       (struct sockaddr*)addr,
136 				       sizeof(struct sockaddr));
137 			if (count < 0) {
138 				if (measure_status == HOSTDOWN)
139 					measure_status = UNREACHABLE;
140 				goto quit;
141 			}
142 			++oicp->icmp_seq;
143 
144 			ttrans = tcur;
145 			timevaladd(&ttrans, &twait);
146 		} else {
147 			ttrans = tdone;
148 		}
149 
150 		while (rcvcount < trials) {
151 			timevalsub(&tout, &ttrans, &tcur);
152 			if (tout.tv_sec < 0)
153 				tout.tv_sec = 0;
154 
155 			FD_SET(sock_raw, &ready);
156 			count = select(sock_raw+1, &ready, (fd_set *)0,
157 				       (fd_set *)0, &tout);
158 			(void)gettimeofday(&tcur, (struct timezone *)0);
159 			if (count <= 0)
160 				break;
161 
162 			length = sizeof(struct sockaddr_in);
163 			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
164 				      0,&length);
165 			if (cc < 0)
166 				goto quit;
167 
168 			/*
169 			 * got something.  See if it is ours
170 			 */
171 			icp = (struct icmp *)(packet + (ip->ip_hl << 2));
172 			if (cc < sizeof(*ip)
173 			    || icp->icmp_type != ICMP_TSTAMPREPLY
174 			    || icp->icmp_id != oicp->icmp_id
175 			    || icp->icmp_seq < seqno
176 			    || icp->icmp_seq >= oicp->icmp_seq)
177 				continue;
178 
179 
180 			sendtime = ntohl(icp->icmp_otime);
181 			recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
182 				    tcur.tv_usec / 1000);
183 
184 			total = recvtime-sendtime;
185 			if (total < 0)	/* do not hassle midnight */
186 				continue;
187 
188 			rcvcount++;
189 			histime1 = ntohl(icp->icmp_rtime);
190 			histime2 = ntohl(icp->icmp_ttime);
191 			/*
192 			 * a host using a time format different from
193 			 * msec. since midnight UT (as per RFC792) should
194 			 * set the high order bit of the 32-bit time
195 			 * value it transmits.
196 			 */
197 			if ((histime1 & 0x80000000) != 0) {
198 				measure_status = NONSTDTIME;
199 				goto quit;
200 			}
201 			measure_status = GOOD;
202 
203 			idelta = recvtime-histime2;
204 			odelta = histime1-sendtime;
205 
206 			/* do not be confused by midnight */
207 			if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
208 			else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
209 
210 			if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
211 			else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
212 
213 			/* save the quantization error so that we can get a
214 			 * measurement finer than our system clock.
215 			 */
216 			if (total < MIN_ROUND) {
217 				measure_delta = (odelta - idelta)/2;
218 				goto quit;
219 			}
220 
221 			if (idelta < min_idelta)
222 				min_idelta = idelta;
223 			if (odelta < min_odelta)
224 				min_odelta = odelta;
225 
226 			measure_delta = (min_odelta - min_idelta)/2;
227 		}
228 
229 		if (tcur.tv_sec > tdone.tv_sec
230 		    || (tcur.tv_sec == tdone.tv_sec
231 			&& tcur.tv_usec >= tdone.tv_usec))
232 			break;
233 	}
234 
235 quit:
236 	seqno += TRIALS;		/* allocate our sequence numbers */
237 
238 	/*
239 	 * If no answer is received for TRIALS consecutive times,
240 	 * the machine is assumed to be down
241 	 */
242 	if (measure_status == GOOD) {
243 		if (trace) {
244 			fprintf(fd,
245 				"measured delta %4d, %d trials to %-15s %s\n",
246 			   	measure_delta, trials,
247 				inet_ntoa(addr->sin_addr), hname);
248 		}
249 	} else if (print) {
250 		if (errno != 0)
251 			fprintf(stderr, "measure %s: %s\n", hname,
252 				strerror(errno));
253 	} else {
254 		if (errno != 0) {
255 			syslog(LOG_ERR, "measure %s: %m", hname);
256 		} else {
257 			syslog(LOG_ERR, "measure: %s did not respond", hname);
258 		}
259 		if (trace) {
260 			fprintf(fd,
261 				"measure: %s failed after %d trials\n",
262 				hname, trials);
263 			(void)fflush(fd);
264 		}
265 	}
266 
267 	return(measure_status);
268 }
269 
270 
271 
272 
273 
274 /*
275  * round a number of milliseconds into a struct timeval
276  */
277 void
278 mstotvround(res, x)
279 	struct timeval *res;
280 	long x;
281 {
282 #ifndef sgi
283 	if (x < 0)
284 		x = -((-x + 3)/5);
285 	else
286 		x = (x+3)/5;
287 	x *= 5;
288 #endif /* sgi */
289 	res->tv_sec = x/1000;
290 	res->tv_usec = (x-res->tv_sec*1000)*1000;
291 	if (res->tv_usec < 0) {
292 		res->tv_usec += 1000000;
293 		res->tv_sec--;
294 	}
295 }
296 
297 void
298 timevaladd(tv1, tv2)
299 	struct timeval *tv1, *tv2;
300 {
301 	tv1->tv_sec += tv2->tv_sec;
302 	tv1->tv_usec += tv2->tv_usec;
303 	if (tv1->tv_usec >= 1000000) {
304 		tv1->tv_sec++;
305 		tv1->tv_usec -= 1000000;
306 	}
307 	if (tv1->tv_usec < 0) {
308 		tv1->tv_sec--;
309 		tv1->tv_usec += 1000000;
310 	}
311 }
312 
313 void
314 timevalsub(res, tv1, tv2)
315 	struct timeval *res, *tv1, *tv2;
316 {
317 	res->tv_sec = tv1->tv_sec - tv2->tv_sec;
318 	res->tv_usec = tv1->tv_usec - tv2->tv_usec;
319 	if (res->tv_usec >= 1000000) {
320 		res->tv_sec++;
321 		res->tv_usec -= 1000000;
322 	}
323 	if (res->tv_usec < 0) {
324 		res->tv_sec--;
325 		res->tv_usec += 1000000;
326 	}
327 }
328