slave.c revision 173412
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 173412 2007-11-07 10:53:41Z kevlo $";
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(struct tsp *, char *);
57static void setmaster(struct tsp *);
58static void answerdelay(void);
59
60extern void logwtmp(char *, char *, char *);
61
62int
63slave()
64{
65	int tries;
66	long electiontime, refusetime, looktime, looptime, adjtime;
67	u_short seq;
68	long fastelection;
69#define FASTTOUT 3
70	struct in_addr cadr;
71	struct timeval otime;
72	struct sockaddr_in taddr;
73	char tname[MAXHOSTNAMELEN];
74	struct tsp *msg, to;
75	struct timeval ntime, wait, tmptv;
76	time_t tsp_time_sec;
77	struct tsp *answer;
78	int timeout();
79	char olddate[32];
80	char newdate[32];
81	struct netinfo *ntp;
82	struct hosttbl *htp;
83
84
85	old_slavenet = 0;
86	seq = 0;
87	refusetime = 0;
88	adjtime = 0;
89
90	(void)gettimeofday(&ntime, 0);
91	electiontime = ntime.tv_sec + delay2;
92	fastelection = ntime.tv_sec + FASTTOUT;
93	if (justquit)
94		looktime = electiontime;
95	else
96		looktime = fastelection;
97	looptime = fastelection;
98
99	if (slavenet)
100		xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
101	if (status & MASTER) {
102		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
103			if (ntp->status == MASTER)
104				masterup(ntp);
105		}
106	}
107
108loop:
109	get_goodgroup(0);
110	(void)gettimeofday(&ntime, (struct timezone *)0);
111	if (ntime.tv_sec > electiontime) {
112		if (trace)
113			fprintf(fd, "election timer expired\n");
114		longjmp(jmpenv, 1);
115	}
116
117	if (ntime.tv_sec >= looktime) {
118		if (trace)
119			fprintf(fd, "Looking for nets to master\n");
120
121		if (Mflag && nignorednets > 0) {
122			for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
123				if (ntp->status == IGNORE
124				    || ntp->status == NOMASTER) {
125					lookformaster(ntp);
126					if (ntp->status == MASTER) {
127						masterup(ntp);
128					} else if (ntp->status == MASTER) {
129						ntp->status = NOMASTER;
130					}
131				}
132				if (ntp->status == MASTER
133				    && --ntp->quit_count < 0)
134					ntp->quit_count = 0;
135			}
136			makeslave(slavenet);	/* prune extras */
137			setstatus();
138		}
139		(void)gettimeofday(&ntime, 0);
140		looktime = ntime.tv_sec + delay2;
141	}
142	if (ntime.tv_sec >= looptime) {
143		if (trace)
144			fprintf(fd, "Looking for loops\n");
145		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
146		    if (ntp->status == MASTER) {
147			to.tsp_type = TSP_LOOP;
148			to.tsp_vers = TSPVERSION;
149			to.tsp_seq = sequence++;
150			to.tsp_hopcnt = MAX_HOPCNT;
151			(void)strcpy(to.tsp_name, hostname);
152			bytenetorder(&to);
153			if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
154				   (struct sockaddr*)&ntp->dest_addr,
155				   sizeof(ntp->dest_addr)) < 0) {
156				trace_sendto_err(ntp->dest_addr.sin_addr);
157			}
158		    }
159		}
160		(void)gettimeofday(&ntime, 0);
161		looptime = ntime.tv_sec + delay2;
162	}
163
164	wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
165	if (wait.tv_sec < 0)
166		wait.tv_sec = 0;
167	wait.tv_sec += FASTTOUT;
168	wait.tv_usec = 0;
169	msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
170
171	if (msg != NULL) {
172		/*
173		 * filter stuff not for us
174		 */
175		switch (msg->tsp_type) {
176		case TSP_SETDATE:
177		case TSP_TRACEOFF:
178		case TSP_TRACEON:
179			/*
180			 * XXX check to see they are from ourself
181			 */
182			break;
183
184		case TSP_TEST:
185		case TSP_MSITE:
186			break;
187
188		case TSP_MASTERUP:
189			if (!fromnet) {
190				if (trace) {
191					fprintf(fd, "slave ignored: ");
192					print(msg, &from);
193				}
194				goto loop;
195			}
196			break;
197
198		default:
199			if (!fromnet
200			    || fromnet->status == IGNORE
201			    || fromnet->status == NOMASTER) {
202				if (trace) {
203					fprintf(fd, "slave ignored: ");
204					print(msg, &from);
205				}
206				goto loop;
207			}
208			break;
209		}
210
211
212		/*
213		 * now process the message
214		 */
215		switch (msg->tsp_type) {
216
217		case TSP_ADJTIME:
218			if (fromnet != slavenet)
219				break;
220			if (!good_host_name(msg->tsp_name)) {
221				syslog(LOG_NOTICE,
222				   "attempted time adjustment by %s",
223				       msg->tsp_name);
224				suppress(&from, msg->tsp_name, fromnet);
225				break;
226			}
227			/*
228			 * Speed up loop detection in case we have a loop.
229			 * Otherwise the clocks can race until the loop
230			 * is found.
231			 */
232			(void)gettimeofday(&otime, 0);
233			if (adjtime < otime.tv_sec)
234				looptime -= (looptime-otime.tv_sec)/2 + 1;
235
236			setmaster(msg);
237			if (seq != msg->tsp_seq) {
238				seq = msg->tsp_seq;
239				synch(tvtomsround(msg->tsp_time));
240			}
241			(void)gettimeofday(&ntime, 0);
242			electiontime = ntime.tv_sec + delay2;
243			fastelection = ntime.tv_sec + FASTTOUT;
244			adjtime = ntime.tv_sec + SAMPLEINTVL*2;
245			break;
246
247		case TSP_SETTIME:
248			if (fromnet != slavenet)
249				break;
250			if (seq == msg->tsp_seq)
251				break;
252			seq = msg->tsp_seq;
253
254			/* adjust time for residence on the queue */
255			(void)gettimeofday(&otime, 0);
256			adj_msg_time(msg,&otime);
257			/*
258			 * the following line is necessary due to syslog
259			 * calling ctime() which clobbers the static buffer
260			 */
261			(void)strcpy(olddate, date());
262			tsp_time_sec = msg->tsp_time.tv_sec;
263			(void)strcpy(newdate, ctime(&tsp_time_sec));
264
265			if (!good_host_name(msg->tsp_name)) {
266				syslog(LOG_NOTICE,
267			    "attempted time setting by untrusted %s to %s",
268				       msg->tsp_name, newdate);
269				suppress(&from, msg->tsp_name, fromnet);
270				break;
271			}
272
273			setmaster(msg);
274 			tmptv.tv_sec = msg->tsp_time.tv_sec;
275 			tmptv.tv_usec = msg->tsp_time.tv_usec;
276			timevalsub(&ntime, &tmptv, &otime);
277			if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
278				/*
279				 * do not change the clock if we can adjust it
280				 */
281				synch(tvtomsround(ntime));
282			} else {
283				logwtmp("|", "date", "");
284 				(void)settimeofday(&tmptv, 0);
285				logwtmp("{", "date", "");
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