measure.c revision 30761
1/*- 2 * Copyright (c) 1985, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 20 * 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 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$Id: measure.c,v 1.4 1997/10/22 06:19:48 charnier Exp $"; 40#endif /* not lint */ 41 42#include "globals.h" 43#include <netinet/in_systm.h> 44#include <netinet/ip.h> 45#include <netinet/ip_icmp.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 58static n_short seqno = 0; 59 60/* 61 * Measures the differences between machines' clocks using 62 * ICMP timestamp messages. 63 */ 64int /* status val defined in globals.h */ 65measure(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 */ 68 char *hname; 69 struct sockaddr_in *addr; 70 int print; /* print complaints on stderr */ 71{ 72 int length; 73 int measure_status; 74 int rcvcount, trials; 75 int cc, count; 76 fd_set ready; 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; 85 86 min_idelta = min_odelta = 0x7fffffff; 87 measure_status = HOSTDOWN; 88 measure_delta = HOSTDOWN; 89 errno = 0; 90 91 /* open raw socket used to measure time differences */ 92 if (sock_raw < 0) { 93 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 94 if (sock_raw < 0) { 95 syslog(LOG_ERR, "opening raw socket: %m"); 96 goto quit; 97 } 98 } 99 100 101 /* 102 * empty the icmp input queue 103 */ 104 FD_ZERO(&ready); 105 for (;;) { 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, 111 0,&length); 112 if (cc < 0) 113 goto quit; 114 continue; 115 } 116 break; 117 } 118 119 /* 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. 123 */ 124 125 oicp->icmp_type = ICMP_TSTAMP; 126 oicp->icmp_code = 0; 127 oicp->icmp_id = getpid(); 128 oicp->icmp_rtime = 0; 129 oicp->icmp_ttime = 0; 130 oicp->icmp_seq = seqno; 131 132 FD_ZERO(&ready); 133 134#ifdef sgi 135 sginap(1); /* start at a clock tick */ 136#endif /* sgi */ 137 138 (void)gettimeofday(&tdone, 0); 139 mstotvround(&tout, maxmsec); 140 timevaladd(&tdone, &tout); /* when we give up */ 141 142 mstotvround(&twait, wmsec); 143 144 rcvcount = 0; 145 trials = 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 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, 158 sizeof(*oicp)); 159 160 count = sendto(sock_raw, opacket, sizeof(*oicp), 0, 161 (struct sockaddr*)addr, 162 sizeof(struct sockaddr)); 163 if (count < 0) { 164 if (measure_status == HOSTDOWN) 165 measure_status = UNREACHABLE; 166 goto quit; 167 } 168 ++oicp->icmp_seq; 169 170 ttrans = tcur; 171 timevaladd(&ttrans, &twait); 172 } else { 173 ttrans = tdone; 174 } 175 176 while (rcvcount < trials) { 177 timevalsub(&tout, &ttrans, &tcur); 178 if (tout.tv_sec < 0) 179 tout.tv_sec = 0; 180 181 FD_SET(sock_raw, &ready); 182 count = select(sock_raw+1, &ready, (fd_set *)0, 183 (fd_set *)0, &tout); 184 (void)gettimeofday(&tcur, (struct timezone *)0); 185 if (count <= 0) 186 break; 187 188 length = sizeof(struct sockaddr_in); 189 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 190 0,&length); 191 if (cc < 0) 192 goto quit; 193 194 /* 195 * got something. See if it is ours 196 */ 197 icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 198 if (cc < sizeof(*ip) 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) 203 continue; 204 205 206 sendtime = ntohl(icp->icmp_otime); 207 recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 208 tcur.tv_usec / 1000); 209 210 total = recvtime-sendtime; 211 if (total < 0) /* do not hassle midnight */ 212 continue; 213 214 rcvcount++; 215 histime1 = ntohl(icp->icmp_rtime); 216 histime2 = ntohl(icp->icmp_ttime); 217 /* 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. 222 */ 223 if ((histime1 & 0x80000000) != 0) { 224 measure_status = NONSTDTIME; 225 goto quit; 226 } 227 measure_status = GOOD; 228 229 idelta = recvtime-histime2; 230 odelta = histime1-sendtime; 231 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; 235 236 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 237 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 238 239 /* save the quantization error so that we can get a 240 * measurement finer than our system clock. 241 */ 242 if (total < MIN_ROUND) { 243 measure_delta = (odelta - idelta)/2; 244 goto quit; 245 } 246 247 if (idelta < min_idelta) 248 min_idelta = idelta; 249 if (odelta < min_odelta) 250 min_odelta = odelta; 251 252 measure_delta = (min_odelta - min_idelta)/2; 253 } 254 255 if (tcur.tv_sec > tdone.tv_sec 256 || (tcur.tv_sec == tdone.tv_sec 257 && tcur.tv_usec >= tdone.tv_usec)) 258 break; 259 } 260 261quit: 262 seqno += TRIALS; /* allocate our sequence numbers */ 263 264 /* 265 * If no answer is received for TRIALS consecutive times, 266 * the machine is assumed to be down 267 */ 268 if (measure_status == GOOD) { 269 if (trace) { 270 fprintf(fd, 271 "measured delta %4d, %d trials to %-15s %s\n", 272 measure_delta, trials, 273 inet_ntoa(addr->sin_addr), hname); 274 } 275 } else if (print) { 276 if (errno != 0) 277 warn("measure %s", hname); 278 } else { 279 if (errno != 0) { 280 syslog(LOG_ERR, "measure %s: %m", hname); 281 } else { 282 syslog(LOG_ERR, "measure: %s did not respond", hname); 283 } 284 if (trace) { 285 fprintf(fd, 286 "measure: %s failed after %d trials\n", 287 hname, trials); 288 (void)fflush(fd); 289 } 290 } 291 292 return(measure_status); 293} 294 295 296 297 298 299/* 300 * round a number of milliseconds into a struct timeval 301 */ 302void 303mstotvround(res, x) 304 struct timeval *res; 305 long x; 306{ 307#ifndef sgi 308 if (x < 0) 309 x = -((-x + 3)/5); 310 else 311 x = (x+3)/5; 312 x *= 5; 313#endif /* sgi */ 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; 318 res->tv_sec--; 319 } 320} 321 322void 323timevaladd(tv1, tv2) 324 struct timeval *tv1, *tv2; 325{ 326 tv1->tv_sec += tv2->tv_sec; 327 tv1->tv_usec += tv2->tv_usec; 328 if (tv1->tv_usec >= 1000000) { 329 tv1->tv_sec++; 330 tv1->tv_usec -= 1000000; 331 } 332 if (tv1->tv_usec < 0) { 333 tv1->tv_sec--; 334 tv1->tv_usec += 1000000; 335 } 336} 337 338void 339timevalsub(res, tv1, tv2) 340 struct timeval *res, *tv1, *tv2; 341{ 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) { 345 res->tv_sec++; 346 res->tv_usec -= 1000000; 347 } 348 if (res->tv_usec < 0) { 349 res->tv_sec--; 350 res->tv_usec += 1000000; 351 } 352} 353