xref: /openbsd-src/usr.sbin/mrouted/main.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$NetBSD: main.c,v 1.6 1995/12/10 10:07:05 mycroft Exp $	*/
2 
3 /*
4  * The mrouted program is covered by the license in the accompanying file
5  * named "LICENSE".  Use of the mrouted program represents acceptance of
6  * the terms and conditions listed in that file.
7  *
8  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9  * Leland Stanford Junior University.
10  */
11 
12 /*
13  * Written by Steve Deering, Stanford University, February 1989.
14  *
15  * (An earlier version of DVMRP was implemented by David Waitzman of
16  *  BBN STC by extending Berkeley's routed program.  Some of Waitzman's
17  *  extensions have been incorporated into mrouted, but none of the
18  *  original routed code has been adopted.)
19  */
20 
21 
22 #include "defs.h"
23 #include <stdarg.h>
24 #include <fcntl.h>
25 #include <util.h>
26 #include <err.h>
27 
28 extern char *configfilename;
29 char versionstring[100];
30 
31 static char dumpfilename[] = _PATH_MROUTED_DUMP;
32 static char cachefilename[] = _PATH_MROUTED_CACHE;
33 static char genidfilename[] = _PATH_MROUTED_GENID;
34 
35 int cache_lifetime	= DEFAULT_CACHE_LIFETIME;
36 int max_prune_lifetime	= DEFAULT_CACHE_LIFETIME * 2;
37 
38 int debug = 0;
39 u_char pruning = 1;	/* Enable pruning by default */
40 
41 #define NHANDLERS	2
42 
43 static struct ihandler {
44     int fd;			/* File descriptor		 */
45     ihfunc_t func;		/* Function to call with &fd_set */
46 } ihandlers[NHANDLERS];
47 static int nhandlers = 0;
48 
49 /*
50  * Forward declarations.
51  */
52 static void fasttimer(int);
53 static void done(int);
54 static void dump(int);
55 static void fdump(int);
56 static void cdump(int);
57 static void restart(int);
58 static void timer(void);
59 static void cleanup(void);
60 static void resetlogging(void *);
61 
62 int
63 register_input_handler(int fd, ihfunc_t func)
64 {
65     if (nhandlers >= NHANDLERS)
66 	return -1;
67 
68     ihandlers[nhandlers].fd = fd;
69     ihandlers[nhandlers++].func = func;
70 
71     return 0;
72 }
73 
74 int
75 main(int argc, char *argv[])
76 {
77     register int recvlen;
78     int dummy;
79     FILE *fp;
80     struct timeval tv;
81     u_int32_t prev_genid;
82     int vers;
83     fd_set rfds, readers;
84     int nfds, n, i, ch;
85     sigset_t mask, omask;
86     const char *errstr;
87 
88     if (geteuid() != 0) {
89 	fprintf(stderr, "must be root\n");
90 	exit(1);
91     }
92     setlinebuf(stderr);
93 
94     while ((ch = getopt(argc, argv, "c:d::p")) != -1) {
95 	    switch (ch) {
96 	    case 'c':
97 		    configfilename = optarg;
98 		    break;
99 	    case 'd':
100 		    if (!optarg)
101 			    debug = DEFAULT_DEBUG;
102 		    else {
103 			    debug = strtonum(optarg, 0, 3, &errstr);
104 			    if (errstr) {
105 				    warnx("debug level %s", errstr);
106 				    debug = DEFAULT_DEBUG;
107 			    }
108 		    }
109 		    break;
110 	    case 'p':
111 		    pruning = 0;
112 		    break;
113 	    default:
114 		    goto usage;
115 	    }
116     }
117     argc -= optind;
118     argv += optind;
119 
120     if (argc > 0) {
121 usage:	fprintf(stderr,
122 		"usage: mrouted [-p] [-c config_file] [-d [debug_level]]\n");
123 	exit(1);
124     }
125 
126     if (debug == 0) {
127 	/*
128 	 * Detach from the terminal
129 	 */
130 	int t;
131 
132 	if (fork()) exit(0);
133 	(void)close(0);
134 	(void)close(1);
135 	(void)close(2);
136 	(void)open("/", 0);
137 	(void)dup2(0, 1);
138 	(void)dup2(0, 2);
139 #ifdef SYSV
140 	(void)setpgrp();
141 #else
142 #ifdef TIOCNOTTY
143 	t = open("/dev/tty", 2);
144 	if (t >= 0) {
145 	    (void)ioctl(t, TIOCNOTTY, (char *)0);
146 	    (void)close(t);
147 	}
148 #else
149 	if (setsid() < 0)
150 	    perror("setsid");
151 #endif
152 #endif
153     }
154     else
155 	fprintf(stderr, "debug level %u\n", debug);
156 
157 #ifdef LOG_DAEMON
158     (void)openlog("mrouted", LOG_PID, LOG_DAEMON);
159     (void)setlogmask(LOG_UPTO(LOG_NOTICE));
160 #else
161     (void)openlog("mrouted", LOG_PID);
162 #endif
163     snprintf(versionstring, sizeof versionstring, "mrouted version %d.%d",
164 			PROTOCOL_VERSION, MROUTED_VERSION);
165 
166     logit(LOG_NOTICE, 0, "%s", versionstring);
167 
168 #ifdef SYSV
169     srand48(time(NULL));
170 #else
171     srandom(gethostid());
172 #endif
173 
174     /*
175      * Get generation id
176      */
177     gettimeofday(&tv, 0);
178     dvmrp_genid = tv.tv_sec;
179 
180     fp = fopen(genidfilename, "r");
181     if (fp != NULL) {
182 	fscanf(fp, "%d", &prev_genid);
183 	if (prev_genid == dvmrp_genid)
184 	    dvmrp_genid++;
185 	(void) fclose(fp);
186     }
187 
188     fp = fopen(genidfilename, "w");
189     if (fp != NULL) {
190 	fprintf(fp, "%d", dvmrp_genid);
191 	(void) fclose(fp);
192     }
193 
194     callout_init();
195     init_igmp();
196     init_routes();
197     init_ktable();
198     k_init_dvmrp();		/* enable DVMRP routing in kernel */
199 
200 #ifndef OLD_KERNEL
201     vers = k_get_version();
202     /*XXX
203      * This function must change whenever the kernel version changes
204      */
205     if ((((vers >> 8) & 0xff) != 3) ||
206 	 ((vers & 0xff) != 5))
207 	logit(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch",
208 		(vers >> 8) & 0xff, vers & 0xff,
209 		PROTOCOL_VERSION, MROUTED_VERSION);
210 #endif
211 
212     init_vifs();
213 
214 #ifdef RSRR
215     rsrr_init();
216 #endif /* RSRR */
217 
218     /*
219      * Allow cleanup if unexpected exit.  Apparently some architectures
220      * have a kernel bug where closing the socket doesn't do an
221      * ip_mrouter_done(), so we attempt to do it on exit.
222      */
223     atexit(cleanup);
224 
225     if (debug)
226 	fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
227 
228     pidfile(NULL);
229 
230     (void)signal(SIGALRM, fasttimer);
231 
232     (void)signal(SIGHUP,  restart);
233     (void)signal(SIGTERM, done);
234     (void)signal(SIGINT,  done);
235     (void)signal(SIGUSR1, fdump);
236     (void)signal(SIGUSR2, cdump);
237     if (debug != 0)
238 	(void)signal(SIGQUIT, dump);
239 
240     FD_ZERO(&readers);
241     if (igmp_socket >= FD_SETSIZE)
242 	logit(LOG_ERR, 0, "descriptor too big");
243     FD_SET(igmp_socket, &readers);
244     nfds = igmp_socket + 1;
245     for (i = 0; i < nhandlers; i++) {
246 	if (ihandlers[i].fd >= FD_SETSIZE)
247 	    logit(LOG_ERR, 0, "descriptor too big");
248 	FD_SET(ihandlers[i].fd, &readers);
249 	if (ihandlers[i].fd >= nfds)
250 	    nfds = ihandlers[i].fd + 1;
251     }
252 
253     /*
254      * Install the vifs in the kernel as late as possible in the
255      * initialization sequence.
256      */
257     init_installvifs();
258 
259     if (debug >= 2) dump(0);
260 
261     /* Start up the log rate-limiter */
262     resetlogging(NULL);
263 
264     (void)alarm(1);	 /* schedule first timer interrupt */
265 
266     /*
267      * Main receive loop.
268      */
269     dummy = 0;
270     for(;;) {
271 	bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
272 	if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0) {
273             if (errno != EINTR) /* SIGALRM is expected */
274                 logit(LOG_WARNING, errno, "select failed");
275             continue;
276         }
277 
278 	if (FD_ISSET(igmp_socket, &rfds)) {
279 	    recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
280 			       0, NULL, &dummy);
281 	    if (recvlen < 0) {
282 		if (errno != EINTR) logit(LOG_ERR, errno, "recvfrom");
283 		continue;
284 	    }
285 	    (void)sigemptyset(&mask);
286 	    (void)sigaddset(&mask, SIGALRM);
287 	    if (sigprocmask(SIG_BLOCK, &mask, &omask) < 0)
288 		    logit(LOG_ERR, errno, "sigprocmask");
289 	    accept_igmp(recvlen);
290 	    (void)sigprocmask(SIG_SETMASK, &omask, NULL);
291         }
292 
293 	for (i = 0; i < nhandlers; i++) {
294 	    if (FD_ISSET(ihandlers[i].fd, &rfds)) {
295 		(*ihandlers[i].func)(ihandlers[i].fd, &rfds);
296 	    }
297 	}
298     }
299 }
300 
301 
302 /*
303  * routine invoked every second.  Its main goal is to cycle through
304  * the routing table and send partial updates to all neighbors at a
305  * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
306  * seconds.  Also, every TIMER_INTERVAL seconds it calls timer() to
307  * do all the other time-based processing.
308  */
309 static void
310 fasttimer(int i)
311 {
312     static unsigned int tlast;
313     static unsigned int nsent;
314     register unsigned int t = tlast + 1;
315     register int n;
316 
317     /*
318      * if we're in the last second, send everything that's left.
319      * otherwise send at least the fraction we should have sent by now.
320      */
321     if (t >= ROUTE_REPORT_INTERVAL) {
322 	register int nleft = nroutes - nsent;
323 	while (nleft > 0) {
324 	    if ((n = report_next_chunk()) <= 0)
325 		break;
326 	    nleft -= n;
327 	}
328 	tlast = 0;
329 	nsent = 0;
330     } else {
331 	register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
332 	while (nsent < ncum) {
333 	    if ((n = report_next_chunk()) <= 0)
334 		break;
335 	    nsent += n;
336 	}
337 	tlast = t;
338     }
339     if ((t % TIMER_INTERVAL) == 0)
340 	timer();
341 
342     age_callout_queue();/* Advance the timer for the callout queue
343 				for groups */
344     alarm(1);
345 }
346 
347 /*
348  * The 'virtual_time' variable is initialized to a value that will cause the
349  * first invocation of timer() to send a probe or route report to all vifs
350  * and send group membership queries to all subnets for which this router is
351  * querier.  This first invocation occurs approximately TIMER_INTERVAL seconds
352  * after the router starts up.   Note that probes for neighbors and queries
353  * for group memberships are also sent at start-up time, as part of initial-
354  * ization.  This repetition after a short interval is desirable for quickly
355  * building up topology and membership information in the presence of possible
356  * packet loss.
357  *
358  * 'virtual_time' advances at a rate that is only a crude approximation of
359  * real time, because it does not take into account any time spent processing,
360  * and because the timer intervals are sometimes shrunk by a random amount to
361  * avoid unwanted synchronization with other routers.
362  */
363 
364 static u_long virtual_time = 0;
365 
366 
367 /*
368  * Timer routine.  Performs periodic neighbor probing, route reporting, and
369  * group querying duties, and drives various timers in routing entries and
370  * virtual interface data structures.
371  */
372 static void
373 timer(void)
374 {
375     age_routes();	/* Advance the timers in the route entries     */
376     age_vifs();		/* Advance the timers for neighbors */
377     age_table_entry();	/* Advance the timers for the cache entries */
378 
379     if (virtual_time % GROUP_QUERY_INTERVAL == 0) {
380 	/*
381 	 * Time to query the local group memberships on all subnets
382 	 * for which this router is the elected querier.
383 	 */
384 	query_groups();
385     }
386 
387     if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
388 	/*
389 	 * Time to send a probe on all vifs from which no neighbors have
390 	 * been heard.  Also, check if any inoperative interfaces have now
391 	 * come up.  (If they have, they will also be probed as part of
392 	 * their initialization.)
393 	 */
394 	probe_for_neighbors();
395 
396 	if (vifs_down)
397 	    check_vif_state();
398     }
399 
400     delay_change_reports = FALSE;
401     if (routes_changed) {
402 	/*
403 	 * Some routes have changed since the last timer interrupt, but
404 	 * have not been reported yet.  Report the changed routes to all
405 	 * neighbors.
406 	 */
407 	report_to_all_neighbors(CHANGED_ROUTES);
408     }
409 
410     /*
411      * Advance virtual time
412      */
413     virtual_time += TIMER_INTERVAL;
414 }
415 
416 
417 /*
418  * On termination, let everyone know we're going away.
419  */
420 static void
421 done(int i)
422 {
423     logit(LOG_NOTICE, 0, "%s exiting", versionstring);
424     cleanup();
425     _exit(1);
426 }
427 
428 static void
429 cleanup(void)
430 {
431     static in_cleanup = 0;
432 
433     if (!in_cleanup) {
434 	in_cleanup++;
435 #ifdef RSRR
436 	rsrr_clean();
437 #endif /* RSRR */
438 	expire_all_routes();
439 	report_to_all_neighbors(ALL_ROUTES);
440 	k_stop_dvmrp();
441     }
442 }
443 
444 
445 /*
446  * Dump internal data structures to stderr.
447  */
448 static void
449 dump(int i)
450 {
451     dump_vifs(stderr);
452     dump_routes(stderr);
453 }
454 
455 
456 /*
457  * Dump internal data structures to a file.
458  */
459 static void
460 fdump(int i)
461 {
462     FILE *fp;
463 
464     fp = fopen(dumpfilename, "w");
465     if (fp != NULL) {
466 	dump_vifs(fp);
467 	dump_routes(fp);
468 	(void) fclose(fp);
469     }
470 }
471 
472 
473 /*
474  * Dump local cache contents to a file.
475  */
476 static void
477 cdump(int i)
478 {
479     FILE *fp;
480 
481     fp = fopen(cachefilename, "w");
482     if (fp != NULL) {
483 	dump_cache(fp);
484 	(void) fclose(fp);
485     }
486 }
487 
488 
489 /*
490  * Restart mrouted
491  */
492 static void
493 restart(int i)
494 {
495     sigset_t mask, omask;
496 
497     logit(LOG_NOTICE, 0, "%s restart", versionstring);
498 
499     /*
500      * reset all the entries
501      */
502     (void)sigemptyset(&mask);
503     (void)sigaddset(&mask, SIGALRM);
504     if (sigprocmask(SIG_BLOCK, &mask, &omask) < 0)
505 	logit(LOG_ERR, errno, "sigprocmask");
506     free_all_prunes();
507     free_all_routes();
508     stop_all_vifs();
509     k_stop_dvmrp();
510     close(igmp_socket);
511     close(udp_socket);
512 
513     /*
514      * start processing again
515      */
516     dvmrp_genid++;
517     pruning = 1;
518 
519     init_igmp();
520     init_routes();
521     init_ktable();
522     init_vifs();
523     k_init_dvmrp();		/* enable DVMRP routing in kernel */
524     init_installvifs();
525 
526     (void)sigprocmask(SIG_SETMASK, &omask, NULL);
527 }
528 
529 #define LOG_MAX_MSGS	20	/* if > 20/minute then shut up for a while */
530 #define LOG_SHUT_UP	600	/* shut up for 10 minutes */
531 static int log_nmsgs = 0;
532 
533 static void
534 resetlogging(void *arg)
535 {
536     int nxttime = 60;
537     void *narg = NULL;
538 
539     if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) {
540 	nxttime = LOG_SHUT_UP;
541 	narg = (void *)&log_nmsgs;	/* just need some valid void * */
542 	syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes",
543 			LOG_SHUT_UP / 60);
544     } else {
545 	log_nmsgs = 0;
546     }
547 
548     timer_setTimer(nxttime, resetlogging, narg);
549 }
550 
551 /*
552  * Log errors and other messages to the system log daemon and to stderr,
553  * according to the severity of the message and the current debug level.
554  * For errors of severity LOG_ERR or worse, terminate the program.
555  */
556 void
557 logit(int severity, int syserr, char *format, ...)
558 {
559     va_list ap;
560     static char fmt[211] = "warning - ";
561     char *msg;
562     char tbuf[20];
563     struct timeval now;
564     struct tm *thyme;
565     time_t t;
566 
567     va_start(ap, format);
568     vsnprintf(&fmt[10], sizeof fmt - 10, format, ap);
569     va_end(ap);
570     msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
571 
572     switch (debug) {
573 	case 0: break;
574 	case 1: if (severity > LOG_NOTICE) break;
575 	case 2: if (severity > LOG_INFO  ) break;
576 	default:
577 	    gettimeofday(&now,NULL);
578 	    t = now.tv_sec;
579 	    thyme = localtime(&t);
580 	    strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme);
581 	    fprintf(stderr, tbuf, now.tv_usec / 1000);
582 	    fprintf(stderr, "%s", msg);
583 	    if (syserr == 0)
584 		fprintf(stderr, "\n");
585 	    else if (syserr < sys_nerr)
586 		fprintf(stderr, ": %s\n", sys_errlist[syserr]);
587 	    else
588 		fprintf(stderr, ": errno %d\n", syserr);
589     }
590 
591     if (severity <= LOG_NOTICE) {
592 	if (log_nmsgs++ < LOG_MAX_MSGS) {
593 	    if (syserr != 0) {
594 		errno = syserr;
595 		syslog(severity, "%s: %m", msg);
596 	    } else
597 		syslog(severity, "%s", msg);
598 	}
599 
600 	if (severity <= LOG_ERR) exit(1);
601     }
602 }
603 
604 #ifdef DEBUG_MFC
605 void
606 md_logit(int what, u_int32_t origin, u_int32_t mcastgrp)
607 {
608     static FILE *f = NULL;
609     struct timeval tv;
610     u_int32_t buf[4];
611 
612     if (!f) {
613 	if ((f = fopen("/tmp/mrouted.clog", "w")) == NULL) {
614 	    logit(LOG_ERR, errno, "open /tmp/mrouted.clog");
615 	}
616     }
617 
618     gettimeofday(&tv, NULL);
619     buf[0] = tv.tv_sec;
620     buf[1] = what;
621     buf[2] = origin;
622     buf[3] = mcastgrp;
623 
624     fwrite(buf, sizeof(u_int32_t), 4, f);
625 }
626 #endif
627