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 */ 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 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 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