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