slave.c revision 299709
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 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