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