slave.c revision 86644
1/*- 2 * Copyright (c) 1985, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: head/usr.sbin/timed/timed/slave.c 86644 2001-11-20 06:36:09Z jhb $"; 40#endif /* not lint */ 41 42#include "globals.h" 43#include <setjmp.h> 44#include "pathnames.h" 45 46extern jmp_buf jmpenv; 47extern int Mflag; 48extern int justquit; 49 50extern u_short sequence; 51 52static char master_name[MAXHOSTNAMELEN]; 53static struct netinfo *old_slavenet; 54static int old_status; 55 56static void schgdate __P((struct tsp *, char *)); 57static void setmaster __P((struct tsp *)); 58static void answerdelay __P((void)); 59 60#ifdef sgi 61extern void logwtmp __P((struct timeval *, struct timeval *)); 62#else 63extern void logwtmp __P((char *, char *, char *)); 64#endif /* sgi */ 65 66int 67slave() 68{ 69 int tries; 70 long electiontime, refusetime, looktime, looptime, adjtime; 71 u_short seq; 72 long fastelection; 73#define FASTTOUT 3 74 struct in_addr cadr; 75 struct timeval otime; 76 struct sockaddr_in taddr; 77 char tname[MAXHOSTNAMELEN]; 78 struct tsp *msg, to; 79 struct timeval ntime, wait, tmptv; 80 time_t tsp_time_sec; 81 struct tsp *answer; 82 int timeout(); 83 char olddate[32]; 84 char newdate[32]; 85 struct netinfo *ntp; 86 struct hosttbl *htp; 87 88 89 old_slavenet = 0; 90 seq = 0; 91 refusetime = 0; 92 adjtime = 0; 93 94 (void)gettimeofday(&ntime, 0); 95 electiontime = ntime.tv_sec + delay2; 96 fastelection = ntime.tv_sec + FASTTOUT; 97 if (justquit) 98 looktime = electiontime; 99 else 100 looktime = fastelection; 101 looptime = fastelection; 102 103 if (slavenet) 104 xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr); 105 if (status & MASTER) { 106 for (ntp = nettab; ntp != NULL; ntp = ntp->next) { 107 if (ntp->status == MASTER) 108 masterup(ntp); 109 } 110 } 111 112loop: 113 get_goodgroup(0); 114 (void)gettimeofday(&ntime, (struct timezone *)0); 115 if (ntime.tv_sec > electiontime) { 116 if (trace) 117 fprintf(fd, "election timer expired\n"); 118 longjmp(jmpenv, 1); 119 } 120 121 if (ntime.tv_sec >= looktime) { 122 if (trace) 123 fprintf(fd, "Looking for nets to master\n"); 124 125 if (Mflag && nignorednets > 0) { 126 for (ntp = nettab; ntp != NULL; ntp = ntp->next) { 127 if (ntp->status == IGNORE 128 || ntp->status == NOMASTER) { 129 lookformaster(ntp); 130 if (ntp->status == MASTER) { 131 masterup(ntp); 132 } else if (ntp->status == MASTER) { 133 ntp->status = NOMASTER; 134 } 135 } 136 if (ntp->status == MASTER 137 && --ntp->quit_count < 0) 138 ntp->quit_count = 0; 139 } 140 makeslave(slavenet); /* prune extras */ 141 setstatus(); 142 } 143 (void)gettimeofday(&ntime, 0); 144 looktime = ntime.tv_sec + delay2; 145 } 146 if (ntime.tv_sec >= looptime) { 147 if (trace) 148 fprintf(fd, "Looking for loops\n"); 149 for (ntp = nettab; ntp != NULL; ntp = ntp->next) { 150 if (ntp->status == MASTER) { 151 to.tsp_type = TSP_LOOP; 152 to.tsp_vers = TSPVERSION; 153 to.tsp_seq = sequence++; 154 to.tsp_hopcnt = MAX_HOPCNT; 155 (void)strcpy(to.tsp_name, hostname); 156 bytenetorder(&to); 157 if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, 158 (struct sockaddr*)&ntp->dest_addr, 159 sizeof(ntp->dest_addr)) < 0) { 160 trace_sendto_err(ntp->dest_addr.sin_addr); 161 } 162 } 163 } 164 (void)gettimeofday(&ntime, 0); 165 looptime = ntime.tv_sec + delay2; 166 } 167 168 wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec; 169 if (wait.tv_sec < 0) 170 wait.tv_sec = 0; 171 wait.tv_sec += FASTTOUT; 172 wait.tv_usec = 0; 173 msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); 174 175 if (msg != NULL) { 176 /* 177 * filter stuff not for us 178 */ 179 switch (msg->tsp_type) { 180 case TSP_SETDATE: 181 case TSP_TRACEOFF: 182 case TSP_TRACEON: 183 /* 184 * XXX check to see they are from ourself 185 */ 186 break; 187 188 case TSP_TEST: 189 case TSP_MSITE: 190 break; 191 192 case TSP_MASTERUP: 193 if (!fromnet) { 194 if (trace) { 195 fprintf(fd, "slave ignored: "); 196 print(msg, &from); 197 } 198 goto loop; 199 } 200 break; 201 202 default: 203 if (!fromnet 204 || fromnet->status == IGNORE 205 || fromnet->status == NOMASTER) { 206 if (trace) { 207 fprintf(fd, "slave ignored: "); 208 print(msg, &from); 209 } 210 goto loop; 211 } 212 break; 213 } 214 215 216 /* 217 * now process the message 218 */ 219 switch (msg->tsp_type) { 220 221 case TSP_ADJTIME: 222 if (fromnet != slavenet) 223 break; 224 if (!good_host_name(msg->tsp_name)) { 225 syslog(LOG_NOTICE, 226 "attempted time adjustment by %s", 227 msg->tsp_name); 228 suppress(&from, msg->tsp_name, fromnet); 229 break; 230 } 231 /* 232 * Speed up loop detection in case we have a loop. 233 * Otherwise the clocks can race until the loop 234 * is found. 235 */ 236 (void)gettimeofday(&otime, 0); 237 if (adjtime < otime.tv_sec) 238 looptime -= (looptime-otime.tv_sec)/2 + 1; 239 240 setmaster(msg); 241 if (seq != msg->tsp_seq) { 242 seq = msg->tsp_seq; 243 synch(tvtomsround(msg->tsp_time)); 244 } 245 (void)gettimeofday(&ntime, 0); 246 electiontime = ntime.tv_sec + delay2; 247 fastelection = ntime.tv_sec + FASTTOUT; 248 adjtime = ntime.tv_sec + SAMPLEINTVL*2; 249 break; 250 251 case TSP_SETTIME: 252 if (fromnet != slavenet) 253 break; 254 if (seq == msg->tsp_seq) 255 break; 256 seq = msg->tsp_seq; 257 258 /* adjust time for residence on the queue */ 259 (void)gettimeofday(&otime, 0); 260 adj_msg_time(msg,&otime); 261#ifdef sgi 262 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); 263 (void)cftime(olddate, "%D %T", &otime.tv_sec); 264#else 265 /* 266 * the following line is necessary due to syslog 267 * calling ctime() which clobbers the static buffer 268 */ 269 (void)strcpy(olddate, date()); 270 tsp_time_sec = msg->tsp_time.tv_sec; 271 (void)strcpy(newdate, ctime(&tsp_time_sec)); 272#endif /* sgi */ 273 274 if (!good_host_name(msg->tsp_name)) { 275 syslog(LOG_NOTICE, 276 "attempted time setting by untrusted %s to %s", 277 msg->tsp_name, newdate); 278 suppress(&from, msg->tsp_name, fromnet); 279 break; 280 } 281 282 setmaster(msg); 283 tmptv.tv_sec = msg->tsp_time.tv_sec; 284 tmptv.tv_usec = msg->tsp_time.tv_usec; 285 timevalsub(&ntime, &tmptv, &otime); 286 if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { 287 /* 288 * do not change the clock if we can adjust it 289 */ 290 synch(tvtomsround(ntime)); 291 } else { 292#ifdef sgi 293 if (0 > settimeofday(&msg->tsp_time, 0)) { 294 syslog(LOG_ERR,"settimeofdate(): %m"); 295 break; 296 } 297 logwtmp(&otime, &msg->tsp_time); 298#else 299 logwtmp("|", "date", ""); 300 (void)settimeofday(&tmptv, 0); 301 logwtmp("{", "date", ""); 302#endif /* sgi */ 303 syslog(LOG_NOTICE, 304 "date changed by %s from %s", 305 msg->tsp_name, olddate); 306 if (status & MASTER) 307 spreadtime(); 308 } 309 (void)gettimeofday(&ntime, 0); 310 electiontime = ntime.tv_sec + delay2; 311 fastelection = ntime.tv_sec + FASTTOUT; 312 313/* This patches a bad protocol bug. Imagine a system with several networks, 314 * where there are a pair of redundant gateways between a pair of networks, 315 * each running timed. Assume that we start with a third machine mastering 316 * one of the networks, and one of the gateways mastering the other. 317 * Imagine that the third machine goes away and the non-master gateway 318 * decides to replace it. If things are timed just 'right,' we will have 319 * each gateway mastering one network for a little while. If a SETTIME 320 * message gets into the network at that time, perhaps from the newly 321 * masterful gateway as it was taking control, the SETTIME will loop 322 * forever. Each time a gateway receives it on its slave side, it will 323 * call spreadtime to forward it on its mastered network. We are now in 324 * a permanent loop, since the SETTIME msgs will keep any clock 325 * in the network from advancing. Normally, the 'LOOP' stuff will detect 326 * and correct the situation. However, with the clocks stopped, the 327 * 'looptime' timer cannot expire. While they are in this state, the 328 * masters will try to saturate the network with SETTIME packets. 329 */ 330 looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1; 331 break; 332 333 case TSP_MASTERUP: 334 if (slavenet && fromnet != slavenet) 335 break; 336 if (!good_host_name(msg->tsp_name)) { 337 suppress(&from, msg->tsp_name, fromnet); 338 if (electiontime > fastelection) 339 electiontime = fastelection; 340 break; 341 } 342 makeslave(fromnet); 343 setmaster(msg); 344 setstatus(); 345 answerdelay(); 346 xmit(TSP_SLAVEUP, 0, &from); 347 (void)gettimeofday(&ntime, 0); 348 electiontime = ntime.tv_sec + delay2; 349 fastelection = ntime.tv_sec + FASTTOUT; 350 refusetime = 0; 351 break; 352 353 case TSP_MASTERREQ: 354 if (fromnet->status != SLAVE) 355 break; 356 (void)gettimeofday(&ntime, 0); 357 electiontime = ntime.tv_sec + delay2; 358 break; 359 360 case TSP_SETDATE: 361#ifdef sgi 362 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); 363#else 364 tsp_time_sec = msg->tsp_time.tv_sec; 365 (void)strcpy(newdate, ctime(&tsp_time_sec)); 366#endif /* sgi */ 367 schgdate(msg, newdate); 368 break; 369 370 case TSP_SETDATEREQ: 371 if (fromnet->status != MASTER) 372 break; 373#ifdef sgi 374 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); 375#else 376 tsp_time_sec = msg->tsp_time.tv_sec; 377 (void)strcpy(newdate, ctime(&tsp_time_sec)); 378#endif /* sgi */ 379 htp = findhost(msg->tsp_name); 380 if (0 == htp) { 381 syslog(LOG_WARNING, 382 "DATEREQ from uncontrolled machine"); 383 break; 384 } 385 if (!htp->good) { 386 syslog(LOG_WARNING, 387 "attempted date change by untrusted %s to %s", 388 htp->name, newdate); 389 spreadtime(); 390 break; 391 } 392 schgdate(msg, newdate); 393 break; 394 395 case TSP_TRACEON: 396 traceon(); 397 break; 398 399 case TSP_TRACEOFF: 400 traceoff("Tracing ended at %s\n"); 401 break; 402 403 case TSP_SLAVEUP: 404 newslave(msg); 405 break; 406 407 case TSP_ELECTION: 408 if (fromnet->status == SLAVE) { 409 (void)gettimeofday(&ntime, 0); 410 electiontime = ntime.tv_sec + delay2; 411 fastelection = ntime.tv_sec + FASTTOUT; 412 seq = 0; 413 if (!good_host_name(msg->tsp_name)) { 414 syslog(LOG_NOTICE, 415 "suppress election of %s", 416 msg->tsp_name); 417 to.tsp_type = TSP_QUIT; 418 electiontime = fastelection; 419 } else if (cadr.s_addr != from.sin_addr.s_addr 420 && ntime.tv_sec < refusetime) { 421/* if the candidate has to repeat itself, the old code would refuse it 422 * the second time. That would prevent elections. 423 */ 424 to.tsp_type = TSP_REFUSE; 425 } else { 426 cadr.s_addr = from.sin_addr.s_addr; 427 to.tsp_type = TSP_ACCEPT; 428 refusetime = ntime.tv_sec + 30; 429 } 430 taddr = from; 431 (void)strcpy(tname, msg->tsp_name); 432 (void)strcpy(to.tsp_name, hostname); 433 answerdelay(); 434 if (!acksend(&to, &taddr, tname, 435 TSP_ACK, 0, 0)) 436 syslog(LOG_WARNING, 437 "no answer from candidate %s\n", 438 tname); 439 440 } else { /* fromnet->status == MASTER */ 441 htp = addmach(msg->tsp_name, &from,fromnet); 442 to.tsp_type = TSP_QUIT; 443 (void)strcpy(to.tsp_name, hostname); 444 if (!acksend(&to, &htp->addr, htp->name, 445 TSP_ACK, 0, htp->noanswer)) { 446 syslog(LOG_ERR, 447 "no reply from %s to ELECTION-QUIT", 448 htp->name); 449 (void)remmach(htp); 450 } 451 } 452 break; 453 454 case TSP_CONFLICT: 455 if (fromnet->status != MASTER) 456 break; 457 /* 458 * After a network partition, there can be 459 * more than one master: the first slave to 460 * come up will notify here the situation. 461 */ 462 (void)strcpy(to.tsp_name, hostname); 463 464 /* The other master often gets into the same state, 465 * with boring results. 466 */ 467 ntp = fromnet; /* (acksend() can leave fromnet=0 */ 468 for (tries = 0; tries < 3; tries++) { 469 to.tsp_type = TSP_RESOLVE; 470 answer = acksend(&to, &ntp->dest_addr, 471 ANYADDR, TSP_MASTERACK, 472 ntp, 0); 473 if (answer == NULL) 474 break; 475 htp = addmach(answer->tsp_name,&from,ntp); 476 to.tsp_type = TSP_QUIT; 477 answer = acksend(&to, &htp->addr, htp->name, 478 TSP_ACK, 0, htp->noanswer); 479 if (!answer) { 480 syslog(LOG_WARNING, 481 "conflict error: no reply from %s to QUIT", 482 htp->name); 483 (void)remmach(htp); 484 } 485 } 486 masterup(ntp); 487 break; 488 489 case TSP_MSITE: 490 if (!slavenet) 491 break; 492 taddr = from; 493 to.tsp_type = TSP_MSITEREQ; 494 to.tsp_vers = TSPVERSION; 495 to.tsp_seq = 0; 496 (void)strcpy(to.tsp_name, hostname); 497 answer = acksend(&to, &slavenet->dest_addr, 498 ANYADDR, TSP_ACK, 499 slavenet, 0); 500 if (answer != NULL 501 && good_host_name(answer->tsp_name)) { 502 setmaster(answer); 503 to.tsp_type = TSP_ACK; 504 (void)strcpy(to.tsp_name, answer->tsp_name); 505 bytenetorder(&to); 506 if (sendto(sock, (char *)&to, 507 sizeof(struct tsp), 0, 508 (struct sockaddr*)&taddr, 509 sizeof(taddr)) < 0) { 510 trace_sendto_err(taddr.sin_addr); 511 } 512 } 513 break; 514 515 case TSP_MSITEREQ: 516 break; 517 518 case TSP_ACCEPT: 519 case TSP_REFUSE: 520 case TSP_RESOLVE: 521 break; 522 523 case TSP_QUIT: 524 doquit(msg); /* become a slave */ 525 break; 526 527 case TSP_TEST: 528 electiontime = 0; 529 break; 530 531 case TSP_LOOP: 532 /* looking for loops of masters */ 533 if (!(status & MASTER)) 534 break; 535 if (fromnet->status == SLAVE) { 536 if (!strcmp(msg->tsp_name, hostname)) { 537 /* 538 * Someone forwarded our message back to 539 * us. There must be a loop. Tell the 540 * master of this network to quit. 541 * 542 * The other master often gets into 543 * the same state, with boring results. 544 */ 545 ntp = fromnet; 546 for (tries = 0; tries < 3; tries++) { 547 to.tsp_type = TSP_RESOLVE; 548 answer = acksend(&to, &ntp->dest_addr, 549 ANYADDR, TSP_MASTERACK, 550 ntp,0); 551 if (answer == NULL) 552 break; 553 taddr = from; 554 (void)strcpy(tname, answer->tsp_name); 555 to.tsp_type = TSP_QUIT; 556 (void)strcpy(to.tsp_name, hostname); 557 if (!acksend(&to, &taddr, tname, 558 TSP_ACK, 0, 1)) { 559 syslog(LOG_ERR, 560 "no reply from %s to slave LOOP-QUIT", 561 tname); 562 } else { 563 electiontime = 0; 564 } 565 } 566 (void)gettimeofday(&ntime, 0); 567 looptime = ntime.tv_sec + FASTTOUT; 568 } else { 569 if (msg->tsp_hopcnt-- < 1) 570 break; 571 bytenetorder(msg); 572 for (ntp = nettab; ntp != 0; ntp = ntp->next) { 573 if (ntp->status == MASTER 574 && 0 > sendto(sock, (char *)msg, 575 sizeof(struct tsp), 0, 576 (struct sockaddr*)&ntp->dest_addr, 577 sizeof(ntp->dest_addr))) 578 trace_sendto_err(ntp->dest_addr.sin_addr); 579 } 580 } 581 } else { /* fromnet->status == MASTER */ 582 /* 583 * We should not have received this from a net 584 * we are master on. There must be two masters, 585 * unless the packet was really from us. 586 */ 587 if (from.sin_addr.s_addr 588 == fromnet->my_addr.s_addr) { 589 if (trace) 590 fprintf(fd,"discarding forwarded LOOP\n"); 591 break; 592 } 593 594 /* 595 * The other master often gets into the same 596 * state, with boring results. 597 */ 598 ntp = fromnet; 599 for (tries = 0; tries < 3; tries++) { 600 to.tsp_type = TSP_RESOLVE; 601 answer = acksend(&to, &ntp->dest_addr, 602 ANYADDR, TSP_MASTERACK, 603 ntp,0); 604 if (!answer) 605 break; 606 htp = addmach(answer->tsp_name, 607 &from,ntp); 608 to.tsp_type = TSP_QUIT; 609 (void)strcpy(to.tsp_name, hostname); 610 if (!acksend(&to,&htp->addr,htp->name, 611 TSP_ACK, 0, htp->noanswer)) { 612 syslog(LOG_ERR, 613 "no reply from %s to master LOOP-QUIT", 614 htp->name); 615 (void)remmach(htp); 616 } 617 } 618 (void)gettimeofday(&ntime, 0); 619 looptime = ntime.tv_sec + FASTTOUT; 620 } 621 break; 622 default: 623 if (trace) { 624 fprintf(fd, "garbage message: "); 625 print(msg, &from); 626 } 627 break; 628 } 629 } 630 goto loop; 631} 632 633 634/* 635 * tell the world who our master is 636 */ 637static void 638setmaster(msg) 639 struct tsp *msg; 640{ 641 if (slavenet 642 && (slavenet != old_slavenet 643 || strcmp(msg->tsp_name, master_name) 644 || old_status != status)) { 645 (void)strcpy(master_name, msg->tsp_name); 646 old_slavenet = slavenet; 647 old_status = status; 648 649 if (status & MASTER) { 650 syslog(LOG_NOTICE, "submaster to %s", master_name); 651 if (trace) 652 fprintf(fd, "submaster to %s\n", master_name); 653 654 } else { 655 syslog(LOG_NOTICE, "slave to %s", master_name); 656 if (trace) 657 fprintf(fd, "slave to %s\n", master_name); 658 } 659 } 660} 661 662 663 664/* 665 * handle date change request on a slave 666 */ 667static void 668schgdate(msg, newdate) 669 struct tsp *msg; 670 char *newdate; 671{ 672 struct tsp to; 673 u_short seq; 674 struct sockaddr_in taddr; 675 struct timeval otime; 676 677 if (!slavenet) 678 return; /* no where to forward */ 679 680 taddr = from; 681 seq = msg->tsp_seq; 682 683 syslog(LOG_INFO, 684 "forwarding date change by %s to %s", 685 msg->tsp_name, newdate); 686 687 /* adjust time for residence on the queue */ 688 (void)gettimeofday(&otime, 0); 689 adj_msg_time(msg, &otime); 690 691 to.tsp_type = TSP_SETDATEREQ; 692 to.tsp_time = msg->tsp_time; 693 (void)strcpy(to.tsp_name, hostname); 694 if (!acksend(&to, &slavenet->dest_addr, 695 ANYADDR, TSP_DATEACK, 696 slavenet, 0)) 697 return; /* no answer */ 698 699 xmit(TSP_DATEACK, seq, &taddr); 700} 701 702 703/* 704 * Used before answering a broadcast message to avoid network 705 * contention and likely collisions. 706 */ 707static void 708answerdelay() 709{ 710#ifdef sgi 711 sginap(delay1); 712#else 713 struct timeval timeout; 714 715 timeout.tv_sec = 0; 716 timeout.tv_usec = delay1; 717 718 (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, 719 &timeout); 720 return; 721#endif /* sgi */ 722} 723