xref: /netbsd/usr.sbin/timed/timed/slave.c (revision 0aaeb943)
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