slave.c revision 216372
1303980Sngie/*-
2303980Sngie * Copyright (c) 1985, 1993
3303980Sngie *	The Regents of the University of California.  All rights reserved.
4303980Sngie *
5303980Sngie * Redistribution and use in source and binary forms, with or without
6303980Sngie * modification, are permitted provided that the following conditions
7303980Sngie * 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 216372 2010-12-11 09:38:12Z joel $";
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()
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 = 0;
82	seq = 0;
83	refusetime = 0;
84	adjtime = 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			(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, 0);
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, 0);
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, 0);
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, 0);
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)strcpy(olddate, date());
258			tsp_time_sec = msg->tsp_time.tv_sec;
259			(void)strcpy(newdate, ctime(&tsp_time_sec));
260
261			if (!good_host_name(msg->tsp_name)) {
262				syslog(LOG_NOTICE,
263			    "attempted time setting by untrusted %s to %s",
264				       msg->tsp_name, newdate);
265				suppress(&from, msg->tsp_name, fromnet);
266				break;
267			}
268
269			setmaster(msg);
270 			tmptv.tv_sec = msg->tsp_time.tv_sec;
271 			tmptv.tv_usec = msg->tsp_time.tv_usec;
272			timevalsub(&ntime, &tmptv, &otime);
273			if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
274				/*
275				 * do not change the clock if we can adjust it
276				 */
277				synch(tvtomsround(ntime));
278			} else {
279				utx.ut_type = OLD_TIME;
280				gettimeofday(&utx.ut_tv, NULL);
281				pututxline(&utx);
282				(void)settimeofday(&tmptv, 0);
283				utx.ut_type = NEW_TIME;
284				gettimeofday(&utx.ut_tv, NULL);
285				pututxline(&utx);
286				syslog(LOG_NOTICE,
287				       "date changed by %s from %s",
288					msg->tsp_name, olddate);
289				if (status & MASTER)
290					spreadtime();
291			}
292			(void)gettimeofday(&ntime, 0);
293			electiontime = ntime.tv_sec + delay2;
294			fastelection = ntime.tv_sec + FASTTOUT;
295
296/* This patches a bad protocol bug.  Imagine a system with several networks,
297 * where there are a pair of redundant gateways between a pair of networks,
298 * each running timed.  Assume that we start with a third machine mastering
299 * one of the networks, and one of the gateways mastering the other.
300 * Imagine that the third machine goes away and the non-master gateway
301 * decides to replace it.  If things are timed just 'right,' we will have
302 * each gateway mastering one network for a little while.  If a SETTIME
303 * message gets into the network at that time, perhaps from the newly
304 * masterful gateway as it was taking control, the SETTIME will loop
305 * forever.  Each time a gateway receives it on its slave side, it will
306 * call spreadtime to forward it on its mastered network.  We are now in
307 * a permanent loop, since the SETTIME msgs will keep any clock
308 * in the network from advancing.  Normally, the 'LOOP' stuff will detect
309 * and correct the situation.  However, with the clocks stopped, the
310 * 'looptime' timer cannot expire.  While they are in this state, the
311 * masters will try to saturate the network with SETTIME packets.
312 */
313			looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
314			break;
315
316		case TSP_MASTERUP:
317			if (slavenet && fromnet != slavenet)
318				break;
319			if (!good_host_name(msg->tsp_name)) {
320				suppress(&from, msg->tsp_name, fromnet);
321				if (electiontime > fastelection)
322					electiontime = fastelection;
323				break;
324			}
325			makeslave(fromnet);
326			setmaster(msg);
327			setstatus();
328			answerdelay();
329			xmit(TSP_SLAVEUP, 0, &from);
330			(void)gettimeofday(&ntime, 0);
331			electiontime = ntime.tv_sec + delay2;
332			fastelection = ntime.tv_sec + FASTTOUT;
333			refusetime = 0;
334			break;
335
336		case TSP_MASTERREQ:
337			if (fromnet->status != SLAVE)
338				break;
339			(void)gettimeofday(&ntime, 0);
340			electiontime = ntime.tv_sec + delay2;
341			break;
342
343		case TSP_SETDATE:
344			tsp_time_sec = msg->tsp_time.tv_sec;
345			(void)strcpy(newdate, ctime(&tsp_time_sec));
346			schgdate(msg, newdate);
347			break;
348
349		case TSP_SETDATEREQ:
350			if (fromnet->status != MASTER)
351				break;
352			tsp_time_sec = msg->tsp_time.tv_sec;
353			(void)strcpy(newdate, ctime(&tsp_time_sec));
354			htp = findhost(msg->tsp_name);
355			if (0 == htp) {
356				syslog(LOG_WARNING,
357				       "DATEREQ from uncontrolled machine");
358				break;
359			}
360			if (!htp->good) {
361				syslog(LOG_WARNING,
362				"attempted date change by untrusted %s to %s",
363				       htp->name, newdate);
364				spreadtime();
365				break;
366			}
367			schgdate(msg, newdate);
368			break;
369
370		case TSP_TRACEON:
371			traceon();
372			break;
373
374		case TSP_TRACEOFF:
375			traceoff("Tracing ended at %s\n");
376			break;
377
378		case TSP_SLAVEUP:
379			newslave(msg);
380			break;
381
382		case TSP_ELECTION:
383			if (fromnet->status == SLAVE) {
384				(void)gettimeofday(&ntime, 0);
385				electiontime = ntime.tv_sec + delay2;
386				fastelection = ntime.tv_sec + FASTTOUT;
387				seq = 0;
388				if (!good_host_name(msg->tsp_name)) {
389					syslog(LOG_NOTICE,
390					       "suppress election of %s",
391					       msg->tsp_name);
392					to.tsp_type = TSP_QUIT;
393					electiontime = fastelection;
394				} else if (cadr.s_addr != from.sin_addr.s_addr
395					   && ntime.tv_sec < refusetime) {
396/* if the candidate has to repeat itself, the old code would refuse it
397 * the second time.  That would prevent elections.
398 */
399					to.tsp_type = TSP_REFUSE;
400				} else {
401					cadr.s_addr = from.sin_addr.s_addr;
402					to.tsp_type = TSP_ACCEPT;
403					refusetime = ntime.tv_sec + 30;
404				}
405				taddr = from;
406				(void)strcpy(tname, msg->tsp_name);
407				(void)strcpy(to.tsp_name, hostname);
408				answerdelay();
409				if (!acksend(&to, &taddr, tname,
410					     TSP_ACK, 0, 0))
411					syslog(LOG_WARNING,
412					     "no answer from candidate %s\n",
413					       tname);
414
415			} else {	/* fromnet->status == MASTER */
416				htp = addmach(msg->tsp_name, &from,fromnet);
417				to.tsp_type = TSP_QUIT;
418				(void)strcpy(to.tsp_name, hostname);
419				if (!acksend(&to, &htp->addr, htp->name,
420					     TSP_ACK, 0, htp->noanswer)) {
421					syslog(LOG_ERR,
422					  "no reply from %s to ELECTION-QUIT",
423					       htp->name);
424					(void)remmach(htp);
425				}
426			}
427			break;
428
429		case TSP_CONFLICT:
430			if (fromnet->status != MASTER)
431				break;
432			/*
433			 * After a network partition, there can be
434			 * more than one master: the first slave to
435			 * come up will notify here the situation.
436			 */
437			(void)strcpy(to.tsp_name, hostname);
438
439			/* The other master often gets into the same state,
440			 * with boring results.
441			 */
442			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
443			for (tries = 0; tries < 3; tries++) {
444				to.tsp_type = TSP_RESOLVE;
445				answer = acksend(&to, &ntp->dest_addr,
446						 ANYADDR, TSP_MASTERACK,
447						 ntp, 0);
448				if (answer == NULL)
449					break;
450				htp = addmach(answer->tsp_name,&from,ntp);
451				to.tsp_type = TSP_QUIT;
452				answer = acksend(&to, &htp->addr, htp->name,
453						 TSP_ACK, 0, htp->noanswer);
454				if (!answer) {
455					syslog(LOG_WARNING,
456				  "conflict error: no reply from %s to QUIT",
457						htp->name);
458					(void)remmach(htp);
459				}
460			}
461			masterup(ntp);
462			break;
463
464		case TSP_MSITE:
465			if (!slavenet)
466				break;
467			taddr = from;
468			to.tsp_type = TSP_MSITEREQ;
469			to.tsp_vers = TSPVERSION;
470			to.tsp_seq = 0;
471			(void)strcpy(to.tsp_name, hostname);
472			answer = acksend(&to, &slavenet->dest_addr,
473					 ANYADDR, TSP_ACK,
474					 slavenet, 0);
475			if (answer != NULL
476			    && good_host_name(answer->tsp_name)) {
477				setmaster(answer);
478				to.tsp_type = TSP_ACK;
479				(void)strcpy(to.tsp_name, answer->tsp_name);
480				bytenetorder(&to);
481				if (sendto(sock, (char *)&to,
482					   sizeof(struct tsp), 0,
483					   (struct sockaddr*)&taddr,
484					   sizeof(taddr)) < 0) {
485					trace_sendto_err(taddr.sin_addr);
486				}
487			}
488			break;
489
490		case TSP_MSITEREQ:
491			break;
492
493		case TSP_ACCEPT:
494		case TSP_REFUSE:
495		case TSP_RESOLVE:
496			break;
497
498		case TSP_QUIT:
499			doquit(msg);		/* become a slave */
500			break;
501
502		case TSP_TEST:
503			electiontime = 0;
504			break;
505
506		case TSP_LOOP:
507			/* looking for loops of masters */
508			if (!(status & MASTER))
509				break;
510			if (fromnet->status == SLAVE) {
511			    if (!strcmp(msg->tsp_name, hostname)) {
512				/*
513				 * Someone forwarded our message back to
514				 * us.  There must be a loop.  Tell the
515				 * master of this network to quit.
516				 *
517				 * The other master often gets into
518				 * the same state, with boring results.
519				 */
520				ntp = fromnet;
521				for (tries = 0; tries < 3; tries++) {
522				    to.tsp_type = TSP_RESOLVE;
523				    answer = acksend(&to, &ntp->dest_addr,
524						     ANYADDR, TSP_MASTERACK,
525						     ntp,0);
526				    if (answer == NULL)
527					break;
528				    taddr = from;
529				    (void)strcpy(tname, answer->tsp_name);
530				    to.tsp_type = TSP_QUIT;
531				    (void)strcpy(to.tsp_name, hostname);
532				    if (!acksend(&to, &taddr, tname,
533						 TSP_ACK, 0, 1)) {
534					syslog(LOG_ERR,
535					"no reply from %s to slave LOOP-QUIT",
536						 tname);
537				    } else {
538					electiontime = 0;
539				    }
540				}
541				(void)gettimeofday(&ntime, 0);
542				looptime = ntime.tv_sec + FASTTOUT;
543			    } else {
544				if (msg->tsp_hopcnt-- < 1)
545				    break;
546				bytenetorder(msg);
547				for (ntp = nettab; ntp != 0; ntp = ntp->next) {
548				    if (ntp->status == MASTER
549					&& 0 > sendto(sock, (char *)msg,
550						      sizeof(struct tsp), 0,
551					      (struct sockaddr*)&ntp->dest_addr,
552						      sizeof(ntp->dest_addr)))
553				    trace_sendto_err(ntp->dest_addr.sin_addr);
554				}
555			    }
556			} else {	/* fromnet->status == MASTER */
557			    /*
558			     * We should not have received this from a net
559			     * we are master on.  There must be two masters,
560			     * unless the packet was really from us.
561			     */
562			    if (from.sin_addr.s_addr
563				== fromnet->my_addr.s_addr) {
564				if (trace)
565				    fprintf(fd,"discarding forwarded LOOP\n");
566				break;
567			    }
568
569			    /*
570			     * The other master often gets into the same
571			     * state, with boring results.
572			     */
573			    ntp = fromnet;
574			    for (tries = 0; tries < 3; tries++) {
575				to.tsp_type = TSP_RESOLVE;
576				answer = acksend(&to, &ntp->dest_addr,
577						 ANYADDR, TSP_MASTERACK,
578						ntp,0);
579				if (!answer)
580					break;
581				htp = addmach(answer->tsp_name,
582					      &from,ntp);
583				to.tsp_type = TSP_QUIT;
584				(void)strcpy(to.tsp_name, hostname);
585				if (!acksend(&to,&htp->addr,htp->name,
586					     TSP_ACK, 0, htp->noanswer)) {
587					syslog(LOG_ERR,
588				    "no reply from %s to master LOOP-QUIT",
589					       htp->name);
590					(void)remmach(htp);
591				}
592			    }
593			    (void)gettimeofday(&ntime, 0);
594			    looptime = ntime.tv_sec + FASTTOUT;
595			}
596			break;
597		default:
598			if (trace) {
599				fprintf(fd, "garbage message: ");
600				print(msg, &from);
601			}
602			break;
603		}
604	}
605	goto loop;
606}
607
608
609/*
610 * tell the world who our master is
611 */
612static void
613setmaster(msg)
614	struct tsp *msg;
615{
616	if (slavenet
617	    && (slavenet != old_slavenet
618		|| strcmp(msg->tsp_name, master_name)
619		|| old_status != status)) {
620		(void)strcpy(master_name, msg->tsp_name);
621		old_slavenet = slavenet;
622		old_status = status;
623
624		if (status & MASTER) {
625			syslog(LOG_NOTICE, "submaster to %s", master_name);
626			if (trace)
627				fprintf(fd, "submaster to %s\n", master_name);
628
629		} else {
630			syslog(LOG_NOTICE, "slave to %s", master_name);
631			if (trace)
632				fprintf(fd, "slave to %s\n", master_name);
633		}
634	}
635}
636
637
638
639/*
640 * handle date change request on a slave
641 */
642static void
643schgdate(msg, newdate)
644	struct tsp *msg;
645	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, 0);
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()
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