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