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