MFC r1.13 (HEAD):
[dragonfly.git] / usr.sbin / timed / timed / measure.c
blobda20aa782e7287500d6018f1e788f52fd559c674
1 /*-
2 * Copyright (c) 1985, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * @(#)measure.c 8.1 (Berkeley) 6/6/93
34 * $FreeBSD: src/usr.sbin/timed/timed/measure.c,v 1.6 1999/08/28 01:20:17 peter Exp $
35 * $DragonFly: src/usr.sbin/timed/timed/measure.c,v 1.7 2004/09/05 02:20:15 dillon Exp $
38 #include "globals.h"
39 #include <netinet/in_systm.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_icmp.h>
43 #define MSEC_DAY (SECDAY*1000)
45 #define PACKET_IN 1024
47 #define MSGS 5 /* timestamps to average */
48 #define TRIALS 10 /* max # of timestamps sent */
50 extern int sock_raw;
52 int measure_delta;
54 static n_short seqno = 0;
57 * Measures the differences between machines' clocks using
58 * ICMP timestamp messages.
60 int /* status val defined in globals.h */
61 measure(u_long maxmsec, /* wait this many msec at most */
62 u_long wmsec, /* msec to wait for an answer */
63 char *hname, struct sockaddr_in *addr,
64 int printerr) /* print complaints on stderr */
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 struct icmp *icp = (struct icmp *)packet;
77 struct icmp *oicp = (struct icmp *)opacket;
78 struct ip *ip = (struct ip *)packet;
80 min_idelta = min_odelta = 0x7fffffff;
81 measure_status = HOSTDOWN;
82 measure_delta = HOSTDOWN;
83 errno = 0;
84 trials = 0; /* avoid compiler warning due to goto quit below */
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;
97 * empty the icmp input queue
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;
111 break;
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.
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;
127 FD_ZERO(&ready);
129 gettimeofday(&tdone, 0);
130 mstotvround(&tout, maxmsec);
131 timevaladd(&tdone, &tout); /* when we give up */
133 mstotvround(&twait, wmsec);
135 rcvcount = 0;
136 trials = 0;
137 while (rcvcount < MSGS) {
138 gettimeofday(&tcur, 0);
141 * keep sending until we have sent the max
143 if (trials < TRIALS) {
144 trials++;
145 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
146 + tcur.tv_usec / 1000);
147 oicp->icmp_cksum = 0;
148 oicp->icmp_cksum = in_cksum((u_short*)oicp,
149 sizeof(*oicp));
151 count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
152 (struct sockaddr*)addr,
153 sizeof(struct sockaddr));
154 if (count < 0) {
155 if (measure_status == HOSTDOWN)
156 measure_status = UNREACHABLE;
157 goto quit;
159 ++oicp->icmp_seq;
161 ttrans = tcur;
162 timevaladd(&ttrans, &twait);
163 } else {
164 ttrans = tdone;
167 while (rcvcount < trials) {
168 timevalsub(&tout, &ttrans, &tcur);
169 if (tout.tv_sec < 0)
170 tout.tv_sec = 0;
172 FD_SET(sock_raw, &ready);
173 count = select(sock_raw+1, &ready, (fd_set *)0,
174 (fd_set *)0, &tout);
175 gettimeofday(&tcur, (struct timezone *)0);
176 if (count <= 0)
177 break;
179 length = sizeof(struct sockaddr_in);
180 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
181 0,&length);
182 if (cc < 0)
183 goto quit;
186 * got something. See if it is ours
188 icp = (struct icmp *)(packet + (ip->ip_hl << 2));
189 if (cc < (int)sizeof(*ip)
190 || icp->icmp_type != ICMP_TSTAMPREPLY
191 || icp->icmp_id != oicp->icmp_id
192 || icp->icmp_seq < seqno
193 || icp->icmp_seq >= oicp->icmp_seq)
194 continue;
197 sendtime = ntohl(icp->icmp_otime);
198 recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
199 tcur.tv_usec / 1000);
201 total = recvtime-sendtime;
202 if (total < 0) /* do not hassle midnight */
203 continue;
205 rcvcount++;
206 histime1 = ntohl(icp->icmp_rtime);
207 histime2 = ntohl(icp->icmp_ttime);
209 * a host using a time format different from
210 * msec. since midnight UT (as per RFC792) should
211 * set the high order bit of the 32-bit time
212 * value it transmits.
214 if ((histime1 & 0x80000000) != 0) {
215 measure_status = NONSTDTIME;
216 goto quit;
218 measure_status = GOOD;
220 idelta = recvtime-histime2;
221 odelta = histime1-sendtime;
223 /* do not be confused by midnight */
224 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
225 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
227 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
228 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
230 /* save the quantization error so that we can get a
231 * measurement finer than our system clock.
233 if (total < MIN_ROUND) {
234 measure_delta = (odelta - idelta)/2;
235 goto quit;
238 if (idelta < min_idelta)
239 min_idelta = idelta;
240 if (odelta < min_odelta)
241 min_odelta = odelta;
243 measure_delta = (min_odelta - min_idelta)/2;
246 if (tcur.tv_sec > tdone.tv_sec
247 || (tcur.tv_sec == tdone.tv_sec
248 && tcur.tv_usec >= tdone.tv_usec))
249 break;
252 quit:
253 seqno += TRIALS; /* allocate our sequence numbers */
256 * If no answer is received for TRIALS consecutive times,
257 * the machine is assumed to be down
259 if (measure_status == GOOD) {
260 if (trace) {
261 fprintf(fd,
262 "measured delta %4d, %d trials to %-15s %s\n",
263 measure_delta, trials,
264 inet_ntoa(addr->sin_addr), hname);
266 } else if (printerr) {
267 if (errno != 0)
268 warn("measure %s", hname);
269 } else {
270 if (errno != 0) {
271 syslog(LOG_ERR, "measure %s: %m", hname);
272 } else {
273 syslog(LOG_ERR, "measure: %s did not respond", hname);
275 if (trace) {
276 fprintf(fd,
277 "measure: %s failed after %d trials\n",
278 hname, trials);
279 fflush(fd);
283 return(measure_status);
291 * round a number of milliseconds into a struct timeval
293 void
294 mstotvround(struct timeval *res, long x)
297 if (x < 0)
298 x = -((-x + 3)/5);
299 else
300 x = (x+3)/5;
301 x *= 5;
303 res->tv_sec = x/1000;
304 res->tv_usec = (x-res->tv_sec*1000)*1000;
305 if (res->tv_usec < 0) {
306 res->tv_usec += 1000000;
307 res->tv_sec--;
311 void
312 timevaladd(struct timeval *tv1, struct timeval *tv2)
315 tv1->tv_sec += tv2->tv_sec;
316 tv1->tv_usec += tv2->tv_usec;
317 if (tv1->tv_usec >= 1000000) {
318 tv1->tv_sec++;
319 tv1->tv_usec -= 1000000;
321 if (tv1->tv_usec < 0) {
322 tv1->tv_sec--;
323 tv1->tv_usec += 1000000;
327 void
328 timevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2)
331 res->tv_sec = tv1->tv_sec - tv2->tv_sec;
332 res->tv_usec = tv1->tv_usec - tv2->tv_usec;
333 if (res->tv_usec >= 1000000) {
334 res->tv_sec++;
335 res->tv_usec -= 1000000;
337 if (res->tv_usec < 0) {
338 res->tv_sec--;
339 res->tv_usec += 1000000;