/* $NetBSD: measure.c,v 1.17 2012/11/04 22:36:58 christos Exp $ */ /*- * Copyright (c) 1985, 1993 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint #if 0 static char sccsid[] = "@(#)measure.c 8.2 (Berkeley) 3/26/95"; #else __RCSID("$NetBSD: measure.c,v 1.17 2012/11/04 22:36:58 christos Exp $"); #endif #endif /* not lint */ #include "globals.h" #include #include #include #include #ifdef SUPPORT_UTMPX #include #endif #define MSEC_DAY (SECDAY*1000) #define PACKET_IN 1024 #define MSGS 5 /* timestamps to average */ #define TRIALS 10 /* max # of timestamps sent */ extern int sock_raw; int measure_delta; extern int in_cksum(const void *, int); static n_short seqno = 0; /* * Measures the differences between machines' clocks using * ICMP timestamp messages. */ int /* status val defined in globals.h */ measure(u_long maxmsec, /* wait this many msec at most */ u_long wmsec, /* msec to wait for an answer */ const char *hname, const struct sockaddr_in *addr, int printerr) /* print complaints on stderr */ { socklen_t length; int measure_status; int rcvcount, trials; int count; struct pollfd set[1]; long sendtime, recvtime, histime1, histime2; long idelta, odelta, total; long min_idelta, min_odelta; struct timeval tdone, tcur, ttrans, twait, tout; u_char packet[PACKET_IN]; struct icmp icp; struct icmp oicp; struct ip ip; min_idelta = min_odelta = 0x7fffffff; measure_status = HOSTDOWN; measure_delta = HOSTDOWN; errno = 0; trials = 0; /* open raw socket used to measure time differences */ if (sock_raw < 0) { sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sock_raw < 0) { syslog(LOG_ERR, "opening raw socket: %m"); goto quit; } } set[0].fd = sock_raw; set[0].events = POLLIN; /* * empty the icmp input queue */ for (;;) { if (poll(set, 1, 0)) { ssize_t ret; length = sizeof(struct sockaddr_in); ret = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 0,&length); if (ret < 0) goto quit; continue; } break; } /* * Choose the smallest transmission time in each of the two * directions. Use these two latter quantities to compute the delta * between the two clocks. */ oicp.icmp_type = ICMP_TSTAMP; oicp.icmp_code = 0; oicp.icmp_id = getpid(); oicp.icmp_rtime = 0; oicp.icmp_ttime = 0; oicp.icmp_seq = seqno; (void)gettimeofday(&tdone, 0); mstotvround(&tout, maxmsec); timeradd(&tdone, &tout, &tdone); /* when we give up */ mstotvround(&twait, wmsec); tout.tv_sec = 0; tout.tv_usec = 0; rcvcount = 0; while (rcvcount < MSGS) { (void)gettimeofday(&tcur, 0); /* * keep sending until we have sent the max */ if (trials < TRIALS) { uint32_t otime; trials++; otime = (tcur.tv_sec % SECDAY) * 1000 + tcur.tv_usec / 1000; oicp.icmp_otime = htonl(otime); oicp.icmp_cksum = 0; oicp.icmp_cksum = in_cksum(&oicp, sizeof(oicp)); count = sendto(sock_raw, &oicp, sizeof(oicp), 0, (const struct sockaddr*)addr, sizeof(struct sockaddr)); if (count < 0) { if (measure_status == HOSTDOWN) measure_status = UNREACHABLE; goto quit; } oicp.icmp_seq++; timeradd(&tcur, &twait, &ttrans); } else { ttrans = tdone; } while (rcvcount < trials) { ssize_t ret; timersub(&ttrans, &tcur, &tout); if (tout.tv_sec < 0) tout.tv_sec = 0; count = poll(set, 1, tout.tv_sec * 1000 + tout.tv_usec / 1000); (void)gettimeofday(&tcur, (struct timezone *)0); if (count <= 0) break; length = sizeof(struct sockaddr_in); ret = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 0,&length); if (ret < 0) goto quit; /* * got something. See if it is ours */ if ((size_t)ret < sizeof(ip)) continue; memcpy(&ip, packet, sizeof(ip)); if ((size_t)ret < (size_t)ip.ip_hl << 2) continue; ret -= ip.ip_hl << 2; memset(&icp, 0, sizeof(icp)); memcpy(&icp, &packet[ip.ip_hl << 2], MIN((size_t)ret, sizeof(icp))); if (icp.icmp_type != ICMP_TSTAMPREPLY || icp.icmp_id != oicp.icmp_id || icp.icmp_seq < seqno || icp.icmp_seq >= oicp.icmp_seq) continue; sendtime = ntohl(icp.icmp_otime); recvtime = ((tcur.tv_sec % SECDAY) * 1000 + tcur.tv_usec / 1000); total = recvtime - sendtime; if (total < 0) /* do not hassle midnight */ continue; rcvcount++; histime1 = ntohl(icp.icmp_rtime); histime2 = ntohl(icp.icmp_ttime); /* * a host using a time format different from * msec. since midnight UT (as per RFC792) should * set the high order bit of the 32-bit time * value it transmits. */ if ((histime1 & 0x80000000) != 0) { measure_status = NONSTDTIME; goto quit; } measure_status = GOOD; idelta = recvtime-histime2; odelta = histime1-sendtime; /* do not be confused by midnight */ if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; /* save the quantization error so that we can get a * measurement finer than our system clock. */ if (total < MIN_ROUND) { measure_delta = (odelta - idelta)/2; goto quit; } if (idelta < min_idelta) min_idelta = idelta; if (odelta < min_odelta) min_odelta = odelta; measure_delta = (min_odelta - min_idelta)/2; } if (tcur.tv_sec > tdone.tv_sec || (tcur.tv_sec == tdone.tv_sec && tcur.tv_usec >= tdone.tv_usec)) break; } quit: seqno += TRIALS; /* allocate our sequence numbers */ /* * If no answer is received for TRIALS consecutive times, * the machine is assumed to be down */ if (measure_status == GOOD) { if (trace) { fprintf(fd, "measured delta %4d, %d trials to %-15s %s\n", measure_delta, trials, inet_ntoa(addr->sin_addr), hname); } } else if (printerr) { if (errno != 0) fprintf(stderr, "measure %s: %s\n", hname, strerror(errno)); } else { if (errno != 0) { syslog(LOG_ERR, "measure %s: %m", hname); } else { syslog(LOG_ERR, "measure: %s did not respond", hname); } if (trace) { fprintf(fd, "measure: %s failed after %d trials\n", hname, trials); (void)fflush(fd); } } return(measure_status); } /* * round a number of milliseconds into a struct timeval */ void mstotvround(struct timeval *res, long x) { if (x < 0) x = -((-x + 3)/5); else x = (x+3)/5; x *= 5; res->tv_sec = x/1000; res->tv_usec = (x-res->tv_sec*1000)*1000; if (res->tv_usec < 0) { res->tv_usec += 1000000; res->tv_sec--; } } void update_time(struct timeval *tv, const struct tsp *msg) { #ifdef SUPPORT_UTMP logwtmp("|", "date", ""); #endif #ifdef SUPPORT_UTMPX logwtmpx("|", "date", "", 0, OLD_TIME); #endif tv->tv_sec = msg->tsp_time.tv_sec; tv->tv_usec = msg->tsp_time.tv_usec; (void)settimeofday(tv, 0); #ifdef SUPPORT_UTMP logwtmp("}", "date", ""); #endif #ifdef SUPPORT_UTMPX logwtmpx("}", "date", "", 0, NEW_TIME); #endif }