slave.c revision 50479
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 50479 1999-08-28 01:35:59Z peter $"; 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; 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 timevalsub(&ntime, &msg->tsp_time, &otime); 284 if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { 285 /* 286 * do not change the clock if we can adjust it 287 */ 288 synch(tvtomsround(ntime)); 289 } else { 290#ifdef sgi 291 if (0 > settimeofday(&msg->tsp_time, 0)) { 292 syslog(LOG_ERR,"settimeofdate(): %m"); 293 break; 294 } 295 logwtmp(&otime, &msg->tsp_time); 296#else 297 logwtmp("|", "date", ""); 298 (void)settimeofday(&msg->tsp_time, 0); 299 logwtmp("{", "date", ""); 300#endif /* sgi */ 301 syslog(LOG_NOTICE, 302 "date changed by %s from %s", 303 msg->tsp_name, olddate); 304 if (status & MASTER) 305 spreadtime(); 306 } 307 (void)gettimeofday(&ntime, 0); 308 electiontime = ntime.tv_sec + delay2; 309 fastelection = ntime.tv_sec + FASTTOUT; 310 311/* This patches a bad protocol bug. Imagine a system with several networks, 312 * where there are a pair of redundant gateways between a pair of networks, 313 * each running timed. Assume that we start with a third machine mastering 314 * one of the networks, and one of the gateways mastering the other. 315 * Imagine that the third machine goes away and the non-master gateway 316 * decides to replace it. If things are timed just 'right,' we will have 317 * each gateway mastering one network for a little while. If a SETTIME 318 * message gets into the network at that time, perhaps from the newly 319 * masterful gateway as it was taking control, the SETTIME will loop 320 * forever. Each time a gateway receives it on its slave side, it will 321 * call spreadtime to forward it on its mastered network. We are now in 322 * a permanent loop, since the SETTIME msgs will keep any clock 323 * in the network from advancing. Normally, the 'LOOP' stuff will detect 324 * and correct the situation. However, with the clocks stopped, the 325 * 'looptime' timer cannot expire. While they are in this state, the 326 * masters will try to saturate the network with SETTIME packets. 327 */ 328 looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1; 329 break; 330 331 case TSP_MASTERUP: 332 if (slavenet && fromnet != slavenet) 333 break; 334 if (!good_host_name(msg->tsp_name)) { 335 suppress(&from, msg->tsp_name, fromnet); 336 if (electiontime > fastelection) 337 electiontime = fastelection; 338 break; 339 } 340 makeslave(fromnet); 341 setmaster(msg); 342 setstatus(); 343 answerdelay(); 344 xmit(TSP_SLAVEUP, 0, &from); 345 (void)gettimeofday(&ntime, 0); 346 electiontime = ntime.tv_sec + delay2; 347 fastelection = ntime.tv_sec + FASTTOUT; 348 refusetime = 0; 349 break; 350 351 case TSP_MASTERREQ: 352 if (fromnet->status != SLAVE) 353 break; 354 (void)gettimeofday(&ntime, 0); 355 electiontime = ntime.tv_sec + delay2; 356 break; 357 358 case TSP_SETDATE: 359#ifdef sgi 360 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); 361#else 362 tsp_time_sec = msg->tsp_time.tv_sec; 363 (void)strcpy(newdate, ctime(&tsp_time_sec)); 364#endif /* sgi */ 365 schgdate(msg, newdate); 366 break; 367 368 case TSP_SETDATEREQ: 369 if (fromnet->status != MASTER) 370 break; 371#ifdef sgi 372 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); 373#else 374 tsp_time_sec = msg->tsp_time.tv_sec; 375 (void)strcpy(newdate, ctime(&tsp_time_sec)); 376#endif /* sgi */ 377 htp = findhost(msg->tsp_name); 378 if (0 == htp) { 379 syslog(LOG_WARNING, 380 "DATEREQ from uncontrolled machine"); 381 break; 382 } 383 if (!htp->good) { 384 syslog(LOG_WARNING, 385 "attempted date change by untrusted %s to %s", 386 htp->name, newdate); 387 spreadtime(); 388 break; 389 } 390 schgdate(msg, newdate); 391 break; 392 393 case TSP_TRACEON: 394 traceon(); 395 break; 396 397 case TSP_TRACEOFF: 398 traceoff("Tracing ended at %s\n"); 399 break; 400 401 case TSP_SLAVEUP: 402 newslave(msg); 403 break; 404 405 case TSP_ELECTION: 406 if (fromnet->status == SLAVE) { 407 (void)gettimeofday(&ntime, 0); 408 electiontime = ntime.tv_sec + delay2; 409 fastelection = ntime.tv_sec + FASTTOUT; 410 seq = 0; 411 if (!good_host_name(msg->tsp_name)) { 412 syslog(LOG_NOTICE, 413 "suppress election of %s", 414 msg->tsp_name); 415 to.tsp_type = TSP_QUIT; 416 electiontime = fastelection; 417 } else if (cadr.s_addr != from.sin_addr.s_addr 418 && ntime.tv_sec < refusetime) { 419/* if the candidate has to repeat itself, the old code would refuse it 420 * the second time. That would prevent elections. 421 */ 422 to.tsp_type = TSP_REFUSE; 423 } else { 424 cadr.s_addr = from.sin_addr.s_addr; 425 to.tsp_type = TSP_ACCEPT; 426 refusetime = ntime.tv_sec + 30; 427 } 428 taddr = from; 429 (void)strcpy(tname, msg->tsp_name); 430 (void)strcpy(to.tsp_name, hostname); 431 answerdelay(); 432 if (!acksend(&to, &taddr, tname, 433 TSP_ACK, 0, 0)) 434 syslog(LOG_WARNING, 435 "no answer from candidate %s\n", 436 tname); 437 438 } else { /* fromnet->status == MASTER */ 439 htp = addmach(msg->tsp_name, &from,fromnet); 440 to.tsp_type = TSP_QUIT; 441 (void)strcpy(to.tsp_name, hostname); 442 if (!acksend(&to, &htp->addr, htp->name, 443 TSP_ACK, 0, htp->noanswer)) { 444 syslog(LOG_ERR, 445 "no reply from %s to ELECTION-QUIT", 446 htp->name); 447 (void)remmach(htp); 448 } 449 } 450 break; 451 452 case TSP_CONFLICT: 453 if (fromnet->status != MASTER) 454 break; 455 /* 456 * After a network partition, there can be 457 * more than one master: the first slave to 458 * come up will notify here the situation. 459 */ 460 (void)strcpy(to.tsp_name, hostname); 461 462 /* The other master often gets into the same state, 463 * with boring results. 464 */ 465 ntp = fromnet; /* (acksend() can leave fromnet=0 */ 466 for (tries = 0; tries < 3; tries++) { 467 to.tsp_type = TSP_RESOLVE; 468 answer = acksend(&to, &ntp->dest_addr, 469 ANYADDR, TSP_MASTERACK, 470 ntp, 0); 471 if (answer == NULL) 472 break; 473 htp = addmach(answer->tsp_name,&from,ntp); 474 to.tsp_type = TSP_QUIT; 475 answer = acksend(&to, &htp->addr, htp->name, 476 TSP_ACK, 0, htp->noanswer); 477 if (!answer) { 478 syslog(LOG_WARNING, 479 "conflict error: no reply from %s to QUIT", 480 htp->name); 481 (void)remmach(htp); 482 } 483 } 484 masterup(ntp); 485 break; 486 487 case TSP_MSITE: 488 if (!slavenet) 489 break; 490 taddr = from; 491 to.tsp_type = TSP_MSITEREQ; 492 to.tsp_vers = TSPVERSION; 493 to.tsp_seq = 0; 494 (void)strcpy(to.tsp_name, hostname); 495 answer = acksend(&to, &slavenet->dest_addr, 496 ANYADDR, TSP_ACK, 497 slavenet, 0); 498 if (answer != NULL 499 && good_host_name(answer->tsp_name)) { 500 setmaster(answer); 501 to.tsp_type = TSP_ACK; 502 (void)strcpy(to.tsp_name, answer->tsp_name); 503 bytenetorder(&to); 504 if (sendto(sock, (char *)&to, 505 sizeof(struct tsp), 0, 506 (struct sockaddr*)&taddr, 507 sizeof(taddr)) < 0) { 508 trace_sendto_err(taddr.sin_addr); 509 } 510 } 511 break; 512 513 case TSP_MSITEREQ: 514 break; 515 516 case TSP_ACCEPT: 517 case TSP_REFUSE: 518 case TSP_RESOLVE: 519 break; 520 521 case TSP_QUIT: 522 doquit(msg); /* become a slave */ 523 break; 524 525 case TSP_TEST: 526 electiontime = 0; 527 break; 528 529 case TSP_LOOP: 530 /* looking for loops of masters */ 531 if (!(status & MASTER)) 532 break; 533 if (fromnet->status == SLAVE) { 534 if (!strcmp(msg->tsp_name, hostname)) { 535 /* 536 * Someone forwarded our message back to 537 * us. There must be a loop. Tell the 538 * master of this network to quit. 539 * 540 * The other master often gets into 541 * the same state, with boring results. 542 */ 543 ntp = fromnet; 544 for (tries = 0; tries < 3; tries++) { 545 to.tsp_type = TSP_RESOLVE; 546 answer = acksend(&to, &ntp->dest_addr, 547 ANYADDR, TSP_MASTERACK, 548 ntp,0); 549 if (answer == NULL) 550 break; 551 taddr = from; 552 (void)strcpy(tname, answer->tsp_name); 553 to.tsp_type = TSP_QUIT; 554 (void)strcpy(to.tsp_name, hostname); 555 if (!acksend(&to, &taddr, tname, 556 TSP_ACK, 0, 1)) { 557 syslog(LOG_ERR, 558 "no reply from %s to slave LOOP-QUIT", 559 tname); 560 } else { 561 electiontime = 0; 562 } 563 } 564 (void)gettimeofday(&ntime, 0); 565 looptime = ntime.tv_sec + FASTTOUT; 566 } else { 567 if (msg->tsp_hopcnt-- < 1) 568 break; 569 bytenetorder(msg); 570 for (ntp = nettab; ntp != 0; ntp = ntp->next) { 571 if (ntp->status == MASTER 572 && 0 > sendto(sock, (char *)msg, 573 sizeof(struct tsp), 0, 574 (struct sockaddr*)&ntp->dest_addr, 575 sizeof(ntp->dest_addr))) 576 trace_sendto_err(ntp->dest_addr.sin_addr); 577 } 578 } 579 } else { /* fromnet->status == MASTER */ 580 /* 581 * We should not have received this from a net 582 * we are master on. There must be two masters, 583 * unless the packet was really from us. 584 */ 585 if (from.sin_addr.s_addr 586 == fromnet->my_addr.s_addr) { 587 if (trace) 588 fprintf(fd,"discarding forwarded LOOP\n"); 589 break; 590 } 591 592 /* 593 * The other master often gets into the same 594 * state, with boring results. 595 */ 596 ntp = fromnet; 597 for (tries = 0; tries < 3; tries++) { 598 to.tsp_type = TSP_RESOLVE; 599 answer = acksend(&to, &ntp->dest_addr, 600 ANYADDR, TSP_MASTERACK, 601 ntp,0); 602 if (!answer) 603 break; 604 htp = addmach(answer->tsp_name, 605 &from,ntp); 606 to.tsp_type = TSP_QUIT; 607 (void)strcpy(to.tsp_name, hostname); 608 if (!acksend(&to,&htp->addr,htp->name, 609 TSP_ACK, 0, htp->noanswer)) { 610 syslog(LOG_ERR, 611 "no reply from %s to master LOOP-QUIT", 612 htp->name); 613 (void)remmach(htp); 614 } 615 } 616 (void)gettimeofday(&ntime, 0); 617 looptime = ntime.tv_sec + FASTTOUT; 618 } 619 break; 620 default: 621 if (trace) { 622 fprintf(fd, "garbage message: "); 623 print(msg, &from); 624 } 625 break; 626 } 627 } 628 goto loop; 629} 630 631 632/* 633 * tell the world who our master is 634 */ 635static void 636setmaster(msg) 637 struct tsp *msg; 638{ 639 if (slavenet 640 && (slavenet != old_slavenet 641 || strcmp(msg->tsp_name, master_name) 642 || old_status != status)) { 643 (void)strcpy(master_name, msg->tsp_name); 644 old_slavenet = slavenet; 645 old_status = status; 646 647 if (status & MASTER) { 648 syslog(LOG_NOTICE, "submaster to %s", master_name); 649 if (trace) 650 fprintf(fd, "submaster to %s\n", master_name); 651 652 } else { 653 syslog(LOG_NOTICE, "slave to %s", master_name); 654 if (trace) 655 fprintf(fd, "slave to %s\n", master_name); 656 } 657 } 658} 659 660 661 662/* 663 * handle date change request on a slave 664 */ 665static void 666schgdate(msg, newdate) 667 struct tsp *msg; 668 char *newdate; 669{ 670 struct tsp to; 671 u_short seq; 672 struct sockaddr_in taddr; 673 struct timeval otime; 674 675 if (!slavenet) 676 return; /* no where to forward */ 677 678 taddr = from; 679 seq = msg->tsp_seq; 680 681 syslog(LOG_INFO, 682 "forwarding date change by %s to %s", 683 msg->tsp_name, newdate); 684 685 /* adjust time for residence on the queue */ 686 (void)gettimeofday(&otime, 0); 687 adj_msg_time(msg, &otime); 688 689 to.tsp_type = TSP_SETDATEREQ; 690 to.tsp_time = msg->tsp_time; 691 (void)strcpy(to.tsp_name, hostname); 692 if (!acksend(&to, &slavenet->dest_addr, 693 ANYADDR, TSP_DATEACK, 694 slavenet, 0)) 695 return; /* no answer */ 696 697 xmit(TSP_DATEACK, seq, &taddr); 698} 699 700 701/* 702 * Used before answering a broadcast message to avoid network 703 * contention and likely collisions. 704 */ 705static void 706answerdelay() 707{ 708#ifdef sgi 709 sginap(delay1); 710#else 711 struct timeval timeout; 712 713 timeout.tv_sec = 0; 714 timeout.tv_usec = delay1; 715 716 (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, 717 &timeout); 718 return; 719#endif /* sgi */ 720} 721