int main(int argc, char *argv[]) { int dummy, dummysigalrm, foreground = 0; struct timeval tv, difftime, curtime, lasttime, *timeout; fd_set rfds, readers; int nfds, n, i, secs, ch; struct sigaction sa; time_t boottime; struct option long_options[] = { {"config", 1, 0, 'c'}, {"debug", 2, 0, 'd'}, {"foreground", 0, 0, 'f'}, {"disable-vifs", 0, 0, 'N'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"quit-daemon", 0, 0, 'q'}, {"reload-config", 0, 0, 'l'}, {"show-routes", 0, 0, 'r'}, /* {"show-cache", 0, 0, 'i'}, */ /* {"show-debug", 0, 0, 'p'}, */ {0, 0, 0, 0} }; snprintf(versionstring, sizeof (versionstring), "pimd version %s", todaysversion); while ((ch = getopt_long (argc, argv, "c:d::fhlNP::vqr", long_options, NULL)) != EOF) { switch (ch) { case 'c': configfilename = optarg; break; case 'd': if (!optarg) { debug = DEBUG_DEFAULT; } else { char *p,*q; size_t i, len; struct debugname *d; debug = 0; p = optarg; q = NULL; while (p) { q = strchr(p, ','); if (q) *q++ = '\0'; len = strlen(p); for (i = 0, d = debugnames; i < ARRAY_LEN(debugnames); i++, d++) if (len >= d->nchars && strncmp(d->name, p, len) == 0) break; if (i == ARRAY_LEN(debugnames)) return usage(); debug |= d->level; p = q; } } break; case 'f': foreground = 1; break; case 'h': return usage(); case 'l': killshow(SIGHUP, NULL); return 0; case 'N': disable_all_by_default = 1; break; case 'P': #ifdef SNMP if (!optarg) dest_port = DEFAULT_PORT; else { dest_port = strtonum(optarg, 1, 65535, &errstr); if (errstr) { warnx("destination port %s", errstr); dest_port = DEFAULT_PORT; } } #else warnx("SNMP support missing, please feel free to submit a patch."); #endif break; case 'v': printf("%s\n", versionstring); return 0; case 'q': killshow(SIGTERM, NULL); return 0; case 'r': killshow(SIGUSR1, _PATH_PIMD_DUMP); return 0; #if 0 /* XXX: TODO */ case 'i': killshow(SIGUSR2, _PATH_PIMD_CACHE); return 0; case 'p': killshow(SIGQUIT, NULL); return 0; #endif default: return usage(); } } argc -= optind; argv += optind; if (argc > 0) { return usage(); } if (geteuid() != 0) { fprintf(stderr, "%s: must be root\n", __progname); exit(1); } setlinebuf(stderr); if (debug != 0) { struct debugname *d; char c; int tmpd = debug; fprintf(stderr, "debug level 0x%lx ", debug); c = '('; for (d = debugnames; d < debugnames + ARRAY_LEN(debugnames); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); } /* * Create directory for runtime files */ mkdir(_PATH_PIMD_RUNDIR, 0755); /* * Setup logging */ #ifdef LOG_DAEMON (void)openlog("pimd", LOG_PID, LOG_DAEMON); (void)setlogmask(LOG_UPTO(LOG_NOTICE)); #else (void)openlog("pimd", LOG_PID); #endif /* LOG_DAEMON */ logit(LOG_DEBUG, 0, "%s starting", versionstring); do_randomize(); time(&boottime); /* Start up the log rate-limiter */ resetlogging(NULL); callout_init(); init_igmp(); init_pim(); #ifdef HAVE_ROUTING_SOCKETS init_routesock(); #endif /* HAVE_ROUTING_SOCKETS */ init_pim_mrt(); init_timers(); /* TODO: check the kernel DVMRP/MROUTED/PIM support version */ #ifdef SNMP if (i = snmp_init()) return i; #endif /* SNMP */ init_vifs(); init_rp_and_bsr(); /* Must be after init_vifs() */ #ifdef RSRR rsrr_init(); #endif /* RSRR */ sa.sa_handler = handler; sa.sa_flags = 0; /* Interrupt system calls */ sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } IF_DEBUG(DEBUG_IF) dump_vifs(stderr); IF_DEBUG(DEBUG_PIM_MRT) dump_pim_mrt(stderr); /* schedule first timer interrupt */ timer_setTimer(TIMER_INTERVAL, timer, NULL); if (!debug && !foreground) { /* Detach from the terminal */ haveterminal = 0; if (fork()) exit(0); (void)close(0); (void)close(1); (void)close(2); (void)open("/", 0); (void)dup2(0, 1); (void)dup2(0, 2); #ifdef SYSV (void)setpgrp(); #else #ifdef TIOCNOTTY n = open("/dev/tty", 2); if (n >= 0) { (void)ioctl(n, TIOCNOTTY, (char *)0); (void)close(n); } #else if (setsid() < 0) perror("setsid"); #endif /* TIOCNOTTY */ #endif /* SYSV */ } /* End of child process code */ if (pidfile(NULL)) { warn("Cannot create pidfile"); } /* * Main receive loop. */ dummy = 0; dummysigalrm = SIGALRM; difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; while (1) { memcpy(&rfds, &readers, sizeof(rfds)); secs = timer_nextTimer(); if (secs == -1) timeout = NULL; else { timeout = &tv; timeout->tv_sec = secs; timeout->tv_usec = 0; } if (boottime) { time_t n; time(&n); if (n > boottime + 15) { struct rp_hold *rph = g_rp_hold; while(rph) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, rph->address, 1, (u_int16)0xffffff, rph->group, rph->mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); rph = rph->next; } boottime = 0; } } if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; break; } if (sighandled & GOT_SIGHUP) { sighandled &= ~GOT_SIGHUP; restart(SIGHUP); /* reconstruct readers and nfds */ FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } memcpy(&rfds, &readers, sizeof(rfds)); } if (sighandled & GOT_SIGUSR1) { sighandled &= ~GOT_SIGUSR1; fdump(SIGUSR1); } if (sighandled & GOT_SIGUSR2) { sighandled &= ~GOT_SIGUSR2; cdump(SIGUSR2); } if (sighandled & GOT_SIGALRM) { sighandled &= ~GOT_SIGALRM; timer(&dummysigalrm); } } if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { if (errno != EINTR) /* SIGALRM is expected */ logit(LOG_WARNING, errno, "select failed"); continue; } if (n > 0) { /* TODO: shall check first igmp_socket for better performance? */ for (i = 0; i < nhandlers; i++) { if (FD_ISSET(ihandlers[i].fd, &rfds)) { (*ihandlers[i].func)(ihandlers[i].fd, &rfds); } } } /* * Handle timeout queue. * * If select + packet processing took more than 1 second, * or if there is a timeout pending, age the timeout queue. * * If not, collect usec in difftime to make sure that the * time doesn't drift too badly. * * If the timeout handlers took more than 1 second, * age the timeout queue again. XXX This introduces the * potential for infinite loops! */ do { /* * If the select timed out, then there's no other * activity to account for and we don't need to * call gettimeofday. */ if (n == 0) { curtime.tv_sec = lasttime.tv_sec + secs; curtime.tv_usec = lasttime.tv_usec; n = -1; /* don't do this next time through the loop */ } else gettimeofday(&curtime, NULL); difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; while (difftime.tv_usec >= 1000000) { difftime.tv_sec++; difftime.tv_usec -= 1000000; } if (difftime.tv_usec < 0) { difftime.tv_sec--; difftime.tv_usec += 1000000; } lasttime = curtime; if (secs == 0 || difftime.tv_sec > 0) age_callout_queue(difftime.tv_sec); secs = -1; } while (difftime.tv_sec > 0); } /* Main loop */ logit(LOG_NOTICE, 0, "%s exiting", versionstring); cleanup(); exit(0); }
mrtentry_t *find_route(uint32_t source, uint32_t group, uint16_t flags, char create) { srcentry_t *src = NULL; grpentry_t *grp = NULL; mrtentry_t *mrt = NULL; mrtentry_t *mrt_wc = NULL; mrtentry_t *mrt_pmbr = NULL; mrtentry_t *mrt2 = NULL; rpentry_t *rp = NULL; rp_grp_entry_t *rp_grp = NULL; if (flags & (MRTF_SG | MRTF_WC)) { if (!IN_MULTICAST(ntohl(group))) { logit(LOG_WARNING, 0, "find_route: Not a multicast group address (%s) ...", inet_fmt(group, s1, sizeof(s1))); return NULL; } } if (flags & MRTF_SG) { if (!inet_valid_host(source) && !IN_PIM_SSM_RANGE(group)) { logit(LOG_WARNING, 0, "find_route: Not a valid host (%s) ...", inet_fmt(source, s1, sizeof(s1))); return NULL; } } if (create == DONT_CREATE) { if (flags & (MRTF_SG | MRTF_WC)) { if (search_grplist(group, &grp) == FALSE) { /* Group not found. Return the (*,*,RP) entry */ if (flags & MRTF_PMBR) { rp = rp_match(group); if (rp) { logit(LOG_DEBUG, 0 , "find_route: Group not found. Return the (*,*,RP) entry"); return rp->mrtlink; } } logit(LOG_DEBUG, 0 , "find_route: Not PMBR, return NULL"); return NULL; } /* Search for the source */ if (flags & MRTF_SG) { if (search_grpmrtlink(grp, source, &mrt) == TRUE) { /* Exact (S,G) entry found */ logit(LOG_DEBUG, 0 , "find_route: exact (S,G) entry found"); return mrt; } logit(LOG_DEBUG, 0 , "find_route:(S,G) entry not found"); } /* No (S,G) entry. Return the (*,G) entry (if exist) */ if ((flags & MRTF_WC) && grp->grp_route) { logit(LOG_DEBUG, 0 , "find_route: No (S,G) entry. Return the (*,G) entry"); return grp->grp_route; } } /* Return the (*,*,RP) entry */ if (flags & MRTF_PMBR) { rp = NULL; if (group != INADDR_ANY_N) rp = rp_match(group); else if (source != INADDR_ANY_N) rp = rp_find(source); if (rp) { logit(LOG_DEBUG, 0 , "find_route: Return the (*,*,RP) entry"); return rp->mrtlink; } } logit(LOG_DEBUG, 0 , "find_route: No SG|WC, return NULL"); return NULL; } /* Creation allowed */ if (flags & (MRTF_SG | MRTF_WC)) { grp = create_grpentry(group); if (!grp) return NULL; if (IN_PIM_SSM_RANGE(group)) { if (rp_match(group) == (rpentry_t *) NULL) { /* For SSM, virtual RP entry has to be created. RP is at local link 169.254.0.1 to be sure not to send any register messages outside, although sending them has been disabled for SSM also in PIM protocol. The address does not need to be really configured in any interface. TODO: Avoid need for virtual RP by implementing SSM-specific state structures */ add_rp_grp_entry(&cand_rp_list, &grp_mask_list, htonl(0xa9fe0001), 20, 90, group, 0xffffffff, curr_bsr_hash_mask, curr_bsr_fragment_tag); } } if (!grp->active_rp_grp) { rp_grp = rp_grp_match(group); if (!rp_grp) { if (!grp->mrtlink && !grp->grp_route) /* New created grpentry. Delete it. */ delete_grpentry(grp); return NULL; } rp = rp_grp->rp->rpentry; grp->active_rp_grp = rp_grp; grp->rpaddr = rp->address; /* Link to the top of the rp_grp_chain */ grp->rpnext = rp_grp->grplink; rp_grp->grplink = grp; if (grp->rpnext) grp->rpnext->rpprev = grp; } else { rp = grp->active_rp_grp->rp->rpentry; } } mrt_wc = mrt_pmbr = NULL; if (flags & MRTF_WC) { /* Setup the (*,G) routing entry */ mrt_wc = create_mrtentry(NULL, grp, MRTF_WC); if (!mrt_wc) { if (!grp->mrtlink) /* New created grpentry. Delete it. */ delete_grpentry(grp); return NULL; } if (mrt_wc->flags & MRTF_NEW) { mrt_pmbr = rp->mrtlink; /* Copy the oif list from the (*,*,RP) entry */ if (mrt_pmbr) VOIF_COPY(mrt_pmbr, mrt_wc); mrt_wc->incoming = rp->incoming; mrt_wc->upstream = rp->upstream; mrt_wc->metric = rp->metric; mrt_wc->preference = rp->preference; move_kernel_cache(mrt_wc, 0); #ifdef RSRR rsrr_cache_bring_up(mrt_wc); #endif /* RSRR */ } if (!(flags & MRTF_SG)) return mrt_wc; } if (flags & MRTF_SG) { /* Setup the (S,G) routing entry */ src = create_srcentry(source); if (!src) { /* TODO: XXX: The MRTF_NEW flag check may be misleading?? check */ if ((!grp->grp_route || (grp->grp_route && (grp->grp_route->flags & MRTF_NEW))) && !grp->mrtlink) { /* New created grpentry. Delete it. */ delete_grpentry(grp); } return NULL; } mrt = create_mrtentry(src, grp, MRTF_SG); if (!mrt) { if ((!grp->grp_route || (grp->grp_route && (grp->grp_route->flags & MRTF_NEW))) && !grp->mrtlink) { /* New created grpentry. Delete it. */ delete_grpentry(grp); } if (!src->mrtlink) /* New created srcentry. Delete it. */ delete_srcentry(src); return NULL; } if (mrt->flags & MRTF_NEW) { mrt2 = grp->grp_route; if (!mrt2) mrt2 = rp->mrtlink; /* Copy the oif list from the existing (*,G) or (*,*,RP) entry */ if (mrt2) { VOIF_COPY(mrt2, mrt); if (flags & MRTF_RP) { /* ~(S,G) prune entry */ mrt->incoming = mrt2->incoming; mrt->upstream = mrt2->upstream; mrt->metric = mrt2->metric; mrt->preference = mrt2->preference; mrt->flags |= MRTF_RP; } } if (!(mrt->flags & MRTF_RP)) { mrt->incoming = src->incoming; mrt->upstream = src->upstream; mrt->metric = src->metric; mrt->preference = src->preference; } move_kernel_cache(mrt, 0); #ifdef RSRR rsrr_cache_bring_up(mrt); #endif /* RSRR */ } return mrt; } if (flags & MRTF_PMBR) { /* Get/return the (*,*,RP) routing entry */ if (group != INADDR_ANY_N) rp = rp_match(group); else if (source != INADDR_ANY_N) rp = rp_find(source); else return NULL; /* source == group == INADDR_ANY */ if (!rp) return NULL; if (rp->mrtlink) return rp->mrtlink; mrt = create_mrtentry(rp, NULL, MRTF_PMBR); if (!mrt) return NULL; mrt->incoming = rp->incoming; mrt->upstream = rp->upstream; mrt->metric = rp->metric; mrt->preference = rp->preference; return mrt; } return NULL; }