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