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);
+}