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