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