1#include <time.h>
2#include <sys/types.h>
3#include <sys/param.h>
4#include <stdio.h>
5#include <unistd.h>
6#include <stdlib.h>
7#include <math.h>
8#include <string.h>
9#include <sys/time.h>
10#include <sys/timex.h>
11#include <errno.h>
12#include <sys/socket.h>
13#include <netinet/in.h>
14#include <netinet/ip.h>
15#include <netinet/ip_icmp.h>
16#define TSPTYPES
17#include <protocols/timed.h>
18#include <fcntl.h>
19#include <netdb.h>
20#include <arpa/inet.h>
21#include <errno.h>
22
23void usage(void) __attribute__((noreturn));
24
25
26/*
27 * Checksum routine for Internet Protocol family headers.
28 *
29 * This routine is very heavily used in the network
30 * code and should be modified for each CPU to be as fast as possible.
31 *
32 * This implementation is TAHOE version.
33 */
34
35#undef	ADDCARRY
36#define ADDCARRY(sum) { \
37	if (sum & 0xffff0000) {	\
38		sum &= 0xffff; \
39		sum++; \
40	} \
41}
42
43int in_cksum(u_short *addr, int len)
44{
45	union word {
46		char	c[2];
47		u_short	s;
48	} u;
49	int sum = 0;
50
51	while (len > 0) {
52		/*
53		 * add by words.
54		 */
55		while ((len -= 2) >= 0) {
56			if ((unsigned long)addr & 0x1) {
57				/* word is not aligned */
58				u.c[0] = *(char *)addr;
59				u.c[1] = *((char *)addr+1);
60				sum += u.s;
61				addr++;
62			} else
63				sum += *addr++;
64			ADDCARRY(sum);
65		}
66		if (len == -1)
67			/*
68			 * Odd number of bytes.
69			 */
70			u.c[0] = *(u_char *)addr;
71	}
72	if (len == -1) {
73		/* The last mbuf has odd # of bytes. Follow the
74		   standard (the odd byte is shifted left by 8 bits) */
75		u.c[1] = 0;
76		sum += u.s;
77		ADDCARRY(sum);
78	}
79	return (~sum & 0xffff);
80}
81
82#define ON		1
83#define OFF		0
84
85#define RANGE		1		/* best expected round-trip time, ms */
86#define MSGS 		50
87#define TRIALS		10
88
89#define GOOD		0
90#define UNREACHABLE	2
91#define NONSTDTIME	3
92#define HOSTDOWN 	0x7fffffff
93
94
95int interactive = 0;
96int id;
97int sock;
98int sock_raw;
99struct sockaddr_in server;
100int ip_opt_len = 0;
101
102#define BIASP	 	43199999
103#define BIASN		-43200000
104#define MODULO	 	86400000
105#define PROCESSING_TIME	0 	/* ms. to reduce error in measurement */
106
107#define PACKET_IN	1024
108
109int measure_delta;
110int measure_delta1;
111static u_short seqno, seqno0, acked;
112long rtt = 1000;
113long min_rtt;
114long rtt_sigma = 0;
115
116/*
117 * Measures the differences between machines' clocks using
118 * ICMP timestamp messages.
119 */
120int
121measure(struct sockaddr_in * addr)
122{
123	int length;
124	int msgcount;
125	int cc, count;
126	fd_set ready;
127	long sendtime, recvtime, histime,  histime1;
128	long min1, min2, diff;
129	long delta1, delta2;
130	struct timeval tv1, tout;
131	u_char packet[PACKET_IN], opacket[64];
132	struct icmphdr *icp = (struct icmphdr *) packet;
133	struct icmphdr *oicp = (struct icmphdr *) opacket;
134	struct iphdr *ip = (struct iphdr *) packet;
135
136	min1 = min2 = 0x7fffffff;
137	min_rtt = 0x7fffffff;
138	measure_delta = HOSTDOWN;
139	measure_delta1 = HOSTDOWN;
140
141/* empties the icmp input queue */
142	FD_ZERO(&ready);
143
144empty:
145	tout.tv_sec = tout.tv_usec = 0;
146	FD_SET(sock_raw, &ready);
147	if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
148		length = sizeof(struct sockaddr_in);
149		cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
150		    (struct sockaddr *)NULL, &length);
151		if (cc < 0)
152			return -1;
153		goto empty;
154	}
155
156	/*
157	 * To measure the difference, select MSGS messages whose round-trip
158	 * time is smaller than RANGE if ckrange is 1, otherwise simply
159	 * select MSGS messages regardless of round-trip transmission time.
160	 * Choose the smallest transmission time in each of the two directions.
161	 * Use these two latter quantities to compute the delta between
162	 * the two clocks.
163	 */
164
165	length = sizeof(struct sockaddr_in);
166	oicp->type = ICMP_TIMESTAMP;
167	oicp->code = 0;
168	oicp->checksum = 0;
169	oicp->un.echo.id = id;
170	((__u32*)(oicp+1))[0] = 0;
171	((__u32*)(oicp+1))[1] = 0;
172	((__u32*)(oicp+1))[2] = 0;
173	FD_ZERO(&ready);
174	msgcount = 0;
175
176        acked = seqno = seqno0 = 0;
177
178	for (msgcount = 0; msgcount < MSGS; ) {
179
180	/*
181	 * If no answer is received for TRIALS consecutive times,
182	 * the machine is assumed to be down
183	 */
184	        if (seqno - acked > TRIALS)
185			return HOSTDOWN;
186
187		oicp->un.echo.sequence = ++seqno;
188		oicp->checksum = 0;
189
190    		(void)gettimeofday (&tv1, (struct timezone *)0);
191		*(__u32*)(oicp+1) = htonl((tv1.tv_sec % (24*60*60)) * 1000
192					  + tv1.tv_usec / 1000);
193		oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp) + 12);
194
195		count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
196			       (struct sockaddr *)addr, sizeof(struct sockaddr_in));
197
198		if (count < 0)
199			return UNREACHABLE;
200
201		for (;;) {
202		        FD_ZERO(&ready);
203			FD_SET(sock_raw, &ready);
204			{
205                          long tmo = rtt + rtt_sigma;
206		          tout.tv_sec =  tmo/1000;
207		          tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
208                        }
209
210			if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
211			    (fd_set *)0, &tout)) <= 0)
212				goto send_next;
213
214			(void)gettimeofday(&tv1, (struct timezone *)0);
215			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
216			    (struct sockaddr *)NULL, &length);
217
218			if (cc < 0)
219				return(-1);
220
221			icp = (struct icmphdr *)(packet + (ip->ihl << 2));
222			if( icp->type == ICMP_TIMESTAMPREPLY &&
223			    icp->un.echo.id == id && icp->un.echo.sequence >= seqno0 &&
224                                                  icp->un.echo.sequence <= seqno) {
225			  if (acked < icp->un.echo.sequence)
226			    acked = icp->un.echo.sequence;
227
228		          recvtime = (tv1.tv_sec % (24*60*60)) * 1000 +
229		                     tv1.tv_usec / 1000;
230                          sendtime = ntohl(*(__u32*)(icp+1));
231		          diff = recvtime - sendtime;
232		/*
233		 * diff can be less than 0 aroud midnight
234		 */
235		          if (diff < 0)
236			    continue;
237			  rtt = (rtt * 3 + diff)/4;
238			  rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
239		          msgcount++;
240		          histime = ntohl(((__u32*)(icp+1))[1]);
241		          histime1 = ntohl(((__u32*)(icp+1))[2]);
242		/*
243		 * a hosts using a time format different from
244		 * ms. since midnight UT (as per RFC792) should
245		 * set the high order bit of the 32-bit time
246		 * value it transmits.
247		 */
248		        if ((histime & 0x80000000) != 0)
249			  return NONSTDTIME;
250
251			if (interactive) {
252			  printf(".");
253			  fflush(stdout);
254			}
255
256			delta1 = histime - sendtime;
257		/*
258		 * Handles wrap-around to avoid that around
259		 * midnight small time differences appear
260		 * enormous. However, the two machine's clocks
261		 * must be within 12 hours from each other.
262		 */
263        		if (delta1 < BIASN)
264        			delta1 += MODULO;
265        		else if (delta1 > BIASP)
266        			delta1 -= MODULO;
267
268        		delta2 = recvtime - histime;
269        		if (delta2 < BIASN)
270        			delta2 += MODULO;
271        		else if (delta2 > BIASP)
272        			delta2 -= MODULO;
273
274        		if (delta1 < min1)
275        			min1 = delta1;
276        		if (delta2 < min2)
277        			min2 = delta2;
278			if (delta1 + delta2 < min_rtt) {
279			  min_rtt  = delta1 + delta2;
280			  measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
281			}
282        		if (diff < RANGE) {
283        			min1 = delta1;
284        			min2 = delta2;
285        			goto good_exit;
286        		}
287                      }
288		}
289send_next: ;
290	}
291
292good_exit:
293	measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
294	return GOOD;
295}
296
297char *myname, *hisname;
298
299int
300measure_opt(struct sockaddr_in * addr)
301{
302	int length;
303	int msgcount;
304	int cc, count;
305	fd_set ready;
306	long sendtime, recvtime, histime, histime1;
307	long min1, min2, diff;
308	long delta1, delta2;
309	struct timeval tv1, tout;
310	u_char packet[PACKET_IN], opacket[64];
311	struct icmphdr *icp = (struct icmphdr *) packet;
312	struct icmphdr *oicp = (struct icmphdr *) opacket;
313	struct iphdr *ip = (struct iphdr *) packet;
314
315	min1 = min2 = 0x7fffffff;
316	min_rtt = 0x7fffffff;
317	measure_delta = HOSTDOWN;
318	measure_delta1 = HOSTDOWN;
319
320/* empties the icmp input queue */
321	FD_ZERO(&ready);
322empty:
323	tout.tv_sec = tout.tv_usec = 0;
324	FD_SET(sock_raw, &ready);
325	if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
326		length = sizeof(struct sockaddr_in);
327		cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
328		    (struct sockaddr *)NULL, &length);
329		if (cc < 0)
330			return -1;
331		goto empty;
332	}
333
334	/*
335	 * To measure the difference, select MSGS messages whose round-trip
336	 * time is smaller than RANGE if ckrange is 1, otherwise simply
337	 * select MSGS messages regardless of round-trip transmission time.
338	 * Choose the smallest transmission time in each of the two directions.
339	 * Use these two latter quantities to compute the delta between
340	 * the two clocks.
341	 */
342
343	length = sizeof(struct sockaddr_in);
344	oicp->type = ICMP_ECHO;
345	oicp->code = 0;
346	oicp->checksum = 0;
347	oicp->un.echo.id = id;
348	((__u32*)(oicp+1))[0] = 0;
349	((__u32*)(oicp+1))[1] = 0;
350	((__u32*)(oicp+1))[2] = 0;
351
352	FD_ZERO(&ready);
353	msgcount = 0;
354
355        acked = seqno = seqno0 = 0;
356
357	for (msgcount = 0; msgcount < MSGS; ) {
358
359	/*
360	 * If no answer is received for TRIALS consecutive times,
361	 * the machine is assumed to be down
362	 */
363	        if ( seqno - acked > TRIALS) {
364			errno = EHOSTDOWN;
365			return HOSTDOWN;
366                }
367		oicp->un.echo.sequence = ++seqno;
368		oicp->checksum = 0;
369
370    		gettimeofday (&tv1, NULL);
371		((__u32*)(oicp+1))[0] = htonl((tv1.tv_sec % (24*60*60)) * 1000
372					      + tv1.tv_usec / 1000);
373		oicp->checksum = in_cksum((u_short *)oicp, sizeof(*oicp)+12);
374
375		count = sendto(sock_raw, (char *)opacket, sizeof(*oicp)+12, 0,
376			       (struct sockaddr *)addr, sizeof(struct sockaddr_in));
377
378		if (count < 0) {
379			errno = EHOSTUNREACH;
380			return UNREACHABLE;
381		}
382
383		for (;;) {
384		        FD_ZERO(&ready);
385			FD_SET(sock_raw, &ready);
386			{
387				long tmo = rtt + rtt_sigma;
388				tout.tv_sec =  tmo/1000;
389				tout.tv_usec = (tmo - (tmo/1000)*1000)*1000;
390                        }
391
392			if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
393			    (fd_set *)0, &tout)) <= 0)
394				goto send_next;
395
396			(void)gettimeofday(&tv1, (struct timezone *)0);
397			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
398				      (struct sockaddr *)NULL, &length);
399
400			if (cc < 0)
401				return(-1);
402
403			icp = (struct icmphdr *)(packet + (ip->ihl << 2));
404			if (icp->type == ICMP_ECHOREPLY &&
405			    packet[20] == IPOPT_TIMESTAMP &&
406			    icp->un.echo.id == id &&
407			    icp->un.echo.sequence >= seqno0 &&
408			    icp->un.echo.sequence <= seqno) {
409				int i;
410				__u8 *opt = packet+20;
411
412				if (acked < icp->un.echo.sequence)
413					acked = icp->un.echo.sequence;
414				if ((opt[3]&0xF) != IPOPT_TS_PRESPEC) {
415					fprintf(stderr, "Wrong timestamp %d\n", opt[3]&0xF);
416					return NONSTDTIME;
417				}
418				if (opt[3]>>4) {
419					if ((opt[3]>>4) != 1 || ip_opt_len != 4+3*8)
420						fprintf(stderr, "Overflow %d hops\n", opt[3]>>4);
421				}
422				sendtime = recvtime = histime = histime1 = 0;
423				for (i=0; i < (opt[2]-5)/8; i++) {
424					__u32 *timep = (__u32*)(opt+4+i*8+4);
425					__u32 t = ntohl(*timep);
426
427					if (t & 0x80000000)
428						return NONSTDTIME;
429
430					if (i == 0)
431						sendtime = t;
432					if (i == 1)
433						histime = histime1 = t;
434					if (i == 2) {
435						if (ip_opt_len == 4+4*8)
436							histime1 = t;
437						else
438							recvtime = t;
439					}
440					if (i == 3)
441						recvtime = t;
442				}
443
444				if (!(sendtime&histime&histime1&recvtime)) {
445					fprintf(stderr, "wrong timestamps\n");
446					return -1;
447				}
448
449				diff = recvtime - sendtime;
450				/*
451				 * diff can be less than 0 aroud midnight
452				 */
453				if (diff < 0)
454					continue;
455				rtt = (rtt * 3 + diff)/4;
456				rtt_sigma = (rtt_sigma *3 + abs(diff-rtt))/4;
457				msgcount++;
458
459				if (interactive) {
460					printf(".");
461					fflush(stdout);
462				}
463
464				delta1 = histime - sendtime;
465				/*
466				 * Handles wrap-around to avoid that around
467				 * midnight small time differences appear
468				 * enormous. However, the two machine's clocks
469				 * must be within 12 hours from each other.
470				 */
471				if (delta1 < BIASN)
472					delta1 += MODULO;
473				else if (delta1 > BIASP)
474					delta1 -= MODULO;
475
476				delta2 = recvtime - histime1;
477				if (delta2 < BIASN)
478					delta2 += MODULO;
479				else if (delta2 > BIASP)
480					delta2 -= MODULO;
481
482				if (delta1 < min1)
483					min1 = delta1;
484				if (delta2 < min2)
485					min2 = delta2;
486				if (delta1 + delta2 < min_rtt) {
487					min_rtt  = delta1 + delta2;
488					measure_delta1 = (delta1 - delta2)/2 + PROCESSING_TIME;
489				}
490				if (diff < RANGE) {
491					min1 = delta1;
492					min2 = delta2;
493					goto good_exit;
494				}
495			}
496		}
497send_next: ;
498	}
499
500good_exit:
501	measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
502	return GOOD;
503}
504
505
506/*
507 * Clockdiff computes the difference between the time of the machine on
508 * which it is called and the time of the machines given as argument.
509 * The time differences measured by clockdiff are obtained using a sequence
510 * of ICMP TSTAMP messages which are returned to the sender by the IP module
511 * in the remote machine.
512 * In order to compare clocks of machines in different time zones, the time
513 * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
514 * If a hosts uses a different time format, it should set the high order
515 * bit of the 32-bit quantity it transmits.
516 * However, VMS apparently transmits the time in milliseconds since midnight
517 * local time (rather than GMT) without setting the high order bit.
518 * Furthermore, it does not understand daylight-saving time.  This makes
519 * clockdiff behaving inconsistently with hosts running VMS.
520 *
521 * In order to reduce the sensitivity to the variance of message transmission
522 * time, clockdiff sends a sequence of messages.  Yet, measures between
523 * two `distant' hosts can be affected by a small error. The error can, however,
524 * be reduced by increasing the number of messages sent in each measurement.
525 */
526
527void
528usage() {
529  fprintf(stderr, "Usage: clockdiff [-o] <host>\n");
530  exit(1);
531}
532
533
534int
535main(int argc, char *argv[])
536{
537	int measure_status;
538	struct hostent * hp;
539	char hostname[MAXHOSTNAMELEN];
540	int s_errno = 0;
541
542	if (argc < 2) {
543		setuid(getuid());
544		usage();
545	}
546
547	sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
548	s_errno = errno;
549
550	setuid(getuid());
551
552	if (argc == 3) {
553		if (strcmp(argv[1], "-o") == 0) {
554			ip_opt_len = 4 + 4*8;
555			argv++;
556		} else if (strcmp(argv[1], "-o1") == 0) {
557			ip_opt_len = 4 + 3*8;
558			argv++;
559		} else
560			usage();
561	} else if (argc != 2)
562		usage();
563
564	if (sock_raw < 0)  {
565		errno = s_errno;
566		perror("clockdiff: socket");
567		exit(1);
568	}
569
570	if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
571		interactive = 1;
572
573	id = getpid();
574
575	(void)gethostname(hostname,sizeof(hostname));
576	hp = gethostbyname(hostname);
577	if (hp == NULL) {
578		fprintf(stderr, "clockdiff: %s: my host not found\n", hostname);
579		exit(1);
580	}
581	myname = strdup(hp->h_name);
582
583	hp = gethostbyname(argv[1]);
584	if (hp == NULL) {
585		fprintf(stderr, "clockdiff: %s: host not found\n", argv[1]);
586		exit(1);
587	}
588	hisname = strdup(hp->h_name);
589
590	memset(&server, 0, sizeof(server));
591	server.sin_family = hp->h_addrtype;
592	bcopy(hp->h_addr, &(server.sin_addr.s_addr), 4);
593
594	if (connect(sock_raw, (struct sockaddr*)&server, sizeof(server)) == -1) {
595		perror("connect");
596		exit(1);
597	}
598	if (ip_opt_len) {
599		struct sockaddr_in myaddr;
600		int addrlen = sizeof(myaddr);
601		unsigned char rspace[ip_opt_len];
602
603	        bzero(rspace, sizeof(rspace));
604		rspace[0] = IPOPT_TIMESTAMP;
605		rspace[1] = ip_opt_len;
606		rspace[2] = 5;
607		rspace[3] = IPOPT_TS_PRESPEC;
608		if (getsockname(sock_raw, (struct sockaddr*)&myaddr, &addrlen) == -1) {
609			perror("getsockname");
610			exit(1);
611		}
612		((__u32*)(rspace+4))[0*2] = myaddr.sin_addr.s_addr;
613		((__u32*)(rspace+4))[1*2] = server.sin_addr.s_addr;
614		((__u32*)(rspace+4))[2*2] = myaddr.sin_addr.s_addr;
615		if (ip_opt_len == 4+4*8) {
616			((__u32*)(rspace+4))[2*2] = server.sin_addr.s_addr;
617			((__u32*)(rspace+4))[3*2] = myaddr.sin_addr.s_addr;
618		}
619
620		if (setsockopt(sock_raw, IPPROTO_IP, IP_OPTIONS, rspace, ip_opt_len) < 0) {
621			perror("ping: IP_OPTIONS (fallback to icmp tstamps)");
622			ip_opt_len = 0;
623		}
624	}
625
626	nice(-16);
627
628	if ((measure_status = (ip_opt_len ? measure_opt : measure)(&server)) < 0) {
629		if (errno)
630			perror("measure");
631		else
632			fprintf(stderr, "measure: unknown failure\n");
633		exit(1);
634	}
635
636	switch (measure_status) {
637	case HOSTDOWN:
638		fprintf(stderr, "%s is down\n", hisname);
639		exit(1);
640	case NONSTDTIME:
641		fprintf(stderr, "%s time transmitted in a non-standard format\n", hisname);
642		exit(1);
643	case UNREACHABLE:
644		fprintf(stderr, "%s is unreachable\n", hisname);
645		exit(1);
646	default:
647		break;
648	}
649
650
651	{
652		time_t now = time(NULL);
653
654		if (interactive)
655			printf("\nhost=%s rtt=%ld(%ld)ms/%ldms delta=%dms/%dms %s", hisname,
656			       rtt, rtt_sigma, min_rtt,
657			       measure_delta, measure_delta1,
658			       ctime(&now));
659		else
660			printf("%ld %d %d\n", now, measure_delta, measure_delta1);
661	}
662	exit(0);
663}
664