Index: stable/2.1/usr.sbin/mrouted/Makefile =================================================================== --- stable/2.1/usr.sbin/mrouted/Makefile (revision 10584) +++ stable/2.1/usr.sbin/mrouted/Makefile (revision 10585) @@ -1,5 +1,5 @@ -# $Id$ +# $Id: Makefile,v 1.2 1995/06/15 19:23:02 wollman Exp $ -SUBDIR= common mrouted mrinfo map-mbone mtrace +SUBDIR= common mrouted mrinfo map-mbone mtrace testrsrr .include Index: stable/2.1/usr.sbin/mrouted/Makefile.inc =================================================================== --- stable/2.1/usr.sbin/mrouted/Makefile.inc (revision 10584) +++ stable/2.1/usr.sbin/mrouted/Makefile.inc (revision 10585) @@ -1 +1,2 @@ .include "${.CURDIR}/../../Makefile.inc" +CFLAGS+= -DRSRR Index: stable/2.1/usr.sbin/mrouted/callout.c =================================================================== --- stable/2.1/usr.sbin/mrouted/callout.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/callout.c (revision 10585) @@ -1,203 +1,208 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: callout.c,v 1.3 1995/05/16 00:28:42 jkh Exp $ + * $Id: callout.c,v 1.6 1995/06/28 17:58:25 wollman Exp $ */ #include "defs.h" /* the code below implements a callout queue */ static int id = 0; static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */ static int in_callout= 0; -typedef void (* cfunc_t)(); - struct timeout_q { struct timeout_q *next; /* next event */ - int id; - cfunc_t func ; /* function to call */ + int id; + cfunc_t func; /* function to call */ char *data; /* func's data */ int time; /* time offset to next event*/ }; +#ifdef IGMP_DEBUG +static void print_Q __P((void)); +#else +#define print_Q() +#endif -static void print_Q(); - -void callout_init() +void +callout_init() { Q = (struct timeout_q *) 0; } /* * signal handler for SIGALARM that is called once every second */ -void age_callout_queue() +void +age_callout_queue() { struct timeout_q *ptr; - + if (in_callout) return; in_callout = 1; ptr = Q; - + while (ptr){ if (!ptr->time ) { /* timeout has happened */ - if(ptr->func) + if (ptr->func) ptr->func(ptr->data); Q = Q->next; - + free(ptr); ptr = Q; } else { ptr->time --; #ifdef IGMP_DEBUG log(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time); -#endif IGMP_DEBUG +#endif /* IGMP_DEBUG */ in_callout = 0; return; } } in_callout = 0; return; } -/* +/* * sets the timer */ -int timer_setTimer(delay, action, data) +int +timer_setTimer(delay, action, data) int delay; /* number of units for timeout */ cfunc_t action; /* function to be called on timeout */ char *data; /* what to call the timeout function with */ { struct timeout_q *ptr, *node, *prev; - + if (in_callout) return -1; in_callout = 1; - - /* create a node */ - node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); - if ((int) node <= 0) { + + /* create a node */ + node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); + if (node == 0) { log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); in_callout = 0; return -1; } - node->func = action; + node->func = action; node->data = data; - node->time = delay; - node->next = 0; + node->time = delay; + node->next = 0; node->id = ++id; - + prev = ptr = Q; - + /* insert node in the queue */ - + /* if the queue is empty, insert the node and return */ if (!Q) Q = node; else { /* chase the pointer looking for the right place */ - while (ptr){ - - if (delay < ptr->time){ + while (ptr) { + + if (delay < ptr->time) { /* right place */ - + node->next = ptr; if (ptr == Q) Q = node; else prev->next = node; ptr->time -= node->time; print_Q(); in_callout = 0; return node->id; - } - else { + } else { /* keep moving */ - + delay -= ptr->time; node->time = delay; prev = ptr; ptr = ptr->next; } } prev->next = node; } print_Q(); in_callout = 0; return node->id; } /* clears the associated timer */ -void timer_clearTimer( id) - int id; +void +timer_clearTimer(timer_id) + int timer_id; { struct timeout_q *ptr, *prev; + + if (in_callout) + return; + if (!timer_id) + return; - if (in_callout) return; in_callout = 1; - - - if ( !id ) {in_callout = 0; return;} - + prev = ptr = Q; - + /* * find the right node, delete it. the subsequent node's time * gets bumped up */ - + print_Q(); - while (ptr){ - if (ptr->id == id){ + while (ptr) { + if (ptr->id == timer_id) { /* got the right node */ - + /* unlink it from the queue */ - if ( ptr == Q) + if (ptr == Q) Q = Q->next; else prev->next = ptr->next; - + /* increment next node if any */ if (ptr->next != 0) (ptr->next)->time += ptr->time; - + free(ptr->data); free(ptr); print_Q(); in_callout = 0; return; } prev = ptr; ptr = ptr->next; } print_Q(); in_callout = 0; } +#ifdef IGMP_DEBUG /* * debugging utility */ -static void print_Q() +static void +print_Q() { -#ifdef IGMP_DEBUG struct timeout_q *ptr; - + for(ptr = Q; ptr; ptr = ptr->next) log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time); -#endif IGMP_DEBUG } - +#endif /* IGMP_DEBUG */ Index: stable/2.1/usr.sbin/mrouted/config.c =================================================================== --- stable/2.1/usr.sbin/mrouted/config.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/config.c (revision 10585) @@ -1,796 +1,147 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: config.c,v 1.3 1995/04/10 18:42:10 wollman Exp $ + * $Id: config.c,v 1.6 1995/06/28 17:58:27 wollman Exp $ */ #include "defs.h" -char *configfilename = "/etc/mrouted.conf"; - -extern int cache_lifetime; -extern int max_prune_lifetime; - /* - * Forward declarations. - */ -static char *next_word(); - - -/* * Query the kernel to find network interfaces that are multicast-capable * and install them in the uvifs array. */ -void config_vifs_from_kernel() +void +config_vifs_from_kernel() { struct ifreq ifbuf[32]; - struct ifreq *ifrp, *ifend, *mp; + struct ifreq *ifrp, *ifend; struct ifconf ifc; register struct uvif *v; register vifi_t vifi; - int i, n; - u_long addr, mask, subnet; + int n; + u_int32 addr, mask, subnet; short flags; ifc.ifc_buf = (char *)ifbuf; ifc.ifc_len = sizeof(ifbuf); if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) log(LOG_ERR, errno, "ioctl SIOCGIFCONF"); ifrp = (struct ifreq *)ifbuf; ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len); /* * Loop through all of the interfaces. */ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) { struct ifreq ifr; #if BSD >= 199006 n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (n < sizeof(*ifrp)) n = sizeof(*ifrp); #else n = sizeof(*ifrp); #endif /* * Ignore any interface for an address family other than IP. */ - addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr; if (ifrp->ifr_addr.sa_family != AF_INET) continue; + addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr; + /* * Need a template to preserve address info that is * used below to locate the next entry. (Otherwise, * SIOCGIFFLAGS stomps over it because the requests * are returned in a union.) */ bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); /* * Ignore loopback interfaces and interfaces that do not support * multicast. */ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); flags = ifr.ifr_flags; if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue; /* * Ignore any interface whose address and mask do not define a * valid subnet number, or whose address is of the form {subnet,0} * or {subnet,-1}. */ if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0) log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name); mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; subnet = addr & mask; if (!inet_valid_subnet(subnet, mask) || addr == subnet || addr == (subnet | ~mask)) { log(LOG_WARNING, 0, - "ignoring %s, has invalid address (%s) and/or mask (%08x)", - ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask)); + "ignoring %s, has invalid address (%s) and/or mask (%s)", + ifr.ifr_name, inet_fmt(addr, s1), inet_fmt(mask, s2)); continue; } /* * Ignore any interface that is connected to the same subnet as * one already installed in the uvifs array. */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if ((addr & v->uv_subnetmask) == v->uv_subnet || (v->uv_subnet & mask) == subnet) { log(LOG_WARNING, 0, "ignoring %s, same subnet as %s", ifr.ifr_name, v->uv_name); break; } } if (vifi != numvifs) continue; /* * If there is room in the uvifs array, install this interface. */ if (numvifs == MAXVIFS) { log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name); continue; } v = &uvifs[numvifs]; v->uv_flags = 0; v->uv_metric = DEFAULT_METRIC; - v->uv_rate_limit = DEFAULT_RATE_LIMIT; + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; v->uv_threshold = DEFAULT_THRESHOLD; v->uv_lcl_addr = addr; v->uv_rmt_addr = 0; v->uv_subnet = subnet; v->uv_subnetmask = mask; v->uv_subnetbcast = subnet | ~mask; strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ); v->uv_groups = NULL; v->uv_neighbors = NULL; v->uv_acl = NULL; + v->uv_addrs = NULL; log(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d", v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2), numvifs, v->uv_rate_limit); ++numvifs; /* * If the interface is not yet up, set the vifs_down flag to * remind us to check again later. */ if (!(flags & IFF_UP)) { v->uv_flags |= VIFF_DOWN; vifs_down = TRUE; - } - } -} - -static struct ifreq * -ifconfaddr(ifcp, a) - struct ifconf *ifcp; - u_long a; -{ - int n; - struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf; - struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len); - - while (ifrp < ifend) { - if (ifrp->ifr_addr.sa_family == AF_INET && - ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a) - return (ifrp); -#if BSD >= 199006 - n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); - if (n < sizeof(*ifrp)) - ++ifrp; - else - ifrp = (struct ifreq *)((char *)ifrp + n); -#else - ++ifrp; -#endif - } - return (0); -} - -/* - * Checks if the string constitutes a valid interface name - */ -static u_long valid_if(w) -char *w; -{ - register vifi_t vifi; - register struct uvif *v; - - for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) - if (EQUAL(v->uv_name, w)) - return v->uv_lcl_addr; - - return NULL; -} - -/* - * Read the config file to learn about tunnel vifs and - * non-default phyint parameters. - */ -void config_vifs_from_file() -{ - FILE *f; - char linebuf[100]; - char *w, *s, c; - u_long lcl_addr, rmt_addr; - struct ifconf ifc; - struct ifreq *ifr; - struct ifreq ffr; - int i; - u_int n; - struct ifreq ifbuf[32]; - vifi_t vifi; - struct uvif *v; - u_char order = 0; - vifi_t prev_vif = NO_VIF; - - f = fopen(configfilename, "r"); - if (f == NULL) { - if (errno != ENOENT) - log(LOG_ERR, errno, "can't open %s", configfilename); - return; - } - - ifc.ifc_buf = (char *)ifbuf; - ifc.ifc_len = sizeof(ifbuf); - if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) - log(LOG_ERR, errno, "ioctl SIOCGIFCONF"); - - while (fgets(linebuf, sizeof(linebuf), f) != NULL) { - - s = linebuf; - if (EQUAL((w = next_word(&s)), "")) { - /* - * blank or comment line; ignore - */ - } - - /* Set the cache_lifetime for kernel entries */ - else if (EQUAL(w, "cache_lifetime")) { - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing cache_lifetime value in %s", - configfilename); - continue; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 300 || n > 86400 ) { - log(LOG_ERR, 0, - "invalid cache_lifetime '%s' (30086400) in %s", - w, configfilename); - break; - } - prev_vif = NO_VIF; - cache_lifetime = n; - max_prune_lifetime = cache_lifetime * 2; - } - - /* Check if pruning is to be turned off */ - else if (EQUAL(w, "pruning")) { - if (!EQUAL((w = next_word(&s)), "off") && - !EQUAL(w, "on")) { - log(LOG_ERR, 0, - "invalid word '%s' in %s", - w, configfilename); - continue; - } - if (EQUAL(w, "off")) - pruning = 0; - - prev_vif = NO_VIF; - } - - /* Check for boundary statements (as continuation of a prev. line) */ - else if (EQUAL(w, "boundary") && prev_vif != NO_VIF) { - register struct vif_acl *v_acl; - register u_long baddr; - - v = &uvifs[prev_vif]; - - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing group address for boundary %s in %s", - inet_fmt(lcl_addr, s1), configfilename); - w = "garbage"; - break; - } - - if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) || - n < 0 || n> 32) { - log(LOG_ERR, 0, - "incorrect boundary format %s in %s", - w, configfilename); - w = "garbage"; - break; - } - - if ((baddr = inet_parse(s1)) == 0xffffffff || - (ntohl(baddr) & 0xff000000) != 0xef000000) { - log(LOG_ERR, 0, - "incorrect boundary address %s in %s", - s1, configfilename); - continue; - } - - v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl)); - if (v_acl == NULL) - log(LOG_ERR, 0, - "out of memory"); - VAL_TO_MASK(v_acl->acl_mask, n); - v_acl->acl_addr = baddr & v_acl->acl_mask; - - /* - * link into data structure - */ - v_acl->acl_next = v->uv_acl; - v->uv_acl = v_acl; - } - - else if (EQUAL(w, "phyint")) { - /* - * phyint [disable] [metric ] [threshold ] - * [rate_limit ] - */ - - /* - * Check if phyint was the first line - scream if not - */ - if (order) { - log(LOG_ERR, 0, - "phyint stmnts should occur before tunnel stmnts in %s", - configfilename); - continue; - } - - /* - * Parse the local address. - */ - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing phyint address in %s", - configfilename); - continue; - } - - if (isalpha(*w) && !(lcl_addr = valid_if(w))) { - log(LOG_ERR, 0, - "invalid phyint name '%s' in %s", - w, configfilename); - continue; - } - - if (isdigit(*w)) { - if ((lcl_addr = inet_parse(w)) == 0xffffffff || - !inet_valid_host(lcl_addr)) { - log(LOG_ERR, 0, - "invalid phyint address '%s' in %s", - w, configfilename); - continue; - } - } - - /* - * Look up the vif with the specified local address. - */ - for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if (!(v->uv_flags & VIFF_TUNNEL) && - lcl_addr == v->uv_lcl_addr) { - break; - } - } - - if (vifi == numvifs) { - log(LOG_ERR, 0, - "phyint %s in %s is not a configured interface", - inet_fmt(lcl_addr, s1), configfilename); - continue; - } - - /* - * Look for "disable", "metric", "threshold", "rate_limit" - * and "boundary" options. - */ - prev_vif = vifi; - - while (!EQUAL((w = next_word(&s)), "")) { - if (EQUAL(w, "disable")) { - v->uv_flags |= VIFF_DISABLED; - } - else if (EQUAL(w, "metric")) { - if(EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing metric for phyint %s in %s", - inet_fmt(lcl_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 1 || n >= UNREACHABLE ) { - log(LOG_ERR, 0, - "invalid metric '%s' for phyint %s in %s", - w, inet_fmt(lcl_addr, s1), configfilename); - break; - } - v->uv_metric = n; - } - else if (EQUAL(w, "threshold")) { - if(EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing threshold for phyint %s in %s", - inet_fmt(lcl_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 1 || n > 255 ) { - log(LOG_ERR, 0, - "invalid threshold '%s' for phyint %s in %s", - w, inet_fmt(lcl_addr, s1), configfilename); - break; - } - v->uv_threshold = n; - } - else if (EQUAL(w, "rate_limit")) { - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing rate_limit for phyint %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 0 || n > MAX_RATE_LIMIT ) { - log(LOG_ERR, 0, - "invalid rate limit '%s' for phyint %s in %s", - w, inet_fmt(lcl_addr, s1), configfilename); - break; - } - v->uv_rate_limit = n; - } - else if (EQUAL(w, "boundary")) { - register struct vif_acl *v_acl; - register u_long baddr; - - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing group address for boundary %s in %s", - inet_fmt(lcl_addr, s1), configfilename); - w = "garbage"; - break; - } - - if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) || - n < 0 || n> 32) { - log(LOG_ERR, 0, - "incorrect boundary format %s in %s", - w, configfilename); - w = "garbage"; - break; - } - - if ((baddr = inet_parse(s1)) == 0xffffffff || - (ntohl(baddr) & 0xef000000) != 0xef000000) { - log(LOG_ERR, 0, - "incorrect boundary address %s in %s", - s1, configfilename); - continue; - } - - v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl)); - if (v_acl == NULL) - log(LOG_ERR, 0, - "out of memory"); - VAL_TO_MASK(v_acl->acl_mask, n); - v_acl->acl_addr = baddr & v_acl->acl_mask; - - /* - * link into data structure - */ - v_acl->acl_next = v->uv_acl; - v->uv_acl = v_acl; - } - else { - log(LOG_ERR, 0, - "invalid keyword (%s) in %s", - w, configfilename); - break; - } - } - if (!EQUAL(w, "")) continue; - } - - else if (EQUAL(w, "tunnel")) { - /* - * tunnel [srcrt] [metric ] - * [threshold ] [rate_limit ] - */ - - order++; - - /* - * Parse the local address. - */ - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing tunnel local address in %s", - configfilename); - continue; - } - if ((lcl_addr = inet_parse(w)) == 0xffffffff || - !inet_valid_host(lcl_addr)) { - log(LOG_ERR, 0, - "invalid tunnel local address '%s' in %s", - w, configfilename); - continue; - } - - /* - * Make sure the local address is one of ours. - */ - ifr = ifconfaddr(&ifc, lcl_addr); - if (ifr == 0) { - log(LOG_ERR, 0, - "tunnel local address %s in %s is not one of ours", - inet_fmt(lcl_addr, s1), configfilename); - continue; - } - - /* - * Make sure the local address doesn't name a loopback interface.. - */ - strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ); - if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) { - log(LOG_ERR, errno, - "ioctl SIOCGIFFLAGS for %s", ffr.ifr_name); - } - if (ffr.ifr_flags & IFF_LOOPBACK) { - log(LOG_ERR, 0, - "tunnel local address %s in %s is a loopback interface", - inet_fmt(lcl_addr, s1), configfilename); - continue; - } - - /* - * Parse the remote address. - */ - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing tunnel remote address in %s", - configfilename); - continue; - } - if ((rmt_addr = inet_parse(w)) == 0xffffffff || - !inet_valid_host(rmt_addr)) { - log(LOG_ERR, 0, - "invalid tunnel remote address %s in %s", - w, configfilename); - continue; - } - - /* - * Make sure the remote address is not one of ours. - */ - if (ifconfaddr(&ifc, rmt_addr) != 0) { - log(LOG_ERR, 0, - "tunnel remote address %s in %s is one of ours", - inet_fmt(rmt_addr, s1), configfilename); - continue; - } - - /* - * Make sure the remote address has not been used for another - * tunnel and does not belong to a subnet to which we have direct - * access on an enabled phyint. - */ - for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if (v->uv_flags & VIFF_TUNNEL) { - if (rmt_addr == v->uv_rmt_addr) { - log(LOG_ERR, 0, - "duplicate tunnel remote address %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - break; - } - } - else if (!(v->uv_flags & VIFF_DISABLED)) { - if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) { - log(LOG_ERR, 0, - "unnecessary tunnel remote address %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - break; - } - } - } - if (vifi != numvifs) continue; - - /* - * OK, let's initialize a uvif structure for the tunnel. - */ - if (numvifs == MAXVIFS) { - log(LOG_ERR, 0, "too many vifs, ignoring tunnel to %s", - inet_fmt(rmt_addr, s1)); - continue; - } - v = &uvifs[numvifs]; - v->uv_flags = VIFF_TUNNEL; - v->uv_metric = DEFAULT_METRIC; - v->uv_rate_limit = DEFAULT_RATE_LIMIT; - v->uv_threshold = DEFAULT_THRESHOLD; - v->uv_lcl_addr = lcl_addr; - v->uv_rmt_addr = rmt_addr; - v->uv_subnet = 0; - v->uv_subnetmask = 0; - v->uv_subnetbcast = 0; - strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ); - v->uv_groups = NULL; - v->uv_neighbors = NULL; - v->uv_acl = NULL; - - /* - * set variable to define which interface - */ - prev_vif = numvifs; - - /* - * Look for "metric", "threshold", "srcrt", "rate_limit" - * and "boundary" options. - */ - while (!EQUAL((w = next_word(&s)), "")) { - if (EQUAL(w, "metric")) { - if(EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing metric for tunnel to %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 1 || n >= UNREACHABLE ) { - log(LOG_ERR, 0, - "invalid metric '%s' for tunnel to %s in %s", - w, inet_fmt(rmt_addr, s1), configfilename); - break; - } - v->uv_metric = n; - } - else if (EQUAL(w, "threshold")) { - if(EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing threshold for tunnel to %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 1 || n > 255 ) { - log(LOG_ERR, 0, - "invalid threshold '%s' for tunnel to %s in %s", - w, inet_fmt(rmt_addr, s1), configfilename); - break; - } - v->uv_threshold = n; - } - else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) { - v->uv_flags |= VIFF_SRCRT; - } - else if (EQUAL(w, "rate_limit")) { - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing rate_limit for tunnel to %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - w = "garbage"; - break; - } - if(sscanf(w, "%u%c", &n, &c) != 1 || - n < 0 || n > MAX_RATE_LIMIT ) { - log(LOG_ERR, 0, - "invalid rate_limit '%s' for tunnel to %s in %s", - w, inet_fmt(rmt_addr, s1), configfilename); - break; - } - v->uv_rate_limit = n; - } - else if (EQUAL(w, "boundary")) { - register struct vif_acl *v_acl; - register u_long baddr; - - if (EQUAL((w = next_word(&s)), "")) { - log(LOG_ERR, 0, - "missing group address for tunnel to %s in %s", - inet_fmt(rmt_addr, s1), configfilename); - w = "garbage"; - break; - } - - if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) || - n < 0 || n> 32) { - log(LOG_ERR, 0, - "incorrect format '%s' for tunnel to %s in %s", - w, inet_fmt(rmt_addr, s1), configfilename); - break; - } - - if ((baddr = inet_parse(s1)) == 0xffffffff || - (ntohl(baddr) & 0xef000000) != 0xef000000) { - log(LOG_ERR, 0, - "incorrect address %s for tunnel to %s in %s", - s1, inet_fmt(rmt_addr, s1), configfilename); - continue; - } - - v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl)); - if (v_acl == NULL) - log(LOG_ERR, 0, - "out of memory"); - VAL_TO_MASK(v_acl->acl_mask, n); - v_acl->acl_addr = baddr & v_acl->acl_mask; - - /* - * link into data structure - */ - v_acl->acl_next = v->uv_acl; - v->uv_acl = v_acl; - } - else { - log(LOG_ERR, 0, - "invalid keyword (%s) in %s", - w, configfilename); - break; - } - } - if (!EQUAL(w, "")) continue; - - log(LOG_INFO, 0, - "installing %stunnel from %s to %s as vif #%u - rate=%d", - v->uv_flags & VIFF_SRCRT? "srcrt " : "", - inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2), - numvifs, v->uv_rate_limit); - - ++numvifs; - - if (!(ffr.ifr_flags & IFF_UP)) { - v->uv_flags |= VIFF_DOWN; - vifs_down = TRUE; - } - } - - else { - log(LOG_ERR, 0, - "unknown command '%s' in %s", w, configfilename); - } - } - - close(f); -} - - -/* - * Return a pointer to the next "word" in the string to which '*s' points, - * lower-cased and null terminated, and advance '*s' to point beyond the word. - * Words are separated by blanks and/or tabs, and the input string is - * considered to terminate at a newline, '#' (comment), or null character. - * If no words remain, a pointer to a null string ("") is returned. - * Warning: This function clobbers the input string. - */ -static char *next_word(s) - char **s; -{ - char *w; - - w = *s; - while (*w == ' ' || *w == '\t') - ++w; - - *s = w; - for(;;) { - switch (**s) { - - case ' ' : - case '\t' : **s = '\0'; - ++*s; - return (w); - - case '\n' : - case '#' : **s = '\0'; - return (w); - - case '\0' : return (w); - - default : if (isascii(**s) && isupper(**s)) - **s = tolower(**s); - ++*s; } } } Index: stable/2.1/usr.sbin/mrouted/defs.h =================================================================== --- stable/2.1/usr.sbin/mrouted/defs.h (revision 10584) +++ stable/2.1/usr.sbin/mrouted/defs.h (revision 10585) @@ -1,172 +1,292 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: defs.h,v 1.2 1994/09/08 02:51:13 wollman Exp $ + * $Id: defs.h,v 1.5 1995/06/28 17:58:29 wollman Exp $ */ #include +#include +#include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include +#ifdef RSRR +#include +#endif /* RSRR */ +/*XXX*/ +typedef u_int u_int32; + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +typedef void (*cfunc_t) __P((void *)); +typedef void (*ihfunc_t) __P((fd_set *)); + #include "dvmrp.h" #include "vif.h" #include "route.h" #include "prune.h" +#include "pathnames.h" +#ifdef RSRR +#include "rsrr.h" +#include "rsrr_var.h" +#endif /* RSRR */ /* * Miscellaneous constants and macros. */ #define FALSE 0 #define TRUE 1 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) #define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY #define PROTOCOL_VERSION 3 /* increment when packet format/content changes */ -#define MROUTED_VERSION 4 /* increment on local changes or bug fixes, */ +#define MROUTED_VERSION 6 /* increment on local changes or bug fixes, */ /* reset to 0 whever PROTOCOL_VERSION increments */ -#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION ) +#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION | \ + ((NF_PRUNE | NF_GENID | NF_MTRACE) << 16)) /* for IGMP 'group' field of DVMRP messages */ +#define LEAF_FLAGS (( vifs_with_neighbors == 1 ) ? 0x010000 : 0) + /* more for IGMP 'group' field of DVMRP messages */ #define DEL_RTE_GROUP 0 #define DEL_ALL_ROUTES 1 /* for Deleting kernel table entries */ +/* obnoxious gcc gives an extraneous warning about this constant... */ +#if defined(__STDC__) || defined(__GNUC__) +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +#else +#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ +#define const /**/ +#endif + +#ifdef RSRR +#define BIT_ZERO(X) ((X) = 0) +#define BIT_SET(X,n) ((X) |= 1 << (n)) +#define BIT_CLR(X,n) ((X) &= ~(1 << (n))) +#define BIT_TST(X,n) ((X) & 1 << (n)) +#endif /* RSRR */ + /* * External declarations for global variables and functions. */ -extern char recv_buf[MAX_IP_PACKET_LEN]; -extern char send_buf[MAX_IP_PACKET_LEN]; +#define RECV_BUF_SIZE MAX_IP_PACKET_LEN +extern char *recv_buf; +extern char *send_buf; extern int igmp_socket; -extern u_long allhosts_group; -extern u_long dvmrp_group; -extern u_long dvmrp_genid; +#ifdef RSRR +extern int rsrr_socket; +#endif /* RSRR */ +extern u_int32 allhosts_group; +extern u_int32 allrtrs_group; +extern u_int32 dvmrp_group; +extern u_int32 dvmrp_genid; #define DEFAULT_DEBUG 2 /* default if "-d" given without value */ extern int debug; extern u_char pruning; extern int routes_changed; extern int delay_change_reports; extern unsigned nroutes; extern struct uvif uvifs[MAXVIFS]; extern vifi_t numvifs; extern int vifs_down; extern int udp_socket; +extern int vifs_with_neighbors; extern char s1[]; extern char s2[]; extern char s3[]; +extern char s4[]; +#if !(defined(BSD) && (BSD >= 199103)) extern int errno; extern int sys_nerr; -#ifndef __FreeBSD__ extern char * sys_errlist[]; #endif -extern void log(); +/* main.c */ +extern void log __P((int, int, char *, ...)); +extern int register_input_handler __P((int fd, ihfunc_t func)); -extern void init_igmp(); -extern void accept_igmp(); -extern void send_igmp(); +/* igmp.c */ +extern void init_igmp __P((void)); +extern void accept_igmp __P((int recvlen)); +extern void send_igmp __P((u_int32 src, u_int32 dst, int type, + int code, u_int32 group, + int datalen)); -extern void init_routes(); -extern void start_route_updates(); -extern void update_route(); -extern void age_routes(); -extern void expire_all_routes(); -extern void free_all_routes(); +/* callout.c */ +extern void callout_init __P((void)); +extern void age_callout_queue __P((void)); +extern int timer_setTimer __P((int delay, cfunc_t action, + char *data)); +extern void timer_clearTimer __P((int timer_id)); -extern void accept_probe(); -extern void accept_report(); -extern void report(); -extern void report_to_all_neighbors(); -extern int report_next_chunk(); -extern void add_vif_to_routes(); -extern void delete_vif_from_routes(); -extern void delete_neighbor_from_routes(); -extern void dump_routes(); +/* route.c */ +extern void init_routes __P((void)); +extern void start_route_updates __P((void)); +extern void update_route __P((u_int32 origin, u_int32 mask, + int metric, u_int32 src, + vifi_t vifi)); +extern void age_routes __P((void)); +extern void expire_all_routes __P((void)); +extern void free_all_routes __P((void)); +extern void accept_probe __P((u_int32 src, u_int32 dst, + char *p, int datalen, + u_int32 level)); +extern void accept_report __P((u_int32 src, u_int32 dst, + char *p, int datalen, + u_int32 level)); +extern struct rtentry * determine_route __P((u_int32 src)); +extern void report __P((int which_routes, vifi_t vifi, + u_int32 dst)); +extern void report_to_all_neighbors __P((int which_routes)); +extern int report_next_chunk __P((void)); +extern void add_vif_to_routes __P((vifi_t vifi)); +extern void delete_vif_from_routes __P((vifi_t vifi)); +extern void delete_neighbor_from_routes __P((u_int32 addr, + vifi_t vifi)); +extern void dump_routes __P((FILE *fp)); +extern void start_route_updates __P((void)); -extern void init_vifs(); -extern void check_vif_state(); -extern vifi_t find_vif(); -extern void age_vifs(); -extern void dump_vifs(); -extern void stop_all_vifs(); +/* vif.c */ +extern void init_vifs __P((void)); +extern void check_vif_state __P((void)); +extern vifi_t find_vif __P((u_int32 src, u_int32 dst)); +extern void age_vifs __P((void)); +extern void dump_vifs __P((FILE *fp)); +extern void stop_all_vifs __P((void)); +extern struct listaddr *neighbor_info __P((vifi_t vifi, u_int32 addr)); +extern void accept_group_report __P((u_int32 src, u_int32 dst, + u_int32 group, int r_type)); +extern void query_groups __P((void)); +extern void probe_for_neighbors __P((void)); +extern int update_neighbor __P((vifi_t vifi, u_int32 addr, + int msgtype, char *p, int datalen, + u_int32 level)); +extern void accept_neighbor_request __P((u_int32 src, u_int32 dst)); +extern void accept_neighbor_request2 __P((u_int32 src, + u_int32 dst)); +extern void accept_neighbors __P((u_int32 src, u_int32 dst, + u_char *p, int datalen, u_int32 level)); +extern void accept_neighbors2 __P((u_int32 src, u_int32 dst, + u_char *p, int datalen, u_int32 level)); +extern void accept_leave_message __P((u_int32 src, u_int32 dst, + u_int32 group)); +extern void accept_membership_query __P((u_int32 src, u_int32 dst, + u_int32 group, int tmo)); -extern void accept_group_report(); -extern void query_groups(); -extern void probe_for_neighbors(); -extern int update_neighbor(); -extern void accept_neighbor_request(); +/* config.c */ +extern void config_vifs_from_kernel __P((void)); -extern void config_vifs_from_kernel(); -extern void config_vifs_from_file(); +/* cfparse.y */ +extern void config_vifs_from_file __P((void)); -extern int inet_valid_host(); -extern int inet_valid_subnet(); -extern char * inet_fmt(); -extern char * inet_fmts(); -extern u_long inet_parse(); -extern int inet_cksum(); +/* inet.c */ +extern int inet_valid_host __P((u_int32 naddr)); +extern int inet_valid_subnet __P((u_int32 nsubnet, u_int32 nmask)); +extern char * inet_fmt __P((u_int32 addr, char *s)); +extern char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s)); +extern u_int32 inet_parse __P((char *s)); +extern int inet_cksum __P((u_short *addr, u_int len)); -extern struct rtentry * determine_route(); - -extern void init_ktable(); -extern int grplst_mem(); -extern void add_table_entry(); -extern void del_table_entry(); -extern void update_table_entry(); -extern void update_lclgrp(); -extern void delete_lclgrp(); - +/* prune.c */ extern unsigned kroutes; -extern void send_prune(); -extern void accept_prune(); -extern int no_entry_exists(); -extern struct ktable * find_src_grp(); -extern int rtr_cnt(); -extern void free_all_prunes(); -extern void age_table_entry(); -extern void dump_cache(); +extern void add_table_entry __P((u_int32 origin, u_int32 mcastgrp)); +extern void del_table_entry __P((struct rtentry *r, + u_int32 mcastgrp, u_int del_flag)); +extern void update_table_entry __P((struct rtentry *r)); +extern void init_ktable __P((void)); +extern void accept_prune __P((u_int32 src, u_int32 dst, char *p, + int datalen)); +extern void steal_sources __P((struct rtentry *rt)); +extern void reset_neighbor_state __P((vifi_t vifi, u_int32 addr)); +extern int grplst_mem __P((vifi_t vifi, u_int32 mcastgrp)); +extern int scoped_addr __P((vifi_t vifi, u_int32 addr)); +extern void free_all_prunes __P((void)); +extern void age_table_entry __P((void)); +extern void dump_cache __P((FILE *fp2)); +extern void update_lclgrp __P((vifi_t vifi, u_int32 mcastgrp)); +extern void delete_lclgrp __P((vifi_t vifi, u_int32 mcastgrp)); +extern void chkgrp_graft __P((vifi_t vifi, u_int32 mcastgrp)); +extern void accept_graft __P((u_int32 src, u_int32 dst, char *p, + int datalen)); +extern void accept_g_ack __P((u_int32 src, u_int32 dst, char *p, + int datalen)); +/* u_int is promoted u_char */ +extern void accept_mtrace __P((u_int32 src, u_int32 dst, + u_int32 group, char *data, u_int no, + int datalen)); -extern void chkgrp_graft(); -extern void accept_graft(); -extern void send_graft_ack(); -extern void send_graft(); -extern void accept_g_ack(); -extern void mtrace(); +/* kern.c */ +extern void k_set_rcvbuf __P((int bufsize)); +extern void k_hdr_include __P((int bool)); +extern void k_set_ttl __P((int t)); +extern void k_set_loop __P((int l)); +extern void k_set_if __P((u_int32 ifa)); +extern void k_join __P((u_int32 grp, u_int32 ifa)); +extern void k_leave __P((u_int32 grp, u_int32 ifa)); +extern void k_init_dvmrp __P((void)); +extern void k_stop_dvmrp __P((void)); +extern void k_add_vif __P((vifi_t vifi, struct uvif *v)); +extern void k_del_vif __P((vifi_t vifi)); +extern void k_add_rg __P((u_int32 origin, struct gtable *g)); +extern int k_del_rg __P((u_int32 origin, struct gtable *g)); +extern int k_get_version __P((void)); -extern char * malloc(); -extern char * fgets(); -extern FILE * fopen(); - -#ifndef htonl -extern u_long htonl(); -extern u_long ntohl(); +#ifdef SNMP +/* prune.c */ +extern struct rtentry * snmp_find_route __P(()); +extern struct gtable * find_grp __P(()); +extern struct stable * find_grp_src __P(()); #endif + +#ifdef RSRR +/* prune.c */ +extern struct gtable *kernel_table; +extern struct gtable *gtp; +extern int find_src_grp __P((u_int32 src, u_int32 mask, + u_int32 grp)); + +/* rsrr.c */ +extern void rsrr_init __P((void)); +extern void rsrr_read __P((fd_set *rfd)); +extern void rsrr_clean __P((void)); +extern void rsrr_cache_send __P((struct gtable *gt, int notify)); +extern void rsrr_cache_clean __P((struct gtable *gt)); +#endif /* RSRR */ Index: stable/2.1/usr.sbin/mrouted/dvmrp.h =================================================================== --- stable/2.1/usr.sbin/mrouted/dvmrp.h (revision 10584) +++ stable/2.1/usr.sbin/mrouted/dvmrp.h (revision 10585) @@ -1,152 +1,161 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: dvmrp.h,v 1.6 1994/08/24 23:53:30 thyagara Exp $ + * $Id: dvmrp.h,v 1.4 1995/06/28 17:58:31 wollman Exp $ */ /* * A DVMRP message consists of an IP header + an IGMP header + (for some types) * zero or more bytes of data. * * For REPORT messages, the data is route information; the route information * consists of one or more lists of the following form: * * (mask, (origin, metric), (origin, metric), ...) * * where: * * "mask" is the subnet mask for all the origins in the list. * It is always THREE bytes long, containing the low-order * three bytes of the mask (the high-order byte is always * 0xff and therefore need not be transmitted). * * "origin" is the number of a subnet from which multicast datagrams * may originate. It is from one to four bytes long, * depending on the value of "mask": * if all bytes of the mask are zero * the subnet number is one byte long * else if the low-order two bytes of the mask are zero * the subnet number is two bytes long * else if the lowest-order byte of the mask is zero * the subnet number is three bytes long, * else * the subnet number is four bytes long. * * "metric" is a one-byte value consisting of two subfields: * - the high-order bit is a flag which, when set, indicates * the last (origin, metric) pair of a list. * - the low-order seven bits contain the routing metric for * the corresponding origin, relative to the sender of the * DVMRP report. The metric may have the value of UNREACHABLE * added to it as a "split horizon" indication (so called * "poisoned reverse"). * * Within a list, the origin subnet numbers must be in ascending order, and * the lists themselves are in order of increasing mask value. A message may * not exceed 576 bytes, the default maximum IP reassembly size, including * the IP and IGMP headers; the route information may be split across more * than one message if necessary, by terminating a list in one message and * starting a new list in the next message (repeating the same mask value, * if necessary). * * For NEIGHBORS messages, the data is neighboring-router information * consisting of one or more lists of the following form: * * (local-addr, metric, threshold, ncount, neighbor, neighbor, ...) * * where: * * "local-addr" is the sending router's address as seen by the neighbors * in this list; it is always four bytes long. * "metric" is a one-byte unsigned value, the TTL `cost' of forwarding * packets to any of the neighbors on this list. * "threshold" is a one-byte unsigned value, a lower bound on the TTL a * packet must have to be forwarded to any of the neighbors on * this list. * "ncount" is the number of neighbors in this list. * "neighbor" is the address of a neighboring router, four bytes long. * * As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes, * including the IP and IGMP headers; split longer messages by terminating the * list in one and continuing in another, repeating the local-addr, etc., if * necessary. * * For NEIGHBORS2 messages, the data is identical to NEIGHBORS except * there is a flags byte before the neighbor count: * * (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...) */ /* * DVMRP message types (carried in the "code" field of an IGMP header) */ #define DVMRP_PROBE 1 /* for finding neighbors */ #define DVMRP_REPORT 2 /* for reporting some or all routes */ #define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */ /* of this router's neighbors. */ #define DVMRP_NEIGHBORS 4 /* response to such a request */ #define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */ #define DVMRP_NEIGHBORS2 6 #define DVMRP_PRUNE 7 /* prune message */ #define DVMRP_GRAFT 8 /* graft message */ #define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */ /* * 'flags' byte values in DVMRP_NEIGHBORS2 reply. */ #define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */ #define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */ +#define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */ #define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ #define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ #define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ +#define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf */ /* * Limit on length of route data */ #define MAX_IP_PACKET_LEN 576 #define MIN_IP_HEADER_LEN 20 #define MAX_IP_HEADER_LEN 60 #define MAX_DVMRP_DATA_LEN \ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN ) /* * Various protocol constants (all times in seconds) */ /* address for multicast DVMRP msgs */ -#define INADDR_DVMRP_GROUP (u_long)0xe0000004 /* 224.0.0.4 */ +#define INADDR_DVMRP_GROUP (u_int32)0xe0000004 /* 224.0.0.4 */ + /* address for multicast mtrace msg */ +#define INADDR_ALLRTRS_GROUP (u_int32)0xe0000002 /* 224.0.0.2 */ #define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */ /* (This is the timer interrupt */ /* interval; all times must be */ /* multiples of this value.) */ #define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */ #define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */ #define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */ #define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */ #define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */ #define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */ #define NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */ #define GROUP_QUERY_INTERVAL 125 /* periodic group query interval */ #define GROUP_EXPIRE_TIME 270 /* time to consider group gone */ +#define LEAVE_EXPIRE_TIME 3 /* " " after receiving a leave */ +/* Note: LEAVE_EXPIRE_TIME should ideally be shorter, but the resolution of + * the timer in mrouted doesn't allow us to follow the spec and make it any + * shorter. */ #define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */ #define DEFAULT_METRIC 1 /* default subnet/tunnel metric */ #define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */ -#define MAX_RATE_LIMIT 100000 /* max rate limit */ -#define DEFAULT_RATE_LIMIT 0 /* default rate limit */ +#define MAX_RATE_LIMIT 100000 /* max rate limit */ +#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */ +#define DEFAULT_TUN_RATE_LIMIT 500 /* default tunnel rate limit */ #define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */ #define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */ #define OLD_AGE_THRESHOLD 2 Index: stable/2.1/usr.sbin/mrouted/igmp.c =================================================================== --- stable/2.1/usr.sbin/mrouted/igmp.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/igmp.c (revision 10585) @@ -1,294 +1,339 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * igmp.c,v 1.2 1994/09/08 02:51:15 wollman Exp + * $Id: igmp.c,v 1.7 1995/06/28 17:58:32 wollman Exp $ */ #include "defs.h" /* * Exported variables. */ -char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */ -char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */ +char *recv_buf; /* input packet buffer */ +char *send_buf; /* output packet buffer */ int igmp_socket; /* socket for all network I/O */ -u_long allhosts_group; /* allhosts addr in net order */ -u_long dvmrp_group; /* DVMRP grp addr in net order */ -u_long dvmrp_genid; /* IGMP generation id */ +u_int32 allhosts_group; /* All hosts addr in net order */ +u_int32 allrtrs_group; /* All-Routers " in net order */ +u_int32 dvmrp_group; /* DVMRP grp addr in net order */ +u_int32 dvmrp_genid; /* IGMP generation id */ /* + * Local function definitions. + */ +/* u_char promoted to u_int */ +static char * packet_kind __P((u_int type, u_int code)); +static int igmp_log_level __P((u_int type, u_int code)); + +/* * Open and initialize the igmp socket, and fill in the non-changing * IP header fields in the output packet buffer. */ -void init_igmp() +void +init_igmp() { struct ip *ip; - if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) + recv_buf = malloc(RECV_BUF_SIZE); + send_buf = malloc(RECV_BUF_SIZE); + + if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) log(LOG_ERR, errno, "IGMP socket"); k_hdr_include(TRUE); /* include IP header when sending */ k_set_rcvbuf(48*1024); /* lots of input buffering */ k_set_ttl(1); /* restrict multicasts to one hop */ k_set_loop(FALSE); /* disable multicast loopback */ ip = (struct ip *)send_buf; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_v = IPVERSION; ip->ip_tos = 0; ip->ip_off = 0; ip->ip_p = IPPROTO_IGMP; ip->ip_ttl = MAXTTL; /* applies to unicasts only */ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); dvmrp_group = htonl(INADDR_DVMRP_GROUP); + allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); } -/* %%% hack for PIM %%% */ -#define IGMP_PIM 0x14 #define PIM_QUERY 0 #define PIM_REGISTER 1 #define PIM_REGISTER_STOP 2 #define PIM_JOIN_PRUNE 3 #define PIM_RP_REACHABLE 4 #define PIM_ASSERT 5 #define PIM_GRAFT 6 #define PIM_GRAFT_ACK 7 -static char *packet_kind(type, code) - u_char type, code; +static char * +packet_kind(type, code) + u_int type, code; { switch (type) { case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report "; - case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "new membership report "; - case IGMP_HOST_LEAVE_MESSAGE: return "leave message"; + case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "new member report "; + case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; case IGMP_DVMRP: switch (code) { case DVMRP_PROBE: return "neighbor probe "; case DVMRP_REPORT: return "route report "; case DVMRP_ASK_NEIGHBORS: return "neighbor request "; case DVMRP_NEIGHBORS: return "neighbor list "; case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; case DVMRP_NEIGHBORS2: return "neighbor list 2 "; - case DVMRP_PRUNE: return "prune message "; - case DVMRP_GRAFT: return "graft message "; + case DVMRP_PRUNE: return "prune message "; + case DVMRP_GRAFT: return "graft message "; case DVMRP_GRAFT_ACK: return "graft message ack "; default: return "unknown DVMRP msg "; } - case IGMP_PIM: /* %%% hack for PIM %%% */ + case IGMP_PIM: switch (code) { case PIM_QUERY: return "PIM Router-Query "; case PIM_REGISTER: return "PIM Register "; case PIM_REGISTER_STOP: return "PIM Register-Stop "; case PIM_JOIN_PRUNE: return "PIM Join/Prune "; case PIM_RP_REACHABLE: return "PIM RP-Reachable "; case PIM_ASSERT: return "PIM Assert "; case PIM_GRAFT: return "PIM Graft "; case PIM_GRAFT_ACK: return "PIM Graft-Ack "; default: return "unknown PIM msg "; } case IGMP_MTRACE: return "IGMP trace query "; case IGMP_MTRACE_RESP: return "IGMP trace reply "; default: return "unknown IGMP msg "; } } /* * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ -void accept_igmp(recvlen) +void +accept_igmp(recvlen) int recvlen; { - register u_long src, dst, group; + register u_int32 src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; if (recvlen < sizeof(struct ip)) { log(LOG_WARNING, 0, "received packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; - /* + /* * this is most likely a message from the kernel indicating that - * a new src grp pair message has arrived and so, it would be + * a new src grp pair message has arrived and so, it would be * necessary to install a route into the kernel for this. */ if (ip->ip_p == 0) { if (src == 0 || dst == 0) log(LOG_WARNING, 0, "kernel request not accurate"); else add_table_entry(src, dst); return; } iphdrlen = ip->ip_hl << 2; ipdatalen = ip->ip_len; if (iphdrlen + ipdatalen != recvlen) { log(LOG_WARNING, 0, "received packet shorter (%u bytes) than hdr+data length (%u+%u)", recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { log(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inet_fmt(src, s1)); return; } log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", packet_kind(igmp->igmp_type, igmp->igmp_code), inet_fmt(src, s1), inet_fmt(dst, s2)); switch (igmp->igmp_type) { case IGMP_HOST_MEMBERSHIP_QUERY: - /* we have to do the determination of the querrier router here */ + accept_membership_query(src, dst, group, igmp->igmp_code); return; case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_NEW_MEMBERSHIP_REPORT: - accept_group_report(src, dst, group,igmp->igmp_type); + accept_group_report(src, dst, group, igmp->igmp_type); return; - + case IGMP_HOST_LEAVE_MESSAGE: - leave_group_message(src, dst, group); + accept_leave_message(src, dst, group); return; case IGMP_DVMRP: - switch (igmp->igmp_code) { + group = ntohl(group); + switch (igmp->igmp_code) { case DVMRP_PROBE: accept_probe(src, dst, - (char *)(igmp+1), igmpdatalen, ntohl(group)); + (char *)(igmp+1), igmpdatalen, group); return; case DVMRP_REPORT: accept_report(src, dst, - (char *)(igmp+1), igmpdatalen, ntohl(group)); + (char *)(igmp+1), igmpdatalen, group); return; case DVMRP_ASK_NEIGHBORS: accept_neighbor_request(src, dst); return; case DVMRP_ASK_NEIGHBORS2: accept_neighbor_request2(src, dst); return; case DVMRP_NEIGHBORS: - accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen, - group); + accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, + group); return; case DVMRP_NEIGHBORS2: - accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen, - group); + accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, + group); return; case DVMRP_PRUNE: accept_prune(src, dst, (char *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT: accept_graft(src, dst, (char *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT_ACK: accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen); return; default: log(LOG_INFO, 0, "ignoring unknown DVMRP message code %u from %s to %s", igmp->igmp_code, inet_fmt(src, s1), inet_fmt(dst, s2)); return; } - - case IGMP_PIM: /* %%% hack for PIM %%% */ + case IGMP_PIM: return; + case IGMP_MTRACE_RESP: + return; + case IGMP_MTRACE: - mtrace(src, dst, group, (char *)(igmp+1), + accept_mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code, igmpdatalen); return; default: log(LOG_INFO, 0, - "ignoring unknown IGMP message type %u from %s to %s", + "ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inet_fmt(src, s1), inet_fmt(dst, s2)); return; } } +/* + * Some IGMP messages are more important than others. This routine + * determines the logging level at which to log a send error (often + * "No route to host"). This is important when there is asymmetric + * reachability and someone is trying to, i.e., mrinfo me periodically. + */ +static int +igmp_log_level(type, code) + u_int type, code; +{ + switch (type) { + case IGMP_MTRACE_RESP: + return LOG_INFO; + case IGMP_DVMRP: + switch (code) { + case DVMRP_NEIGHBORS: + case DVMRP_NEIGHBORS2: + return LOG_INFO; + } + } + return LOG_WARNING; +} + /* * Construct an IGMP message in the output packet buffer. The caller may * have already placed data in that buffer, of length 'datalen'. Then send * the message from the interface with IP address 'src' to destination 'dst'. */ -void send_igmp(src, dst, type, code, group, datalen) - u_long src, dst; +void +send_igmp(src, dst, type, code, group, datalen) + u_int32 src, dst; int type, code; - u_long group; + u_int32 group; int datalen; { - static struct sockaddr_in sdst = {AF_INET, sizeof sdst}; + static struct sockaddr_in sdst; struct ip *ip; struct igmp *igmp; ip = (struct ip *)send_buf; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); igmp->igmp_type = type; igmp->igmp_code = code; igmp->igmp_group.s_addr = group; igmp->igmp_cksum = 0; igmp->igmp_cksum = inet_cksum((u_short *)igmp, IGMP_MINLEN + datalen); if (IN_MULTICAST(ntohl(dst))) k_set_if(src); if (dst == allhosts_group) k_set_loop(TRUE); + bzero(&sdst, sizeof(sdst)); + sdst.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) + sdst.sin_len = sizeof(sdst); +#endif sdst.sin_addr.s_addr = dst; if (sendto(igmp_socket, send_buf, ip->ip_len, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) check_vif_state(); else - log(LOG_WARNING, errno, + log(igmp_log_level(type, code), errno, "sendto to %s on %s", inet_fmt(dst, s1), inet_fmt(src, s2)); } if (dst == allhosts_group) k_set_loop(FALSE); log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); } Index: stable/2.1/usr.sbin/mrouted/inet.c =================================================================== --- stable/2.1/usr.sbin/mrouted/inet.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/inet.c (revision 10585) @@ -1,187 +1,209 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: inet.c,v 1.4 1993/05/30 01:36:38 deering Exp $ + * $Id: inet.c,v 1.4 1995/06/28 17:58:33 wollman Exp $ */ #include "defs.h" /* * Exported variables. */ -char s1[16]; /* buffers to hold the string representations */ -char s2[16]; /* of IP addresses, to be passed to inet_fmt() */ -char s3[16]; /* or inet_fmts(). */ +char s1[19]; /* buffers to hold the string representations */ +char s2[19]; /* of IP addresses, to be passed to inet_fmt() */ +char s3[19]; /* or inet_fmts(). */ +char s4[19]; /* * Verify that a given IP address is credible as a host address. * (Without a mask, cannot detect addresses of the form {subnet,0} or * {subnet,-1}.) */ -int inet_valid_host(naddr) - u_long naddr; +int +inet_valid_host(naddr) + u_int32 naddr; { - register u_long addr; + register u_int32 addr; addr = ntohl(naddr); return (!(IN_MULTICAST(addr) || IN_BADCLASS (addr) || (addr & 0xff000000) == 0)); } /* * Verify that a given subnet number and mask pair are credible. + * + * With CIDR, almost any subnet and mask are credible. mrouted still + * can't handle aggregated class A's, so we still check that, but + * otherwise the only requirements are that the subnet address is + * within the [ABC] range and that the host bits of the subnet + * are all 0. */ -int inet_valid_subnet(nsubnet, nmask) - u_long nsubnet, nmask; +int +inet_valid_subnet(nsubnet, nmask) + u_int32 nsubnet, nmask; { - register u_long subnet, mask; + register u_int32 subnet, mask; subnet = ntohl(nsubnet); mask = ntohl(nmask); if ((subnet & mask) != subnet) return (FALSE); + if (subnet == 0 && mask == 0) + return (TRUE); + if (IN_CLASSA(subnet)) { if (mask < 0xff000000 || - (subnet & 0xff000000) == 0 || (subnet & 0xff000000) == 0x7f000000) return (FALSE); } - else if (IN_CLASSB(subnet)) { - if (mask < 0xffff0000) return (FALSE); + else if (IN_CLASSD(subnet) || IN_BADCLASS(subnet)) { + /* Above Class C address space */ + return (FALSE); } - else if (IN_CLASSC(subnet)) { - if (mask < 0xffffff00) return (FALSE); + else if (subnet & ~mask) { + /* Host bits are set in the subnet */ + return (FALSE); } - else return (FALSE); return (TRUE); } /* * Convert an IP address in u_long (network) format into a printable string. */ -char *inet_fmt(addr, s) - u_long addr; +char * +inet_fmt(addr, s) + u_int32 addr; char *s; { register u_char *a; a = (u_char *)&addr; sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); return (s); } /* * Convert an IP subnet number in u_long (network) format into a printable - * string. + * string including the netmask as a number of bits. */ -char *inet_fmts(addr, mask, s) - u_long addr, mask; +char * +inet_fmts(addr, mask, s) + u_int32 addr, mask; char *s; { register u_char *a, *m; + int bits; + if ((addr == 0) && (mask == 0)) { + sprintf(s, "default"); + return (s); + } a = (u_char *)&addr; m = (u_char *)&mask; + bits = 33 - ffs(ntohl(mask)); - if (m[3] != 0) sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); - else if (m[2] != 0) sprintf(s, "%u.%u.%u", a[0], a[1], a[2]); - else if (m[1] != 0) sprintf(s, "%u.%u", a[0], a[1]); - else sprintf(s, "%u", a[0]); + if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3], + bits); + else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits); + else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits); + else sprintf(s, "%u/%d", a[0], bits); return (s); } - /* * Convert the printable string representation of an IP address into the * u_long (network) format. Return 0xffffffff on error. (To detect the * legal address with that value, you must explicitly compare the string * with "255.255.255.255".) */ -u_long inet_parse(s) +u_int32 +inet_parse(s) char *s; { - u_long a; + u_int32 a = 0; u_int a0, a1, a2, a3; char c; if (sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c) != 4 || a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255) return (0xffffffff); ((u_char *)&a)[0] = a0; ((u_char *)&a)[1] = a1; ((u_char *)&a)[2] = a2; ((u_char *)&a)[3] = a3; return (a); } /* * inet_cksum extracted from: * P I N G . C * * Author - * Mike Muuss * U. S. Army Ballistic Research Laboratory * December, 1983 * Modified at Uc Berkeley * * (ping.c) Status - * Public Domain. Distribution Unlimited. * * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ -int inet_cksum(addr, len) +int +inet_cksum(addr, len) u_short *addr; u_int len; { register int nleft = (int)len; register u_short *w = addr; u_short answer = 0; register int sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ - while( nleft > 1 ) { + while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ - if( nleft == 1 ) { + if (nleft == 1) { *(u_char *) (&answer) = *(u_char *)w ; sum += answer; } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } Index: stable/2.1/usr.sbin/mrouted/kern.c =================================================================== --- stable/2.1/usr.sbin/mrouted/kern.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/kern.c (revision 10585) @@ -1,183 +1,223 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: kern.c,v 1.2 1994/09/08 02:51:17 wollman Exp $ + * $Id: kern.c,v 1.5 1995/06/28 17:58:34 wollman Exp $ */ #include "defs.h" void k_set_rcvbuf(bufsize) int bufsize; { if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize); } void k_hdr_include(bool) int bool; { #ifdef IP_HDRINCL if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL, (char *)&bool, sizeof(bool)) < 0) log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool); #endif } void k_set_ttl(t) int t; { u_char ttl; ttl = t; if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) < 0) log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl); } void k_set_loop(l) int l; { u_char loop; loop = l; if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop)) < 0) log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop); } void k_set_if(ifa) - u_long ifa; + u_int32 ifa; { struct in_addr adr; adr.s_addr = ifa; if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&adr, sizeof(adr)) < 0) log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s", inet_fmt(ifa, s1)); } void k_join(grp, ifa) - u_long grp; - u_long ifa; + u_int32 grp; + u_int32 ifa; { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = grp; mreq.imr_interface.s_addr = ifa; if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) log(LOG_WARNING, errno, "can't join group %s on interface %s", inet_fmt(grp, s1), inet_fmt(ifa, s2)); } void k_leave(grp, ifa) - u_long grp; - u_long ifa; + u_int32 grp; + u_int32 ifa; { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = grp; mreq.imr_interface.s_addr = ifa; if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) log(LOG_WARNING, errno, "can't leave group %s on interface %s", inet_fmt(grp, s1), inet_fmt(ifa, s2)); } void k_init_dvmrp() { - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_INIT, +#ifdef OLD_KERNEL + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, (char *)NULL, 0) < 0) - log(LOG_ERR, errno, "can't enable DVMRP routing in kernel"); +#else + int v=1; + + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT, + (char *)&v, sizeof(int)) < 0) +#endif + log(LOG_ERR, errno, "can't enable Multicast routing in kernel"); } void k_stop_dvmrp() { - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE, + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DONE, (char *)NULL, 0) < 0) - log(LOG_WARNING, errno, "can't disable DVMRP routing in kernel"); + log(LOG_WARNING, errno, "can't disable Multicast routing in kernel"); } void k_add_vif(vifi, v) vifi_t vifi; struct uvif *v; { struct vifctl vc; vc.vifc_vifi = vifi; vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS; vc.vifc_threshold = v->uv_threshold; vc.vifc_rate_limit = v->uv_rate_limit; vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr; vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr; - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_VIF, + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF, (char *)&vc, sizeof(vc)) < 0) - log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF"); + log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF"); } void k_del_vif(vifi) vifi_t vifi; { - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF, + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF, (char *)&vifi, sizeof(vifi)) < 0) - log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF"); + log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF"); } /* * Adds a (source, mcastgrp) entry to the kernel */ -void k_add_rg(kt) - struct ktable *kt; +void k_add_rg(origin, g) + u_int32 origin; + struct gtable *g; { struct mfcctl mc; + int i; /* copy table values so that setsockopt can process it */ - COPY_TABLES(kt, mc); + mc.mfcc_origin.s_addr = origin; +#ifdef OLD_KERNEL + mc.mfcc_originmask.s_addr = 0xffffffff; +#endif + mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp; + mc.mfcc_parent = g->gt_route ? g->gt_route->rt_parent : NO_VIF; + for (i = 0; i < numvifs; i++) + mc.mfcc_ttls[i] = g->gt_ttls[i]; /* write to kernel space */ - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MFC, + if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_MFC, (char *)&mc, sizeof(mc)) < 0) - log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MFC"); + log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC"); } /* * Deletes a (source, mcastgrp) entry from the kernel */ -void k_del_rg(kt) - struct ktable *kt; +int k_del_rg(origin, g) + u_int32 origin; + struct gtable *g; { struct mfcctl mc; + int retval; /* copy table values so that setsockopt can process it */ - COPY_TABLES(kt, mc); + mc.mfcc_origin.s_addr = origin; +#ifdef OLD_KERNEL + mc.mfcc_originmask.s_addr = 0xffffffff; +#endif + mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp; /* write to kernel space */ - if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_MFC, - (char *)&mc, sizeof(mc)) < 0) - log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_MFC"); + if ((retval = setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_MFC, + (char *)&mc, sizeof(mc))) < 0) + log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC"); + + return retval; +} + +/* + * Get the kernel's idea of what version of mrouted needs to run with it. + */ +int k_get_version() +{ + int vers; + int len = sizeof(vers); + + if (getsockopt(igmp_socket, IPPROTO_IP, MRT_VERSION, + (char *)&vers, &len) < 0) + log(LOG_ERR, errno, + "getsockopt MRT_VERSION: perhaps your kernel is too old"); + + return vers; } Index: stable/2.1/usr.sbin/mrouted/main.c =================================================================== --- stable/2.1/usr.sbin/mrouted/main.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/main.c (revision 10585) @@ -1,439 +1,644 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: main.c,v 1.3 1995/03/31 21:16:43 wollman Exp $ + * $Id: main.c,v 1.6 1995/06/28 17:58:35 wollman Exp $ */ /* * Written by Steve Deering, Stanford University, February 1989. * * (An earlier version of DVMRP was implemented by David Waitzman of * BBN STC by extending Berkeley's routed program. Some of Waitzman's * extensions have been incorporated into mrouted, but none of the * original routed code has been adopted.) */ #include "defs.h" +#ifdef __STDC__ +#include +#else +#include +#endif +#include +#ifdef SNMP +#include "snmp.h" +#endif + extern char *configfilename; -static char pidfilename[] = "/var/run/mrouted.pid"; -static char dumpfilename[] = "/var/tmp/mrouted.dump"; -static char cachefilename[] = "/var/tmp/mrouted.cache"; -static char genidfilename[] = "/var/tmp/mrouted.genid"; +static char pidfilename[] = _PATH_MROUTED_PID; +static char dumpfilename[] = _PATH_MROUTED_DUMP; +static char cachefilename[] = _PATH_MROUTED_CACHE; +static char genidfilename[] = _PATH_MROUTED_GENID; int cache_lifetime = DEFAULT_CACHE_LIFETIME; int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2; int debug = 0; u_char pruning = 1; /* Enable pruning by default */ +#define NHANDLERS 2 +static struct ihandler { + int fd; /* File descriptor */ + ihfunc_t func; /* Function to call with &fd_set */ +} ihandlers[NHANDLERS]; +static int nhandlers = 0; + /* * Forward declarations. */ -static void fasttimer(); -static void timer(); -static void hup(); -static void dump(); -static void fdump(); -static void cdump(); -static void restart(); +static void fasttimer __P((int)); +static void done __P((int)); +static void dump __P((int)); +static void fdump __P((int)); +static void cdump __P((int)); +static void restart __P((int)); +static void timer __P((void)); +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[]; { register int recvlen; register int omask; int dummy; FILE *fp; - extern uid_t geteuid(); struct timeval tv; - struct timezone tzp; - u_long prev_genid; + u_int32 prev_genid; + int vers; + fd_set rfds, readers; + int nfds, n, i; +#ifdef SNMP + char *myname; + fd_set wfds; + + if (myname = strrchr(argv[0], '/')) + myname++; + if (myname == NULL || *myname == 0) + myname = argv[0]; + isodetailor (myname, 0); +#endif + +#ifdef SYSV + setvbuf(stderr, NULL, _IOLBF, 0); +#else setlinebuf(stderr); +#endif if (geteuid() != 0) { fprintf(stderr, "must be root\n"); exit(1); } argv++, argc--; while (argc > 0 && *argv[0] == '-') { if (strcmp(*argv, "-d") == 0) { if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++, argc--; debug = atoi(*argv); } else debug = DEFAULT_DEBUG; } else if (strcmp(*argv, "-c") == 0) { if (argc > 1) { argv++, argc--; configfilename = *argv; } else goto usage; } else if (strcmp(*argv, "-p") == 0) { pruning = 0; } else goto usage; argv++, argc--; } if (argc > 0) { -usage: fprintf(stderr, +usage: fprintf(stderr, "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n"); exit(1); } if (debug == 0) { /* * Detach from the terminal */ int t; 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 TIOCNOTTY t = open("/dev/tty", 2); if (t >= 0) { (void)ioctl(t, TIOCNOTTY, (char *)0); (void)close(t); } +#else + if (setsid() < 0) + perror("setsid"); +#endif } else fprintf(stderr, "debug level %u\n", debug); #ifdef LOG_DAEMON (void)openlog("mrouted", LOG_PID, LOG_DAEMON); (void)setlogmask(LOG_UPTO(LOG_NOTICE)); #else (void)openlog("mrouted", LOG_PID); #endif log(LOG_NOTICE, 0, "mrouted version %d.%d", PROTOCOL_VERSION, MROUTED_VERSION); +#ifdef SYSV + srand48(time(NULL)); +#else srandom(gethostid()); +#endif /* - * Get generation id + * Get generation id */ - gettimeofday(&tv, &tzp); + gettimeofday(&tv, 0); dvmrp_genid = tv.tv_sec; fp = fopen(genidfilename, "r"); if (fp != NULL) { fscanf(fp, "%d", &prev_genid); if (prev_genid == dvmrp_genid) dvmrp_genid++; (void) fclose(fp); } fp = fopen(genidfilename, "w"); if (fp != NULL) { fprintf(fp, "%d", dvmrp_genid); (void) fclose(fp); } callout_init(); + +#ifdef SNMP + snmp_init(); +#endif + init_igmp(); k_init_dvmrp(); /* enable DVMRP routing in kernel */ + +#ifndef OLD_KERNEL + vers = k_get_version(); + /*XXX + * This function must change whenever the kernel version changes + */ + if ((((vers >> 8) & 0xff) != 3) || + ((vers & 0xff) != 5)) + log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch", + (vers >> 8) & 0xff, vers & 0xff, + PROTOCOL_VERSION, MROUTED_VERSION); +#endif + init_routes(); init_ktable(); init_vifs(); +#ifdef RSRR + rsrr_init(); +#endif /* RSRR */ +#if defined(__STDC__) || defined(__GNUC__) + /* Allow cleanup if unexpected exit. Apparently some architectures + * have a kernel bug where closing the socket doesn't do an + * ip_mrouter_done(), so we attempt to do it on exit. + */ + atexit(cleanup); +#endif + if (debug) fprintf(stderr, "pruning %s\n", pruning ? "on" : "off"); - fp = fopen(pidfilename, "w"); + fp = fopen(pidfilename, "w"); if (fp != NULL) { - fprintf(fp, "%d\n", getpid()); + fprintf(fp, "%d\n", (int)getpid()); (void) fclose(fp); } - if (debug >= 2) dump(); + if (debug >= 2) dump(0); (void)signal(SIGALRM, fasttimer); (void)signal(SIGHUP, restart); - (void)signal(SIGTERM, hup); - (void)signal(SIGINT, hup); + (void)signal(SIGTERM, done); + (void)signal(SIGINT, done); (void)signal(SIGUSR1, fdump); (void)signal(SIGUSR2, cdump); if (debug != 0) (void)signal(SIGQUIT, dump); + 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; + } + (void)alarm(1); /* schedule first timer interrupt */ /* * Main receive loop. */ dummy = 0; for(;;) { - recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), - 0, NULL, &dummy); - if (recvlen < 0) { - if (errno != EINTR) log(LOG_ERR, errno, "recvfrom"); - continue; + bcopy((char *)&readers, (char *)&rfds, sizeof(rfds)); +#ifdef SNMP + FD_ZERO(&wfds); + + if (smux_fd != NOTOK) { + if (rock_and_roll) + FD_SET(smux_fd, &rfds); + else + FD_SET(smux_fd, &wfds); + if (smux_fd >= nfds) + nfds = smux_fd + 1; + } + + if ((n = xselect(nfds, &rfds, &wfds, NULLFD, NOTOK))==NOTOK) { +#else + if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0) { +#endif + if (errno != EINTR) /* SIGALRM is expected */ + log(LOG_WARNING, errno, "select failed"); + continue; + } + + if (FD_ISSET(igmp_socket, &rfds)) { + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, + 0, NULL, &dummy); + if (recvlen < 0) { + if (errno != EINTR) log(LOG_ERR, errno, "recvfrom"); + continue; + } + omask = sigblock(sigmask(SIGALRM)); + accept_igmp(recvlen); + (void)sigsetmask(omask); + } + + for (i = 0; i < nhandlers; i++) { + if (FD_ISSET(ihandlers[i].fd, &rfds)) { + (*ihandlers[i].func)(&rfds); + } } - omask = sigblock(sigmask(SIGALRM)); - accept_igmp(recvlen); - (void)sigsetmask(omask); + +#ifdef SNMP + if (smux_fd != NOTOK) { + if (rock_and_roll) { + if (FD_ISSET(smux_fd, &rfds)) + doit_smux(); + } else if (FD_ISSET(smux_fd, &wfds)) + start_smux(); + } +#endif } } /* - * routine invoked every second. It's main goal is to cycle through + * routine invoked every second. Its main goal is to cycle through * the routing table and send partial updates to all neighbors at a * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to * do all the other time-based processing. */ -static void fasttimer() +static void +fasttimer(i) + int i; { static unsigned int tlast; static unsigned int nsent; register unsigned int t = tlast + 1; register int n; /* * if we're in the last second, send everything that's left. * otherwise send at least the fraction we should have sent by now. */ if (t >= ROUTE_REPORT_INTERVAL) { register int nleft = nroutes - nsent; while (nleft > 0) { if ((n = report_next_chunk()) <= 0) break; nleft -= n; } tlast = 0; nsent = 0; } else { register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL; while (nsent < ncum) { if ((n = report_next_chunk()) <= 0) break; nsent += n; } tlast = t; } if ((t % TIMER_INTERVAL) == 0) timer(); age_callout_queue();/* Advance the timer for the callout queue - for groups */ + for groups */ alarm(1); } /* * 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. */ static u_long virtual_time = 0; /* * Timer routine. Performs periodic neighbor probing, route reporting, and * group querying duties, and drives various timers in routing entries and * virtual interface data structures. */ -static void timer() +static void +timer() { age_routes(); /* Advance the timers in the route entries */ age_vifs(); /* Advance the timers for neighbors */ age_table_entry(); /* Advance the timers for the cache entries */ if (virtual_time % GROUP_QUERY_INTERVAL == 0) { /* * Time to query the local group memberships on all subnets * for which this router is the elected querier. */ query_groups(); } if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) { /* * Time to send a probe on all vifs from which no neighbors have * been heard. Also, check if any inoperative interfaces have now * come up. (If they have, they will also be probed as part of * their initialization.) */ probe_for_neighbors(); if (vifs_down) check_vif_state(); } delay_change_reports = FALSE; if (routes_changed) { /* * Some routes have changed since the last timer interrupt, but * have not been reported yet. Report the changed routes to all * neighbors. */ report_to_all_neighbors(CHANGED_ROUTES); } +#ifdef SNMP + if (smux_fd == NOTOK && !dont_bother_anymore + && virtual_time % SNMPD_RETRY_INTERVAL == 0) { + /* + * Time to check for snmpd running. + */ + try_smux_init(); + } +#endif + /* * Advance virtual time */ virtual_time += TIMER_INTERVAL; } /* - * On hangup signal, let everyone know we're going away. + * On termination, let everyone know we're going away. */ -static void hup() +static void +done(i) + int i; { - log(LOG_INFO, 0, "hup"); - expire_all_routes(); - report_to_all_neighbors(ALL_ROUTES); - exit(1); + log(LOG_NOTICE, 0, "mrouted version %d.%d exiting", + PROTOCOL_VERSION, MROUTED_VERSION); + cleanup(); + _exit(1); } +static void +cleanup() +{ + static in_cleanup = 0; + if (!in_cleanup) { + in_cleanup++; +#ifdef RSRR + rsrr_clean(); +#endif /* RSRR */ + expire_all_routes(); + report_to_all_neighbors(ALL_ROUTES); + k_stop_dvmrp(); + } +} + + /* * Dump internal data structures to stderr. */ -static void dump() +static void +dump(i) + int i; { dump_vifs(stderr); dump_routes(stderr); } /* * Dump internal data structures to a file. */ -static void fdump() +static void +fdump(i) + int i; { FILE *fp; fp = fopen(dumpfilename, "w"); if (fp != NULL) { dump_vifs(fp); dump_routes(fp); (void) fclose(fp); } } /* * Dump local cache contents to a file. */ -static void cdump() +static void +cdump(i) + int i; { FILE *fp; fp = fopen(cachefilename, "w"); if (fp != NULL) { - dump_cache(fp); + dump_cache(fp); (void) fclose(fp); } } /* * Restart mrouted */ -static void restart() +static void +restart(i) + int i; { register int omask; - log(LOG_INFO, 0, "restart"); + log(LOG_NOTICE, 0, "mrouted version %d.%d restart", + PROTOCOL_VERSION, MROUTED_VERSION); /* * reset all the entries */ omask = sigblock(sigmask(SIGALRM)); free_all_prunes(); free_all_routes(); stop_all_vifs(); k_stop_dvmrp(); + close(igmp_socket); + close(udp_socket); /* * start processing again */ dvmrp_genid++; pruning = 1; init_igmp(); k_init_dvmrp(); /* enable DVMRP routing in kernel */ init_routes(); init_ktable(); init_vifs(); (void)sigsetmask(omask); } /* * Log errors and other messages to the system log daemon and to stderr, * according to the severity of the message and the current debug level. * For errors of severity LOG_ERR or worse, terminate the program. */ -void log(severity, syserr, format, a, b, c, d, e) +#ifdef __STDC__ +void +log(int severity, int syserr, char *format, ...) +{ + va_list ap; + static char fmt[211] = "warning - "; + char *msg; + char tbuf[20]; + struct timeval now; + struct tm *thyme; + + va_start(ap, format); +#else +/*VARARGS3*/ +void +log(severity, syserr, format, va_alist) int severity, syserr; char *format; - int a, b, c, d, e; + va_dcl { - char fmt[100]; + va_list ap; + static char fmt[211] = "warning - "; + char *msg; + char tbuf[20]; + struct timeval now; + struct tm *thyme; + va_start(ap); +#endif + vsprintf(&fmt[10], format, ap); + va_end(ap); + msg = (severity == LOG_WARNING) ? fmt : &fmt[10]; + switch (debug) { case 0: break; case 1: if (severity > LOG_NOTICE) break; case 2: if (severity > LOG_INFO ) break; default: - fmt[0] = '\0'; - if (severity == LOG_WARNING) strcat(fmt, "warning - "); - strncat(fmt, format, 80); - fprintf(stderr, fmt, a, b, c, d, e); + gettimeofday(&now,NULL); + thyme = localtime(&now.tv_sec); + strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme); + fprintf(stderr, tbuf, now.tv_usec / 1000); + fprintf(stderr, "%s", msg); if (syserr == 0) fprintf(stderr, "\n"); - else if(syserr < sys_nerr) + else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); } if (severity <= LOG_NOTICE) { - fmt[0] = '\0'; - if (severity == LOG_WARNING) strcat(fmt, "warning - "); - strncat(fmt, format, 80); if (syserr != 0) { - strcat(fmt, ": %m"); - errno = syserr; - } - syslog(severity, fmt, a, b, c, d, e); + errno = syserr; + syslog(severity, "%s: %m", msg); + } else + syslog(severity, "%s", msg); if (severity <= LOG_ERR) exit(-1); } } Index: stable/2.1/usr.sbin/mrouted/map-mbone.8 =================================================================== --- stable/2.1/usr.sbin/mrouted/map-mbone.8 (revision 10584) +++ stable/2.1/usr.sbin/mrouted/map-mbone.8 (revision 10585) @@ -1,97 +1,89 @@ -.Dd March 31, 1995 -.Dt MAP-MBONE 8 -.Os FreeBSD 2.0 -.Sh NAME -.Nm map-mbone -.Nd multicast connection mapper -.Sh SYNOPSIS -.Nm map-mbone -.Op Fl d Ar debuglevel -.Op Fl f -.Op Fl g -.Op Fl n -.Op Fl r Ar retries -.Op Fl t Ar timeout -.Op Ar router -.Sh DESCRIPTION -.Nm map-mbone +.TH MAP-MBONE 8 +.UC 5 +.SH NAME +map-mbone \- Multicast connection mapper +.SH SYNOPSIS +.B /usr/sbin/map-mbone +[ +.B \-d +.I debug_level +] [ +.B \-f +] [ +.B \-g +] [ +.B \-n +] [ +.B \-r +.I retry_count +] [ +.B \-t +.I timeout_count +] [ +.B starting_router +] +.SH DESCRIPTION +.I map-mbone attempts to display all multicast routers that are reachable from the multicast -router -.Ar router . -If not specified on the command line, the default -.Ar router -is the local host. -.Nm -traverses neighboring multicast routers by sending the -.Dv ASK_NEIGHBORS -.Tn IGMP -message to each router. If this multicast router responds, the version -number and a list of their neighboring multicast router addresses is -part of that response. If the responding router has recent multicast -version number, then -.Nm +.I starting_router. +If not specified on the command line, the default multicast +.I starting_router +is the localhost. +.PP +.I map-mbone +traverses neighboring multicast routers by sending the ASK_NEIGHBORS IGMP +message to the multicast starting_router. If this multicast router responds, +the version number and a list of their neighboring multicast router addresses is +part of that response. If the responding router has recent multicast version +number, then +.I map-mbone requests additional information such as metrics, thresholds, and flags from the multicast router. For each new occurrence of neighboring multicast router in the reply and provided the flooding option has been selected, then -.Nm +.I map-mbone asks each of this multicast router for a list of neighbors. This search for unique routers will continue until no new neighboring multicast routers are reported. -.Pp -The options supported by -.Nm -are as follows: -.Bl -tag -width XXXdebuglevel -.It Fl d Ar debuglevel -This sets the debug level to -.Ar debuglevel . -When the debug level is greater than the default value of 0, -additional debugging messages are printed. Regardless of the debug -level, an error condition, will always write an error message and will -cause +.br +.ne 5 +.SH INVOCATION +.PP +"\-d" option sets the debug level. When the debug level is greater than the +default value of 0, addition debugging messages are printed. Regardless of +the debug level, an error condition, will always write an error message and will +cause .I map-mbone to terminate. Non-zero debug levels have the following effects: -.Bl -tag -width "level 3" -.It level 1 +.IP "level 1" packet warnings are printed to stderr. -.It level 2 +.IP "level 2" all level 1 messages plus notifications down networks are printed to stderr. -.It level 3 +.IP "level 3" all level 2 messages plus notifications of all packet timeouts are printed to stderr. -.El -.It Fl f -This option enables flooding. Flooding allows -.Nm -to perform recursive search -of neighboring multicast routers and is enabled by default when an -initial -.Ar router -is not specified. -.It Fl g -This option enables graphing in GraphEd format. -.It Fl n -This option disables the DNS lookup for the multicast routers' names. -.It Fl r Ar retries -This options sets the neighbor query retry limit to -.Ar retries . -The default is one retry. -.It Fl t Ar timeout -This option sets the number of seconds to wait for a neighbor query -reply before retrying. The default timeout is two seconds. -.Sh RESTRICTIONS -.Nm -must be run as `root'. -.Sh SEE ALSO -.Xr mrinfo 8 , -.Xr mrouted 8 , -.Xr mtrace 8 -.Sh AUTHOR +.PP +"\-f" option sets flooding option. Flooding allows the recursive search +of neighboring multicast routers and is enable by default when starting_router +is not used. +.PP +"\-g" option sets graphing in GraphEd format. +.PP +"\-n" option disables the DNS lookup for the multicast routers names. +.PP +"\-r retry_count" sets the neighbor query retry limit. Default is 1 retry. +.PP +"\-t timeout_count" sets the number of seconds to wait for a neighbor query +reply before retrying. Default timeout is 2 seconds. +.PP +.SH IMPORTANT NOTE +.I map-mbone +must be run as root. +.PP +.SH SEE ALSO +.BR mrouted (8) , +.BR mrinfo (8) , +.BR mtrace (8) +.PP +.SH AUTHOR Pavel Curtis -.Sh HISTORY -A -.Nm -command first appeared in -.Tn FreeBSD -2.0. Index: stable/2.1/usr.sbin/mrouted/mapper.c =================================================================== --- stable/2.1/usr.sbin/mrouted/mapper.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mapper.c (revision 10585) @@ -1,954 +1,1019 @@ /* Mapper for connections between MRouteD multicast routers. * Written by Pavel Curtis * - * $Id: mapper.c,v 1.2 1994/09/08 02:51:19 wollman Exp $ + * $Id: mapper.c,v 1.5 1995/06/28 17:58:35 wollman Exp $ */ /* * Copyright (c) Xerox Corporation 1992. All rights reserved. - * + * * License is granted to copy, to use, and to make and to use derivative * works for research and evaluation purposes, provided that Xerox is * acknowledged in all documentation pertaining to any such copy or derivative * work. Xerox grants no other licenses expressed or implied. The Xerox trade * name should not be used in any advertising without its written permission. - * + * * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE * FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without * express or implied warranty of any kind. - * + * * These notices must be retained in any copies of any part of this software. */ +#include #include #include #include "defs.h" +#include +#ifdef __STDC__ +#include +#else +#include +#endif #define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */ #define DEFAULT_RETRIES 1 /* How many times to ask each router */ /* All IP addresses are stored in the data structure in NET order. */ typedef struct neighbor { struct neighbor *next; - u_long addr; /* IP address in NET order */ + u_int32 addr; /* IP address in NET order */ u_char metric; /* TTL cost of forwarding */ u_char threshold; /* TTL threshold to forward */ u_short flags; /* flags on connection */ #define NF_PRESENT 0x8000 /* True if flags are meaningful */ } Neighbor; typedef struct interface { struct interface *next; - u_long addr; /* IP address of the interface in NET order */ + u_int32 addr; /* IP address of the interface in NET order */ Neighbor *neighbors; /* List of neighbors' IP addresses */ } Interface; typedef struct node { - u_long addr; /* IP address of this entry in NET order */ - u_long version; /* which mrouted version is running */ + u_int32 addr; /* IP address of this entry in NET order */ + u_int32 version; /* which mrouted version is running */ int tries; /* How many requests sent? -1 for aliases */ union { struct node *alias; /* If alias, to what? */ struct interface *interfaces; /* Else, neighbor data */ } u; struct node *left, *right; } Node; Node *routers = 0; -u_long our_addr, target_addr = 0; /* in NET order */ +u_int32 our_addr, target_addr = 0; /* in NET order */ int debug = 0; int retries = DEFAULT_RETRIES; int timeout = DEFAULT_TIMEOUT; int show_names = TRUE; vifi_t numvifs; /* to keep loader happy */ /* (see COPY_TABLES macro called in kern.c) */ +Node * find_node __P((u_int32 addr, Node **ptr)); +Interface * find_interface __P((u_int32 addr, Node *node)); +Neighbor * find_neighbor __P((u_int32 addr, Node *node)); +int main __P((int argc, char *argv[])); +void ask __P((u_int32 dst)); +void ask2 __P((u_int32 dst)); +int retry_requests __P((Node *node)); +char * inet_name __P((u_int32 addr)); +void print_map __P((Node *node)); +char * graph_name __P((u_int32 addr, char *buf)); +void graph_edges __P((Node *node)); +void elide_aliases __P((Node *node)); +void graph_map __P((void)); +int get_number __P((int *var, int deflt, char ***pargv, + int *pargc)); +u_int32 host_addr __P((char *name)); + Node *find_node(addr, ptr) - u_long addr; + u_int32 addr; Node **ptr; { Node *n = *ptr; if (!n) { *ptr = n = (Node *) malloc(sizeof(Node)); n->addr = addr; n->version = 0; n->tries = 0; n->u.interfaces = 0; n->left = n->right = 0; return n; } else if (addr == n->addr) return n; else if (addr < n->addr) return find_node(addr, &(n->left)); else return find_node(addr, &(n->right)); } Interface *find_interface(addr, node) - u_long addr; + u_int32 addr; Node *node; { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) if (ifc->addr == addr) return ifc; ifc = (Interface *) malloc(sizeof(Interface)); ifc->addr = addr; ifc->next = node->u.interfaces; node->u.interfaces = ifc; ifc->neighbors = 0; return ifc; } Neighbor *find_neighbor(addr, node) - u_long addr; + u_int32 addr; Node *node; { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; for (nb = ifc->neighbors; nb; nb = nb->next) if (nb->addr == addr) return nb; } return 0; } /* * Log errors and other messages to stderr, according to the severity of the * message and the current debug level. For errors of severity LOG_ERR or * worse, terminate the program. */ -void log(severity, syserr, format, a, b, c, d, e) - int severity, syserr; - char *format; - int a, b, c, d, e; +#ifdef __STDC__ +void +log(int severity, int syserr, char *format, ...) { - char fmt[100]; + va_list ap; + char fmt[100]; + va_start(ap, format); +#else +/*VARARGS3*/ +void +log(severity, syserr, format, va_alist) + int severity, syserr; + char *format; + va_dcl +{ + va_list ap; + char fmt[100]; + + va_start(ap); +#endif + switch (debug) { case 0: if (severity > LOG_WARNING) return; case 1: if (severity > LOG_NOTICE ) return; case 2: if (severity > LOG_INFO ) return; default: fmt[0] = '\0'; if (severity == LOG_WARNING) strcat(fmt, "warning - "); strncat(fmt, format, 80); - fprintf(stderr, fmt, a, b, c, d, e); + vfprintf(stderr, fmt, ap); if (syserr == 0) fprintf(stderr, "\n"); else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); } if (severity <= LOG_ERR) exit(-1); } /* * Send a neighbors-list request. */ void ask(dst) - u_long dst; + u_int32 dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, htonl(MROUTED_LEVEL), 0); } void ask2(dst) - u_long dst; + u_int32 dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, htonl(MROUTED_LEVEL), 0); } /* * Process an incoming group membership report. */ -void accept_group_report(src, dst, group) - u_long src, dst, group; +void accept_group_report(src, dst, group, r_type) + u_int32 src, dst, group; + int r_type; { log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor probe message. */ -void accept_probe(src, dst) - u_long src, dst; +void accept_probe(src, dst, p, datalen, level) + u_int32 src, dst, level; + char *p; + int datalen; { log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming route report message. */ -void accept_report(src, dst, p, datalen) - u_long src, dst; +void accept_report(src, dst, p, datalen, level) + u_int32 src, dst, level; char *p; int datalen; { log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list request message. */ void accept_neighbor_request(src, dst) - u_long src, dst; + u_int32 src, dst; { if (src != our_addr) log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor request from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } void accept_neighbor_request2(src, dst) - u_long src, dst; + u_int32 src, dst; { if (src != our_addr) log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor request2 from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list message. */ void accept_neighbors(src, dst, p, datalen, level) - u_long src, dst, level; + u_int32 src, dst, level; u_char *p; int datalen; { Node *node = find_node(src, &routers); if (node->tries == 0) /* Never heard of 'em; must have hit them at */ node->tries = 1; /* least once, though...*/ else if (node->tries == -1) /* follow alias link */ node = node->u.alias; -#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\ - a += ((u_long)*p++ << 8), a += *p++) +#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\ + a += ((u_int32)*p++ << 8), a += *p++) /* if node is running a recent mrouted, ask for additional info */ if (level != 0) { - node->version = ntohl(level); + node->version = level; node->tries = 0; ask2(src); return; } if (debug > 3) { int i; fprintf(stderr, " datalen = %d\n", datalen); for (i = 0; i < datalen; i++) { if ((i & 0xF) == 0) fprintf(stderr, " "); fprintf(stderr, " %02x", p[i]); if ((i & 0xF) == 0xF) fprintf(stderr, "\n"); } if ((datalen & 0xF) != 0xF) fprintf(stderr, "\n"); } while (datalen > 0) { /* loop through interfaces */ - u_long ifc_addr; + u_int32 ifc_addr; u_char metric, threshold, ncount; Node *ifc_node; Interface *ifc; Neighbor *old_neighbors; if (datalen < 4 + 3) { log(LOG_WARNING, 0, "received truncated interface record from %s", inet_fmt(src, s1)); return; } GET_ADDR(ifc_addr); ifc_addr = htonl(ifc_addr); metric = *p++; threshold = *p++; ncount = *p++; datalen -= 4 + 3; /* Fix up any alias information */ ifc_node = find_node(ifc_addr, &routers); if (ifc_node->tries == 0) { /* new node */ ifc_node->tries = -1; ifc_node->u.alias = node; } else if (ifc_node != node && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { /* must merge two hosts' nodes */ Interface *ifc_i, *next_ifc_i; if (ifc_node->tries == -1) { Node *tmp = ifc_node->u.alias; ifc_node->u.alias = node; ifc_node = tmp; } /* Merge ifc_node (foo_i) into node (foo_n) */ if (ifc_node->tries > node->tries) node->tries = ifc_node->tries; for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { Neighbor *nb_i, *next_nb_i, *nb_n; Interface *ifc_n = find_interface(ifc_i->addr, node); old_neighbors = ifc_n->neighbors; for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { next_nb_i = nb_i->next; for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) if (nb_i->addr == nb_n->addr) { if (nb_i->metric != nb_n->metric || nb_i->threshold != nb_i->threshold) log(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb_i->addr, s1), inet_fmt(node->addr, s2)); free(nb_i); break; } if (!nb_n) { /* no match for this neighbor yet */ nb_i->next = ifc_n->neighbors; ifc_n->neighbors = nb_i; } } next_ifc_i = ifc_i->next; free(ifc_i); } ifc_node->tries = -1; ifc_node->u.alias = node; } - + ifc = find_interface(ifc_addr, node); old_neighbors = ifc->neighbors; - + /* Add the neighbors for this interface */ while (ncount--) { - u_long neighbor; + u_int32 neighbor; Neighbor *nb; Node *n_node; if (datalen < 4) { log(LOG_WARNING, 0, "received truncated neighbor list from %s", inet_fmt(src, s1)); return; } GET_ADDR(neighbor); neighbor = htonl(neighbor); datalen -= 4; for (nb = old_neighbors; nb; nb = nb->next) if (nb->addr == neighbor) { if (metric != nb->metric || threshold != nb->threshold) log(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); goto next_neighbor; } nb = (Neighbor *) malloc(sizeof(Neighbor)); nb->next = ifc->neighbors; ifc->neighbors = nb; nb->addr = neighbor; nb->metric = metric; nb->threshold = threshold; nb->flags = 0; n_node = find_node(neighbor, &routers); if (n_node->tries == 0 && !target_addr) { /* it's a new router */ ask(neighbor); n_node->tries = 1; } next_neighbor: ; } } } -void accept_neighbors2(src, dst, p, datalen) - u_long src, dst; +void accept_neighbors2(src, dst, p, datalen, level) + u_int32 src, dst, level; u_char *p; int datalen; { Node *node = find_node(src, &routers); if (node->tries == 0) /* Never heard of 'em; must have hit them at */ node->tries = 1; /* least once, though...*/ else if (node->tries == -1) /* follow alias link */ node = node->u.alias; while (datalen > 0) { /* loop through interfaces */ - u_long ifc_addr; + u_int32 ifc_addr; u_char metric, threshold, ncount, flags; Node *ifc_node; Interface *ifc; Neighbor *old_neighbors; if (datalen < 4 + 4) { log(LOG_WARNING, 0, "received truncated interface record from %s", inet_fmt(src, s1)); return; } - ifc_addr = *(u_long*)p; + ifc_addr = *(u_int32*)p; p += 4; metric = *p++; threshold = *p++; flags = *p++; ncount = *p++; datalen -= 4 + 4; /* Fix up any alias information */ ifc_node = find_node(ifc_addr, &routers); if (ifc_node->tries == 0) { /* new node */ ifc_node->tries = -1; ifc_node->u.alias = node; } else if (ifc_node != node && (ifc_node->tries > 0 || ifc_node->u.alias != node)) { /* must merge two hosts' nodes */ Interface *ifc_i, *next_ifc_i; if (ifc_node->tries == -1) { Node *tmp = ifc_node->u.alias; ifc_node->u.alias = node; ifc_node = tmp; } /* Merge ifc_node (foo_i) into node (foo_n) */ if (ifc_node->tries > node->tries) node->tries = ifc_node->tries; for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) { Neighbor *nb_i, *next_nb_i, *nb_n; Interface *ifc_n = find_interface(ifc_i->addr, node); old_neighbors = ifc_n->neighbors; for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) { next_nb_i = nb_i->next; for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next) if (nb_i->addr == nb_n->addr) { if (nb_i->metric != nb_n->metric || nb_i->threshold != nb_i->threshold) log(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb_i->addr, s1), inet_fmt(node->addr, s2)); free(nb_i); break; } if (!nb_n) { /* no match for this neighbor yet */ nb_i->next = ifc_n->neighbors; ifc_n->neighbors = nb_i; } } next_ifc_i = ifc_i->next; free(ifc_i); } ifc_node->tries = -1; ifc_node->u.alias = node; } - + ifc = find_interface(ifc_addr, node); old_neighbors = ifc->neighbors; - + /* Add the neighbors for this interface */ - while (ncount--) { - u_long neighbor; + while (ncount-- && datalen > 0) { + u_int32 neighbor; Neighbor *nb; Node *n_node; if (datalen < 4) { log(LOG_WARNING, 0, "received truncated neighbor list from %s", inet_fmt(src, s1)); return; } - neighbor = *(u_long*)p; + neighbor = *(u_int32*)p; p += 4; datalen -= 4; if (neighbor == 0) /* make leaf nets point to themselves */ neighbor = ifc_addr; for (nb = old_neighbors; nb; nb = nb->next) if (nb->addr == neighbor) { if (metric != nb->metric || threshold != nb->threshold) log(LOG_WARNING, 0, "inconsistent %s for neighbor %s of %s", "metric/threshold", inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2)); goto next_neighbor; } nb = (Neighbor *) malloc(sizeof(Neighbor)); nb->next = ifc->neighbors; ifc->neighbors = nb; nb->addr = neighbor; nb->metric = metric; nb->threshold = threshold; nb->flags = flags | NF_PRESENT; n_node = find_node(neighbor, &routers); if (n_node->tries == 0 && !target_addr) { /* it's a new router */ ask(neighbor); n_node->tries = 1; } next_neighbor: ; } } } void check_vif_state() { log(LOG_NOTICE, 0, "network marked down..."); } int retry_requests(node) Node *node; { int result; if (node) { result = retry_requests(node->left); if (node->tries > 0 && node->tries < retries) { if (node->version) ask2(node->addr); else ask(node->addr); node->tries++; result = 1; } return retry_requests(node->right) || result; } else return 0; } char *inet_name(addr) - u_long addr; + u_int32 addr; { struct hostent *e; e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); return e ? e->h_name : 0; } void print_map(node) Node *node; { if (node) { char *name, *addr; - + print_map(node->left); addr = inet_fmt(node->addr, s1); if (!target_addr || (node->tries >= 0 && node->u.interfaces) || (node->tries == -1 && node->u.alias->tries >= 0 && node->u.alias->u.interfaces)) { if (show_names && (name = inet_name(node->addr))) printf("%s (%s):", addr, name); else printf("%s:", addr); if (node->tries < 0) printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1)); else if (!node->u.interfaces) printf(" no response to query\n\n"); else { Interface *ifc; if (node->version) printf(" ", node->version & 0xff, (node->version >> 8) & 0xff); printf("\n"); for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; char *ifc_name = inet_fmt(ifc->addr, s1); int ifc_len = strlen(ifc_name); int count = 0; printf(" %s:", ifc_name); for (nb = ifc->neighbors; nb; nb = nb->next) { if (count > 0) printf("%*s", ifc_len + 5, ""); printf(" %s", inet_fmt(nb->addr, s1)); if (show_names && (name = inet_name(nb->addr))) printf(" (%s)", name); printf(" [%d/%d", nb->metric, nb->threshold); if (nb->flags) { u_short flags = nb->flags; if (flags & DVMRP_NF_TUNNEL) printf("/tunnel"); if (flags & DVMRP_NF_SRCRT) printf("/srcrt"); if (flags & DVMRP_NF_QUERIER) printf("/querier"); if (flags & DVMRP_NF_DISABLED) printf("/disabled"); if (flags & DVMRP_NF_DOWN) printf("/down"); } printf("]\n"); count++; } } printf("\n"); } } print_map(node->right); } } char *graph_name(addr, buf) - u_long addr; + u_int32 addr; char *buf; { char *name; if (show_names && (name = inet_name(addr))) strcpy(buf, name); else inet_fmt(addr, buf); return buf; } void graph_edges(node) Node *node; { Interface *ifc; Neighbor *nb; char name[100]; if (node) { graph_edges(node->left); if (node->tries >= 0) { printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n", (int) node->addr, node->addr & 0xFF, (node->addr >> 8) & 0xFF, graph_name(node->addr, name), node->u.interfaces ? "" : "*"); for (ifc = node->u.interfaces; ifc; ifc = ifc->next) for (nb = ifc->neighbors; nb; nb = nb->next) { Node *nb_node = find_node(nb->addr, &routers); Neighbor *nb2; if (nb_node->tries < 0) nb_node = nb_node->u.alias; if (node != nb_node && (!(nb2 = find_neighbor(node->addr, nb_node)) || node->addr < nb_node->addr)) { printf(" %d \"%d/%d", nb_node->addr, nb->metric, nb->threshold); if (nb2 && (nb2->metric != nb->metric || nb2->threshold != nb->threshold)) printf(",%d/%d", nb2->metric, nb2->threshold); if (nb->flags & NF_PRESENT) printf("%s%s", nb->flags & DVMRP_NF_SRCRT ? "" : nb->flags & DVMRP_NF_TUNNEL ? "E" : "P", nb->flags & DVMRP_NF_DOWN ? "D" : ""); printf("\"\n"); } } printf(" ;\n"); } graph_edges(node->right); } } void elide_aliases(node) Node *node; { if (node) { elide_aliases(node->left); if (node->tries >= 0) { Interface *ifc; for (ifc = node->u.interfaces; ifc; ifc = ifc->next) { Neighbor *nb; for (nb = ifc->neighbors; nb; nb = nb->next) { Node *nb_node = find_node(nb->addr, &routers); if (nb_node->tries < 0) nb->addr = nb_node->u.alias->addr; } } } elide_aliases(node->right); } } void graph_map() { - u_long now = time(0); + time_t now = time(0); char *nowstr = ctime(&now); nowstr[24] = '\0'; /* Kill the newline at the end */ elide_aliases(routers); printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n", nowstr); graph_edges(routers); printf("END\n"); } int get_number(var, deflt, pargv, pargc) int *var, *pargc, deflt; char ***pargv; { if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */ if (*pargc > 1 && isdigit((*pargv)[1][0])) { (*pargv)++, (*pargc)--; *var = atoi((*pargv)[0]); return 1; } else if (deflt >= 0) { *var = deflt; return 1; } else return 0; } else { /* Get value from the rest of this argument */ if (isdigit((*pargv)[0][2])) { *var = atoi((*pargv)[0] + 2); return 1; } else { return 0; } } } -u_long host_addr(name) +u_int32 host_addr(name) char *name; { struct hostent *e = gethostbyname(name); int addr; if (e) memcpy(&addr, e->h_addr_list[0], e->h_length); else { addr = inet_addr(name); if (addr == -1) addr = 0; } return addr; } -main(argc, argv) +int main(argc, argv) int argc; char *argv[]; { int flood = FALSE, graph = FALSE; - + #ifdef SYSV setvbuf(stderr, NULL, _IOLBF, 0); #else setlinebuf(stderr); #endif if (geteuid() != 0) { fprintf(stderr, "must be root\n"); exit(1); } argv++, argc--; while (argc > 0 && argv[0][0] == '-') { switch (argv[0][1]) { case 'd': if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc)) goto usage; break; case 'f': flood = TRUE; break; case 'g': graph = TRUE; break; case 'n': show_names = FALSE; break; case 'r': if (!get_number(&retries, -1, &argv, &argc)) goto usage; break; case 't': if (!get_number(&timeout, -1, &argv, &argc)) goto usage; break; default: goto usage; } argv++, argc--; } if (argc > 1) { - usage: + usage: fprintf(stderr, "Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n", "[-r retries] [-d [debug-level]] [router]"); fprintf(stderr, "\t-f Flood the routing graph with queries\n"); fprintf(stderr, "\t (True by default unless `router' is given)\n"); fprintf(stderr, "\t-g Generate output in GraphEd format\n"); fprintf(stderr, "\t-n Don't look up DNS names for routers\n"); exit(1); } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) { fprintf(stderr, "Unknown host: %s\n", argv[0]); exit(2); } if (debug) fprintf(stderr, "Debug level %u\n", debug); init_igmp(); { /* Find a good local address for us. */ int udp; struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof addr; +#endif addr.sin_addr.s_addr = dvmrp_group; addr.sin_port = htons(2000); /* any port over 1024 will do... */ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { perror("Determining local address"); exit(-1); } close(udp); our_addr = addr.sin_addr.s_addr; } /* Send initial seed message to all local routers */ ask(target_addr ? target_addr : allhosts_group); if (target_addr) { Node *n = find_node(target_addr, &routers); n->tries = 1; if (flood) target_addr = 0; } /* Main receive loop */ for(;;) { fd_set fds; struct timeval tv; int count, recvlen, dummy = 0; FD_ZERO(&fds); FD_SET(igmp_socket, &fds); tv.tv_sec = timeout; tv.tv_usec = 0; count = select(igmp_socket + 1, &fds, 0, 0, &tv); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { log(LOG_DEBUG, 0, "Timed out receiving neighbor lists"); if (retry_requests(routers)) continue; else break; } - recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy); if (recvlen >= 0) accept_igmp(recvlen); else if (errno != EINTR) perror("recvfrom"); } printf("\n"); if (graph) graph_map(); else { if (!target_addr) printf("Multicast Router Connectivity:\n\n"); print_map(routers); } exit(0); } -void accept_prune() +/* dummies */ +void accept_prune(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_graft() +void accept_graft(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_g_ack() +void accept_g_ack(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void add_table_entry() +void add_table_entry(origin, mcastgrp) + u_int32 origin, mcastgrp; { } -void leave_group_message() +void accept_leave_message(src, dst, group) + u_int32 src, dst, group; { } -void mtrace() +void accept_mtrace(src, dst, group, data, no, datalen) + u_int32 src, dst, group; + char *data; + u_int no; + int datalen; +{ +} +void accept_membership_query(src, dst, group, tmo) + u_int32 src, dst, group; + int tmo; { } Index: stable/2.1/usr.sbin/mrouted/mrinfo/Makefile =================================================================== --- stable/2.1/usr.sbin/mrouted/mrinfo/Makefile (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrinfo/Makefile (revision 10585) @@ -1,20 +1,22 @@ -# $Id: Makefile,v 1.1 1994/09/08 02:51:34 wollman Exp $ +# $Id: Makefile,v 1.3 1995/06/21 18:30:10 wollman Exp $ PROG= mrinfo S= ${.CURDIR}/.. .PATH: $S CFLAGS+= -I$S LDADD+= -lmrouted .if exists($S/common/obj) LDDESTDIR+= -L$S/common/obj DPADD+= $S/common/obj/libmrouted.a .else LDDESTDIR+= -L$S/common DPADD+= $S/common/libmrouted.a .endif SRCS= mrinfo.c MAN8= ${.CURDIR}/../mrinfo.8 +BINOWN= root +BINMODE=4755 .include Index: stable/2.1/usr.sbin/mrouted/mrinfo.8 =================================================================== --- stable/2.1/usr.sbin/mrouted/mrinfo.8 (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrinfo.8 (revision 10585) @@ -1,92 +1,83 @@ -.Dd March 31, 1995 -.Dt MRINFO 8 -.Sh NAME -.Nm mrinfo -.Nd displays configuration info from a multicast router -.Sh SYNOPSIS -.Nm mrinfo -.Op Fl d Ar debuglevel -.Op Fl r Ar retries -.Op Fl t Ar timeout -.Ar router -.Sh DESCRIPTION -The -.Nm mrinfo -program attempts to display the configuration information from the -multicast router -.Ar router . -.Pp -.Nm -uses the -.Dv ASK_NEIGHBORS -.Tn IGMP -message to the specified multicast router. If this multicast router -responds, the version number and a list of their neighboring multicast -router addresses is part of that response. If the responding router -has a recent multicast version number, then -.Nm mrinfo -requests additional information such as metrics, thresholds, and flags -from the multicast router. Once the specified multicast router -responds, the configuration is displayed to the standard output. -.Pp -The -.Nm -program accepts the following options: -.Bl -tag -width XXXdebuglevel -.It Fl d Ar debuglevel -This option sets the debug level to -.Ar debuglevel . -When the debug level is greater than the default value of 0, addition -debugging messages are printed. Regardless of the debug level, an -error condition, will always write an error message and will cause -.Nm +.TH MRINFO 8 +.UC 5 +.SH NAME +mrinfo \- Displays configuration info from a multicast router +.SH SYNOPSIS +.B /usr/sbin/mrinfo +[ +.B \-d +.I debug_level +] [ +.B \-r +.I retry_count +] [ +.B \-t +.I timeout_count +] +.B multicast_router + +.SH DESCRIPTION +.I mrinfo +attempts to display the configuration information from the multicast router +.I multicast_router. +.PP +.I mrinfo +uses the ASK_NEIGHBORS IGMP message to the specified multicast router. If this +multicast router responds, the version number and a list of their neighboring +multicast router addresses is part of that response. If the responding router +has a recent multicast version number, then +.I mrinfo +requests additional information such as metrics, thresholds, and flags from the +multicast router. Once the specified multicast router responds, the +configuration is displayed to the standard output. +.br +.ne 5 +.SH INVOCATION +.PP +"\-d" option sets the debug level. When the debug level is greater than the +default value of 0, addition debugging messages are printed. Regardless of +the debug level, an error condition, will always write an error message and will +cause +.I mrinfo to terminate. Non-zero debug levels have the following effects: -.Bl -tag -width "level 3" -.It level 1 +.IP "level 1" packet warnings are printed to stderr. -.It level 2 +.IP "level 2" all level 1 messages plus notifications down networks are printed to stderr. -.It level 3 +.IP "level 3" all level 2 messages plus notifications of all packet timeouts are printed to stderr. -.El -.It Fl r Ar retries -This option sets the neighbor query retry limit to -.Ar retries . -The default is three retries. -.It Fl t Ar timeout -This sets the number of seconds to wait for a neighbor query -reply. The default timeout is four seconds. -.El -.Sh SAMPLE OUTPUT -.Bd -literal -# mrinfo mbone.phony.dom.net +.PP +"\-r retry_count" sets the neighbor query retry limit. Default is 3 retry. +.PP +"\-t timeout_count" sets the number of seconds to wait for a neighbor query +reply. Default timeout is 4 seconds. +.PP +.SH SAMPLE OUTPUT +.nf +.I mrinfo mbone.phony.dom.net 127.148.176.10 (mbone.phony.dom.net) [version 3.3]: 127.148.176.10 -> 0.0.0.0 (?) [1/1/querier] 127.148.176.10 -> 127.0.8.4 (mbone2.phony.dom.net) [1/45/tunnel] 127.148.176.10 -> 105.1.41.9 (momoney.com) [1/32/tunnel/down] 127.148.176.10 -> 143.192.152.119 (mbone.dipu.edu) [1/32/tunnel] -.Ed -.Pp -For each neighbor of the queried multicast router, the IP of the -queried router is displayed, followed by the IP and name of the -neighbor. In square brackets the metric (cost of connection) and the -threshold (minimum TTL to forward) are displayed. If the queried multicast -router has a newer version number, the type (tunnel, srcrt) and status -(disabled, down) of the connection are also displayed. -.Sh RESTRICTIONS -.Nm -must be run as `root'. -.Sh SEE ALSO -.Xr map-mbone 8 , -.Xr mrouted 8 , -.Xr mtrace 8 -.Sh AUTHOR -Pavel Curtis -.Sh HISTORY -An -.Nm -command first appeared in -.Tn FreeBSD -2.0. +.fi +.PP +For each neighbor of the queried multicast router, the IP of the queried router +is displayed, followed by the IP and name of the neighbor. In square brackets +the metric (cost of connection), the treashold (multicast ttl) is displayed. If +the queried multicast router has a newer version number, the type (tunnel, +srcrt) and status (disabled, down) of the connection is displayed. +.PP +.SH IMPORTANT NOTE +.I mrinfo +must be run as root. +.PP +.SH SEE ALSO +.BR mrouted (8) , +.BR map-mbone (8) , +.BR mtrace (8) +.PP +.SH AUTHOR +Van Jacobson Index: stable/2.1/usr.sbin/mrouted/mrinfo.c =================================================================== --- stable/2.1/usr.sbin/mrouted/mrinfo.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrinfo.c (revision 10585) @@ -1,498 +1,612 @@ /* * This tool requests configuration info from a multicast router * and prints the reply (if any). Invoke it as: * * mrinfo router-name-or-address * * Written Wed Mar 24 1993 by Van Jacobson (adapted from the * multicast mapper written by Pavel Curtis). * * The lawyers insist we include the following UC copyright notice. * The mapper from which this is derived contained a Xerox copyright * notice which follows the UC one. Try not to get depressed noting * that the legal gibberish is larger than the program. * * Copyright (c) 1993 Regents of the University of California. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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) Xerox Corporation 1992. All rights reserved. - * + * * License is granted to copy, to use, and to make and to use derivative works * for research and evaluation purposes, provided that Xerox is acknowledged * in all documentation pertaining to any such copy or derivative work. Xerox * grants no other licenses expressed or implied. The Xerox trade name should * not be used in any advertising without its written permission. - * + * * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR * ANY PARTICULAR PURPOSE. The software is provided "as is" without express * or implied warranty of any kind. - * + * * These notices must be retained in any copies of any part of this software. */ #ifndef lint static char rcsid[] = - "@(#) $Id: mrinfo.c,v 1.3 1995/05/16 00:28:46 jkh Exp $"; + "@(#) $Id: mrinfo.c,v 1.7 1995/07/10 16:13:03 wollman Exp $"; /* original rcsid: "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)"; */ #endif #include #include #include "defs.h" +#include +#ifdef __STDC__ +#include +#else +#include +#endif #define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */ #define DEFAULT_RETRIES 3 /* How many times to ask each router */ -u_long our_addr, target_addr = 0; /* in NET order */ +u_int32 our_addr, target_addr = 0; /* in NET order */ int debug = 0; +int nflag = 0; int retries = DEFAULT_RETRIES; int timeout = DEFAULT_TIMEOUT; -int target_level; +int target_level = 0; vifi_t numvifs; /* to keep loader happy */ /* (see COPY_TABLES macro called in kern.c) */ +char * inet_name __P((u_int32 addr)); +void ask __P((u_int32 dst)); +void ask2 __P((u_int32 dst)); +int get_number __P((int *var, int deflt, char ***pargv, + int *pargc)); +u_int32 host_addr __P((char *name)); +void usage __P((void)); + +/* to shut up -Wstrict-prototypes */ +int main __P((int argc, char *argv[])); + + char * inet_name(addr) - u_long addr; + u_int32 addr; { struct hostent *e; + struct in_addr in; - e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + if (addr == 0) + return "local"; - return e ? e->h_name : "?"; + if (nflag || + (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) { + in.s_addr = addr; + return (inet_ntoa(in)); + } + return (e->h_name); } /* * Log errors and other messages to stderr, according to the severity of the * message and the current debug level. For errors of severity LOG_ERR or * worse, terminate the program. */ +#ifdef __STDC__ void -log(severity, syserr, format, a, b, c, d, e) +log(int severity, int syserr, char *format, ...) +{ + va_list ap; + char fmt[100]; + + va_start(ap, format); +#else +void +log(severity, syserr, format, va_alist) int severity, syserr; char *format; - int a, b, c, d, e; + va_dcl { + va_list ap; char fmt[100]; + va_start(ap); +#endif switch (debug) { case 0: if (severity > LOG_WARNING) return; case 1: if (severity > LOG_NOTICE) return; case 2: if (severity > LOG_INFO) return; default: fmt[0] = '\0'; if (severity == LOG_WARNING) strcat(fmt, "warning - "); strncat(fmt, format, 80); - fprintf(stderr, fmt, a, b, c, d, e); + vfprintf(stderr, fmt, ap); if (syserr == 0) fprintf(stderr, "\n"); else if (syserr < sys_nerr) fprintf(stderr, ": %s\n", sys_errlist[syserr]); else fprintf(stderr, ": errno %d\n", syserr); } if (severity <= LOG_ERR) exit(-1); } /* * Send a neighbors-list request. */ -void +void ask(dst) - u_long dst; + u_int32 dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, htonl(MROUTED_LEVEL), 0); } -void +void ask2(dst) - u_long dst; + u_int32 dst; { send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, htonl(MROUTED_LEVEL), 0); } /* * Process an incoming neighbor-list message. */ -void -accept_neighbors(src, dst, p, datalen) - u_long src, dst; - u_char *p; +void +accept_neighbors(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; int datalen; { u_char *ep = p + datalen; -#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\ - a += ((u_long)*p++ << 8), a += *p++) +#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\ + a += ((u_int32)*p++ << 8), a += *p++) printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src)); while (p < ep) { - register u_long laddr; + register u_int32 laddr; register u_char metric; register u_char thresh; register int ncount; GET_ADDR(laddr); laddr = htonl(laddr); metric = *p++; thresh = *p++; ncount = *p++; while (--ncount >= 0) { - register u_long neighbor; + register u_int32 neighbor; GET_ADDR(neighbor); neighbor = htonl(neighbor); printf(" %s -> ", inet_fmt(laddr, s1)); printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1), inet_name(neighbor), metric, thresh); } } } -void -accept_neighbors2(src, dst, p, datalen) - u_long src, dst; - u_char *p; +void +accept_neighbors2(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; int datalen; { u_char *ep = p + datalen; + u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */ + /* well, only possibly_broken_cisco, but that's too long to type. */ printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src), - target_level & 0xff, (target_level >> 8) & 0xff); + level & 0xff, (level >> 8) & 0xff); + while (p < ep) { register u_char metric; register u_char thresh; register u_char flags; register int ncount; - register u_long laddr = *(u_long*)p; + register u_int32 laddr = *(u_int32*)p; p += 4; metric = *p++; thresh = *p++; flags = *p++; ncount = *p++; - while (--ncount >= 0) { - register u_long neighbor = *(u_long*)p; + if (broken_cisco && ncount == 0) /* dumb Ciscos */ + ncount = 1; + if (broken_cisco && ncount > 15) /* dumb Ciscos */ + ncount = ncount & 0xf; + while (--ncount >= 0 && p < ep) { + register u_int32 neighbor = *(u_int32*)p; p += 4; printf(" %s -> ", inet_fmt(laddr, s1)); printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1), inet_name(neighbor), metric, thresh); if (flags & DVMRP_NF_TUNNEL) printf("/tunnel"); if (flags & DVMRP_NF_SRCRT) printf("/srcrt"); + if (flags & DVMRP_NF_PIM) + printf("/pim"); if (flags & DVMRP_NF_QUERIER) printf("/querier"); if (flags & DVMRP_NF_DISABLED) printf("/disabled"); if (flags & DVMRP_NF_DOWN) printf("/down"); + if (flags & DVMRP_NF_LEAF) + printf("/leaf"); printf("]\n"); } } } -int +int get_number(var, deflt, pargv, pargc) int *var, *pargc, deflt; char ***pargv; { if ((*pargv)[0][2] == '\0') { /* Get the value from the next * argument */ if (*pargc > 1 && isdigit((*pargv)[1][0])) { (*pargv)++, (*pargc)--; *var = atoi((*pargv)[0]); return 1; } else if (deflt >= 0) { *var = deflt; return 1; } else return 0; } else { /* Get value from the rest of this argument */ if (isdigit((*pargv)[0][2])) { *var = atoi((*pargv)[0] + 2); return 1; } else { return 0; } } } -u_long +u_int32 host_addr(name) char *name; { - struct hostent *e = gethostbyname(name); - int addr; + struct hostent *e; + u_int32 addr; - if (e) + addr = inet_addr(name); + if ((int)addr == -1) { + e = gethostbyname(name); + if (e == NULL || e->h_length != sizeof(addr)) + return (0); memcpy(&addr, e->h_addr_list[0], e->h_length); - else { - addr = inet_addr(name); - if (addr == -1) - addr = 0; } + return(addr); +} - return addr; +void +usage() +{ + fprintf(stderr, + "Usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n"); + exit(1); } - -int main(argc, argv) +int +main(argc, argv) int argc; char *argv[]; { + int tries = 0; + int trynew = 1; + struct timeval et; + setlinebuf(stderr); if (geteuid() != 0) { - fprintf(stderr, "must be root\n"); + fprintf(stderr, "mrinfo: must be root\n"); exit(1); } argv++, argc--; while (argc > 0 && argv[0][0] == '-') { switch (argv[0][1]) { case 'd': if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc)) - goto usage; + usage(); break; + case 'n': + ++nflag; + break; case 'r': if (!get_number(&retries, -1, &argv, &argc)) - goto usage; + usage(); break; case 't': if (!get_number(&timeout, -1, &argv, &argc)) - goto usage; + usage(); break; default: - goto usage; + usage(); } argv++, argc--; } + if (argc > 1) + usage(); + if (argc == 1) + target_addr = host_addr(argv[0]); + else + target_addr = host_addr("127.0.0.1"); - if (argc > 1 || (argc == 1 && !(target_addr = host_addr(argv[0])))) { -usage: fprintf(stderr, - "Usage: mrinfo [-t timeout] [-r retries] router\n"); + if (target_addr == 0) { + fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]); exit(1); } - if (target_addr == 0) - goto usage; if (debug) fprintf(stderr, "Debug level %u\n", debug); init_igmp(); { /* Find a good local address for us. */ int udp; struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof addr; +#endif addr.sin_addr.s_addr = target_addr; addr.sin_port = htons(2000); /* any port over 1024 will * do... */ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0 || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) { perror("Determining local address"); exit(-1); } close(udp); our_addr = addr.sin_addr.s_addr; } - ask(target_addr); + /* + * New strategy: send 'ask2' for two timeouts, then fall back + * to 'ask', since it's not very likely that we are going to + * find someone who only responds to 'ask' these days + */ + ask2(target_addr); + gettimeofday(&et, 0); + et.tv_sec += timeout; + /* Main receive loop */ for (;;) { fd_set fds; - struct timeval tv; + struct timeval tv, now; int count, recvlen, dummy = 0; - register u_long src, dst, group; + register u_int32 src, dst, group; struct ip *ip; struct igmp *igmp; int ipdatalen, iphdrlen, igmpdatalen; FD_ZERO(&fds); FD_SET(igmp_socket, &fds); - tv.tv_sec = timeout; - tv.tv_usec = 0; + gettimeofday(&now, 0); + tv.tv_sec = et.tv_sec - now.tv_sec; + tv.tv_usec = et.tv_usec - now.tv_usec; + if (tv.tv_usec < 0) { + tv.tv_usec += 1000000L; + --tv.tv_sec; + } + if (tv.tv_sec < 0) + tv.tv_sec = tv.tv_usec = 0; + count = select(igmp_socket + 1, &fds, 0, 0, &tv); if (count < 0) { if (errno != EINTR) perror("select"); continue; } else if (count == 0) { log(LOG_DEBUG, 0, "Timed out receiving neighbor lists"); - if (--retries < 0) + if (++tries > retries) exit(1); - if (target_level == 0) + /* If we've tried ASK_NEIGHBORS2 twice with + * no response, fall back to ASK_NEIGHBORS + */ + if (tries == 2 && target_level == 0) + trynew = 0; + if (target_level == 0 && trynew == 0) ask(target_addr); else ask2(target_addr); + gettimeofday(&et, 0); + et.tv_sec += timeout; continue; } - recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy); if (recvlen <= 0) { if (recvlen && errno != EINTR) perror("recvfrom"); continue; } if (recvlen < sizeof(struct ip)) { log(LOG_WARNING, 0, "packet too short (%u bytes) for IP header", recvlen); continue; } ip = (struct ip *) recv_buf; if (ip->ip_p == 0) continue; /* Request to install cache entry */ src = ip->ip_src.s_addr; - if (src != target_addr) { - fprintf(stderr, "mrinfo: got reply from %s", - inet_fmt(src, s1)); - fprintf(stderr, " instead of %s\n", - inet_fmt(target_addr, s1)); - continue; - } dst = ip->ip_dst.s_addr; iphdrlen = ip->ip_hl << 2; ipdatalen = ip->ip_len; if (iphdrlen + ipdatalen != recvlen) { log(LOG_WARNING, 0, "packet shorter (%u bytes) than hdr+data length (%u+%u)", recvlen, iphdrlen, ipdatalen); continue; } igmp = (struct igmp *) (recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { log(LOG_WARNING, 0, "IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inet_fmt(src, s1)); continue; } if (igmp->igmp_type != IGMP_DVMRP) continue; switch (igmp->igmp_code) { case DVMRP_NEIGHBORS: case DVMRP_NEIGHBORS2: if (src != target_addr) { fprintf(stderr, "mrinfo: got reply from %s", inet_fmt(src, s1)); fprintf(stderr, " instead of %s\n", inet_fmt(target_addr, s1)); - continue; + /*continue;*/ } break; default: continue; /* ignore all other DVMRP messages */ } switch (igmp->igmp_code) { case DVMRP_NEIGHBORS: if (group) { /* knows about DVMRP_NEIGHBORS2 msg */ if (target_level == 0) { target_level = ntohl(group); ask2(target_addr); } } else { - accept_neighbors(src, dst, (char *)(igmp + 1), - igmpdatalen); + accept_neighbors(src, dst, (u_char *)(igmp + 1), + igmpdatalen, ntohl(group)); exit(0); } break; case DVMRP_NEIGHBORS2: - accept_neighbors2(src, dst, (char *)(igmp + 1), - igmpdatalen); + accept_neighbors2(src, dst, (u_char *)(igmp + 1), + igmpdatalen, ntohl(group)); exit(0); } } } /* dummies */ -void accept_probe() +void accept_probe(src, dst, p, datalen, level) + u_int32 src, dst, level; + char *p; + int datalen; { } -void accept_group_report() +void accept_group_report(src, dst, group, r_type) + u_int32 src, dst, group; + int r_type; { } -void accept_neighbor_request2() +void accept_neighbor_request2(src, dst) + u_int32 src, dst; { } -void accept_report() +void accept_report(src, dst, p, datalen, level) + u_int32 src, dst, level; + char *p; + int datalen; { } -void accept_neighbor_request() +void accept_neighbor_request(src, dst) + u_int32 src, dst; { } -void accept_prune() +void accept_prune(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_graft() +void accept_graft(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_g_ack() +void accept_g_ack(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void add_table_entry() +void add_table_entry(origin, mcastgrp) + u_int32 origin, mcastgrp; { } void check_vif_state() { } -void leave_group_message() +void accept_leave_message(src, dst, group) + u_int32 src, dst, group; { } -void mtrace() +void accept_mtrace(src, dst, group, data, no, datalen) + u_int32 src, dst, group; + char *data; + u_int no; + int datalen; +{ +} +void accept_membership_query(src, dst, group, tmo) + u_int32 src, dst, group; + int tmo; { } Index: stable/2.1/usr.sbin/mrouted/mrouted/Makefile =================================================================== --- stable/2.1/usr.sbin/mrouted/mrouted/Makefile (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrouted/Makefile (revision 10585) @@ -1,20 +1,21 @@ -# $Id$ +# $Id: Makefile,v 1.3 1995/07/08 22:36:06 ats Exp $ PROG= mrouted S= ${.CURDIR}/.. .PATH: $S CFLAGS+= -I$S LDADD+= -lmrouted .if exists($S/common/obj) LDDESTDIR+= -L$S/common/obj DPADD+= $S/common/obj/libmrouted.a .else LDDESTDIR+= -L$S/common DPADD+= $S/common/libmrouted.a .endif -SRCS= config.c main.c route.c vif.c prune.c callout.c +SRCS= config.c cfparse.y main.c route.c vif.c prune.c callout.c rsrr.c +CLEANFILES+= y.tab.h MAN8= ${.CURDIR}/../mrouted.8 .include Index: stable/2.1/usr.sbin/mrouted/mrouted.8 =================================================================== --- stable/2.1/usr.sbin/mrouted/mrouted.8 (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrouted.8 (revision 10585) @@ -1,379 +1,399 @@ '\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University. -.Dd March 25, 1995 -.Dt MROUTED 8 -.Os FreeBSD 2.0 -.Sh NAME -.Nm mrouted -.Nd IP multicast routing process -.Sh SYNOPSIS -.Nm mrouted -.Op Fl p -.Op Fl c Ar config -.Op Fl d Ar debuglevel -.Sh DESCRIPTION -The -.Nm -program +'\"$Id: mrouted.8,v 1.4 1995/06/28 17:58:37 wollman Exp $ +.TH MROUTED 8 +.UC 5 +.SH NAME +mrouted \- IP multicast routing daemon +.SH SYNOPSIS +.B /etc/mrouted +[ +.B \-p +] [ +.B \-c +.I config_file +] [ +.B \-d +[ +.I debug_level +]] +.SH DESCRIPTION +.I Mrouted is an implementation of the Distance-Vector Multicast Routing Protocol (DVMRP), an earlier version of which is specified in RFC-1075. It maintains topological knowledge via a distance-vector routing protocol (like RIP, described in RFC-1058), upon which it implements a multicast datagram forwarding algorithm called Reverse Path Multicasting. -.Pp -.Nm +.PP +.I Mrouted forwards a multicast datagram along a shortest (reverse) path tree rooted at the subnet on which the datagram originates. The multicast delivery tree may be thought of as a broadcast delivery tree that has been pruned back so that it does not extend beyond those subnetworks that have members of the destination group. Hence, datagrams are not forwarded along those branches which have no listeners of the multicast group. The IP time-to-live of a multicast datagram can be used to limit the range of multicast datagrams. -.Pp +.PP In order to support multicasting among subnets that are separated by (unicast) routers that do not support IP multicasting, -.Nm +.I mrouted includes support for -.Dq tunnels , -which are virtual point-to-point links between pairs of -.Nm -programs located anywhere in an internet. IP multicast packets are -encapsulated for transmission through tunnels, so that they look like -normal unicast datagrams to intervening routers and subnets. The -encapsulation is added on entry to a tunnel, and stripped off on exit -from a tunnel. By default, the packets are encapsulated using the -IP-in-IP protocol (IP protocol number 4). Older versions of -.Nm -tunneled using IP source routing, which puts a heavy load on some +"tunnels", which are virtual point-to-point links between pairs of +.IR mrouted s +located anywhere in an internet. IP multicast packets are encapsulated for +transmission through tunnels, so that they look like normal unicast datagrams +to intervening routers and subnets. The encapsulation +is added on entry to a tunnel, and stripped off +on exit from a tunnel. +By default, the packets are encapsulated using the IP-in-IP protocol +(IP protocol number 4). +Older versions of +.I mrouted +tunnel using IP source routing, which puts a heavy load on some types of routers. -This version supports IP source route tunnelling only for backwards -compatibility. -.Pp +This version does not support IP source route tunnelling. +.PP The tunnelling mechanism allows -.Nm +.I mrouted to establish a virtual internet, for the purpose of multicasting only, which is independent of the physical internet, and which may span multiple Autonomous Systems. This capability is intended for experimental support of internet multicasting only, pending widespread support for multicast routing by the regular (unicast) routers. -.Nm +.I Mrouted suffers from the well-known scaling problems of any distance-vector routing protocol, and does not (yet) support hierarchical multicast routing. -.Pp -.Nm +.PP +.I Mrouted handles multicast routing only; there may or may not be unicast routing software running on the same machine as -.Nm mrouted . +.IR mrouted . With the use of tunnels, it is not necessary for -.Nm +.I mrouted to have access to more than one physical subnet in order to perform multicast forwarding. -.Pp -If no -.Fl d -option is given, or if the debug level is specified as 0, -.Nm -detaches from its controlling terminal. Otherwise, it remains attached to the -terminal and responsive to signals. -.Pp -The -.Nm -program accepts the following command-line options: -.Bl -tag -width XXXdebuglevel -.It Fl c Ar config -This option specifies an alternate configuration file location as -.Ar config . -The default location is -.Pa /etc/mrouted.conf . -.It Fl d Ar debuglevel -This option sets the debuging level to -.Ar debuglevel . -Regardless of the debug level, -.Nm +.br +.ne 5 +.SH INVOCATION +.PP +If no "\-d" option is given, or if the debug level is specified as 0, +.I mrouted +detaches from the invoking terminal. Otherwise, it remains attached to the +invoking terminal and responsive to signals from that terminal. If "\-d" is +given with no argument, the debug level defaults to 2. Regardless of the +debug level, +.I mrouted always writes warning and error messages to the system log demon. Non-zero debug levels have the following effects: -.Bl -tag -width "level 3" -.It level 1 +.IP "level 1" all syslog'ed messages are also printed to stderr. -.It level 2 -all level 1 messages plus notifications of -.Dq significant +.IP "level 2" +all level 1 messages plus notifications of "significant" events are printed to stderr. -.It level 3 +.IP "level 3" all level 2 messages plus notifications of all packet arrivals and departures are printed to stderr. -.El -.It Fl p -This option disables pruning of uninterested links in the multicast -distribution tree. -.El -.Sh CONFIGURATION -.Nm +.PP +Upon startup, mrouted writes its pid to the file /etc/mrouted.pid . +.SH CONFIGURATION +.PP +.I Mrouted automatically configures itself to forward on all multicast-capable -interfaces; i.e., interfaces that have the -.Dv IFF_MULTICAST -flag set (excluding the loopback), and it finds other -.Nm -programs directly reachable via those interfaces. To override the -default configuration, or to add tunnel links to other -.Nm -programs, configuration commands may be placed in -.Pa /etc/mrouted.conf -(or an alternate location can be specified using the -.Fl c -option). +interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding +the loopback "interface"), and it finds other +.IR mrouted s +directly reachable +via those interfaces. To override the default configuration, or to add +tunnel links to other +.IR mrouted s, +configuration commands may be placed in +/etc/mrouted.conf (or an alternative file, specified by the "\-c" option). There are four types of configuration commands: -.Bd -literal +.nf phyint [disable] [metric ] [threshold ] [rate_limit ] - [boundary /] + [boundary (|/)] + [altnet /] tunnel [metric ] - [threshold ] [srcrt] [rate_limit ] - [boundary /] + [threshold ] [rate_limit ] + [boundary (|/)] cache_lifetime pruning -.Ed -.Pp -One note about the configuration commands--all the phyint and tunnel -command options must be on a single line except for the boundary option -which may begin on a separate line. A single phyint or tunnel command may -have multiple boundary options. -.Pp + name / + +.fi +.PP +The file format is free-form; whitespace (including newlines) is not +significant. +The +.I boundary +and +.I altnet +options may be specified as many times as necessary. +.PP The phyint command can be used to disable multicast routing on the physical interface identified by local IP address , or to associate a non-default metric or threshold with the specified physical interface. The local IP address may be alternatively replaced by the -interface name (e.g., le0) for the phyint command only. +interface name (e.g le0). +If a phyint is attached to multiple IP subnets, describe each additional subnet +with the altnet keyword. Phyint commands must precede tunnel commands. -.Pp +.PP The tunnel command can be used to establish a tunnel link between local IP address and remote IP address , and to associate a non-default metric or threshold with that tunnel. The tunnel must be set up in the mrouted.conf files of both routers before it can be used. -For backwards compatibility with older -.Nm -programs, the srcrt keyword specifies -encapsulation using IP source routing. -.Pp +'\"For backwards compatibility with older +'\".IR mrouted s, +'\"the srcrt keyword specifies +'\"encapsulation using IP source routing. +.PP The cache_lifetime is a value that determines the amount of time that a cached multicast route stays in kernel before timing out. The value of this entry should lie between 300 (5 min) and 86400 (1 day). It defaults to 300. -.Pp +.PP The pruning option is provided for -.Nm +.IR mrouted to act as a non-pruning router. It is also possible to start -.Nm -in a non-pruning mode using the -.Fl p -flag on the command line. It is expected that a router would be -configured in this manner for test purposes only. The default mode is -pruning enabled. -.Pp -The metric is the -.Dq cost -associated with sending a datagram on the given +.IR mrouted +in a non-pruning mode using the "-p" option on the command line. It is +expected that a router would be configured in this manner for test +purposes only. The default mode is pruning enabled. +.PP +You may assign names to boundaries to make configuration easier with +the name keyword. The boundary option on phyint or tunnel commands +can accept either a name or a boundary. +.PP +The metric is the "cost" associated with sending a datagram on the given interface or tunnel; it may be used to influence the choice of routes. The metric defaults to 1. Metrics should be kept as small as possible, because -.Nm +.I mrouted cannot route along paths with a sum of metrics greater than 31. -.Pp +.LP The threshold is the minimum IP time-to-live required for a multicast datagram to be forwarded to the given interface or tunnel. It is used to control the scope of multicast datagrams. (The TTL of forwarded packets is only compared to the threshold, it is not decremented by the threshold. Every multicast router decrements the TTL by 1.) The default threshold is 1. -.Pp +.LP In general, all -.Nm mrouted -programs connected to a particular subnet or tunnel should +.IR mrouted s +connected to a particular subnet or tunnel should use the same metric and threshold for that subnet or tunnel. -.Pp +.PP The rate_limit option allows the network administrator to specify a certain bandwidth in Kbits/second which would be allocated to multicast -traffic. -.Pp +traffic. It defaults to 500Kbps on tunnels, and 0 (unlimited) on physical +interfaces. +.PP The boundary option allows an interface to be configured as an administrative boundary for the specified scoped address. Packets belonging to this address will not -be forwarded on a scoped interface. -.Pp -The -.Nm -program +be forwarded on a scoped interface. The boundary option accepts either +a name or a boundary spec. +.PP +.I Mrouted will not initiate execution if it has fewer than two enabled vifs, where a vif (virtual interface) is either a physical multicast-capable interface or a tunnel. It will log a warning if all of its vifs are tunnels; such an -.Nm +.I mrouted configuration would be better replaced by more direct tunnels (i.e., eliminate the middle man). -.Sh SIGNALS -The -.Nm -program responds to the following signals: -.Bl -tag -width SIGTERMx -.It Dv SIGHUP +.SH "EXAMPLE CONFIGURATION" +.PP +This is an example configuration for a mythical multicast router at a big +school. +.sp +.nf +# +# mrouted.conf example +# +# Name our boundaries to make it easier +name LOCAL 239.255.0.0/16 +name EE 239.254.0.0/16 +# +# le1 is our gateway to compsci, don't forward our +# local groups to them +phyint le1 boundary EE +# +# le2 is our interface on the classroom net, it has four +# different length subnets on it. +# note that you can use either an ip address or an +# interface name +phyint 172.16.12.38 boundary EE altnet 172.16.15.0/26 + altnet 172.16.15.128/26 altnet 172.16.48.0/24 +# +# atm0 is our ATM interface, which doesn't properly +# support multicasting. +phyint atm0 disable +# +# This is an internal tunnel to another EE subnet +# Remove the default tunnel rate limit, since this +# tunnel is over ethernets +tunnel 192.168.5.4 192.168.55.101 metric 1 threshold 1 + rate_limit 0 +# +# This is our tunnel to the outside world. +# Careful with those boundaries, Eugene. +tunnel 192.168.5.4 10.11.12.13 metric 1 threshold 32 + boundary LOCAL boundary EE +.fi +.SH SIGNALS +.PP +.I Mrouted +responds to the following signals: +.IP HUP restarts -.Nm mrouted . +.I mrouted . The configuration file is reread every time this signal is evoked. -.It Dv SIGINT +.IP INT terminates execution gracefully (i.e., by sending good-bye messages to all neighboring routers). -.It Dv SIGTERM -same as -.Dv SIGINT -.It Dv SIGUSR1 -dumps the internal routing tables to -.Pa /var/tmp/mrouted.dump . -.It Dv SIGUSR2 -dumps the internal cache tables to -.Pa /var/tmp/mrouted.cache . -.It Dv SIGQUIT +.IP TERM +same as INT +.IP USR1 +dumps the internal routing tables to /usr/tmp/mrouted.dump. +.IP USR2 +dumps the internal cache tables to /usr/tmp/mrouted.cache. +.IP QUIT dumps the internal routing tables to stderr (only if -.Nm +.I mrouted was invoked with a non-zero debug level). -.El -.Sh EXAMPLE +.PP +For convenience in sending signals, +.I mrouted +writes its pid to /etc/mrouted.pid upon startup. +.bp +.SH EXAMPLE +.PP The routing tables look like this: -.Bd -literal +.nf Virtual Interface Table Vif Local-Address Metric Thresh Flags 0 36.2.0.8 subnet: 36.2 1 1 querier groups: 224.0.2.1 224.0.0.4 pkts in: 3456 pkts out: 2322323 1 36.11.0.1 subnet: 36.11 1 1 querier groups: 224.0.2.1 224.0.1.0 224.0.0.4 pkts in: 345 pkts out: 3456 2 36.2.0.8 tunnel: 36.8.0.77 3 1 peers: 36.8.0.77 (2.2) boundaries: 239.0.1 : 239.1.2 pkts in: 34545433 pkts out: 234342 3 36.2.0.8 tunnel: 36.6.8.23 3 16 Multicast Routing Table (1136 entries) - Origin-Subnet From-Gateway Metric In-Vif Out-Vifs - 36.2 1 0 1* 2 3* - 36.8 36.8.0.77 4 2 0* 1* 3* - 36.11 1 1 0* 2 3* + Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs + 36.2 1 45 0 1* 2 3* + 36.8 36.8.0.77 4 15 2 0* 1* 3* + 36.11 1 20 1 0* 2 3* . . . -.Ed -.Pp +.fi In this example, there are four vifs connecting to two subnets and two tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and vif 1 subnets have some groups present; tunnels never have any groups. This instance of -.Nm +.I mrouted is the one responsible for sending periodic group membership queries on the vif 0 and vif 1 subnets, as indicated by the -.Dq querier -flags. The list of boundaries indicate the scoped addresses on that -interface. A count of the number of incoming and outgoing packets is -also shown at each interface. -.Pp -Associated with each subnet from which a multicast datagram can -originate is the address of the previous hop router (unless the subnet -is directly connected), the metric of the path back to the origin, -the incoming vif for multicasts from that origin, and a list of -outgoing vifs. -.Dq Li \&* -means that the outgoing vif is connected to a leaf of the broadcast -tree rooted at the origin, and a multicast datagram from that origin -will be forwarded on that outgoing vif only if there are members of -the destination group on that leaf. -.Pp -.Nm +"querier" flags. The list of boundaries indicate the scoped addresses on that +interface. A count of the no. of incoming and outgoing packets is also +shown at each interface. +.PP +Associated with each subnet from which a multicast datagram can originate +is the address of the previous hop router (unless the subnet is directly- +connected), the metric of the path back to the origin, the amount of time +since we last received an update for this subnet, the incoming vif for +multicasts from that origin, and a list of outgoing vifs. "*" means that +the outgoing vif is connected to a leaf of the broadcast tree rooted at the +origin, and a multicast datagram from that origin will be forwarded on that +outgoing vif only if there are members of the destination group on that leaf. +.bp +.PP +.I Mrouted also maintains a copy of the kernel forwarding cache table. Entries are created and deleted by -.Nm mrouted . -.Pp +.I mrouted. +.PP The cache tables look like this: -.Bd -literal +.nf -Multicast Routing Cache Table (325 entries) - Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs - 134.207.7 224.2.140.239 300 1 0 0 2 - 138.15.103 224.2.203.214 295 1 2 P 0p 2p - 128.237.0 224.2.253.119 290 1 1 0 2p - 129.215.200 224.2.207.48 40 1 1 0p 2 - 36.77.14 239.0.1.234 345 2b +Multicast Routing Cache Table (147 entries) + Origin Mcast-group CTmr Age Ptmr IVif Forwvifs + 13.2.116/22 224.2.127.255 3m 2m - 0 1 +>13.2.116.19 +>13.2.116.196 + 138.96.48/21 224.2.127.255 5m 2m - 0 1 +>138.96.48.108 + 128.9.160/20 224.2.127.255 3m 2m - 0 1 +>128.9.160.45 + 198.106.194/24 224.2.135.190 9m 28s 9m 0P +>198.106.194.22 -.Ed -.Pp -Each entry is characterized by the origin subnet number and the -destination multicast group. The -.Dq CTmr -field indicates the lifetime -(in seconds) of the entry. The entry is deleted from the cache table -when the timer decrements to zero. The -.Dq Ivif -field indicates the +.fi +Each entry is characterized by the origin subnet number and mask and the +destination multicast group. The 'CTmr' field indicates the lifetime +of the entry. The entry is deleted from the cache table +when the timer decrements to zero. The 'Age' field is the time since +this cache entry was originally created. Since cache entries get refreshed +if traffic is flowing, routing entries can grow very old. +The 'Ptmr' field is simply a dash if no prune was sent upstream, or the +amount of time until the upstream prune will time out. +The 'Ivif' field indicates the incoming vif for multicast packets from that origin. Each router also -maintains a record of the number of prunes received from neighbouring +maintains a record of the number of prunes received from neighboring routers for a particular source and group. If there are no members of a multicast group on any downward link of the multicast tree for a subnet, a prune message is sent to the upstream router. They are -indicated by a -.Dq Li \&P -in the -.Dq Psnt -field. The -.Dq Forwvifs -field shows the +indicated by a "P" after the vif number. The Forwvifs field shows the interfaces along which datagrams belonging to the source-group are -forwarded. A -.Dq Li \&p -indicates that no datagrams are being forwarded along +forwarded. A "p" indicates that no datagrams are being forwarded along that interface. An unlisted interface is a leaf subnet with are no -members of the particular group on that subnet. A -.Dq Li \&b -on an interface -indicates that it is a boundary interface; i.e., traffic will not be +members of the particular group on that subnet. A "b" on an interface +indicates that it is a boundary interface, i.e. traffic will not be forwarded on the scoped address on that interface. -.Sh FILES -.Bl -tag -compact -width /var/tmp/mrouted.cache -.It Pa /etc/mrouted.conf -default configuration file -.It Pa /var/tmp/mrouted.cache -kernel forwarding cache dump file -.It Pa /var/tmp/mrouted.dump -routing table dump file -.El -.Sh SEE ALSO -.Xr map-mbone 8 , -.Xr mrinfo 8 , -.Xr mtrace 8 -.Rs -.%A "S. Deering" -.%B "Proceedings of the ACM SIGCOMM '88 Conference" -.%T "Multicast Routing in Internetworks and Extended LANs" -.Re -.Sh AUTHORS -Steve Deering & Ajit Thyagarajan -.Sh HISTORY -The -.Nm -program first appeared in -.Tn FreeBSD -2.0. +An additional line with a ">" as the first character is printed for +each source on the subnet. Note that there can be many sources in +one subnet. +.SH FILES +/etc/mrouted.conf +.br +/etc/mrouted.pid +.br +/usr/tmp/mrouted.dump +.br +/usr/tmp/mrouted.cache +.SH SEE ALSO +.BR mrinfo (8) , +.BR mtrace (8) , +.BR map-mbone (8) +.sp +DVMRP is described, along with other multicast routing algorithms, in the +paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering, +in the Proceedings of the ACM SIGCOMM '88 Conference. +.SH AUTHORS +Steve Deering, Ajit Thyagarajan, Bill Fenner Index: stable/2.1/usr.sbin/mrouted/mrouted.conf =================================================================== --- stable/2.1/usr.sbin/mrouted/mrouted.conf (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mrouted.conf (revision 10585) @@ -1,26 +1,37 @@ -# $Id: mrouted.conf,v 1.5 1994/08/24 23:54:21 thyagara Exp $ +# $Id: mrouted.conf,v 1.4 1995/06/28 17:58:38 wollman Exp $ # # This is the configuration file for "mrouted", an IP multicast router. # mrouted looks for it in "/etc/mrouted.conf". # # Command formats: # -# cache_lifetime 3600 +# name / +# cache_lifetime 3600 # seconds # pruning on # # phyint [disable] [metric ] [threshold ] [rate_limit ] -# [boundary /] +# [boundary (|/)] +# [altnet (/|)] # tunnel [srcrt] [metric ] # [threshold ] [rate_limit ] -# [boundary /] +# [boundary (|/)] # # NOTE: any phyint commands MUST precede any tunnel commands -# NOTE: boundary commands may appear on a separate line -# (OTHER keywords must be on the same line as phyint or tunnel) # NOTE: the mask-len is the no. of leading 1's in the mask +# NOTE: rate_limit is in kilobits, and defaults to 500 for tunnels # - -phyint 128.4.2.2 metric 1 threshold 16 boundary 239.2.0.0/16 - boundary 239.5.8.0/24 -tunnel 128.4.0.77 128.4.0.8 metric 3 rate_limit 500 # <-- EXAMPLE - boundary 239.2.3.3/16 # 239.2.x.x is scoped +# Example of named bounary: +#name LOCAL 239.255.0.0/16 +#name EE 239.254.0.0/16 # i.e. the EE dept wants local groups +# +# Example of use of named boundary +#phyint le1 boundary EE # le1 is our interface to comp sci, +# # keep them away from our local groups +# +# +# Template tunnel for mcast_install +tunnel 128.4.0.77 128.4.0.8 metric 1 threshold 64 rate_limit 500 # <-- REPLACE +# boundary LOCAL +# +# You might want to specify a boundary on your tunnel to the outside world, +# as above. Index: stable/2.1/usr.sbin/mrouted/mtrace/Makefile =================================================================== --- stable/2.1/usr.sbin/mrouted/mtrace/Makefile (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mtrace/Makefile (revision 10585) @@ -1,20 +1,22 @@ -# $Id$ +# $Id: Makefile,v 1.2 1995/06/21 18:30:16 wollman Exp $ PROG= mtrace S= ${.CURDIR}/.. .PATH: $S CFLAGS+= -I$S LDADD+= -lmrouted .if exists($S/common/obj) LDDESTDIR+= -L$S/common/obj DPADD+= $S/common/obj/libmrouted.a .else LDDESTDIR+= -L$S/common DPADD+= $S/common/libmrouted.a .endif SRCS= mtrace.c -NOMAN= +MAN8= ${.CURDIR}/../mtrace.8 +BINOWN= root +BINMODE=4755 .include Index: stable/2.1/usr.sbin/mrouted/mtrace.c =================================================================== --- stable/2.1/usr.sbin/mrouted/mtrace.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/mtrace.c (revision 10585) @@ -1,450 +1,1463 @@ +/* + * mtrace.c + * + * This tool traces the branch of a multicast tree from a source to a + * receiver for a particular multicast group and gives statistics + * about packet rate and loss for each hop along the path. It can + * usually be invoked just as + * + * mtrace source + * + * to trace the route from that source to the local host for a default + * group when only the route is desired and not group-specific packet + * counts. See the usage line for more complex forms. + * + * + * Released 4 Apr 1995. This program was adapted by Steve Casner + * (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and + * Xerox PARC). It attempts to parallel in command syntax and output + * format the unicast traceroute program written by Van Jacobson (LBL) + * for the parts where that makes sense. + * + * Copyright (c) 1995 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 non-commercial 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, Information + * Sciences Institute. The name of the University may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no 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. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + * + * In particular, parts of the prototype version of this program may + * have been derived from mrouted programs sources covered by the + * license in the accompanying file named "LICENSE". + * + * $Id: mtrace.c,v 1.6 1995/06/28 17:58:40 wollman Exp $ + */ + #include #include +#include +#include +#include +#include #include "defs.h" +#include +#ifdef __STDC__ +#include +#else +#include +#endif -#define DEFAULT_TIMEOUT 10 /* How long to wait before retrying requests */ -#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */ +#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */ +#define DEFAULT_RETRIES 3 /* How many times to try */ +#define MAXHOPS UNREACHABLE /* Don't need more hops than max metric */ +#define UNICAST_TTL 255 /* TTL for unicast response */ +#define MULTICAST_TTL1 64 /* Default TTL for multicast query/response */ +#define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */ +#define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */ -int timeout = DEFAULT_TIMEOUT; +struct resp_buf { + u_long qtime; /* Time query was issued */ + u_long rtime; /* Time response was received */ + int len; /* Number of reports or length of data */ + struct igmp igmp; /* IGMP header */ + union { + struct { + struct tr_query q; /* Query/response header */ + struct tr_resp r[MAXHOPS]; /* Per-hop reports */ + } t; + char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */ + } u; +} base, incr[2]; +#define qhdr u.t.q +#define resps u.t.r +#define ndata u.d + +char names[MAXHOPS][40]; + +int timeout = DEFAULT_TIMEOUT; +int nqueries = DEFAULT_RETRIES; +int numeric = FALSE; +int debug = 0; +int passive = FALSE; +int multicast = FALSE; + +u_int32 defgrp; /* Default group if not specified */ +u_int32 query_cast; /* All routers multicast addr */ +u_int32 resp_cast; /* Mtrace response multicast addr */ + +u_int32 lcl_addr = 0; /* This host address, in NET order */ +u_int32 dst_netmask; /* netmask to go with qdst */ + +/* + * Query/response parameters, all initialized to zero and set later + * to default values or from options. + */ +u_int32 qsrc = 0; /* Source address in the query */ +u_int32 qgrp = 0; /* Group address in the query */ +u_int32 qdst = 0; /* Destination (receiver) address in query */ +u_char qno = 0; /* Max number of hops to query */ +u_int32 raddr = 0; /* Address where response should be sent */ +int qttl = 0; /* TTL for the query packet */ +u_char rttl = 0; /* TTL for the response packet */ +u_int32 gwy = 0; /* User-supplied last-hop router address */ +u_int32 tdst = 0; /* Address where trace is sent (last-hop) */ + vifi_t numvifs; /* to keep loader happy */ - /* (see COPY_TABLES macro called in kern.c) */ + /* (see kern.c) */ +#ifndef SYSV +extern long random(); +#endif +extern int errno; +char * inet_name __P((u_int32 addr)); +u_int32 host_addr __P((char *name)); +/* u_int is promoted u_char */ +char * proto_type __P((u_int type)); +char * flag_type __P((u_int type)); +u_int32 get_netmask __P((int s, u_int32 dst)); +int get_ttl __P((struct resp_buf *buf)); +int t_diff __P((u_long a, u_long b)); +u_long fixtime __P((u_long time)); +int send_recv __P((u_int32 dst, int type, int code, + int tries, struct resp_buf *save)); +char * print_host __P((u_int32 addr)); +void print_trace __P((int index, struct resp_buf *buf)); +int what_kind __P((struct resp_buf *buf)); +char * scale __P((int *hop)); +void stat_line __P((struct tr_resp *r, struct tr_resp *s, + int have_next)); +void fixup_stats __P((struct resp_buf *base, + struct resp_buf *new)); +int print_stats __P((struct resp_buf *base, + struct resp_buf *prev, + struct resp_buf *new)); +void check_vif_state __P((void)); + +int main __P((int argc, char *argv[])); + + + char * inet_name(addr) - u_long addr; + u_int32 addr; { - struct hostent *e; + struct hostent *e; - e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); - return e ? e->h_name : "?"; + return e ? e->h_name : "?"; } -u_long + +u_int32 host_addr(name) - char *name; + char *name; { - struct hostent *e = gethostbyname(name); - int addr; + struct hostent *e = (struct hostent *)0; + u_int32 addr; + int i, dots = 3; + char buf[40]; + char *ip = name; + char *op = buf; - if (e) - memcpy(&addr, e->h_addr_list[0], e->h_length); - else { - addr = inet_addr(name); - if (addr == -1) - addr = 0; - } + /* + * Undo BSD's favor -- take fewer than 4 octets as net/subnet address + * if the name is all numeric. + */ + for (i = sizeof(buf) - 7; i > 0; --i) { + if (*ip == '.') --dots; + else if (*ip == '\0') break; + else if (!isdigit(*ip)) dots = 0; /* Not numeric, don't add zeroes */ + *op++ = *ip++; + } + for (i = 0; i < dots; ++i) { + *op++ = '.'; + *op++ = '0'; + } + *op = '\0'; - return addr; + if (dots <= 0) e = gethostbyname(name); + if (e) memcpy((char *)&addr, e->h_addr_list[0], e->h_length); + else { + addr = inet_addr(buf); + if (addr == -1) { + addr = 0; + printf("Could not parse %s as host name or address\n", name); + } + } + return addr; } + + char * proto_type(type) - u_char type; + u_int type; { + static char buf[80]; + switch (type) { case PROTO_DVMRP: - return ("PROTO_DVMRP"); + return ("DVMRP"); case PROTO_MOSPF: - return ("PROTO_MOSPF"); + return ("MOSPF"); case PROTO_PIM: - return ("PROTO_PIM"); + return ("PIM"); case PROTO_CBT: - return ("PROTO_CBT"); + return ("CBT"); default: - return ("PROTO_UNKNOWN"); + (void) sprintf(buf, "Unknown protocol code %d", type); + return (buf); } } + char * flag_type(type) - u_char type; + u_int type; { + static char buf[80]; + switch (type) { case TR_NO_ERR: - return ("NO_ERR"); + return (""); case TR_WRONG_IF: - return ("WRONG_IF"); + return ("Wrong interface"); case TR_PRUNED: - return ("PRUNED"); + return ("Prune sent upstream"); + case TR_OPRUNED: + return ("Output pruned"); case TR_SCOPED: - return ("SCOPED"); + return ("Hit scope boundary"); case TR_NO_RTE: - return ("NO_RTE"); + return ("No route"); + case TR_OLD_ROUTER: + return ("Next router no mtrace"); + case TR_NO_FWD: + return ("Not forwarding"); + case TR_NO_SPACE: + return ("No space in packet"); default: - return ("INVALID ERR"); + (void) sprintf(buf, "Unknown error code %d", type); + return (buf); } +} + +/* + * If destination is on a local net, get the netmask, else set the + * netmask to all ones. There are two side effects: if the local + * address was not explicitly set, and if the destination is on a + * local net, use that one; in either case, verify that the local + * address is valid. + */ + +u_int32 +get_netmask(s, dst) + int s; + u_int32 dst; +{ + unsigned int i; + char ifbuf[5000]; + struct ifconf ifc; + struct ifreq *ifr; + u_int32 if_addr, if_mask; + u_int32 retval = 0xFFFFFFFF; + int found = FALSE; + + ifc.ifc_buf = ifbuf; + ifc.ifc_len = sizeof(ifbuf); + if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) { + perror("ioctl (SIOCGIFCONF)"); + return (retval); + } + i = ifc.ifc_len / sizeof(struct ifreq); + ifr = ifc.ifc_req; + for (; i > 0; i--, ifr++) { + if_addr = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr; + if (ioctl(s, SIOCGIFNETMASK, (char *)ifr) >= 0) { + if_mask = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr; + if ((dst & if_mask) == (if_addr & if_mask)) { + retval = if_mask; + if (lcl_addr == 0) lcl_addr = if_addr; + } + } + if (lcl_addr == if_addr) found = TRUE; + } + if (!found && lcl_addr != 0) { + printf("Interface address is not valid\n"); + exit(1); + } + return (retval); } + int +get_ttl(buf) + struct resp_buf *buf; +{ + register rno; + register struct tr_resp *b; + register ttl; + + if (buf && (rno = buf->len) > 0) { + b = buf->resps + rno - 1; + ttl = b->tr_fttl; + + while (--rno > 0) { + --b; + if (ttl < b->tr_fttl) ttl = b->tr_fttl; + else ++ttl; + } + ttl += MULTICAST_TTL_INC; + if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1; + if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX; + return (ttl); + } else return(MULTICAST_TTL1); +} + +/* + * Calculate the difference between two 32-bit NTP timestamps and return + * the result in milliseconds. + */ +int t_diff(a, b) u_long a, b; { int d = a - b; return ((d * 125) >> 13); } -main(argc, argv) -int argc; -char *argv[]; +/* + * Fixup for incorrect time format in 3.3 mrouted. + * This is possible because (JAN_1970 mod 64K) is quite close to 32K, + * so correct and incorrect times will be far apart. + */ +u_long +fixtime(time) + u_long time; { - struct timeval tq; - struct timezone tzp; - u_long querytime, resptime; + if (abs((int)(time-base.qtime)) > 0x3FFFFFFF) + time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) + + ((time & 0xFFFF) << 14) / 15625; + return (time); +} - int udp; - struct sockaddr_in addr; - int addrlen = sizeof(addr); - u_long lcl_addr = 0; /* in NET order */ +int +send_recv(dst, type, code, tries, save) + u_int32 dst; + int type, code, tries; + struct resp_buf *save; +{ + fd_set fds; + struct timeval tq, tr, tv; + struct ip *ip; + struct igmp *igmp; + struct tr_query *query, *rquery; + int ipdatalen, iphdrlen, igmpdatalen; + u_int32 local, group; + int datalen; + int count, recvlen, dummy = 0; + int len; + int i; - u_long qid = ((u_long)random() >> 8); - u_long qsrc = NULL; - u_long qgrp = NULL; - u_long qdst = NULL; - u_char qno = 0; - u_long raddr = NULL; - u_char qttl = 1; - u_char rttl = 1; - u_long dst = NULL; + if (type == IGMP_MTRACE) { + group = qgrp; + datalen = sizeof(struct tr_query); + } else { + group = htonl(MROUTED_LEVEL); + datalen = 0; + } + if (IN_MULTICAST(ntohl(dst))) local = lcl_addr; + else local = INADDR_ANY; - struct tr_query *query; + /* + * If the reply address was not explictly specified, start off + * with the unicast address of this host. Then, if there is no + * response after trying half the tries with unicast, switch to + * the standard multicast reply address. If the TTL was also not + * specified, set a multicast TTL and if needed increase it for the + * last quarter of the tries. + */ + query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); + query->tr_raddr = raddr ? raddr : multicast ? resp_cast : lcl_addr; + query->tr_rttl = rttl ? rttl : + IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL; + query->tr_src = qsrc; + query->tr_dst = qdst; - struct tr_rlist *tr_rlist = NULL; + for (i = tries ; i > 0; --i) { + if (tries == nqueries && raddr == 0) { + if (i == ((nqueries + 1) >> 1)) { + query->tr_raddr = resp_cast; + if (rttl == 0) query->tr_rttl = get_ttl(save); + } + if (i <= ((nqueries + 3) >> 2) && rttl == 0) { + query->tr_rttl += MULTICAST_TTL_INC; + if (query->tr_rttl > MULTICAST_TTL_MAX) + query->tr_rttl = MULTICAST_TTL_MAX; + } + } - char *p; - int datalen = 0; + /* + * Change the qid for each request sent to avoid being confused + * by duplicate responses + */ +#ifdef SYSV + query->tr_qid = ((u_int32)lrand48() >> 8); +#else + query->tr_qid = ((u_int32)random() >> 8); +#endif + /* + * Set timer to calculate delays, then send query + */ + gettimeofday(&tq, 0); + send_igmp(local, dst, type, code, group, datalen); + + /* + * Wait for response, discarding false alarms + */ + while (TRUE) { + FD_ZERO(&fds); + FD_SET(igmp_socket, &fds); + gettimeofday(&tv, 0); + tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec; + tv.tv_usec = tq.tv_usec - tv.tv_usec; + if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec; + if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0; + + count = select(igmp_socket + 1, &fds, (fd_set *)0, (fd_set *)0, + &tv); + + if (count < 0) { + if (errno != EINTR) perror("select"); + continue; + } else if (count == 0) { + printf("* "); + fflush(stdout); + break; + } + + gettimeofday(&tr, 0); + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, + 0, (struct sockaddr *)0, &dummy); + + if (recvlen <= 0) { + if (recvlen && errno != EINTR) perror("recvfrom"); + continue; + } + + if (recvlen < sizeof(struct ip)) { + fprintf(stderr, + "packet too short (%u bytes) for IP header", recvlen); + continue; + } + ip = (struct ip *) recv_buf; + if (ip->ip_p == 0) /* ignore cache creation requests */ + continue; + + iphdrlen = ip->ip_hl << 2; + ipdatalen = ip->ip_len; + if (iphdrlen + ipdatalen != recvlen) { + fprintf(stderr, + "packet shorter (%u bytes) than hdr+data len (%u+%u)\n", + recvlen, iphdrlen, ipdatalen); + continue; + } + + igmp = (struct igmp *) (recv_buf + iphdrlen); + igmpdatalen = ipdatalen - IGMP_MINLEN; + if (igmpdatalen < 0) { + fprintf(stderr, + "IP data field too short (%u bytes) for IGMP from %s\n", + ipdatalen, inet_fmt(ip->ip_src.s_addr, s1)); + continue; + } + + switch (igmp->igmp_type) { + + case IGMP_DVMRP: + if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue; + len = igmpdatalen; + /* + * Accept DVMRP_NEIGHBORS2 response if it comes from the + * address queried or if that address is one of the local + * addresses in the response. + */ + if (ip->ip_src.s_addr != dst) { + register u_int32 *p = (u_int32 *)(igmp + 1); + register u_int32 *ep = p + (len >> 2); + while (p < ep) { + register u_int32 laddr = *p++; + register int n = ntohl(*p++) & 0xFF; + if (laddr == dst) { + ep = p + 1; /* ensure p < ep after loop */ + break; + } + p += n; + } + if (p >= ep) continue; + } + break; + + case IGMP_MTRACE: /* For backward compatibility with 3.3 */ + case IGMP_MTRACE_RESP: + if (igmpdatalen <= QLEN) continue; + if ((igmpdatalen - QLEN)%RLEN) { + printf("packet with incorrect datalen\n"); + continue; + } + + /* + * Ignore responses that don't match query. + */ + rquery = (struct tr_query *)(igmp + 1); + if (rquery->tr_qid != query->tr_qid) continue; + if (rquery->tr_src != qsrc) continue; + if (rquery->tr_dst != qdst) continue; + len = (igmpdatalen - QLEN)/RLEN; + + /* + * Ignore trace queries passing through this node when + * mtrace is run on an mrouter that is in the path + * (needed only because IGMP_MTRACE is accepted above + * for backward compatibility with multicast release 3.3). + */ + if (igmp->igmp_type == IGMP_MTRACE) { + struct tr_resp *r = (struct tr_resp *)(rquery+1) + len - 1; + u_int32 smask; + + VAL_TO_MASK(smask, r->tr_smask); + if (len < code && (r->tr_inaddr & smask) != (qsrc & smask) + && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80)) + continue; + } + + /* + * A match, we'll keep this one. + */ + if (len > code) { + fprintf(stderr, + "Num hops received (%d) exceeds request (%d)\n", + len, code); + } + rquery->tr_raddr = query->tr_raddr; /* Insure these are */ + rquery->tr_rttl = query->tr_rttl; /* as we sent them */ + break; + + default: + continue; + } + + /* + * Most of the sanity checking done at this point. + * Return this packet we have been waiting for. + */ + if (save) { + save->qtime = ((tq.tv_sec + JAN_1970) << 16) + + (tq.tv_usec << 10) / 15625; + save->rtime = ((tr.tv_sec + JAN_1970) << 16) + + (tr.tv_usec << 10) / 15625; + save->len = len; + bcopy((char *)igmp, (char *)&save->igmp, ipdatalen); + } + return (recvlen); + } + } + return (0); +} + + +char * +print_host(addr) + u_int32 addr; +{ + char *name; + + if (numeric) { + printf("%s", inet_fmt(addr, s1)); + return (""); + } + name = inet_name(addr); + printf("%s (%s)", name, inet_fmt(addr, s1)); + return (name); +} + +/* + * Print responses as received (reverse path from dst to src) + */ +void +print_trace(index, buf) + int index; + struct resp_buf *buf; +{ + struct tr_resp *r; + char *name; int i; - int done = 0; + i = abs(index); + r = buf->resps + i - 1; + + for (; i <= buf->len; ++i, ++r) { + if (index > 0) printf("%3d ", -i); + name = print_host(r->tr_outaddr); + printf(" %s thresh^ %d %d ms %s\n", proto_type(r->tr_rproto), + r->tr_fttl, t_diff(fixtime(ntohl(r->tr_qarr)), buf->qtime), + flag_type(r->tr_rflags)); + memcpy(names[i-1], name, sizeof(names[0]) - 1); + names[i-1][sizeof(names[0])-1] = '\0'; + } +} + +/* + * See what kind of router is the next hop + */ +int +what_kind(buf) + struct resp_buf *buf; +{ + u_int32 smask; + int retval; + int hops = buf->len; + struct tr_resp *r = buf->resps + hops - 1; + u_int32 next = r->tr_rmtaddr; + + retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0]); + print_host(next); + if (retval) { + u_int32 version = ntohl(incr[0].igmp.igmp_group.s_addr); + u_int32 *p = (u_int32 *)incr[0].ndata; + u_int32 *ep = p + (incr[0].len >> 2); + char *type = ""; + retval = 0; + switch (version & 0xFF) { + case 1: + type = "proteon/mrouted "; + retval = 1; + break; + + case 2: + case 3: + if (((version >> 8) & 0xFF) < 3) retval = 1; + /* Fall through */ + case 4: + type = "mrouted "; + break; + + case 10: + type = "cisco "; + } + printf(" [%s%d.%d] didn't respond\n", + type, version & 0xFF, (version >> 8) & 0xFF); + VAL_TO_MASK(smask, r->tr_smask); + while (p < ep) { + register u_int32 laddr = *p++; + register int flags = (ntohl(*p) & 0xFF00) >> 8; + register int n = ntohl(*p++) & 0xFF; + if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) && + (laddr & smask) == (qsrc & smask)) { + printf("%3d ", -(hops+2)); + print_host(qsrc); + printf("\n"); + return 1; + } + p += n; + } + return retval; + } + printf(" didn't respond\n"); + return 0; +} + + +char * +scale(hop) + int *hop; +{ + if (*hop > -1000 && *hop < 10000) return (" ms"); + *hop /= 1000; + if (*hop > -1000 && *hop < 10000) return (" s "); + return ("s "); +} + +/* + * Calculate and print one line of packet loss and packet rate statistics. + * Checks for count of all ones from mrouted 2.3 that doesn't have counters. + */ +#define NEITHER 0 +#define INS 1 +#define OUTS 2 +#define BOTH 3 +void +stat_line(r, s, have_next) + struct tr_resp *r, *s; + int have_next; +{ + register timediff = (fixtime(ntohl(s->tr_qarr)) - + fixtime(ntohl(r->tr_qarr))) >> 16; + register v_lost, v_pct; + register g_lost, g_pct; + register v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout); + register g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt); + register v_pps, g_pps; + char v_str[8], g_str[8]; + register have = NEITHER; + + if (timediff == 0) timediff = 1; + v_pps = v_out / timediff; + g_pps = g_out / timediff; + + if (v_out || s->tr_vifout != 0xFFFFFFFF) have |= OUTS; + + if (have_next) { + --r, --s; + if (s->tr_vifin != 0xFFFFFFFF || r->tr_vifin != 0xFFFFFFFF) + have |= INS; + } + + switch (have) { + case BOTH: + v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin)); + if (v_out) v_pct = (v_lost * 100 + (v_out >> 1)) / v_out; + else v_pct = 0; + if (-100 < v_pct && v_pct < 101 && v_out > 10) + sprintf(v_str, "%3d", v_pct); + else memcpy(v_str, " --", 4); + + g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)); + if (g_out) g_pct = (g_lost * 100 + (g_out >> 1))/ g_out; + else g_pct = 0; + if (-100 < g_pct && g_pct < 101 && g_out > 10) + sprintf(g_str, "%3d", g_pct); + else memcpy(g_str, " --", 4); + + printf("%6d/%-5d=%s%%%4d pps%6d/%-5d=%s%%%4d pps\n", + v_lost, v_out, v_str, v_pps, g_lost, g_out, g_str, g_pps); + if (debug > 2) { + printf("\t\t\t\tv_in: %ld ", ntohl(s->tr_vifin)); + printf("v_out: %ld ", ntohl(s->tr_vifout)); + printf("pkts: %ld\n", ntohl(s->tr_pktcnt)); + printf("\t\t\t\tv_in: %ld ", ntohl(r->tr_vifin)); + printf("v_out: %ld ", ntohl(r->tr_vifout)); + printf("pkts: %ld\n", ntohl(r->tr_pktcnt)); + printf("\t\t\t\tv_in: %ld ",ntohl(s->tr_vifin)-ntohl(r->tr_vifin)); + printf("v_out: %ld ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout)); + printf("pkts: %ld ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)); + printf("time: %d\n", timediff); + } + break; + + case INS: + v_out = (ntohl(s->tr_vifin) - ntohl(r->tr_vifin)); + g_out = (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)); + v_pps = v_out / timediff; + g_pps = g_out / timediff; + /* Fall through */ + + case OUTS: + printf(" %-5d %4d pps %-5d %4d pps\n", + v_out, v_pps, g_out, g_pps); + break; + + case NEITHER: + printf("\n"); + break; + } +} + +/* + * A fixup to check if any pktcnt has been reset. + */ +void +fixup_stats(base, new) + struct resp_buf *base, *new; +{ + register rno = base->len; + register struct tr_resp *b = base->resps + rno; + register struct tr_resp *n = new->resps + rno; + + while (--rno >= 0) + if (ntohl((--n)->tr_pktcnt) < ntohl((--b)->tr_pktcnt)) break; + + if (rno < 0) return; + + rno = base->len; + b = base->resps + rno; + n = new->resps + rno; + + while (--rno >= 0) (--b)->tr_pktcnt = (--n)->tr_pktcnt; +} + +/* + * Print responses with statistics for forward path (from src to dst) + */ +int +print_stats(base, prev, new) + struct resp_buf *base, *prev, *new; +{ + int rtt, hop; + register char *ms; + register u_int32 smask; + register rno = base->len - 1; + register struct tr_resp *b = base->resps + rno; + register struct tr_resp *p = prev->resps + rno; + register struct tr_resp *n = new->resps + rno; + register u_long resptime = new->rtime; + register u_long qarrtime = fixtime(ntohl(n->tr_qarr)); + register ttl = n->tr_fttl; + + VAL_TO_MASK(smask, b->tr_smask); + printf(" Source Response Dest"); + printf(" Packet Statistics For Only For Traffic\n"); + printf("%-15s %-15s All Multicast Traffic From %s\n", + ((b->tr_inaddr & smask) == (qsrc & smask)) ? s1 : " * * * ", + inet_fmt(base->qhdr.tr_raddr, s2), inet_fmt(qsrc, s1)); + rtt = t_diff(resptime, new->qtime); + ms = scale(&rtt); + printf(" | __/ rtt%5d%s Lost/Sent = Pct Rate To %s\n", + rtt, ms, inet_fmt(qgrp, s2)); + hop = t_diff(resptime, qarrtime); + ms = scale(&hop); + printf(" v / hop%5d%s", hop, ms); + printf(" --------------------- --------------------\n"); + if (debug > 2) { + printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin)); + printf("v_out: %ld ", ntohl(n->tr_vifout)); + printf("pkts: %ld\n", ntohl(n->tr_pktcnt)); + printf("\t\t\t\tv_in: %ld ", ntohl(b->tr_vifin)); + printf("v_out: %ld ", ntohl(b->tr_vifout)); + printf("pkts: %ld\n", ntohl(b->tr_pktcnt)); + printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin)); + printf("v_out: %ld ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout)); + printf("pkts: %ld\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt)); + } + + while (TRUE) { + if ((n->tr_inaddr != b->tr_inaddr) || (n->tr_inaddr != b->tr_inaddr)) + return 1; /* Route changed */ + + if ((n->tr_inaddr != n->tr_outaddr)) + printf("%-15s\n", inet_fmt(n->tr_inaddr, s1)); + printf("%-15s %-14s %s\n", inet_fmt(n->tr_outaddr, s1), names[rno], + flag_type(n->tr_rflags)); + + if (rno-- < 1) break; + + printf(" | ^ ttl%5d ", ttl); + if (prev == new) printf("\n"); + else stat_line(p, n, TRUE); + resptime = qarrtime; + qarrtime = fixtime(ntohl((n-1)->tr_qarr)); + hop = t_diff(resptime, qarrtime); + ms = scale(&hop); + printf(" v | hop%5d%s", hop, ms); + stat_line(b, n, TRUE); + + --b, --p, --n; + if (ttl < n->tr_fttl) ttl = n->tr_fttl; + else ++ttl; + } + + printf(" | \\__ ttl%5d ", ttl); + if (prev == new) printf("\n"); + else stat_line(p, n, FALSE); + hop = t_diff(qarrtime, new->qtime); + ms = scale(&hop); + printf(" v \\ hop%5d%s", hop, ms); + stat_line(b, n, FALSE); + printf("%-15s %s\n", inet_fmt(qdst, s1), inet_fmt(lcl_addr, s2)); + printf(" Receiver Query Source\n\n"); + return 0; +} + + +/*************************************************************************** + * main + ***************************************************************************/ + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int udp; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + int recvlen; + struct timeval tv; + struct resp_buf *prev, *new; + struct tr_resp *r; + u_int32 smask; + int rno; + int hops, nexthop, tries; + u_int32 lastout = 0; + int numstats = 1; + int waittime; + int seed; + if (geteuid() != 0) { - fprintf(stderr, "must be root\n"); + fprintf(stderr, "mtrace: must be root\n"); exit(1); } argv++, argc--; - if (argc == 0) goto usage; while (argc > 0 && *argv[0] == '-') { - switch (argv[0][1]) { - case 's': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - qsrc = host_addr(argv[0]); + register char *p = *argv++; argc--; + p++; + do { + register char c = *p++; + register char *arg = (char *) 0; + if (isdigit(*p)) { + arg = p; + p = ""; + } else if (argc > 0) arg = argv[0]; + switch (c) { + case 'd': /* Unlisted debug print option */ + if (arg && isdigit(*arg)) { + debug = atoi(arg); + if (debug < 0) debug = 0; + if (debug > 3) debug = 3; + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'M': /* Use multicast for reponse */ + multicast = TRUE; break; - } else - goto usage; - case 'g': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - qgrp = host_addr(argv[0]); + case 'l': /* Loop updating stats indefinitely */ + numstats = 3153600; break; - } else - goto usage; - case 'd': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - qdst = host_addr(argv[0]); + case 'n': /* Don't reverse map host addresses */ + numeric = TRUE; break; - } else - goto usage; - case 'x': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - dst = host_addr(argv[0]); + case 'p': /* Passive listen for traces */ + passive = TRUE; break; - } else - goto usage; - case 't': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - qttl = atoi(argv[0]); - if (qttl < 1) - qttl = 1; + case 's': /* Short form, don't wait for stats */ + numstats = 0; break; - } else + case 'w': /* Time to wait for packet arrival */ + if (arg && isdigit(*arg)) { + timeout = atoi(arg); + if (timeout < 1) timeout = 1; + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'm': /* Max number of hops to trace */ + if (arg && isdigit(*arg)) { + qno = atoi(arg); + if (qno > MAXHOPS) qno = MAXHOPS; + else if (qno < 1) qno = 0; + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'q': /* Number of query retries */ + if (arg && isdigit(*arg)) { + nqueries = atoi(arg); + if (nqueries < 1) nqueries = 1; + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'g': /* Last-hop gateway (dest of query) */ + if (arg && (gwy = host_addr(arg))) { + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 't': /* TTL for query packet */ + if (arg && isdigit(*arg)) { + qttl = atoi(arg); + if (qttl < 1) qttl = 1; + rttl = qttl; + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'r': /* Dest for response packet */ + if (arg && (raddr = host_addr(arg))) { + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + case 'i': /* Local interface address */ + if (arg && (lcl_addr = host_addr(arg))) { + if (arg == argv[0]) argv++, argc--; + break; + } else + goto usage; + default: goto usage; - case 'n': - if (argc > 1 && isdigit(*(argv + 1)[0])) { + } + } while (*p); + } + + if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */ + if (IN_MULTICAST(ntohl(qsrc))) goto usage; + argv++, argc--; + if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */ + argv++, argc--; + if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */ argv++, argc--; - qno = atoi(argv[0]); - break; - } else - goto usage; - case 'l': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - rttl = atoi(argv[0]); - break; - } else - goto usage; - case 'r': - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++, argc--; - raddr = host_addr(argv[0]); - break; - } else - goto usage; - default: - goto usage; + } + if (IN_MULTICAST(ntohl(qdst))) { + u_int32 temp = qdst; + qdst = qgrp; + qgrp = temp; + if (IN_MULTICAST(ntohl(qdst))) goto usage; + } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) goto usage; } - argv++, argc--; } - if (argc > 0) { -usage: printf("usage: mtrace -s -g -d -n <# reports> \n"); - printf(" -t [-x ] [-r ] [-l ]\n"); + if (argc > 0 || qsrc == 0) { +usage: printf("\ +Usage: mtrace [-Mlnps] [-w wait] [-m max_hops] [-q nqueries] [-g gateway]\n\ + [-t ttl] [-r resp_dest] [-i if_addr] source [receiver] [group]\n"); exit(1); } - printf("Mtrace src %s grp %s dst %s #%d\n", inet_fmt(qsrc, s1), - inet_fmt(qgrp, s2), inet_fmt(qdst, s3), qno); - printf(" resp ttl %d resp addr %s\n", rttl, inet_fmt(raddr, s1)); - init_igmp(); - /* Obtain the local address from which to send out packets */ + /* + * Set useful defaults for as many parameters as possible. + */ + defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */ + query_cast = htonl(0xE0000002); /* All routers multicast addr */ + resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */ + if (qgrp == 0) qgrp = defgrp; + + /* + * Get default local address for multicasts to use in setting defaults. + */ addr.sin_family = AF_INET; - addr.sin_len = sizeof addr; +#if (defined(BSD) && (BSD >= 199103)) + addr.sin_len = sizeof(addr); +#endif addr.sin_addr.s_addr = qgrp; - addr.sin_port = htons(2000); + addr.sin_port = htons(2000); /* Any port above 1024 will do */ if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) || (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { perror("Determining local address"); exit(-1); } + + /* + * Default destination for path to be queried is the local host. + */ + if (qdst == 0) qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr; + dst_netmask = get_netmask(udp, qdst); close(udp); - lcl_addr = addr.sin_addr.s_addr; + if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr; - /* Got the local address now */ - /* Now, make up the IGMP packet to send */ + /* + * Initialize the seed for random query identifiers. + */ + gettimeofday(&tv, 0); + seed = tv.tv_usec ^ lcl_addr; +#ifdef SYSV + srand48(seed); +#else + srandom(seed); +#endif - query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); + /* + * Protect against unicast queries to mrouted versions that might crash. + */ + if (gwy && !IN_MULTICAST(ntohl(gwy))) + if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) { + int version = ntohl(incr[0].igmp.igmp_group.s_addr) & 0xFFFF; + if (version == 0x0303 || version == 0x0503) { + printf("Don't use -g to address an mrouted 3.%d, it might crash\n", + (version >> 8) & 0xFF); + exit(0); + } + } - query->tr_src = qsrc; - query->tr_dst = qdst; - query->tr_qid = qid; - if (raddr) - query->tr_raddr = raddr; - else - query->tr_raddr = lcl_addr; - query->tr_rttl = rttl; + printf("Mtrace from %s to %s via group %s\n", + inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3)); - datalen += sizeof(struct tr_query); + if ((qdst & dst_netmask) == (qsrc & dst_netmask)) { + printf("Source & receiver are directly connected, no path to trace\n"); + exit(0); + } - if (IN_MULTICAST(ntohl(qgrp))) - k_set_ttl(qttl); - else - k_set_ttl(1); + /* + * If the response is to be a multicast address, make sure we + * are listening on that multicast address. + */ + if (raddr) { + if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr); + } else k_join(resp_cast, lcl_addr); - if (dst == NULL) - dst = qgrp; - /* - * set timer to calculate delays & send query + * If the destination is on the local net, the last-hop router can + * be found by multicast to the all-routers multicast group. + * Otherwise, use the group address that is the subject of the + * query since by definition the last-hop router will be a member. + * Set default TTLs for local remote multicasts. */ - gettimeofday(&tq, &tzp); - querytime = ((tq.tv_sec + JAN_1970) << 16) + (tq.tv_usec << 10) / 15625; + restart: - send_igmp(lcl_addr, dst, IGMP_MTRACE, qno, - qgrp, datalen); + if (gwy == 0) + if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast; + else tdst = qgrp; + else tdst = gwy; + if (IN_MULTICAST(ntohl(tdst))) { + k_set_loop(1); /* If I am running on a router, I need to hear this */ + if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1); + else k_set_ttl(qttl ? qttl : MULTICAST_TTL1); + } + /* - * If the response is to be a multicast address, make sure we - * are listening on that multicast address. + * Try a query at the requested number of hops or MAXOPS if unspecified. */ - if (IN_MULTICAST(ntohl(raddr))) - k_join(raddr, lcl_addr); + if (qno == 0) { + hops = MAXHOPS; + tries = 1; + printf("Querying full reverse path... "); + fflush(stdout); + } else { + hops = qno; + tries = nqueries; + printf("Querying reverse path, maximum %d hops... ", qno); + fflush(stdout); + } + base.rtime = 0; + base.len = 0; - /* Wait for our reply now */ - while (!done) { - fd_set fds; - struct timeval tv; - struct timezone tzp; + recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base); - int count, recvlen, dummy = 0; - register u_long src, group, smask; - struct ip *ip; - struct igmp *igmp; - struct tr_resp *resp; - int ipdatalen, iphdrlen, igmpdatalen; - int rno; + /* + * If the initial query was successful, print it. Otherwise, if + * the query max hop count is the default of zero, loop starting + * from one until there is no response for four hops. The extra + * hops allow getting past an mtrace-capable mrouter that can't + * send multicast packets because all phyints are disabled. + */ + if (recvlen) { + printf("\n 0 "); + print_host(qdst); + printf("\n"); + print_trace(1, &base); + r = base.resps + base.len - 1; + if (r->tr_rflags == TR_OLD_ROUTER) { + printf("%3d ", -(base.len+1)); + what_kind(&base); + } else { + VAL_TO_MASK(smask, r->tr_smask); + if ((r->tr_inaddr & smask) == (qsrc & smask)) { + printf("%3d ", -(base.len+1)); + print_host(qsrc); + printf("\n"); + } + } + } else if (qno == 0) { + printf("switching to hop-by-hop:\n 0 "); + print_host(qdst); + printf("\n"); - FD_ZERO(&fds); - FD_SET(igmp_socket, &fds); + for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) { + printf("%3d ", -hops); + fflush(stdout); - /* need to input timeout as optional argument */ - tv.tv_sec = timeout; - tv.tv_usec = 0; + /* + * After a successful first hop, try switching to the unicast + * address of the last-hop router instead of multicasting the + * trace query. This should be safe for mrouted versions 3.3 + * and 3.5 because there is a long route timeout with metric + * infinity before a route disappears. Switching to unicast + * reduces the amount of multicast traffic and avoids a bug + * with duplicate suppression in mrouted 3.5. + */ + if (hops == 2 && gwy == 0 && + (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base))) + tdst = lastout; + else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base); - count = select(igmp_socket + 1, &fds, 0, 0, &tv); + if (recvlen == 0) { + if (hops == 1) break; + if (hops == nexthop) { + if (what_kind(&base)) { + /* the ask_neighbors determined that the + * not-responding router is the first-hop. */ + break; + } + } else if (hops < nexthop + 3) { + printf("\n"); + } else { + printf("...giving up\n"); + break; + } + continue; + } + r = base.resps + base.len - 1; + if (base.len == hops && + (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) { + if (hops == nexthop) { + print_trace(-hops, &base); + } else { + printf("\nResuming...\n"); + print_trace(nexthop, &base); + } + } else { + if (base.len == hops - 1) { + if (nexthop <= base.len) { + printf("\nResuming...\n"); + print_trace(nexthop, &base); + } + } else { + hops = base.len; + printf("\nRoute must have changed...\n"); + print_trace(1, &base); + } + if (r->tr_rflags == TR_OLD_ROUTER) { + what_kind(&base); + break; + } + if (r->tr_rflags == TR_NO_SPACE) { + printf("No space left in trace packet for more hops\n"); + break; /* XXX could do segmented trace */ + } + } + lastout = r->tr_outaddr; + nexthop = hops + 1; - if (count < 0) { - if (errno != EINTR) - perror("select"); - continue; - } else if (count == 0) { - printf("Timed out receiving responses\n"); - exit(1); + VAL_TO_MASK(smask, r->tr_smask); + if ((r->tr_inaddr & smask) == (qsrc & smask)) { + printf("%3d ", -nexthop); + print_host(qsrc); + printf("\n"); + break; + } + if (r->tr_rmtaddr == 0 || (r->tr_rflags & 0x80)) break; } + } - gettimeofday(&tq, &tzp); - resptime = ((tq.tv_sec + JAN_1970) << 16) + (tq.tv_usec << 10) / 15625; + if (base.rtime == 0) { + printf("Timed out receiving responses\n"); + if (IN_MULTICAST(ntohl(tdst))) + if (tdst == query_cast) + printf("Perhaps no local router has a route for source %s\n", + inet_fmt(qsrc, s1)); + else + printf("Perhaps receiver %s is not a member of group %s,\n\ +or no router local to it has a route for source %s,\n\ +or multicast at ttl %d doesn't reach its last-hop router for that source\n", + inet_fmt(qdst, s2), inet_fmt(qgrp, s3), inet_fmt(qsrc, s1), + qttl ? qttl : MULTICAST_TTL1); + exit(1); + } - recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), - 0, NULL, &dummy); + printf("Round trip time %d ms\n\n", t_diff(base.rtime, base.qtime)); - if (recvlen <= 0) { - if (recvlen && errno != EINTR) - perror("recvfrom"); - continue; - } + /* + * Use the saved response which was the longest one received, + * and make additional probes after delay to measure loss. + */ + raddr = base.qhdr.tr_raddr; + rttl = base.qhdr.tr_rttl; + gettimeofday(&tv, 0); + waittime = 10 - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16)); + prev = new = &incr[numstats&1]; - if (recvlen < sizeof(struct ip)) { - log(LOG_WARNING, 0, - "packet too short (%u bytes) for IP header", - recvlen); - continue; + while (numstats--) { + if (waittime < 1) printf("\n"); + else { + printf("Waiting to accumulate statistics... "); + fflush(stdout); + sleep((unsigned)waittime); } - ip = (struct ip *) recv_buf; + rno = base.len; + recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new); - iphdrlen = ip->ip_hl << 2; - ipdatalen = ip->ip_len; - if (iphdrlen + ipdatalen != recvlen) { - printf("packet shorter (%u bytes) than hdr+data length (%u+%u)\n", - recvlen, iphdrlen, ipdatalen); - continue; + if (recvlen == 0) { + printf("Timed out.\n"); + exit(1); } - igmp = (struct igmp *) (recv_buf + iphdrlen); - group = igmp->igmp_group.s_addr; - igmpdatalen = ipdatalen - IGMP_MINLEN; - if (igmpdatalen < 0) { - printf("IP data field too short (%u bytes) for IGMP, from %s\n", - ipdatalen, inet_fmt(src, s1)); - continue; + if (rno != new->len) { + printf("Trace length doesn't match:\n"); + print_trace(1, new); + printf("Restarting.\n\n"); + goto restart; } - if (igmp->igmp_type != IGMP_MTRACE && - igmp->igmp_type != IGMP_MTRACE_RESP) - continue; - - if (igmpdatalen == QLEN) - continue; - - if ((igmpdatalen - QLEN)%RLEN) { - printf("packet with incorrect datalen\n"); - continue; + printf("Results after %d seconds:\n\n", + (int)((new->qtime - base.qtime) >> 16)); + fixup_stats(&base, new); + if (print_stats(&base, prev, new)) { + printf("Route changed:\n"); + print_trace(1, new); + printf("Restarting.\n\n"); + goto restart; } + prev = new; + new = &incr[numstats&1]; + waittime = 10; + } - query = (struct tr_query *)(igmp + 1); + /* + * If the response was multicast back, leave the group + */ + if (raddr) { + if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr); + } else k_leave(resp_cast, lcl_addr); - /* If this is query with a different id, ignore! */ - if (query->tr_qid != qid) - continue; + return (0); +} - /* - * Most of the sanity checking done at this point. - * This is the packet we have been waiting for all this time - */ - resp = (struct tr_resp *)(query + 1); +void +check_vif_state() +{ + log(LOG_WARNING, errno, "sendto"); +} - rno = (igmpdatalen - QLEN)/RLEN; +/* + * Log errors and other messages to stderr, according to the severity + * of the message and the current debug level. For errors of severity + * LOG_ERR or worse, terminate the program. + */ +#ifdef __STDC__ +void +log(int severity, int syserr, char *format, ...) +{ + va_list ap; + char fmt[100]; - /* - * print the responses out in reverse order (from src to dst) - */ - printf("src: <%s> grp: <%s> dst: <%s> rtt: %d ms\n\n", - inet_fmt(qsrc, s1), inet_fmt(qgrp, s2), inet_fmt(qdst, s3), - t_diff(resptime, querytime)); + va_start(ap, format); +#else +/*VARARGS3*/ +void +log(severity, syserr, format, va_alist) + int severity, syserr; + char *format; + va_dcl +{ + va_list ap; + char fmt[100]; - VAL_TO_MASK(smask, (resp+rno-1)->tr_smask); + va_start(ap); +#endif - if (((resp+rno-1)->tr_inaddr & smask) == (qsrc & smask)) - printf(" %-15s \n", inet_fmt(qsrc, s1)); - else - printf(" * * *\n"); - - while (rno--) { - struct tr_resp *r = resp + rno; - - printf(" | \n"); - printf(" %-15s ", inet_fmt(r->tr_inaddr, s1)); - printf("ttl %d ", r->tr_fttl); - printf("cum: %d ms ", t_diff(r->tr_qarr, querytime)); - printf("hop: %d ms ", t_diff(resptime, r->tr_qarr)); - printf("%s ", proto_type(r->tr_rproto)); - printf("%s\n", flag_type(r->tr_rflags)); - - printf(" %-15s ", inet_fmt(r->tr_outaddr, s1)); - printf("v_in: %ld ", r->tr_vifin); - printf("v_out: %ld ", r->tr_vifout); - printf("pkts: %ld\n", r->tr_pktcnt); - - resptime = r->tr_qarr; - } - printf(" | \n"); - printf(" %-15s \n", inet_fmt(qdst, s1)); - - /* - * if the response was multicast back, leave the group - */ - if (IN_MULTICAST(ntohl(raddr))) - k_leave(raddr, lcl_addr); - - /* If I don't expect any more replies, exit here */ - exit(0); + switch (debug) { + case 0: if (severity > LOG_WARNING) return; + case 1: if (severity > LOG_NOTICE) return; + case 2: if (severity > LOG_INFO ) return; + default: + fmt[0] = '\0'; + if (severity == LOG_WARNING) strcat(fmt, "warning - "); + strncat(fmt, format, 80); + vfprintf(stderr, fmt, ap); + if (syserr == 0) + fprintf(stderr, "\n"); + else if(syserr < sys_nerr) + fprintf(stderr, ": %s\n", sys_errlist[syserr]); + else + fprintf(stderr, ": errno %d\n", syserr); } + if (severity <= LOG_ERR) exit(-1); } + /* dummies */ -void log() +void accept_probe(src, dst, p, datalen, level) + u_int32 src, dst, level; + char *p; + int datalen; { } -void accept_probe() +void accept_group_report(src, dst, group, r_type) + u_int32 src, dst, group; + int r_type; { } -void accept_group_report() +void accept_neighbor_request2(src, dst) + u_int32 src, dst; { } -void accept_neighbors() +void accept_report(src, dst, p, datalen, level) + u_int32 src, dst, level; + char *p; + int datalen; { } -void accept_neighbors2() +void accept_neighbor_request(src, dst) + u_int32 src, dst; { } -void accept_neighbor_request2() +void accept_prune(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_report() +void accept_graft(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_neighbor_request() +void accept_g_ack(src, dst, p, datalen) + u_int32 src, dst; + char *p; + int datalen; { } -void accept_prune() +void add_table_entry(origin, mcastgrp) + u_int32 origin, mcastgrp; { } -void accept_graft() +void accept_leave_message(src, dst, group) + u_int32 src, dst, group; { } -void accept_g_ack() +void accept_mtrace(src, dst, group, data, no, datalen) + u_int32 src, dst, group; + char *data; + u_int no; + int datalen; { } -void add_table_entry() +void accept_membership_query(src, dst, group, tmo) + u_int32 src, dst, group; + int tmo; { } -void check_vif_state() +void accept_neighbors(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; + int datalen; { } -void mtrace() -{ -} -void leave_group_message() +void accept_neighbors2(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; + int datalen; { } Index: stable/2.1/usr.sbin/mrouted/prune.c =================================================================== --- stable/2.1/usr.sbin/mrouted/prune.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/prune.c (revision 10585) @@ -1,1416 +1,2335 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: prune.c,v 1.4 1995/05/16 00:28:48 jkh Exp $ + * $Id: prune.c,v 1.9 1995/07/10 16:13:05 wollman Exp $ */ #include "defs.h" -#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */ - extern int cache_lifetime; extern int max_prune_lifetime; +extern struct rtentry *routing_table; +extern int phys_vif; + /* * dither cache lifetime to obtain a value between x and 2*x */ +#ifdef SYSV +#define CACHE_LIFETIME(x) ((x) + (lrand48() % (x))) +#else #define CACHE_LIFETIME(x) ((x) + (random() % (x))) +#endif #define CHK_GS(x, y) { \ switch(x) { \ case 2: \ case 4: \ case 8: \ case 16: \ case 32: \ case 64: \ case 128: \ case 256: y = 1; \ break; \ default: y = 0; \ } \ } - -static struct ktable *kernel_rtable; /* ptr to list of kernel rt entries */ + +struct gtable *kernel_table; /* ptr to list of kernel grp entries*/ +static struct gtable *kernel_no_route; /* list of grp entries w/o routes */ +struct gtable *gtp; /* pointer for kernel rt entries */ unsigned int kroutes; /* current number of cache entries */ +/**************************************************************************** + Functions that are local to prune.c +****************************************************************************/ +static void prun_add_ttls __P((struct gtable *gt)); +static int pruning_neighbor __P((vifi_t vifi, u_int32 addr)); +static int can_mtrace __P((vifi_t vifi, u_int32 addr)); +static struct ptable * find_prune_entry __P((u_int32 vr, struct ptable *pt)); +static void send_prune __P((struct gtable *gt)); +static void send_graft __P((struct gtable *gt)); +static void send_graft_ack __P((u_int32 src, u_int32 dst, + u_int32 origin, u_int32 grp)); +static void update_kernel __P((struct gtable *g)); +static char * scaletime __P((u_long t)); -/* - * Initialize the kernel table structure +/* + * Updates the ttl values for each vif. */ -void init_ktable() +static void +prun_add_ttls(gt) + struct gtable *gt; { - kernel_rtable = NULL; - kroutes = 0; + struct uvif *v; + vifi_t vifi; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (VIFM_ISSET(vifi, gt->gt_grpmems)) + gt->gt_ttls[vifi] = v->uv_threshold; + else + gt->gt_ttls[vifi] = 0; + } } /* + * checks for scoped multicast addresses + */ +#define GET_SCOPE(gt) { \ + register int _i; \ + if ((ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \ + for (_i = 0; _i < numvifs; _i++) \ + if (scoped_addr(_i, (gt)->gt_mcastgrp)) \ + VIFM_SET(_i, (gt)->gt_scope); \ + } + +int +scoped_addr(vifi, addr) + vifi_t vifi; + u_int32 addr; +{ + struct vif_acl *acl; + + for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next) + if ((addr & acl->acl_mask) == acl->acl_addr) + return 1; + + return 0; +} + +/* * Determine if mcastgrp has a listener on vifi */ -int grplst_mem(vifi, mcastgrp) +int +grplst_mem(vifi, mcastgrp) vifi_t vifi; - u_long mcastgrp; + u_int32 mcastgrp; { register struct listaddr *g; register struct uvif *v; - + v = &uvifs[vifi]; - + for (g = v->uv_groups; g != NULL; g = g->al_next) - if (mcastgrp == g->al_addr) + if (mcastgrp == g->al_addr) return 1; - + return 0; } /* - * Updates the ttl values for each vif. + * Finds the group entry with the specified source and netmask. + * If netmask is 0, it uses the route's netmask. + * + * Returns TRUE if found a match, and the global variable gtp is left + * pointing to entry before the found entry. + * Returns FALSE if no exact match found, gtp is left pointing to before + * the entry in question belongs, or is NULL if the it belongs at the + * head of the list. */ -void prun_add_ttls(kt) - struct ktable *kt; +int +find_src_grp(src, mask, grp) + u_int32 src; + u_int32 mask; + u_int32 grp; { - struct uvif *v; - vifi_t vifi; + struct gtable *gt; - for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if (VIFM_ISSET(vifi, kt->kt_grpmems)) - kt->kt_ttls[vifi] = v->uv_threshold; - else - kt->kt_ttls[vifi] = 0; + gtp = NULL; + gt = kernel_table; + while (gt != NULL) { + if (grp == gt->gt_mcastgrp && + (mask ? (gt->gt_route->rt_origin == src && + gt->gt_route->rt_originmask == mask) : + ((src & gt->gt_route->rt_originmask) == + gt->gt_route->rt_origin))) + return TRUE; + if (ntohl(grp) > ntohl(gt->gt_mcastgrp) || + (grp == gt->gt_mcastgrp && + (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) || + (mask == gt->gt_route->rt_originmask && + (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) { + gtp = gt; + gt = gt->gt_gnext; + } + else break; } + return FALSE; } /* - * checks for scoped multicast addresses + * Check if the neighbor supports pruning */ -#define GET_SCOPE(kt) { \ - register int _i; \ - if (((kt)->kt_mcastgrp & 0xff000000) == 0xef000000) \ - for (_i = 0; _i < numvifs; _i++) \ - if (scoped_addr(_i, (kt)->kt_mcastgrp)) \ - VIFM_SET(_i, (kt)->kt_scope); \ - } - -int scoped_addr(vifi, addr) +static int +pruning_neighbor(vifi, addr) vifi_t vifi; - u_long addr; + u_int32 addr; { - struct vif_acl *acl; + struct listaddr *n = neighbor_info(vifi, addr); + int vers; - for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next) - if ((addr & acl->acl_mask) == acl->acl_addr) - return 1; + if (n == NULL) + return 0; - return 0; + if (n->al_flags & NF_PRUNE) + return 1; + + /* + * Versions from 3.0 to 3.4 relied on the version number to identify + * that they could handle pruning. + */ + vers = NBR_VERS(n); + return (vers >= 0x0300 && vers <= 0x0304); } /* - * Add a new table entry for (origin, mcastgrp) + * Can the neighbor in question handle multicast traceroute? */ -void add_table_entry(origin, mcastgrp) - u_long origin; - u_long mcastgrp; +static int +can_mtrace(vifi, addr) + vifi_t vifi; + u_int32 addr; { - struct rtentry *r; - struct ktable *kt; + struct listaddr *n = neighbor_info(vifi, addr); + int vers; + + if (n == NULL) + return 0; + + if (n->al_flags & NF_MTRACE) + return 1; + + /* + * Versions 3.3 and 3.4 relied on the version number to identify + * that they could handle traceroute. + */ + vers = NBR_VERS(n); + return (vers >= 0x0303 && vers <= 0x0304); +} + +/* + * Returns the prune entry of the router, or NULL if none exists + */ +static struct ptable * +find_prune_entry(vr, pt) + u_int32 vr; + struct ptable *pt; +{ + while (pt) { + if (pt->pt_router == vr) + return pt; + pt = pt->pt_next; + } + + return NULL; +} + +/* + * Send a prune message to the dominant router for + * this source. + * + * Record an entry that a prune was sent for this group + */ +static void +send_prune(gt) + struct gtable *gt; +{ + struct ptable *pt; + char *p; int i; + int datalen; + u_int32 src; + u_int32 dst; + u_int32 tmp; + + /* Don't process any prunes if router is not pruning */ + if (pruning == 0) + return; + + /* Can't process a prune if we don't have an associated route */ + if (gt->gt_route == NULL) + return; - if ((kt = find_src_grp(origin, mcastgrp)) != NULL) { - log(LOG_DEBUG, 0, "kernel entry exists for (%s %s)", - inet_fmt(origin, s1), inet_fmt(mcastgrp, s2)); + /* Don't send a prune to a non-pruning router */ + if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway)) return; + + /* + * sends a prune message to the router upstream. + */ + src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr; + dst = gt->gt_route->rt_gateway; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + /* + * determine prune lifetime + */ + gt->gt_prsent_timer = gt->gt_timer; + for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) + if (pt->pt_timer < gt->gt_prsent_timer) + gt->gt_prsent_timer = pt->pt_timer; + + /* + * If we have a graft pending, cancel graft retransmission + */ + gt->gt_grftsnt = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_route->rt_origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_mcastgrp))[i]; + tmp = htonl(gt->gt_prsent_timer); + for (i = 0; i < 4; i++) + *p++ = ((char *)&(tmp))[i]; + datalen += 12; + + send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE, + htonl(MROUTED_LEVEL), datalen); + + log(LOG_DEBUG, 0, "sent prune for (%s %s)/%d on vif %d to %s", + inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + gt->gt_prsent_timer, gt->gt_route->rt_parent, + inet_fmt(gt->gt_route->rt_gateway, s3)); +} + +/* + * a prune was sent upstream + * so, a graft has to be sent to annul the prune + * set up a graft timer so that if an ack is not + * heard within that time, another graft request + * is sent out. + */ +static void +send_graft(gt) + struct gtable *gt; +{ + register char *p; + register int i; + int datalen; + u_int32 src; + u_int32 dst; + + /* Can't send a graft without an associated route */ + if (gt->gt_route == NULL) + return; + + src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr; + dst = gt->gt_route->rt_gateway; + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_route->rt_origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(gt->gt_mcastgrp))[i]; + datalen += 8; + + if (datalen != 0) { + send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT, + htonl(MROUTED_LEVEL), datalen); } + log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d", + inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent); +} - r = determine_route(origin); +/* + * Send an ack that a graft was received + */ +static void +send_graft_ack(src, dst, origin, grp) + u_int32 src; + u_int32 dst; + u_int32 origin; + u_int32 grp; +{ + register char *p; + register int i; + int datalen; - /* allocate space for the new entry */ - kt = (struct ktable *)malloc(sizeof(struct ktable)); - if (kt == NULL) - log(LOG_ERR, 0, "ran out of memory"); /* fatal */ + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(origin))[i]; + for (i = 0; i < 4; i++) + *p++ = ((char *)&(grp))[i]; + datalen += 8; + + send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK, + htonl(MROUTED_LEVEL), datalen); - kroutes++; + log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s", + inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3)); +} - /* add the new values in */ +/* + * Update the kernel cache with all the routes hanging off the group entry + */ +static void +update_kernel(g) + struct gtable *g; +{ + struct stable *st; + + for (st = g->gt_srctbl; st; st = st->st_next) + k_add_rg(st->st_origin, g); +} + +/**************************************************************************** + Functions that are used externally +****************************************************************************/ + +#ifdef SNMP +#include +#include "snmp.h" + +/* + * Find a specific group entry in the group table + */ +struct gtable * +find_grp(grp) + u_long grp; +{ + struct gtable *gt; + + for (gt = kernel_table; gt; gt = gt->gt_gnext) { + if (ntohl(grp) < ntohl(gt->gt_mcastgrp)) + break; + if (gt->gt_mcastgrp == grp) + return gt; + } + return NULL; +} + +/* + * Given a group entry and source, find the corresponding source table + * entry + */ +struct stable * +find_grp_src(gt, src) + struct gtable *gt; + u_long src; +{ + struct stable *st; + u_long grp = gt->gt_mcastgrp; + struct gtable *gtcurr; + + for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) { + for (st = gtcurr->gt_srctbl; st; st = st->st_next) + if (st->st_origin == src) + return st; + } + return NULL; +} + +/* + * Find next entry > specification + */ +int +next_grp_src_mask(gtpp, stpp, grp, src, mask) + struct gtable **gtpp; /* ordered by group */ + struct stable **stpp; /* ordered by source */ + u_long grp; + u_long src; + u_long mask; +{ + struct gtable *gt, *gbest = NULL; + struct stable *st, *sbest = NULL; + + /* Find first group entry >= grp spec */ + (*gtpp) = kernel_table; + while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp)) + (*gtpp)=(*gtpp)->gt_gnext; + if (!(*gtpp)) + return 0; /* no more groups */ + + for (gt = kernel_table; gt; gt=gt->gt_gnext) { + /* Since grps are ordered, we can stop when group changes from gbest */ + if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp) + break; + for (st = gt->gt_srctbl; st; st=st->st_next) { + + /* Among those entries > spec, find "lowest" one */ + if (((ntohl(gt->gt_mcastgrp)> ntohl(grp)) + || (ntohl(gt->gt_mcastgrp)==ntohl(grp) + && ntohl(st->st_origin)> ntohl(src)) + || (ntohl(gt->gt_mcastgrp)==ntohl(grp) + && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask))) + && (!gbest + || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp)) + || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp) + && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) { + gbest = gt; + sbest = st; + } + } + } + (*gtpp) = gbest; + (*stpp) = sbest; + return (*gtpp)!=0; +} + +/* + * Ensure that sg contains current information for the given group,source. + * This is fetched from the kernel as a unit so that counts for the entry + * are consistent, i.e. packet and byte counts for the same entry are + * read at the same time. + */ +void +refresh_sg(sg, gt, st) + struct sioc_sg_req *sg; + struct gtable *gt; + struct stable *st; +{ + static int lastq = -1; + + if (quantum != lastq || sg->src.s_addr!=st->st_origin + || sg->grp.s_addr!=gt->gt_mcastgrp) { + lastq = quantum; + sg->src.s_addr = st->st_origin; + sg->grp.s_addr = gt->gt_mcastgrp; + ioctl(udp_socket, SIOCGETSGCNT, (char *)sg); + } +} + +/* + * Return pointer to a specific route entry. This must be a separate + * function from find_route() which modifies rtp. + */ +struct rtentry * +snmp_find_route(src, mask) + register u_long src, mask; +{ + register struct rtentry *rt; + + for (rt = routing_table; rt; rt = rt->rt_next) { + if (src == rt->rt_origin && mask == rt->rt_originmask) + return rt; + } + return NULL; +} + +/* + * Find next route entry > specification + */ +int +next_route(rtpp, src, mask) + struct rtentry **rtpp; + u_long src; + u_long mask; +{ + struct rtentry *rt, *rbest = NULL; + + /* Among all entries > spec, find "lowest" one in order */ + for (rt = routing_table; rt; rt=rt->rt_next) { + if ((ntohl(rt->rt_origin) > ntohl(src) + || (ntohl(rt->rt_origin) == ntohl(src) + && ntohl(rt->rt_originmask) > ntohl(mask))) + && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin)) + || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin) + && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask)))) + rbest = rt; + } + (*rtpp) = rbest; + return (*rtpp)!=0; +} + +/* + * Given a routing table entry, and a vifi, find the next vifi/entry + */ +int +next_route_child(rtpp, src, mask, vifi) + struct rtentry **rtpp; + u_long src; + u_long mask; + vifi_t *vifi; /* vif at which to start looking */ +{ + struct rtentry *rt; + + /* Get (S,M) entry */ + if (!((*rtpp) = snmp_find_route(src,mask))) + if (!next_route(rtpp, src, mask)) + return 0; + + /* Continue until we get one with a valid next vif */ + do { + for (; (*rtpp)->rt_children && *vifirt_children)) + return 1; + *vifi = 0; + } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) ); + + return 0; +} + +/* + * Given a routing table entry, and a vifi, find the next entry + * equal to or greater than those + */ +int +next_child(gtpp, stpp, grp, src, mask, vifi) + struct gtable **gtpp; + struct stable **stpp; + u_long grp; + u_long src; + u_long mask; + vifi_t *vifi; /* vif at which to start looking */ +{ + struct stable *st; + + /* Get (G,S,M) entry */ + if (mask!=0xFFFFFFFF + || !((*gtpp) = find_grp(grp)) + || !((*stpp) = find_grp_src((*gtpp),src))) + if (!next_grp_src_mask(gtpp, stpp, grp, src, mask)) + return 0; + + /* Continue until we get one with a valid next vif */ + do { + for (; (*gtpp)->gt_route->rt_children && *vifigt_route->rt_children)) + return 1; + *vifi = 0; + } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp, + (*stpp)->st_origin, 0xFFFFFFFF) ); + + return 0; +} +#endif /* SNMP */ + +/* + * Initialize the kernel table structure + */ +void +init_ktable() +{ + kernel_table = NULL; + kernel_no_route = NULL; + kroutes = 0; +} + +/* + * Add a new table entry for (origin, mcastgrp) + */ +void +add_table_entry(origin, mcastgrp) + u_int32 origin; + u_int32 mcastgrp; +{ + struct rtentry *r; + struct gtable *gt,**gtnp,*prev_gt; + struct stable *st,**stnp; + int i; + + r = determine_route(origin); + prev_gt = NULL; if (r == NULL) { - kt->kt_origin = origin; - kt->kt_mcastgrp = mcastgrp; - kt->kt_originmask = 0xffffffff; - kt->kt_parent = NO_VIF; - kt->kt_gateway = 0; - kt->kt_children = 0; - kt->kt_leaves = 0; - kt->kt_timer = CACHE_LIFETIME(cache_lifetime); - kt->kt_grpmems = 0; - kt->kt_rlist = NULL; - kt->kt_prsent_timer = 0; - kt->kt_grftsnt = 0; - kt->kt_prun_count = 0; - kt->kt_scope = 0; + /* + * Look for it on the no_route table; if it is found then + * it will be detected as a duplicate below. + */ + for (gt = kernel_no_route; gt; gt = gt->gt_next) + if (mcastgrp == gt->gt_mcastgrp && + gt->gt_srctbl && gt->gt_srctbl->st_origin == origin) + break; + gtnp = &kernel_no_route; + } else { + gtnp = &r->rt_groups; + while ((gt = *gtnp) != NULL) { + if (gt->gt_mcastgrp >= mcastgrp) + break; + gtnp = >->gt_next; + prev_gt = gt; + } } - else { - kt->kt_origin = r->rt_origin; - kt->kt_mcastgrp = mcastgrp; - kt->kt_originmask = r->rt_originmask; - kt->kt_parent = r->rt_parent; - kt->kt_gateway = r->rt_gateway; - kt->kt_timer = CACHE_LIFETIME(cache_lifetime); - kt->kt_grpmems = 0; - kt->kt_rlist = NULL; - kt->kt_prsent_timer = 0; - kt->kt_grftsnt = 0; - kt->kt_prun_count = 0; - kt->kt_scope = 0; - VIFM_COPY(r->rt_children, kt->kt_children); - VIFM_COPY(r->rt_leaves, kt->kt_leaves); + if (gt == NULL || gt->gt_mcastgrp != mcastgrp) { + gt = (struct gtable *)malloc(sizeof(struct gtable)); + if (gt == NULL) + log(LOG_ERR, 0, "ran out of memory"); - /* obtain the multicast group membership list */ - for (i = 0; i < numvifs; i++) { - if (VIFM_ISSET(i, kt->kt_children) && - !(VIFM_ISSET(i, kt->kt_leaves))) - VIFM_SET(i, kt->kt_grpmems); + gt->gt_mcastgrp = mcastgrp; + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + time(>->gt_ctime); + gt->gt_grpmems = 0; + gt->gt_scope = 0; + gt->gt_prsent_timer = 0; + gt->gt_grftsnt = 0; + gt->gt_srctbl = NULL; + gt->gt_pruntbl = NULL; + gt->gt_route = r; +#ifdef RSRR + gt->gt_rsrr_cache = NULL; +#endif - if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, mcastgrp)) - VIFM_SET(i, kt->kt_grpmems); + if (r != NULL) { + /* obtain the multicast group membership list */ + for (i = 0; i < numvifs; i++) { + if (VIFM_ISSET(i, r->rt_children) && + !(VIFM_ISSET(i, r->rt_leaves))) + VIFM_SET(i, gt->gt_grpmems); + + if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp)) + VIFM_SET(i, gt->gt_grpmems); + } + GET_SCOPE(gt); + if (VIFM_ISSET(r->rt_parent, gt->gt_scope)) + gt->gt_scope = -1; + gt->gt_grpmems &= ~gt->gt_scope; + } else { + gt->gt_scope = -1; + gt->gt_grpmems = 0; } - GET_SCOPE(kt); - if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { - kt->kt_grpmems = 0; - kt->kt_scope = -1; /* make sure we don't forward */ + + /* update ttls */ + prun_add_ttls(gt); + + gt->gt_next = *gtnp; + *gtnp = gt; + if (gt->gt_next) + gt->gt_next->gt_prev = gt; + gt->gt_prev = prev_gt; + + if (r) { + if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) { + struct gtable *g; + + g = gtp ? gtp->gt_gnext : kernel_table; + log(LOG_WARNING, 0, "Entry for (%s %s) (rt:%x) exists (rt:%x)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), + r, g->gt_route); + } else { + if (gtp) { + gt->gt_gnext = gtp->gt_gnext; + gt->gt_gprev = gtp; + gtp->gt_gnext = gt; + } else { + gt->gt_gnext = kernel_table; + gt->gt_gprev = NULL; + kernel_table = gt; + } + if (gt->gt_gnext) + gt->gt_gnext->gt_gprev = gt; + } + } else { + gt->gt_gnext = gt->gt_prev = NULL; } - else - kt->kt_grpmems &= ~kt->kt_scope; } - /* update the kernel_rtable pointer */ - kt->kt_next = kernel_rtable; - kernel_rtable = kt; + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + if (ntohl(st->st_origin) >= ntohl(origin)) + break; + stnp = &st->st_next; + } - /* update ttls and add entry into kernel */ - prun_add_ttls(kt); - k_add_rg(kt); + if (st == NULL || st->st_origin != origin) { + st = (struct stable *)malloc(sizeof(struct stable)); + if (st == NULL) + log(LOG_ERR, 0, "ran out of memory"); - log(LOG_DEBUG, 0, "add entry (%s %s) vif-list:%x", - inet_fmt(kt->kt_origin, s1), - inet_fmt(kt->kt_mcastgrp, s2), - kt->kt_grpmems); + st->st_origin = origin; + st->st_pktcnt = 0; + st->st_next = *stnp; + *stnp = st; + } else { + log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)", + inet_fmt(origin, s1), inet_fmt(mcastgrp, s2)); + return; + } + kroutes++; + k_add_rg(origin, gt); + + log(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d", + inet_fmt(origin, s1), + inet_fmt(mcastgrp, s2), + gt->gt_grpmems, r ? r->rt_parent : -1); + /* If there are no leaf vifs * which have this group, then - * mark this src-grp as a prune candidate. - * One thing to do is to check if parent vif is the source - * and not send a prune to that. + * mark this src-grp as a prune candidate. */ - if (!kt->kt_grpmems && kt->kt_gateway) - send_prune(kt); + if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway) + send_prune(gt); } /* * An mrouter has gone down and come up on an interface * Forward on that interface immediately */ -void reset_neighbor_state(vifi, addr) +void +reset_neighbor_state(vifi, addr) vifi_t vifi; - u_long addr; + u_int32 addr; { - struct ktable *prev_kt, *kt; - struct prunlst *prev_krl, *krl; + struct rtentry *r; + struct gtable *g; + struct ptable *pt, *prev_pt; + struct stable *st, *prev_st; + + for (g = kernel_table; g; g = g->gt_gnext) { + r = g->gt_route; - /* Check each src-grp entry to see if it was pruned on that interface - If so, forward on that interface */ - for (prev_kt = (struct ktable *)&kernel_rtable, - kt = kernel_rtable; kt; - prev_kt = kt, kt = kt->kt_next) { - for (prev_krl = (struct prunlst *)&kt->kt_rlist, - krl = prev_krl->rl_next; - krl; - prev_krl = krl, krl = krl->rl_next) { - if (krl->rl_router == addr) { - prev_krl->rl_next = krl->rl_next; - free(krl); - krl = prev_krl; - kt->kt_prun_count--; - } - } - /* * If neighbor was the parent, remove the prune sent state * Don't send any grafts upstream. */ - if (vifi == kt->kt_parent) { - k_del_rg(kt); - prev_kt->kt_next = kt->kt_next; - while (krl = kt->kt_rlist) { - kt->kt_rlist = krl->rl_next; - free((char *)krl); + if (vifi == r->rt_parent) { + if (addr == r->rt_gateway) { + log(LOG_DEBUG, 0, "reset_neighbor_state del prunes (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + + pt = g->gt_pruntbl; + while (pt) { + /* + * Expire prune, send again on this vif. + */ + VIFM_SET(pt->pt_vifi, g->gt_grpmems); + prev_pt = pt; + pt = prev_pt->pt_next; + free(prev_pt); + } + g->gt_pruntbl = NULL; + + st = g->gt_srctbl; + while (st) { + log(LOG_DEBUG, 0, "reset_neighbor_state del sg (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "reset_neighbor_state trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + } + kroutes--; + prev_st = st; + st = prev_st->st_next; + free(prev_st); + } + g->gt_srctbl = NULL; + /* + * Keep the group entries themselves around since the + * state will likely just come right back, and if not, + * the group entries will time out with no kernel entries + * and no prune state. + */ + g->gt_prsent_timer = 0; + g->gt_grftsnt = 0; } - free((char *)kt); - kt = prev_kt; - kroutes--; - continue; - } + } else { + /* + * Neighbor was not the parent, send grafts to join the groups + */ + if (g->gt_prsent_timer) { + g->gt_grftsnt = 1; + send_graft(g); + g->gt_prsent_timer = 0; + } - /* - * Neighbor was not the parent, send grafts to join the groups - */ - if (kt->kt_prsent_timer) { - kt->kt_grftsnt = 1; - send_graft(kt); - kt->kt_prsent_timer = 0; - } + /* + * Remove any prunes that this router has sent us. + */ + prev_pt = (struct ptable *)&g->gt_pruntbl; + for (pt = g->gt_pruntbl; pt; pt = pt->pt_next) { + if (pt->pt_vifi == vifi && pt->pt_router == addr) { + prev_pt->pt_next = pt->pt_next; + free(pt); + } else + prev_pt = pt; + } - if (!VIFM_ISSET(vifi, kt->kt_grpmems)) { - if (VIFM_ISSET(vifi, kt->kt_children) && - !(VIFM_ISSET(vifi, kt->kt_leaves))) - VIFM_SET(vifi, kt->kt_grpmems); + /* + * And see if we want to forward again. + */ + if (!VIFM_ISSET(vifi, g->gt_grpmems)) { + if (VIFM_ISSET(vifi, r->rt_children) && + !(VIFM_ISSET(vifi, r->rt_leaves))) + VIFM_SET(vifi, g->gt_grpmems); + + if (VIFM_ISSET(vifi, r->rt_leaves) && + grplst_mem(vifi, g->gt_mcastgrp)) + VIFM_SET(vifi, g->gt_grpmems); + + g->gt_grpmems &= ~g->gt_scope; + prun_add_ttls(g); - if (VIFM_ISSET(vifi, kt->kt_leaves) && - grplst_mem(vifi, kt->kt_mcastgrp)) - VIFM_SET(vifi, kt->kt_grpmems); + /* Update kernel state */ + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ - kt->kt_grpmems &= ~kt->kt_scope; - prun_add_ttls(kt); - k_add_rg(kt); + log(LOG_DEBUG, 0, "reset member state (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + } } } } /* * Delete table entry from the kernel * del_flag determines how many entries to delete */ -void del_table_entry(r, mcastgrp, del_flag) +void +del_table_entry(r, mcastgrp, del_flag) struct rtentry *r; - u_long mcastgrp; + u_int32 mcastgrp; u_int del_flag; { - struct ktable *kt, *prev_kt; - struct prunlst *krl; - + struct gtable *g, *prev_g; + struct stable *st, *prev_st; + struct ptable *pt, *prev_pt; + if (del_flag == DEL_ALL_ROUTES) { - for (prev_kt = (struct ktable *)&kernel_rtable; - kt = prev_kt->kt_next; - prev_kt = kt) { - if ((kt->kt_origin & r->rt_originmask) == r->rt_origin) { - log(LOG_DEBUG, 0, "delete all rtes %s", - inet_fmt(kt->kt_origin, s1)); - - k_del_rg(kt); - - /* free prun list entries */ - while (kt->kt_rlist) { - krl = kt->kt_rlist; - kt->kt_rlist = krl->rl_next; - free((char *)krl); + g = r->rt_groups; + while (g) { + log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + st = g->gt_srctbl; + while (st) { + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "del_table_entry trying to delete (%s, %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); } - - /* free the source mcastgrp entry */ - prev_kt->kt_next = kt->kt_next; - free((char *)kt); kroutes--; - kt = prev_kt; + prev_st = st; + st = st->st_next; + free(prev_st); } + g->gt_srctbl = NULL; + + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; + } + g->gt_pruntbl = NULL; + + if (g->gt_gnext) + g->gt_gnext->gt_gprev = g->gt_gprev; + if (g->gt_gprev) + g->gt_gprev->gt_gnext = g->gt_gnext; + else + kernel_table = g->gt_gnext; + + prev_g = g->gt_next; +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,0); + rsrr_cache_clean(g); +#endif /* RSRR */ + free(g); + g = prev_g; } + r->rt_groups = NULL; } - + + /* + * Dummy routine - someday this may be needed, so it is just there + */ if (del_flag == DEL_RTE_GROUP) { - for (prev_kt = (struct ktable *)&kernel_rtable; - (prev_kt) && (kt = prev_kt->kt_next); - prev_kt = kt) { - if ((kt->kt_origin & r->rt_originmask) == r->rt_origin && - kt->kt_mcastgrp == mcastgrp) { - log(LOG_DEBUG, 0, "delete (%s, %s)", - inet_fmt(kt->kt_origin, s1), inet_fmt(mcastgrp, s2)); + prev_g = (struct gtable *)&r->rt_groups; + for (g = r->rt_groups; g; g = g->gt_next) { + if (g->gt_mcastgrp == mcastgrp) { + log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2)); + st = g->gt_srctbl; + while (st) { + if (k_del_rg(st->st_origin, g) < 0) { + log(LOG_WARNING, errno, + "del_table_entry trying to delete (%s, %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(g->gt_mcastgrp, s2)); + } + kroutes--; + prev_st = st->st_next; + free(st); + st = prev_st; + } + g->gt_srctbl = NULL; - k_del_rg(kt); - - /* free prun list entries */ - while (kt->kt_rlist) { - krl = kt->kt_rlist; - kt->kt_rlist = krl->rl_next; - free((char *)krl); + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; } + g->gt_pruntbl = NULL; - /* free the source mcastgrp entry */ - prev_kt->kt_next = kt->kt_next; - free((char *)kt); - kroutes--; - break; + if (g->gt_gnext) + g->gt_gnext->gt_gprev = g->gt_gprev; + if (g->gt_gprev) + g->gt_gprev->gt_gnext = g->gt_gnext; + else + kernel_table = g->gt_gnext; + + if (prev_g != (struct gtable *)&r->rt_groups) + g->gt_next->gt_prev = prev_g; + else + g->gt_next->gt_prev = NULL; + prev_g->gt_next = g->gt_next; + +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,0); + rsrr_cache_clean(g); +#endif /* RSRR */ + free(g); + g = prev_g; + } else { + prev_g = g; } } } } /* * update kernel table entry when a route entry changes */ -void update_table_entry(r) +void +update_table_entry(r) struct rtentry *r; { - struct ktable *kt; - struct prunlst *krl; + struct gtable *g; + struct ptable *pt, *prev_pt; int i; - int changed; - for (kt = kernel_rtable; kt; kt = kt->kt_next) - if ((kt->kt_origin & r->rt_originmask)== r->rt_origin) { - changed = 0; + for (g = r->rt_groups; g; g = g->gt_next) { + pt = g->gt_pruntbl; + while (pt) { + prev_pt = pt->pt_next; + free(pt); + pt = prev_pt; + } + g->gt_pruntbl = NULL; - if (kt->kt_leaves != r->rt_leaves) - changed |= 0x1; - if (kt->kt_children != r->rt_children) - changed |= 0x2; - if (kt->kt_parent != r->rt_parent) - changed |= 0x4; + g->gt_grpmems = 0; - if (!changed) - continue; + /* obtain the multicast group membership list */ + for (i = 0; i < numvifs; i++) { + if (VIFM_ISSET(i, r->rt_children) && + !(VIFM_ISSET(i, r->rt_leaves))) + VIFM_SET(i, g->gt_grpmems); + + if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, g->gt_mcastgrp)) + VIFM_SET(i, g->gt_grpmems); + } + if (VIFM_ISSET(r->rt_parent, g->gt_scope)) + g->gt_scope = -1; + g->gt_grpmems &= ~g->gt_scope; - log(LOG_DEBUG, 0, "updating entry: (%s %s) code:%x", - inet_fmt(kt->kt_origin, s1), - inet_fmt(kt->kt_mcastgrp, s2), changed); + log(LOG_DEBUG, 0, "updating cache entries (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), + g->gt_grpmems); - /* free prun list entries */ - while (kt->kt_rlist) { - krl = kt->kt_rlist; - kt->kt_rlist = krl->rl_next; - free((char *)krl); - } + if (g->gt_grpmems && g->gt_prsent_timer) { + g->gt_grftsnt = 1; + send_graft(g); + g->gt_prsent_timer = 0; + } - kt->kt_parent = r->rt_parent; - kt->kt_gateway = r->rt_gateway; - kt->kt_grpmems = 0; - kt->kt_prun_count = 0; - VIFM_COPY(r->rt_children, kt->kt_children); - VIFM_COPY(r->rt_leaves, kt->kt_leaves); + /* update ttls and add entry into kernel */ + prun_add_ttls(g); + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ - /* obtain the multicast group membership list */ - for (i = 0; i < numvifs; i++) { - if (VIFM_ISSET(i, kt->kt_children) && - !(VIFM_ISSET(i, kt->kt_leaves))) - VIFM_SET(i, kt->kt_grpmems); - - if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, kt->kt_mcastgrp)) - VIFM_SET(i, kt->kt_grpmems); - } - if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { - kt->kt_grpmems = 0; - kt->kt_scope = -1; - } - else - kt->kt_grpmems &= ~kt->kt_scope; - - if (kt->kt_grpmems && kt->kt_prsent_timer) { - kt->kt_grftsnt = 1; - send_graft(kt); - kt->kt_prsent_timer = 0; - } - - /* update ttls and add entry into kernel */ - prun_add_ttls(kt); - k_add_rg(kt); - - if (!kt->kt_grpmems && kt->kt_gateway) { - kt->kt_timer = CACHE_LIFETIME(cache_lifetime); - send_prune(kt); - } + /* Check if we want to prune this group */ + if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) { + g->gt_timer = CACHE_LIFETIME(cache_lifetime); + send_prune(g); } + } } - - /* * set the forwarding flag for all mcastgrps on this vifi */ -void update_lclgrp(vifi, mcastgrp) +void +update_lclgrp(vifi, mcastgrp) vifi_t vifi; - u_long mcastgrp; + u_int32 mcastgrp; { - struct ktable *kt; - - log(LOG_DEBUG, 0, "group %s joined at vif %d", + struct rtentry *r; + struct gtable *g; + + log(LOG_DEBUG, 0, "group %s joined on vif %d", inet_fmt(mcastgrp, s1), vifi); + + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; - for (kt = kernel_rtable; kt; kt = kt->kt_next) - if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children)) { - VIFM_SET(vifi, kt->kt_grpmems); - kt->kt_grpmems &= ~kt->kt_scope; - if (kt->kt_grpmems == 0) + r = g->gt_route; + if (g->gt_mcastgrp == mcastgrp && + VIFM_ISSET(vifi, r->rt_children)) { + + VIFM_SET(vifi, g->gt_grpmems); + g->gt_grpmems &= ~g->gt_scope; + if (g->gt_grpmems == 0) continue; - prun_add_ttls(kt); - k_add_rg(kt); + + prun_add_ttls(g); + log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ } + } } /* * reset forwarding flag for all mcastgrps on this vifi */ -void delete_lclgrp(vifi, mcastgrp) +void +delete_lclgrp(vifi, mcastgrp) vifi_t vifi; - u_long mcastgrp; + u_int32 mcastgrp; { - struct ktable *kt; - - log(LOG_DEBUG, 0, "group %s left at vif %d", + struct rtentry *r; + struct gtable *g; + + log(LOG_DEBUG, 0, "group %s left on vif %d", inet_fmt(mcastgrp, s1), vifi); + + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; - for (kt = kernel_rtable; kt; kt = kt->kt_next) - if (kt->kt_mcastgrp == mcastgrp) { - struct listaddr *vr; + if (g->gt_mcastgrp == mcastgrp) { int stop_sending = 1; - for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) - if (no_entry_exists(vr->al_addr, kt)) { - stop_sending = 0; - break; - } + r = g->gt_route; + /* + * If this is not a leaf, then we have router neighbors on this + * vif. Only turn off forwarding if they have all pruned. + */ + if (!VIFM_ISSET(vifi, r->rt_leaves)) { + struct listaddr *vr; + for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) + if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) { + stop_sending = 0; + break; + } + } + if (stop_sending) { - VIFM_CLR(vifi, kt->kt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); + VIFM_CLR(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); + prun_add_ttls(g); + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ + /* * If there are no more members of this particular group, * send prune upstream */ - if (kt->kt_grpmems == NULL && kt->kt_gateway) - send_prune(kt); + if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) + send_prune(g); } } + } } /* - * Check if the neighbor supports pruning - */ -int pruning_neighbor(vifi, addr) - vifi_t vifi; - u_long addr; -{ - struct listaddr *u; - - for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) - if ((u->al_addr == addr) && (u->al_pv > 2)) - return 1; - - return 0; -} - -/* - * Send a prune message to the upstream router - * given by the kt->kt_gateway argument. The origin and - * multicast group can be determined from the kt - * structure. - * - * Also, record an entry that a prune was sent for this group - */ -void send_prune(kt) - struct ktable *kt; -{ - struct prunlst *krl; - char *p; - int i; - int datalen; - u_long src; - u_long dst; - - /* Don't process any prunes if router is not pruning */ - if (pruning == 0) - return; - - /* Don't send a prune to a non-pruning router */ - if (!pruning_neighbor(kt->kt_parent, kt->kt_gateway)) - return; - - /* - * sends a prune message to the router upstream. - */ - src = uvifs[kt->kt_parent].uv_lcl_addr; - dst = kt->kt_gateway; - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - - /* - * determine prune lifetime - */ - kt->kt_prsent_timer = kt->kt_timer; - for (krl = kt->kt_rlist; krl; krl = krl->rl_next) - if (krl->rl_timer < kt->kt_prsent_timer) - kt->kt_prsent_timer = krl->rl_timer; - - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_origin))[i]; - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_mcastgrp))[i]; -#if BYTE_ORDER == BIG_ENDIAN - for (i = 0; i < 4; i++) -#endif -#if BYTE_ORDER == LITTLE_ENDIAN - for (i = 3; i >= 0; i--) -#endif - *p++ = ((char *)&(kt->kt_prsent_timer))[i]; - datalen += 12; - - send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE, - htonl(MROUTED_LEVEL), datalen); - - /* log(LOG_DEBUG, 0, "send prune for src:%x, grp:%x up to %x", - kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway);*/ -} - -/* * Takes the prune message received and then strips it to * determine the (src, grp) pair to be pruned. * * Adds the router to the (src, grp) entry then. * * Determines if further packets have to be sent down that vif * * Determines if a corresponding prune message has to be generated */ -void accept_prune(src, dst, p, datalen) - u_long src; - u_long dst; +void +accept_prune(src, dst, p, datalen) + u_int32 src; + u_int32 dst; char *p; int datalen; { - u_long prun_src; - u_long prun_dst; - u_long prun_tmr; + u_int32 prun_src; + u_int32 prun_grp; + u_int32 prun_tmr; vifi_t vifi; int i; - int stop_sending; - struct ktable *kt; - struct prunlst *pr_recv; + int stop_sending; + struct rtentry *r; + struct gtable *g; + struct ptable *pt; struct listaddr *vr; - + /* Don't process any prunes if router is not pruning */ if (pruning == 0) return; - + if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, "ignoring prune report from non-neighbor %s", inet_fmt(src, s1)); return; } - + /* Check if enough data is present */ - if (datalen < 12) { - log(LOG_WARNING, 0, - "non-decipherable prune from %s", - inet_fmt(src, s1)); - return; - } - + if (datalen < 12) + { + log(LOG_WARNING, 0, + "non-decipherable prune from %s", + inet_fmt(src, s1)); + return; + } + for (i = 0; i< 4; i++) ((char *)&prun_src)[i] = *p++; for (i = 0; i< 4; i++) - ((char *)&prun_dst)[i] = *p++; -#if BYTE_ORDER == BIG_ENDIAN + ((char *)&prun_grp)[i] = *p++; for (i = 0; i< 4; i++) -#endif -#if BYTE_ORDER == LITTLE_ENDIAN - for (i = 3; i >= 0; i--) -#endif ((char *)&prun_tmr)[i] = *p++; + prun_tmr = ntohl(prun_tmr); + + log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d", + inet_fmt(src, s1), vifi, + inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr); - kt = find_src_grp(prun_src, prun_dst); + /* + * Find the subnet for the prune + */ + if (find_src_grp(prun_src, 0, prun_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + r = g->gt_route; - if (kt == NULL) - { - log(LOG_WARNING, 0, "prune message received incorrectly"); + if (!VIFM_ISSET(vifi, r->rt_children)) { + log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)", + inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3)); return; } - - if (!VIFM_ISSET(vifi, kt->kt_children)) - { - log(LOG_INFO, 0, - "ignoring prune report from non-child %s", inet_fmt(src, s1)); + if (VIFM_ISSET(vifi, g->gt_scope)) { + log(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)", + inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3)); return; } - if (VIFM_ISSET(vifi, kt->kt_scope)) { - log(LOG_INFO, 0, - "ignoring prune report from %s on scoped vif %d", - inet_fmt(src, s1), vifi); - return; - } - /* check if prune has been received from this source */ - if (!no_entry_exists(src, kt)) - { - log(LOG_INFO, 0, "duplicate prune from %s", inet_fmt(src, s1)); - return; + if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) { + /* + * If it's about to expire, then it's only still around because + * of timer granularity, so don't warn about it. + */ + if (pt->pt_timer > 10) { + log(LOG_WARNING, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x", + "duplicate prune received on vif", + vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2), + inet_fmt(prun_grp, s3), prun_tmr, + "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems); + } + pt->pt_timer = prun_tmr; + } else { + /* allocate space for the prune structure */ + pt = (struct ptable *)(malloc(sizeof(struct ptable))); + if (pt == NULL) + log(LOG_ERR, 0, "pt: ran out of memory"); + + pt->pt_vifi = vifi; + pt->pt_router = src; + pt->pt_timer = prun_tmr; + + pt->pt_next = g->gt_pruntbl; + g->gt_pruntbl = pt; } - log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s) tmr %d", - inet_fmt(src, s1), vifi, - inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3), prun_tmr); + /* Refresh the group's lifetime */ + g->gt_timer = CACHE_LIFETIME(cache_lifetime); + if (g->gt_timer < prun_tmr) + g->gt_timer = prun_tmr; + + /* + * check if any more packets need to be sent on the + * vif which sent this message + */ + stop_sending = 1; + for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next) + if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) { + stop_sending = 0; + break; + } - /* allocate space for the prune structure */ - pr_recv = (struct prunlst *)(malloc(sizeof(struct prunlst))); + if (stop_sending && !grplst_mem(vifi, prun_grp)) { + VIFM_CLR(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems); - if (pr_recv == NULL) - log(LOG_ERR, 0, "pr_recv: ran out of memory"); - - pr_recv->rl_vifi = vifi; - pr_recv->rl_router = src; - pr_recv->rl_timer = prun_tmr; - - /* - * add this prune message to the list of prunes received - * for this src group pair - */ - pr_recv->rl_next = kt->kt_rlist; - kt->kt_rlist = pr_recv; - - kt->kt_prun_count++; - kt->kt_timer = CACHE_LIFETIME(cache_lifetime); - if (kt->kt_timer < prun_tmr) - kt->kt_timer = prun_tmr; - - /* - * check if any more packets need to be sent on the - * vif which sent this message - */ - for (vr = uvifs[vifi].uv_neighbors, stop_sending = 1; - vr; vr = vr->al_next) - if (no_entry_exists(vr->al_addr, kt)) { - stop_sending = 0; - break; + prun_add_ttls(g); + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ } - if (stop_sending && !grplst_mem(vifi, prun_dst)) { - VIFM_CLR(vifi, kt->kt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); + /* + * check if all the child routers have expressed no interest + * in this group and if this group does not exist in the + * interface + * Send a prune message then upstream + */ + if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) { + send_prune(g); + } + } else { + /* + * There is no kernel entry for this group. Therefore, we can + * simply ignore the prune, as we are not forwarding this traffic + * downstream. + */ + log(LOG_DEBUG, 0, "%s (%s %s)/%d from %s", + "prune message received with no kernel entry for", + inet_fmt(prun_src, s1), inet_fmt(prun_grp, s2), + prun_tmr, inet_fmt(src, s3)); + return; } - - /* - * check if all the child routers have expressed no interest - * in this group and if this group does not exist in the - * interface - * Send a prune message then upstream - */ - if(kt->kt_grpmems == NULL && kt->kt_gateway) { - log(LOG_DEBUG, 0, "snt prun up %d %d", kt->kt_prun_count, rtr_cnt(kt)); - send_prune(kt); - } } /* - * Returns 1 if router vr is not present in the prunlist of kt - */ -int no_entry_exists(vr, kt) - u_long vr; - struct ktable *kt; -{ - struct prunlst *krl; - - for (krl = kt->kt_rlist; krl; krl = krl->rl_next) - if (krl->rl_router == vr) - return 0; - - return 1; -} - -/* - * Finds the entry for the source group pair in the table - */ -struct ktable *find_src_grp(src, grp) - u_long src; - u_long grp; -{ - struct ktable *kt; - - for (kt = kernel_rtable; kt; kt = kt->kt_next) - if ((kt->kt_origin == (src & kt->kt_originmask)) && - (kt->kt_mcastgrp == grp)) - return kt; - - return NULL; -} - -/* - * scans through the neighbor list of this router and then - * determines the total no. of child routers present - */ -int rtr_cnt(kt) - struct ktable *kt; -{ - int ri; - int rcount = 0; - struct listaddr *u; - - for (ri = 0; ri < numvifs; ri++) - if (VIFM_ISSET(ri, kt->kt_children)) - for(u = uvifs[ri].uv_neighbors; u; u = u->al_next) - rcount++; - - return rcount; -} - -/* * Checks if this mcastgrp is present in the kernel table * If so and if a prune was sent, it sends a graft upwards */ -void chkgrp_graft(vifi, mcastgrp) +void +chkgrp_graft(vifi, mcastgrp) vifi_t vifi; - u_long mcastgrp; + u_int32 mcastgrp; { - struct ktable *kt; + struct rtentry *r; + struct gtable *g; - for (kt = kernel_rtable; kt; kt = kt->kt_next) - if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children)) - if (kt->kt_prsent_timer) { - VIFM_SET(vifi, kt->kt_grpmems); + for (g = kernel_table; g; g = g->gt_gnext) { + if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp)) + break; + + r = g->gt_route; + if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children)) + if (g->gt_prsent_timer) { + VIFM_SET(vifi, g->gt_grpmems); + /* * If the vif that was joined was a scoped vif, * ignore it ; don't graft back */ - kt->kt_grpmems &= ~kt->kt_scope; - if (kt->kt_grpmems == 0) + g->gt_grpmems &= ~g->gt_scope; + if (g->gt_grpmems == 0) continue; /* set the flag for graft retransmission */ - kt->kt_grftsnt = 1; - + g->gt_grftsnt = 1; + /* send graft upwards */ - send_graft(kt); - + send_graft(g); + /* reset the prune timer and update cache timer*/ - kt->kt_prsent_timer = 0; - kt->kt_timer = max_prune_lifetime; + g->gt_prsent_timer = 0; + g->gt_timer = max_prune_lifetime; + + log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); + prun_add_ttls(g); + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ } + } } /* determine the multicast group and src - * - * if it does, then determine if a prune was sent + * + * if it does, then determine if a prune was sent * upstream. * if prune sent upstream, send graft upstream and send * ack downstream. - * + * * if no prune sent upstream, change the forwarding bit * for this interface and send ack downstream. * - * if no entry exists for this group just ignore the message - * [this may not be the right thing to do. but lets see what - * happens for the time being and then we might decide to do - * a modification to the code depending on the type of behaviour - * that we see in this] + * if no entry exists for this group send ack downstream. */ -void accept_graft(src, dst, p, datalen) - u_long src; - u_long dst; +void +accept_graft(src, dst, p, datalen) + u_int32 src; + u_int32 dst; char *p; int datalen; { vifi_t vifi; - u_long prun_src; - u_long prun_dst; - struct ktable *kt; + u_int32 graft_src; + u_int32 graft_grp; int i; - struct prunlst *krl; - struct prunlst *prev_krl; - + struct rtentry *r; + struct gtable *g; + struct ptable *pt, **ptnp; + if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, "ignoring graft from non-neighbor %s", inet_fmt(src, s1)); return; } - + if (datalen < 8) { log(LOG_WARNING, 0, "received non-decipherable graft from %s", inet_fmt(src, s1)); return; } - + for (i = 0; i< 4; i++) - ((char *)&prun_src)[i] = *p++; + ((char *)&graft_src)[i] = *p++; for (i = 0; i< 4; i++) - ((char *)&prun_dst)[i] = *p++; - + ((char *)&graft_grp)[i] = *p++; + log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)", inet_fmt(src, s1), vifi, - inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3)); - - kt = find_src_grp(prun_src, prun_dst); - if (kt == NULL) { - log(LOG_DEBUG, 0, "incorrect graft received from %s", inet_fmt(src, s1)); - return; - } - - if (VIFM_ISSET(vifi, kt->kt_scope)) { - log(LOG_INFO, 0, - "incorrect graft received from %s on scoped vif %d", - inet_fmt(src, s1), vifi); - return; - } - /* remove prune entry from the list - * allow forwarding on that vif, make change in the kernel + inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3)); + + /* + * Find the subnet for the graft */ - for (prev_krl = (struct prunlst *)&kt->kt_rlist; - krl = prev_krl->rl_next; - prev_krl = krl) - if ((krl->rl_vifi) == vifi && (krl->rl_router == src)) { - prev_krl->rl_next = krl->rl_next; - free((char *)krl); - krl = prev_krl; + if (find_src_grp(graft_src, 0, graft_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + r = g->gt_route; - kt->kt_prun_count--; - VIFM_SET(vifi, kt->kt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); - break; + if (VIFM_ISSET(vifi, g->gt_scope)) { + log(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)", + inet_fmt(src, s1), inet_fmt(graft_src, s2), + inet_fmt(graft_grp, s3)); + return; } - /* send ack downstream */ - send_graft_ack(kt, src); - kt->kt_timer = max_prune_lifetime; + ptnp = &g->gt_pruntbl; + while ((pt = *ptnp) != NULL) { + if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) { + *ptnp = pt->pt_next; + free(pt); - if (kt->kt_prsent_timer) { - /* set the flag for graft retransmission */ - kt->kt_grftsnt = 1; + VIFM_SET(vifi, g->gt_grpmems); + log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems); - /* send graft upwards */ - send_graft(kt); + prun_add_ttls(g); + update_kernel(g); +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(g,1); +#endif /* RSRR */ + break; + } else { + ptnp = &pt->pt_next; + } + } - /* reset the prune sent timer */ - kt->kt_prsent_timer = 0; - } -} + /* send ack downstream */ + send_graft_ack(dst, src, graft_src, graft_grp); + g->gt_timer = max_prune_lifetime; + + if (g->gt_prsent_timer) { + /* set the flag for graft retransmission */ + g->gt_grftsnt = 1; -/* - * Send an ack that a graft was received - */ -void send_graft_ack(kt, to) - struct ktable *kt; - u_long to; -{ - register char *p; - register int i; - int datalen; - u_long src; - u_long dst; + /* send graft upwards */ + send_graft(g); - src = uvifs[kt->kt_parent].uv_lcl_addr; - dst = to; - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_origin))[i]; - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_mcastgrp))[i]; - datalen += 8; - - send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK, - htonl(MROUTED_LEVEL), datalen); - - log(LOG_DEBUG, 0, "sent graft ack (%s, %s) to %s", - inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2), - inet_fmt(dst, s3)); - -} - -/* - * a prune was sent upstream - * so, a graft has to be sent to annul the prune - * set up a graft timer so that if an ack is not - * heard within that time, another graft request - * is sent out. - */ -void send_graft(kt) - struct ktable *kt; -{ - register char *p; - register int i; - int datalen; - u_long src; - u_long dst; - - src = uvifs[kt->kt_parent].uv_lcl_addr; - dst = kt->kt_gateway; - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_origin))[i]; - for (i = 0; i < 4; i++) - *p++ = ((char *)&(kt->kt_mcastgrp))[i]; - datalen += 8; - - if (datalen != 0) { - send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT, - htonl(MROUTED_LEVEL), datalen); + /* reset the prune sent timer */ + g->gt_prsent_timer = 0; + } + } else { + /* + * We have no state for the source and group in question. + * We can simply acknowledge the graft, since we know + * that we have no prune state, and grafts are requests + * to remove prune state. + */ + send_graft_ack(dst, src, graft_src, graft_grp); + log(LOG_DEBUG, 0, "%s (%s %s) from %s", + "graft received with no kernel entry for", + inet_fmt(graft_src, s1), inet_fmt(graft_grp, s2), + inet_fmt(src, s3)); + return; } - log(LOG_DEBUG, 0, "sent graft (%s, %s) to %s", - inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2), - inet_fmt(kt->kt_gateway, s3)); } /* - * find out which group is involved first of all + * find out which group is involved first of all * then determine if a graft was sent. * if no graft sent, ignore the message - * if graft was sent and the ack is from the right - * source, remove the graft timer so that we don't + * if graft was sent and the ack is from the right + * source, remove the graft timer so that we don't * have send a graft again */ -void accept_g_ack(src, dst, p, datalen) - u_long src; - u_long dst; +void +accept_g_ack(src, dst, p, datalen) + u_int32 src; + u_int32 dst; char *p; int datalen; { + struct gtable *g; vifi_t vifi; - u_long grft_src; - u_long grft_dst; - struct ktable *kt; + u_int32 grft_src; + u_int32 grft_grp; int i; - + if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, - "ignoring graft ack report from non-neighbor %s", + "ignoring graft ack from non-neighbor %s", inet_fmt(src, s1)); return; } - - if (datalen < 8) { + + if (datalen < 0 || datalen > 8) { log(LOG_WARNING, 0, - "received non-decipherable graft ack report from %s", + "received non-decipherable graft ack from %s", inet_fmt(src, s1)); return; } - + for (i = 0; i< 4; i++) ((char *)&grft_src)[i] = *p++; for (i = 0; i< 4; i++) - ((char *)&grft_dst)[i] = *p++; - - log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s %s)", + ((char *)&grft_grp)[i] = *p++; + + log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s, %s)", inet_fmt(src, s1), vifi, - inet_fmt(grft_src, s2), inet_fmt(grft_dst, s3)); - - kt = find_src_grp(grft_src, grft_dst); - - if (kt == NULL) { - log(LOG_WARNING, 0, "received wrong graft ack from %s", inet_fmt(src, s1)); + inet_fmt(grft_src, s2), inet_fmt(grft_grp, s3)); + + /* + * Find the subnet for the graft ack + */ + if (find_src_grp(grft_src, 0, grft_grp)) { + g = gtp ? gtp->gt_gnext : kernel_table; + g->gt_grftsnt = 0; + } else { + log(LOG_WARNING, 0, "%s (%s, %s) from %s", + "rcvd graft ack with no kernel entry for", + inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2), + inet_fmt(src, s3)); return; } - - if (kt->kt_grftsnt) - kt->kt_grftsnt = 0; } /* - * free all prune entries + * free all prune entries and kernel routes + * normally, this should inform the kernel that all of its routes + * are going away, but this is only called by restart(), which is + * about to call MRT_DONE which does that anyway. */ -void free_all_prunes() +void +free_all_prunes() { - register struct ktable *kt; - register struct prunlst *krl; + register struct rtentry *r; + register struct gtable *g, *prev_g; + register struct stable *s, *prev_s; + register struct ptable *p, *prev_p; - while (kernel_rtable != NULL) { - kt = kernel_rtable; - kernel_rtable = kt->kt_next; + for (r = routing_table; r; r = r->rt_next) { + g = r->rt_groups; + while (g) { + s = g->gt_srctbl; + while (s) { + prev_s = s->st_next; + free(s); + s = prev_s; + } - while (kt->kt_rlist != NULL) { - krl = kt->kt_rlist; - kt->kt_rlist = krl->rl_next; - free((char *)krl); + p = g->gt_pruntbl; + while (p) { + prev_p = p->pt_next; + free(p); + p = prev_p; + } + + prev_g = g->gt_next; + free(g); + g = prev_g; } + r->rt_groups = NULL; + } + kernel_table = NULL; - free((char *)kt); - kroutes--; + g = kernel_no_route; + while (g) { + if (g->gt_srctbl) + free(g->gt_srctbl); + + prev_g = g->gt_next; + free(g); + g = prev_g; } + kernel_no_route = NULL; } +/* + * When a new route is created, search + * a) The less-specific part of the routing table + * b) The route-less kernel table + * for sources that the new route might want to handle. + * + * "Inheriting" these sources might be cleanest, but simply deleting + * them is easier, and letting the kernel re-request them. + */ +void +steal_sources(rt) + struct rtentry *rt; +{ + register struct rtentry *rp; + register struct gtable *gt, **gtnp; + register struct stable *st, **stnp; + for (rp = rt->rt_next; rp; rp = rp->rt_next) { + if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) { + log(LOG_DEBUG, 0, "Route for %s stealing sources from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmts(rp->rt_origin, rp->rt_originmask, s2)); + for (gt = rp->rt_groups; gt; gt = gt->gt_next) { + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) { + log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmt(st->st_origin, s3), + inet_fmt(gt->gt_mcastgrp, s4), + inet_fmts(rp->rt_origin, rp->rt_originmask, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s, %s)", + "steal_sources trying to delete", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + *stnp = st->st_next; + kroutes--; + free(st); + } else { + stnp = &st->st_next; + } + } + } + } + } + + gtnp = &kernel_no_route; + while ((gt = *gtnp) != NULL) { + if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask) + == rt->rt_origin)) { + log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1), + inet_fmt(gt->gt_srctbl->st_origin, s3), + inet_fmt(gt->gt_mcastgrp, s4), + "no_route table"); + if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "steal_sources trying to delete", + inet_fmt(gt->gt_srctbl->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + kroutes--; + free(gt->gt_srctbl); + *gtnp = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + free(gt); + } else { + gtnp = >->gt_next; + } + } +} + /* * Advance the timers on all the cache entries. * If there are any entries whose timers have expired, * remove these entries from the kernel cache. */ -void age_table_entry() +void +age_table_entry() { - struct ktable *kt; - struct ktable *prev_kt; - struct prunlst *krl; - struct prunlst *prev_krl; + struct rtentry *r; + struct gtable *gt, **gtnptr; + struct stable *st, **stnp; + struct ptable *pt, **ptnp; + struct sioc_sg_req sg_req; + + log(LOG_DEBUG, 0, "ageing entries"); + + gtnptr = &kernel_table; + while ((gt = *gtnptr) != NULL) { + r = gt->gt_route; - log(LOG_DEBUG, 0, "kr:%x pr:%x", - kernel_rtable, (struct ktable *)&kernel_rtable); - - for (prev_kt = (struct ktable *)&kernel_rtable; - kt = prev_kt->kt_next; - prev_kt = kt) { /* advance the timer for the kernel entry */ - kt->kt_timer -= ROUTE_MAX_REPORT_DELAY; + gt->gt_timer -= ROUTE_MAX_REPORT_DELAY; + /* decrement prune timer if need be */ + if (gt->gt_prsent_timer > 0) { + gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY; + if (gt->gt_prsent_timer <= 0) { + log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + gt->gt_prsent_timer = -1; + } + } + /* retransmit graft if graft sent flag is still set */ - if (kt->kt_grftsnt) { + if (gt->gt_grftsnt) { register int y; - CHK_GS(kt->kt_grftsnt++, y); + CHK_GS(gt->gt_grftsnt++, y); if (y) - send_graft(kt); + send_graft(gt); } - /* delete the entry only if there are no subordinate - routers - - Now, if there are subordinate routers, then, what we - have to do is to decrement each and every router's prune - time entry too and decide if we want to forward on - that link basically - */ - for (prev_krl = (struct prunlst *)&kt->kt_rlist, - krl = prev_krl->rl_next; krl; - prev_krl = krl, krl = krl->rl_next) { - - /* decrement prune timer received from downstream routers */ - if ((krl->rl_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) { - log(LOG_DEBUG, 0, "forw again (%s, %s) on vif %d", - inet_fmt(kt->kt_origin, s1), - inet_fmt(kt->kt_mcastgrp, s2), - krl->rl_vifi); - + /* + * Age prunes + * + * If a prune expires, forward again on that vif. + */ + ptnp = >->gt_pruntbl; + while ((pt = *ptnp) != NULL) { + if ((pt->pt_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) { + log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), + inet_fmt(pt->pt_router, s3), + pt->pt_vifi); + /* - * forwarding now, so entry is not pruned anymore - * reset the cache timer to a largish value also + * No need to send a graft, any prunes that we sent + * will expire before any prunes that we have received. */ - kt->kt_prsent_timer = 0; + if (gt->gt_prsent_timer > 0) { + log(LOG_DEBUG, 0, "prune expired with %d left on %s", + gt->gt_prsent_timer, "prsent_timer"); + gt->gt_prsent_timer = 0; + } /* modify the kernel entry to forward packets */ - if (!VIFM_ISSET(krl->rl_vifi, kt->kt_grpmems)) { - VIFM_SET(krl->rl_vifi, kt->kt_grpmems); - prun_add_ttls(kt); - k_add_rg(kt); + if (!VIFM_ISSET(pt->pt_vifi, gt->gt_grpmems)) { + VIFM_SET(pt->pt_vifi, gt->gt_grpmems); + log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems, + pt->pt_vifi); + + prun_add_ttls(gt); + update_kernel(gt); +#ifdef RSRR + /* Send route change notification to reservation + * protocol. + */ + rsrr_cache_send(gt,1); +#endif /* RSRR */ } /* remove the router's prune entry and await new one */ - kt->kt_prun_count--; - prev_krl->rl_next = krl->rl_next; - free((char *)krl); - krl = prev_krl; - - if (krl == NULL) - break; + *ptnp = pt->pt_next; + free(pt); + } else { + ptnp = &pt->pt_next; } } - if (kt->kt_timer <= 0) { + /* + * If the cache entry has expired, check for downstream prunes. + * + * If there are downstream prunes, refresh the cache entry's timer. + * Otherwise, check for traffic. If no traffic, delete this + * entry. + */ + if (gt->gt_timer <= 0) { + if (gt->gt_pruntbl) { + if (gt->gt_prsent_timer == -1) + gt->gt_prsent_timer = 0; + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + gtnptr = >->gt_gnext; + continue; + } + /* - * If there are prune entries still outstanding, - * update the cache timer otherwise expire entry. + * If this entry was pruned, but all downstream prunes + * have expired, then it is safe to simply delete it. + * Otherwise, check for traffic before deleting. */ - if (kt->kt_rlist) { - kt->kt_timer = CACHE_LIFETIME(cache_lifetime); - } - else { - log(LOG_DEBUG, 0, "aging entry (%s, %s)", - inet_fmt(kt->kt_origin, s1), - inet_fmt(kt->kt_mcastgrp, s2)); + if (gt->gt_prsent_timer == 0) { + sg_req.grp.s_addr = gt->gt_mcastgrp; + stnp = >->gt_srctbl; + while ((st = *stnp) != NULL) { + sg_req.src.s_addr = st->st_origin; + if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) + < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "age_table_entry: SIOCGETSGCNT failing for", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + /* Make sure it gets deleted below */ + sg_req.pktcnt = st->st_pktcnt; + } + if (sg_req.pktcnt == st->st_pktcnt) { + *stnp = st->st_next; + log(LOG_DEBUG, 0, + "age_table_entry deleting (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, + "age_table_entry trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + kroutes--; + free(st); + } else { + stnp = &st->st_next; + } + } - k_del_rg(kt); - prev_kt->kt_next = kt->kt_next; - - /* free all the prune list entries */ - krl = kt->kt_rlist; - while(krl) { - prev_krl = krl; - krl = krl->rl_next; - free((char *)prev_krl); + if (gt->gt_srctbl) { + /* At least one source in the list still has traffic */ + gt->gt_timer = CACHE_LIFETIME(cache_lifetime); + gtnptr = >->gt_gnext; + continue; } + } - free((char *)kt); + log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + + /* free all the source entries */ + while ((st = gt->gt_srctbl) != NULL) { + log(LOG_DEBUG, 0, + "age_table_entry (P) deleting (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + if (k_del_rg(st->st_origin, gt) < 0) { + log(LOG_WARNING, errno, + "age_table_entry (P) trying to delete (%s %s)", + inet_fmt(st->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } kroutes--; - kt = prev_kt; + gt->gt_srctbl = st->st_next; + free(st); } + + /* free all the prune list entries */ + while ((pt = gt->gt_pruntbl) != NULL) { + gt->gt_pruntbl = pt->pt_next; + free(pt); + } + + if (gt->gt_prev) + gt->gt_prev->gt_next = gt->gt_next; + else + gt->gt_route->rt_groups = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + + if (gt->gt_gprev) { + gt->gt_gprev->gt_gnext = gt->gt_gnext; + gtnptr = >->gt_gprev->gt_gnext; + } else { + kernel_table = gt->gt_gnext; + gtnptr = &kernel_table; + } + if (gt->gt_gnext) + gt->gt_gnext->gt_gprev = gt->gt_gprev; + +#ifdef RSRR + /* Send route change notification to reservation protocol. */ + rsrr_cache_send(gt,0); + rsrr_cache_clean(gt); +#endif /* RSRR */ + free((char *)gt); + } else { + if (gt->gt_prsent_timer == -1) + gt->gt_prsent_timer = 0; + gtnptr = >->gt_gnext; } } + + /* + * When traversing the no_route table, the decision is much easier. + * Just delete it if it has timed out. + */ + gtnptr = &kernel_no_route; + while ((gt = *gtnptr) != NULL) { + /* advance the timer for the kernel entry */ + gt->gt_timer -= ROUTE_MAX_REPORT_DELAY; + + if (gt->gt_timer < 0) { + if (gt->gt_srctbl) { + if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) { + log(LOG_WARNING, errno, "%s (%s %s)", + "age_table_entry trying to delete no-route", + inet_fmt(gt->gt_srctbl->st_origin, s1), + inet_fmt(gt->gt_mcastgrp, s2)); + } + free(gt->gt_srctbl); + } + *gtnptr = gt->gt_next; + if (gt->gt_next) + gt->gt_next->gt_prev = gt->gt_prev; + + free((char *)gt); + } else { + gtnptr = >->gt_next; + } + } } +static char * +scaletime(t) + u_long t; +{ + static char buf1[5]; + static char buf2[5]; + static char *buf=buf1; + char s; + char *p; + + p = buf; + if (buf == buf1) + buf = buf2; + else + buf = buf1; + + if (t < 120) { + s = 's'; + } else if (t < 3600) { + t /= 60; + s = 'm'; + } else if (t < 86400) { + t /= 3600; + s = 'h'; + } else if (t < 864000) { + t /= 86400; + s = 'd'; + } else { + t /= 604800; + s = 'w'; + } + if (t > 999) + return "*** "; + + sprintf(p,"%3d%c", (int)t, s); + + return p; +} + /* - * Print the contents of the routing table on file 'fp'. + * Print the contents of the cache table on file 'fp2'. */ -void dump_cache(fp2) +void +dump_cache(fp2) FILE *fp2; { - register struct ktable *kt; - register struct prunlst *krl; + register struct rtentry *r; + register struct gtable *gt; + register struct stable *st; + register struct ptable *pt; register int i; - register int count; + register time_t thyme = time(0); fprintf(fp2, "Multicast Routing Cache Table (%d entries)\n%s", kroutes, - " Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs\n"); + " Origin Mcast-group CTmr Age Ptmr IVif Forwvifs\n"); + + for (gt = kernel_no_route; gt; gt = gt->gt_next) { + if (gt->gt_srctbl) { + fprintf(fp2, " %-18s %-15s %-4s %-4s - -1\n", + inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff, s1), + inet_fmt(gt->gt_mcastgrp, s2), scaletime(gt->gt_timer), + scaletime(thyme - gt->gt_ctime)); + fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin, s1)); + } + } - for (kt = kernel_rtable, count = 0; kt != NULL; kt = kt->kt_next) { + for (gt = kernel_table; gt; gt = gt->gt_gnext) { + r = gt->gt_route; + fprintf(fp2, " %-18s %-15s", + inet_fmts(r->rt_origin, r->rt_originmask, s1), + inet_fmt(gt->gt_mcastgrp, s2)); - fprintf(fp2, " %-15s %-15s", - inet_fmts(kt->kt_origin, kt->kt_originmask, s1), - inet_fmt(kt->kt_mcastgrp, s2)); + fprintf(fp2, " %-4s", scaletime(gt->gt_timer)); - if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) { - fprintf(fp2, " %5u %2ub %3u %c ", - kt->kt_timer, kt->kt_parent, kt->kt_prun_count, - kt->kt_prsent_timer ? 'P' : ' '); - fprintf(fp2, "\n"); - continue; - } - else - fprintf(fp2, " %5u %2u %3u %c ", - kt->kt_timer, kt->kt_parent, kt->kt_prun_count, - kt->kt_prsent_timer ? 'P' : ' '); + fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime), + gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) : + " -"); + fprintf(fp2, "%2u%c%c ", r->rt_parent, + gt->gt_prsent_timer ? 'P' : ' ', + VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' '); + for (i = 0; i < numvifs; ++i) { - if (VIFM_ISSET(i, kt->kt_grpmems)) + if (VIFM_ISSET(i, gt->gt_grpmems)) fprintf(fp2, " %u ", i); - else if (VIFM_ISSET(i, kt->kt_children) && - !VIFM_ISSET(i, kt->kt_leaves) && - VIFM_ISSET(i, kt->kt_scope)) - fprintf(fp2, " %u%c", i, 'b'); - else if (VIFM_ISSET(i, kt->kt_children) && - !VIFM_ISSET(i, kt->kt_leaves)) - fprintf(fp2, " %u%c", i, 'p'); + else if (VIFM_ISSET(i, r->rt_children) && + !VIFM_ISSET(i, r->rt_leaves)) + fprintf(fp2, " %u%c", i, + VIFM_ISSET(i, gt->gt_scope) ? 'b' : 'p'); } fprintf(fp2, "\n"); - count++; + for (st = gt->gt_srctbl; st; st = st->st_next) { + fprintf(fp2, ">%s\n", inet_fmt(st->st_origin, s1)); + } +#ifdef DEBUG_PRUNES + for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) { + fprintf(fp2, "pt_router, s1), + pt->pt_vifi, pt->pt_timer); + } +#endif } } - /* - * Checks if there are any routers that can understand traceroute - * downstream - */ -int can_forward(vifi) - vifi_t vifi; -{ - struct listaddr *u; - - for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) - if (((u->al_pv > 2) && (u->al_mv > 2)) || - (u->al_pv > 3)) - return 1; - - return 0; -} - -/* * Traceroute function which returns traceroute replies to the requesting * router. Also forwards the request to downstream routers. */ -void mtrace(src, dst, group, data, no, datalen) - u_long src; - u_long dst; - u_long group; +void +accept_mtrace(src, dst, group, data, no, datalen) + u_int32 src; + u_int32 dst; + u_int32 group; char *data; - u_char no; + u_int no; /* promoted u_char */ int datalen; { u_char type; struct rtentry *rt; + struct gtable *gt; struct tr_query *qry; struct tr_resp *resp; - struct uvif *v; int vifi; char *p; - struct ktable *kt; int rcount; - + int errcode = TR_NO_ERR; + int resptype; struct timeval tp; - struct timezone tzp; struct sioc_vif_req v_req; struct sioc_sg_req sg_req; + /* Remember qid across invocations */ + static u_int32 oqid = 0; + /* timestamp the request/response */ - gettimeofday(&tp, &tzp); + gettimeofday(&tp, 0); /* * Check if it is a query or a response */ if (datalen == QLEN) { type = QUERY; - printf("Traceroute query rcvd\n"); + log(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s", + inet_fmt(src, s1), inet_fmt(dst, s2)); } - else if ((datalen - QLEN)%RLEN == 0) { + else if ((datalen - QLEN) % RLEN == 0) { type = RESP; - printf("Traceroute response rcvd\n"); + log(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s", + inet_fmt(src, s1), inet_fmt(dst, s2)); + if (IN_MULTICAST(ntohl(dst))) { + log(LOG_DEBUG, 0, "Dropping multicast response"); + return; + } } else { - printf("Non decipherable trace request %s", inet_fmt(src, s1)); + log(LOG_WARNING, 0, "%s from %s to %s", + "Non decipherable traceroute request recieved", + inet_fmt(src, s1), inet_fmt(dst, s2)); return; } qry = (struct tr_query *)data; + if (oqid == qry->tr_qid) { + /* + * If the multicast router is a member of the group being + * queried, and the query is multicasted, then the router can + * recieve multiple copies of the same query. If we have already + * replied to this traceroute, just ignore it this time. + * + * This is not a total solution, but since if this fails you + * only get N copies, N <= the number of interfaces on the router, + * it is not fatal. + */ + log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet"); + return; + } + /* - * if it is a multicast packet with all reports filled, drop it + * if it is a packet with all reports filled, drop it */ if ((rcount = (datalen - QLEN)/RLEN) == no) { - printf("multicast packet with reports filled in\n"); + log(LOG_DEBUG, 0, "packet with all reports filled in"); return; } - printf("s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1), - inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3)); - printf("rttl: %d rd: %s\n", qry->tr_rttl, inet_fmt(qry->tr_raddr, s1)); - printf("rcount:%d\n", rcount); + log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1), + inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3)); + log(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl, + inet_fmt(qry->tr_raddr, s1)); + log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid); /* determine the routing table entry for this traceroute */ rt = determine_route(qry->tr_src); + if (rt) { + log(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d", + rt->rt_parent, inet_fmt(rt->rt_gateway, s1), rt->rt_metric); + log(LOG_DEBUG, 0, "rt origin %s", + inet_fmts(rt->rt_origin, rt->rt_originmask, s1)); + } else + log(LOG_DEBUG, 0, "...no route"); /* - * Query type packet - check if rte exists + * Query type packet - check if rte exists * Check if the query destination is a vif connected to me. * and if so, whether I should start response back */ if (type == QUERY) { if (rt == NULL) { - printf("Mcast traceroute: no route entry %s\n", + log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s", inet_fmt(qry->tr_src, s1)); if (IN_MULTICAST(ntohl(dst))) return; } - for (v = uvifs, vifi = 0; vifi < numvifs; ++vifi, ++v) - if (!(v->uv_flags & VIFF_TUNNEL) && - ((qry->tr_dst & v->uv_subnetmask) == v->uv_subnet)) - break; - - if (vifi == numvifs) { - printf("Destination %s not an interface\n", + vifi = find_vif(qry->tr_dst, 0); + + if (vifi == NO_VIF) { + /* The traceroute destination is not on one of my subnet vifs. */ + log(LOG_DEBUG, 0, "Destination %s not an interface", inet_fmt(qry->tr_dst, s1)); - return; - } - if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) { - printf("Destination %s not on forwarding tree for src %s\n", + if (IN_MULTICAST(ntohl(dst))) + return; + errcode = TR_WRONG_IF; + } else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) { + log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); - return; + if (IN_MULTICAST(ntohl(dst))) + return; + errcode = TR_WRONG_IF; } } else { /* * determine which interface the packet came in on + * RESP packets travel hop-by-hop so this either traversed + * a tunnel or came from a directly attached mrouter. */ if ((vifi = find_vif(src, dst)) == NO_VIF) { - printf("Wrong interface for packet\n"); - return; + log(LOG_DEBUG, 0, "Wrong interface for packet"); + errcode = TR_WRONG_IF; } - } + } + + /* Now that we've decided to send a response, save the qid */ + oqid = qry->tr_qid; - printf("Sending traceroute response\n"); - + log(LOG_DEBUG, 0, "Sending traceroute response"); + /* copy the packet to the sending buffer */ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - + bcopy(data, p, datalen); - + p += datalen; + + /* + * If there is no room to insert our reply, coopt the previous hop + * error indication to relay this fact. + */ + if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) { + resp = (struct tr_resp *)p - 1; + resp->tr_rflags = TR_NO_SPACE; + rt = NULL; + goto sendit; + } /* * fill in initial response fields */ resp = (struct tr_resp *)p; - resp->tr_qarr = ((tp.tv_sec + JAN_1970) << 16) + - ((tp.tv_usec << 10) / 15625); + bzero(resp, sizeof(struct tr_resp)); + datalen += RLEN; - resp->tr_vifin = 0; /* default values */ - resp->tr_pktcnt = 0; /* default values */ + resp->tr_qarr = htonl((tp.tv_sec + JAN_1970) << 16) + + ((tp.tv_usec >> 4) & 0xffff); + resp->tr_rproto = PROTO_DVMRP; - resp->tr_smask = 0; + if (errcode != TR_NO_ERR) { + resp->tr_rflags = errcode; + rt = NULL; /* hack to enforce send straight to requestor */ + goto sendit; + } resp->tr_outaddr = uvifs[vifi].uv_lcl_addr; resp->tr_fttl = uvifs[vifi].uv_threshold; resp->tr_rflags = TR_NO_ERR; /* * obtain # of packets out on interface */ v_req.vifi = vifi; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) - resp->tr_vifout = v_req.ocount; + resp->tr_vifout = htonl(v_req.ocount); /* * fill in scoping & pruning information */ - kt = find_src_grp(qry->tr_src, group); + if (rt) + for (gt = rt->rt_groups; gt; gt = gt->gt_next) { + if (gt->gt_mcastgrp >= group) + break; + } + else + gt = NULL; - if (kt != NULL) { + if (gt && gt->gt_mcastgrp == group) { sg_req.src.s_addr = qry->tr_src; sg_req.grp.s_addr = group; if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0) - resp->tr_pktcnt = sg_req.count; + resp->tr_pktcnt = htonl(sg_req.pktcnt); - if (VIFM_ISSET(vifi, kt->kt_scope)) + if (VIFM_ISSET(vifi, gt->gt_scope)) resp->tr_rflags = TR_SCOPED; - else if (kt->kt_prsent_timer) + else if (gt->gt_prsent_timer) resp->tr_rflags = TR_PRUNED; + else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) + if (VIFM_ISSET(vifi, rt->rt_children) && + !VIFM_ISSET(vifi, rt->rt_leaves)) + resp->tr_rflags = TR_OPRUNED; + else + resp->tr_rflags = TR_NO_FWD; + } else { + if (scoped_addr(vifi, group)) + resp->tr_rflags = TR_SCOPED; + else if (rt && !VIFM_ISSET(vifi, rt->rt_children)) + resp->tr_rflags = TR_NO_FWD; } /* * if no rte exists, set NO_RTE error */ if (rt == NULL) { src = dst; /* the dst address of resp. pkt */ resp->tr_inaddr = 0; resp->tr_rflags = TR_NO_RTE; - resp->tr_rmtaddr = 0; - } - else { + resp->tr_rmtaddr = 0; + } else { /* get # of packets in on interface */ v_req.vifi = rt->rt_parent; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) - resp->tr_vifin = v_req.icount; + resp->tr_vifin = htonl(v_req.icount); MASK_TO_VAL(rt->rt_originmask, resp->tr_smask); src = uvifs[rt->rt_parent].uv_lcl_addr; resp->tr_inaddr = src; resp->tr_rmtaddr = rt->rt_gateway; if (!VIFM_ISSET(vifi, rt->rt_children)) { - printf("Destination %s not on forwarding tree for src %s\n", + log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); resp->tr_rflags = TR_WRONG_IF; } + if (rt->rt_metric >= UNREACHABLE) { + resp->tr_rflags = TR_NO_RTE; + /* Hack to send reply directly */ + rt = NULL; + } } +sendit: /* * if metric is 1 or no. of reports is 1, send response to requestor - * else send to upstream router. + * else send to upstream router. If the upstream router can't handle + * mtrace, set an error code and send to requestor anyway. */ - printf("rcount:%d, no:%d\n", rcount, no); + log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no); - if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) + if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) { + resptype = IGMP_MTRACE_RESP; dst = qry->tr_raddr; - else - dst = rt->rt_gateway; + } else + if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) { + dst = qry->tr_raddr; + resp->tr_rflags = TR_OLD_ROUTER; + resptype = IGMP_MTRACE_RESP; + } else { + dst = rt->rt_gateway; + resptype = IGMP_MTRACE; + } if (IN_MULTICAST(ntohl(dst))) { - k_set_ttl(qry->tr_rttl); - send_igmp(INADDR_ANY, dst, - IGMP_MTRACE_RESP, no, group, - datalen + RLEN); - k_set_ttl(1); - } - else + /* + * Send the reply on a known multicast capable vif. + * If we don't have one, we can't source any multicasts anyway. + */ + if (phys_vif != -1) { + log(LOG_DEBUG, 0, "Sending reply to %s from %s", + inet_fmt(dst, s1), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2)); + k_set_ttl(qry->tr_rttl); + send_igmp(uvifs[phys_vif].uv_lcl_addr, dst, + resptype, no, group, + datalen); + k_set_ttl(1); + } else + log(LOG_INFO, 0, "No enabled phyints -- %s", + "dropping traceroute reply"); + } else { + log(LOG_DEBUG, 0, "Sending %s to %s from %s", + resptype == IGMP_MTRACE_RESP ? "reply" : "request on", + inet_fmt(dst, s1), inet_fmt(src, s2)); + send_igmp(src, dst, - IGMP_MTRACE, no, group, - datalen + RLEN); + resptype, no, group, + datalen); + } return; } Index: stable/2.1/usr.sbin/mrouted/prune.h =================================================================== --- stable/2.1/usr.sbin/mrouted/prune.h (revision 10584) +++ stable/2.1/usr.sbin/mrouted/prune.h (revision 10585) @@ -1,123 +1,143 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: prune.h,v 1.2 1994/09/08 02:51:24 wollman Exp $ + * $Id: prune.h,v 1.5 1995/06/28 17:58:43 wollman Exp $ */ /* - * Macro for copying the user-level cache table to the kernel - * level table variable passed on by the setsock option + * Group table + * + * Each group entry is a member of two doubly-linked lists: + * + * a) A list hanging off of the routing table entry for this source (rt_groups) + * sorted by group address under the routing entry (gt_next, gt_prev) + * b) An independent list pointed to by kernel_table, which is a list of + * active source,group's (gt_gnext, gt_gprev). + * */ - -#define COPY_TABLES(from, to) { \ - register u_int _i; \ - (to).mfcc_origin.s_addr = (from)->kt_origin; \ - (to).mfcc_mcastgrp.s_addr = (from)->kt_mcastgrp; \ - (to).mfcc_originmask.s_addr = (from)->kt_originmask; \ - (to).mfcc_parent = (from)->kt_parent; \ - for (_i = 0; _i < numvifs; _i++) \ - (to).mfcc_ttls[_i] = (from)->kt_ttls[_i]; \ +struct gtable { + struct gtable *gt_next; /* pointer to the next entry */ + struct gtable *gt_prev; /* back pointer for linked list */ + struct gtable *gt_gnext; /* fwd pointer for group list */ + struct gtable *gt_gprev; /* rev pointer for group list */ + u_int32 gt_mcastgrp; /* multicast group associated */ + vifbitmap_t gt_scope; /* scoped interfaces */ + u_char gt_ttls[MAXVIFS]; /* ttl vector for forwarding */ + vifbitmap_t gt_grpmems; /* forw. vifs for src, grp */ + int gt_prsent_timer; /* prune timer for this group */ + int gt_timer; /* timer for this group entry */ + time_t gt_ctime; /* time of entry creation */ + u_char gt_grftsnt; /* graft sent/retransmit timer */ + struct stable *gt_srctbl; /* source table */ + struct ptable *gt_pruntbl; /* prune table */ + struct rtentry *gt_route; /* parent route */ +#ifdef RSRR + struct rsrr_cache *gt_rsrr_cache; /* RSRR cache */ +#endif /* RSRR */ }; - /* - * User level Kernel Cache Table structure + * Source table * - * A copy of the kernel table is kept at the user level. Modifications are - * made to this table and then passed on to the kernel. A timeout value is - * an extra field in the user level table. - * + * When source-based prunes exist, there will be a struct ptable here as well. */ -struct ktable +struct stable { - struct ktable *kt_next; /* pointer to the next entry */ - u_long kt_origin; /* subnet origin of multicasts */ - u_long kt_mcastgrp; /* multicast group associated */ - u_long kt_originmask; /* subnet mask for origin */ - vifi_t kt_parent; /* incoming vif */ - u_long kt_gateway; /* upstream router */ - vifbitmap_t kt_children; /* outgoing children vifs */ - vifbitmap_t kt_leaves; /* subset of outgoing children vifs */ - vifbitmap_t kt_scope; /* scoped interfaces */ - u_char kt_ttls[MAXVIFS]; /* ttl vector for forwarding */ - vifbitmap_t kt_grpmems; /* forw. vifs for src, grp */ - int kt_timer; /* for timing out entry in cache */ - struct prunlst *kt_rlist; /* router list nghboring this rter */ - u_short kt_prun_count; /* count of total no. of prunes */ - int kt_prsent_timer; /* prune lifetime timer */ - u_int kt_grftsnt; /* graft sent upstream */ + struct stable *st_next; /* pointer to the next entry */ + u_int32 st_origin; /* host origin of multicasts */ + u_long st_pktcnt; /* packet count for src-grp entry */ }; /* - * structure to store incoming prunes + * structure to store incoming prunes. Can hang off of either group or source. */ -struct prunlst +struct ptable { - struct prunlst *rl_next; - u_long rl_router; - u_long rl_router_subnet; - vifi_t rl_vifi; - int rl_timer; + struct ptable *pt_next; /* pointer to the next entry */ + u_int32 pt_router; /* router that sent this prune */ + vifi_t pt_vifi; /* vif prune received on */ + int pt_timer; /* timer for prune */ }; +/* + * The packet format for a traceroute request. + */ struct tr_query { - u_long tr_src; /* traceroute source */ - u_long tr_dst; /* traceroute destination */ - u_long tr_raddr; /* traceroute response address */ + u_int32 tr_src; /* traceroute source */ + u_int32 tr_dst; /* traceroute destination */ + u_int32 tr_raddr; /* traceroute response address */ +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) struct { + u_int qid : 24; /* traceroute query id */ + u_int ttl : 8; /* traceroute response ttl */ + } q; +#else + struct { u_int ttl : 8; /* traceroute response ttl */ u_int qid : 24; /* traceroute query id */ } q; -} tr_query; +#endif /* BYTE_ORDER */ +}; #define tr_rttl q.ttl #define tr_qid q.qid +/* + * Traceroute response format. A traceroute response has a tr_query at the + * beginning, followed by one tr_resp for each hop taken. + */ struct tr_resp { - u_long tr_qarr; /* query arrival time */ - u_long tr_inaddr; /* incoming interface address */ - u_long tr_outaddr; /* outgoing interface address */ - u_long tr_rmtaddr; /* parent address in source tree */ - u_long tr_vifin; /* input packet count on interface */ - u_long tr_vifout; /* output packet count on interface */ - u_long tr_pktcnt; /* total incoming packets for src-grp */ + u_int32 tr_qarr; /* query arrival time */ + u_int32 tr_inaddr; /* incoming interface address */ + u_int32 tr_outaddr; /* outgoing interface address */ + u_int32 tr_rmtaddr; /* parent address in source tree */ + u_int32 tr_vifin; /* input packet count on interface */ + u_int32 tr_vifout; /* output packet count on interface */ + u_int32 tr_pktcnt; /* total incoming packets for src-grp */ u_char tr_rproto; /* routing protocol deployed on router */ u_char tr_fttl; /* ttl required to forward on outvif */ u_char tr_smask; /* subnet mask for src addr */ u_char tr_rflags; /* forwarding error codes */ -} tr_resp; +}; /* defs within mtrace */ #define QUERY 1 #define RESP 2 #define QLEN sizeof(struct tr_query) #define RLEN sizeof(struct tr_resp) /* fields for tr_rflags (forwarding error codes) */ -#define TR_NO_ERR 0x0 -#define TR_WRONG_IF 0x1 -#define TR_PRUNED 0x2 -#define TR_SCOPED 0x4 -#define TR_NO_RTE 0x5 +#define TR_NO_ERR 0 +#define TR_WRONG_IF 1 +#define TR_PRUNED 2 +#define TR_OPRUNED 3 +#define TR_SCOPED 4 +#define TR_NO_RTE 5 +#define TR_NO_FWD 7 +#define TR_NO_SPACE 0x81 +#define TR_OLD_ROUTER 0x82 /* fields for tr_rproto (routing protocol) */ -#define PROTO_DVMRP 0x1 -#define PROTO_MOSPF 0x2 -#define PROTO_PIM 0x3 -#define PROTO_CBT 0x4 +#define PROTO_DVMRP 1 +#define PROTO_MOSPF 2 +#define PROTO_PIM 3 +#define PROTO_CBT 4 #define MASK_TO_VAL(x, i) { \ - (i) = 0; \ - while ((x) << (i)) \ + u_int32 _x = ntohl(x); \ + (i) = 1; \ + while ((_x) <<= 1) \ (i)++; \ - } + }; #define VAL_TO_MASK(x, i) { \ - x = ~((1 << (32 - (i))) - 1); \ - } + x = htonl(~((1 << (32 - (i))) - 1)); \ + }; + +#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv) Index: stable/2.1/usr.sbin/mrouted/route.c =================================================================== --- stable/2.1/usr.sbin/mrouted/route.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/route.c (revision 10585) @@ -1,1076 +1,1142 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: route.c,v 1.2 1994/09/08 02:51:25 wollman Exp $ + * $Id: route.c,v 1.5 1995/06/28 17:58:44 wollman Exp $ */ #include "defs.h" /* + * This define statement saves a lot of space later + */ +#define RT_ADDR (struct rtentry *)&routing_table + +/* * Exported variables. */ int routes_changed; /* 1=>some routes have changed */ int delay_change_reports; /* 1=>postpone change reports */ /* + * The routing table is shared with prune.c , so must not be static. + */ +struct rtentry *routing_table; /* pointer to list of route entries */ + +/* * Private variables. */ -static struct rtentry *routing_table; /* pointer to list of route entries */ static struct rtentry *rtp; /* pointer to a route entry */ +static struct rtentry *rt_end; /* pointer to last route entry */ unsigned int nroutes; /* current number of route entries */ +/* + * Private functions. + */ +static int init_children_and_leaves __P((struct rtentry *r, + vifi_t parent)); +static int find_route __P((u_int32 origin, u_int32 mask)); +static void create_route __P((u_int32 origin, u_int32 mask)); +static void discard_route __P((struct rtentry *prev_r)); +static int compare_rts __P((const void *rt1, const void *rt2)); +static int report_chunk __P((struct rtentry *start_rt, vifi_t vifi, + u_int32 dst)); /* * Initialize the routing table and associated variables. */ -void init_routes() +void +init_routes() { routing_table = NULL; + rt_end = RT_ADDR; nroutes = 0; routes_changed = FALSE; delay_change_reports = FALSE; } /* * Initialize the children and leaf bits for route 'r', along with the * associated dominant, subordinate, and leaf timing data structures. * Return TRUE if this changes the value of either the children or * leaf bitmaps for 'r'. */ -static int init_children_and_leaves(r, parent) +static int +init_children_and_leaves(r, parent) register struct rtentry *r; register vifi_t parent; { register vifi_t vifi; register struct uvif *v; vifbitmap_t old_children, old_leaves; VIFM_COPY(r->rt_children, old_children); VIFM_COPY(r->rt_leaves, old_leaves ); VIFM_CLRALL(r->rt_children); VIFM_CLRALL(r->rt_leaves); r->rt_flags &= ~RTF_LEAF_TIMING; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { r->rt_dominants [vifi] = 0; r->rt_subordinates[vifi] = 0; if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { VIFM_SET(vifi, r->rt_children); if (v->uv_neighbors == NULL) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; } else { r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } } else { r->rt_leaf_timers[vifi] = 0; } } return (!VIFM_SAME(r->rt_children, old_children) || !VIFM_SAME(r->rt_leaves, old_leaves)); } /* * A new vif has come up -- update the children and leaf bitmaps in all route * entries to take that into account. */ -void add_vif_to_routes(vifi) +void +add_vif_to_routes(vifi) register vifi_t vifi; { register struct rtentry *r; register struct uvif *v; v = &uvifs[vifi]; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE && !VIFM_ISSET(vifi, r->rt_children)) { VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; r->rt_subordinates[vifi] = 0; if (v->uv_neighbors == NULL) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; } else { VIFM_CLR(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } update_table_entry(r); } } } /* * A vif has gone down -- expire all routes that have that vif as parent, * and update the children bitmaps in all other route entries to take into * account the failed vif. */ -void delete_vif_from_routes(vifi) +void +delete_vif_from_routes(vifi) register vifi_t vifi; { register struct rtentry *r; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE) { if (vifi == r->rt_parent) { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_timer = ROUTE_EXPIRE_TIME; r->rt_metric = UNREACHABLE; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (VIFM_ISSET(vifi, r->rt_children)) { VIFM_CLR(vifi, r->rt_children); VIFM_CLR(vifi, r->rt_leaves); r->rt_subordinates[vifi] = 0; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } else { r->rt_dominants[vifi] = 0; } } } } /* * A neighbor has failed or become unreachable. If that neighbor was * considered a dominant or subordinate router in any route entries, * take appropriate action. */ -void delete_neighbor_from_routes(addr, vifi) - register u_long addr; +void +delete_neighbor_from_routes(addr, vifi) + register u_int32 addr; register vifi_t vifi; { register struct rtentry *r; register struct uvif *v; v = &uvifs[vifi]; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE) { if (r->rt_dominants[vifi] == addr) { VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; r->rt_subordinates[vifi] = 0; if (v->uv_neighbors == NULL) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; } else { VIFM_CLR(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } update_table_entry(r); } else if (r->rt_subordinates[vifi] == addr) { r->rt_subordinates[vifi] = 0; if (v->uv_neighbors == NULL) { VIFM_SET(vifi, r->rt_leaves); update_table_entry(r); } else { r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } } else if (v->uv_neighbors == NULL && r->rt_leaf_timers[vifi] != 0) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; update_table_entry(r); } } } } /* * Prepare for a sequence of ordered route updates by initializing a pointer * to the start of the routing table. The pointer is used to remember our * position in the routing table in order to avoid searching from the * beginning for each update; this relies on having the route reports in * a single message be in the same order as the route entries in the routing * table. */ -void start_route_updates() +void +start_route_updates() { - rtp = (struct rtentry *)&routing_table; + rtp = RT_ADDR; } /* * Starting at the route entry following the one to which 'rtp' points, * look for a route entry matching the specified origin and mask. If a * match is found, return TRUE and leave 'rtp' pointing at the found entry. * If no match is found, return FALSE and leave 'rtp' pointing to the route * entry preceding the point at which the new origin should be inserted. * This code is optimized for the normal case in which the first entry to * be examined is the matching entry. */ -static int find_route(origin, mask) - register u_long origin, mask; +static int +find_route(origin, mask) + register u_int32 origin, mask; { register struct rtentry *r; r = rtp->rt_next; while (r != NULL) { if (origin == r->rt_origin && mask == r->rt_originmask) { rtp = r; return (TRUE); } - if (ntohl(mask) > ntohl(r->rt_originmask) || + if (ntohl(mask) < ntohl(r->rt_originmask) || (mask == r->rt_originmask && - ntohl(origin) > ntohl(r->rt_origin))) { + ntohl(origin) < ntohl(r->rt_origin))) { rtp = r; r = r->rt_next; } else break; } return (FALSE); } - /* - * Search the entire routing table, looking for an entry which conflicts - * with the given origin and mask, for example, an entry which has the same - * origin under a different mask. If a conflicting entry is found, return - * a pointer to the entry preceding it (to facilitate deletion); if no - * conflict is found, return NULL. - */ -static struct rtentry *find_conflicting_route(origin, mask) - register u_long origin, mask; -{ - register struct rtentry *r, *prev_r; - - for (prev_r = (struct rtentry *)&routing_table, r = routing_table; - r != NULL; - prev_r = r, r = r->rt_next ) { - if ((origin & r->rt_originmask) == r->rt_origin || - (r->rt_origin & mask) == origin) { - return (prev_r); - } - } - return (NULL); -} - - -/* * Create a new routing table entry for the specified origin and link it into * the routing table. The shared variable 'rtp' is assumed to point to the * routing entry after which the new one should be inserted. It is left * pointing to the new entry. * * Only the origin, originmask, originwidth and flags fields are initialized * in the new route entry; the caller is responsible for filling in the the * rest. */ -static void create_route(origin, mask) - u_long origin, mask; +static void +create_route(origin, mask) + u_int32 origin, mask; { register struct rtentry *r; - if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) - + (3 * numvifs * sizeof(u_long)))) == NULL) { + if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) + + (2 * numvifs * sizeof(u_int32)) + + (numvifs * sizeof(u_int)))) == NULL) { log(LOG_ERR, 0, "ran out of memory"); /* fatal */ } r->rt_origin = origin; r->rt_originmask = mask; if (((char *)&mask)[3] != 0) r->rt_originwidth = 4; else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3; else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2; else r->rt_originwidth = 1; r->rt_flags = 0; - r->rt_dominants = (u_long *)(r + 1); - r->rt_subordinates = (u_long *)(r->rt_dominants + numvifs); - r->rt_leaf_timers = (u_long *)(r->rt_subordinates + numvifs); + r->rt_dominants = (u_int32 *)(r + 1); + r->rt_subordinates = (u_int32 *)(r->rt_dominants + numvifs); + r->rt_leaf_timers = (u_int *)(r->rt_subordinates + numvifs); + r->rt_groups = NULL; r->rt_next = rtp->rt_next; rtp->rt_next = r; + r->rt_prev = rtp; + if (r->rt_next != NULL) + (r->rt_next)->rt_prev = r; + else + rt_end = r; rtp = r; ++nroutes; } /* * Discard the routing table entry following the one to which 'prev_r' points. */ -static void discard_route(prev_r) +static void +discard_route(prev_r) register struct rtentry *prev_r; { register struct rtentry *r; r = prev_r->rt_next; prev_r->rt_next = r->rt_next; + if (prev_r->rt_next != NULL) + (prev_r->rt_next)->rt_prev = prev_r; + else + rt_end = prev_r; free((char *)r); --nroutes; } /* * Process a route report for a single origin, creating or updating the * corresponding routing table entry if necessary. 'src' is either the * address of a neighboring router from which the report arrived, or zero * to indicate a change of status of one of our own interfaces. */ -void update_route(origin, mask, metric, src, vifi) - u_long origin, mask; +void +update_route(origin, mask, metric, src, vifi) + u_int32 origin, mask; int metric; - u_long src; + u_int32 src; vifi_t vifi; { register struct rtentry *r; - struct rtentry *prev_r; int adj_metric; /* * Compute an adjusted metric, taking into account the cost of the * subnet or tunnel over which the report arrived, and normalizing * all unreachable/poisoned metrics into a single value. */ if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) { log(LOG_WARNING, 0, "%s reports out-of-range metric %u for origin %s", inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2)); return; } adj_metric = metric + uvifs[vifi].uv_metric; if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE; /* * Look up the reported origin in the routing table. */ if (!find_route(origin, mask)) { /* * Not found. * Don't create a new entry if the report says it's unreachable, * or if the reported origin and mask are invalid. */ if (adj_metric == UNREACHABLE) { return; } if (src != 0 && !inet_valid_subnet(origin, mask)) { log(LOG_WARNING, 0, "%s reports an invalid origin (%s) and/or mask (%08x)", inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); return; } /* - * If the new origin and mask are inconsistent with an entry - * already in the routing table, either ignore this update - * (if it came from another router), or delete the conflicting - * entry (if the update is for a directly-connected subnet). - */ - if ((prev_r = find_conflicting_route(origin, mask)) != NULL ) { - if (src != 0) { - log(LOG_INFO, 0, - "%s reports a conflicting origin (%s) and mask (%08x)", - inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); - return; - } - else { - r = prev_r->rt_next; - log(LOG_INFO, 0, - "deleting route with conflicting origin (%s), mask (%08x)", - inet_fmt(r->rt_origin, s1), ntohl(r->rt_originmask)); - - if (r->rt_metric != UNREACHABLE) { - del_table_entry(r, 0, DEL_ALL_ROUTES); - } - discard_route(prev_r); - if (rtp == r) rtp = prev_r; - } - } - - /* * OK, create the new routing entry. 'rtp' will be left pointing * to the new entry. */ create_route(origin, mask); + /* + * Now "steal away" any sources that belong under this route + * by deleting any cache entries they might have created + * and allowing the kernel to re-request them. + */ + steal_sources(rtp); + rtp->rt_metric = UNREACHABLE; /* temporary; updated below */ } /* * We now have a routing entry for the reported origin. Update it? */ r = rtp; if (r->rt_metric == UNREACHABLE) { /* * The routing entry is for a formerly-unreachable or new origin. * If the report claims reachability, update the entry to use * the reported route. */ if (adj_metric == UNREACHABLE) return; r->rt_parent = vifi; init_children_and_leaves(r, vifi); r->rt_gateway = src; r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; update_table_entry(r); } else if (src == r->rt_gateway) { /* * The report has come either from the interface directly-connected * to the origin subnet (src and r->rt_gateway both equal zero) or * from the gateway we have chosen as the best first-hop gateway back * towards the origin (src and r->rt_gateway not equal zero). Reset * the route timer and, if the reported metric has changed, update * our entry accordingly. */ r->rt_timer = 0; if (adj_metric == r->rt_metric) return; if (adj_metric == UNREACHABLE) { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_timer = ROUTE_EXPIRE_TIME; } else if (adj_metric < r->rt_metric) { if (init_children_and_leaves(r, vifi)) { update_table_entry(r); } } r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (src == 0 || (r->rt_gateway != 0 && (adj_metric < r->rt_metric || (adj_metric == r->rt_metric && r->rt_timer >= ROUTE_SWITCH_TIME)))) { /* * The report is for an origin we consider reachable; the report * comes either from one of our own interfaces or from a gateway * other than the one we have chosen as the best first-hop gateway * back towards the origin. If the source of the update is one of * our own interfaces, or if the origin is not a directly-connected * subnet and the reported metric for that origin is better than * what our routing entry says, update the entry to use the new * gateway and metric. We also switch gateways if the reported * metric is the same as the one in the route entry and the gateway * associated with the route entry has not been heard from recently. * Did you get all that? */ if (r->rt_parent != vifi || adj_metric < r->rt_metric) { r->rt_parent = vifi; if (init_children_and_leaves(r, vifi)) { update_table_entry(r); } } r->rt_gateway = src; r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (vifi != r->rt_parent) { /* * The report came from a vif other than the route's parent vif. * Update the children and leaf info, if necessary. */ if (VIFM_ISSET(vifi, r->rt_children)) { /* * Vif is a child vif for this route. */ if (metric < r->rt_metric || (metric == r->rt_metric && ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) { /* * Neighbor has lower metric to origin (or has same metric * and lower IP address) -- it becomes the dominant router, * and vif is no longer a child for me. */ VIFM_CLR(vifi, r->rt_children); VIFM_CLR(vifi, r->rt_leaves); r->rt_dominants [vifi] = src; r->rt_subordinates[vifi] = 0; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } else if (metric > UNREACHABLE) { /* "poisoned reverse" */ /* * Neighbor considers this vif to be on path to route's * origin; if no subordinate recorded, record this neighbor * as subordinate and clear the leaf flag. */ if (r->rt_subordinates[vifi] == 0) { VIFM_CLR(vifi, r->rt_leaves); r->rt_subordinates[vifi] = src; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } } else if (src == r->rt_subordinates[vifi]) { /* * Current subordinate no longer considers this vif to be on * path to route's origin; it is no longer a subordinate * router, and we set the leaf confirmation timer to give * us time to hear from other subordinates. */ r->rt_subordinates[vifi] = 0; if (uvifs[vifi].uv_neighbors == NULL || uvifs[vifi].uv_neighbors->al_next == NULL) { VIFM_SET(vifi, r->rt_leaves); update_table_entry(r); } else { r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } } } else if (src == r->rt_dominants[vifi] && (metric > r->rt_metric || (metric == r->rt_metric && ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) { /* * Current dominant no longer has a lower metric to origin * (or same metric and lower IP address); we adopt the vif * as our own child. */ VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; if (metric > UNREACHABLE) { r->rt_subordinates[vifi] = src; } else if (uvifs[vifi].uv_neighbors == NULL || uvifs[vifi].uv_neighbors->al_next == NULL) { VIFM_SET(vifi, r->rt_leaves); } else { r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } update_table_entry(r); } } } /* * On every timer interrupt, advance the timer in each routing entry. */ -void age_routes() +void +age_routes() { register struct rtentry *r; register struct rtentry *prev_r; register vifi_t vifi; - for (prev_r = (struct rtentry *)&routing_table, r = routing_table; + for (prev_r = RT_ADDR, r = routing_table; r != NULL; prev_r = r, r = r->rt_next) { if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) { /* * Route is still good; see if any leaf timers need to be * advanced. */ if (r->rt_flags & RTF_LEAF_TIMING) { r->rt_flags &= ~RTF_LEAF_TIMING; for (vifi = 0; vifi < numvifs; ++vifi) { if (r->rt_leaf_timers[vifi] != 0) { /* * Unlike other timers, leaf timers decrement. */ if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){ - VIFM_SET(vifi, r->rt_leaves); - update_table_entry(r); +#ifdef NOTYET + /* If the vif is a physical leaf but has neighbors, + * it is not a tree leaf. If I am a leaf, then no + * interface with neighbors is a tree leaf. */ + if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) || + (vifs_with_neighbors == 1)) && + (uvifs[vifi].uv_neighbors != NULL))) { +#endif + VIFM_SET(vifi, r->rt_leaves); + update_table_entry(r); +#ifdef NOTYET + } +#endif } else { r->rt_flags |= RTF_LEAF_TIMING; } } } } } else if (r->rt_timer >= ROUTE_DISCARD_TIME) { /* * Time to garbage-collect the route entry. */ + del_table_entry(r, 0, DEL_ALL_ROUTES); discard_route(prev_r); r = prev_r; } else if (r->rt_metric != UNREACHABLE) { /* * Time to expire the route entry. If the gateway is zero, * i.e., it is a route to a directly-connected subnet, just * set the timer back to zero; such routes expire only when * the interface to the subnet goes down. */ if (r->rt_gateway == 0) { r->rt_timer = 0; } else { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_metric = UNREACHABLE; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } } } } /* * Mark all routes as unreachable. This function is called only from * hup() in preparation for informing all neighbors that we are going * off the air. For consistency, we ought also to delete all reachable * route entries from the kernel, but since we are about to exit we rely * on the kernel to do its own cleanup -- no point in making all those * expensive kernel calls now. */ -void expire_all_routes() +void +expire_all_routes() { register struct rtentry *r; for (r = routing_table; r != NULL; r = r->rt_next) { r->rt_metric = UNREACHABLE; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } } /* * Delete all the routes in the routing table. */ -void free_all_routes() +void +free_all_routes() { register struct rtentry *r; - r = (struct rtentry *)&routing_table; + r = RT_ADDR; while (r->rt_next) discard_route(r); } /* * Process an incoming neighbor probe message. */ -void accept_probe(src, dst, p, datalen, level) - u_long src; - u_long dst; +void +accept_probe(src, dst, p, datalen, level) + u_int32 src; + u_int32 dst; char *p; int datalen; - u_long level; + u_int32 level; { vifi_t vifi; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, "ignoring probe from non-neighbor %s", inet_fmt(src, s1)); return; } - if (!update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level)) - return; - - report(ALL_ROUTES, vifi, src); + update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level); } struct newrt { - u_long mask; - u_long origin; + u_int32 mask; + u_int32 origin; int metric; int pad; -}; +}; -int compare_rts(r1, r2) - register struct newrt *r1; - register struct newrt *r2; +static int +compare_rts(rt1, rt2) + const void *rt1; + const void *rt2; { - register unsigned long m1 = ntohl(r1->mask); - register unsigned long m2 = ntohl(r2->mask); - register unsigned long o1, o2; + register struct newrt *r1 = (struct newrt *)rt1; + register struct newrt *r2 = (struct newrt *)rt2; + register u_int32 m1 = ntohl(r1->mask); + register u_int32 m2 = ntohl(r2->mask); + register u_int32 o1, o2; if (m1 > m2) - return (1); - if (m1 < m2) return (-1); + if (m1 < m2) + return (1); /* masks are equal */ o1 = ntohl(r1->origin); o2 = ntohl(r2->origin); if (o1 > o2) - return (1); - if (o1 < o2) return (-1); + if (o1 < o2) + return (1); return (0); } /* * Process an incoming route report message. */ -void accept_report(src, dst, p, datalen, level) - u_long src, dst, level; +void +accept_report(src, dst, p, datalen, level) + u_int32 src, dst, level; register char *p; register int datalen; { vifi_t vifi; register int width, i, nrt = 0; int metric; - u_long mask; - u_long origin; + u_int32 mask; + u_int32 origin; struct newrt rt[4096]; if ((vifi = find_vif(src, dst)) == NO_VIF) { log(LOG_INFO, 0, "ignoring route report from non-neighbor %s", inet_fmt(src, s1)); return; } if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)) return; if (datalen > 2*4096) { log(LOG_INFO, 0, "ignoring oversize (%d bytes) route report from %s", datalen, inet_fmt(src, s1)); return; } while (datalen > 0) { /* Loop through per-mask lists. */ if (datalen < 3) { log(LOG_WARNING, 0, - "received truncated route report from %s", + "received truncated route report from %s", inet_fmt(src, s1)); return; } - ((char *)&mask)[0] = 0xff; width = 1; - if ((((char *)&mask)[1] = *p++) != 0) width = 2; - if ((((char *)&mask)[2] = *p++) != 0) width = 3; - if ((((char *)&mask)[3] = *p++) != 0) width = 4; + ((u_char *)&mask)[0] = 0xff; width = 1; + if ((((u_char *)&mask)[1] = *p++) != 0) width = 2; + if ((((u_char *)&mask)[2] = *p++) != 0) width = 3; + if ((((u_char *)&mask)[3] = *p++) != 0) width = 4; datalen -= 3; do { /* Loop through (origin, metric) pairs */ if (datalen < width + 1) { log(LOG_WARNING, 0, - "received truncated route report from %s", + "received truncated route report from %s", inet_fmt(src, s1)); return; } origin = 0; for (i = 0; i < width; ++i) ((char *)&origin)[i] = *p++; metric = *p++; datalen -= width + 1; rt[nrt].mask = mask; rt[nrt].origin = origin; - rt[nrt].metric = metric; + rt[nrt].metric = (metric & 0x7f); ++nrt; } while (!(metric & 0x80)); } qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts); start_route_updates(); + /* + * If the last entry is default, change mask from 0xff000000 to 0 + */ + if (rt[nrt-1].origin == 0) + rt[nrt-1].mask = 0; + + log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt, + inet_fmt(src, s1), inet_fmt(dst, s2)); for (i = 0; i < nrt; ++i) - update_route(rt[i].origin, rt[i].mask, (rt[i].metric & 0x7f), + update_route(rt[i].origin, rt[i].mask, rt[i].metric, src, vifi); if (routes_changed && !delay_change_reports) report_to_all_neighbors(CHANGED_ROUTES); } /* * Send a route report message to destination 'dst', via virtual interface * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. */ -void report(which_routes, vifi, dst) +void +report(which_routes, vifi, dst) int which_routes; vifi_t vifi; - u_long dst; + u_int32 dst; { register struct rtentry *r; register char *p; register int i; - int datalen; - int width; - u_long mask; - u_long src; + int datalen = 0; + int width = 0; + u_int32 mask = 0; + u_int32 src; + u_int32 nflags; src = uvifs[vifi].uv_lcl_addr; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - mask = 0; - for (r = routing_table; r != NULL; r = r->rt_next) { +#ifdef NOTYET + /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */ + if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) { + *p++ = 0; /* 0xff000000 mask */ + *p++ = 0; + *p++ = 0; + *p++ = 0; /* class A net 0.0.0.0 == default */ + *p++ = 0x81; /*XXX metric 1, is this safe? */ + datalen += 5; + send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, + htonl(MROUTED_LEVEL), datalen); + return; + } +#endif + nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; + + for (r = rt_end; r != RT_ADDR; r = r->rt_prev) { + if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) continue; /* * If there is no room for this route in the current message, * send the message and start a new one. */ if (datalen + ((r->rt_originmask == mask) ? (width + 1) : (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { *(p-1) |= 0x80; send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL), datalen); + htonl(MROUTED_LEVEL | nflags), datalen); p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; datalen = 0; mask = 0; } - if(r->rt_originmask != mask) { + if (r->rt_originmask != mask || datalen == 0) { mask = r->rt_originmask; width = r->rt_originwidth; if (datalen != 0) *(p-1) |= 0x80; *p++ = ((char *)&mask)[1]; *p++ = ((char *)&mask)[2]; *p++ = ((char *)&mask)[3]; datalen += 3; } for (i = 0; i < width; ++i) *p++ = ((char *)&(r->rt_origin))[i]; *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ (char)(r->rt_metric); datalen += width + 1; } if (datalen != 0) { *(p-1) |= 0x80; send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL), datalen); + htonl(MROUTED_LEVEL | nflags), datalen); } } /* * Send a route report message to all neighboring routers. * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. */ -void report_to_all_neighbors(which_routes) +void +report_to_all_neighbors(which_routes) int which_routes; { register vifi_t vifi; register struct uvif *v; register struct rtentry *r; int routes_changed_before; /* * Remember the state of the global routes_changed flag before * generating the reports, and clear the flag. */ routes_changed_before = routes_changed; routes_changed = FALSE; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_neighbors != NULL) { report(which_routes, vifi, (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group); } } /* * If there were changed routes before we sent the reports AND * if no new changes occurred while sending the reports, clear * the change flags in the individual route entries. If changes * did occur while sending the reports, new reports will be * generated at the next timer interrupt. */ if (routes_changed_before && !routes_changed) { for (r = routing_table; r != NULL; r = r->rt_next) { r->rt_flags &= ~RTF_CHANGED; } } /* * Set a flag to inhibit further reports of changed routes until the * next timer interrupt. This is to alleviate update storms. */ delay_change_reports = TRUE; } /* * Send a route report message to destination 'dst', via virtual interface * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES. */ -int report_chunk(start_rt, vifi, dst) +static int +report_chunk(start_rt, vifi, dst) register struct rtentry *start_rt; vifi_t vifi; - u_long dst; + u_int32 dst; { register struct rtentry *r; register char *p; register int i; register int nrt = 0; - int datalen; - int width; - u_long mask; - u_long src; + int datalen = 0; + int width = 0; + u_int32 mask = 0; + u_int32 src; + u_int32 nflags; src = uvifs[vifi].uv_lcl_addr; p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - mask = 0; - for (r = start_rt; r != NULL; r = r->rt_next) { + nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS; + + for (r = start_rt; r != RT_ADDR; r = r->rt_prev) { + +#ifdef NOTYET + /* Don't send poisoned routes back to parents if I am a leaf */ + if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi) + && (r->rt_metric > 1)) { + ++nrt; + continue; + } +#endif + /* * If there is no room for this route in the current message, * send it & return how many routes we sent. */ if (datalen + ((r->rt_originmask == mask) ? (width + 1) : (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) { *(p-1) |= 0x80; send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL), datalen); + htonl(MROUTED_LEVEL | nflags), datalen); return (nrt); } - if(r->rt_originmask != mask) { + if (r->rt_originmask != mask || datalen == 0) { mask = r->rt_originmask; width = r->rt_originwidth; if (datalen != 0) *(p-1) |= 0x80; *p++ = ((char *)&mask)[1]; *p++ = ((char *)&mask)[2]; *p++ = ((char *)&mask)[3]; datalen += 3; } for (i = 0; i < width; ++i) *p++ = ((char *)&(r->rt_origin))[i]; *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ? (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */ (char)(r->rt_metric); ++nrt; datalen += width + 1; } if (datalen != 0) { *(p-1) |= 0x80; send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT, - htonl(MROUTED_LEVEL), datalen); + htonl(MROUTED_LEVEL | nflags), datalen); } return (nrt); } /* * send the next chunk of our routing table to all neighbors. + * return the length of the smallest chunk we sent out. */ -int report_next_chunk() +int +report_next_chunk() { register vifi_t vifi; register struct uvif *v; - register struct rtentry *r; register struct rtentry *sr; - register int i, n = 0; + register int i, n = 0, min = 20000; static int start_rt; if (nroutes <= 0) return (0); /* * find this round's starting route. */ - for (sr = routing_table, i = start_rt; --i >= 0; ) { - sr = sr->rt_next; - if (sr == NULL) - sr = routing_table; + for (sr = rt_end, i = start_rt; --i >= 0; ) { + sr = sr->rt_prev; + if (sr == RT_ADDR) + sr = rt_end; } + /* * send one chunk of routes starting at this round's start to * all our neighbors. */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { - if (v->uv_neighbors != NULL) { + if ((v->uv_neighbors != NULL) +#ifdef NOTYET + && !(v->uv_flags & VIFF_LEAF) +#endif + ) { n = report_chunk(sr, vifi, (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group); + if (n < min) + min = n; } } - if (debug) - printf("update %d starting at %d of %d\n", n, start_rt, nroutes); + if (min == 20000) + min = 0; /* Neighborless router didn't send any routes */ + + n = min; + log(LOG_INFO, 0, "update %d starting at %d of %d", + n, (nroutes - start_rt), nroutes); + start_rt = (start_rt + n) % nroutes; return (n); } /* * Print the contents of the routing table on file 'fp'. */ -void dump_routes(fp) +void +dump_routes(fp) FILE *fp; { register struct rtentry *r; register int i; + fprintf(fp, - "Multicast Routing Table (%u %s)\n%s", + "Multicast Routing Table (%u %s)\n%s\n", nroutes, (nroutes == 1) ? "entry" : "entries", - " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n"); + " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs"); for (r = routing_table; r != NULL; r = r->rt_next) { - fprintf(fp, " %-15s %-15s ", + fprintf(fp, " %-18s %-15s ", inet_fmts(r->rt_origin, r->rt_originmask, s1), (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2)); fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ", r->rt_metric); - fprintf(fp, "%7u ", - r->rt_parent); + fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent); for (i = 0; i < numvifs; ++i) { if (VIFM_ISSET(i, r->rt_children)) { fprintf(fp, " %u%c", i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' '); } } fprintf(fp, "\n"); } fprintf(fp, "\n"); } -struct rtentry *determine_route(src) - u_long src; +struct rtentry * +determine_route(src) + u_int32 src; { struct rtentry *rt; for (rt = routing_table; rt != NULL; rt = rt->rt_next) { - if (rt->rt_origin == (src & rt->rt_originmask)) + if (rt->rt_origin == (src & rt->rt_originmask)) break; } return rt; } - Index: stable/2.1/usr.sbin/mrouted/route.h =================================================================== --- stable/2.1/usr.sbin/mrouted/route.h (revision 10584) +++ stable/2.1/usr.sbin/mrouted/route.h (revision 10585) @@ -1,50 +1,51 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: route.h,v 1.3 1993/05/30 01:36:38 deering Exp $ + * $Id: route.h,v 1.4 1995/06/28 17:58:45 wollman Exp $ */ /* * Routing Table Entry, one per subnet from which a multicast could originate. * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) * - * The Routing Table is stored as a singly-linked list of these structures, - * ordered by increasing value of rt_originmask and, secondarily, by - * increasing value of rt_origin within each rt_originmask value. + * The Routing Table is stored as a doubly-linked list of these structures, + * ordered by decreasing value of rt_originmask and, secondarily, by + * decreasing value of rt_origin within each rt_originmask value. * This data structure is efficient for generating route reports, whether * full or partial, for processing received full reports, for clearing the * CHANGED flags, and for periodically advancing the timers in all routes. * It is not so efficient for updating a small number of routes in response * to a partial report. In a stable topology, the latter are rare; if they * turn out to be costing a lot, we can add an auxiliary hash table for * faster access to arbitrary route entries. */ struct rtentry { struct rtentry *rt_next; /* link to next entry MUST BE FIRST */ - u_long rt_origin; /* subnet origin of multicasts */ - u_long rt_originmask; /* subnet mask for origin */ + u_int32 rt_origin; /* subnet origin of multicasts */ + u_int32 rt_originmask; /* subnet mask for origin */ short rt_originwidth; /* # bytes of origin subnet number */ u_char rt_metric; /* cost of route back to origin */ u_char rt_flags; /* RTF_ flags defined below */ - u_long rt_gateway; /* first-hop gateway back to origin */ + u_int32 rt_gateway; /* first-hop gateway back to origin */ vifi_t rt_parent; /* incoming vif (ie towards origin) */ vifbitmap_t rt_children; /* outgoing children vifs */ vifbitmap_t rt_leaves; /* subset of outgoing children vifs */ - u_long *rt_dominants; /* per vif dominant gateways */ - u_long *rt_subordinates; /* per vif subordinate gateways */ - u_long *rt_leaf_timers; /* per vif leaf confirmation timers */ - u_long rt_timer; /* for timing out the route entry */ + u_int32 *rt_dominants; /* per vif dominant gateways */ + u_int32 *rt_subordinates; /* per vif subordinate gateways */ + u_int *rt_leaf_timers; /* per vif leaf confirmation timers */ + u_int rt_timer; /* for timing out the route entry */ + struct rtentry *rt_prev; /* link to previous entry */ + struct gtable *rt_groups; /* link to active groups */ }; #define RTF_CHANGED 0x01 /* route changed but not reported */ #define RTF_LEAF_TIMING 0x02 /* some leaf timers are running */ - #define ALL_ROUTES 0 /* possible arguments to report() */ #define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */ Index: stable/2.1/usr.sbin/mrouted/vif.c =================================================================== --- stable/2.1/usr.sbin/mrouted/vif.c (revision 10584) +++ stable/2.1/usr.sbin/mrouted/vif.c (revision 10585) @@ -1,1172 +1,1351 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: vif.c,v 1.3 1995/05/16 00:28:50 jkh Exp $ + * $Id: vif.c,v 1.6 1995/06/28 17:58:48 wollman Exp $ */ #include "defs.h" +#include /* * Exported variables. */ -struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */ -vifi_t numvifs; /* number of vifs in use */ -int vifs_down; /* 1=>some interfaces are down */ +struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */ +vifi_t numvifs; /* number of vifs in use */ +int vifs_down; /* 1=>some interfaces are down */ +int phys_vif; /* An enabled vif */ int udp_socket; /* Since the honkin' kernel doesn't support */ /* ioctls on raw IP sockets, we need a UDP */ /* socket as well as our IGMP (raw) socket. */ /* How dumb. */ +int vifs_with_neighbors; /* == 1 if I am a leaf */ +typedef struct { + vifi_t vifi; + struct listaddr *g; + int q_time; +} cbk_t; + +static cbk_t *cbk; /* * Forward declarations. */ -static void start_vif(); -static void stop_vif(); -static void age_old_hosts(); +static void start_vif __P((vifi_t vifi)); +static void stop_vif __P((vifi_t vifi)); +static void age_old_hosts __P((void)); +static void send_probe_on_vif __P((struct uvif *v)); +static void DelVif __P((cbk_t *cbk)); +static int SetTimer __P((int vifi, struct listaddr *g)); +static int DeleteTimer __P((int id)); +static void SendQuery __P((cbk_t *cbk)); +static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire, + int q_time)); + /* * Initialize the virtual interfaces. */ -void init_vifs() +void +init_vifs() { vifi_t vifi; struct uvif *v; int enabled_vifs, enabled_phyints; + extern char *configfilename; numvifs = 0; + vifs_with_neighbors = 0; vifs_down = FALSE; /* * Configure the vifs based on the interface configuration of the * the kernel and the contents of the configuration file. * (Open a UDP socket for ioctl use in the config procedures.) */ if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) log(LOG_ERR, errno, "UDP socket"); + log(LOG_INFO,0,"Getting vifs from kernel interfaces"); config_vifs_from_kernel(); + log(LOG_INFO,0,"Getting vifs from %s",configfilename); config_vifs_from_file(); /* * Quit if there are fewer than two enabled vifs. */ enabled_vifs = 0; enabled_phyints = 0; + phys_vif = -1; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & VIFF_DISABLED)) { ++enabled_vifs; - if (!(v->uv_flags & VIFF_TUNNEL)) + if (!(v->uv_flags & VIFF_TUNNEL)) { + if (phys_vif == -1) + phys_vif = vifi; ++enabled_phyints; + } } } if (enabled_vifs < 2) log(LOG_ERR, 0, "can't forward: %s", enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif"); if (enabled_phyints == 0) log(LOG_WARNING, 0, "no enabled interfaces, forwarding via tunnels only"); /* * Start routing on all virtual interfaces that are not down or * administratively disabled. */ + log(LOG_INFO, 0, "Installing vifs in kernel..."); for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & VIFF_DISABLED)) { - if (!(v->uv_flags & VIFF_DOWN)) + if (!(v->uv_flags & VIFF_DOWN)) { + if (v->uv_flags & VIFF_TUNNEL) + log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi, + inet_fmt(v->uv_lcl_addr, s1), + inet_fmt(v->uv_rmt_addr, s2)); + else + log(LOG_INFO, 0, "vif #%d, phyint %s", vifi, + inet_fmt(v->uv_lcl_addr, s1)); start_vif(vifi); - else log(LOG_INFO, 0, + } else log(LOG_INFO, 0, "%s is not yet up; vif #%u not in service", v->uv_name, vifi); } } } /* * See if any interfaces have changed from up state to down, or vice versa, * including any non-multicast-capable interfaces that are in use as local * tunnel end-points. Ignore interfaces that have been administratively * disabled. */ -void check_vif_state() +void +check_vif_state() { register vifi_t vifi; register struct uvif *v; struct ifreq ifr; vifs_down = FALSE; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & VIFF_DISABLED) continue; strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ); if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); if (v->uv_flags & VIFF_DOWN) { if (ifr.ifr_flags & IFF_UP) { v->uv_flags &= ~VIFF_DOWN; start_vif(vifi); log(LOG_INFO, 0, "%s has come up; vif #%u now in service", v->uv_name, vifi); } else vifs_down = TRUE; } else { if (!(ifr.ifr_flags & IFF_UP)) { stop_vif(vifi); v->uv_flags |= VIFF_DOWN; log(LOG_INFO, 0, "%s has gone down; vif #%u taken out of service", v->uv_name, vifi); vifs_down = TRUE; } } } } +/* + * Send a probe message on vif v + */ +static void +send_probe_on_vif(v) + register struct uvif *v; +{ + register char *p; + register int datalen = 0; + struct listaddr *nbr; + int i; + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + + for (i = 0; i < 4; i++) + *p++ = ((char *)&(dvmrp_genid))[i]; + datalen += 4; + + /* + * add the neighbor list on the interface to the message + */ + nbr = v->uv_neighbors; + + while (nbr) { + for (i = 0; i < 4; i++) + *p++ = ((char *)&nbr->al_addr)[i]; + datalen +=4; + nbr = nbr->al_next; + } + + send_igmp(v->uv_lcl_addr, + (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr + : dvmrp_group, + IGMP_DVMRP, DVMRP_PROBE, + htonl(MROUTED_LEVEL | + ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS)), + datalen); +} + /* * Start routing on the specified virtual interface. */ -static void start_vif(vifi) +static void +start_vif(vifi) vifi_t vifi; { struct uvif *v; - u_long src, dst; - int i; - char *p; - int datalen; - struct listaddr *nbr; + u_int32 src; + struct phaddr *p; v = &uvifs[vifi]; src = v->uv_lcl_addr; - dst = (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr : dvmrp_group; /* * Install the interface in the kernel's vif structure. */ - log(LOG_DEBUG, 0, "Installing vif %d in kernel\n", vifi); k_add_vif(vifi, &uvifs[vifi]); /* * Update the existing route entries to take into account the new vif. */ add_vif_to_routes(vifi); if (!(v->uv_flags & VIFF_TUNNEL)) { /* * Join the DVMRP multicast group on the interface. * (This is not strictly necessary, since the kernel promiscuously * receives IGMP packets addressed to ANY IP multicast group while * multicast routing is enabled. However, joining the group allows * this host to receive non-IGMP packets as well, such as 'pings'.) */ k_join(dvmrp_group, src); /* + * Join the ALL-ROUTERS multicast group on the interface. + * This allows mtrace requests to loop back if they are run + * on the multicast router. + */ + k_join(allrtrs_group, src); + + /* * Install an entry in the routing table for the subnet to which * the interface is connected. */ start_route_updates(); update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi); + for (p = v->uv_addrs; p; p = p->pa_next) { + start_route_updates(); + update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi); + } /* * Until neighbors are discovered, assume responsibility for sending * periodic group membership queries to the subnet. Send the first * query. */ v->uv_flags |= VIFF_QUERIER; - send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, + send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY, IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); age_old_hosts(); } + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + /* * Send a probe via the new vif to look for neighbors. */ - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - datalen = 0; - - for (i = 0; i < 4; i++) - *p++ = ((char *)&(dvmrp_genid))[i]; - datalen += 4; - - /* - * add the neighbor list on the interface to the message - */ - nbr = v->uv_neighbors; - - while (nbr) { - for (i = 0; i < 4; i++) - *p++ = ((char *)&nbr->al_addr)[i]; - datalen +=4; - nbr = nbr->al_next; - } - - send_igmp(src, dst, IGMP_DVMRP, DVMRP_PROBE, - htonl(MROUTED_LEVEL), datalen); + send_probe_on_vif(v); } - /* * Stop routing on the specified virtual interface. */ -static void stop_vif(vifi) +static void +stop_vif(vifi) vifi_t vifi; { struct uvif *v; struct listaddr *a; + struct phaddr *p; v = &uvifs[vifi]; if (!(v->uv_flags & VIFF_TUNNEL)) { /* * Depart from the DVMRP multicast group on the interface. */ k_leave(dvmrp_group, v->uv_lcl_addr); /* + * Depart from the ALL-ROUTERS multicast group on the interface. + */ + k_leave(allrtrs_group, v->uv_lcl_addr); + + /* * Update the entry in the routing table for the subnet to which * the interface is connected, to take into account the interface * failure. */ start_route_updates(); update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi); + for (p = v->uv_addrs; p; p = p->pa_next) { + start_route_updates(); + update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi); + } /* * Discard all group addresses. (No need to tell kernel; * the k_del_vif() call, below, will clean up kernel state.) */ while (v->uv_groups != NULL) { a = v->uv_groups; v->uv_groups = a->al_next; free((char *)a); } v->uv_flags &= ~VIFF_QUERIER; } /* * Update the existing route entries to take into account the vif failure. */ delete_vif_from_routes(vifi); /* * Delete the interface from the kernel's vif structure. */ k_del_vif(vifi); /* * Discard all neighbor addresses. */ + if (v->uv_neighbors) + vifs_with_neighbors--; + while (v->uv_neighbors != NULL) { a = v->uv_neighbors; v->uv_neighbors = a->al_next; free((char *)a); } } /* * stop routing on all vifs */ -void stop_all_vifs() +void +stop_all_vifs() { vifi_t vifi; struct uvif *v; struct listaddr *a; struct vif_acl *acl; for (vifi = 0; vifi < numvifs; vifi++) { v = &uvifs[vifi]; while (v->uv_groups != NULL) { a = v->uv_groups; v->uv_groups = a->al_next; free((char *)a); } while (v->uv_neighbors != NULL) { a = v->uv_neighbors; v->uv_neighbors = a->al_next; free((char *)a); } while (v->uv_acl != NULL) { acl = v->uv_acl; v->uv_acl = acl->acl_next; free((char *)acl); } } } /* * Find the virtual interface from which an incoming packet arrived, * based on the packet's source and destination IP addresses. */ -vifi_t find_vif(src, dst) - register u_long src; - register u_long dst; +vifi_t +find_vif(src, dst) + register u_int32 src; + register u_int32 dst; { register vifi_t vifi; register struct uvif *v; + register struct phaddr *p; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { if (v->uv_flags & VIFF_TUNNEL) { if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr) return(vifi); } else { if ((src & v->uv_subnetmask) == v->uv_subnet && - src != v->uv_subnetbcast) + ((v->uv_subnetmask == 0xffffffff) || + (src != v->uv_subnetbcast))) return(vifi); + for (p=v->uv_addrs; p; p=p->pa_next) { + if ((src & p->pa_subnetmask) == p->pa_subnet && + ((p->pa_subnetmask == 0xffffffff) || + (src != p->pa_subnetbcast))) + return(vifi); + } } } } return (NO_VIF); } - -static void age_old_hosts() +static void +age_old_hosts() { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { /* -*- increment the time since an old report was heard */ for (g = v->uv_groups; g != NULL; g = g->al_next) { g->al_last ++; if (g->al_last >= OLD_AGE_THRESHOLD){ g->al_old = 0; g->al_last = OLD_AGE_THRESHOLD; } } } } /* * Send group membership queries to all subnets for which I am querier. */ -void query_groups() +void +query_groups() { register vifi_t vifi; register struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (v->uv_flags & VIFF_QUERIER) { send_igmp(v->uv_lcl_addr, allhosts_group, - IGMP_HOST_MEMBERSHIP_QUERY, + IGMP_HOST_MEMBERSHIP_QUERY, IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0); } } age_old_hosts(); } +/* + * Process an incoming host membership query + */ +void +accept_membership_query(src, dst, group, tmo) + u_int32 src, dst, group; + int tmo; +{ + register vifi_t vifi; + register struct uvif *v; + if ((vifi = find_vif(src, dst)) == NO_VIF || + (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { + log(LOG_INFO, 0, + "ignoring group membership query from non-adjacent host %s", + inet_fmt(src, s1)); + return; + } + + v = &uvifs[vifi]; + + /* If we consider ourselves the querier for this vif, but hear a + * query from a router with a lower IP address, yield to them. + * + * This is done here as well as in the neighbor discovery in case + * there is a querier that doesn't speak DVMRP. + */ + if ((v->uv_flags & VIFF_QUERIER) && + (ntohl(src) < ntohl(v->uv_lcl_addr))) { + + v->uv_flags &= ~VIFF_QUERIER; + + } +} + /* * Process an incoming group membership report. */ -void accept_group_report(src, dst, group, r_type) - u_long src, dst, group; +void +accept_group_report(src, dst, group, r_type) + u_int32 src, dst, group; int r_type; { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; if ((vifi = find_vif(src, dst)) == NO_VIF || (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { log(LOG_INFO, 0, "ignoring group membership report from non-adjacent host %s", inet_fmt(src, s1)); return; } v = &uvifs[vifi]; /* * Look for the group in our group list; if found, reset its timer. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr) { if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT) { g->al_last = OLD_AGE_THRESHOLD; g->al_old = 0; } else { g->al_last = 0; g->al_old = 1; } /** delete old timer set a timer for expiration **/ g->al_timer= GROUP_EXPIRE_TIME; 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(vifi, g); + g->al_timerid = SetTimer(vifi, g); break; } } /* * If not found, add it to the list and update kernel cache. */ if (g == NULL) { g = (struct listaddr *)malloc(sizeof(struct listaddr)); if (g == NULL) log(LOG_ERR, 0, "ran out of memory"); /* fatal */ g->al_addr = group; - if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT){ + if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT) { g->al_last = OLD_AGE_THRESHOLD; g->al_old = 0; } else { g->al_last = 0; g->al_old = 1; } /** set a timer for expiration **/ g->al_query = 0; g->al_timer = GROUP_EXPIRE_TIME; + time(&g->al_ctime); g->al_timerid = SetTimer(vifi, g); g->al_next = v->uv_groups; v->uv_groups = g; update_lclgrp(vifi, group); } - /* + /* * Check if a graft is necessary for this group */ chkgrp_graft(vifi, group); } -void leave_group_message( src, dst, group) - u_long src, dst, group; +void +accept_leave_message(src, dst, group) + u_int32 src, dst, group; { register vifi_t vifi; register struct uvif *v; register struct listaddr *g; if ((vifi = find_vif(src, dst)) == NO_VIF || (uvifs[vifi].uv_flags & VIFF_TUNNEL)) { log(LOG_INFO, 0, - "ignoring group membership report from non-adjacent host %s", + "ignoring group leave report from non-adjacent host %s", inet_fmt(src, s1)); return; } v = &uvifs[vifi]; + if (!(v->uv_flags & VIFF_QUERIER)) + return; + /* - * Look for the group in our group list; if found, reset its timer. + * Look for the group in our group list in order to set up a short-timeout + * query. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr) { log(LOG_DEBUG, 0, - "[vif.c, _leave_group_message] %d %d \n", + "[vif.c, _accept_leave_message] %d %d \n", g->al_old, g->al_query); + /* Ignore the leave message if there are old hosts present */ if (g->al_old) return; + /* still waiting for a reply to a query, ignore the leave */ + if (g->al_query) + return; + /** delete old timer set a timer for expiration **/ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); - if (g->al_query) - return; /** send a group specific querry **/ - g->al_timer = GROUP_EXPIRE_TIME / 10; + g->al_timer = LEAVE_EXPIRE_TIME; send_igmp(v->uv_lcl_addr, g->al_addr, - IGMP_HOST_MEMBERSHIP_QUERY, - GROUP_EXPIRE_TIME / 30 * IGMP_TIMER_SCALE, + IGMP_HOST_MEMBERSHIP_QUERY, + LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE, g->al_addr, 0); - g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3 , - GROUP_EXPIRE_TIME / 30 * IGMP_TIMER_SCALE); - g->al_timerid = SetTimer(vifi, g); + g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3, + LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE); + g->al_timerid = SetTimer(vifi, g); break; } } } /* * Send a periodic probe on all vifs. * Useful to determine one-way interfaces. * Detect neighbor loss faster. */ -void probe_for_neighbors() +void +probe_for_neighbors() { register vifi_t vifi; register struct uvif *v; - int i; - register char *p; - register int datalen = 0; - struct listaddr *nbr; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) { - - p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; - - for (i = 0; i < 4; i++) - *p++ = ((char *)&(dvmrp_genid))[i]; - datalen += 4; - - /* - * add the neighbor list on the interface to the message - */ - nbr = v->uv_neighbors; - - while (nbr) { - for (i = 0; i < 4; i++) - *p++ = ((char *)&nbr->al_addr)[i]; - datalen +=4; - nbr = nbr->al_next; - } - - send_igmp(v->uv_lcl_addr, - (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr - : dvmrp_group, - IGMP_DVMRP, DVMRP_PROBE, htonl(MROUTED_LEVEL), datalen); + send_probe_on_vif(v); } } } /* * Send a list of all of our neighbors to the requestor, `src'. */ -void accept_neighbor_request(src, dst) - u_long src, dst; +void +accept_neighbor_request(src, dst) + u_int32 src, dst; { vifi_t vifi; struct uvif *v; u_char *p, *ncount; struct listaddr *la; int datalen; - u_long temp_addr, us, them = src; + u_int32 temp_addr, us, them = src; /* Determine which of our addresses to use as the source of our response * to this query. */ if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */ int udp; /* find best interface to reply on */ struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof addr; +#endif addr.sin_addr.s_addr = dst; addr.sin_port = htons(2000); /* any port over 1024 will do... */ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { log(LOG_WARNING, errno, "Determining local address"); close(udp); return; } close(udp); us = addr.sin_addr.s_addr; } else /* query sent to us alone */ - us = dst; + us = dst; #define PUT_ADDR(a) temp_addr = ntohl(a); \ - *p++ = temp_addr >> 24; \ - *p++ = (temp_addr >> 16) & 0xFF; \ - *p++ = (temp_addr >> 8) & 0xFF; \ - *p++ = temp_addr & 0xFF; + *p++ = temp_addr >> 24; \ + *p++ = (temp_addr >> 16) & 0xFF; \ + *p++ = (temp_addr >> 8) & 0xFF; \ + *p++ = temp_addr & 0xFF; p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (v->uv_flags & VIFF_DISABLED) continue; ncount = 0; for (la = v->uv_neighbors; la; la = la->al_next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { PUT_ADDR(v->uv_lcl_addr); *p++ = v->uv_metric; *p++ = v->uv_threshold; ncount = p; *p++ = 0; datalen += 4 + 3; } PUT_ADDR(la->al_addr); datalen += 4; (*ncount)++; } } if (datalen != 0) send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL), datalen); } /* * Send a list of all of our neighbors to the requestor, `src'. */ -void accept_neighbor_request2(src, dst) - u_long src, dst; +void +accept_neighbor_request2(src, dst) + u_int32 src, dst; { vifi_t vifi; struct uvif *v; u_char *p, *ncount; struct listaddr *la; int datalen; - u_long us, them = src; + u_int32 us, them = src; /* Determine which of our addresses to use as the source of our response * to this query. */ if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */ int udp; /* find best interface to reply on */ struct sockaddr_in addr; int addrlen = sizeof(addr); addr.sin_family = AF_INET; +#if (defined(BSD) && (BSD >= 199103)) addr.sin_len = sizeof addr; +#endif addr.sin_addr.s_addr = dst; addr.sin_port = htons(2000); /* any port over 1024 will do... */ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0 || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) { log(LOG_WARNING, errno, "Determining local address"); close(udp); return; } close(udp); us = addr.sin_addr.s_addr; } else /* query sent to us alone */ - us = dst; + us = dst; p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { register u_short vflags = v->uv_flags; register u_char rflags = 0; if (vflags & VIFF_TUNNEL) rflags |= DVMRP_NF_TUNNEL; if (vflags & VIFF_SRCRT) rflags |= DVMRP_NF_SRCRT; if (vflags & VIFF_DOWN) rflags |= DVMRP_NF_DOWN; if (vflags & VIFF_DISABLED) rflags |= DVMRP_NF_DISABLED; if (vflags & VIFF_QUERIER) rflags |= DVMRP_NF_QUERIER; + if (vflags & VIFF_LEAF) + rflags |= DVMRP_NF_LEAF; ncount = 0; la = v->uv_neighbors; if (la == NULL) { /* * include down & disabled interfaces and interfaces on * leaf nets. */ if (rflags & DVMRP_NF_TUNNEL) rflags |= DVMRP_NF_DOWN; if (datalen > MAX_DVMRP_DATA_LEN - 12) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; } *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; *p++ = 1; *(u_int*)p = v->uv_rmt_addr; p += 4; datalen += 12; } else { for ( ; la; la = la->al_next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; ncount = p; *p++ = 0; datalen += 4 + 4; } *(u_int*)p = la->al_addr; p += 4; datalen += 4; (*ncount)++; } } } if (datalen != 0) send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL), datalen); } /* * Process an incoming neighbor-list message. */ -void accept_neighbors(src, dst, p, datalen, level) - u_long src, dst, level; - char *p; +void +accept_neighbors(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; int datalen; { log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Process an incoming neighbor-list message. */ -void accept_neighbors2(src, dst, p, datalen, level) - u_long src, dst, level; - char *p; +void +accept_neighbors2(src, dst, p, datalen, level) + u_int32 src, dst, level; + u_char *p; int datalen; { log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s", inet_fmt(src, s1), inet_fmt(dst, s2)); } /* * Update the neighbor entry for neighbor 'addr' on vif 'vifi'. * 'msgtype' is the type of DVMRP message received from the neighbor. * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise. */ -int update_neighbor(vifi, addr, msgtype, p, datalen, level) +int +update_neighbor(vifi, addr, msgtype, p, datalen, level) vifi_t vifi; - u_long addr; + u_int32 addr; int msgtype; char *p; int datalen; - u_long level; + u_int32 level; { register struct uvif *v; register struct listaddr *n; - u_long genid = 0; - u_long router; - int he_hears_me = TRUE; + u_int32 genid = 0; + u_int32 router; + u_int32 send_tables = 0; + int do_reset = FALSE; + int nflags; v = &uvifs[vifi]; + nflags = (level >> 16) & 0xff; /* * Confirm that 'addr' is a valid neighbor address on vif 'vifi'. * IT IS ASSUMED that this was preceded by a call to find_vif(), which * checks that 'addr' is either a valid remote tunnel endpoint or a * non-broadcast address belonging to a directly-connected subnet. * Therefore, here we check only that 'addr' is not our own address * (due to an impostor or erroneous loopback) or an address of the form * {subnet,0} ("the unknown host"). These checks are not performed in * find_vif() because those types of address are acceptable for some * types of IGMP message (such as group membership reports). */ if (!(v->uv_flags & VIFF_TUNNEL) && (addr == v->uv_lcl_addr || addr == v->uv_subnet )) { log(LOG_WARNING, 0, "received DVMRP message from 'the unknown host' or self: %s", inet_fmt(addr, s1)); return (FALSE); } /* - * If we have received a route report from a neighbor, and we believed - * that we had no neighbors on this vif, send a full route report to - * all neighbors on the vif. + * Look for addr in list of neighbors. */ + for (n = v->uv_neighbors; n != NULL; n = n->al_next) { + if (addr == n->al_addr) { + break; + } + } - if (msgtype == DVMRP_REPORT && v->uv_neighbors == NULL) - report(ALL_ROUTES, vifi, - (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group); + /* + * Found it. Reset its timer, and check for a version change + */ + if (n) { + n->al_timer = 0; + /* + * update the neighbors version and protocol number + * if changed => router went down and came up, + * so take action immediately. + */ + if ((n->al_pv != (level & 0xff)) || + (n->al_mv != ((level >> 8) & 0xff))) { + + do_reset = TRUE; + log(LOG_DEBUG, 0, + "version change neighbor %s [old:%d.%d, new:%d.%d]", + inet_fmt(addr, s1), + n->al_pv, n->al_mv, level&0xff, (level >> 8) & 0xff); + + n->al_pv = level & 0xff; + n->al_mv = (level >> 8) & 0xff; + } + } else { + /* + * If not found, add it to the list. If the neighbor has a lower + * IP address than me, yield querier duties to it. + */ + log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x", + inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff, + (level >> 16) & 0xff); + + n = (struct listaddr *)malloc(sizeof(struct listaddr)); + if (n == NULL) + log(LOG_ERR, 0, "ran out of memory"); /* fatal */ + + n->al_addr = addr; + n->al_pv = level & 0xff; + n->al_mv = (level >> 8) & 0xff; + n->al_genid = 0; + + time(&n->al_ctime); + n->al_timer = 0; + n->al_next = v->uv_neighbors; + + /* + * If we thought that we had no neighbors on this vif, send a route + * report to the vif. If this is just a new neighbor on the same + * vif, send the route report just to the new neighbor. + */ + if (v->uv_neighbors == NULL) { + send_tables = (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group; + vifs_with_neighbors++; + } else { + send_tables = addr; + } + + v->uv_neighbors = n; + + if (!(v->uv_flags & VIFF_TUNNEL) && + ntohl(addr) < ntohl(v->uv_lcl_addr)) + v->uv_flags &= ~VIFF_QUERIER; + } + /* * Check if the router gen-ids are the same. * Need to reset the prune state of the router if not. + * Also check for one-way interfaces by seeing if we are in our + * neighbor's list of known routers. */ if (msgtype == DVMRP_PROBE) { - /* if mrouted level > 3.2, analyze further */ - if ((level&0xff) > 3 || - (((level&0xff) == 3) && (((level>>8)&0xff) > 2))) { + /* Check genid neighbor flag. Also check version number; 3.3 and + * 3.4 didn't set this flag. */ + if ((((level >> 16) & 0xff) & NF_GENID) || + (((level & 0xff) == 3) && (((level >> 8) & 0xff) > 2))) { int i; if (datalen < 4) { log(LOG_WARNING, 0, - "received truncated probe message from %s", - inet_fmt(addr, s1)); - return FALSE; + "received truncated probe message from %s (len %d)", + inet_fmt(addr, s1), datalen); + return (FALSE); } for (i = 0; i < 4; i++) ((char *)&genid)[i] = *p++; - datalen -=4; + datalen -= 4; - /* + if (n->al_genid == 0) + n->al_genid = genid; + else if (n->al_genid != genid) { + log(LOG_DEBUG, 0, + "new genid neigbor %s on vif %d [old:%x, new:%x]", + inet_fmt(addr, s1), vifi, n->al_genid, genid); + + n->al_genid = genid; + do_reset = TRUE; + } + + /* * loop through router list and check for one-way ifs. */ - - he_hears_me = FALSE; - + + v->uv_flags |= VIFF_ONEWAY; + while (datalen > 0) { if (datalen < 4) { log(LOG_WARNING, 0, - "received truncated probe message from %s", - inet_fmt(addr, s1)); + "received truncated probe message from %s (len %d)", + inet_fmt(addr, s1), datalen); return (FALSE); } for (i = 0; i < 4; i++) ((char *)&router)[i] = *p++; datalen -= 4; if (router == v->uv_lcl_addr) { - he_hears_me = TRUE; + v->uv_flags &= ~VIFF_ONEWAY; break; } } } } + if (n->al_flags != nflags) { + n->al_flags = nflags; - /* - * Look for addr in list of neighbors; if found, reset its timer. - */ - for (n = v->uv_neighbors; n != NULL; n = n->al_next) { - if (addr == n->al_addr) { - n->al_timer = 0; - - /* If probe message and version no >= 3.3 check genid */ - if (msgtype == DVMRP_PROBE && - ((n->al_pv >= 3 && n->al_mv > 2) || n->al_pv > 3)) { - if (he_hears_me == TRUE && v->uv_flags & VIFF_ONEWAY) - v->uv_flags &= ~VIFF_ONEWAY; - - if (he_hears_me == FALSE) - v->uv_flags |= VIFF_ONEWAY; - - if ((n->al_genid != 0) && (n->al_genid != genid)) { - log(LOG_DEBUG, 0, - "old:%d new:%dreset neighbor %s", - n->al_genid, genid, inet_fmt(addr, s1)); - - reset_neighbor_state(vifi, addr); - n->al_genid = genid; - n->al_pv = level & 0xff; - n->al_mv = (level >> 8) & 0xff; - - /* - * need to do a full route report here - * it gets done by accept_probe() - */ - return (TRUE); - } - } - - /* - * update the neighbors version and protocol number - * if changed => router went down and came up, - * so take action immediately. - */ - if ((n->al_pv != (level & 0xff)) || - ((n->al_mv != (level >> 8)) & 0xff)) { - log(LOG_DEBUG, 0, - "resetting neighbor %s [old:%d.%d, new:%d.%d]", - inet_fmt(addr, s1), - n->al_pv, n->al_mv, level&0xff, (level>>8)&0xff); - - n->al_pv = level & 0xff; - n->al_mv = (level >> 8) & 0xff; - - reset_neighbor_state(vifi, addr); - } - /* recurring probe - so no need to do a route report */ - if (msgtype == DVMRP_PROBE) - return (FALSE); - else - return (TRUE); + if (n->al_flags & NF_LEAF) { + /*XXX If we have non-leaf neighbors then we know we shouldn't + * mark this vif as a leaf. For now we just count on other + * probes and/or reports resetting the timer. */ + if (!v->uv_leaf_timer) + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + } else { + /* If we get a leaf to non-leaf transition, we *must* update + * the routing table. */ + if (v->uv_flags & VIFF_LEAF && send_tables == 0) + send_tables = addr; + v->uv_flags &= ~VIFF_LEAF; + v->uv_leaf_timer = 0; } } - - /* - * If not found, add it to the list. If the neighbor has a lower - * IP address than me, yield querier duties to it. - */ - if (n == NULL) { - n = (struct listaddr *)malloc(sizeof(struct listaddr)); - if (n == NULL) - log(LOG_ERR, 0, "ran out of memory"); /* fatal */ - - n->al_addr = addr; - n->al_pv = level & 0xff; - n->al_mv = (level >> 8) & 0xff; - if (msgtype == DVMRP_PROBE) - n->al_genid = genid; - else - n->al_genid = 0; - - n->al_timer = 0; - n->al_next = v->uv_neighbors; - v->uv_neighbors = n; - - if (!(v->uv_flags & VIFF_TUNNEL) && - ntohl(addr) < ntohl(v->uv_lcl_addr)) - v->uv_flags &= ~VIFF_QUERIER; + if (do_reset) { + reset_neighbor_state(vifi, addr); + if (!send_tables) + send_tables = addr; } + if (send_tables) + report(ALL_ROUTES, vifi, send_tables); return (TRUE); } /* * On every timer interrupt, advance the timer in each neighbor and * group entry on every vif. */ -void age_vifs() +void +age_vifs() { register vifi_t vifi; register struct uvif *v; register struct listaddr *a, *prev_a, *n; - register u_long addr; + register u_int32 addr; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) { + if (v->uv_leaf_timer && (v->uv_leaf_timer -= TIMER_INTERVAL == 0)) { + v->uv_flags |= VIFF_LEAF; + } for (prev_a = (struct listaddr *)&(v->uv_neighbors), a = v->uv_neighbors; a != NULL; prev_a = a, a = a->al_next) { if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME) continue; /* * Neighbor has expired; delete it from the neighbor list, * delete it from the 'dominants' and 'subordinates arrays of * any route entries and assume querier duties unless there is * another neighbor with a lower IP address than mine. */ addr = a->al_addr; prev_a->al_next = a->al_next; free((char *)a); a = prev_a; delete_neighbor_from_routes(addr, vifi); + if (v->uv_neighbors == NULL) + vifs_with_neighbors--; + + v->uv_leaf_timer = LEAF_CONFIRMATION_TIME; + if (!(v->uv_flags & VIFF_TUNNEL)) { v->uv_flags |= VIFF_QUERIER; for (n = v->uv_neighbors; n != NULL; n = n->al_next) { if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) { v->uv_flags &= ~VIFF_QUERIER; - break; } + if (!(n->al_flags & NF_LEAF)) { + v->uv_leaf_timer = 0; + } } } } } } +/* + * Returns the neighbor info struct for a given neighbor + */ +struct listaddr * +neighbor_info(vifi, addr) + vifi_t vifi; + u_int32 addr; +{ + struct listaddr *u; + for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next) + if (u->al_addr == addr) + return u; + + return NULL; +} + /* * Print the contents of the uvifs array on file 'fp'. */ -void dump_vifs(fp) +void +dump_vifs(fp) FILE *fp; { register vifi_t vifi; register struct uvif *v; register struct listaddr *a; + register struct phaddr *p; struct sioc_vif_req v_req; + fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors); + + if (vifs_with_neighbors == 1) + fprintf(fp,"[This host is a leaf]\n\n"); + fprintf(fp, "\nVirtual Interface Table\n%s", - "Vif Name Local-Address "); + "Vif Name Local-Address "); fprintf(fp, "M Thr Rate Flags\n"); for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { - fprintf(fp, "%2u %6s %-15s %6s: %-15s %2u %3u %5u ", + fprintf(fp, "%2u %6s %-15s %6s: %-18s %2u %3u %5u ", vifi, v->uv_name, inet_fmt(v->uv_lcl_addr, s1), (v->uv_flags & VIFF_TUNNEL) ? "tunnel": "subnet", (v->uv_flags & VIFF_TUNNEL) ? inet_fmt(v->uv_rmt_addr, s2) : inet_fmts(v->uv_subnet, v->uv_subnetmask, s3), v->uv_metric, v->uv_threshold, v->uv_rate_limit); if (v->uv_flags & VIFF_ONEWAY) fprintf(fp, " one-way"); if (v->uv_flags & VIFF_DOWN) fprintf(fp, " down"); if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled"); if (v->uv_flags & VIFF_QUERIER) fprintf(fp, " querier"); if (v->uv_flags & VIFF_SRCRT) fprintf(fp, " src-rt"); + if (v->uv_flags & VIFF_LEAF) fprintf(fp, " leaf"); fprintf(fp, "\n"); + if (v->uv_addrs != NULL) { + fprintf(fp, " alternate subnets: %s\n", + inet_fmts(v->uv_addrs->pa_subnet, v->uv_addrs->pa_subnetmask, s1)); + for (p = v->uv_addrs->pa_next; p; p = p->pa_next) { + fprintf(fp, " %s\n", + inet_fmts(p->pa_subnet, p->pa_subnetmask, s1)); + } + } + if (v->uv_neighbors != NULL) { - fprintf(fp, " peers: %s (%d.%d)\n", + fprintf(fp, " peers: %s (%d.%d) (0x%x)\n", inet_fmt(v->uv_neighbors->al_addr, s1), - v->uv_neighbors->al_pv, v->uv_neighbors->al_mv); + v->uv_neighbors->al_pv, v->uv_neighbors->al_mv, + v->uv_neighbors->al_flags); for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) { - fprintf(fp, " %s (%d.%d)\n", - inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv); + fprintf(fp, " %s (%d.%d) (0x%x)\n", + inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv, + a->al_flags); } } if (v->uv_groups != NULL) { fprintf(fp, " groups: %-15s\n", inet_fmt(v->uv_groups->al_addr, s1)); for (a = v->uv_groups->al_next; a != NULL; a = a->al_next) { fprintf(fp, " %-15s\n", inet_fmt(a->al_addr, s1)); } } if (v->uv_acl != NULL) { struct vif_acl *acl; - fprintf(fp, " boundaries: %-15s\n", + fprintf(fp, " boundaries: %-18s\n", inet_fmts(v->uv_acl->acl_addr, v->uv_acl->acl_mask, s1)); for (acl = v->uv_acl->acl_next; acl != NULL; acl = acl->acl_next) { - fprintf(fp, " : %-15s\n", + fprintf(fp, " : %-18s\n", inet_fmts(acl->acl_addr, acl->acl_mask, s1)); } } v_req.vifi = vifi; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) { log(LOG_WARNING, 0, "SIOCGETVIFCNT fails"); } else { - fprintf(fp, " pkts in : %d\n", + fprintf(fp, " pkts in : %ld\n", v_req.icount); - fprintf(fp, " pkts out: %d\n", + fprintf(fp, " pkts out: %ld\n", v_req.ocount); } fprintf(fp, "\n"); } fprintf(fp, "\n"); } /**** the timeout routines ********/ -typedef struct { - vifi_t vifi; - struct listaddr *g; - int q_time; -} cbk_t; - -static cbk_t *cbk; - +static void DelVif(cbk) cbk_t *cbk; { /* -*- make the list consistent */ register vifi_t vifi = cbk->vifi; register struct uvif *v; register struct listaddr *a, *prev_a, *g = cbk->g; v = &uvifs[vifi]; for (prev_a = (struct listaddr *)&(v->uv_groups), a = v->uv_groups; a != NULL; prev_a = a, a = a->al_next) { if (a != g) continue; /* * Group has expired * delete all kernel cache entries with this group */ - if( g->al_query) DeleteTimer(g->al_query); + if (g->al_query) DeleteTimer(g->al_query); delete_lclgrp(vifi, a->al_addr); prev_a->al_next = a->al_next; free((char *)a); a = prev_a; } free(cbk); } - -SetTimer( vifi, g) +static int +SetTimer(vifi, g) vifi_t vifi; struct listaddr *g; { cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->g = g; cbk->vifi = vifi; - return timer_setTimer(g->al_timer,DelVif,cbk); + return timer_setTimer(g->al_timer, (cfunc_t)DelVif, (void *)cbk); } -DeleteTimer( id) +static int +DeleteTimer(id) int id; { timer_clearTimer(id); return 0; } +static void SendQuery(cbk) cbk_t *cbk; { - register struct uvif *v = &uvifs[cbk->vifi]; - send_igmp(v->uv_lcl_addr, cbk->g->al_addr, - IGMP_HOST_MEMBERSHIP_QUERY, - cbk->q_time, 0, 0); - cbk->g->al_query = 0; - free(cbk); + register struct uvif *v = &uvifs[cbk->vifi]; + + send_igmp(v->uv_lcl_addr, cbk->g->al_addr, + IGMP_HOST_MEMBERSHIP_QUERY, + cbk->q_time, 0, 0); + cbk->g->al_query = 0; + free(cbk); } +static int SetQueryTimer(g , vifi, to_expire, q_time) struct listaddr *g; vifi_t vifi; int to_expire, q_time; { cbk = (cbk_t *) malloc(sizeof(cbk_t)); cbk->g = g; cbk->q_time = q_time; cbk-> vifi = vifi; - return timer_setTimer(to_expire,SendQuery,cbk); + return timer_setTimer(to_expire, (cfunc_t)SendQuery, (void *)cbk); } - Index: stable/2.1/usr.sbin/mrouted/vif.h =================================================================== --- stable/2.1/usr.sbin/mrouted/vif.h (revision 10584) +++ stable/2.1/usr.sbin/mrouted/vif.h (revision 10585) @@ -1,62 +1,78 @@ /* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * - * $Id: vif.h,v 1.6 1994/08/24 23:54:47 thyagara Exp $ + * $Id: vif.h,v 1.4 1995/06/28 17:58:49 wollman Exp $ */ /* * User level Virtual Interface structure * * A "virtual interface" is either a physical, multicast-capable interface * (called a "phyint") or a virtual point-to-point link (called a "tunnel"). * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) */ struct uvif { u_short uv_flags; /* VIFF_ flags defined below */ u_char uv_metric; /* cost of this vif */ u_int uv_rate_limit; /* rate limit on this vif */ u_char uv_threshold; /* min ttl required to forward on vif */ - u_long uv_lcl_addr; /* local address of this vif */ - u_long uv_rmt_addr; /* remote end-point addr (tunnels only) */ - u_long uv_subnet; /* subnet number (phyints only) */ - u_long uv_subnetmask; /* subnet mask (phyints only) */ - u_long uv_subnetbcast;/* subnet broadcast addr (phyints only) */ + u_int32 uv_lcl_addr; /* local address of this vif */ + u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */ + u_int32 uv_subnet; /* subnet number (phyints only) */ + u_int32 uv_subnetmask; /* subnet mask (phyints only) */ + u_int32 uv_subnetbcast;/* subnet broadcast addr (phyints only) */ char uv_name[IFNAMSIZ]; /* interface name */ struct listaddr *uv_groups; /* list of local groups (phyints only) */ struct listaddr *uv_neighbors; /* list of neighboring routers */ struct vif_acl *uv_acl; /* access control list of groups */ + int uv_leaf_timer; /* time until this vif is considrd leaf */ + struct phaddr *uv_addrs; /* Additional subnets on this vif */ }; #define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT) #define VIFF_DOWN 0x0100 /* kernel state of interface */ #define VIFF_DISABLED 0x0200 /* administratively disabled */ #define VIFF_QUERIER 0x0400 /* I am the subnet's querier */ #define VIFF_ONEWAY 0x0800 /* Maybe one way interface */ +#define VIFF_LEAF 0x1000 /* all neighbors are leaves */ +struct phaddr { + struct phaddr *pa_next; + u_int32 pa_subnet; /* extra subnet */ + u_int32 pa_subnetmask; /* netmask of extra subnet */ + u_int32 pa_subnetbcast; /* broadcast of extra subnet */ +}; + struct vif_acl { struct vif_acl *acl_next; /* next acl member */ - u_long acl_addr; /* Group address */ - u_long acl_mask; /* Group addr. mask */ + u_int32 acl_addr; /* Group address */ + u_int32 acl_mask; /* Group addr. mask */ }; struct listaddr { struct listaddr *al_next; /* link to next addr, MUST BE FIRST */ - u_long al_addr; /* local group or neighbor address */ + u_int32 al_addr; /* local group or neighbor address */ u_long al_timer; /* for timing out group or neighbor */ - u_long al_genid; /* generation id for neighbor */ + time_t al_ctime; /* neighbor creation time */ + u_int32 al_genid; /* generation id for neighbor */ u_char al_pv; /* router protocol version */ u_char al_mv; /* router mrouted version */ - u_long al_timerid; /* returned by set timer */ - u_long al_query; /* second query in case of leave*/ - u_short al_old; /* if old memberships are present */ - u_short al_last; /* # of query's since last old rep */ + u_long al_timerid; /* returned by set timer */ + u_long al_query; /* second query in case of leave */ + u_short al_old; /* if old memberships are present */ + u_short al_last; /* # of query's since last old rep */ + u_char al_flags; /* flags related to this neighbor */ }; +#define NF_LEAF 0x01 /* This neighbor is a leaf */ +#define NF_PRUNE 0x02 /* This neighbor understands prunes */ +#define NF_GENID 0x04 /* I supply genid & rtrlist in probe*/ +#define NF_MTRACE 0x08 /* I can understand mtrace requests */ #define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */