measure.c revision 331722
1195033Salc/*- 2195033Salc * Copyright (c) 1985, 1993 3195033Salc * The Regents of the University of California. All rights reserved. 4195033Salc * 5195033Salc * Redistribution and use in source and binary forms, with or without 6195033Salc * modification, are permitted provided that the following conditions 7195033Salc * are met: 8195033Salc * 1. Redistributions of source code must retain the above copyright 9195033Salc * notice, this list of conditions and the following disclaimer. 10195033Salc * 2. Redistributions in binary form must reproduce the above copyright 11195033Salc * notice, this list of conditions and the following disclaimer in the 12195033Salc * documentation and/or other materials provided with the distribution. 13195033Salc * 4. Neither the name of the University nor the names of its contributors 14195033Salc * may be used to endorse or promote products derived from this software 15195033Salc * without specific prior written permission. 16195033Salc * 17195033Salc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18195033Salc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19195033Salc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20195033Salc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21195033Salc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22195033Salc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23195033Salc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24195033Salc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25195033Salc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26195033Salc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27195033Salc * SUCH DAMAGE. 28195033Salc */ 29195033Salc 30195033Salc#ifndef lint 31195033Salc#if 0 32295036Smmelstatic char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93"; 33295036Smmel#endif 34295036Smmelstatic const char rcsid[] = 35280712Sian "$FreeBSD: stable/11/usr.sbin/timed/timed/measure.c 331722 2018-03-29 02:50:57Z eadler $"; 36280712Sian#endif /* not lint */ 37291492Smmel 38291492Smmel#include "globals.h" 39291492Smmel#include <netinet/in_systm.h> 40291492Smmel#include <netinet/ip.h> 41291492Smmel#include <netinet/ip_icmp.h> 42280712Sian 43291492Smmel#define MSEC_DAY (SECDAY*1000) 44291492Smmel 45295036Smmel#define PACKET_IN 1024 46295036Smmel 47291492Smmel#define MSGS 5 /* timestamps to average */ 48291492Smmel#define TRIALS 10 /* max # of timestamps sent */ 49295036Smmel 50280712Sianextern int sock_raw; 51244414Scognet 52195649Salcint measure_delta; 53244414Scognet 54280712Sianstatic n_short seqno = 0; 55195033Salc 56195060Salc/* 57 * Measures the differences between machines' clocks using 58 * ICMP timestamp messages. 59 * maxmsec wait this many msec at most 60 * wmsec msec to wait for an answer 61 * print print complaints on stderr 62 */ 63int /* status val defined in globals.h */ 64measure(u_long maxmsec, u_long wmsec, char *hname, struct sockaddr_in *addr, int print) 65{ 66 int length; 67 int measure_status; 68 int rcvcount, trials; 69 int cc, count; 70 fd_set ready; 71 long sendtime, recvtime, histime1, histime2; 72 long idelta, odelta, total; 73 long min_idelta, min_odelta; 74 struct timeval tdone, tcur, ttrans, twait, tout; 75 u_char packet[PACKET_IN], opacket[64]; 76 register struct icmp *icp = (struct icmp *) packet; 77 register struct icmp *oicp = (struct icmp *) opacket; 78 struct ip *ip = (struct ip *) packet; 79 80 min_idelta = min_odelta = 0x7fffffff; 81 measure_status = HOSTDOWN; 82 measure_delta = HOSTDOWN; 83 trials = 0; 84 errno = 0; 85 86 /* open raw socket used to measure time differences */ 87 if (sock_raw < 0) { 88 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 89 if (sock_raw < 0) { 90 syslog(LOG_ERR, "opening raw socket: %m"); 91 goto quit; 92 } 93 } 94 95 96 /* 97 * empty the icmp input queue 98 */ 99 FD_ZERO(&ready); 100 for (;;) { 101 tout.tv_sec = tout.tv_usec = 0; 102 FD_SET(sock_raw, &ready); 103 if (select(sock_raw+1, &ready, 0,0, &tout)) { 104 length = sizeof(struct sockaddr_in); 105 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 106 0,&length); 107 if (cc < 0) 108 goto quit; 109 continue; 110 } 111 break; 112 } 113 114 /* 115 * Choose the smallest transmission time in each of the two 116 * directions. Use these two latter quantities to compute the delta 117 * between the two clocks. 118 */ 119 120 oicp->icmp_type = ICMP_TSTAMP; 121 oicp->icmp_code = 0; 122 oicp->icmp_id = getpid(); 123 oicp->icmp_rtime = 0; 124 oicp->icmp_ttime = 0; 125 oicp->icmp_seq = seqno; 126 127 FD_ZERO(&ready); 128 129 (void)gettimeofday(&tdone, NULL); 130 mstotvround(&tout, maxmsec); 131 timevaladd(&tdone, &tout); /* when we give up */ 132 133 mstotvround(&twait, wmsec); 134 135 rcvcount = 0; 136 while (rcvcount < MSGS) { 137 (void)gettimeofday(&tcur, NULL); 138 139 /* 140 * keep sending until we have sent the max 141 */ 142 if (trials < TRIALS) { 143 trials++; 144 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000 145 + tcur.tv_usec / 1000); 146 oicp->icmp_cksum = 0; 147 oicp->icmp_cksum = in_cksum((u_short*)oicp, 148 sizeof(*oicp)); 149 150 count = sendto(sock_raw, opacket, sizeof(*oicp), 0, 151 (struct sockaddr*)addr, 152 sizeof(struct sockaddr)); 153 if (count < 0) { 154 if (measure_status == HOSTDOWN) 155 measure_status = UNREACHABLE; 156 goto quit; 157 } 158 ++oicp->icmp_seq; 159 160 ttrans = tcur; 161 timevaladd(&ttrans, &twait); 162 } else { 163 ttrans = tdone; 164 } 165 166 while (rcvcount < trials) { 167 timevalsub(&tout, &ttrans, &tcur); 168 if (tout.tv_sec < 0) 169 tout.tv_sec = 0; 170 171 FD_SET(sock_raw, &ready); 172 count = select(sock_raw+1, &ready, (fd_set *)0, 173 (fd_set *)0, &tout); 174 (void)gettimeofday(&tcur, NULL); 175 if (count <= 0) 176 break; 177 178 length = sizeof(struct sockaddr_in); 179 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 180 0,&length); 181 if (cc < 0) 182 goto quit; 183 184 /* 185 * got something. See if it is ours 186 */ 187 icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 188 if (cc < sizeof(*ip) 189 || icp->icmp_type != ICMP_TSTAMPREPLY 190 || icp->icmp_id != oicp->icmp_id 191 || icp->icmp_seq < seqno 192 || icp->icmp_seq >= oicp->icmp_seq) 193 continue; 194 195 196 sendtime = ntohl(icp->icmp_otime); 197 recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 198 tcur.tv_usec / 1000); 199 200 total = recvtime-sendtime; 201 if (total < 0) /* do not hassle midnight */ 202 continue; 203 204 rcvcount++; 205 histime1 = ntohl(icp->icmp_rtime); 206 histime2 = ntohl(icp->icmp_ttime); 207 /* 208 * a host using a time format different from 209 * msec. since midnight UT (as per RFC792) should 210 * set the high order bit of the 32-bit time 211 * value it transmits. 212 */ 213 if ((histime1 & 0x80000000) != 0) { 214 measure_status = NONSTDTIME; 215 goto quit; 216 } 217 measure_status = GOOD; 218 219 idelta = recvtime-histime2; 220 odelta = histime1-sendtime; 221 222 /* do not be confused by midnight */ 223 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; 224 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; 225 226 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 227 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 228 229 /* save the quantization error so that we can get a 230 * measurement finer than our system clock. 231 */ 232 if (total < MIN_ROUND) { 233 measure_delta = (odelta - idelta)/2; 234 goto quit; 235 } 236 237 if (idelta < min_idelta) 238 min_idelta = idelta; 239 if (odelta < min_odelta) 240 min_odelta = odelta; 241 242 measure_delta = (min_odelta - min_idelta)/2; 243 } 244 245 if (tcur.tv_sec > tdone.tv_sec 246 || (tcur.tv_sec == tdone.tv_sec 247 && tcur.tv_usec >= tdone.tv_usec)) 248 break; 249 } 250 251quit: 252 seqno += TRIALS; /* allocate our sequence numbers */ 253 254 /* 255 * If no answer is received for TRIALS consecutive times, 256 * the machine is assumed to be down 257 */ 258 if (measure_status == GOOD) { 259 if (trace) { 260 fprintf(fd, 261 "measured delta %4d, %d trials to %-15s %s\n", 262 measure_delta, trials, 263 inet_ntoa(addr->sin_addr), hname); 264 } 265 } else if (print) { 266 if (errno != 0) 267 warn("measure %s", hname); 268 } else { 269 if (errno != 0) { 270 syslog(LOG_ERR, "measure %s: %m", hname); 271 } else { 272 syslog(LOG_ERR, "measure: %s did not respond", hname); 273 } 274 if (trace) { 275 fprintf(fd, 276 "measure: %s failed after %d trials\n", 277 hname, trials); 278 (void)fflush(fd); 279 } 280 } 281 282 return(measure_status); 283} 284 285 286 287 288 289/* 290 * round a number of milliseconds into a struct timeval 291 */ 292void 293mstotvround(struct timeval *res, long x) 294{ 295 if (x < 0) 296 x = -((-x + 3)/5); 297 else 298 x = (x+3)/5; 299 x *= 5; 300 res->tv_sec = x/1000; 301 res->tv_usec = (x-res->tv_sec*1000)*1000; 302 if (res->tv_usec < 0) { 303 res->tv_usec += 1000000; 304 res->tv_sec--; 305 } 306} 307 308void 309timevaladd(struct timeval *tv1, struct timeval *tv2) 310{ 311 tv1->tv_sec += tv2->tv_sec; 312 tv1->tv_usec += tv2->tv_usec; 313 if (tv1->tv_usec >= 1000000) { 314 tv1->tv_sec++; 315 tv1->tv_usec -= 1000000; 316 } 317 if (tv1->tv_usec < 0) { 318 tv1->tv_sec--; 319 tv1->tv_usec += 1000000; 320 } 321} 322 323void 324timevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2) 325{ 326 res->tv_sec = tv1->tv_sec - tv2->tv_sec; 327 res->tv_usec = tv1->tv_usec - tv2->tv_usec; 328 if (res->tv_usec >= 1000000) { 329 res->tv_sec++; 330 res->tv_usec -= 1000000; 331 } 332 if (res->tv_usec < 0) { 333 res->tv_sec--; 334 res->tv_usec += 1000000; 335 } 336} 337