Home | History | Annotate | Line # | Download | only in mrouted
main.c revision 1.12
      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