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