Home | History | Annotate | Download | only in iputils
      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 
     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
    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
    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
    530 usage() {
    531   fprintf(stderr, "Usage: clockdiff [-o] <host>\n");
    532   exit(1);
    533 }
    534 
    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
    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