Home | History | Annotate | Line # | Download | only in mrouted
main.c revision 1.4
      1 /*
      2  * The mrouted program is covered by the license in the accompanying file
      3  * named "LICENSE".  Use of the mrouted program represents acceptance of
      4  * the terms and conditions listed in that file.
      5  *
      6  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
      7  * Leland Stanford Junior University.
      8  *
      9  *
     10  * $Id: main.c,v 1.4 1995/06/01 02:25:57 mycroft Exp $
     11  */
     12 
     13 /*
     14  * Written by Steve Deering, Stanford University, February 1989.
     15  *
     16  * (An earlier version of DVMRP was implemented by David Waitzman of
     17  *  BBN STC by extending Berkeley's routed program.  Some of Waitzman's
     18  *  extensions have been incorporated into mrouted, but none of the
     19  *  original routed code has been adopted.)
     20  */
     21 
     22 
     23 #include "defs.h"
     24 #include <string.h>
     25 #include <varargs.h>
     26 
     27 #ifdef SNMP
     28 #include "snmp.h"
     29 #endif
     30 
     31 extern char *configfilename;
     32 
     33 static char pidfilename[]  = _PATH_MROUTED_PID;
     34 static char dumpfilename[] = _PATH_MROUTED_DUMP;
     35 static char cachefilename[] = _PATH_MROUTED_CACHE;
     36 static char genidfilename[] = _PATH_MROUTED_GENID;
     37 
     38 int cache_lifetime 	= DEFAULT_CACHE_LIFETIME;
     39 int max_prune_lifetime 	= DEFAULT_CACHE_LIFETIME * 2;
     40 
     41 int debug = 0;
     42 u_char pruning = 1;	/* Enable pruning by default */
     43 
     44 #define NHANDLERS	2
     45 
     46 static struct ihandler {
     47     int fd;			/* File descriptor		 */
     48     void (*func)();		/* Function to call with &fd_set */
     49 } ihandlers[NHANDLERS];
     50 static int nhandlers = 0;
     51 
     52 /*
     53  * Forward declarations.
     54  */
     55 static void fasttimer();
     56 static void timer();
     57 static void cleanup();
     58 static void done();
     59 static void dump();
     60 static void fdump();
     61 static void cdump();
     62 static void restart();
     63 
     64 int
     65 register_input_handler(fd, func)
     66     int fd;
     67     void (*func)();
     68 {
     69     if (nhandlers >= NHANDLERS)
     70 	return -1;
     71 
     72     ihandlers[nhandlers].fd = fd;
     73     ihandlers[nhandlers++].func = func;
     74 
     75     return 0;
     76 }
     77 
     78 int main(argc, argv)
     79     int argc;
     80     char *argv[];
     81 {
     82     register int recvlen;
     83     register int omask;
     84     int dummy;
     85     FILE *fp;
     86     extern uid_t geteuid();
     87     struct timeval tv;
     88     u_long prev_genid;
     89     int vers;
     90     fd_set rfds, readers;
     91     int nfds, n, i;
     92 #ifdef SNMP
     93     char *myname;
     94     fd_set wfds;
     95 
     96 
     97     if (myname = strrchr(argv[0], '/'))
     98         myname++;
     99     if (myname == NULL || *myname == 0)
    100         myname = argv[0];
    101     isodetailor (myname, 0);
    102 #endif
    103 
    104 #ifdef SYSV
    105     setvbuf(stderr, NULL, _IOLBF, 0);
    106 #else
    107     setlinebuf(stderr);
    108 #endif
    109 
    110     if (geteuid() != 0) {
    111 	fprintf(stderr, "must be root\n");
    112 	exit(1);
    113     }
    114 
    115     argv++, argc--;
    116     while (argc > 0 && *argv[0] == '-') {
    117 	if (strcmp(*argv, "-d") == 0) {
    118 	    if (argc > 1 && isdigit(*(argv + 1)[0])) {
    119 		argv++, argc--;
    120 		debug = atoi(*argv);
    121 	    } else
    122 		debug = DEFAULT_DEBUG;
    123 	} else if (strcmp(*argv, "-c") == 0) {
    124 	    if (argc > 1) {
    125 		argv++, argc--;
    126 		configfilename = *argv;
    127 	    } else
    128 		goto usage;
    129 	} else if (strcmp(*argv, "-p") == 0) {
    130 	    pruning = 0;
    131 	} else
    132 	    goto usage;
    133 	argv++, argc--;
    134     }
    135 
    136     if (argc > 0) {
    137 usage:	fprintf(stderr,
    138 		"usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
    139 	exit(1);
    140     }
    141 
    142     if (debug == 0) {
    143 	/*
    144 	 * Detach from the terminal
    145 	 */
    146 	int t;
    147 
    148 	if (fork()) exit(0);
    149 	(void)close(0);
    150 	(void)close(1);
    151 	(void)close(2);
    152 	(void)open("/", 0);
    153 	(void)dup2(0, 1);
    154 	(void)dup2(0, 2);
    155 #ifdef TIOCNOTTY
    156 	t = open("/dev/tty", 2);
    157 	if (t >= 0) {
    158 	    (void)ioctl(t, TIOCNOTTY, (char *)0);
    159 	    (void)close(t);
    160 	}
    161 #else
    162 	if (setsid() < 0)
    163 	    perror("setsid");
    164 #endif
    165     }
    166     else
    167 	fprintf(stderr, "debug level %u\n", debug);
    168 
    169 #ifdef LOG_DAEMON
    170     (void)openlog("mrouted", LOG_PID, LOG_DAEMON);
    171     (void)setlogmask(LOG_UPTO(LOG_NOTICE));
    172 #else
    173     (void)openlog("mrouted", LOG_PID);
    174 #endif
    175     log(LOG_NOTICE, 0, "mrouted version %d.%d",
    176 			PROTOCOL_VERSION, MROUTED_VERSION);
    177 
    178 #ifdef SYSV
    179     srand48(time(NULL));
    180 #else
    181     srandom(gethostid());
    182 #endif
    183 
    184     /*
    185      * Get generation id
    186      */
    187     gettimeofday(&tv, 0);
    188     dvmrp_genid = tv.tv_sec;
    189 
    190     fp = fopen(genidfilename, "r");
    191     if (fp != NULL) {
    192 	fscanf(fp, "%d", &prev_genid);
    193 	if (prev_genid == dvmrp_genid)
    194 	    dvmrp_genid++;
    195 	(void) fclose(fp);
    196     }
    197 
    198     fp = fopen(genidfilename, "w");
    199     if (fp != NULL) {
    200 	fprintf(fp, "%d", dvmrp_genid);
    201 	(void) fclose(fp);
    202     }
    203 
    204     callout_init();
    205 
    206 #ifdef SNMP
    207     snmp_init();
    208 #endif
    209 
    210     init_igmp();
    211     k_init_dvmrp();		/* enable DVMRP routing in kernel */
    212 
    213 #ifndef OLD_KERNEL
    214     vers = k_get_version();
    215     if ((((vers >> 8) & 0xff) != PROTOCOL_VERSION) ||
    216 	 ((vers & 0xff) != MROUTED_VERSION))
    217 	log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch",
    218 		(vers >> 8) & 0xff, vers & 0xff,
    219 		PROTOCOL_VERSION, MROUTED_VERSION);
    220 #endif
    221 
    222     init_routes();
    223     init_ktable();
    224     init_vifs();
    225 #ifdef RSRR
    226     rsrr_init();
    227 #endif /* RSRR */
    228 
    229 #if defined(__STDC__) || defined(__GNUC__)
    230     /* Allow cleanup if unexpected exit.  Apparently some architectures
    231      * have a kernel bug where closing the socket doesn't do an
    232      * ip_mrouter_done(), so we attempt to do it on exit.
    233      */
    234     atexit(cleanup);
    235 #endif
    236 
    237     if (debug)
    238 	fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
    239 
    240     fp = fopen(pidfilename, "w");
    241     if (fp != NULL) {
    242 	fprintf(fp, "%d\n", getpid());
    243 	(void) fclose(fp);
    244     }
    245 
    246     if (debug >= 2) dump();
    247 
    248     (void)signal(SIGALRM, fasttimer);
    249 
    250     (void)signal(SIGHUP,  restart);
    251     (void)signal(SIGTERM, done);
    252     (void)signal(SIGINT,  done);
    253     (void)signal(SIGUSR1, fdump);
    254     (void)signal(SIGUSR2, cdump);
    255     if (debug != 0)
    256 	(void)signal(SIGQUIT, dump);
    257 
    258     FD_ZERO(&readers);
    259     FD_SET(igmp_socket, &readers);
    260     nfds = igmp_socket + 1;
    261     for (i = 0; i < nhandlers; i++) {
    262 	FD_SET(ihandlers[i].fd, &readers);
    263 	if (ihandlers[i].fd >= nfds)
    264 	    nfds = ihandlers[i].fd + 1;
    265     }
    266 
    267     (void)alarm(1);	 /* schedule first timer interrupt */
    268 
    269     /*
    270      * Main receive loop.
    271      */
    272     dummy = 0;
    273     for(;;) {
    274 	bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
    275 #ifdef SNMP
    276         FD_ZERO(&wfds);
    277 
    278         if (smux_fd != NOTOK) {
    279            if (rock_and_roll)
    280               FD_SET(smux_fd, &rfds);
    281            else
    282               FD_SET(smux_fd, &wfds);
    283            if (smux_fd >= nfds)
    284               nfds = smux_fd + 1;
    285         }
    286 
    287         if ((n = xselect(nfds, &rfds, &wfds, NULLFD, NOTOK))==NOTOK) {
    288 #else
    289 	if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0) {
    290 #endif
    291             if (errno != EINTR) /* SIGALRM is expected */
    292                 log(LOG_WARNING, errno, "select failed");
    293             continue;
    294         }
    295 
    296 	if (FD_ISSET(igmp_socket, &rfds)) {
    297 	    recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
    298 			       0, NULL, &dummy);
    299 	    if (recvlen < 0) {
    300 		if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
    301 		continue;
    302 	    }
    303 	    omask = sigblock(sigmask(SIGALRM));
    304 	    accept_igmp(recvlen);
    305 	    (void)sigsetmask(omask);
    306         }
    307 
    308 	for (i = 0; i < nhandlers; i++) {
    309 	    if (FD_ISSET(ihandlers[i].fd, &rfds)) {
    310 		(*ihandlers[i].func)(&rfds);
    311 	    }
    312 	}
    313 
    314 #ifdef SNMP
    315         if (smux_fd != NOTOK) {
    316             if (rock_and_roll) {
    317 		if (FD_ISSET(smux_fd, &rfds))
    318 		    doit_smux();
    319 	    } else if (FD_ISSET(smux_fd, &wfds))
    320                 start_smux();
    321         }
    322 #endif
    323     }
    324 }
    325 
    326 
    327 /*
    328  * routine invoked every second.  Its main goal is to cycle through
    329  * the routing table and send partial updates to all neighbors at a
    330  * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
    331  * seconds.  Also, every TIMER_INTERVAL seconds it calls timer() to
    332  * do all the other time-based processing.
    333  */
    334 static void
    335 fasttimer()
    336 {
    337     static unsigned int tlast;
    338     static unsigned int nsent;
    339     register unsigned int t = tlast + 1;
    340     register int n;
    341 
    342     /*
    343      * if we're in the last second, send everything that's left.
    344      * otherwise send at least the fraction we should have sent by now.
    345      */
    346     if (t >= ROUTE_REPORT_INTERVAL) {
    347 	register int nleft = nroutes - nsent;
    348 	while (nleft > 0) {
    349 	    if ((n = report_next_chunk()) <= 0)
    350 		break;
    351 	    nleft -= n;
    352 	}
    353 	tlast = 0;
    354 	nsent = 0;
    355     } else {
    356 	register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
    357 	while (nsent < ncum) {
    358 	    if ((n = report_next_chunk()) <= 0)
    359 		break;
    360 	    nsent += n;
    361 	}
    362 	tlast = t;
    363     }
    364     if ((t % TIMER_INTERVAL) == 0)
    365 	timer();
    366 
    367     age_callout_queue();/* Advance the timer for the callout queue
    368 				for groups */
    369     alarm(1);
    370 }
    371 
    372 /*
    373  * The 'virtual_time' variable is initialized to a value that will cause the
    374  * first invocation of timer() to send a probe or route report to all vifs
    375  * and send group membership queries to all subnets for which this router is
    376  * querier.  This first invocation occurs approximately TIMER_INTERVAL seconds
    377  * after the router starts up.   Note that probes for neighbors and queries
    378  * for group memberships are also sent at start-up time, as part of initial-
    379  * ization.  This repetition after a short interval is desirable for quickly
    380  * building up topology and membership information in the presence of possible
    381  * packet loss.
    382  *
    383  * 'virtual_time' advances at a rate that is only a crude approximation of
    384  * real time, because it does not take into account any time spent processing,
    385  * and because the timer intervals are sometimes shrunk by a random amount to
    386  * avoid unwanted synchronization with other routers.
    387  */
    388 
    389 static u_long virtual_time = 0;
    390 
    391 
    392 /*
    393  * Timer routine.  Performs periodic neighbor probing, route reporting, and
    394  * group querying duties, and drives various timers in routing entries and
    395  * virtual interface data structures.
    396  */
    397 static void
    398 timer()
    399 {
    400     age_routes();	/* Advance the timers in the route entries     */
    401     age_vifs();		/* Advance the timers for neighbors */
    402     age_table_entry();	/* Advance the timers for the cache entries */
    403 
    404     if (virtual_time % GROUP_QUERY_INTERVAL == 0) {
    405 	/*
    406 	 * Time to query the local group memberships on all subnets
    407 	 * for which this router is the elected querier.
    408 	 */
    409 	query_groups();
    410     }
    411 
    412     if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
    413 	/*
    414 	 * Time to send a probe on all vifs from which no neighbors have
    415 	 * been heard.  Also, check if any inoperative interfaces have now
    416 	 * come up.  (If they have, they will also be probed as part of
    417 	 * their initialization.)
    418 	 */
    419 	probe_for_neighbors();
    420 
    421 	if (vifs_down)
    422 	    check_vif_state();
    423     }
    424 
    425     delay_change_reports = FALSE;
    426     if (routes_changed) {
    427 	/*
    428 	 * Some routes have changed since the last timer interrupt, but
    429 	 * have not been reported yet.  Report the changed routes to all
    430 	 * neighbors.
    431 	 */
    432 	report_to_all_neighbors(CHANGED_ROUTES);
    433     }
    434 
    435 #ifdef SNMP
    436     if (smux_fd == NOTOK && !dont_bother_anymore
    437 		 && virtual_time % SNMPD_RETRY_INTERVAL == 0) {
    438 	/*
    439 	 * Time to check for snmpd running.
    440 	 */
    441         try_smux_init();
    442     }
    443 #endif
    444 
    445     /*
    446      * Advance virtual time
    447      */
    448     virtual_time += TIMER_INTERVAL;
    449 }
    450 
    451 
    452 /*
    453  * On termination, let everyone know we're going away.
    454  */
    455 static void
    456 done()
    457 {
    458     log(LOG_NOTICE, 0, "mrouted version %d.%d exiting",
    459 			PROTOCOL_VERSION, MROUTED_VERSION);
    460     cleanup();
    461     _exit(1);
    462 }
    463 
    464 static void
    465 cleanup()
    466 {
    467     static in_cleanup = 0;
    468 
    469     if (!in_cleanup) {
    470 	in_cleanup++;
    471 #ifdef RSRR
    472 	rsrr_clean();
    473 #endif /* RSRR */
    474 	expire_all_routes();
    475 	report_to_all_neighbors(ALL_ROUTES);
    476 	k_stop_dvmrp();
    477     }
    478 }
    479 
    480 
    481 /*
    482  * Dump internal data structures to stderr.
    483  */
    484 static void
    485 dump()
    486 {
    487     dump_vifs(stderr);
    488     dump_routes(stderr);
    489 }
    490 
    491 
    492 /*
    493  * Dump internal data structures to a file.
    494  */
    495 static void
    496 fdump()
    497 {
    498     FILE *fp;
    499 
    500     fp = fopen(dumpfilename, "w");
    501     if (fp != NULL) {
    502 	dump_vifs(fp);
    503 	dump_routes(fp);
    504 	(void) fclose(fp);
    505     }
    506 }
    507 
    508 
    509 /*
    510  * Dump local cache contents to a file.
    511  */
    512 static void
    513 cdump()
    514 {
    515     FILE *fp;
    516 
    517     fp = fopen(cachefilename, "w");
    518     if (fp != NULL) {
    519 	dump_cache(fp);
    520 	(void) fclose(fp);
    521     }
    522 }
    523 
    524 
    525 /*
    526  * Restart mrouted
    527  */
    528 static void
    529 restart()
    530 {
    531     register int omask;
    532 
    533     log(LOG_NOTICE, 0, "mrouted version %d.%d restart",
    534 			PROTOCOL_VERSION, MROUTED_VERSION);
    535 
    536     /*
    537      * reset all the entries
    538      */
    539     omask = sigblock(sigmask(SIGALRM));
    540     free_all_prunes();
    541     free_all_routes();
    542     stop_all_vifs();
    543     k_stop_dvmrp();
    544     close(igmp_socket);
    545     close(udp_socket);
    546 
    547     /*
    548      * start processing again
    549      */
    550     dvmrp_genid++;
    551     pruning = 1;
    552 
    553     init_igmp();
    554     k_init_dvmrp();		/* enable DVMRP routing in kernel */
    555     init_routes();
    556     init_ktable();
    557     init_vifs();
    558 
    559     (void)sigsetmask(omask);
    560 }
    561 
    562 
    563 /*
    564  * Log errors and other messages to the system log daemon and to stderr,
    565  * according to the severity of the message and the current debug level.
    566  * For errors of severity LOG_ERR or worse, terminate the program.
    567  */
    568 /*VARARGS3*/
    569 void
    570 log(severity, syserr, format, va_alist)
    571     int severity, syserr;
    572     char *format;
    573     va_dcl
    574 {
    575     va_list ap;
    576     static char fmt[211] = "warning - ";
    577     char *msg;
    578     char tbuf[20];
    579     struct timeval now;
    580     struct tm *thyme;
    581 
    582     va_start(ap);
    583     vsprintf(&fmt[10], format, ap);
    584     va_end(ap);
    585     msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
    586 
    587     switch (debug) {
    588 	case 0: break;
    589 	case 1: if (severity > LOG_NOTICE) break;
    590 	case 2: if (severity > LOG_INFO  ) break;
    591 	default:
    592 	    gettimeofday(&now,NULL);
    593 	    thyme = localtime((time_t *)&now.tv_sec);
    594 	    strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme);
    595 	    fprintf(stderr, tbuf, now.tv_usec / 1000);
    596 	    fprintf(stderr, "%s", msg);
    597 	    if (syserr == 0)
    598 		fprintf(stderr, "\n");
    599 	    else
    600 		fprintf(stderr, ": %s\n", strerror(syserr));
    601     }
    602 
    603     if (severity <= LOG_NOTICE) {
    604 	if (syserr != 0) {
    605 	    errno = syserr;
    606 	    syslog(severity, "%s: %m", msg);
    607 	} else
    608 	    syslog(severity, "%s", msg);
    609 
    610 	if (severity <= LOG_ERR) exit(-1);
    611     }
    612 }
    613