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