xref: /original-bsd/usr.sbin/timed/timed/timed.c (revision 1451ccc8)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1985 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)timed.c	2.14 (Berkeley) 06/18/88";
26 #endif /* not lint */
27 
28 #include "globals.h"
29 #define TSPTYPES
30 #include <protocols/timed.h>
31 #include <net/if.h>
32 #include <sys/file.h>
33 #include <sys/ioctl.h>
34 #include <setjmp.h>
35 
36 int id;
37 int trace;
38 int sock, sock_raw = -1;
39 int status = 0;
40 int backoff;
41 int slvcount;				/* no. of slaves controlled by master */
42 int machup;
43 u_short sequence;			/* sequence number */
44 long delay1;
45 long delay2;
46 long random();
47 char hostname[MAXHOSTNAMELEN];
48 struct host hp[NHOSTS];
49 char tracefile[] = "/usr/adm/timed.log";
50 FILE *fd;
51 jmp_buf jmpenv;
52 struct netinfo *nettab = NULL;
53 int nslavenets;		/* Number of networks were I could be a slave */
54 int nmasternets;	/* Number of networks were I could be a master */
55 int nignorednets;	/* Number of ignored networks */
56 int nnets;		/* Number of networks I am connected to */
57 struct netinfo *slavenet;
58 struct netinfo *firstslavenet();
59 int Mflag;
60 int justquit = 0;
61 
62 struct nets {
63 	char *name;
64 	long net;
65 	struct nets *next;
66 } *nets = (struct nets *)0;
67 
68 /*
69  * The timedaemons synchronize the clocks of hosts in a local area network.
70  * One daemon runs as master, all the others as slaves. The master
71  * performs the task of computing clock differences and sends correction
72  * values to the slaves.
73  * Slaves start an election to choose a new master when the latter disappears
74  * because of a machine crash, network partition, or when killed.
75  * A resolution protocol is used to kill all but one of the masters
76  * that happen to exist in segments of a partitioned network when the
77  * network partition is fixed.
78  *
79  * Authors: Riccardo Gusella & Stefano Zatti
80  */
81 
82 main(argc, argv)
83 int argc;
84 char **argv;
85 {
86 	int on;
87 	int ret;
88 	long seed;
89 	int nflag, iflag;
90 	struct timeval time;
91 	struct servent *srvp;
92 	long casual();
93 	char *date();
94 	int n;
95 	int flag;
96 	char buf[BUFSIZ];
97 	struct ifconf ifc;
98 	struct ifreq ifreq, *ifr;
99 	register struct netinfo *ntp;
100 	struct netinfo *ntip;
101 	struct netinfo *savefromnet;
102 	struct sockaddr_in server;
103 	u_short port;
104 	uid_t getuid();
105 
106 #ifdef lint
107 	ntip = NULL;
108 #endif
109 
110 	Mflag = 0;
111 	on = 1;
112 	backoff = 1;
113 	trace = OFF;
114 	nflag = OFF;
115 	iflag = OFF;
116 	openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
117 
118 	if (getuid() != 0) {
119 		fprintf(stderr, "Timed: not superuser\n");
120 		exit(1);
121 	}
122 
123 	while (--argc > 0 && **++argv == '-') {
124 		(*argv)++;
125 		do {
126 			switch (**argv) {
127 
128 			case 'M':
129 				Mflag = 1;
130 				break;
131 			case 't':
132 				trace = ON;
133 				break;
134 			case 'n':
135 				argc--, argv++;
136 				if (iflag) {
137 					fprintf(stderr,
138 				    "timed: -i and -n make no sense together\n");
139 				} else {
140 					nflag = ON;
141 					addnetname(*argv);
142 				}
143 				while (*(++(*argv)+1)) ;
144 				break;
145 			case 'i':
146 				argc--, argv++;
147 				if (nflag) {
148 					fprintf(stderr,
149 				    "timed: -i and -n make no sense together\n");
150 				} else {
151 					iflag = ON;
152 					addnetname(*argv);
153 				}
154 				while (*(++(*argv)+1)) ;
155 				break;
156 			default:
157 				fprintf(stderr, "timed: -%c: unknown option\n",
158 							**argv);
159 				break;
160 			}
161 		} while (*++(*argv));
162 	}
163 
164 #ifndef DEBUG
165 	if (fork())
166 		exit(0);
167 	{ int s;
168 	  for (s = getdtablesize(); s >= 0; --s)
169 		(void) close(s);
170 	  (void) open("/dev/null", 0);
171 	  (void) dup2(0, 1);
172 	  (void) dup2(0, 2);
173 	  s = open("/dev/tty", 2);
174 	  if (s >= 0) {
175 		(void) ioctl(s, TIOCNOTTY, (char *)0);
176 		(void) close(s);
177 	  }
178 	}
179 #endif
180 
181 	if (trace == ON) {
182 		fd = fopen(tracefile, "w");
183 		setlinebuf(fd);
184 		fprintf(fd, "Tracing started on: %s\n\n",
185 					date());
186 	}
187 
188 	srvp = getservbyname("timed", "udp");
189 	if (srvp == 0) {
190 		syslog(LOG_CRIT, "unknown service 'timed/udp'");
191 		exit(1);
192 	}
193 	port = srvp->s_port;
194 	server.sin_port = srvp->s_port;
195 	server.sin_family = AF_INET;
196 	sock = socket(AF_INET, SOCK_DGRAM, 0);
197 	if (sock < 0) {
198 		syslog(LOG_ERR, "socket: %m");
199 		exit(1);
200 	}
201 	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
202 							sizeof(on)) < 0) {
203 		syslog(LOG_ERR, "setsockopt: %m");
204 		exit(1);
205 	}
206 	if (bind(sock, &server, sizeof(server))) {
207 		if (errno == EADDRINUSE)
208 		        syslog(LOG_ERR, "server already running");
209 		else
210 		        syslog(LOG_ERR, "bind: %m");
211 		exit(1);
212 	}
213 
214 	/* choose a unique seed for random number generation */
215 	(void)gettimeofday(&time, (struct timezone *)0);
216 	seed = time.tv_sec + time.tv_usec;
217 	srandom(seed);
218 
219 	sequence = random();     /* initial seq number */
220 
221 	/* rounds kernel variable time to multiple of 5 ms. */
222 	time.tv_sec = 0;
223 	time.tv_usec = -((time.tv_usec/1000) % 5) * 1000;
224 	(void)adjtime(&time, (struct timeval *)0);
225 
226 	id = getpid();
227 
228 	if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
229 		syslog(LOG_ERR, "gethostname: %m");
230 		exit(1);
231 	}
232 	hp[0].name = hostname;
233 
234 	if (nflag || iflag) {
235 		struct netent *getnetent();
236 		struct netent *n;
237 		struct nets *np;
238 		for ( np = nets ; np ; np = np->next) {
239 			n = getnetbyname(np->name);
240 			if (n == NULL) {
241 				syslog(LOG_ERR, "getnetbyname: unknown net %s",
242 					np->name);
243 				exit(1);
244 			}
245 			np->net = n->n_net;
246 		}
247 	}
248 	ifc.ifc_len = sizeof(buf);
249 	ifc.ifc_buf = buf;
250 	if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
251 		syslog(LOG_ERR, "get interface configuration: %m");
252 		exit(1);
253 	}
254 	n = ifc.ifc_len/sizeof(struct ifreq);
255 	ntp = NULL;
256 	for (ifr = ifc.ifc_req; n > 0; n--, ifr++) {
257 		if (ifr->ifr_addr.sa_family != AF_INET)
258 			continue;
259 		ifreq = *ifr;
260 		if (ntp == NULL)
261 			ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
262 		ntp->my_addr =
263 			((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
264 		if (ioctl(sock, SIOCGIFFLAGS,
265 					(char *)&ifreq) < 0) {
266 			syslog(LOG_ERR, "get interface flags: %m");
267 			continue;
268 		}
269 		if ((ifreq.ifr_flags & IFF_UP) == 0 ||
270 			((ifreq.ifr_flags & IFF_BROADCAST) == 0 &&
271 			(ifreq.ifr_flags & IFF_POINTOPOINT) == 0)) {
272 			continue;
273 		}
274 		if (ifreq.ifr_flags & IFF_BROADCAST)
275 			flag = 1;
276 		else
277 			flag = 0;
278 		if (ioctl(sock, SIOCGIFNETMASK,
279 					(char *)&ifreq) < 0) {
280 			syslog(LOG_ERR, "get netmask: %m");
281 			continue;
282 		}
283 		ntp->mask = ((struct sockaddr_in *)
284 			&ifreq.ifr_addr)->sin_addr.s_addr;
285 		if (flag) {
286 			if (ioctl(sock, SIOCGIFBRDADDR,
287 						(char *)&ifreq) < 0) {
288 				syslog(LOG_ERR, "get broadaddr: %m");
289 				continue;
290 			}
291 			ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
292 		} else {
293 			if (ioctl(sock, SIOCGIFDSTADDR,
294 						(char *)&ifreq) < 0) {
295 				syslog(LOG_ERR, "get destaddr: %m");
296 				continue;
297 			}
298 			ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
299 		}
300 		ntp->dest_addr.sin_port = port;
301 		if (nflag || iflag) {
302 			u_long addr, mask;
303 			struct nets *n;
304 
305 			addr = ntohl(ntp->dest_addr.sin_addr.s_addr);
306 			mask = ntohl(ntp->mask);
307 			while ((mask & 1) == 0) {
308 				addr >>= 1;
309 				mask >>= 1;
310 			}
311 			for (n = nets ; n ; n = n->next)
312 				if (addr == n->net)
313 					break;
314 			if (nflag && !n || iflag && n)
315 				continue;
316 		}
317 		ntp->net = ntp->mask & ntp->dest_addr.sin_addr.s_addr;
318 		ntp->next = NULL;
319 		if (nettab == NULL) {
320 			nettab = ntp;
321 		} else {
322 			ntip->next = ntp;
323 		}
324 		ntip = ntp;
325 		ntp = NULL;
326 	}
327 	if (ntp)
328 		(void) free((char *)ntp);
329 	if (nettab == NULL) {
330 		syslog(LOG_ERR, "No network usable");
331 		exit(1);
332 	}
333 
334 	for (ntp = nettab; ntp != NULL; ntp = ntp->next)
335 		lookformaster(ntp);
336 	setstatus();
337 	/*
338 	 * Take care of some basic initialization.
339 	 */
340 	/* us. delay to be used in response to broadcast */
341 	delay1 = casual((long)10000, 200000);
342 
343 	/* election timer delay in secs. */
344 	delay2 = casual((long)MINTOUT, (long)MAXTOUT);
345 
346 	if (Mflag) {
347 		/*
348 		 * number (increased by 1) of slaves controlled by master:
349 		 * used in master.c, candidate.c, networkdelta.c, and
350 		 * correct.c
351 		 */
352 		slvcount = 1;
353 		ret = setjmp(jmpenv);
354 
355 		switch (ret) {
356 
357 		case 0:
358 			makeslave(firstslavenet());
359 			setstatus();
360 			break;
361 		case 1:
362 			/* Just lost our master */
363 			setstatus();
364 			slavenet->status = election(slavenet);
365 			checkignorednets();
366 			setstatus();
367 			if (slavenet->status == MASTER)
368 				makeslave(firstslavenet());
369 			else
370 				makeslave(slavenet);
371 			setstatus();
372 			break;
373 		case 2:
374 			/* Just been told to quit */
375 			fromnet->status = SLAVE;
376 			setstatus();
377 			savefromnet = fromnet;
378 			rmnetmachs(fromnet);
379 			checkignorednets();
380 			if (slavenet)
381 				makeslave(slavenet);
382 			else
383 				makeslave(savefromnet);
384 			setstatus();
385 			justquit = 1;
386 			break;
387 
388 		default:
389 			/* this should not happen */
390 			syslog(LOG_ERR, "Attempt to enter invalid state");
391 			break;
392 		}
393 
394 		if (status & MASTER) {
395 			/* open raw socket used to measure time differences */
396 			if (sock_raw == -1) {
397 			    sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
398 			    if (sock_raw < 0)  {
399 				    syslog(LOG_ERR, "opening raw socket: %m");
400 				    exit (1);
401 			    }
402 			}
403 		} else {
404 			/* sock_raw is not being used now */
405 			if (sock_raw != -1) {
406 			    (void)close(sock_raw);
407 			    sock_raw = -1;
408 			}
409 		}
410 
411 		if (status == MASTER)
412 			master();
413 		else
414 			slave();
415 	} else {
416 		/* if Mflag is not set timedaemon is forced to act as a slave */
417 		status = SLAVE;
418 		if (setjmp(jmpenv)) {
419 			setstatus();
420 			checkignorednets();
421 		}
422 		makeslave(firstslavenet());
423 		for (ntp = nettab; ntp != NULL; ntp = ntp->next)
424 			if (ntp->status == MASTER)
425 				ntp->status = IGNORE;
426 		setstatus();
427 		slave();
428 	}
429 }
430 
431 /*
432  * Try to become master over ignored nets..
433  */
434 checkignorednets()
435 {
436 	register struct netinfo *ntp;
437 	for (ntp = nettab; ntp != NULL; ntp = ntp->next)
438 		if (ntp->status == IGNORE)
439 			lookformaster(ntp);
440 }
441 
442 lookformaster(ntp)
443 	register struct netinfo *ntp;
444 {
445 	struct tsp resp, conflict, *answer, *readmsg(), *acksend();
446 	struct timeval time;
447 	char mastername[MAXHOSTNAMELEN];
448 	struct sockaddr_in masteraddr;
449 
450 	ntp->status = SLAVE;
451 	/* look for master */
452 	resp.tsp_type = TSP_MASTERREQ;
453 	(void)strcpy(resp.tsp_name, hostname);
454 	answer = acksend(&resp, &ntp->dest_addr, (char *)ANYADDR,
455 	    TSP_MASTERACK, ntp);
456 	if (answer == NULL) {
457 		/*
458 		 * Various conditions can cause conflict: race between
459 		 * two just started timedaemons when no master is
460 		 * present, or timedaemon started during an election.
461 		 * Conservative approach is taken: give up and became a
462 		 * slave postponing election of a master until first
463 		 * timer expires.
464 		 */
465 		time.tv_sec = time.tv_usec = 0;
466 		answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR,
467 		    &time, ntp);
468 		if (answer != NULL) {
469 			ntp->status = SLAVE;
470 			return;
471 		}
472 
473 		time.tv_sec = time.tv_usec = 0;
474 		answer = readmsg(TSP_MASTERUP, (char *)ANYADDR,
475 		    &time, ntp);
476 		if (answer != NULL) {
477 			ntp->status = SLAVE;
478 			return;
479 		}
480 
481 		time.tv_sec = time.tv_usec = 0;
482 		answer = readmsg(TSP_ELECTION, (char *)ANYADDR,
483 		    &time, ntp);
484 		if (answer != NULL) {
485 			ntp->status = SLAVE;
486 			return;
487 		}
488 		ntp->status = MASTER;
489 	} else {
490 		(void)strcpy(mastername, answer->tsp_name);
491 		masteraddr = from;
492 
493 		/*
494 		 * If network has been partitioned, there might be other
495 		 * masters; tell the one we have just acknowledged that
496 		 * it has to gain control over the others.
497 		 */
498 		time.tv_sec = 0;
499 		time.tv_usec = 300000;
500 		answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time,
501 		    ntp);
502 		/*
503 		 * checking also not to send CONFLICT to ack'ed master
504 		 * due to duplicated MASTERACKs
505 		 */
506 		if (answer != NULL &&
507 		    strcmp(answer->tsp_name, mastername) != 0) {
508 			conflict.tsp_type = TSP_CONFLICT;
509 			(void)strcpy(conflict.tsp_name, hostname);
510 			if (acksend(&conflict, &masteraddr, mastername,
511 			    TSP_ACK, (struct netinfo *)NULL) == NULL) {
512 				syslog(LOG_ERR,
513 				    "error on sending TSP_CONFLICT");
514 				exit(1);
515 			}
516 		}
517 	}
518 }
519 /*
520  * based on the current network configuration, set the status, and count
521  * networks;
522  */
523 setstatus()
524 {
525 	register struct netinfo *ntp;
526 
527 	status = 0;
528 	nmasternets = nslavenets = nnets = nignorednets = 0;
529 	if (trace)
530 		fprintf(fd, "Net status:\n");
531 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
532 		switch ((int)ntp->status) {
533 		  case MASTER:
534 			nmasternets++;
535 			break;
536 		  case SLAVE:
537 			nslavenets++;
538 			break;
539 		  case IGNORE:
540 			nignorednets++;
541 			break;
542 		}
543 		if (trace) {
544 			fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
545 			switch ((int)ntp->status) {
546 			  case MASTER:
547 				fprintf(fd, "MASTER\n");
548 				break;
549 			  case SLAVE:
550 				fprintf(fd, "SLAVE\n");
551 				break;
552 			  case IGNORE:
553 				fprintf(fd, "IGNORE\n");
554 				break;
555 			  default:
556 				fprintf(fd, "invalid state %d\n",(int)ntp->status);
557 				break;
558 			}
559 		}
560 		nnets++;
561 		status |= ntp->status;
562 	}
563 	status &= ~IGNORE;
564 	if (trace)
565 		fprintf(fd,
566 		      "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
567 		      nnets, nmasternets, nslavenets, nignorednets);
568 }
569 
570 makeslave(net)
571 	struct netinfo *net;
572 {
573 	register struct netinfo *ntp;
574 
575 	for (ntp = nettab; ntp != NULL; ntp = ntp->next)
576 		if (ntp->status == SLAVE && ntp != net)
577 			ntp->status = IGNORE;
578 	slavenet = net;
579 }
580 
581 struct netinfo *
582 firstslavenet()
583 {
584 	register struct netinfo *ntp;
585 
586 	for (ntp = nettab; ntp != NULL; ntp = ntp->next)
587 		if (ntp->status == SLAVE)
588 			return (ntp);
589 	return ((struct netinfo *)0);
590 }
591 
592 /*
593  * `casual' returns a random number in the range [inf, sup]
594  */
595 
596 long
597 casual(inf, sup)
598 long inf;
599 long sup;
600 {
601 	float value;
602 
603 	value = (float)(random() & 0x7fffffff) / 0x7fffffff;
604 	return(inf + (sup - inf) * value);
605 }
606 
607 char *
608 date()
609 {
610 	char    *ctime();
611 	struct	timeval tv;
612 
613 	(void)gettimeofday(&tv, (struct timezone *)0);
614 	return (ctime(&tv.tv_sec));
615 }
616 
617 addnetname(name)
618 	char *name;
619 {
620 	register struct nets **netlist = &nets;
621 
622 	while (*netlist)
623 		netlist = &((*netlist)->next);
624 	*netlist = (struct nets *)malloc(sizeof **netlist);
625 	if (*netlist == (struct nets *)0) {
626 		syslog(LOG_ERR, "malloc failed");
627 		exit(1);
628 	}
629 	bzero((char *)*netlist, sizeof(**netlist));
630 	(*netlist)->name = name;
631 }
632