measure.c revision 209344
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 "$FreeBSD: head/usr.sbin/timed/timed/measure.c 209344 2010-06-19 17:04:01Z gavin $"; 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 trials = 0; 90 errno = 0; 91 92 /* open raw socket used to measure time differences */ 93 if (sock_raw < 0) { 94 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 95 if (sock_raw < 0) { 96 syslog(LOG_ERR, "opening raw socket: %m"); 97 goto quit; 98 } 99 } 100 101 102 /* 103 * empty the icmp input queue 104 */ 105 FD_ZERO(&ready); 106 for (;;) { 107 tout.tv_sec = tout.tv_usec = 0; 108 FD_SET(sock_raw, &ready); 109 if (select(sock_raw+1, &ready, 0,0, &tout)) { 110 length = sizeof(struct sockaddr_in); 111 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 112 0,&length); 113 if (cc < 0) 114 goto quit; 115 continue; 116 } 117 break; 118 } 119 120 /* 121 * Choose the smallest transmission time in each of the two 122 * directions. Use these two latter quantities to compute the delta 123 * between the two clocks. 124 */ 125 126 oicp->icmp_type = ICMP_TSTAMP; 127 oicp->icmp_code = 0; 128 oicp->icmp_id = getpid(); 129 oicp->icmp_rtime = 0; 130 oicp->icmp_ttime = 0; 131 oicp->icmp_seq = seqno; 132 133 FD_ZERO(&ready); 134 135 (void)gettimeofday(&tdone, 0); 136 mstotvround(&tout, maxmsec); 137 timevaladd(&tdone, &tout); /* when we give up */ 138 139 mstotvround(&twait, wmsec); 140 141 rcvcount = 0; 142 while (rcvcount < MSGS) { 143 (void)gettimeofday(&tcur, 0); 144 145 /* 146 * keep sending until we have sent the max 147 */ 148 if (trials < TRIALS) { 149 trials++; 150 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000 151 + tcur.tv_usec / 1000); 152 oicp->icmp_cksum = 0; 153 oicp->icmp_cksum = in_cksum((u_short*)oicp, 154 sizeof(*oicp)); 155 156 count = sendto(sock_raw, opacket, sizeof(*oicp), 0, 157 (struct sockaddr*)addr, 158 sizeof(struct sockaddr)); 159 if (count < 0) { 160 if (measure_status == HOSTDOWN) 161 measure_status = UNREACHABLE; 162 goto quit; 163 } 164 ++oicp->icmp_seq; 165 166 ttrans = tcur; 167 timevaladd(&ttrans, &twait); 168 } else { 169 ttrans = tdone; 170 } 171 172 while (rcvcount < trials) { 173 timevalsub(&tout, &ttrans, &tcur); 174 if (tout.tv_sec < 0) 175 tout.tv_sec = 0; 176 177 FD_SET(sock_raw, &ready); 178 count = select(sock_raw+1, &ready, (fd_set *)0, 179 (fd_set *)0, &tout); 180 (void)gettimeofday(&tcur, (struct timezone *)0); 181 if (count <= 0) 182 break; 183 184 length = sizeof(struct sockaddr_in); 185 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 186 0,&length); 187 if (cc < 0) 188 goto quit; 189 190 /* 191 * got something. See if it is ours 192 */ 193 icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 194 if (cc < sizeof(*ip) 195 || icp->icmp_type != ICMP_TSTAMPREPLY 196 || icp->icmp_id != oicp->icmp_id 197 || icp->icmp_seq < seqno 198 || icp->icmp_seq >= oicp->icmp_seq) 199 continue; 200 201 202 sendtime = ntohl(icp->icmp_otime); 203 recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 204 tcur.tv_usec / 1000); 205 206 total = recvtime-sendtime; 207 if (total < 0) /* do not hassle midnight */ 208 continue; 209 210 rcvcount++; 211 histime1 = ntohl(icp->icmp_rtime); 212 histime2 = ntohl(icp->icmp_ttime); 213 /* 214 * a host using a time format different from 215 * msec. since midnight UT (as per RFC792) should 216 * set the high order bit of the 32-bit time 217 * value it transmits. 218 */ 219 if ((histime1 & 0x80000000) != 0) { 220 measure_status = NONSTDTIME; 221 goto quit; 222 } 223 measure_status = GOOD; 224 225 idelta = recvtime-histime2; 226 odelta = histime1-sendtime; 227 228 /* do not be confused by midnight */ 229 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; 230 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; 231 232 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 233 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 234 235 /* save the quantization error so that we can get a 236 * measurement finer than our system clock. 237 */ 238 if (total < MIN_ROUND) { 239 measure_delta = (odelta - idelta)/2; 240 goto quit; 241 } 242 243 if (idelta < min_idelta) 244 min_idelta = idelta; 245 if (odelta < min_odelta) 246 min_odelta = odelta; 247 248 measure_delta = (min_odelta - min_idelta)/2; 249 } 250 251 if (tcur.tv_sec > tdone.tv_sec 252 || (tcur.tv_sec == tdone.tv_sec 253 && tcur.tv_usec >= tdone.tv_usec)) 254 break; 255 } 256 257quit: 258 seqno += TRIALS; /* allocate our sequence numbers */ 259 260 /* 261 * If no answer is received for TRIALS consecutive times, 262 * the machine is assumed to be down 263 */ 264 if (measure_status == GOOD) { 265 if (trace) { 266 fprintf(fd, 267 "measured delta %4d, %d trials to %-15s %s\n", 268 measure_delta, trials, 269 inet_ntoa(addr->sin_addr), hname); 270 } 271 } else if (print) { 272 if (errno != 0) 273 warn("measure %s", hname); 274 } else { 275 if (errno != 0) { 276 syslog(LOG_ERR, "measure %s: %m", hname); 277 } else { 278 syslog(LOG_ERR, "measure: %s did not respond", hname); 279 } 280 if (trace) { 281 fprintf(fd, 282 "measure: %s failed after %d trials\n", 283 hname, trials); 284 (void)fflush(fd); 285 } 286 } 287 288 return(measure_status); 289} 290 291 292 293 294 295/* 296 * round a number of milliseconds into a struct timeval 297 */ 298void 299mstotvround(res, x) 300 struct timeval *res; 301 long x; 302{ 303 if (x < 0) 304 x = -((-x + 3)/5); 305 else 306 x = (x+3)/5; 307 x *= 5; 308 res->tv_sec = x/1000; 309 res->tv_usec = (x-res->tv_sec*1000)*1000; 310 if (res->tv_usec < 0) { 311 res->tv_usec += 1000000; 312 res->tv_sec--; 313 } 314} 315 316void 317timevaladd(tv1, tv2) 318 struct timeval *tv1, *tv2; 319{ 320 tv1->tv_sec += tv2->tv_sec; 321 tv1->tv_usec += tv2->tv_usec; 322 if (tv1->tv_usec >= 1000000) { 323 tv1->tv_sec++; 324 tv1->tv_usec -= 1000000; 325 } 326 if (tv1->tv_usec < 0) { 327 tv1->tv_sec--; 328 tv1->tv_usec += 1000000; 329 } 330} 331 332void 333timevalsub(res, tv1, tv2) 334 struct timeval *res, *tv1, *tv2; 335{ 336 res->tv_sec = tv1->tv_sec - tv2->tv_sec; 337 res->tv_usec = tv1->tv_usec - tv2->tv_usec; 338 if (res->tv_usec >= 1000000) { 339 res->tv_sec++; 340 res->tv_usec -= 1000000; 341 } 342 if (res->tv_usec < 0) { 343 res->tv_sec--; 344 res->tv_usec += 1000000; 345 } 346} 347