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