Initial import from ToT git.
Bug: 9469682
Change-Id: I6fc32550557dc5e94e9ecbbb57b0ec30a844adb4
Upstream: git://git.linux-ipv6.org/gitroot/iputils.git
Commit: 608419a7804caf36a359875d2fdae0b3eb181387
diff --git a/clockdiff.c b/clockdiff.c
new file mode 100644
index 0000000..7c1ea1b
--- /dev/null
+++ b/clockdiff.c
@@ -0,0 +1,687 @@
+#include <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#define TSPTYPES
+#include <protocols/timed.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/types.h>
+#ifdef CAPABILITIES
+#include <sys/capability.h>
+#endif
+
+void usage(void) __attribute__((noreturn));
+
+#define MAX_HOSTNAMELEN NI_MAXHOST
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * This implementation is TAHOE version.
+ */
+
+#undef ADDCARRY
+#define ADDCARRY(sum) { \
+ if (sum & 0xffff0000) { \
+ sum &= 0xffff; \
+ sum++; \
+ } \
+}
+
+int in_cksum(u_short *addr, int len)
+{
+ union word {
+ char c[2];
+ u_short s;
+ } u;
+ int sum = 0;
+
+ while (len > 0) {
+ /*
+ * add by words.
+ */
+ while ((len -= 2) >= 0) {
+ if ((unsigned long)addr & 0x1) {
+ /* word is not aligned */
+ u.c[0] = *(char *)addr;
+ u.c[1] = *((char *)addr+1);
+ sum += u.s;
+ addr++;
+ } else
+ sum += *addr++;
+ ADDCARRY(sum);
+ }
+ if (len == -1)
+ /*
+ * Odd number of bytes.
+ */
+ u.c[0] = *(u_char *)addr;
+ }
+ if (len == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte is shifted left by 8 bits) */
+ u.c[1] = 0;
+ sum += u.s;
+ ADDCARRY(sum);
+ }
+ return (~sum & 0xffff);
+}
+
+#define ON 1
+#define OFF 0
+
+#define RANGE 1 /* best expected round-trip time, ms */
+#define MSGS 50
+#define TRIALS 10
+
+#define GOOD 0
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+
+int interactive = 0;
+uint16_t id;
+int sock;
+int sock_raw;
+struct sockaddr_in server;
+int ip_opt_len = 0;
+
+#define BIASP 43199999
+#define BIASN -43200000
+#define MODULO 86400000
+#define PROCESSING_TIME 0 /* ms. to reduce error in measurement */
+
+#define PACKET_IN 1024
+
+int measure_delta;
+int measure_delta1;
+static u_short seqno, seqno0, acked;
+long rtt = 1000;
+long min_rtt;
+long rtt_sigma = 0;
+
+/*
+ * Measures the differences between machines' clocks using
+ * ICMP timestamp messages.
+ */
+int
+measure(struct sockaddr_in * addr)
+{
+ socklen_t length;
+ int msgcount;
+ int cc, count;
+ fd_set ready;
+ long sendtime, recvtime, histime;
+ long min1, min2, diff;
+ long delta1, delta2;
+ struct timeval tv1, tout;
+ u_char packet[PACKET_IN], opacket[64];
+ struct icmphdr *icp = (struct icmphdr *) packet;
+ struct icmphdr *oicp = (struct icmphdr *) opacket;
+ struct iphdr *ip = (struct iphdr *) packet;
+
+ min1 = min2 = 0x7fffffff;
+ min_rtt = 0x7fffffff;
+ measure_delta = HOSTDOWN;
+ measure_delta1 = HOSTDOWN;
+
+/* empties the icmp input queue */
+ FD_ZERO(&ready);
+
+empty:
+ tout.tv_sec = tout.tv_usec = 0;
+ FD_SET(sock_raw, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ (struct sockaddr *)NULL, &length);
+ if (cc < 0)
+ return -1;
+ goto empty;
+ }
+
+ /*
+ * To measure the difference, select MSGS messages whose round-trip
+ * time is smaller than RANGE if ckrange is 1, otherwise simply
+ * select MSGS messages regardless of round-trip transmission time.
+ * Choose the smallest transmission time in each of the two directions.
+ * Use these two latter quantities to compute the delta between
+ * the two clocks.
+ */
+
+ length = sizeof(struct sockaddr_in);
+ oicp->type = ICMP_TIMESTAMP;
+ oicp->code = 0;
+ oicp->checksum = 0;
+ oicp->un.echo.id = id;
+ ((__u32*)(oicp+1))[0] = 0;
+ ((__u32*)(oicp+1))[1] = 0;
+ ((__u32*)(oicp+1))[2] = 0;
+ FD_ZERO(&ready);
+ msgcount = 0;
+
+ acked = seqno = seqno0 = 0;
+
+ for (msgcount = 0; msgcount < MSGS; ) {
+
+ /*
+ * If no answer is received for TRIALS consecutive times,
+ * the machine is assumed to be down
+ */
+ if (seqno - acked > TRIALS)
+ return HOSTDOWN;
+
+ oicp->un.echo.sequence = ++seqno;
+ oicp->checksum = 0;
+
+ (void)gettimeofday (&tv1, (struct timezone *)0);
+ *(__u32*)(oicp+1) = htonl((tv1.tv_sec % (24*60*60)) * 1000
+ + tv1.tv_usec / 1000);
+ oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp) + 12);
+
+ count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
+ (struct sockaddr *)addr, sizeof(struct sockaddr_in));
+
+ if (count < 0)
+ return UNREACHABLE;
+
+ for (;;) {
+ FD_ZERO(&ready);
+ FD_SET(sock_raw, &ready);
+ {
+ long tmo = rtt + rtt_sigma;
+ tout.tv_sec = tmo/1000;
+ tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
+ }
+
+ if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
+ (fd_set *)0, &tout)) <= 0)
+ break;
+
+ (void)gettimeofday(&tv1, (struct timezone *)0);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ (struct sockaddr *)NULL, &length);
+
+ if (cc < 0)
+ return(-1);
+
+ icp = (struct icmphdr *)(packet + (ip->ihl << 2));
+ if( icp->type == ICMP_TIMESTAMPREPLY &&
+ icp->un.echo.id == id && icp->un.echo.sequence >= seqno0 &&
+ icp->un.echo.sequence <= seqno) {
+ if (acked < icp->un.echo.sequence)
+ acked = icp->un.echo.sequence;
+
+ recvtime = (tv1.tv_sec % (24*60*60)) * 1000 +
+ tv1.tv_usec / 1000;
+ sendtime = ntohl(*(__u32*)(icp+1));
+ diff = recvtime - sendtime;
+ /*
+ * diff can be less than 0 aroud midnight
+ */
+ if (diff < 0)
+ continue;
+ rtt = (rtt * 3 + diff)/4;
+ rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
+ msgcount++;
+ histime = ntohl(((__u32*)(icp+1))[1]);
+ /*
+ * a hosts using a time format different from
+ * ms. since midnight UT (as per RFC792) should
+ * set the high order bit of the 32-bit time
+ * value it transmits.
+ */
+ if ((histime & 0x80000000) != 0)
+ return NONSTDTIME;
+
+ if (interactive) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ delta1 = histime - sendtime;
+ /*
+ * Handles wrap-around to avoid that around
+ * midnight small time differences appear
+ * enormous. However, the two machine's clocks
+ * must be within 12 hours from each other.
+ */
+ if (delta1 < BIASN)
+ delta1 += MODULO;
+ else if (delta1 > BIASP)
+ delta1 -= MODULO;
+
+ delta2 = recvtime - histime;
+ if (delta2 < BIASN)
+ delta2 += MODULO;
+ else if (delta2 > BIASP)
+ delta2 -= MODULO;
+
+ if (delta1 < min1)
+ min1 = delta1;
+ if (delta2 < min2)
+ min2 = delta2;
+ if (delta1 + delta2 < min_rtt) {
+ min_rtt = delta1 + delta2;
+ measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
+ }
+ if (diff < RANGE) {
+ min1 = delta1;
+ min2 = delta2;
+ goto good_exit;
+ }
+ }
+ }
+ }
+
+good_exit:
+ measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
+ return GOOD;
+}
+
+char *myname, *hisname;
+
+int
+measure_opt(struct sockaddr_in * addr)
+{
+ socklen_t length;
+ int msgcount;
+ int cc, count;
+ fd_set ready;
+ long sendtime, recvtime, histime, histime1;
+ long min1, min2, diff;
+ long delta1, delta2;
+ struct timeval tv1, tout;
+ u_char packet[PACKET_IN], opacket[64];
+ struct icmphdr *icp = (struct icmphdr *) packet;
+ struct icmphdr *oicp = (struct icmphdr *) opacket;
+ struct iphdr *ip = (struct iphdr *) packet;
+
+ min1 = min2 = 0x7fffffff;
+ min_rtt = 0x7fffffff;
+ measure_delta = HOSTDOWN;
+ measure_delta1 = HOSTDOWN;
+
+/* empties the icmp input queue */
+ FD_ZERO(&ready);
+empty:
+ tout.tv_sec = tout.tv_usec = 0;
+ FD_SET(sock_raw, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ (struct sockaddr *)NULL, &length);
+ if (cc < 0)
+ return -1;
+ goto empty;
+ }
+
+ /*
+ * To measure the difference, select MSGS messages whose round-trip
+ * time is smaller than RANGE if ckrange is 1, otherwise simply
+ * select MSGS messages regardless of round-trip transmission time.
+ * Choose the smallest transmission time in each of the two directions.
+ * Use these two latter quantities to compute the delta between
+ * the two clocks.
+ */
+
+ length = sizeof(struct sockaddr_in);
+ oicp->type = ICMP_ECHO;
+ oicp->code = 0;
+ oicp->checksum = 0;
+ oicp->un.echo.id = id;
+ ((__u32*)(oicp+1))[0] = 0;
+ ((__u32*)(oicp+1))[1] = 0;
+ ((__u32*)(oicp+1))[2] = 0;
+
+ FD_ZERO(&ready);
+ msgcount = 0;
+
+ acked = seqno = seqno0 = 0;
+
+ for (msgcount = 0; msgcount < MSGS; ) {
+
+ /*
+ * If no answer is received for TRIALS consecutive times,
+ * the machine is assumed to be down
+ */
+ if ( seqno - acked > TRIALS) {
+ errno = EHOSTDOWN;
+ return HOSTDOWN;
+ }
+ oicp->un.echo.sequence = ++seqno;
+ oicp->checksum = 0;
+
+ gettimeofday (&tv1, NULL);
+ ((__u32*)(oicp+1))[0] = htonl((tv1.tv_sec % (24*60*60)) * 1000
+ + tv1.tv_usec / 1000);
+ oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp)+12);
+
+ count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
+ (struct sockaddr *)addr, sizeof(struct sockaddr_in));
+
+ if (count < 0) {
+ errno = EHOSTUNREACH;
+ return UNREACHABLE;
+ }
+
+ for (;;) {
+ FD_ZERO(&ready);
+ FD_SET(sock_raw, &ready);
+ {
+ long tmo = rtt + rtt_sigma;
+ tout.tv_sec = tmo/1000;
+ tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
+ }
+
+ if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
+ (fd_set *)0, &tout)) <= 0)
+ break;
+
+ (void)gettimeofday(&tv1, (struct timezone *)0);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ (struct sockaddr *)NULL, &length);
+
+ if (cc < 0)
+ return(-1);
+
+ icp = (struct icmphdr *)(packet + (ip->ihl << 2));
+ if (icp->type == ICMP_ECHOREPLY &&
+ packet[20] == IPOPT_TIMESTAMP &&
+ icp->un.echo.id == id &&
+ icp->un.echo.sequence >= seqno0 &&
+ icp->un.echo.sequence <= seqno) {
+ int i;
+ __u8 *opt = packet+20;
+
+ if (acked < icp->un.echo.sequence)
+ acked = icp->un.echo.sequence;
+ if ((opt[3]&0xF) != IPOPT_TS_PRESPEC) {
+ fprintf(stderr, "Wrong timestamp %d\n", opt[3]&0xF);
+ return NONSTDTIME;
+ }
+ if (opt[3]>>4) {
+ if ((opt[3]>>4) != 1 || ip_opt_len != 4+3*8)
+ fprintf(stderr, "Overflow %d hops\n", opt[3]>>4);
+ }
+ sendtime = recvtime = histime = histime1 = 0;
+ for (i=0; i < (opt[2]-5)/8; i++) {
+ __u32 *timep = (__u32*)(opt+4+i*8+4);
+ __u32 t = ntohl(*timep);
+
+ if (t & 0x80000000)
+ return NONSTDTIME;
+
+ if (i == 0)
+ sendtime = t;
+ if (i == 1)
+ histime = histime1 = t;
+ if (i == 2) {
+ if (ip_opt_len == 4+4*8)
+ histime1 = t;
+ else
+ recvtime = t;
+ }
+ if (i == 3)
+ recvtime = t;
+ }
+
+ if (!(sendtime&histime&histime1&recvtime)) {
+ fprintf(stderr, "wrong timestamps\n");
+ return -1;
+ }
+
+ diff = recvtime - sendtime;
+ /*
+ * diff can be less than 0 aroud midnight
+ */
+ if (diff < 0)
+ continue;
+ rtt = (rtt * 3 + diff)/4;
+ rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
+ msgcount++;
+
+ if (interactive) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ delta1 = histime - sendtime;
+ /*
+ * Handles wrap-around to avoid that around
+ * midnight small time differences appear
+ * enormous. However, the two machine's clocks
+ * must be within 12 hours from each other.
+ */
+ if (delta1 < BIASN)
+ delta1 += MODULO;
+ else if (delta1 > BIASP)
+ delta1 -= MODULO;
+
+ delta2 = recvtime - histime1;
+ if (delta2 < BIASN)
+ delta2 += MODULO;
+ else if (delta2 > BIASP)
+ delta2 -= MODULO;
+
+ if (delta1 < min1)
+ min1 = delta1;
+ if (delta2 < min2)
+ min2 = delta2;
+ if (delta1 + delta2 < min_rtt) {
+ min_rtt = delta1 + delta2;
+ measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
+ }
+ if (diff < RANGE) {
+ min1 = delta1;
+ min2 = delta2;
+ goto good_exit;
+ }
+ }
+ }
+ }
+
+good_exit:
+ measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
+ return GOOD;
+}
+
+
+/*
+ * Clockdiff computes the difference between the time of the machine on
+ * which it is called and the time of the machines given as argument.
+ * The time differences measured by clockdiff are obtained using a sequence
+ * of ICMP TSTAMP messages which are returned to the sender by the IP module
+ * in the remote machine.
+ * In order to compare clocks of machines in different time zones, the time
+ * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
+ * If a hosts uses a different time format, it should set the high order
+ * bit of the 32-bit quantity it transmits.
+ * However, VMS apparently transmits the time in milliseconds since midnight
+ * local time (rather than GMT) without setting the high order bit.
+ * Furthermore, it does not understand daylight-saving time. This makes
+ * clockdiff behaving inconsistently with hosts running VMS.
+ *
+ * In order to reduce the sensitivity to the variance of message transmission
+ * time, clockdiff sends a sequence of messages. Yet, measures between
+ * two `distant' hosts can be affected by a small error. The error can, however,
+ * be reduced by increasing the number of messages sent in each measurement.
+ */
+
+void
+usage() {
+ fprintf(stderr, "Usage: clockdiff [-o] <host>\n");
+ exit(1);
+}
+
+void drop_rights(void) {
+#ifdef CAPABILITIES
+ cap_t caps = cap_init();
+ if (cap_set_proc(caps)) {
+ perror("clockdiff: cap_set_proc");
+ exit(-1);
+ }
+ cap_free(caps);
+#endif
+ if (setuid(getuid())) {
+ perror("clockdiff: setuid");
+ exit(-1);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int measure_status;
+ struct hostent * hp;
+ char hostname[MAX_HOSTNAMELEN];
+ int s_errno = 0;
+ int n_errno = 0;
+
+ if (argc < 2) {
+ drop_rights();
+ usage();
+ }
+
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ s_errno = errno;
+
+ errno = 0;
+ if (nice(-16) == -1)
+ n_errno = errno;
+ drop_rights();
+
+ if (argc == 3) {
+ if (strcmp(argv[1], "-o") == 0) {
+ ip_opt_len = 4 + 4*8;
+ argv++;
+ } else if (strcmp(argv[1], "-o1") == 0) {
+ ip_opt_len = 4 + 3*8;
+ argv++;
+ } else
+ usage();
+ } else if (argc != 2)
+ usage();
+
+ if (sock_raw < 0) {
+ errno = s_errno;
+ perror("clockdiff: socket");
+ exit(1);
+ }
+
+ if (n_errno < 0) {
+ errno = n_errno;
+ perror("clockdiff: nice");
+ exit(1);
+ }
+
+ if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
+ interactive = 1;
+
+ id = getpid();
+
+ (void)gethostname(hostname,sizeof(hostname));
+ hp = gethostbyname(hostname);
+ if (hp == NULL) {
+ fprintf(stderr, "clockdiff: %s: my host not found\n", hostname);
+ exit(1);
+ }
+ myname = strdup(hp->h_name);
+
+ hp = gethostbyname(argv[1]);
+ if (hp == NULL) {
+ fprintf(stderr, "clockdiff: %s: host not found\n", argv[1]);
+ exit(1);
+ }
+ hisname = strdup(hp->h_name);
+
+ memset(&server, 0, sizeof(server));
+ server.sin_family = hp->h_addrtype;
+ memcpy(&(server.sin_addr.s_addr), hp->h_addr, 4);
+
+ if (connect(sock_raw, (struct sockaddr*)&server, sizeof(server)) == -1) {
+ perror("connect");
+ exit(1);
+ }
+ if (ip_opt_len) {
+ struct sockaddr_in myaddr;
+ socklen_t addrlen = sizeof(myaddr);
+ unsigned char rspace[ip_opt_len];
+
+ memset(rspace, 0, sizeof(rspace));
+ rspace[0] = IPOPT_TIMESTAMP;
+ rspace[1] = ip_opt_len;
+ rspace[2] = 5;
+ rspace[3] = IPOPT_TS_PRESPEC;
+ if (getsockname(sock_raw, (struct sockaddr*)&myaddr, &addrlen) == -1) {
+ perror("getsockname");
+ exit(1);
+ }
+ ((__u32*)(rspace+4))[0*2] = myaddr.sin_addr.s_addr;
+ ((__u32*)(rspace+4))[1*2] = server.sin_addr.s_addr;
+ ((__u32*)(rspace+4))[2*2] = myaddr.sin_addr.s_addr;
+ if (ip_opt_len == 4+4*8) {
+ ((__u32*)(rspace+4))[2*2] = server.sin_addr.s_addr;
+ ((__u32*)(rspace+4))[3*2] = myaddr.sin_addr.s_addr;
+ }
+
+ if (setsockopt(sock_raw, IPPROTO_IP, IP_OPTIONS, rspace, ip_opt_len) < 0) {
+ perror("ping: IP_OPTIONS (fallback to icmp tstamps)");
+ ip_opt_len = 0;
+ }
+ }
+
+ if ((measure_status = (ip_opt_len ? measure_opt : measure)(&server)) < 0) {
+ if (errno)
+ perror("measure");
+ else
+ fprintf(stderr, "measure: unknown failure\n");
+ exit(1);
+ }
+
+ switch (measure_status) {
+ case HOSTDOWN:
+ fprintf(stderr, "%s is down\n", hisname);
+ exit(1);
+ case NONSTDTIME:
+ fprintf(stderr, "%s time transmitted in a non-standard format\n", hisname);
+ exit(1);
+ case UNREACHABLE:
+ fprintf(stderr, "%s is unreachable\n", hisname);
+ exit(1);
+ default:
+ break;
+ }
+
+
+ {
+ time_t now = time(NULL);
+
+ if (interactive)
+ printf("\nhost=%s rtt=%ld(%ld)ms/%ldms delta=%dms/%dms %s", hisname,
+ rtt, rtt_sigma, min_rtt,
+ measure_delta, measure_delta1,
+ ctime(&now));
+ else
+ printf("%ld %d %d\n", now, measure_delta, measure_delta1);
+ }
+ exit(0);
+}