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