]> git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/measure.c
6b809b2264ff22bb4cae6f4dec9e99fb41bed773
[apple/network_cmds.git] / timed.tproj / timed.tproj / measure.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*-
25 * Copyright (c) 1985, 1993
26 * The Regents of the University of California. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57 #ifndef lint
58 static char sccsid[] = "@(#)measure.c 8.2 (Berkeley) 3/26/95";
59 #endif /* not lint */
60
61 #ifdef sgi
62 #ident "$Revision: 1.1.1.1 $"
63 #endif
64
65 #include "globals.h"
66 #include <netinet/in_systm.h>
67 #include <netinet/ip.h>
68 #include <netinet/ip_icmp.h>
69
70 #define MSEC_DAY (SECDAY*1000)
71
72 #define PACKET_IN 1024
73
74 #define MSGS 5 /* timestamps to average */
75 #define TRIALS 10 /* max # of timestamps sent */
76
77 extern int sock_raw;
78
79 int measure_delta;
80
81 static n_short seqno = 0;
82
83 /*
84 * Measures the differences between machines' clocks using
85 * ICMP timestamp messages.
86 */
87 int /* status val defined in globals.h */
88 measure(maxmsec, wmsec, hname, addr, print)
89 u_long maxmsec; /* wait this many msec at most */
90 u_long wmsec; /* msec to wait for an answer */
91 char *hname;
92 struct sockaddr_in *addr;
93 int print; /* print complaints on stderr */
94 {
95 int length;
96 int measure_status;
97 int rcvcount, trials;
98 int cc, count;
99 fd_set ready;
100 long sendtime, recvtime, histime1, histime2;
101 long idelta, odelta, total;
102 long min_idelta, min_odelta;
103 struct timeval tdone, tcur, ttrans, twait, tout;
104 u_char packet[PACKET_IN], opacket[64];
105 register struct icmp *icp = (struct icmp *) packet;
106 register struct icmp *oicp = (struct icmp *) opacket;
107 struct ip *ip = (struct ip *) packet;
108
109 min_idelta = min_odelta = 0x7fffffff;
110 measure_status = HOSTDOWN;
111 measure_delta = HOSTDOWN;
112 errno = 0;
113
114 /* open raw socket used to measure time differences */
115 if (sock_raw < 0) {
116 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
117 if (sock_raw < 0) {
118 syslog(LOG_ERR, "opening raw socket: %m");
119 goto quit;
120 }
121 }
122
123
124 /*
125 * empty the icmp input queue
126 */
127 FD_ZERO(&ready);
128 for (;;) {
129 tout.tv_sec = tout.tv_usec = 0;
130 FD_SET(sock_raw, &ready);
131 if (select(sock_raw+1, &ready, 0,0, &tout)) {
132 length = sizeof(struct sockaddr_in);
133 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
134 0,&length);
135 if (cc < 0)
136 goto quit;
137 continue;
138 }
139 break;
140 }
141
142 /*
143 * Choose the smallest transmission time in each of the two
144 * directions. Use these two latter quantities to compute the delta
145 * between the two clocks.
146 */
147
148 oicp->icmp_type = ICMP_TSTAMP;
149 oicp->icmp_code = 0;
150 oicp->icmp_id = getpid();
151 oicp->icmp_rtime = 0;
152 oicp->icmp_ttime = 0;
153 oicp->icmp_seq = seqno;
154
155 FD_ZERO(&ready);
156
157 #ifdef sgi
158 sginap(1); /* start at a clock tick */
159 #endif /* sgi */
160
161 (void)gettimeofday(&tdone, 0);
162 mstotvround(&tout, maxmsec);
163 timevaladd(&tdone, &tout); /* when we give up */
164
165 mstotvround(&twait, wmsec);
166
167 rcvcount = 0;
168 trials = 0;
169 while (rcvcount < MSGS) {
170 (void)gettimeofday(&tcur, 0);
171
172 /*
173 * keep sending until we have sent the max
174 */
175 if (trials < TRIALS) {
176 trials++;
177 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
178 + tcur.tv_usec / 1000);
179 oicp->icmp_cksum = 0;
180 oicp->icmp_cksum = in_cksum((u_short*)oicp,
181 sizeof(*oicp));
182
183 count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
184 (struct sockaddr*)addr,
185 sizeof(struct sockaddr));
186 if (count < 0) {
187 if (measure_status == HOSTDOWN)
188 measure_status = UNREACHABLE;
189 goto quit;
190 }
191 ++oicp->icmp_seq;
192
193 ttrans = tcur;
194 timevaladd(&ttrans, &twait);
195 } else {
196 ttrans = tdone;
197 }
198
199 while (rcvcount < trials) {
200 timevalsub(&tout, &ttrans, &tcur);
201 if (tout.tv_sec < 0)
202 tout.tv_sec = 0;
203
204 FD_SET(sock_raw, &ready);
205 count = select(sock_raw+1, &ready, (fd_set *)0,
206 (fd_set *)0, &tout);
207 (void)gettimeofday(&tcur, (struct timezone *)0);
208 if (count <= 0)
209 break;
210
211 length = sizeof(struct sockaddr_in);
212 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
213 0,&length);
214 if (cc < 0)
215 goto quit;
216
217 /*
218 * got something. See if it is ours
219 */
220 icp = (struct icmp *)(packet + (ip->ip_hl << 2));
221 if (cc < sizeof(*ip)
222 || icp->icmp_type != ICMP_TSTAMPREPLY
223 || icp->icmp_id != oicp->icmp_id
224 || icp->icmp_seq < seqno
225 || icp->icmp_seq >= oicp->icmp_seq)
226 continue;
227
228
229 sendtime = ntohl(icp->icmp_otime);
230 recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
231 tcur.tv_usec / 1000);
232
233 total = recvtime-sendtime;
234 if (total < 0) /* do not hassle midnight */
235 continue;
236
237 rcvcount++;
238 histime1 = ntohl(icp->icmp_rtime);
239 histime2 = ntohl(icp->icmp_ttime);
240 /*
241 * a host using a time format different from
242 * msec. since midnight UT (as per RFC792) should
243 * set the high order bit of the 32-bit time
244 * value it transmits.
245 */
246 if ((histime1 & 0x80000000) != 0) {
247 measure_status = NONSTDTIME;
248 goto quit;
249 }
250 measure_status = GOOD;
251
252 idelta = recvtime-histime2;
253 odelta = histime1-sendtime;
254
255 /* do not be confused by midnight */
256 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
257 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
258
259 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
260 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
261
262 /* save the quantization error so that we can get a
263 * measurement finer than our system clock.
264 */
265 if (total < MIN_ROUND) {
266 measure_delta = (odelta - idelta)/2;
267 goto quit;
268 }
269
270 if (idelta < min_idelta)
271 min_idelta = idelta;
272 if (odelta < min_odelta)
273 min_odelta = odelta;
274
275 measure_delta = (min_odelta - min_idelta)/2;
276 }
277
278 if (tcur.tv_sec > tdone.tv_sec
279 || (tcur.tv_sec == tdone.tv_sec
280 && tcur.tv_usec >= tdone.tv_usec))
281 break;
282 }
283
284 quit:
285 seqno += TRIALS; /* allocate our sequence numbers */
286
287 /*
288 * If no answer is received for TRIALS consecutive times,
289 * the machine is assumed to be down
290 */
291 if (measure_status == GOOD) {
292 if (trace) {
293 fprintf(fd,
294 "measured delta %4d, %d trials to %-15s %s\n",
295 measure_delta, trials,
296 inet_ntoa(addr->sin_addr), hname);
297 }
298 } else if (print) {
299 if (errno != 0)
300 fprintf(stderr, "measure %s: %s\n", hname,
301 strerror(errno));
302 } else {
303 if (errno != 0) {
304 syslog(LOG_ERR, "measure %s: %m", hname);
305 } else {
306 syslog(LOG_ERR, "measure: %s did not respond", hname);
307 }
308 if (trace) {
309 fprintf(fd,
310 "measure: %s failed after %d trials\n",
311 hname, trials);
312 (void)fflush(fd);
313 }
314 }
315
316 return(measure_status);
317 }
318
319
320
321
322
323 /*
324 * round a number of milliseconds into a struct timeval
325 */
326 void
327 mstotvround(res, x)
328 struct timeval *res;
329 long x;
330 {
331 #ifndef sgi
332 if (x < 0)
333 x = -((-x + 3)/5);
334 else
335 x = (x+3)/5;
336 x *= 5;
337 #endif /* sgi */
338 res->tv_sec = x/1000;
339 res->tv_usec = (x-res->tv_sec*1000)*1000;
340 if (res->tv_usec < 0) {
341 res->tv_usec += 1000000;
342 res->tv_sec--;
343 }
344 }
345
346 void
347 timevaladd(tv1, tv2)
348 struct timeval *tv1, *tv2;
349 {
350 tv1->tv_sec += tv2->tv_sec;
351 tv1->tv_usec += tv2->tv_usec;
352 if (tv1->tv_usec >= 1000000) {
353 tv1->tv_sec++;
354 tv1->tv_usec -= 1000000;
355 }
356 if (tv1->tv_usec < 0) {
357 tv1->tv_sec--;
358 tv1->tv_usec += 1000000;
359 }
360 }
361
362 void
363 timevalsub(res, tv1, tv2)
364 struct timeval *res, *tv1, *tv2;
365 {
366 res->tv_sec = tv1->tv_sec - tv2->tv_sec;
367 res->tv_usec = tv1->tv_usec - tv2->tv_usec;
368 if (res->tv_usec >= 1000000) {
369 res->tv_sec++;
370 res->tv_usec -= 1000000;
371 }
372 if (res->tv_usec < 0) {
373 res->tv_sec--;
374 res->tv_usec += 1000000;
375 }
376 }