slave.c revision 299707
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: head/usr.sbin/timed/timed/slave.c 299707 2016-05-14 00:46:38Z 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)strcpy(olddate, date()); 258 tsp_time_sec = msg->tsp_time.tv_sec; 259 (void)strcpy(newdate, ctime(&tsp_time_sec)); 260 261 if (!good_host_name(msg->tsp_name)) { 262 syslog(LOG_NOTICE, 263 "attempted time setting by untrusted %s to %s", 264 msg->tsp_name, newdate); 265 suppress(&from, msg->tsp_name, fromnet); 266 break; 267 } 268 269 setmaster(msg); 270 tmptv.tv_sec = msg->tsp_time.tv_sec; 271 tmptv.tv_usec = msg->tsp_time.tv_usec; 272 timevalsub(&ntime, &tmptv, &otime); 273 if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { 274 /* 275 * do not change the clock if we can adjust it 276 */ 277 synch(tvtomsround(ntime)); 278 } else { 279 utx.ut_type = OLD_TIME; 280 gettimeofday(&utx.ut_tv, NULL); 281 pututxline(&utx); 282 (void)settimeofday(&tmptv, 0); 283 utx.ut_type = NEW_TIME; 284 gettimeofday(&utx.ut_tv, NULL); 285 pututxline(&utx); 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, NULL); 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, NULL); 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, NULL); 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 (htp == NULL) { 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, NULL); 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, NULL); 542 looptime = ntime.tv_sec + FASTTOUT; 543 } else { 544 if (msg->tsp_hopcnt-- < 1) 545 break; 546 bytenetorder(msg); 547 for (ntp = nettab; ntp != NULL; 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, NULL); 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(struct tsp *msg) 614{ 615 if (slavenet 616 && (slavenet != old_slavenet 617 || strcmp(msg->tsp_name, master_name) 618 || old_status != status)) { 619 (void)strcpy(master_name, msg->tsp_name); 620 old_slavenet = slavenet; 621 old_status = status; 622 623 if (status & MASTER) { 624 syslog(LOG_NOTICE, "submaster to %s", master_name); 625 if (trace) 626 fprintf(fd, "submaster to %s\n", master_name); 627 628 } else { 629 syslog(LOG_NOTICE, "slave to %s", master_name); 630 if (trace) 631 fprintf(fd, "slave to %s\n", master_name); 632 } 633 } 634} 635 636 637 638/* 639 * handle date change request on a slave 640 */ 641static void 642schgdate(struct tsp *msg, char *newdate) 643{ 644 struct tsp to; 645 u_short seq; 646 struct sockaddr_in taddr; 647 struct timeval otime; 648 649 if (!slavenet) 650 return; /* no where to forward */ 651 652 taddr = from; 653 seq = msg->tsp_seq; 654 655 syslog(LOG_INFO, 656 "forwarding date change by %s to %s", 657 msg->tsp_name, newdate); 658 659 /* adjust time for residence on the queue */ 660 (void)gettimeofday(&otime, NULL); 661 adj_msg_time(msg, &otime); 662 663 to.tsp_type = TSP_SETDATEREQ; 664 to.tsp_time = msg->tsp_time; 665 (void)strcpy(to.tsp_name, hostname); 666 if (!acksend(&to, &slavenet->dest_addr, 667 ANYADDR, TSP_DATEACK, 668 slavenet, 0)) 669 return; /* no answer */ 670 671 xmit(TSP_DATEACK, seq, &taddr); 672} 673 674 675/* 676 * Used before answering a broadcast message to avoid network 677 * contention and likely collisions. 678 */ 679static void 680answerdelay(void) 681{ 682 struct timeval timeout; 683 684 timeout.tv_sec = 0; 685 timeout.tv_usec = delay1; 686 687 (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, 688 &timeout); 689 return; 690} 691