1 /* $NetBSD: slave.c,v 1.19 2018/01/16 07:57:58 maya 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
35 static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93";
36 #else
37 __RCSID("$NetBSD: slave.c,v 1.19 2018/01/16 07:57:58 maya Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "globals.h"
42 #include <setjmp.h>
43 #include "pathnames.h"
44
45 extern jmp_buf jmpenv;
46 extern int Mflag;
47 extern int justquit;
48
49 extern u_short sequence;
50
51 static char master_name[MAXHOSTNAMELEN+1];
52 static struct netinfo *old_slavenet;
53 static int old_status;
54
55 static void schgdate(struct tsp *, char *);
56 static void setmaster(struct tsp *);
57 static void answerdelay(void);
58
59 void
slave(void)60 slave(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
104 loop:
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 */
593 static void
setmaster(struct tsp * msg)594 setmaster(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 */
622 static void
schgdate(struct tsp * msg,char * newdate)623 schgdate(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 */
660 static void
answerdelay(void)661 answerdelay(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