measure.c revision 331722
1195033Salc/*-
2195033Salc * Copyright (c) 1985, 1993
3195033Salc *	The Regents of the University of California.  All rights reserved.
4195033Salc *
5195033Salc * Redistribution and use in source and binary forms, with or without
6195033Salc * modification, are permitted provided that the following conditions
7195033Salc * are met:
8195033Salc * 1. Redistributions of source code must retain the above copyright
9195033Salc *    notice, this list of conditions and the following disclaimer.
10195033Salc * 2. Redistributions in binary form must reproduce the above copyright
11195033Salc *    notice, this list of conditions and the following disclaimer in the
12195033Salc *    documentation and/or other materials provided with the distribution.
13195033Salc * 4. Neither the name of the University nor the names of its contributors
14195033Salc *    may be used to endorse or promote products derived from this software
15195033Salc *    without specific prior written permission.
16195033Salc *
17195033Salc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18195033Salc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19195033Salc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20195033Salc * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21195033Salc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22195033Salc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23195033Salc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24195033Salc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25195033Salc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26195033Salc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27195033Salc * SUCH DAMAGE.
28195033Salc */
29195033Salc
30195033Salc#ifndef lint
31195033Salc#if 0
32295036Smmelstatic char sccsid[] = "@(#)measure.c	8.1 (Berkeley) 6/6/93";
33295036Smmel#endif
34295036Smmelstatic const char rcsid[] =
35280712Sian  "$FreeBSD: stable/11/usr.sbin/timed/timed/measure.c 331722 2018-03-29 02:50:57Z eadler $";
36280712Sian#endif /* not lint */
37291492Smmel
38291492Smmel#include "globals.h"
39291492Smmel#include <netinet/in_systm.h>
40291492Smmel#include <netinet/ip.h>
41291492Smmel#include <netinet/ip_icmp.h>
42280712Sian
43291492Smmel#define MSEC_DAY	(SECDAY*1000)
44291492Smmel
45295036Smmel#define PACKET_IN	1024
46295036Smmel
47291492Smmel#define MSGS		5		/* timestamps to average */
48291492Smmel#define TRIALS		10		/* max # of timestamps sent */
49295036Smmel
50280712Sianextern int sock_raw;
51244414Scognet
52195649Salcint measure_delta;
53244414Scognet
54280712Sianstatic n_short seqno = 0;
55195033Salc
56195060Salc/*
57 * Measures the differences between machines' clocks using
58 * ICMP timestamp messages.
59 * maxmsec	wait this many msec at most
60 * wmsec	msec to wait for an answer
61 * print	print complaints on stderr
62 */
63int					/* status val defined in globals.h */
64measure(u_long maxmsec, u_long wmsec, char *hname, struct sockaddr_in *addr, int print)
65{
66	int length;
67	int measure_status;
68	int rcvcount, trials;
69	int cc, count;
70	fd_set ready;
71	long sendtime, recvtime, histime1, histime2;
72	long idelta, odelta, total;
73	long min_idelta, min_odelta;
74	struct timeval tdone, tcur, ttrans, twait, tout;
75	u_char packet[PACKET_IN], opacket[64];
76	register struct icmp *icp = (struct icmp *) packet;
77	register struct icmp *oicp = (struct icmp *) opacket;
78	struct ip *ip = (struct ip *) packet;
79
80	min_idelta = min_odelta = 0x7fffffff;
81	measure_status = HOSTDOWN;
82	measure_delta = HOSTDOWN;
83	trials = 0;
84	errno = 0;
85
86	/* open raw socket used to measure time differences */
87	if (sock_raw < 0) {
88		sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
89		if (sock_raw < 0)  {
90			syslog(LOG_ERR, "opening raw socket: %m");
91			goto quit;
92		}
93	}
94
95
96	/*
97	 * empty the icmp input queue
98	 */
99	FD_ZERO(&ready);
100	for (;;) {
101		tout.tv_sec = tout.tv_usec = 0;
102		FD_SET(sock_raw, &ready);
103		if (select(sock_raw+1, &ready, 0,0, &tout)) {
104			length = sizeof(struct sockaddr_in);
105			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
106				      0,&length);
107			if (cc < 0)
108				goto quit;
109			continue;
110		}
111		break;
112	}
113
114	/*
115	 * Choose the smallest transmission time in each of the two
116	 * directions. Use these two latter quantities to compute the delta
117	 * between the two clocks.
118	 */
119
120	oicp->icmp_type = ICMP_TSTAMP;
121	oicp->icmp_code = 0;
122	oicp->icmp_id = getpid();
123	oicp->icmp_rtime = 0;
124	oicp->icmp_ttime = 0;
125	oicp->icmp_seq = seqno;
126
127	FD_ZERO(&ready);
128
129	(void)gettimeofday(&tdone, NULL);
130	mstotvround(&tout, maxmsec);
131	timevaladd(&tdone, &tout);		/* when we give up */
132
133	mstotvround(&twait, wmsec);
134
135	rcvcount = 0;
136	while (rcvcount < MSGS) {
137		(void)gettimeofday(&tcur, NULL);
138
139		/*
140		 * keep sending until we have sent the max
141		 */
142		if (trials < TRIALS) {
143			trials++;
144			oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
145						 + tcur.tv_usec / 1000);
146			oicp->icmp_cksum = 0;
147			oicp->icmp_cksum = in_cksum((u_short*)oicp,
148						    sizeof(*oicp));
149
150			count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
151				       (struct sockaddr*)addr,
152				       sizeof(struct sockaddr));
153			if (count < 0) {
154				if (measure_status == HOSTDOWN)
155					measure_status = UNREACHABLE;
156				goto quit;
157			}
158			++oicp->icmp_seq;
159
160			ttrans = tcur;
161			timevaladd(&ttrans, &twait);
162		} else {
163			ttrans = tdone;
164		}
165
166		while (rcvcount < trials) {
167			timevalsub(&tout, &ttrans, &tcur);
168			if (tout.tv_sec < 0)
169				tout.tv_sec = 0;
170
171			FD_SET(sock_raw, &ready);
172			count = select(sock_raw+1, &ready, (fd_set *)0,
173				       (fd_set *)0, &tout);
174			(void)gettimeofday(&tcur, NULL);
175			if (count <= 0)
176				break;
177
178			length = sizeof(struct sockaddr_in);
179			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
180				      0,&length);
181			if (cc < 0)
182				goto quit;
183
184			/*
185			 * got something.  See if it is ours
186			 */
187			icp = (struct icmp *)(packet + (ip->ip_hl << 2));
188			if (cc < sizeof(*ip)
189			    || icp->icmp_type != ICMP_TSTAMPREPLY
190			    || icp->icmp_id != oicp->icmp_id
191			    || icp->icmp_seq < seqno
192			    || icp->icmp_seq >= oicp->icmp_seq)
193				continue;
194
195
196			sendtime = ntohl(icp->icmp_otime);
197			recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
198				    tcur.tv_usec / 1000);
199
200			total = recvtime-sendtime;
201			if (total < 0)	/* do not hassle midnight */
202				continue;
203
204			rcvcount++;
205			histime1 = ntohl(icp->icmp_rtime);
206			histime2 = ntohl(icp->icmp_ttime);
207			/*
208			 * a host using a time format different from
209			 * msec. since midnight UT (as per RFC792) should
210			 * set the high order bit of the 32-bit time
211			 * value it transmits.
212			 */
213			if ((histime1 & 0x80000000) != 0) {
214				measure_status = NONSTDTIME;
215				goto quit;
216			}
217			measure_status = GOOD;
218
219			idelta = recvtime-histime2;
220			odelta = histime1-sendtime;
221
222			/* do not be confused by midnight */
223			if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
224			else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
225
226			if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
227			else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
228
229			/* save the quantization error so that we can get a
230			 * measurement finer than our system clock.
231			 */
232			if (total < MIN_ROUND) {
233				measure_delta = (odelta - idelta)/2;
234				goto quit;
235			}
236
237			if (idelta < min_idelta)
238				min_idelta = idelta;
239			if (odelta < min_odelta)
240				min_odelta = odelta;
241
242			measure_delta = (min_odelta - min_idelta)/2;
243		}
244
245		if (tcur.tv_sec > tdone.tv_sec
246		    || (tcur.tv_sec == tdone.tv_sec
247			&& tcur.tv_usec >= tdone.tv_usec))
248			break;
249	}
250
251quit:
252	seqno += TRIALS;		/* allocate our sequence numbers */
253
254	/*
255	 * If no answer is received for TRIALS consecutive times,
256	 * the machine is assumed to be down
257	 */
258	if (measure_status == GOOD) {
259		if (trace) {
260			fprintf(fd,
261				"measured delta %4d, %d trials to %-15s %s\n",
262			   	measure_delta, trials,
263				inet_ntoa(addr->sin_addr), hname);
264		}
265	} else if (print) {
266		if (errno != 0)
267			warn("measure %s", hname);
268	} else {
269		if (errno != 0) {
270			syslog(LOG_ERR, "measure %s: %m", hname);
271		} else {
272			syslog(LOG_ERR, "measure: %s did not respond", hname);
273		}
274		if (trace) {
275			fprintf(fd,
276				"measure: %s failed after %d trials\n",
277				hname, trials);
278			(void)fflush(fd);
279		}
280	}
281
282	return(measure_status);
283}
284
285
286
287
288
289/*
290 * round a number of milliseconds into a struct timeval
291 */
292void
293mstotvround(struct timeval *res, long x)
294{
295	if (x < 0)
296		x = -((-x + 3)/5);
297	else
298		x = (x+3)/5;
299	x *= 5;
300	res->tv_sec = x/1000;
301	res->tv_usec = (x-res->tv_sec*1000)*1000;
302	if (res->tv_usec < 0) {
303		res->tv_usec += 1000000;
304		res->tv_sec--;
305	}
306}
307
308void
309timevaladd(struct timeval *tv1, struct timeval *tv2)
310{
311	tv1->tv_sec += tv2->tv_sec;
312	tv1->tv_usec += tv2->tv_usec;
313	if (tv1->tv_usec >= 1000000) {
314		tv1->tv_sec++;
315		tv1->tv_usec -= 1000000;
316	}
317	if (tv1->tv_usec < 0) {
318		tv1->tv_sec--;
319		tv1->tv_usec += 1000000;
320	}
321}
322
323void
324timevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2)
325{
326	res->tv_sec = tv1->tv_sec - tv2->tv_sec;
327	res->tv_usec = tv1->tv_usec - tv2->tv_usec;
328	if (res->tv_usec >= 1000000) {
329		res->tv_sec++;
330		res->tv_usec -= 1000000;
331	}
332	if (res->tv_usec < 0) {
333		res->tv_sec--;
334		res->tv_usec += 1000000;
335	}
336}
337