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