measure.c revision 1553
11553Srgrimes/*- 21553Srgrimes * Copyright (c) 1985, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 3. All advertising materials mentioning features or use of this software 141553Srgrimes * must display the following acknowledgement: 151553Srgrimes * This product includes software developed by the University of 161553Srgrimes * California, Berkeley and its contributors. 171553Srgrimes * 4. Neither the name of the University nor the names of its contributors 181553Srgrimes * may be used to endorse or promote products derived from this software 191553Srgrimes * without specific prior written permission. 201553Srgrimes * 211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311553Srgrimes * SUCH DAMAGE. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#ifndef lint 351553Srgrimesstatic char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93"; 361553Srgrimes#endif /* not lint */ 371553Srgrimes 381553Srgrimes#ifdef sgi 391553Srgrimes#ident "$Revision: 1.8 $" 401553Srgrimes#endif 411553Srgrimes 421553Srgrimes#include "globals.h" 431553Srgrimes#include <netinet/in_systm.h> 441553Srgrimes#include <netinet/ip.h> 451553Srgrimes#include <netinet/ip_icmp.h> 461553Srgrimes 471553Srgrimes#define MSEC_DAY (SECDAY*1000) 481553Srgrimes 491553Srgrimes#define PACKET_IN 1024 501553Srgrimes 511553Srgrimes#define MSGS 5 /* timestamps to average */ 521553Srgrimes#define TRIALS 10 /* max # of timestamps sent */ 531553Srgrimes 541553Srgrimesextern int sock_raw; 551553Srgrimes 561553Srgrimesint measure_delta; 571553Srgrimes 581553Srgrimesstatic n_short seqno = 0; 591553Srgrimes 601553Srgrimes/* 611553Srgrimes * Measures the differences between machines' clocks using 621553Srgrimes * ICMP timestamp messages. 631553Srgrimes */ 641553Srgrimesint /* status val defined in globals.h */ 651553Srgrimesmeasure(maxmsec, wmsec, hname, addr, print) 661553Srgrimes u_long maxmsec; /* wait this many msec at most */ 671553Srgrimes u_long wmsec; /* msec to wait for an answer */ 681553Srgrimes char *hname; 691553Srgrimes struct sockaddr_in *addr; 701553Srgrimes int print; /* print complaints on stderr */ 711553Srgrimes{ 721553Srgrimes int length; 731553Srgrimes int measure_status; 741553Srgrimes int rcvcount, trials; 751553Srgrimes int cc, count; 761553Srgrimes fd_set ready; 771553Srgrimes long sendtime, recvtime, histime1, histime2; 781553Srgrimes long idelta, odelta, total; 791553Srgrimes long min_idelta, min_odelta; 801553Srgrimes struct timeval tdone, tcur, ttrans, twait, tout; 811553Srgrimes u_char packet[PACKET_IN], opacket[64]; 821553Srgrimes register struct icmp *icp = (struct icmp *) packet; 831553Srgrimes register struct icmp *oicp = (struct icmp *) opacket; 841553Srgrimes struct ip *ip = (struct ip *) packet; 851553Srgrimes 861553Srgrimes min_idelta = min_odelta = 0x7fffffff; 871553Srgrimes measure_status = HOSTDOWN; 881553Srgrimes measure_delta = HOSTDOWN; 891553Srgrimes errno = 0; 901553Srgrimes 911553Srgrimes /* open raw socket used to measure time differences */ 921553Srgrimes if (sock_raw < 0) { 931553Srgrimes sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 941553Srgrimes if (sock_raw < 0) { 951553Srgrimes syslog(LOG_ERR, "opening raw socket: %m"); 961553Srgrimes goto quit; 971553Srgrimes } 981553Srgrimes } 991553Srgrimes 1001553Srgrimes 1011553Srgrimes /* 1021553Srgrimes * empty the icmp input queue 1031553Srgrimes */ 1041553Srgrimes FD_ZERO(&ready); 1051553Srgrimes for (;;) { 1061553Srgrimes tout.tv_sec = tout.tv_usec = 0; 1071553Srgrimes FD_SET(sock_raw, &ready); 1081553Srgrimes if (select(sock_raw+1, &ready, 0,0, &tout)) { 1091553Srgrimes length = sizeof(struct sockaddr_in); 1101553Srgrimes cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 1111553Srgrimes 0,&length); 1121553Srgrimes if (cc < 0) 1131553Srgrimes goto quit; 1141553Srgrimes continue; 1151553Srgrimes } 1161553Srgrimes break; 1171553Srgrimes } 1181553Srgrimes 1191553Srgrimes /* 1201553Srgrimes * Choose the smallest transmission time in each of the two 1211553Srgrimes * directions. Use these two latter quantities to compute the delta 1221553Srgrimes * between the two clocks. 1231553Srgrimes */ 1241553Srgrimes 1251553Srgrimes oicp->icmp_type = ICMP_TSTAMP; 1261553Srgrimes oicp->icmp_code = 0; 1271553Srgrimes oicp->icmp_id = getpid(); 1281553Srgrimes oicp->icmp_rtime = 0; 1291553Srgrimes oicp->icmp_ttime = 0; 1301553Srgrimes oicp->icmp_seq = seqno; 1311553Srgrimes 1321553Srgrimes FD_ZERO(&ready); 1331553Srgrimes 1341553Srgrimes#ifdef sgi 1351553Srgrimes sginap(1); /* start at a clock tick */ 1361553Srgrimes#endif /* sgi */ 1371553Srgrimes 1381553Srgrimes (void)gettimeofday(&tdone, 0); 1391553Srgrimes mstotvround(&tout, maxmsec); 1401553Srgrimes timevaladd(&tdone, &tout); /* when we give up */ 1411553Srgrimes 1421553Srgrimes mstotvround(&twait, wmsec); 1431553Srgrimes 1441553Srgrimes rcvcount = 0; 1451553Srgrimes trials = 0; 1461553Srgrimes while (rcvcount < MSGS) { 1471553Srgrimes (void)gettimeofday(&tcur, 0); 1481553Srgrimes 1491553Srgrimes /* 1501553Srgrimes * keep sending until we have sent the max 1511553Srgrimes */ 1521553Srgrimes if (trials < TRIALS) { 1531553Srgrimes trials++; 1541553Srgrimes oicp->icmp_otime = ((tcur.tv_sec % SECDAY) * 1000 1551553Srgrimes + tcur.tv_usec / 1000); 1561553Srgrimes oicp->icmp_cksum = 0; 1571553Srgrimes oicp->icmp_cksum = in_cksum((u_short*)oicp, 1581553Srgrimes sizeof(*oicp)); 1591553Srgrimes 1601553Srgrimes count = sendto(sock_raw, opacket, sizeof(*oicp), 0, 1611553Srgrimes (struct sockaddr*)addr, 1621553Srgrimes sizeof(struct sockaddr)); 1631553Srgrimes if (count < 0) { 1641553Srgrimes if (measure_status == HOSTDOWN) 1651553Srgrimes measure_status = UNREACHABLE; 1661553Srgrimes goto quit; 1671553Srgrimes } 1681553Srgrimes ++oicp->icmp_seq; 1691553Srgrimes 1701553Srgrimes ttrans = tcur; 1711553Srgrimes timevaladd(&ttrans, &twait); 1721553Srgrimes } else { 1731553Srgrimes ttrans = tdone; 1741553Srgrimes } 1751553Srgrimes 1761553Srgrimes while (rcvcount < trials) { 1771553Srgrimes timevalsub(&tout, &ttrans, &tcur); 1781553Srgrimes if (tout.tv_sec < 0) 1791553Srgrimes tout.tv_sec = 0; 1801553Srgrimes 1811553Srgrimes FD_SET(sock_raw, &ready); 1821553Srgrimes count = select(sock_raw+1, &ready, (fd_set *)0, 1831553Srgrimes (fd_set *)0, &tout); 1841553Srgrimes (void)gettimeofday(&tcur, (struct timezone *)0); 1851553Srgrimes if (count <= 0) 1861553Srgrimes break; 1871553Srgrimes 1881553Srgrimes length = sizeof(struct sockaddr_in); 1891553Srgrimes cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 1901553Srgrimes 0,&length); 1911553Srgrimes if (cc < 0) 1921553Srgrimes goto quit; 1931553Srgrimes 1941553Srgrimes /* 1951553Srgrimes * got something. See if it is ours 1961553Srgrimes */ 1971553Srgrimes icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 1981553Srgrimes if (cc < sizeof(*ip) 1991553Srgrimes || icp->icmp_type != ICMP_TSTAMPREPLY 2001553Srgrimes || icp->icmp_id != oicp->icmp_id 2011553Srgrimes || icp->icmp_seq < seqno 2021553Srgrimes || icp->icmp_seq >= oicp->icmp_seq) 2031553Srgrimes continue; 2041553Srgrimes 2051553Srgrimes 2061553Srgrimes sendtime = ntohl(icp->icmp_otime); 2071553Srgrimes recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 2081553Srgrimes tcur.tv_usec / 1000); 2091553Srgrimes 2101553Srgrimes total = recvtime-sendtime; 2111553Srgrimes if (total < 0) /* do not hassle midnight */ 2121553Srgrimes continue; 2131553Srgrimes 2141553Srgrimes rcvcount++; 2151553Srgrimes histime1 = ntohl(icp->icmp_rtime); 2161553Srgrimes histime2 = ntohl(icp->icmp_ttime); 2171553Srgrimes /* 2181553Srgrimes * a host using a time format different from 2191553Srgrimes * msec. since midnight UT (as per RFC792) should 2201553Srgrimes * set the high order bit of the 32-bit time 2211553Srgrimes * value it transmits. 2221553Srgrimes */ 2231553Srgrimes if ((histime1 & 0x80000000) != 0) { 2241553Srgrimes measure_status = NONSTDTIME; 2251553Srgrimes goto quit; 2261553Srgrimes } 2271553Srgrimes measure_status = GOOD; 2281553Srgrimes 2291553Srgrimes idelta = recvtime-histime2; 2301553Srgrimes odelta = histime1-sendtime; 2311553Srgrimes 2321553Srgrimes /* do not be confused by midnight */ 2331553Srgrimes if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; 2341553Srgrimes else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; 2351553Srgrimes 2361553Srgrimes if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 2371553Srgrimes else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 2381553Srgrimes 2391553Srgrimes /* save the quantization error so that we can get a 2401553Srgrimes * measurement finer than our system clock. 2411553Srgrimes */ 2421553Srgrimes if (total < MIN_ROUND) { 2431553Srgrimes measure_delta = (odelta - idelta)/2; 2441553Srgrimes goto quit; 2451553Srgrimes } 2461553Srgrimes 2471553Srgrimes if (idelta < min_idelta) 2481553Srgrimes min_idelta = idelta; 2491553Srgrimes if (odelta < min_odelta) 2501553Srgrimes min_odelta = odelta; 2511553Srgrimes 2521553Srgrimes measure_delta = (min_odelta - min_idelta)/2; 2531553Srgrimes } 2541553Srgrimes 2551553Srgrimes if (tcur.tv_sec > tdone.tv_sec 2561553Srgrimes || (tcur.tv_sec == tdone.tv_sec 2571553Srgrimes && tcur.tv_usec >= tdone.tv_usec)) 2581553Srgrimes break; 2591553Srgrimes } 2601553Srgrimes 2611553Srgrimesquit: 2621553Srgrimes seqno += TRIALS; /* allocate our sequence numbers */ 2631553Srgrimes 2641553Srgrimes /* 2651553Srgrimes * If no answer is received for TRIALS consecutive times, 2661553Srgrimes * the machine is assumed to be down 2671553Srgrimes */ 2681553Srgrimes if (measure_status == GOOD) { 2691553Srgrimes if (trace) { 2701553Srgrimes fprintf(fd, 2711553Srgrimes "measured delta %4d, %d trials to %-15s %s\n", 2721553Srgrimes measure_delta, trials, 2731553Srgrimes inet_ntoa(addr->sin_addr), hname); 2741553Srgrimes } 2751553Srgrimes } else if (print) { 2761553Srgrimes if (errno != 0) 2771553Srgrimes fprintf(stderr, "measure %s: %s\n", hname, 2781553Srgrimes strerror(errno)); 2791553Srgrimes } else { 2801553Srgrimes if (errno != 0) { 2811553Srgrimes syslog(LOG_ERR, "measure %s: %m", hname); 2821553Srgrimes } else { 2831553Srgrimes syslog(LOG_ERR, "measure: %s did not respond", hname); 2841553Srgrimes } 2851553Srgrimes if (trace) { 2861553Srgrimes fprintf(fd, 2871553Srgrimes "measure: %s failed after %d trials\n", 2881553Srgrimes hname, trials); 2891553Srgrimes (void)fflush(fd); 2901553Srgrimes } 2911553Srgrimes } 2921553Srgrimes 2931553Srgrimes return(measure_status); 2941553Srgrimes} 2951553Srgrimes 2961553Srgrimes 2971553Srgrimes 2981553Srgrimes 2991553Srgrimes 3001553Srgrimes/* 3011553Srgrimes * round a number of milliseconds into a struct timeval 3021553Srgrimes */ 3031553Srgrimesvoid 3041553Srgrimesmstotvround(res, x) 3051553Srgrimes struct timeval *res; 3061553Srgrimes long x; 3071553Srgrimes{ 3081553Srgrimes#ifndef sgi 3091553Srgrimes if (x < 0) 3101553Srgrimes x = -((-x + 3)/5); 3111553Srgrimes else 3121553Srgrimes x = (x+3)/5; 3131553Srgrimes x *= 5; 3141553Srgrimes#endif /* sgi */ 3151553Srgrimes res->tv_sec = x/1000; 3161553Srgrimes res->tv_usec = (x-res->tv_sec*1000)*1000; 3171553Srgrimes if (res->tv_usec < 0) { 3181553Srgrimes res->tv_usec += 1000000; 3191553Srgrimes res->tv_sec--; 3201553Srgrimes } 3211553Srgrimes} 3221553Srgrimes 3231553Srgrimesvoid 3241553Srgrimestimevaladd(tv1, tv2) 3251553Srgrimes struct timeval *tv1, *tv2; 3261553Srgrimes{ 3271553Srgrimes tv1->tv_sec += tv2->tv_sec; 3281553Srgrimes tv1->tv_usec += tv2->tv_usec; 3291553Srgrimes if (tv1->tv_usec >= 1000000) { 3301553Srgrimes tv1->tv_sec++; 3311553Srgrimes tv1->tv_usec -= 1000000; 3321553Srgrimes } 3331553Srgrimes if (tv1->tv_usec < 0) { 3341553Srgrimes tv1->tv_sec--; 3351553Srgrimes tv1->tv_usec += 1000000; 3361553Srgrimes } 3371553Srgrimes} 3381553Srgrimes 3391553Srgrimesvoid 3401553Srgrimestimevalsub(res, tv1, tv2) 3411553Srgrimes struct timeval *res, *tv1, *tv2; 3421553Srgrimes{ 3431553Srgrimes res->tv_sec = tv1->tv_sec - tv2->tv_sec; 3441553Srgrimes res->tv_usec = tv1->tv_usec - tv2->tv_usec; 3451553Srgrimes if (res->tv_usec >= 1000000) { 3461553Srgrimes res->tv_sec++; 3471553Srgrimes res->tv_usec -= 1000000; 3481553Srgrimes } 3491553Srgrimes if (res->tv_usec < 0) { 3501553Srgrimes res->tv_sec--; 3511553Srgrimes res->tv_usec += 1000000; 3521553Srgrimes } 3531553Srgrimes} 354