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