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