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
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
36 static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93";
38 static const char rcsid[] =
39 "$FreeBSD: src/usr.sbin/timed/timed/measure.c,v 1.6 1999/08/28 01:20:17 peter Exp $";
43 #include <netinet/in_systm.h>
44 #include <netinet/ip.h>
45 #include <netinet/ip_icmp.h>
47 #define MSEC_DAY (SECDAY*1000)
49 #define PACKET_IN 1024
51 #define MSGS 5 /* timestamps to average */
52 #define TRIALS 10 /* max # of timestamps sent */
58 static n_short seqno = 0;
61 * Measures the differences between machines' clocks using
62 * ICMP timestamp messages.
64 int /* status val defined in globals.h */
65 measure(maxmsec, wmsec, hname, addr, print)
66 u_long maxmsec; /* wait this many msec at most */
67 u_long wmsec; /* msec to wait for an answer */
69 struct sockaddr_in *addr;
70 int print; /* print complaints on stderr */
77 long sendtime, recvtime, histime1, histime2;
78 long idelta, odelta, total;
79 long min_idelta, min_odelta;
80 struct timeval tdone, tcur, ttrans, twait, tout;
81 u_char packet[PACKET_IN], opacket[64];
82 register struct icmp *icp = (struct icmp *) packet;
83 register struct icmp *oicp = (struct icmp *) opacket;
84 struct ip *ip = (struct ip *) packet;
86 min_idelta = min_odelta = 0x7fffffff;
87 measure_status = HOSTDOWN;
88 measure_delta = HOSTDOWN;
91 /* open raw socket used to measure time differences */
93 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
95 syslog(LOG_ERR, "opening raw socket: %m");
102 * empty the icmp input queue
106 tout.tv_sec = tout.tv_usec = 0;
107 FD_SET(sock_raw, &ready);
108 if (select(sock_raw+1, &ready, 0,0, &tout)) {
109 length = sizeof(struct sockaddr_in);
110 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
120 * Choose the smallest transmission time in each of the two
121 * directions. Use these two latter quantities to compute the delta
122 * between the two clocks.
125 oicp->icmp_type = ICMP_TSTAMP;
127 oicp->icmp_id = getpid();
128 oicp->icmp_rtime = 0;
129 oicp->icmp_ttime = 0;
130 oicp->icmp_seq = seqno;
135 sginap(1); /* start at a clock tick */
138 (void)gettimeofday(&tdone, 0);
139 mstotvround(&tout, maxmsec);
140 timevaladd(&tdone, &tout); /* when we give up */
142 mstotvround(&twait, wmsec);
146 while (rcvcount < MSGS) {
147 (void)gettimeofday(&tcur, 0);
150 * keep sending until we have sent the max
152 if (trials < TRIALS) {
154 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
155 + tcur.tv_usec / 1000);
156 oicp->icmp_cksum = 0;
157 oicp->icmp_cksum = in_cksum((u_short*)oicp,
160 count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
161 (struct sockaddr*)addr,
162 sizeof(struct sockaddr));
164 if (measure_status == HOSTDOWN)
165 measure_status = UNREACHABLE;
171 timevaladd(&ttrans, &twait);
176 while (rcvcount < trials) {
177 timevalsub(&tout, &ttrans, &tcur);
181 FD_SET(sock_raw, &ready);
182 count = select(sock_raw+1, &ready, (fd_set *)0,
184 (void)gettimeofday(&tcur, (struct timezone *)0);
188 length = sizeof(struct sockaddr_in);
189 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
195 * got something. See if it is ours
197 icp = (struct icmp *)(packet + (ip->ip_hl << 2));
199 || icp->icmp_type != ICMP_TSTAMPREPLY
200 || icp->icmp_id != oicp->icmp_id
201 || icp->icmp_seq < seqno
202 || icp->icmp_seq >= oicp->icmp_seq)
206 sendtime = ntohl(icp->icmp_otime);
207 recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
208 tcur.tv_usec / 1000);
210 total = recvtime-sendtime;
211 if (total < 0) /* do not hassle midnight */
215 histime1 = ntohl(icp->icmp_rtime);
216 histime2 = ntohl(icp->icmp_ttime);
218 * a host using a time format different from
219 * msec. since midnight UT (as per RFC792) should
220 * set the high order bit of the 32-bit time
221 * value it transmits.
223 if ((histime1 & 0x80000000) != 0) {
224 measure_status = NONSTDTIME;
227 measure_status = GOOD;
229 idelta = recvtime-histime2;
230 odelta = histime1-sendtime;
232 /* do not be confused by midnight */
233 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
234 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
236 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
237 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
239 /* save the quantization error so that we can get a
240 * measurement finer than our system clock.
242 if (total < MIN_ROUND) {
243 measure_delta = (odelta - idelta)/2;
247 if (idelta < min_idelta)
249 if (odelta < min_odelta)
252 measure_delta = (min_odelta - min_idelta)/2;
255 if (tcur.tv_sec > tdone.tv_sec
256 || (tcur.tv_sec == tdone.tv_sec
257 && tcur.tv_usec >= tdone.tv_usec))
262 seqno += TRIALS; /* allocate our sequence numbers */
265 * If no answer is received for TRIALS consecutive times,
266 * the machine is assumed to be down
268 if (measure_status == GOOD) {
271 "measured delta %4d, %d trials to %-15s %s\n",
272 measure_delta, trials,
273 inet_ntoa(addr->sin_addr), hname);
277 warn("measure %s", hname);
280 syslog(LOG_ERR, "measure %s: %m", hname);
282 syslog(LOG_ERR, "measure: %s did not respond", hname);
286 "measure: %s failed after %d trials\n",
292 return(measure_status);
300 * round a number of milliseconds into a struct timeval
314 res->tv_sec = x/1000;
315 res->tv_usec = (x-res->tv_sec*1000)*1000;
316 if (res->tv_usec < 0) {
317 res->tv_usec += 1000000;
324 struct timeval *tv1, *tv2;
326 tv1->tv_sec += tv2->tv_sec;
327 tv1->tv_usec += tv2->tv_usec;
328 if (tv1->tv_usec >= 1000000) {
330 tv1->tv_usec -= 1000000;
332 if (tv1->tv_usec < 0) {
334 tv1->tv_usec += 1000000;
339 timevalsub(res, tv1, tv2)
340 struct timeval *res, *tv1, *tv2;
342 res->tv_sec = tv1->tv_sec - tv2->tv_sec;
343 res->tv_usec = tv1->tv_usec - tv2->tv_usec;
344 if (res->tv_usec >= 1000000) {
346 res->tv_usec -= 1000000;
348 if (res->tv_usec < 0) {
350 res->tv_usec += 1000000;