drm/linux: Add io_schedule_timeout()
[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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)measure.c        8.1 (Berkeley) 6/6/93
30  * $FreeBSD: src/usr.sbin/timed/timed/measure.c,v 1.6 1999/08/28 01:20:17 peter Exp $
31  * $DragonFly: src/usr.sbin/timed/timed/measure.c,v 1.7 2004/09/05 02:20:15 dillon Exp $
32  */
33
34 #include "globals.h"
35 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip_icmp.h>
38
39 #define MSEC_DAY        (SECDAY*1000)
40
41 #define PACKET_IN       1024
42
43 #define MSGS            5               /* timestamps to average */
44 #define TRIALS          10              /* max # of timestamps sent */
45
46 extern int sock_raw;
47
48 int measure_delta;
49
50 static n_short seqno = 0;
51
52 /*
53  * Measures the differences between machines' clocks using
54  * ICMP timestamp messages.
55  */
56 int                                     /* status val defined in globals.h */
57 measure(u_long maxmsec,                 /* wait this many msec at most */
58         u_long wmsec,                   /* msec to wait for an answer */
59         char *hname, struct sockaddr_in *addr,
60         int printerr)                   /* print complaints on stderr */
61 {
62         int length;
63         int measure_status;
64         int rcvcount, trials;
65         int cc, count;
66         fd_set ready;
67         long sendtime, recvtime, histime1, histime2;
68         long idelta, odelta, total;
69         long min_idelta, min_odelta;
70         struct timeval tdone, tcur, ttrans, twait, tout;
71         u_char packet[PACKET_IN], opacket[64];
72         struct icmp *icp = (struct icmp *)packet;
73         struct icmp *oicp = (struct icmp *)opacket;
74         struct ip *ip = (struct ip *)packet;
75
76         min_idelta = min_odelta = 0x7fffffff;
77         measure_status = HOSTDOWN;
78         measure_delta = HOSTDOWN;
79         errno = 0;
80         trials = 0;     /* avoid compiler warning due to goto quit below */
81
82         /* open raw socket used to measure time differences */
83         if (sock_raw < 0) {
84                 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
85                 if (sock_raw < 0)  {
86                         syslog(LOG_ERR, "opening raw socket: %m");
87                         goto quit;
88                 }
89         }
90
91
92         /*
93          * empty the icmp input queue
94          */
95         FD_ZERO(&ready);
96         for (;;) {
97                 tout.tv_sec = tout.tv_usec = 0;
98                 FD_SET(sock_raw, &ready);
99                 if (select(sock_raw+1, &ready, 0,0, &tout)) {
100                         length = sizeof(struct sockaddr_in);
101                         cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
102                                       0,&length);
103                         if (cc < 0)
104                                 goto quit;
105                         continue;
106                 }
107                 break;
108         }
109
110         /*
111          * Choose the smallest transmission time in each of the two
112          * directions. Use these two latter quantities to compute the delta
113          * between the two clocks.
114          */
115
116         oicp->icmp_type = ICMP_TSTAMP;
117         oicp->icmp_code = 0;
118         oicp->icmp_id = getpid();
119         oicp->icmp_rtime = 0;
120         oicp->icmp_ttime = 0;
121         oicp->icmp_seq = seqno;
122
123         FD_ZERO(&ready);
124
125         gettimeofday(&tdone, 0);
126         mstotvround(&tout, maxmsec);
127         timevaladd(&tdone, &tout);              /* when we give up */
128
129         mstotvround(&twait, wmsec);
130
131         rcvcount = 0;
132         trials = 0;
133         while (rcvcount < MSGS) {
134                 gettimeofday(&tcur, 0);
135
136                 /*
137                  * keep sending until we have sent the max
138                  */
139                 if (trials < TRIALS) {
140                         trials++;
141                         oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
142                                                  + tcur.tv_usec / 1000);
143                         oicp->icmp_cksum = 0;
144                         oicp->icmp_cksum = in_cksum((u_short*)oicp,
145                                                     sizeof(*oicp));
146
147                         count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
148                                        (struct sockaddr*)addr,
149                                        sizeof(struct sockaddr));
150                         if (count < 0) {
151                                 if (measure_status == HOSTDOWN)
152                                         measure_status = UNREACHABLE;
153                                 goto quit;
154                         }
155                         ++oicp->icmp_seq;
156
157                         ttrans = tcur;
158                         timevaladd(&ttrans, &twait);
159                 } else {
160                         ttrans = tdone;
161                 }
162
163                 while (rcvcount < trials) {
164                         timevalsub(&tout, &ttrans, &tcur);
165                         if (tout.tv_sec < 0)
166                                 tout.tv_sec = 0;
167
168                         FD_SET(sock_raw, &ready);
169                         count = select(sock_raw+1, &ready, NULL, NULL, &tout);
170                         gettimeofday(&tcur, NULL);
171                         if (count <= 0)
172                                 break;
173
174                         length = sizeof(struct sockaddr_in);
175                         cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
176                                       0,&length);
177                         if (cc < 0)
178                                 goto quit;
179
180                         /*
181                          * got something.  See if it is ours
182                          */
183                         icp = (struct icmp *)(packet + (ip->ip_hl << 2));
184                         if (cc < (int)sizeof(*ip)
185                             || icp->icmp_type != ICMP_TSTAMPREPLY
186                             || icp->icmp_id != oicp->icmp_id
187                             || icp->icmp_seq < seqno
188                             || icp->icmp_seq >= oicp->icmp_seq)
189                                 continue;
190
191
192                         sendtime = ntohl(icp->icmp_otime);
193                         recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
194                                     tcur.tv_usec / 1000);
195
196                         total = recvtime-sendtime;
197                         if (total < 0)  /* do not hassle midnight */
198                                 continue;
199
200                         rcvcount++;
201                         histime1 = ntohl(icp->icmp_rtime);
202                         histime2 = ntohl(icp->icmp_ttime);
203                         /*
204                          * a host using a time format different from
205                          * msec. since midnight UT (as per RFC792) should
206                          * set the high order bit of the 32-bit time
207                          * value it transmits.
208                          */
209                         if ((histime1 & 0x80000000) != 0) {
210                                 measure_status = NONSTDTIME;
211                                 goto quit;
212                         }
213                         measure_status = GOOD;
214
215                         idelta = recvtime-histime2;
216                         odelta = histime1-sendtime;
217
218                         /* do not be confused by midnight */
219                         if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
220                         else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
221
222                         if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
223                         else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
224
225                         /* save the quantization error so that we can get a
226                          * measurement finer than our system clock.
227                          */
228                         if (total < MIN_ROUND) {
229                                 measure_delta = (odelta - idelta)/2;
230                                 goto quit;
231                         }
232
233                         if (idelta < min_idelta)
234                                 min_idelta = idelta;
235                         if (odelta < min_odelta)
236                                 min_odelta = odelta;
237
238                         measure_delta = (min_odelta - min_idelta)/2;
239                 }
240
241                 if (tcur.tv_sec > tdone.tv_sec
242                     || (tcur.tv_sec == tdone.tv_sec
243                         && tcur.tv_usec >= tdone.tv_usec))
244                         break;
245         }
246
247 quit:
248         seqno += TRIALS;                /* allocate our sequence numbers */
249
250         /*
251          * If no answer is received for TRIALS consecutive times,
252          * the machine is assumed to be down
253          */
254         if (measure_status == GOOD) {
255                 if (trace) {
256                         fprintf(fd,
257                                 "measured delta %4d, %d trials to %-15s %s\n",
258                                 measure_delta, trials,
259                                 inet_ntoa(addr->sin_addr), hname);
260                 }
261         } else if (printerr) {
262                 if (errno != 0)
263                         warn("measure %s", hname);
264         } else {
265                 if (errno != 0) {
266                         syslog(LOG_ERR, "measure %s: %m", hname);
267                 } else {
268                         syslog(LOG_ERR, "measure: %s did not respond", hname);
269                 }
270                 if (trace) {
271                         fprintf(fd,
272                                 "measure: %s failed after %d trials\n",
273                                 hname, trials);
274                         fflush(fd);
275                 }
276         }
277
278         return(measure_status);
279 }
280
281
282
283
284
285 /*
286  * round a number of milliseconds into a struct timeval
287  */
288 void
289 mstotvround(struct timeval *res, long x)
290 {
291
292         if (x < 0)
293                 x = -((-x + 3)/5);
294         else
295                 x = (x+3)/5;
296         x *= 5;
297
298         res->tv_sec = x/1000;
299         res->tv_usec = (x-res->tv_sec*1000)*1000;
300         if (res->tv_usec < 0) {
301                 res->tv_usec += 1000000;
302                 res->tv_sec--;
303         }
304 }
305
306 void
307 timevaladd(struct timeval *tv1, struct timeval *tv2)
308 {
309
310         tv1->tv_sec += tv2->tv_sec;
311         tv1->tv_usec += tv2->tv_usec;
312         if (tv1->tv_usec >= 1000000) {
313                 tv1->tv_sec++;
314                 tv1->tv_usec -= 1000000;
315         }
316         if (tv1->tv_usec < 0) {
317                 tv1->tv_sec--;
318                 tv1->tv_usec += 1000000;
319         }
320 }
321
322 void
323 timevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2)
324 {
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 }