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