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