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