Index: head/usr.sbin/pim6sd/main.c =================================================================== --- head/usr.sbin/pim6sd/main.c (revision 75575) +++ head/usr.sbin/pim6sd/main.c (revision 75576) @@ -1,767 +1,767 @@ /* * Copyright (c) 1998 by the University of Southern California. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation in source and binary forms for lawful * purposes and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both * the copyright notice and this permission notice appear in supporting * documentation, and that any documentation, advertising materials, * and other materials related to such distribution and use acknowledge * that the software was developed by the University of Southern * California and/or Information Sciences Institute. * The name of the University of Southern California may not * be used to endorse or promote products derived from this software * without specific prior written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND * NON-INFRINGEMENT. * * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, * THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Other copyrights might apply to parts of this software and are so * noted when applicable. */ /* * Questions concerning this software should be directed to * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg. * */ /* * This program has been derived from pim6dd. * The pim6dd program is covered by the license in the accompanying file * named "LICENSE.pim6dd". */ /* * This program has been derived from pimd. * The pimd program is covered by the license in the accompanying file * named "LICENSE.pimd". * */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "defs.h" #include "debug.h" #include "mld6.h" #include "pim6.h" #include "vif.h" #include "routesock.h" #include "callout.h" #include "mrt.h" #include "timer.h" #include "rp.h" #include "kern.h" #include "cfparse.h" char configfilename[256] = _PATH_PIM6D_CONF; char versionstring[100]; char logfilename[256] = _PATH_PIM6D_LOGFILE; /* TODO: not used static char genidfilename[] = _PATH_PIM6D_GENID; */ static char pidfilename[] = _PATH_PIM6D_PID; FILE *log_fp = stderr; char *progname; static int foreground = 0; static int sighandled = 0; #define GOT_SIGINT 0x01 #define GOT_SIGHUP 0x02 #define GOT_SIGUSR1 0x04 #define GOT_SIGUSR2 0x08 #define GOT_SIGALRM 0x10 #define GOT_SIGINFO 0x20 #define NHANDLERS 3 static struct ihandler { int fd; /* File descriptor */ ihfunc_t func; /* Function to call with &fd_set */ } ihandlers[NHANDLERS]; static int nhandlers = 0; static struct debugname { char *name; int level; int nchars; } debugnames[] = { { "mld_proto", DEBUG_MLD_PROTO, 5 }, { "mld_timer", DEBUG_MLD_TIMER, 5 }, { "mld_member", DEBUG_MLD_MEMBER, 5 }, { "mld", DEBUG_MLD, 3 }, { "switch", DEBUG_SWITCH, 2 }, { "trace", DEBUG_TRACE, 2 }, { "mtrace", DEBUG_TRACE, 2 }, { "traceroute", DEBUG_TRACE, 2 }, { "timeout", DEBUG_TIMEOUT, 2 }, { "callout", DEBUG_TIMEOUT, 3 }, { "pkt", DEBUG_PKT, 2 }, { "packets", DEBUG_PKT, 2 }, { "interfaces", DEBUG_IF, 2 }, { "vif", DEBUG_IF, 1 }, { "kernel", DEBUG_KERN, 2 }, { "cache", DEBUG_MFC, 1 }, { "mfc", DEBUG_MFC, 2 }, { "k_cache", DEBUG_MFC, 2 }, { "k_mfc", DEBUG_MFC, 2 }, { "rsrr", DEBUG_RSRR, 2 }, { "pim_detail", DEBUG_PIM_DETAIL, 5 }, { "pim_hello", DEBUG_PIM_HELLO, 5 }, { "pim_neighbors", DEBUG_PIM_HELLO, 5 }, { "pim_register", DEBUG_PIM_REGISTER, 5 }, { "registers", DEBUG_PIM_REGISTER, 2 }, { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 }, { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 }, { "bsr", DEBUG_PIM_BOOTSTRAP, 1 }, { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 }, { "pim_asserts", DEBUG_PIM_ASSERT, 5 }, { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_rp", DEBUG_PIM_CAND_RP, 6 }, { "rp", DEBUG_PIM_CAND_RP, 2 }, { "pim_routes", DEBUG_PIM_MRT, 6 }, { "pim_routing", DEBUG_PIM_MRT, 6 }, { "pim_mrt", DEBUG_PIM_MRT, 5 }, { "pim_timers", DEBUG_PIM_TIMER, 5 }, { "pim_rpf", DEBUG_PIM_RPF, 6 }, { "rpf", DEBUG_RPF, 3 }, { "pim", DEBUG_PIM, 1 }, { "routes", DEBUG_MRT, 1 }, { "routing", DEBUG_MRT, 1 }, { "mrt", DEBUG_MRT, 1 }, { "routers", DEBUG_NEIGHBORS, 6 }, { "mrouters", DEBUG_NEIGHBORS, 7 }, { "neighbors", DEBUG_NEIGHBORS, 1 }, { "timers", DEBUG_TIMER, 1 }, { "asserts", DEBUG_ASSERT, 1 }, { "all", DEBUG_ALL, 2 }, { "3", 0xffffffff, 1 } /* compat. */ }; /* * Forward declarations. */ static void handler __P((int)); static void timer __P((void *)); static void cleanup __P((void)); static void restart __P((int)); static void cleanup __P((void)); /* To shut up gcc -Wstrict-prototypes */ int main __P((int argc, char **argv)); int register_input_handler(fd, func) int fd; ihfunc_t func; { if (nhandlers >= NHANDLERS) return -1; ihandlers[nhandlers].fd = fd; ihandlers[nhandlers++].func = func; return 0; } int main(argc, argv) int argc; char *argv[]; { int dummy, dummysigalrm; FILE *fp; struct timeval tv, difftime, curtime, lasttime, *timeout; fd_set rfds, readers; int nfds=0, n, i, secs; extern char todaysversion[]; struct sigaction sa; struct debugname *d; char c; int tmpd; setlinebuf(stderr); if (geteuid() != 0) { fprintf(stderr, "pim6sd: must be root\n"); exit(1); } progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; argv++; argc--; while (argc > 0 && *argv[0] == '-') { if (strcmp(*argv, "-d") == 0) { if (argc > 1 && *(argv + 1)[0] != '-') { char *p, *q; int i, len; struct debugname *d; int no=0; argv++; argc--; debug = 0; p = *argv; q = NULL; while (p) { q = strchr(p, ','); if (q) *q++ = '\0'; if(p[0]=='-') { no=1; p++; } len = strlen(p); for (i = 0, d = debugnames; i < sizeof(debugnames) / sizeof(debugnames[0]); i++, d++) if (len >= d->nchars && strncmp(d->name, p, len) == 0) break; if (i == sizeof(debugnames) / sizeof(debugnames[0])) { int j = 0xffffffff; int k = 0; fprintf(stderr, "Valid debug levels: "); for (i = 0, d = debugnames; i < sizeof(debugnames) / sizeof(debugnames[0]); i++, d++) { if ((j & d->level) == d->level) { if (k++) putc(',', stderr); fputs(d->name, stderr); j &= ~d->level; } } putc('\n', stderr); goto usage; } if(no) { debug &=~d->level; no=0; } else debug |= d->level; p = q; } } else debug = DEBUG_DEFAULT; } else if (strcmp(*argv, "-c") == 0) { if (argc > 1) { argv++; argc--; strcpy(configfilename, *argv); } else goto usage; } else if (strcmp(*argv, "-f") == 0) foreground = 1; else goto usage; argv++; argc--; } if (argc > 0) { usage: tmpd = 0xffffffff; fprintf(stderr, "usage: pim6sd [-c configfile] [-d [debug_level][,debug_level]]\n"); fprintf(stderr, "debug levels: "); c = '('; for (d = debugnames; d < debugnames + sizeof(debugnames) / sizeof(debugnames[0]); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); exit(1); } if (debug != 0) { tmpd = debug; fprintf(stderr, "debug level 0x%lx ", debug); c = '('; for (d = debugnames; d < debugnames + sizeof(debugnames) / sizeof(debugnames[0]); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); } #ifdef LOG_DAEMON (void) openlog("pim6sd", LOG_PID, LOG_DAEMON); // (void) setlogmask(LOG_UPTO(LOG_NOTICE)); #else (void) openlog("pim6sd", LOG_PID); #endif /* LOG_DAEMON */ /* open a log file */ if ((log_fp = fopen(logfilename, "w")) == NULL) log(LOG_ERR, errno, "fopen(%s)", logfilename); setlinebuf(log_fp); sprintf(versionstring, "pim6sd version %s", todaysversion); log(LOG_INFO, 0, "%s starting", versionstring); /* * TODO: XXX: use a combination of time and hostid to initialize the * random generator. */ #ifdef SYSV srand48(time(NULL)); #else srandom(time(NULL)); #endif callout_init(); init_mld6(); init_pim6(); #ifdef HAVE_ROUTING_SOCKETS init_routesock(); #endif /* HAVE_ROUTING_SOCKETS */ init_pim6_mrt(); init_timers(); /* TODO: check the kernel DVMRP/MROUTED/PIM support version */ init_vifs(); init_rp6_and_bsr6(); /* Must be after init_vifs() */ 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); sigaction(SIGINFO, &sa, NULL); FD_ZERO(&readers); FD_SET(mld6_socket, &readers); 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(log_fp); IF_DEBUG(DEBUG_PIM_MRT) dump_pim_mrt(log_fp); /* schedule first timer interrupt */ timer_setTimer(timer_interval, timer, NULL); if (foreground == 0) { /* Detach from the terminal */ #ifdef TIOCNOTTY int t; #endif /* TIOCNOTTY */ if (fork()) exit(0); #ifdef HAVE_ROUTING_SOCKETS pid = getpid(); #endif (void) close(0); (void) close(1); (void) close(2); (void) open("/", 0); (void) dup2(0, 1); (void) dup2(0, 2); #if defined(SYSV) || defined(linux) (void) setpgrp(); #else #ifdef TIOCNOTTY t = open(_PATH_TTY, 2); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *) 0); (void) close(t); } #else if (setsid() < 0) perror("setsid"); #endif /* TIOCNOTTY */ #endif /* SYSV */ } /* End of child process code */ fp = fopen(pidfilename, "w"); if (fp != NULL) { fprintf(fp, "%d\n", (int) getpid()); (void) fclose(fp); } /* * Main receive loop. */ dummy = 0; dummysigalrm = SIGALRM; difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; for (;;) { bcopy((char *) &readers, (char *) &rfds, sizeof(rfds)); secs = timer_nextTimer(); if (secs == -1) timeout = NULL; else { timeout = &tv; timeout->tv_sec = secs; timeout->tv_usec = 0; } if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; break; } if (sighandled & GOT_SIGHUP) { sighandled &= ~GOT_SIGHUP; restart(SIGHUP); } if (sighandled & GOT_SIGINFO) { sighandled &= ~GOT_SIGINFO; dump_stat(); } if (sighandled & GOT_SIGUSR1) { sighandled &= ~GOT_SIGUSR1; fdump(SIGUSR1); } if (sighandled & GOT_SIGUSR2) { sighandled &= ~GOT_SIGUSR2; #ifdef notyet cdump(SIGUSR2); #else cfparse(0, 1); /* reset debug level */ #endif } if (sighandled & GOT_SIGALRM) { sighandled &= ~GOT_SIGALRM; timer(&dummysigalrm); } } if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { if (errno != EINTR) log(LOG_WARNING, errno, "select failed"); continue; } /* * 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; #ifdef TIMERDEBUG IF_DEBUG(DEBUG_TIMEOUT) log(LOG_DEBUG, 0, "TIMEOUT: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec); #endif 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) if (secs == 0 || difftime.tv_sec > 0) { #ifdef TIMERDEBUG IF_DEBUG(DEBUG_TIMEOUT) log(LOG_DEBUG, 0, "\taging callouts: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec); #endif age_callout_queue(difftime.tv_sec); } secs = -1; } while (difftime.tv_sec > 0); /* Handle sockets */ if (n > 0) { for (i = 0; i < nhandlers; i++) { if (FD_ISSET(ihandlers[i].fd, &rfds)) { (*ihandlers[i].func) (ihandlers[i].fd, &rfds); } } } } /* Main loop */ log(LOG_NOTICE, 0, "%s exiting", versionstring); cleanup(); exit(0); } /* * The 'virtual_time' variable is initialized to a value that will cause the * first invocation of timer() to send a probe or route report to all vifs * and send group membership queries to all subnets for which this router is * querier. This first invocation occurs approximately timer_interval * seconds after the router starts up. Note that probes for neighbors and * queries for group memberships are also sent at start-up time, as part of * initial- ization. This repetition after a short interval is desirable for * quickly building up topology and membership information in the presence of * possible packet loss. * * 'virtual_time' advances at a rate that is only a crude approximation of real * time, because it does not take into account any time spent processing, and * because the timer intervals are sometimes shrunk by a random amount to * avoid unwanted synchronization with other routers. */ u_long virtual_time = 0; /* * Timer routine. Performs all perodic functions: aging interfaces, quering * neighbors and members, etc... The granularity is equal to timer_interval. * this granularity is configurable ( see file pim6sd.conf.sample) */ static void timer(i) void *i; { age_vifs(); /* Timeout neighbors and groups */ age_routes(); /* Timeout routing entries */ age_misc(); /* Timeout the rest (Cand-RP list, etc) */ virtual_time += timer_interval; timer_setTimer(timer_interval, timer, NULL); } /* * Performs all necessary functions to quit gracefully */ /* TODO: implement all necessary stuff */ static void cleanup() { /* * TODO: XXX (not in the spec): if I am the BSR, somehow inform the other * routers I am going down and need to elect another BSR? (probably by * sending a the Cand-RP-set with my_priority=LOWEST?) * */ k_stop_pim(mld6_socket); } /* * Signal handler. Take note of the fact that the signal arrived so that the * main loop can take care of it. */ static void handler(sig) int sig; { switch (sig) { case SIGALRM: sighandled |= GOT_SIGALRM; case SIGINT: case SIGTERM: sighandled |= GOT_SIGINT; break; case SIGHUP: sighandled |= GOT_SIGHUP; break; case SIGUSR1: sighandled |= GOT_SIGUSR1; break; case SIGUSR2: sighandled |= GOT_SIGUSR2; break; case SIGINFO: sighandled |= GOT_SIGINFO; break; } } /* TODO: not verified */ /* * Restart the daemon */ static void restart(i) int i; { - log(LOG_NOTICE, 0, "% restart", versionstring); + log(LOG_NOTICE, 0, "%s restart", versionstring); /* * reset all the entries */ /* * TODO: delete? free_all_routes(); */ free_all_callouts(); stop_all_vifs(); nhandlers=0; k_stop_pim(mld6_socket); close(mld6_socket); close(pim6_socket); close(udp_socket); /* * start processing again */ init_mld6(); init_pim6(); #ifdef HAVE_ROUTING_SOCKETS init_routesock(); #endif /* HAVE_ROUTING_SOCKETS */ init_pim6_mrt(); init_vifs(); /* schedule timer interrupts */ timer_setTimer(timer_interval, timer, NULL); } Index: head/usr.sbin/pim6sd/mld6_proto.c =================================================================== --- head/usr.sbin/pim6sd/mld6_proto.c (revision 75575) +++ head/usr.sbin/pim6sd/mld6_proto.c (revision 75576) @@ -1,619 +1,619 @@ /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1998 by the University of Southern California. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation in source and binary forms for lawful * purposes and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both * the copyright notice and this permission notice appear in supporting * documentation, and that any documentation, advertising materials, * and other materials related to such distribution and use acknowledge * that the software was developed by the University of Southern * California and/or Information Sciences Institute. * The name of the University of Southern California may not * be used to endorse or promote products derived from this software * without specific prior written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND * NON-INFRINGEMENT. * * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, * THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Other copyrights might apply to parts of this software and are so * noted when applicable. */ /* * Questions concerning this software should be directed to * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg. * */ /* * This program has been derived from pim6dd. * The pim6dd program is covered by the license in the accompanying file * named "LICENSE.pim6dd". */ /* * This program has been derived from pimd. * The pimd program is covered by the license in the accompanying file * named "LICENSE.pimd". * */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "mld6.h" #include "vif.h" #include "debug.h" #include "inet6.h" #include "route.h" #include "callout.h" #include "timer.h" #include "mld6_proto.h" extern struct in6_addr in6addr_any; typedef struct { mifi_t mifi; struct listaddr *g; int q_time; } cbk_t; /* * Forward declarations. */ static void DelVif __P((void *arg)); static int SetTimer __P((int mifi, struct listaddr * g)); static int DeleteTimer __P((int id)); static void SendQuery __P((void *arg)); static int SetQueryTimer __P((struct listaddr * g, int mifi, int to_expire, int q_time)); /* * Send group membership queries on that interface if I am querier. */ void query_groups(v) register struct uvif *v; { register struct listaddr *g; v->uv_gq_timer = MLD6_QUERY_INTERVAL; if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) { send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr, NULL, (struct in6_addr *)&in6addr_any, v->uv_ifindex, MLD6_QUERY_RESPONSE_INTERVAL, 0, 1); v->uv_out_mld_query++; } /* * Decrement the old-hosts-present timer for each active group on that * vif. */ for (g = v->uv_groups; g != NULL; g = g->al_next) if (g->al_old > timer_interval) g->al_old -= timer_interval; else g->al_old = 0; } /* * Process an incoming host membership query */ void accept_listener_query(src, dst, group, tmo) struct sockaddr_in6 *src; struct in6_addr *dst, *group; int tmo; { register int mifi; register struct uvif *v; struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; /* Ignore my own listener query */ if (local_address(src) != NO_VIF) return; if ((mifi = find_vif_direct(src)) == NO_VIF) { IF_DEBUG(DEBUG_MLD) log(LOG_INFO, 0, "accept_listener_query: can't find a mif"); return; } v = &uvifs[mifi]; v->uv_in_mld_query++; IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "accepting multicast listener query on %s: " "src %s, dst %s, grp %s", v->uv_name, inet6_fmt(&src->sin6_addr), inet6_fmt(dst), inet6_fmt(group)); if (v->uv_querier == NULL || !inet6_equal(&v->uv_querier->al_addr, src)) { /* * This might be: * - A query from a new querier, with a lower source address than * the current querier (who might be me). * - A query from a new router that just started up and doesn't know * who the querier is. */ if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr : &v->uv_linklocal->pa_addr))) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "new querier %s (was %s) " "on mif %d", inet6_fmt(&src->sin6_addr), v->uv_querier ? inet6_fmt(&v->uv_querier->al_addr.sin6_addr) : "me", mifi); if (!v->uv_querier) { /* this should be impossible... */ v->uv_querier = (struct listaddr *)malloc(sizeof(struct listaddr)); memset(v->uv_querier, 0, sizeof(struct listaddr)); } v->uv_flags &= ~VIFF_QUERIER; v->uv_querier->al_addr = *src; time(&v->uv_querier->al_ctime); } } /* * Reset the timer since we've received a query. */ if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr)) v->uv_querier->al_timer = MLD6_OTHER_QUERIER_PRESENT_INTERVAL; /* * If this is a Group-Specific query which we did not source, we must set * our membership timer to [Last Member Query Count] * the [Max Response * Time] in the packet. */ if (!IN6_IS_ADDR_UNSPECIFIED(group) && inet6_equal(src, &v->uv_linklocal->pa_addr)) { register struct listaddr *g; IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "%s for %s from %s on mif %d, timer %d", "Group-specific membership query", inet6_fmt(group), inet6_fmt(&src->sin6_addr), mifi, tmo); group_sa.sin6_addr = *group; group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); for (g = v->uv_groups; g != NULL; g = g->al_next) { if (inet6_equal(&group_sa, &g->al_addr) && g->al_query == 0) { /* setup a timeout to remove the group membership */ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT * tmo / MLD6_TIMER_SCALE; /* * use al_query to record our presence in last-member state */ g->al_query = -1; g->al_timerid = SetTimer(mifi, g); IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "timer for grp %s on mif %d " "set to %d", inet6_fmt(group), mifi, g->al_timer); break; } } } } /* * Process an incoming group membership report. */ void accept_listener_report(src, dst, group) struct sockaddr_in6 *src; struct in6_addr *dst, *group; { register mifi_t mifi; register struct uvif *v; register struct listaddr *g; struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; if (IN6_IS_ADDR_MC_LINKLOCAL(group)) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "accept_listener_report: group(%s) has the " "link-local scope. discard", inet6_fmt(group)); return; } if ((mifi = find_vif_direct_local(src)) == NO_VIF) { IF_DEBUG(DEBUG_MLD) log(LOG_INFO, 0, "accept_listener_report: can't find a mif"); return; } IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "accepting multicast listener report: " "src %s,dst %s, grp %s", inet6_fmt(&src->sin6_addr),inet6_fmt(dst), inet6_fmt(group)); v = &uvifs[mifi]; v->uv_in_mld_report++; /* * Look for the group in our group list; if found, reset its timer. */ group_sa.sin6_addr = *group; group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); for (g = v->uv_groups; g != NULL; g = g->al_next) { if (inet6_equal(&group_sa, &g->al_addr)) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG,0, "The group already exist"); g->al_reporter = *src; /* delete old timers, set a timer for expiration */ g->al_timer = MLD6_LISTENER_INTERVAL; if (g->al_query) g->al_query = DeleteTimer(g->al_query); if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); g->al_timerid = SetTimer(mifi, g); add_leaf(mifi, NULL, &group_sa); break; } } /* * If not found, add it to the list and update kernel cache. */ if (g == NULL) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG,0, "The group don't exist , trying to add it"); g = (struct listaddr *) malloc(sizeof(struct listaddr)); if (g == NULL) log(LOG_ERR, 0, "ran out of memory"); /* fatal */ g->al_addr = group_sa; g->al_old = 0; /** set a timer for expiration **/ g->al_query = 0; g->al_timer = MLD6_LISTENER_INTERVAL; g->al_reporter = *src; g->al_timerid = SetTimer(mifi, g); g->al_next = v->uv_groups; v->uv_groups = g; time(&g->al_ctime); add_leaf(mifi, NULL, &group_sa); } } /* TODO: send PIM prune message if the last member? */ void accept_listener_done(src, dst, group) struct sockaddr_in6 *src; struct in6_addr *dst, *group; { register mifi_t mifi; register struct uvif *v; register struct listaddr *g; struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; /* Don't create routing entries for the LAN scoped addresses */ if (IN6_IS_ADDR_MC_NODELOCAL(group)) /* sanity? */ { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "accept_listener_done: address multicast node local(%s)," " ignore it...", inet6_fmt(group)); return; } if (IN6_IS_ADDR_MC_LINKLOCAL(group)) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "accept_listener_done: address multicast link local(%s), " "ignore it ...", inet6_fmt(group)); return; } if ((mifi = find_vif_direct_local(src)) == NO_VIF) { IF_DEBUG(DEBUG_MLD) log(LOG_INFO, 0, "accept_listener_done: can't find a mif"); return; } IF_DEBUG(DEBUG_MLD) log(LOG_INFO, 0, - "accepting listener done message: src %s, dst% s, grp %s", + "accepting listener done message: src %s, dst %s, grp %s", inet6_fmt(&src->sin6_addr), inet6_fmt(dst), inet6_fmt(group)); v = &uvifs[mifi]; v->uv_in_mld_done++; if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))) return; /* * Look for the group in our group list in order to set up a * short-timeout query. */ group_sa.sin6_addr = *group; group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); for (g = v->uv_groups; g != NULL; g = g->al_next) { if (inet6_equal(&group_sa, &g->al_addr)) { IF_DEBUG(DEBUG_MLD) log(LOG_DEBUG, 0, "[accept_done_message] %d %d \n", g->al_old, g->al_query); /* * Ignore the done message if there are old hosts present */ if (g->al_old) return; /* * still waiting for a reply to a query, ignore the done */ if (g->al_query) return; /** delete old timer set a timer for expiration **/ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); /** send a group specific querry **/ g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE) * (MLD6_LAST_LISTENER_QUERY_COUNT + 1); if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) { send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr, NULL, &g->al_addr.sin6_addr, v->uv_ifindex, MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1); v->uv_out_mld_query++; } g->al_query = SetQueryTimer(g, mifi, MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE, MLD6_LAST_LISTENER_QUERY_INTERVAL); g->al_timerid = SetTimer(mifi, g); break; } } } /* * Time out record of a group membership on a vif */ static void DelVif(arg) void *arg; { cbk_t *cbk = (cbk_t *) arg; mifi_t mifi = cbk->mifi; struct uvif *v = &uvifs[mifi]; struct listaddr *a, **anp, *g = cbk->g; /* * Group has expired delete all kernel cache entries with this group */ if (g->al_query) DeleteTimer(g->al_query); delete_leaf(mifi, NULL, &g->al_addr); /* increment statistics */ v->uv_listener_timo++; anp = &(v->uv_groups); while ((a = *anp) != NULL) { if (a == g) { *anp = a->al_next; free((char *) a); } else { anp = &a->al_next; } } free(cbk); } /* * Set a timer to delete the record of a group membership on a vif. */ static int SetTimer(mifi, g) mifi_t mifi; struct listaddr *g; { cbk_t *cbk; cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->mifi = mifi; cbk->g = g; return timer_setTimer(g->al_timer, DelVif, cbk); } /* * Delete a timer that was set above. */ static int DeleteTimer(id) int id; { timer_clearTimer(id); return 0; } /* * Send a group-specific query. */ static void SendQuery(arg) void *arg; { cbk_t *cbk = (cbk_t *) arg; register struct uvif *v = &uvifs[cbk->mifi]; if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) { send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr, NULL, &cbk->g->al_addr.sin6_addr, v->uv_ifindex, cbk->q_time, 0, 1); v->uv_out_mld_query++; } cbk->g->al_query = 0; free(cbk); } /* * Set a timer to send a group-specific query. */ static int SetQueryTimer(g, mifi, to_expire, q_time) struct listaddr *g; mifi_t mifi; int to_expire; int q_time; { cbk_t *cbk; cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->g = g; cbk->q_time = q_time; cbk->mifi = mifi; return timer_setTimer(to_expire, SendQuery, cbk); } /* * Checks for MLD listener: returns TRUE if there is a receiver for the group * on the given uvif, or returns FALSE otherwise. */ int check_multicast_listener(v, group) struct uvif *v; struct sockaddr_in6 *group; { register struct listaddr *g; /* * Look for the group in our listener list; */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (inet6_equal(group, &g->al_addr)) return TRUE; } return FALSE; }