Home | History | Annotate | Line # | Download | only in mrouted
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