1 /* $NetBSD: measure.c,v 1.8 2001/09/02 00:13:06 reinoud 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)measure.c 8.2 (Berkeley) 3/26/95"; 40 #else 41 __RCSID("$NetBSD: measure.c,v 1.8 2001/09/02 00:13:06 reinoud Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include "globals.h" 46 #include <netinet/in_systm.h> 47 #include <netinet/ip.h> 48 #include <netinet/ip_icmp.h> 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(u_short*, 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 char *hname, 73 struct sockaddr_in *addr, 74 int print) /* print complaints on stderr */ 75 { 76 int length; 77 int measure_status; 78 int rcvcount, trials; 79 int cc, count; 80 fd_set ready; 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], opacket[64]; 86 register struct icmp *icp = (struct icmp *) packet; 87 register struct icmp *oicp = (struct icmp *) opacket; 88 struct ip *ip = (struct ip *) packet; 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 106 /* 107 * empty the icmp input queue 108 */ 109 FD_ZERO(&ready); 110 for (;;) { 111 tout.tv_sec = tout.tv_usec = 0; 112 FD_SET(sock_raw, &ready); 113 if (select(sock_raw+1, &ready, 0,0, &tout)) { 114 length = sizeof(struct sockaddr_in); 115 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 116 0,&length); 117 if (cc < 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 FD_ZERO(&ready); 138 139 (void)gettimeofday(&tdone, 0); 140 mstotvround(&tout, maxmsec); 141 timeradd(&tdone, &tout, &tdone); /* when we give up */ 142 143 mstotvround(&twait, wmsec); 144 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 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 timeradd(&tcur, &twait, &ttrans); 171 } else { 172 ttrans = tdone; 173 } 174 175 while (rcvcount < trials) { 176 timersub(&ttrans, &tcur, &tout); 177 if (tout.tv_sec < 0) 178 tout.tv_sec = 0; 179 180 FD_SET(sock_raw, &ready); 181 count = select(sock_raw+1, &ready, (fd_set *)0, 182 (fd_set *)0, &tout); 183 (void)gettimeofday(&tcur, (struct timezone *)0); 184 if (count <= 0) 185 break; 186 187 length = sizeof(struct sockaddr_in); 188 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 189 0,&length); 190 if (cc < 0) 191 goto quit; 192 193 /* 194 * got something. See if it is ours 195 */ 196 icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 197 if (cc < sizeof(*ip) 198 || icp->icmp_type != ICMP_TSTAMPREPLY 199 || icp->icmp_id != oicp->icmp_id 200 || icp->icmp_seq < seqno 201 || icp->icmp_seq >= oicp->icmp_seq) 202 continue; 203 204 205 sendtime = ntohl(icp->icmp_otime); 206 recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 207 tcur.tv_usec / 1000); 208 209 total = recvtime-sendtime; 210 if (total < 0) /* do not hassle midnight */ 211 continue; 212 213 rcvcount++; 214 histime1 = ntohl(icp->icmp_rtime); 215 histime2 = ntohl(icp->icmp_ttime); 216 /* 217 * a host using a time format different from 218 * msec. since midnight UT (as per RFC792) should 219 * set the high order bit of the 32-bit time 220 * value it transmits. 221 */ 222 if ((histime1 & 0x80000000) != 0) { 223 measure_status = NONSTDTIME; 224 goto quit; 225 } 226 measure_status = GOOD; 227 228 idelta = recvtime-histime2; 229 odelta = histime1-sendtime; 230 231 /* do not be confused by midnight */ 232 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; 233 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; 234 235 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 236 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 237 238 /* save the quantization error so that we can get a 239 * measurement finer than our system clock. 240 */ 241 if (total < MIN_ROUND) { 242 measure_delta = (odelta - idelta)/2; 243 goto quit; 244 } 245 246 if (idelta < min_idelta) 247 min_idelta = idelta; 248 if (odelta < min_odelta) 249 min_odelta = odelta; 250 251 measure_delta = (min_odelta - min_idelta)/2; 252 } 253 254 if (tcur.tv_sec > tdone.tv_sec 255 || (tcur.tv_sec == tdone.tv_sec 256 && tcur.tv_usec >= tdone.tv_usec)) 257 break; 258 } 259 260 quit: 261 seqno += TRIALS; /* allocate our sequence numbers */ 262 263 /* 264 * If no answer is received for TRIALS consecutive times, 265 * the machine is assumed to be down 266 */ 267 if (measure_status == GOOD) { 268 if (trace) { 269 fprintf(fd, 270 "measured delta %4d, %d trials to %-15s %s\n", 271 measure_delta, trials, 272 inet_ntoa(addr->sin_addr), hname); 273 } 274 } else if (print) { 275 if (errno != 0) 276 fprintf(stderr, "measure %s: %s\n", hname, 277 strerror(errno)); 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 */ 302 void 303 mstotvround(struct timeval *res, long x) 304 { 305 if (x < 0) 306 x = -((-x + 3)/5); 307 else 308 x = (x+3)/5; 309 x *= 5; 310 311 res->tv_sec = x/1000; 312 res->tv_usec = (x-res->tv_sec*1000)*1000; 313 if (res->tv_usec < 0) { 314 res->tv_usec += 1000000; 315 res->tv_sec--; 316 } 317 } 318