1 /* 2 * ntp_util.c - stuff I didn't have any other place for 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #include "ntpd.h" 10 #include "ntp_io.h" 11 #include "ntp_unixtime.h" 12 #include "ntp_filegen.h" 13 #include "ntp_if.h" 14 #include "ntp_stdlib.h" 15 16 #include <stdio.h> 17 #include <ctype.h> 18 #include <sys/types.h> 19 #ifdef HAVE_SYS_IOCTL_H 20 # include <sys/ioctl.h> 21 #endif 22 23 #ifdef HAVE_IEEEFP_H 24 # include <ieeefp.h> 25 #endif 26 #ifdef HAVE_MATH_H 27 # include <math.h> 28 #endif 29 30 #ifdef DOSYNCTODR 31 # if !defined(VMS) 32 # include <sys/resource.h> 33 # endif /* VMS */ 34 #endif 35 36 #if defined(VMS) 37 # include <descrip.h> 38 #endif /* VMS */ 39 40 /* 41 * This contains odds and ends. Right now the only thing you'll find 42 * in here is the hourly stats printer and some code to support 43 * rereading the keys file, but I may eventually put other things in 44 * here such as code to do something with the leap bits. 45 */ 46 /* 47 * Name of the keys file 48 */ 49 static char *key_file_name; 50 51 /* 52 * The name of the drift_comp file and the temporary. 53 */ 54 static char *stats_drift_file; 55 static char *stats_temp_file; 56 int stats_write_period = 3600; /* # of seconds between writes. */ 57 double stats_write_tolerance = 0; 58 static double prev_drift_comp = 99999.; 59 60 /* 61 * Statistics file stuff 62 */ 63 #ifndef NTP_VAR 64 # ifndef SYS_WINNT 65 # define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ 66 # else 67 # define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */ 68 # endif /* SYS_WINNT */ 69 #endif 70 71 #ifndef MAXPATHLEN 72 # define MAXPATHLEN 256 73 #endif 74 75 static char statsdir[MAXPATHLEN] = NTP_VAR; 76 77 static FILEGEN peerstats; 78 static FILEGEN loopstats; 79 static FILEGEN clockstats; 80 static FILEGEN rawstats; 81 static FILEGEN sysstats; 82 #ifdef DEBUG_TIMING 83 static FILEGEN timingstats; 84 #endif 85 #ifdef OPENSSL 86 static FILEGEN cryptostats; 87 #endif /* OPENSSL */ 88 89 /* 90 * This controls whether stats are written to the fileset. Provided 91 * so that ntpdc can turn off stats when the file system fills up. 92 */ 93 int stats_control; 94 95 /* 96 * Initial frequency offset later passed to the loopfilter. 97 */ 98 double old_drift; 99 100 /* 101 * init_util - initialize the utilities 102 */ 103 void 104 init_util(void) 105 { 106 stats_drift_file = 0; 107 stats_temp_file = 0; 108 key_file_name = 0; 109 110 filegen_register(&statsdir[0], "peerstats", &peerstats); 111 112 filegen_register(&statsdir[0], "loopstats", &loopstats); 113 114 filegen_register(&statsdir[0], "clockstats", &clockstats); 115 116 filegen_register(&statsdir[0], "rawstats", &rawstats); 117 118 filegen_register(&statsdir[0], "sysstats", &sysstats); 119 120 #ifdef OPENSSL 121 filegen_register(&statsdir[0], "cryptostats", &cryptostats); 122 #endif /* OPENSSL */ 123 124 #ifdef DEBUG_TIMING 125 filegen_register(&statsdir[0], "timingstats", &timingstats); 126 #endif 127 } 128 129 130 /* 131 * hourly_stats - print some interesting stats 132 */ 133 void 134 write_stats(void) 135 { 136 FILE *fp; 137 138 #ifdef DOSYNCTODR 139 struct timeval tv; 140 #if !defined(VMS) 141 int prio_set; 142 #endif 143 #ifdef HAVE_GETCLOCK 144 struct timespec ts; 145 #endif 146 int o_prio; 147 148 /* 149 * Sometimes having a Sun can be a drag. 150 * 151 * The kernel variable dosynctodr controls whether the system's 152 * soft clock is kept in sync with the battery clock. If it 153 * is zero, then the soft clock is not synced, and the battery 154 * clock is simply left to rot. That means that when the system 155 * reboots, the battery clock (which has probably gone wacky) 156 * sets the soft clock. That means ntpd starts off with a very 157 * confused idea of what time it is. It then takes a large 158 * amount of time to figure out just how wacky the battery clock 159 * has made things drift, etc, etc. The solution is to make the 160 * battery clock sync up to system time. The way to do THAT is 161 * to simply set the time of day to the current time of day, but 162 * as quickly as possible. This may, or may not be a sensible 163 * thing to do. 164 * 165 * CAVEAT: settimeofday() steps the sun clock by about 800 us, 166 * so setting DOSYNCTODR seems a bad idea in the 167 * case of us resolution 168 */ 169 170 #if !defined(VMS) 171 /* (prr) getpriority returns -1 on error, but -1 is also a valid 172 * return value (!), so instead we have to zero errno before the 173 * call and check it for non-zero afterwards. 174 */ 175 errno = 0; 176 prio_set = 0; 177 o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ 178 179 /* 180 * (prr) if getpriority succeeded, call setpriority to raise 181 * scheduling priority as high as possible. If that succeeds 182 * as well, set the prio_set flag so we remember to reset 183 * priority to its previous value below. Note that on Solaris 184 * 2.6 (and beyond?), both getpriority and setpriority will fail 185 * with ESRCH, because sched_setscheduler (called from main) put 186 * us in the real-time scheduling class which setpriority 187 * doesn't know about. Being in the real-time class is better 188 * than anything setpriority can do, anyhow, so this error is 189 * silently ignored. 190 */ 191 if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) 192 prio_set = 1; /* overdrive */ 193 #endif /* VMS */ 194 #ifdef HAVE_GETCLOCK 195 (void) getclock(TIMEOFDAY, &ts); 196 tv.tv_sec = ts.tv_sec; 197 tv.tv_usec = ts.tv_nsec / 1000; 198 #else /* not HAVE_GETCLOCK */ 199 GETTIMEOFDAY(&tv,(struct timezone *)NULL); 200 #endif /* not HAVE_GETCLOCK */ 201 if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) { 202 msyslog(LOG_ERR, "can't sync battery time: %m"); 203 } 204 #if !defined(VMS) 205 if (prio_set) 206 setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ 207 #endif /* VMS */ 208 #endif /* DOSYNCTODR */ 209 210 NLOG(NLOG_SYSSTATIST) 211 msyslog(LOG_INFO, 212 "offset %.6f sec freq %.3f ppm error %.6f poll %d", 213 last_offset, drift_comp * 1e6, sys_jitter, 214 sys_poll); 215 216 217 record_sys_stats(); 218 if ((u_long)(fabs(prev_drift_comp - drift_comp) * 1e9) <= 219 (u_long)(fabs(stats_write_tolerance * drift_comp) * 1e9)) { 220 return; 221 } 222 prev_drift_comp = drift_comp; 223 if (stats_drift_file != 0) { 224 if ((fp = fopen(stats_temp_file, "w")) == NULL) { 225 msyslog(LOG_ERR, "can't open %s: %m", 226 stats_temp_file); 227 return; 228 } 229 fprintf(fp, "%.3f\n", drift_comp * 1e6); 230 (void)fclose(fp); 231 /* atomic */ 232 #ifdef SYS_WINNT 233 (void) _unlink(stats_drift_file); /* rename semantics differ under NT */ 234 #endif /* SYS_WINNT */ 235 236 #ifndef NO_RENAME 237 (void) rename(stats_temp_file, stats_drift_file); 238 #else 239 /* we have no rename NFS of ftp in use */ 240 if ((fp = fopen(stats_drift_file, "w")) == NULL) { 241 msyslog(LOG_ERR, "can't open %s: %m", 242 stats_drift_file); 243 return; 244 } 245 246 #endif 247 248 #if defined(VMS) 249 /* PURGE */ 250 { 251 $DESCRIPTOR(oldvers,";-1"); 252 struct dsc$descriptor driftdsc = { 253 strlen(stats_drift_file),0,0,stats_drift_file }; 254 255 while(lib$delete_file(&oldvers,&driftdsc) & 1) ; 256 } 257 #endif 258 } 259 } 260 261 262 /* 263 * stats_config - configure the stats operation 264 */ 265 void 266 stats_config( 267 int item, 268 const char *invalue /* only one type so far */ 269 ) 270 { 271 FILE *fp; 272 const char *value; 273 int len; 274 275 /* 276 * Expand environment strings under Windows NT, since the 277 * command interpreter doesn't do this, the program must. 278 */ 279 #ifdef SYS_WINNT 280 char newvalue[MAX_PATH], parameter[MAX_PATH]; 281 282 if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) { 283 switch(item) { 284 case STATS_FREQ_FILE: 285 strcpy(parameter,"STATS_FREQ_FILE"); 286 break; 287 case STATS_STATSDIR: 288 strcpy(parameter,"STATS_STATSDIR"); 289 break; 290 case STATS_PID_FILE: 291 strcpy(parameter,"STATS_PID_FILE"); 292 break; 293 default: 294 strcpy(parameter,"UNKNOWN"); 295 break; 296 } 297 value = invalue; 298 299 msyslog(LOG_ERR, 300 "ExpandEnvironmentStrings(%s) failed: %m\n", parameter); 301 } else { 302 value = newvalue; 303 } 304 #else 305 value = invalue; 306 #endif /* SYS_WINNT */ 307 308 switch(item) { 309 case STATS_FREQ_FILE: 310 if (stats_drift_file != 0) { 311 (void) free(stats_drift_file); 312 (void) free(stats_temp_file); 313 stats_drift_file = 0; 314 stats_temp_file = 0; 315 } 316 317 if (value == 0 || (len = strlen(value)) == 0) 318 break; 319 320 stats_drift_file = (char*)emalloc((u_int)(len + 1)); 321 #if !defined(VMS) 322 stats_temp_file = (char*)emalloc((u_int)(len + 323 sizeof(".TEMP"))); 324 #else 325 stats_temp_file = (char*)emalloc((u_int)(len + 326 sizeof("-TEMP"))); 327 #endif /* VMS */ 328 memmove(stats_drift_file, value, (unsigned)(len+1)); 329 memmove(stats_temp_file, value, (unsigned)len); 330 #if !defined(VMS) 331 memmove(stats_temp_file + len, ".TEMP", 332 sizeof(".TEMP")); 333 #else 334 memmove(stats_temp_file + len, "-TEMP", 335 sizeof("-TEMP")); 336 #endif /* VMS */ 337 338 /* 339 * Open drift file and read frequency. If the file is 340 * missing or contains errors, tell the loop to reset. 341 */ 342 if ((fp = fopen(stats_drift_file, "r")) == NULL) { 343 old_drift = 1e9; 344 break; 345 } 346 if (fscanf(fp, "%lf", &old_drift) != 1) { 347 msyslog(LOG_ERR, "Frequency format error in %s", 348 stats_drift_file); 349 old_drift = 1e9; 350 fclose(fp); 351 break; 352 } 353 fclose(fp); 354 prev_drift_comp = old_drift / 1e6; 355 msyslog(LOG_INFO, 356 "frequency initialized %.3f PPM from %s", 357 old_drift, stats_drift_file); 358 break; 359 360 case STATS_STATSDIR: 361 if (strlen(value) >= sizeof(statsdir)) { 362 msyslog(LOG_ERR, 363 "value for statsdir too long (>%d, sigh)", 364 (int)sizeof(statsdir)-1); 365 } else { 366 l_fp now; 367 368 get_systime(&now); 369 strcpy(statsdir,value); 370 if(peerstats.prefix == &statsdir[0] && 371 peerstats.fp != NULL) { 372 fclose(peerstats.fp); 373 peerstats.fp = NULL; 374 filegen_setup(&peerstats, now.l_ui); 375 } 376 if(loopstats.prefix == &statsdir[0] && 377 loopstats.fp != NULL) { 378 fclose(loopstats.fp); 379 loopstats.fp = NULL; 380 filegen_setup(&loopstats, now.l_ui); 381 } 382 if(clockstats.prefix == &statsdir[0] && 383 clockstats.fp != NULL) { 384 fclose(clockstats.fp); 385 clockstats.fp = NULL; 386 filegen_setup(&clockstats, now.l_ui); 387 } 388 if(rawstats.prefix == &statsdir[0] && 389 rawstats.fp != NULL) { 390 fclose(rawstats.fp); 391 rawstats.fp = NULL; 392 filegen_setup(&rawstats, now.l_ui); 393 } 394 if(sysstats.prefix == &statsdir[0] && 395 sysstats.fp != NULL) { 396 fclose(sysstats.fp); 397 sysstats.fp = NULL; 398 filegen_setup(&sysstats, now.l_ui); 399 } 400 #ifdef OPENSSL 401 if(cryptostats.prefix == &statsdir[0] && 402 cryptostats.fp != NULL) { 403 fclose(cryptostats.fp); 404 cryptostats.fp = NULL; 405 filegen_setup(&cryptostats, now.l_ui); 406 } 407 #endif /* OPENSSL */ 408 } 409 break; 410 411 case STATS_PID_FILE: 412 if ((fp = fopen(value, "w")) == NULL) { 413 msyslog(LOG_ERR, "Can't open %s: %m", value); 414 break; 415 } 416 fprintf(fp, "%d", (int) getpid()); 417 fclose(fp);; 418 break; 419 420 default: 421 /* oh well */ 422 break; 423 } 424 } 425 426 /* 427 * record_peer_stats - write peer statistics to file 428 * 429 * file format: 430 * day (mjd) 431 * time (s past UTC midnight) 432 * peer (ip address) 433 * peer status word (hex) 434 * peer offset (s) 435 * peer delay (s) 436 * peer error bound (s) 437 * peer error (s) 438 */ 439 void 440 record_peer_stats( 441 struct sockaddr_storage *addr, 442 int status, 443 double offset, 444 double delay, 445 double dispersion, 446 double skew 447 ) 448 { 449 l_fp now; 450 u_long day; 451 452 if (!stats_control) 453 return; 454 455 get_systime(&now); 456 filegen_setup(&peerstats, now.l_ui); 457 day = now.l_ui / 86400 + MJD_1900; 458 now.l_ui %= 86400; 459 if (peerstats.fp != NULL) { 460 fprintf(peerstats.fp, 461 "%lu %s %s %x %.9f %.9f %.9f %.9f\n", 462 day, ulfptoa(&now, 3), stoa(addr), status, offset, 463 delay, dispersion, skew); 464 fflush(peerstats.fp); 465 } 466 } 467 468 /* 469 * record_loop_stats - write loop filter statistics to file 470 * 471 * file format: 472 * day (mjd) 473 * time (s past midnight) 474 * offset (s) 475 * frequency (approx ppm) 476 * time constant (log base 2) 477 */ 478 void 479 record_loop_stats( 480 double offset, 481 double freq, 482 double jitter, 483 double stability, 484 int spoll 485 ) 486 { 487 l_fp now; 488 u_long day; 489 490 if (!stats_control) 491 return; 492 493 get_systime(&now); 494 filegen_setup(&loopstats, now.l_ui); 495 day = now.l_ui / 86400 + MJD_1900; 496 now.l_ui %= 86400; 497 if (loopstats.fp != NULL) { 498 fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n", 499 day, ulfptoa(&now, 3), offset, freq * 1e6, jitter, 500 stability * 1e6, spoll); 501 fflush(loopstats.fp); 502 } 503 } 504 505 /* 506 * record_clock_stats - write clock statistics to file 507 * 508 * file format: 509 * day (mjd) 510 * time (s past midnight) 511 * peer (ip address) 512 * text message 513 */ 514 void 515 record_clock_stats( 516 struct sockaddr_storage *addr, 517 const char *text 518 ) 519 { 520 l_fp now; 521 u_long day; 522 523 if (!stats_control) 524 return; 525 526 get_systime(&now); 527 filegen_setup(&clockstats, now.l_ui); 528 day = now.l_ui / 86400 + MJD_1900; 529 now.l_ui %= 86400; 530 if (clockstats.fp != NULL) { 531 fprintf(clockstats.fp, "%lu %s %s %s\n", 532 day, ulfptoa(&now, 3), stoa(addr), text); 533 fflush(clockstats.fp); 534 } 535 } 536 537 /* 538 * record_raw_stats - write raw timestamps to file 539 * 540 * 541 * file format 542 * time (s past midnight) 543 * peer ip address 544 * local ip address 545 * t1 t2 t3 t4 timestamps 546 */ 547 void 548 record_raw_stats( 549 struct sockaddr_storage *srcadr, 550 struct sockaddr_storage *dstadr, 551 l_fp *t1, 552 l_fp *t2, 553 l_fp *t3, 554 l_fp *t4 555 ) 556 { 557 l_fp now; 558 u_long day; 559 560 if (!stats_control) 561 return; 562 563 get_systime(&now); 564 filegen_setup(&rawstats, now.l_ui); 565 day = now.l_ui / 86400 + MJD_1900; 566 now.l_ui %= 86400; 567 if (rawstats.fp != NULL) { 568 fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n", 569 day, ulfptoa(&now, 3), stoa(srcadr), dstadr ? stoa(dstadr) : "-", 570 ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9), 571 ulfptoa(t4, 9)); 572 fflush(rawstats.fp); 573 } 574 } 575 576 577 /* 578 * record_sys_stats - write system statistics to file 579 * 580 * file format 581 * time (s past midnight) 582 * time since startup (hr) 583 * packets recieved 584 * packets processed 585 * current version 586 * previous version 587 * bad version 588 * access denied 589 * bad length or format 590 * bad authentication 591 * rate exceeded 592 */ 593 void 594 record_sys_stats(void) 595 { 596 l_fp now; 597 u_long day; 598 599 if (!stats_control) 600 return; 601 602 get_systime(&now); 603 filegen_setup(&sysstats, now.l_ui); 604 day = now.l_ui / 86400 + MJD_1900; 605 now.l_ui %= 86400; 606 if (sysstats.fp != NULL) { 607 fprintf(sysstats.fp, 608 "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", 609 day, ulfptoa(&now, 3), sys_stattime / 3600, 610 sys_received, sys_processed, sys_newversionpkt, 611 sys_oldversionpkt, sys_unknownversion, 612 sys_restricted, sys_badlength, sys_badauth, 613 sys_limitrejected); 614 fflush(sysstats.fp); 615 proto_clr_stats(); 616 } 617 } 618 619 620 #ifdef OPENSSL 621 /* 622 * record_crypto_stats - write crypto statistics to file 623 * 624 * file format: 625 * day (mjd) 626 * time (s past midnight) 627 * peer (ip address) 628 * text message 629 */ 630 void 631 record_crypto_stats( 632 struct sockaddr_storage *addr, 633 const char *text 634 ) 635 { 636 l_fp now; 637 u_long day; 638 639 if (!stats_control) 640 return; 641 642 get_systime(&now); 643 filegen_setup(&cryptostats, now.l_ui); 644 day = now.l_ui / 86400 + MJD_1900; 645 now.l_ui %= 86400; 646 if (cryptostats.fp != NULL) { 647 if (addr == NULL) 648 fprintf(cryptostats.fp, "%lu %s %s\n", 649 day, ulfptoa(&now, 3), text); 650 else 651 fprintf(cryptostats.fp, "%lu %s %s %s\n", 652 day, ulfptoa(&now, 3), stoa(addr), text); 653 fflush(cryptostats.fp); 654 } 655 } 656 #endif /* OPENSSL */ 657 658 #ifdef DEBUG_TIMING 659 /* 660 * record_crypto_stats - write crypto statistics to file 661 * 662 * file format: 663 * day (mjd) 664 * time (s past midnight) 665 * text message 666 */ 667 void 668 record_timing_stats( 669 const char *text 670 ) 671 { 672 static unsigned int flshcnt; 673 l_fp now; 674 u_long day; 675 676 if (!stats_control) 677 return; 678 679 get_systime(&now); 680 filegen_setup(&timingstats, now.l_ui); 681 day = now.l_ui / 86400 + MJD_1900; 682 now.l_ui %= 86400; 683 if (timingstats.fp != NULL) { 684 fprintf(timingstats.fp, "%lu %s %s\n", 685 day, lfptoa(&now, 3), text); 686 if (++flshcnt % 100 == 0) 687 fflush(timingstats.fp); 688 } 689 } 690 #endif 691 /* 692 * getauthkeys - read the authentication keys from the specified file 693 */ 694 void 695 getauthkeys( 696 const char *keyfile 697 ) 698 { 699 int len; 700 701 len = strlen(keyfile); 702 if (len == 0) 703 return; 704 705 if (key_file_name != 0) { 706 if (len > (int)strlen(key_file_name)) { 707 (void) free(key_file_name); 708 key_file_name = 0; 709 } 710 } 711 712 if (key_file_name == 0) { 713 #ifndef SYS_WINNT 714 key_file_name = (char*)emalloc((u_int) (len + 1)); 715 #else 716 key_file_name = (char*)emalloc((u_int) (MAXPATHLEN)); 717 #endif 718 } 719 #ifndef SYS_WINNT 720 memmove(key_file_name, keyfile, (unsigned)(len+1)); 721 #else 722 if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN)) 723 { 724 msyslog(LOG_ERR, 725 "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n"); 726 } 727 #endif /* SYS_WINNT */ 728 729 authreadkeys(key_file_name); 730 } 731 732 733 /* 734 * rereadkeys - read the authentication key file over again. 735 */ 736 void 737 rereadkeys(void) 738 { 739 if (key_file_name != 0) 740 authreadkeys(key_file_name); 741 } 742 743 /* 744 * sock_hash - hash an sockaddr_storage structure 745 */ 746 int 747 sock_hash( 748 struct sockaddr_storage *addr 749 ) 750 { 751 int hashVal; 752 int i; 753 int len; 754 char *ch; 755 756 hashVal = 0; 757 len = 0; 758 /* 759 * We can't just hash the whole thing because there are hidden 760 * fields in sockaddr_in6 that might be filled in by recvfrom(), 761 * so just use the family, port and address. 762 */ 763 ch = (char *)&addr->ss_family; 764 hashVal = 37 * hashVal + (int)*ch; 765 if (sizeof(addr->ss_family) > 1) { 766 ch++; 767 hashVal = 37 * hashVal + (int)*ch; 768 } 769 switch(addr->ss_family) { 770 case AF_INET: 771 ch = (char *)&((struct sockaddr_in *)addr)->sin_addr; 772 len = sizeof(struct in_addr); 773 break; 774 case AF_INET6: 775 ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr; 776 len = sizeof(struct in6_addr); 777 break; 778 } 779 780 for (i = 0; i < len ; i++) 781 hashVal = 37 * hashVal + (int)*(ch + i); 782 783 hashVal = hashVal % 128; /* % MON_HASH_SIZE hardcoded */ 784 785 if (hashVal < 0) 786 hashVal += 128; 787 788 return hashVal; 789 } 790 791 #if notyet 792 /* 793 * ntp_exit - document explicitly that ntpd has exited 794 */ 795 void 796 ntp_exit(int retval) 797 { 798 msyslog(LOG_ERR, "EXITING with return code %d", retval); 799 exit(retval); 800 } 801 #endif 802