slave.c revision 202204
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 202204 2010-01-13 18:15:46Z ed $";
40#endif /* not lint */
41
42#include "globals.h"
43#include <setjmp.h>
44#include <utmpx.h>
45#include "pathnames.h"
46
47extern jmp_buf jmpenv;
48extern int Mflag;
49extern int justquit;
50
51extern u_short sequence;
52
53static char master_name[MAXHOSTNAMELEN];
54static struct netinfo *old_slavenet;
55static int old_status;
56
57static void schgdate(struct tsp *, char *);
58static void setmaster(struct tsp *);
59static void answerdelay(void);
60
61int
62slave()
63{
64	int tries;
65	long electiontime, refusetime, looktime, looptime, adjtime;
66	u_short seq;
67	long fastelection;
68#define FASTTOUT 3
69	struct in_addr cadr;
70	struct timeval otime;
71	struct sockaddr_in taddr;
72	char tname[MAXHOSTNAMELEN];
73	struct tsp *msg, to;
74	struct timeval ntime, wait, tmptv;
75	time_t tsp_time_sec;
76	struct tsp *answer;
77	int timeout();
78	char olddate[32];
79	char newdate[32];
80	struct netinfo *ntp;
81	struct hosttbl *htp;
82	struct utmpx utx;
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				utx.ut_type = OLD_TIME;
284				gettimeofday(&utx.ut_tv, NULL);
285				pututxline(&utx);
286				(void)settimeofday(&tmptv, 0);
287				utx.ut_type = NEW_TIME;
288				gettimeofday(&utx.ut_tv, NULL);
289				pututxline(&utx);
290				syslog(LOG_NOTICE,
291				       "date changed by %s from %s",
292					msg->tsp_name, olddate);
293				if (status & MASTER)
294					spreadtime();
295			}
296			(void)gettimeofday(&ntime, 0);
297			electiontime = ntime.tv_sec + delay2;
298			fastelection = ntime.tv_sec + FASTTOUT;
299
300/* This patches a bad protocol bug.  Imagine a system with several networks,
301 * where there are a pair of redundant gateways between a pair of networks,
302 * each running timed.  Assume that we start with a third machine mastering
303 * one of the networks, and one of the gateways mastering the other.
304 * Imagine that the third machine goes away and the non-master gateway
305 * decides to replace it.  If things are timed just 'right,' we will have
306 * each gateway mastering one network for a little while.  If a SETTIME
307 * message gets into the network at that time, perhaps from the newly
308 * masterful gateway as it was taking control, the SETTIME will loop
309 * forever.  Each time a gateway receives it on its slave side, it will
310 * call spreadtime to forward it on its mastered network.  We are now in
311 * a permanent loop, since the SETTIME msgs will keep any clock
312 * in the network from advancing.  Normally, the 'LOOP' stuff will detect
313 * and correct the situation.  However, with the clocks stopped, the
314 * 'looptime' timer cannot expire.  While they are in this state, the
315 * masters will try to saturate the network with SETTIME packets.
316 */
317			looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
318			break;
319
320		case TSP_MASTERUP:
321			if (slavenet && fromnet != slavenet)
322				break;
323			if (!good_host_name(msg->tsp_name)) {
324				suppress(&from, msg->tsp_name, fromnet);
325				if (electiontime > fastelection)
326					electiontime = fastelection;
327				break;
328			}
329			makeslave(fromnet);
330			setmaster(msg);
331			setstatus();
332			answerdelay();
333			xmit(TSP_SLAVEUP, 0, &from);
334			(void)gettimeofday(&ntime, 0);
335			electiontime = ntime.tv_sec + delay2;
336			fastelection = ntime.tv_sec + FASTTOUT;
337			refusetime = 0;
338			break;
339
340		case TSP_MASTERREQ:
341			if (fromnet->status != SLAVE)
342				break;
343			(void)gettimeofday(&ntime, 0);
344			electiontime = ntime.tv_sec + delay2;
345			break;
346
347		case TSP_SETDATE:
348			tsp_time_sec = msg->tsp_time.tv_sec;
349			(void)strcpy(newdate, ctime(&tsp_time_sec));
350			schgdate(msg, newdate);
351			break;
352
353		case TSP_SETDATEREQ:
354			if (fromnet->status != MASTER)
355				break;
356			tsp_time_sec = msg->tsp_time.tv_sec;
357			(void)strcpy(newdate, ctime(&tsp_time_sec));
358			htp = findhost(msg->tsp_name);
359			if (0 == htp) {
360				syslog(LOG_WARNING,
361				       "DATEREQ from uncontrolled machine");
362				break;
363			}
364			if (!htp->good) {
365				syslog(LOG_WARNING,
366				"attempted date change by untrusted %s to %s",
367				       htp->name, newdate);
368				spreadtime();
369				break;
370			}
371			schgdate(msg, newdate);
372			break;
373
374		case TSP_TRACEON:
375			traceon();
376			break;
377
378		case TSP_TRACEOFF:
379			traceoff("Tracing ended at %s\n");
380			break;
381
382		case TSP_SLAVEUP:
383			newslave(msg);
384			break;
385
386		case TSP_ELECTION:
387			if (fromnet->status == SLAVE) {
388				(void)gettimeofday(&ntime, 0);
389				electiontime = ntime.tv_sec + delay2;
390				fastelection = ntime.tv_sec + FASTTOUT;
391				seq = 0;
392				if (!good_host_name(msg->tsp_name)) {
393					syslog(LOG_NOTICE,
394					       "suppress election of %s",
395					       msg->tsp_name);
396					to.tsp_type = TSP_QUIT;
397					electiontime = fastelection;
398				} else if (cadr.s_addr != from.sin_addr.s_addr
399					   && ntime.tv_sec < refusetime) {
400/* if the candidate has to repeat itself, the old code would refuse it
401 * the second time.  That would prevent elections.
402 */
403					to.tsp_type = TSP_REFUSE;
404				} else {
405					cadr.s_addr = from.sin_addr.s_addr;
406					to.tsp_type = TSP_ACCEPT;
407					refusetime = ntime.tv_sec + 30;
408				}
409				taddr = from;
410				(void)strcpy(tname, msg->tsp_name);
411				(void)strcpy(to.tsp_name, hostname);
412				answerdelay();
413				if (!acksend(&to, &taddr, tname,
414					     TSP_ACK, 0, 0))
415					syslog(LOG_WARNING,
416					     "no answer from candidate %s\n",
417					       tname);
418
419			} else {	/* fromnet->status == MASTER */
420				htp = addmach(msg->tsp_name, &from,fromnet);
421				to.tsp_type = TSP_QUIT;
422				(void)strcpy(to.tsp_name, hostname);
423				if (!acksend(&to, &htp->addr, htp->name,
424					     TSP_ACK, 0, htp->noanswer)) {
425					syslog(LOG_ERR,
426					  "no reply from %s to ELECTION-QUIT",
427					       htp->name);
428					(void)remmach(htp);
429				}
430			}
431			break;
432
433		case TSP_CONFLICT:
434			if (fromnet->status != MASTER)
435				break;
436			/*
437			 * After a network partition, there can be
438			 * more than one master: the first slave to
439			 * come up will notify here the situation.
440			 */
441			(void)strcpy(to.tsp_name, hostname);
442
443			/* The other master often gets into the same state,
444			 * with boring results.
445			 */
446			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
447			for (tries = 0; tries < 3; tries++) {
448				to.tsp_type = TSP_RESOLVE;
449				answer = acksend(&to, &ntp->dest_addr,
450						 ANYADDR, TSP_MASTERACK,
451						 ntp, 0);
452				if (answer == NULL)
453					break;
454				htp = addmach(answer->tsp_name,&from,ntp);
455				to.tsp_type = TSP_QUIT;
456				answer = acksend(&to, &htp->addr, htp->name,
457						 TSP_ACK, 0, htp->noanswer);
458				if (!answer) {
459					syslog(LOG_WARNING,
460				  "conflict error: no reply from %s to QUIT",
461						htp->name);
462					(void)remmach(htp);
463				}
464			}
465			masterup(ntp);
466			break;
467
468		case TSP_MSITE:
469			if (!slavenet)
470				break;
471			taddr = from;
472			to.tsp_type = TSP_MSITEREQ;
473			to.tsp_vers = TSPVERSION;
474			to.tsp_seq = 0;
475			(void)strcpy(to.tsp_name, hostname);
476			answer = acksend(&to, &slavenet->dest_addr,
477					 ANYADDR, TSP_ACK,
478					 slavenet, 0);
479			if (answer != NULL
480			    && good_host_name(answer->tsp_name)) {
481				setmaster(answer);
482				to.tsp_type = TSP_ACK;
483				(void)strcpy(to.tsp_name, answer->tsp_name);
484				bytenetorder(&to);
485				if (sendto(sock, (char *)&to,
486					   sizeof(struct tsp), 0,
487					   (struct sockaddr*)&taddr,
488					   sizeof(taddr)) < 0) {
489					trace_sendto_err(taddr.sin_addr);
490				}
491			}
492			break;
493
494		case TSP_MSITEREQ:
495			break;
496
497		case TSP_ACCEPT:
498		case TSP_REFUSE:
499		case TSP_RESOLVE:
500			break;
501
502		case TSP_QUIT:
503			doquit(msg);		/* become a slave */
504			break;
505
506		case TSP_TEST:
507			electiontime = 0;
508			break;
509
510		case TSP_LOOP:
511			/* looking for loops of masters */
512			if (!(status & MASTER))
513				break;
514			if (fromnet->status == SLAVE) {
515			    if (!strcmp(msg->tsp_name, hostname)) {
516				/*
517				 * Someone forwarded our message back to
518				 * us.  There must be a loop.  Tell the
519				 * master of this network to quit.
520				 *
521				 * The other master often gets into
522				 * the same state, with boring results.
523				 */
524				ntp = fromnet;
525				for (tries = 0; tries < 3; tries++) {
526				    to.tsp_type = TSP_RESOLVE;
527				    answer = acksend(&to, &ntp->dest_addr,
528						     ANYADDR, TSP_MASTERACK,
529						     ntp,0);
530				    if (answer == NULL)
531					break;
532				    taddr = from;
533				    (void)strcpy(tname, answer->tsp_name);
534				    to.tsp_type = TSP_QUIT;
535				    (void)strcpy(to.tsp_name, hostname);
536				    if (!acksend(&to, &taddr, tname,
537						 TSP_ACK, 0, 1)) {
538					syslog(LOG_ERR,
539					"no reply from %s to slave LOOP-QUIT",
540						 tname);
541				    } else {
542					electiontime = 0;
543				    }
544				}
545				(void)gettimeofday(&ntime, 0);
546				looptime = ntime.tv_sec + FASTTOUT;
547			    } else {
548				if (msg->tsp_hopcnt-- < 1)
549				    break;
550				bytenetorder(msg);
551				for (ntp = nettab; ntp != 0; ntp = ntp->next) {
552				    if (ntp->status == MASTER
553					&& 0 > sendto(sock, (char *)msg,
554						      sizeof(struct tsp), 0,
555					      (struct sockaddr*)&ntp->dest_addr,
556						      sizeof(ntp->dest_addr)))
557				    trace_sendto_err(ntp->dest_addr.sin_addr);
558				}
559			    }
560			} else {	/* fromnet->status == MASTER */
561			    /*
562			     * We should not have received this from a net
563			     * we are master on.  There must be two masters,
564			     * unless the packet was really from us.
565			     */
566			    if (from.sin_addr.s_addr
567				== fromnet->my_addr.s_addr) {
568				if (trace)
569				    fprintf(fd,"discarding forwarded LOOP\n");
570				break;
571			    }
572
573			    /*
574			     * The other master often gets into the same
575			     * state, with boring results.
576			     */
577			    ntp = fromnet;
578			    for (tries = 0; tries < 3; tries++) {
579				to.tsp_type = TSP_RESOLVE;
580				answer = acksend(&to, &ntp->dest_addr,
581						 ANYADDR, TSP_MASTERACK,
582						ntp,0);
583				if (!answer)
584					break;
585				htp = addmach(answer->tsp_name,
586					      &from,ntp);
587				to.tsp_type = TSP_QUIT;
588				(void)strcpy(to.tsp_name, hostname);
589				if (!acksend(&to,&htp->addr,htp->name,
590					     TSP_ACK, 0, htp->noanswer)) {
591					syslog(LOG_ERR,
592				    "no reply from %s to master LOOP-QUIT",
593					       htp->name);
594					(void)remmach(htp);
595				}
596			    }
597			    (void)gettimeofday(&ntime, 0);
598			    looptime = ntime.tv_sec + FASTTOUT;
599			}
600			break;
601		default:
602			if (trace) {
603				fprintf(fd, "garbage message: ");
604				print(msg, &from);
605			}
606			break;
607		}
608	}
609	goto loop;
610}
611
612
613/*
614 * tell the world who our master is
615 */
616static void
617setmaster(msg)
618	struct tsp *msg;
619{
620	if (slavenet
621	    && (slavenet != old_slavenet
622		|| strcmp(msg->tsp_name, master_name)
623		|| old_status != status)) {
624		(void)strcpy(master_name, msg->tsp_name);
625		old_slavenet = slavenet;
626		old_status = status;
627
628		if (status & MASTER) {
629			syslog(LOG_NOTICE, "submaster to %s", master_name);
630			if (trace)
631				fprintf(fd, "submaster to %s\n", master_name);
632
633		} else {
634			syslog(LOG_NOTICE, "slave to %s", master_name);
635			if (trace)
636				fprintf(fd, "slave to %s\n", master_name);
637		}
638	}
639}
640
641
642
643/*
644 * handle date change request on a slave
645 */
646static void
647schgdate(msg, newdate)
648	struct tsp *msg;
649	char *newdate;
650{
651	struct tsp to;
652	u_short seq;
653	struct sockaddr_in taddr;
654	struct timeval otime;
655
656	if (!slavenet)
657		return;			/* no where to forward */
658
659	taddr = from;
660	seq = msg->tsp_seq;
661
662	syslog(LOG_INFO,
663	       "forwarding date change by %s to %s",
664	       msg->tsp_name, newdate);
665
666	/* adjust time for residence on the queue */
667	(void)gettimeofday(&otime, 0);
668	adj_msg_time(msg, &otime);
669
670	to.tsp_type = TSP_SETDATEREQ;
671	to.tsp_time = msg->tsp_time;
672	(void)strcpy(to.tsp_name, hostname);
673	if (!acksend(&to, &slavenet->dest_addr,
674		     ANYADDR, TSP_DATEACK,
675		     slavenet, 0))
676		return;			/* no answer */
677
678	xmit(TSP_DATEACK, seq, &taddr);
679}
680
681
682/*
683 * Used before answering a broadcast message to avoid network
684 * contention and likely collisions.
685 */
686static void
687answerdelay()
688{
689	struct timeval timeout;
690
691	timeout.tv_sec = 0;
692	timeout.tv_usec = delay1;
693
694	(void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
695	    &timeout);
696	return;
697}
698