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. 59246209Scharnier * maxmsec wait this many msec at most 60246209Scharnier * wmsec msec to wait for an answer 61246209Scharnier * print print complaints on stderr 621553Srgrimes */ 631553Srgrimesint /* status val defined in globals.h */ 64246209Scharniermeasure(u_long maxmsec, u_long wmsec, char *hname, struct sockaddr_in *addr, int print) 651553Srgrimes{ 661553Srgrimes int length; 671553Srgrimes int measure_status; 681553Srgrimes int rcvcount, trials; 691553Srgrimes int cc, count; 701553Srgrimes fd_set ready; 711553Srgrimes long sendtime, recvtime, histime1, histime2; 721553Srgrimes long idelta, odelta, total; 731553Srgrimes long min_idelta, min_odelta; 741553Srgrimes struct timeval tdone, tcur, ttrans, twait, tout; 751553Srgrimes u_char packet[PACKET_IN], opacket[64]; 761553Srgrimes register struct icmp *icp = (struct icmp *) packet; 771553Srgrimes register struct icmp *oicp = (struct icmp *) opacket; 781553Srgrimes struct ip *ip = (struct ip *) packet; 791553Srgrimes 801553Srgrimes min_idelta = min_odelta = 0x7fffffff; 811553Srgrimes measure_status = HOSTDOWN; 821553Srgrimes measure_delta = HOSTDOWN; 83209344Sgavin trials = 0; 841553Srgrimes errno = 0; 851553Srgrimes 861553Srgrimes /* open raw socket used to measure time differences */ 871553Srgrimes if (sock_raw < 0) { 881553Srgrimes sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 891553Srgrimes if (sock_raw < 0) { 901553Srgrimes syslog(LOG_ERR, "opening raw socket: %m"); 911553Srgrimes goto quit; 921553Srgrimes } 931553Srgrimes } 941553Srgrimes 958857Srgrimes 961553Srgrimes /* 971553Srgrimes * empty the icmp input queue 981553Srgrimes */ 991553Srgrimes FD_ZERO(&ready); 1001553Srgrimes for (;;) { 1011553Srgrimes tout.tv_sec = tout.tv_usec = 0; 1021553Srgrimes FD_SET(sock_raw, &ready); 1031553Srgrimes if (select(sock_raw+1, &ready, 0,0, &tout)) { 1041553Srgrimes length = sizeof(struct sockaddr_in); 1051553Srgrimes cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 1061553Srgrimes 0,&length); 1071553Srgrimes if (cc < 0) 1081553Srgrimes goto quit; 1091553Srgrimes continue; 1101553Srgrimes } 1111553Srgrimes break; 1121553Srgrimes } 1131553Srgrimes 1141553Srgrimes /* 1151553Srgrimes * Choose the smallest transmission time in each of the two 1161553Srgrimes * directions. Use these two latter quantities to compute the delta 1171553Srgrimes * between the two clocks. 1181553Srgrimes */ 1191553Srgrimes 1201553Srgrimes oicp->icmp_type = ICMP_TSTAMP; 1211553Srgrimes oicp->icmp_code = 0; 1221553Srgrimes oicp->icmp_id = getpid(); 1231553Srgrimes oicp->icmp_rtime = 0; 1241553Srgrimes oicp->icmp_ttime = 0; 1251553Srgrimes oicp->icmp_seq = seqno; 1261553Srgrimes 1271553Srgrimes FD_ZERO(&ready); 1281553Srgrimes 129239991Sed (void)gettimeofday(&tdone, NULL); 1301553Srgrimes mstotvround(&tout, maxmsec); 1311553Srgrimes timevaladd(&tdone, &tout); /* when we give up */ 1321553Srgrimes 1331553Srgrimes mstotvround(&twait, wmsec); 1341553Srgrimes 1351553Srgrimes rcvcount = 0; 1361553Srgrimes while (rcvcount < MSGS) { 137239991Sed (void)gettimeofday(&tcur, NULL); 1381553Srgrimes 1391553Srgrimes /* 1401553Srgrimes * keep sending until we have sent the max 1411553Srgrimes */ 1421553Srgrimes if (trials < TRIALS) { 1431553Srgrimes trials++; 14430761Scharnier oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000 1454840Sbde + tcur.tv_usec / 1000); 1461553Srgrimes oicp->icmp_cksum = 0; 1471553Srgrimes oicp->icmp_cksum = in_cksum((u_short*)oicp, 1481553Srgrimes sizeof(*oicp)); 1491553Srgrimes 1501553Srgrimes count = sendto(sock_raw, opacket, sizeof(*oicp), 0, 1511553Srgrimes (struct sockaddr*)addr, 1521553Srgrimes sizeof(struct sockaddr)); 1531553Srgrimes if (count < 0) { 1541553Srgrimes if (measure_status == HOSTDOWN) 1551553Srgrimes measure_status = UNREACHABLE; 1561553Srgrimes goto quit; 1571553Srgrimes } 1581553Srgrimes ++oicp->icmp_seq; 1591553Srgrimes 1601553Srgrimes ttrans = tcur; 1611553Srgrimes timevaladd(&ttrans, &twait); 1621553Srgrimes } else { 1631553Srgrimes ttrans = tdone; 1641553Srgrimes } 1651553Srgrimes 1661553Srgrimes while (rcvcount < trials) { 1671553Srgrimes timevalsub(&tout, &ttrans, &tcur); 1681553Srgrimes if (tout.tv_sec < 0) 1691553Srgrimes tout.tv_sec = 0; 1701553Srgrimes 1711553Srgrimes FD_SET(sock_raw, &ready); 1721553Srgrimes count = select(sock_raw+1, &ready, (fd_set *)0, 1731553Srgrimes (fd_set *)0, &tout); 174239991Sed (void)gettimeofday(&tcur, NULL); 1751553Srgrimes if (count <= 0) 1761553Srgrimes break; 1771553Srgrimes 1781553Srgrimes length = sizeof(struct sockaddr_in); 1791553Srgrimes cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 1801553Srgrimes 0,&length); 1811553Srgrimes if (cc < 0) 1821553Srgrimes goto quit; 1831553Srgrimes 1848857Srgrimes /* 1851553Srgrimes * got something. See if it is ours 1861553Srgrimes */ 1871553Srgrimes icp = (struct icmp *)(packet + (ip->ip_hl << 2)); 1881553Srgrimes if (cc < sizeof(*ip) 1891553Srgrimes || icp->icmp_type != ICMP_TSTAMPREPLY 1901553Srgrimes || icp->icmp_id != oicp->icmp_id 1911553Srgrimes || icp->icmp_seq < seqno 1921553Srgrimes || icp->icmp_seq >= oicp->icmp_seq) 1931553Srgrimes continue; 1941553Srgrimes 1951553Srgrimes 1961553Srgrimes sendtime = ntohl(icp->icmp_otime); 1971553Srgrimes recvtime = ((tcur.tv_sec % SECDAY) * 1000 + 1981553Srgrimes tcur.tv_usec / 1000); 1991553Srgrimes 2001553Srgrimes total = recvtime-sendtime; 2011553Srgrimes if (total < 0) /* do not hassle midnight */ 2021553Srgrimes continue; 2031553Srgrimes 2041553Srgrimes rcvcount++; 2051553Srgrimes histime1 = ntohl(icp->icmp_rtime); 2061553Srgrimes histime2 = ntohl(icp->icmp_ttime); 2071553Srgrimes /* 2081553Srgrimes * a host using a time format different from 2091553Srgrimes * msec. since midnight UT (as per RFC792) should 2101553Srgrimes * set the high order bit of the 32-bit time 2111553Srgrimes * value it transmits. 2121553Srgrimes */ 2131553Srgrimes if ((histime1 & 0x80000000) != 0) { 2141553Srgrimes measure_status = NONSTDTIME; 2151553Srgrimes goto quit; 2161553Srgrimes } 2171553Srgrimes measure_status = GOOD; 2181553Srgrimes 2191553Srgrimes idelta = recvtime-histime2; 2201553Srgrimes odelta = histime1-sendtime; 2211553Srgrimes 2221553Srgrimes /* do not be confused by midnight */ 2231553Srgrimes if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; 2241553Srgrimes else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; 2251553Srgrimes 2261553Srgrimes if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; 2271553Srgrimes else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; 2281553Srgrimes 2291553Srgrimes /* save the quantization error so that we can get a 2301553Srgrimes * measurement finer than our system clock. 2311553Srgrimes */ 2321553Srgrimes if (total < MIN_ROUND) { 2331553Srgrimes measure_delta = (odelta - idelta)/2; 2341553Srgrimes goto quit; 2351553Srgrimes } 2361553Srgrimes 2371553Srgrimes if (idelta < min_idelta) 2381553Srgrimes min_idelta = idelta; 2391553Srgrimes if (odelta < min_odelta) 2401553Srgrimes min_odelta = odelta; 2411553Srgrimes 2421553Srgrimes measure_delta = (min_odelta - min_idelta)/2; 2431553Srgrimes } 2441553Srgrimes 2451553Srgrimes if (tcur.tv_sec > tdone.tv_sec 2461553Srgrimes || (tcur.tv_sec == tdone.tv_sec 2471553Srgrimes && tcur.tv_usec >= tdone.tv_usec)) 2481553Srgrimes break; 2491553Srgrimes } 2501553Srgrimes 2511553Srgrimesquit: 2521553Srgrimes seqno += TRIALS; /* allocate our sequence numbers */ 2531553Srgrimes 2541553Srgrimes /* 2551553Srgrimes * If no answer is received for TRIALS consecutive times, 2561553Srgrimes * the machine is assumed to be down 2571553Srgrimes */ 2581553Srgrimes if (measure_status == GOOD) { 2591553Srgrimes if (trace) { 2601553Srgrimes fprintf(fd, 2611553Srgrimes "measured delta %4d, %d trials to %-15s %s\n", 2621553Srgrimes measure_delta, trials, 2631553Srgrimes inet_ntoa(addr->sin_addr), hname); 2641553Srgrimes } 2651553Srgrimes } else if (print) { 2661553Srgrimes if (errno != 0) 26730642Scharnier warn("measure %s", hname); 2681553Srgrimes } else { 2691553Srgrimes if (errno != 0) { 2701553Srgrimes syslog(LOG_ERR, "measure %s: %m", hname); 2711553Srgrimes } else { 2721553Srgrimes syslog(LOG_ERR, "measure: %s did not respond", hname); 2731553Srgrimes } 2741553Srgrimes if (trace) { 2751553Srgrimes fprintf(fd, 2761553Srgrimes "measure: %s failed after %d trials\n", 2771553Srgrimes hname, trials); 2781553Srgrimes (void)fflush(fd); 2791553Srgrimes } 2801553Srgrimes } 2811553Srgrimes 2821553Srgrimes return(measure_status); 2831553Srgrimes} 2841553Srgrimes 2851553Srgrimes 2861553Srgrimes 2871553Srgrimes 2881553Srgrimes 2891553Srgrimes/* 2901553Srgrimes * round a number of milliseconds into a struct timeval 2911553Srgrimes */ 2921553Srgrimesvoid 293246209Scharniermstotvround(struct timeval *res, long x) 2941553Srgrimes{ 2951553Srgrimes if (x < 0) 2961553Srgrimes x = -((-x + 3)/5); 2971553Srgrimes else 2981553Srgrimes x = (x+3)/5; 2991553Srgrimes x *= 5; 3001553Srgrimes res->tv_sec = x/1000; 3011553Srgrimes res->tv_usec = (x-res->tv_sec*1000)*1000; 3021553Srgrimes if (res->tv_usec < 0) { 3031553Srgrimes res->tv_usec += 1000000; 3041553Srgrimes res->tv_sec--; 3051553Srgrimes } 3061553Srgrimes} 3071553Srgrimes 3081553Srgrimesvoid 309246209Scharniertimevaladd(struct timeval *tv1, struct timeval *tv2) 3101553Srgrimes{ 3111553Srgrimes tv1->tv_sec += tv2->tv_sec; 3121553Srgrimes tv1->tv_usec += tv2->tv_usec; 3131553Srgrimes if (tv1->tv_usec >= 1000000) { 3141553Srgrimes tv1->tv_sec++; 3151553Srgrimes tv1->tv_usec -= 1000000; 3161553Srgrimes } 3171553Srgrimes if (tv1->tv_usec < 0) { 3181553Srgrimes tv1->tv_sec--; 3191553Srgrimes tv1->tv_usec += 1000000; 3201553Srgrimes } 3211553Srgrimes} 3221553Srgrimes 3231553Srgrimesvoid 324246209Scharniertimevalsub(struct timeval *res, struct timeval *tv1, struct timeval *tv2) 3251553Srgrimes{ 3261553Srgrimes res->tv_sec = tv1->tv_sec - tv2->tv_sec; 3271553Srgrimes res->tv_usec = tv1->tv_usec - tv2->tv_usec; 3281553Srgrimes if (res->tv_usec >= 1000000) { 3291553Srgrimes res->tv_sec++; 3301553Srgrimes res->tv_usec -= 1000000; 3311553Srgrimes } 3321553Srgrimes if (res->tv_usec < 0) { 3331553Srgrimes res->tv_sec--; 3341553Srgrimes res->tv_usec += 1000000; 3351553Srgrimes } 3361553Srgrimes} 337