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