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