From 21876867d877dec1bf967dcd74dcfdd83a3ab69e Mon Sep 17 00:00:00 2001 From: wollman Date: Thu, 8 Sep 1994 00:26:13 +0000 Subject: mrouted from multicast 3.3 distribution --- usr.sbin/mrouted/LICENSE | 48 ++ usr.sbin/mrouted/callout.c | 201 ++++++ usr.sbin/mrouted/config.c | 796 ++++++++++++++++++++++++ usr.sbin/mrouted/defs.h | 170 +++++ usr.sbin/mrouted/dvmrp.h | 152 +++++ usr.sbin/mrouted/igmp.c | 289 +++++++++ usr.sbin/mrouted/inet.c | 187 ++++++ usr.sbin/mrouted/kern.c | 183 ++++++ usr.sbin/mrouted/main.c | 439 +++++++++++++ usr.sbin/mrouted/mapper.c | 953 ++++++++++++++++++++++++++++ usr.sbin/mrouted/mrinfo.c | 480 +++++++++++++++ usr.sbin/mrouted/mrouted.8 | 319 ++++++++++ usr.sbin/mrouted/mrouted.conf | 26 + usr.sbin/mrouted/mtrace.c | 459 ++++++++++++++ usr.sbin/mrouted/prune.c | 1370 +++++++++++++++++++++++++++++++++++++++++ usr.sbin/mrouted/prune.h | 123 ++++ usr.sbin/mrouted/route.c | 1076 ++++++++++++++++++++++++++++++++ usr.sbin/mrouted/route.h | 50 ++ usr.sbin/mrouted/vif.c | 1136 ++++++++++++++++++++++++++++++++++ usr.sbin/mrouted/vif.h | 62 ++ 20 files changed, 8519 insertions(+) create mode 100644 usr.sbin/mrouted/LICENSE create mode 100644 usr.sbin/mrouted/callout.c create mode 100644 usr.sbin/mrouted/config.c create mode 100644 usr.sbin/mrouted/defs.h create mode 100644 usr.sbin/mrouted/dvmrp.h create mode 100644 usr.sbin/mrouted/igmp.c create mode 100644 usr.sbin/mrouted/inet.c create mode 100644 usr.sbin/mrouted/kern.c create mode 100644 usr.sbin/mrouted/main.c create mode 100644 usr.sbin/mrouted/mapper.c create mode 100644 usr.sbin/mrouted/mrinfo.c create mode 100644 usr.sbin/mrouted/mrouted.8 create mode 100644 usr.sbin/mrouted/mrouted.conf create mode 100644 usr.sbin/mrouted/mtrace.c create mode 100644 usr.sbin/mrouted/prune.c create mode 100644 usr.sbin/mrouted/prune.h create mode 100644 usr.sbin/mrouted/route.c create mode 100644 usr.sbin/mrouted/route.h create mode 100644 usr.sbin/mrouted/vif.c create mode 100644 usr.sbin/mrouted/vif.h diff --git a/usr.sbin/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE new file mode 100644 index 0000000..ef7da47 --- /dev/null +++ b/usr.sbin/mrouted/LICENSE @@ -0,0 +1,48 @@ + +The mrouted program is covered by the following license. Use of the +mrouted program represents acceptance of these terms and conditions. + +1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license +to use, copy and modify the computer software ``mrouted'' (hereinafter +called the ``Program''), upon the terms and conditions hereinafter set +out and until Licensee discontinues use of the Licensed Program. + +2. LICENSEE acknowledges that the Program is a research tool still in +the development state, that it is being supplied ``as is,'' without any +accompanying services from STANFORD, and that this license is entered +into in order to encourage scientific collaboration aimed at further +development and application of the Program. + +3. LICENSEE may copy the Program and may sublicense others to use object +code copies of the Program or any derivative version of the Program. +All copies must contain all copyright and other proprietary notices found +in the Program as provided by STANFORD. Title to copyright to the +Program remains with STANFORD. + +4. LICENSEE may create derivative versions of the Program. LICENSEE +hereby grants STANFORD a royalty-free license to use, copy, modify, +distribute and sublicense any such derivative works. At the time +LICENSEE provides a copy of a derivative version of the Program to a +third party, LICENSEE shall provide STANFORD with one copy of the source +code of the derivative version at no charge to STANFORD. + +5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION +OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR +THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS, +COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable +for any liability nor for any direct, indirect or consequential damages +with respect to any claim by LICENSEE or any third party on account of or +arising from this Agreement or use of the Program. + +6. This agreement shall be construed, interpreted and applied in +accordance with the State of California and any legal action arising +out of this Agreement or use of the Program shall be filed in a court +in the State of California. + +7. Nothing in this Agreement shall be construed as conferring rights to +use in advertising, publicity or otherwise any trademark or the name +of ``Stanford''. + +The mrouted program is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. diff --git a/usr.sbin/mrouted/callout.c b/usr.sbin/mrouted/callout.c new file mode 100644 index 0000000..0538a3f --- /dev/null +++ b/usr.sbin/mrouted/callout.c @@ -0,0 +1,201 @@ +/* + * 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.1 1994/08/24 23:52:49 thyagara 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 */ + char *data; /* func's data */ + int time; /* time offset to next event*/ +}; + + +callout_init() +{ + Q = (struct timeout_q *) 0; +} + + +/* + * signal handler for SIGALARM that is called once every second + */ +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) + 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 + in_callout = 0; return; + } + } + in_callout = 0; + return; +} + + +/* + * sets the timer + */ +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; + + in_callout = 1; + + /* create a node */ + node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); + if ((int) node <= 0) { + log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); + in_callout = 0; + return -1; + } + node->func = action; + node->data = data; + 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){ + /* 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 { + /* 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; +{ + struct timeout_q *ptr, *prev; + + 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){ + /* got the right node */ + + /* unlink it from the queue */ + 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; +} + +/* + * debugging utility + */ +print_Q() +{ + struct timeout_q *ptr; + +#ifdef IGMP_DEBUG + for(ptr = Q; ptr; ptr = ptr->next) + log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time); +#endif IGMP_DEBUG +} + diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c new file mode 100644 index 0000000..c7aa7b0 --- /dev/null +++ b/usr.sbin/mrouted/config.c @@ -0,0 +1,796 @@ +/* + * 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.6 1994/08/24 23:52:54 thyagara 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() +{ + struct ifreq ifbuf[32]; + struct ifreq *ifrp, *ifend, *mp; + struct ifconf ifc; + register struct uvif *v; + register vifi_t vifi; + int i, n; + u_long 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; + + /* + * 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)); + 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_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; + + 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 || + (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 || + (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 || + (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; + } + } +} diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h new file mode 100644 index 0000000..8239f7c --- /dev/null +++ b/usr.sbin/mrouted/defs.h @@ -0,0 +1,170 @@ +/* + * 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.8 1994/08/24 23:53:23 thyagara Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvmrp.h" +#include "vif.h" +#include "route.h" +#include "prune.h" + +/* + * 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 3 /* increment on local changes or bug fixes, */ + /* reset to 0 whever PROTOCOL_VERSION increments */ + +#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION ) + /* for IGMP 'group' field of DVMRP messages */ + +#define DEL_RTE_GROUP 0 +#define DEL_ALL_ROUTES 1 + /* for Deleting kernel table entries */ + +/* + * External declarations for global variables and functions. + */ +extern char recv_buf[MAX_IP_PACKET_LEN]; +extern char send_buf[MAX_IP_PACKET_LEN]; +extern int igmp_socket; +extern u_long allhosts_group; +extern u_long dvmrp_group; +extern u_long 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 char s1[]; +extern char s2[]; +extern char s3[]; + +extern int errno; +extern int sys_nerr; +extern char * sys_errlist[]; + +extern void log(); + +extern void init_igmp(); +extern void accept_igmp(); +extern void send_igmp(); + +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(); + +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(); + +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(); + +extern void accept_group_report(); +extern void query_groups(); +extern void probe_for_neighbors(); +extern int update_neighbor(); +extern void accept_neighbor_request(); + +extern void config_vifs_from_kernel(); +extern void config_vifs_from_file(); + +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(); + +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(); + +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 chkgrp_graft(); +extern void accept_graft(); +extern void send_graft_ack(); +extern void send_graft(); +extern void accept_g_ack(); +extern void mtrace(); + +extern char * malloc(); +extern char * fgets(); +extern FILE * fopen(); + +#ifndef htonl +extern u_long htonl(); +extern u_long ntohl(); +#endif diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h new file mode 100644 index 0000000..5decfdb --- /dev/null +++ b/usr.sbin/mrouted/dvmrp.h @@ -0,0 +1,152 @@ +/* + * 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 $ + */ + +/* + * 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_DOWN 0x10 /* kernel state of interface */ +#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ +#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ + +/* + * 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 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 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 DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */ +#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */ + +#define OLD_AGE_THRESHOLD 2 diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c new file mode 100644 index 0000000..f44e2f1 --- /dev/null +++ b/usr.sbin/mrouted/igmp.c @@ -0,0 +1,289 @@ +/* + * 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: igmp.c,v 1.8 1994/08/24 23:53:32 thyagara 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 */ +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 */ + +/* + * Open and initialize the igmp socket, and fill in the non-changing + * IP header fields in the output packet buffer. + */ +void init_igmp() +{ + struct ip *ip; + + 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_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); +} + +/* %%% 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; +{ + 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_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_GRAFT_ACK: return "graft message ack "; + default: return "unknown DVMRP msg "; + } + case IGMP_PIM: /* %%% hack for 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) + int recvlen; +{ + register vifi_t vifi; + register u_long 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 + * necessary to install a route into the kernel for this. + */ + if (ip->ip_p == 0) { + if (src == NULL || dst == NULL) + 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 */ + return; + + case IGMP_HOST_MEMBERSHIP_REPORT: + case IGMP_HOST_NEW_MEMBERSHIP_REPORT: + accept_group_report(src, dst, group,igmp->igmp_type); + return; + + case IGMP_HOST_LEAVE_MESSAGE: + leave_group_message(src, dst, group); + return; + + case IGMP_DVMRP: + switch (igmp->igmp_code) { + + case DVMRP_PROBE: + accept_probe(src, dst, + (char *)(igmp+1), igmpdatalen, group); + return; + + case DVMRP_REPORT: + accept_report(src, dst, + (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); + return; + + case DVMRP_NEIGHBORS2: + accept_neighbors2(src, dst, (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 %%% */ + return; + + case IGMP_MTRACE: + 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", + igmp->igmp_type, inet_fmt(src, s1), + inet_fmt(dst, s2)); + return; + } +} + + +/* + * 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; + int type, code; + u_long group; + int datalen; +{ + static struct sockaddr_in sdst = {AF_INET}; + 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); + + 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, "sendto on %s", inet_fmt(src, s1)); + } + + 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)); +} diff --git a/usr.sbin/mrouted/inet.c b/usr.sbin/mrouted/inet.c new file mode 100644 index 0000000..5d7442b --- /dev/null +++ b/usr.sbin/mrouted/inet.c @@ -0,0 +1,187 @@ +/* + * 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 $ + */ + + +#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(). */ + + +/* + * 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; +{ + register u_long 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. + */ +int inet_valid_subnet(nsubnet, nmask) + u_long nsubnet, nmask; +{ + register u_long subnet, mask; + + subnet = ntohl(nsubnet); + mask = ntohl(nmask); + + if ((subnet & mask) != subnet) return (FALSE); + + 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_CLASSC(subnet)) { + if (mask < 0xffffff00) 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 *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. + */ +char *inet_fmts(addr, mask, s) + u_long addr, mask; + char *s; +{ + register u_char *a, *m; + + a = (u_char *)&addr; + m = (u_char *)&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]); + + 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) + char *s; +{ + u_long a; + 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) + 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 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + 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); +} diff --git a/usr.sbin/mrouted/kern.c b/usr.sbin/mrouted/kern.c new file mode 100644 index 0000000..a44a15f --- /dev/null +++ b/usr.sbin/mrouted/kern.c @@ -0,0 +1,183 @@ +/* + * 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.6 1994/08/24 23:53:37 thyagara 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; +{ + 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; +{ + 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; +{ + 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, + (char *)NULL, 0) < 0) + log(LOG_ERR, errno, "can't enable DVMRP routing in kernel"); +} + + +void k_stop_dvmrp() +{ + if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE, + (char *)NULL, 0) < 0) + log(LOG_WARNING, errno, "can't disable DVMRP 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, + (char *)&vc, sizeof(vc)) < 0) + log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF"); +} + + +void k_del_vif(vifi) + vifi_t vifi; +{ + if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF, + (char *)&vifi, sizeof(vifi)) < 0) + log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF"); +} + + +/* + * Adds a (source, mcastgrp) entry to the kernel + */ +void k_add_rg(kt) + struct ktable *kt; +{ + struct mfcctl mc; + + /* copy table values so that setsockopt can process it */ + COPY_TABLES(kt, mc); + + /* write to kernel space */ + if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MFC, + (char *)&mc, sizeof(mc)) < 0) + log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MFC"); +} + + +/* + * Deletes a (source, mcastgrp) entry from the kernel + */ +void k_del_rg(kt) + struct ktable *kt; +{ + struct mfcctl mc; + + /* copy table values so that setsockopt can process it */ + COPY_TABLES(kt, mc); + + /* 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"); +} diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c new file mode 100644 index 0000000..22a0214 --- /dev/null +++ b/usr.sbin/mrouted/main.c @@ -0,0 +1,439 @@ +/* + * 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.8 1994/08/24 23:53:42 thyagara 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" + +extern char *configfilename; + +static char pidfilename[] = "/etc/mrouted.pid"; +static char dumpfilename[] = "/usr/tmp/mrouted.dump"; +static char cachefilename[] = "/usr/tmp/mrouted.cache"; +static char genidfilename[] = "/usr/tmp/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 */ + + +/* + * Forward declarations. + */ +static void fasttimer(); +static void timer(); +static void hup(); +static void dump(); +static void fdump(); +static void cdump(); +static void restart(); + +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; + + setlinebuf(stderr); + + 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: 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); + t = open("/dev/tty", 2); + if (t >= 0) { + (void)ioctl(t, TIOCNOTTY, (char *)0); + (void)close(t); + } + } + 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); + + srandom(gethostid()); + + /* + * Get generation id + */ + gettimeofday(&tv, &tzp); + 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(); + init_igmp(); + k_init_dvmrp(); /* enable DVMRP routing in kernel */ + init_routes(); + init_ktable(); + init_vifs(); + + if (debug) + fprintf(stderr, "pruning %s\n", pruning ? "on" : "off"); + + fp = fopen(pidfilename, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + (void) fclose(fp); + } + + if (debug >= 2) dump(); + + (void)signal(SIGALRM, fasttimer); + + (void)signal(SIGHUP, restart); + (void)signal(SIGTERM, hup); + (void)signal(SIGINT, hup); + (void)signal(SIGUSR1, fdump); + (void)signal(SIGUSR2, cdump); + if (debug != 0) + (void)signal(SIGQUIT, dump); + + (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; + } + omask = sigblock(sigmask(SIGALRM)); + accept_igmp(recvlen); + (void)sigsetmask(omask); + } +} + + +/* + * routine invoked every second. It's 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 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 */ + 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() +{ + 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); + } + + /* + * Advance virtual time + */ + virtual_time += TIMER_INTERVAL; +} + + +/* + * On hangup signal, let everyone know we're going away. + */ +static void hup() +{ + log(LOG_INFO, 0, "hup"); + expire_all_routes(); + report_to_all_neighbors(ALL_ROUTES); + exit(1); +} + + +/* + * Dump internal data structures to stderr. + */ +static void dump() +{ + dump_vifs(stderr); + dump_routes(stderr); +} + + +/* + * Dump internal data structures to a file. + */ +static void fdump() +{ + 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() +{ + FILE *fp; + + fp = fopen(cachefilename, "w"); + if (fp != NULL) { + dump_cache(fp); + (void) fclose(fp); + } +} + + +/* + * Restart mrouted + */ +static void restart() +{ + register int omask; + + log(LOG_INFO, 0, "restart"); + + /* + * reset all the entries + */ + omask = sigblock(sigmask(SIGALRM)); + free_all_prunes(); + free_all_routes(); + stop_all_vifs(); + k_stop_dvmrp(); + + /* + * 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) + int severity, syserr; + char *format; + int a, b, c, d, e; +{ + char fmt[100]; + + 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); + 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_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); + + if (severity <= LOG_ERR) exit(-1); + } +} diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c new file mode 100644 index 0000000..64a05b5 --- /dev/null +++ b/usr.sbin/mrouted/mapper.c @@ -0,0 +1,953 @@ +/* Mapper for connections between MRouteD multicast routers. + * Written by Pavel Curtis + * + * $Id: mapper.c,v 1.8 1994/08/24 23:53:54 thyagara 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 "defs.h" + +#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_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 */ + 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 */ + 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 */ +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(addr, ptr) + u_long 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; + 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; + 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; +{ + char fmt[100]; + + 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); + 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; +{ + send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, + htonl(MROUTED_LEVEL), 0); +} + +void ask2(dst) + u_long 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; +{ + 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; +{ + 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; + 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; +{ + 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; +{ + 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_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++) + + /* if node is running a recent mrouted, ask for additional info */ + if (level != 0) { + node->version = ntohl(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_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; + 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; + 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_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; + 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; + 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; + 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; +{ + struct hostent *e; + + e = gethostbyaddr(&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; + 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); + 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) + 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 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: + 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; + 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), + 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() +{ +} +void accept_graft() +{ +} +void accept_g_ack() +{ +} +void add_table_entry() +{ +} +void leave_group_message() +{ +} +void mtrace() +{ +} diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c new file mode 100644 index 0000000..d774cdf --- /dev/null +++ b/usr.sbin/mrouted/mrinfo.c @@ -0,0 +1,480 @@ +/* + * 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.7 1994/08/24 23:54:04 thyagara Exp $"; +/* original rcsid: + "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)"; +*/ +#endif + +#include +#include +#include "defs.h" + +#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 */ +int debug = 0; +int retries = DEFAULT_RETRIES; +int timeout = DEFAULT_TIMEOUT; +int target_level; +vifi_t numvifs; /* to keep loader happy */ + /* (see COPY_TABLES macro called in kern.c) */ + +char * +inet_name(addr) + u_long addr; +{ + struct hostent *e; + + e = gethostbyaddr(&addr, sizeof(addr), AF_INET); + + return e ? 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. + */ +void +log(severity, syserr, format, a, b, c, d, e) + int severity, syserr; + char *format; + int a, b, c, d, e; +{ + char fmt[100]; + + 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); + 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; +{ + send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS, + htonl(MROUTED_LEVEL), 0); +} + +void +ask2(dst) + u_long 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; + 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++) + + printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src)); + while (p < ep) { + register u_long 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; + 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; + int datalen; +{ + u_char *ep = p + datalen; + + printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src), + target_level & 0xff, (target_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; + + p += 4; + metric = *p++; + thresh = *p++; + flags = *p++; + ncount = *p++; + while (--ncount >= 0) { + register u_long neighbor = *(u_long*)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_QUERIER) + printf("/querier"); + if (flags & DVMRP_NF_DISABLED) + printf("/disabled"); + if (flags & DVMRP_NF_DOWN) + printf("/down"); + printf("]\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) + 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 argc; + char *argv[]; +{ + setlinebuf(stderr); + + 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 '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 || (argc == 1 && !(target_addr = host_addr(argv[0])))) { +usage: fprintf(stderr, + "Usage: mrinfo [-t timeout] [-r retries] router\n"); + 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; + 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); + + /* Main receive loop */ + for (;;) { + fd_set fds; + struct timeval tv; + int count, recvlen, dummy = 0; + register u_long 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; + + 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) + exit(1); + if (target_level == 0) + ask(target_addr); + else + ask2(target_addr); + continue; + } + recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), + 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; + 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: + 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); + exit(0); + } + break; + + case DVMRP_NEIGHBORS2: + accept_neighbors2(src, dst, (char *)(igmp + 1), + igmpdatalen); + exit(0); + } + } +} + +/* dummies */ +void accept_probe() +{ +} +void accept_group_report() +{ +} +void accept_neighbor_request2() +{ +} +void accept_report() +{ +} +void accept_neighbor_request() +{ +} +void accept_prune() +{ +} +void accept_graft() +{ +} +void accept_g_ack() +{ +} +void add_table_entry() +{ +} +void check_vif_state() +{ +} +void leave_group_message() +{ +} +void mtrace() +{ +} diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8 new file mode 100644 index 0000000..5b9ee96 --- /dev/null +++ b/usr.sbin/mrouted/mrouted.8 @@ -0,0 +1,319 @@ +'\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University. +.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 +.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 +In order to support multicasting among subnets that are separated by (unicast) +routers that do not support IP multicasting, +.I mrouted +includes support for +"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 +The tunnelling mechanism allows +.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. +.I Mrouted +suffers from the well-known scaling problems of any distance-vector +routing protocol, and does not (yet) support hierarchical multicast routing. +.PP +.I Mrouted +handles multicast routing only; there may or may not be unicast routing +software running on the same machine as +.IR mrouted . +With the use of tunnels, it +is not necessary for +.I mrouted +to have access to more than one physical subnet +in order to perform multicast forwarding. +.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: +.IP "level 1" +all syslog'ed messages are also printed to stderr. +.IP "level 2" +all level 1 messages plus notifications of "significant" +events are printed to stderr. +.IP "level 3" +all level 2 messages plus notifications of all packet +arrivals and departures are printed to stderr. +.SH CONFIGURATION +.PP +.I Mrouted +automatically configures itself to forward on all multicast-capable +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: +.nf + + phyint [disable] [metric ] + [threshold ] [rate_limit ] + [boundary /] + + tunnel [metric ] + [threshold ] [srcrt] [rate_limit ] + [boundary /] + + cache_lifetime + + pruning + +.fi +.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 +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. +Phyint commands must precede tunnel commands. +.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 +.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 +The pruning option is provided for +.IR mrouted +to act as a non-pruning router. It is also possible to start +.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 +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 +.I mrouted +cannot route along paths with a sum of metrics greater +than 31. +.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. +.LP +In general, all +.IR mrouted s +connected to a particular subnet or tunnel should +use the same metric and threshold for that subnet or tunnel. +.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 +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 +.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 +.I mrouted +configuration would be better replaced by more +direct tunnels (i.e., eliminate the middle man). +.SH SIGNALS +.PP +.I Mrouted +responds to the following signals: +.IP HUP +restarts +.I mrouted . +The configuration file is reread every time this signal is evoked. +.IP INT +terminates execution gracefully (i.e., by sending +good-bye messages to all neighboring routers). +.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 +.I mrouted +was invoked with a non-zero debug level). +.bp +.SH EXAMPLE +.PP +The routing tables look like this: +.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* + . + . + . + +.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 +.I mrouted +is the one responsible for sending periodic group +membership queries on the vif 0 and vif 1 subnets, as indicated by the +"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 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 +.I mrouted. +.PP +The cache tables look like this: +.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 + +.fi +Each entry is characterized by the origin subnet number and the +destination multicast group. The '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 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 +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 "P" in the Psnt field. The Forwvifs field shows the +interfaces along which datagrams belonging to the source-group are +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 "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 +/etc/mrouted.conf +.SH SEE ALSO +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 diff --git a/usr.sbin/mrouted/mrouted.conf b/usr.sbin/mrouted/mrouted.conf new file mode 100644 index 0000000..5340bfc --- /dev/null +++ b/usr.sbin/mrouted/mrouted.conf @@ -0,0 +1,26 @@ +# $Id: mrouted.conf,v 1.5 1994/08/24 23:54:21 thyagara 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 +# pruning on +# +# phyint [disable] [metric ] [threshold ] [rate_limit ] +# [boundary /] +# tunnel [srcrt] [metric ] +# [threshold ] [rate_limit ] +# [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 +# + +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 diff --git a/usr.sbin/mrouted/mtrace.c b/usr.sbin/mrouted/mtrace.c new file mode 100644 index 0000000..9bee6b2 --- /dev/null +++ b/usr.sbin/mrouted/mtrace.c @@ -0,0 +1,459 @@ +#include +#include +#include "defs.h" + +#define DEFAULT_TIMEOUT 10 /* How long to wait before retrying requests */ + +int timeout = DEFAULT_TIMEOUT; + +vifi_t numvifs; /* to keep loader happy */ + /* (see COPY_TABLES macro called in kern.c) */ + + +char * +inet_name(addr) + u_long addr; +{ + struct hostent *e; + + e = gethostbyaddr(&addr, sizeof(addr), AF_INET); + + return e ? e->h_name : "?"; +} + +u_long +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; +} +char * +proto_type(type) + u_char type; +{ + switch (type) { + case PROTO_DVMRP: + return ("PROTO_DVMRP"); + case PROTO_MOSPF: + return ("PROTO_MOSPF"); + case PROTO_PIM: + return ("PROTO_PIM"); + case PROTO_CBT: + return ("PROTO_CBT"); + default: + return ("PROTO_UNKNOWN"); + } +} + +char * +flag_type(type) + u_char type; +{ + switch (type) { + case TR_NO_ERR: + return ("NO_ERR"); + case TR_WRONG_IF: + return ("WRONG_IF"); + case TR_PRUNED: + return ("PRUNED"); + case TR_SCOPED: + return ("SCOPED"); + case TR_NO_RTE: + return ("NO_RTE"); + default: + return ("INVALID ERR"); + } +} + +int +t_diff(a_sec, a_usec, b_sec, b_usec) + u_long a_sec, a_usec, b_sec, b_usec; +{ + int d = a_sec - b_sec; + int ms = a_usec - b_usec; + + if ((d < 0) || + ((d == 0) && (ms < 0))) { + d = b_sec - a_sec; + ms = b_usec - a_usec; + } + + switch (d) { + case 0: + break; + case 2: + ms += 1000000; + case 1: + ms += 1000000; + break; + default: + ms += (1000000) * d; + } + return (ms/1000); +} + +main(argc, argv) +int argc; +char *argv[]; +{ + struct timeval tq; + struct timezone tzp; + u_long resptime; + + int udp; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + u_long lcl_addr = 0; /* in NET order */ + + 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; + + struct tr_query *query; + + struct tr_rlist *tr_rlist = NULL; + + char *p; + int datalen = 0; + + int i; + int done = 0; + + argv++, argc--; + + 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]); + break; + } else + goto usage; + case 'g': + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + qgrp = host_addr(argv[0]); + break; + } else + goto usage; + case 'd': + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + qdst = host_addr(argv[0]); + break; + } else + goto usage; + case 'x': + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + dst = host_addr(argv[0]); + break; + } else + goto usage; + case 't': + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + qttl = atoi(argv[0]); + if (qttl < 1) + qttl = 1; + break; + } else + goto usage; + case 'n': + if (argc > 1 && isdigit(*(argv + 1)[0])) { + 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; + } + argv++, argc--; + } + + if (argc > 0) { +usage: printf("usage: mtrace -s -g -d -n <# reports> \n"); + printf(" -t [-x ] [-r ] [-l ]\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 */ + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = qgrp; + addr.sin_port = htons(2000); + + 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); + lcl_addr = addr.sin_addr.s_addr; + + /* Got the local address now */ + /* Now, make up the IGMP packet to send */ + + query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN); + + 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; + + datalen += sizeof(struct tr_query); + + if (IN_MULTICAST(ntohl(qgrp))) + k_set_ttl(qttl); + else + k_set_ttl(1); + + if (dst == NULL) + dst = qgrp; + + /* + * set timer to calculate delays & send query + */ + gettimeofday(&tq, &tzp); + + send_igmp(lcl_addr, dst, IGMP_MTRACE, qno, + qgrp, datalen); + + /* + * If the response is to be a multicast address, make sure we + * are listening on that multicast address. + */ + if (IN_MULTICAST(ntohl(raddr))) + k_join(raddr, lcl_addr); + + /* Wait for our reply now */ + while (!done) { + fd_set fds; + struct timeval tv; + struct timezone tzp; + + int count, recvlen, dummy = 0; + register u_long src, dst, group, smask; + struct ip *ip; + struct igmp *igmp; + struct tr_resp *resp; + int ipdatalen, iphdrlen, igmpdatalen; + int rno; + + FD_ZERO(&fds); + FD_SET(igmp_socket, &fds); + + /* need to input timeout as optional argument */ + 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) { + printf("Timed out receiving responses\n"); + exit(1); + } + + recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf), + 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; + + 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; + } + + 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 (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; + } + + query = (struct tr_query *)(igmp + 1); + + /* If this is query with a different id, ignore! */ + if (query->tr_qid != qid) + continue; + + /* + * 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); + + rno = (igmpdatalen - QLEN)/RLEN; + + /* + * print the responses out in reverse order (from src to dst) + */ + printf("src: <%s> grp: <%s> dst: <%s>\n\n", inet_fmt(qsrc, s1), + inet_fmt(qgrp, s2), inet_fmt(qdst, s3)); + + VAL_TO_MASK(smask, (resp+rno-1)->tr_smask); + + if (((resp+rno-1)->tr_inaddr & smask) == (qsrc & smask)) + printf(" %-15s \n", inet_fmt(qsrc, s1)); + else + printf(" * * *\n"); + + resptime = 0; + 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 >> 16, (r->tr_qarr & 0xffff) << 4, + tq.tv_sec & 0xffff, tq.tv_usec)); + printf("hop: %d ms ", + t_diff(resptime >> 16, (resptime & 0xffff) << 4, + r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4)); + 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); + } +} +/* dummies */ +void log() +{ +} +void accept_probe() +{ +} +void accept_group_report() +{ +} +void accept_neighbors() +{ +} +void accept_neighbors2() +{ +} +void accept_neighbor_request2() +{ +} +void accept_report() +{ +} +void accept_neighbor_request() +{ +} +void accept_prune() +{ +} +void accept_graft() +{ +} +void accept_g_ack() +{ +} +void add_table_entry() +{ +} +void check_vif_state() +{ +} +void mtrace() +{ +} +void leave_group_message() +{ +} diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c new file mode 100644 index 0000000..04387a1 --- /dev/null +++ b/usr.sbin/mrouted/prune.c @@ -0,0 +1,1370 @@ +/* + * 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 1994/08/24 23:54:33 thyagara Exp $ + */ + + +#include "defs.h" + +extern int cache_lifetime; +extern int max_prune_lifetime; + +/* + * dither cache lifetime to obtain a value between x and 2*x + */ +#define CACHE_LIFETIME(x) ((x) + (random() % (x))) + +#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 */ +unsigned int kroutes; /* current number of cache entries */ + + +/* + * Initialize the kernel table structure + */ +void init_ktable() +{ + kernel_rtable = NULL; + kroutes = 0; +} + +/* + * Determine if mcastgrp has a listener on vifi + */ +int grplst_mem(vifi, mcastgrp) + vifi_t vifi; + u_long 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) + return 1; + + return 0; +} + +/* + * Updates the ttl values for each vif. + */ +void prun_add_ttls(kt) + struct ktable *kt; +{ + struct uvif *v; + vifi_t vifi; + + 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] = NULL; + } +} + +/* + * checks for scoped multicast addresses + */ +#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) + vifi_t vifi; + u_long 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; +} + +/* + * Add a new table entry for (origin, mcastgrp) + */ +void add_table_entry(origin, mcastgrp) + u_long origin; + u_long mcastgrp; +{ + struct rtentry *r; + struct ktable *kt; + int i; + + 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)); + return; + } + + r = determine_route(origin); + + /* 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 */ + + kroutes++; + + /* add the new values in */ + 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; + } + 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); + + /* 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, mcastgrp)) + VIFM_SET(i, kt->kt_grpmems); + } + GET_SCOPE(kt); + if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) + kt->kt_grpmems = NULL; + else + kt->kt_grpmems &= ~kt->kt_scope; + } + + /* update the kernel_rtable pointer */ + kt->kt_next = kernel_rtable; + kernel_rtable = kt; + + /* update ttls and add entry into kernel */ + prun_add_ttls(kt); + k_add_rg(kt); + + log(LOG_DEBUG, 0, "add entry s:%x g:%x gm:%x", + kt->kt_origin, kt->kt_mcastgrp, kt->kt_grpmems); + + /* 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. + */ + if (!kt->kt_grpmems && kt->kt_gateway) + send_prune(kt); +} + +/* + * An mrouter has gone down and come up on an interface + * Forward on that interface immediately + */ +void reset_neighbor_state(vifi, addr) + vifi_t vifi; + u_long addr; +{ + struct ktable *prev_kt, *kt; + struct prunlst *prev_krl, *krl; + + /* 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); + } + free((char *)kt); + kt = prev_kt; + kroutes--; + continue; + } + + /* + * 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; + } + + 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); + + if (VIFM_ISSET(vifi, kt->kt_leaves) && + grplst_mem(vifi, kt->kt_mcastgrp)) + VIFM_SET(vifi, kt->kt_grpmems); + + kt->kt_grpmems &= ~kt->kt_scope; + prun_add_ttls(kt); + k_add_rg(kt); + } + } +} + +/* + * Delete table entry from the kernel + * del_flag determines how many entries to delete + */ +void del_table_entry(r, mcastgrp, del_flag) + struct rtentry *r; + u_long mcastgrp; + u_int del_flag; +{ + struct mfcctl mc; + struct ktable *kt, *prev_kt; + struct prunlst *krl; + + 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 %x grp %x", + kt->kt_origin, mcastgrp); + + 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); + } + + /* free the source mcastgrp entry */ + prev_kt->kt_next = kt->kt_next; + free((char *)kt); + kroutes--; + kt = prev_kt; + } + } + } + + 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 src %x grp %x", + kt->kt_origin, mcastgrp); + + 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); + } + + /* free the source mcastgrp entry */ + prev_kt->kt_next = kt->kt_next; + free((char *)kt); + kroutes--; + break; + } + } + } +} + +/* + * update kernel table entry when a route entry changes + */ +void update_table_entry(r) + struct rtentry *r; +{ + struct ktable *kt; + struct prunlst *krl; + 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; + + if (kt->kt_leaves != r->rt_leaves) + changed++; + if (kt->kt_children != r->rt_children) + changed++; + if (kt->kt_parent != r->rt_parent) + changed++; + + if (!changed) + continue; + + log(LOG_DEBUG, 0, "update entry: s %-15s g %-15s", + inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2)); + + /* free prun list entries */ + while (kt->kt_rlist) { + krl = kt->kt_rlist; + kt->kt_rlist = krl->rl_next; + free((char *)krl); + } + + 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); + + /* 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 = NULL; + 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); + } + } +} + + + +/* + * set the forwarding flag for all mcastgrps on this vifi + */ +void update_lclgrp(vifi, mcastgrp) + vifi_t vifi; + u_long mcastgrp; +{ + struct ktable *kt; + + log(LOG_DEBUG, 0, "group %x joined at vif %d", mcastgrp, vifi); + + 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 == NULL) + continue; + prun_add_ttls(kt); + k_add_rg(kt); + } +} + +/* + * reset forwarding flag for all mcastgrps on this vifi + */ +void delete_lclgrp(vifi, mcastgrp) + vifi_t vifi; + u_long mcastgrp; +{ + + struct ktable *kt; + + log(LOG_DEBUG, 0, "group %x left at vif %d", mcastgrp, vifi); + + for (kt = kernel_rtable; kt; kt = kt->kt_next) + if (kt->kt_mcastgrp == mcastgrp) { + VIFM_CLR(vifi, kt->kt_grpmems); + prun_add_ttls(kt); + k_add_rg(kt); + + /* + * If there are no more members of this particular group, + * send prune upstream + */ + if (kt->kt_grpmems == NULL && kt->kt_gateway) + send_prune(kt); + } +} + +/* + * 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]; + for (i = 0; i < 4; i++) + *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; + char *p; + int datalen; +{ + u_long prun_src; + u_long prun_dst; + u_long prun_tmr; + vifi_t vifi; + int i; + int stop_sending; + struct ktable *kt; + struct prunlst *pr_recv; + struct prunlst *krl; + 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; + } + + if (datalen < 0 || datalen > 12) + { + log(LOG_WARNING, 0, + "received non-decipherable prune report 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++; + for (i = 0; i< 4; i++) + ((char *)&prun_tmr)[i] = *p++; + + kt = find_src_grp(prun_src, prun_dst); + + if (kt == NULL) + { + log(LOG_WARNING, 0, "prune message received incorrectly"); + return; + } + + if (!VIFM_ISSET(vifi, kt->kt_children)) + { + log(LOG_INFO, 0, + "ignoring prune report from non-child %s", inet_fmt(src, s1)); + 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; + } + + 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); + + /* allocate space for the prune structure */ + pr_recv = (struct prunlst *)(malloc(sizeof(struct prunlst))); + + 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; + } + + 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(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) + vifi_t vifi; + u_long mcastgrp; +{ + struct ktable *kt; + + 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); + /* + * 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 == NULL) + continue; + + /* set the flag for graft retransmission */ + kt->kt_grftsnt = 1; + + /* send graft upwards */ + send_graft(kt); + + /* reset the prune timer and update cache timer*/ + kt->kt_prsent_timer = 0; + kt->kt_timer = max_prune_lifetime; + + prun_add_ttls(kt); + k_add_rg(kt); + } +} + +/* determine the multicast group and src + * + * 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] + */ +void accept_graft(src, dst, p, datalen) + u_long src; + u_long dst; + char *p; + int datalen; +{ + vifi_t vifi; + u_long prun_src; + u_long prun_dst; + struct ktable *kt; + int i; + struct prunlst *krl; + struct prunlst *prev_krl; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring graft report from non-neighbor %s", inet_fmt(src, s1)); + return; + } + + if (datalen < 0 || datalen > 8) { + log(LOG_WARNING, 0, + "received non-decipherable graft report 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++; + + 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 + */ + 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; + + kt->kt_prun_count--; + VIFM_SET(vifi, kt->kt_grpmems); + prun_add_ttls(kt); + k_add_rg(kt); + break; + } + + /* send ack downstream */ + send_graft_ack(kt, src); + kt->kt_timer = max_prune_lifetime; + + if (kt->kt_prsent_timer) { + /* set the flag for graft retransmission */ + kt->kt_grftsnt = 1; + + /* send graft upwards */ + send_graft(kt); + + /* reset the prune sent timer */ + kt->kt_prsent_timer = 0; + } +} + +/* + * 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; + + 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, "send graft ack for src:%x, grp:%x to %x", + kt->kt_origin, kt->kt_mcastgrp, dst); +} + +/* + * 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); + } + log(LOG_DEBUG, 0, "send graft for src:%x, grp:%x up to %x", + kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway); +} + +/* + * 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 + * have send a graft again + */ +void accept_g_ack(src, dst, p, datalen) + u_long src; + u_long dst; + char *p; + int datalen; +{ + vifi_t vifi; + u_long grft_src; + u_long grft_dst; + struct ktable *kt; + int i; + + if ((vifi = find_vif(src, dst)) == NO_VIF) { + log(LOG_INFO, 0, + "ignoring graft ack report from non-neighbor %s", inet_fmt(src, s1)); + return; + } + + if (datalen < 0 || datalen > 8) { + log(LOG_WARNING, 0, + "received non-decipherable graft ack report 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)", + 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)); + return; + } + + if (kt->kt_grftsnt) + kt->kt_grftsnt = 0; +} + + +/* + * free all prune entries + */ +void free_all_prunes() +{ + register struct ktable *kt; + register struct prunlst *krl; + + while (kernel_rtable != NULL) { + kt = kernel_rtable; + kernel_rtable = kt->kt_next; + + while (kt->kt_rlist != NULL) { + krl = kt->kt_rlist; + kt->kt_rlist = krl->rl_next; + free((char *)krl); + } + + free((char *)kt); + kroutes--; + } +} + + +/* + * 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() +{ + struct ktable *kt; + struct ktable *prev_kt; + struct prunlst *krl; + struct prunlst *prev_krl; + + 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; + + /* decrement prune timer if need be */ + if (kt->kt_prsent_timer) + kt->kt_prsent_timer -= ROUTE_MAX_REPORT_DELAY; + + /* retransmit graft if graft sent flag is still set */ + if (kt->kt_grftsnt) { + register int y; + CHK_GS(kt->kt_grftsnt++, y); + if (y) + send_graft(kt); + } + + /* 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 + 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) { + if ((krl->rl_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) { + log(LOG_DEBUG, 0, "forw again s %x g%x on vif %d", + kt->kt_origin, kt->kt_mcastgrp, krl->rl_vifi); + + 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); + } + + kt->kt_prun_count--; + prev_krl->rl_next = krl->rl_next; + free((char *)krl); + krl = prev_krl; + + if (krl == NULL) + break; + } + } + + if (kt->kt_timer <= 0) { + /* + * If there are prune entries still outstanding, + * update the cache timer otherwise expire entry. + */ + if (kt->kt_rlist) { + kt->kt_timer = CACHE_LIFETIME(cache_lifetime); + } + else { + log(LOG_DEBUG, 0, "age route s %x g %x", + kt->kt_origin, kt->kt_mcastgrp); + + 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); + } + + free((char *)kt); + kroutes--; + kt = prev_kt; + } + } + } +} + +/* + * Print the contents of the routing table on file 'fp'. + */ +void dump_cache(fp2) + FILE *fp2; +{ + register struct ktable *kt; + register struct prunlst *krl; + register int i; + register int count; + + fprintf(fp2, + "Multicast Routing Cache Table (%d entries)\n%s", kroutes, + " Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs\n"); + + for (kt = kernel_rtable, count = 0; kt != NULL; kt = kt->kt_next) { + + fprintf(fp2, " %-15s %-15s", + inet_fmts(kt->kt_origin, kt->kt_originmask, s1), + inet_fmt(kt->kt_mcastgrp, s2)); + + 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' : ' '); + + for (i = 0; i < numvifs; ++i) { + if (VIFM_ISSET(i, kt->kt_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'); + } + fprintf(fp2, "\n"); + count++; + } +} + + +/* + * 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; + char *data; + u_char no; + int datalen; +{ + u_char type; + struct rtentry *rt; + struct tr_query *qry; + struct tr_resp *resp; + struct uvif *v; + int vifi; + char *p; + struct ktable *kt; + int rcount; + + struct timeval tp; + struct timezone tzp; + struct sioc_vif_req v_req; + struct sioc_sg_req sg_req; + + /* timestamp the request/response */ + gettimeofday(&tp, &tzp); + + /* + * Check if it is a query or a response + */ + if (datalen == QLEN) { + type = QUERY; + printf("Traceroute query rcvd\n"); + } + else if ((datalen - QLEN)%RLEN == 0) { + type = RESP; + printf("Traceroute response rcvd\n"); + } + else { + printf("Non decipherable trace request %s", inet_fmt(src, s1)); + return; + } + + qry = (struct tr_query *)data; + + /* + * if it is a multicast packet with all reports filled, drop it + */ + if ((rcount = (datalen - QLEN)/RLEN) == no) { + printf("multicast packet with reports filled in\n"); + 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); + + /* determine the routing table entry for this traceroute */ + rt = determine_route(qry->tr_src); + + /* + * 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", + 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", + 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", + inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); + return; + } + } + else { + /* + * determine which interface the packet came in on + */ + if ((vifi = find_vif(src, dst)) == NO_VIF) { + printf("Wrong interface for packet\n"); + return; + } + } + + printf("Sending traceroute response\n"); + + /* copy the packet to the sending buffer */ + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + + bcopy(data, p, datalen); + + p += datalen; + + /* + * fill in initial response fields + */ + resp = (struct tr_resp *)p; + resp->tr_qarr = ((tp.tv_sec & 0xffff) << 16) + + ((tp.tv_usec >> 4) & 0xffff); + + resp->tr_vifin = 0; /* default values */ + resp->tr_pktcnt = 0; /* default values */ + resp->tr_rproto = PROTO_DVMRP; + resp->tr_smask = 0; + 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; + + /* + * fill in scoping & pruning information + */ + kt = find_src_grp(qry->tr_src, group); + + if (kt != NULL) { + 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; + + if (VIFM_ISSET(vifi, kt->kt_scope)) + resp->tr_rflags = TR_SCOPED; + else if (kt->kt_prsent_timer) + resp->tr_rflags = TR_PRUNED; + } + + /* + * if no rte exists, set NO_RTE error + */ + if (rt == NULL) { + src = dst; /* the dst address of resp. pkt */ + resp->tr_inaddr = NULL; + resp->tr_rflags = TR_NO_RTE; + resp->tr_rmtaddr = NULL; + } + 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; + + 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", + inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2)); + resp->tr_rflags = TR_WRONG_IF; + } + } + + /* + * if metric is 1 or no. of reports is 1, send response to requestor + * else send to upstream router. + */ + printf("rcount:%d, no:%d\n", rcount, no); + + if ((rcount + 1 == no) || (rt->rt_metric == 1)) + dst = qry->tr_raddr; + else + dst = rt->rt_gateway; + + if (IN_MULTICAST(ntohl(dst))) { + k_set_ttl(qry->tr_rttl); + send_igmp(src, dst, + IGMP_MTRACE_RESP, no, group, + datalen + RLEN); + k_set_ttl(1); + } + else + send_igmp(src, dst, + IGMP_MTRACE, no, group, + datalen + RLEN); + return; +} diff --git a/usr.sbin/mrouted/prune.h b/usr.sbin/mrouted/prune.h new file mode 100644 index 0000000..3cce25e --- /dev/null +++ b/usr.sbin/mrouted/prune.h @@ -0,0 +1,123 @@ +/* + * 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.3 1994/08/24 23:54:40 thyagara Exp $ + */ + +/* + * Macro for copying the user-level cache table to the kernel + * level table variable passed on by the setsock option + */ + +#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]; \ +}; + + +/* + * User level Kernel Cache Table structure + * + * 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. + * + */ +struct ktable +{ + 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 */ +}; + +/* + * structure to store incoming prunes + */ +struct prunlst +{ + struct prunlst *rl_next; + u_long rl_router; + u_long rl_router_subnet; + vifi_t rl_vifi; + int rl_timer; +}; + +struct tr_query { + u_long tr_src; /* traceroute source */ + u_long tr_dst; /* traceroute destination */ + u_long tr_raddr; /* traceroute response address */ + struct { + u_int ttl : 8; /* traceroute response ttl */ + u_int qid : 24; /* traceroute query id */ + } q; +} tr_query; + +#define tr_rttl q.ttl +#define tr_qid q.qid + +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_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 + +/* fields for tr_rproto (routing protocol) */ +#define PROTO_DVMRP 0x1 +#define PROTO_MOSPF 0x2 +#define PROTO_PIM 0x3 +#define PROTO_CBT 0x4 + +#define MASK_TO_VAL(x, i) { \ + (i) = 0; \ + while ((x) << (i)) \ + (i)++; \ + } + +#define VAL_TO_MASK(x, i) { \ + x = ~((1 << (32 - (i))) - 1); \ + } diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c new file mode 100644 index 0000000..2debcc7 --- /dev/null +++ b/usr.sbin/mrouted/route.c @@ -0,0 +1,1076 @@ +/* + * 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.8 1994/08/24 23:54:42 thyagara Exp $ + */ + + +#include "defs.h" + + +/* + * Exported variables. + */ +int routes_changed; /* 1=>some routes have changed */ +int delay_change_reports; /* 1=>postpone change reports */ + + +/* + * Private variables. + */ +static struct rtentry *routing_table; /* pointer to list of route entries */ +static struct rtentry *rtp; /* pointer to a route entry */ +unsigned int nroutes; /* current number of route entries */ + + +/* + * Initialize the routing table and associated variables. + */ +void init_routes() +{ + routing_table = NULL; + 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) + 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) + 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) + 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; + 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() +{ + rtp = (struct rtentry *)&routing_table; +} + + +/* + * 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; +{ + 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) || + (mask == r->rt_originmask && + 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; +{ + register struct rtentry *r; + + if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) + + (3 * numvifs * sizeof(u_long)))) == 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_next = rtp->rt_next; + rtp->rt_next = r; + rtp = r; + ++nroutes; +} + + +/* + * Discard the routing table entry following the one to which 'prev_r' points. + */ +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; + 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; + int metric; + u_long 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); + + 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() +{ + register struct rtentry *r; + register struct rtentry *prev_r; + register vifi_t vifi; + + for (prev_r = (struct rtentry *)&routing_table, 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); + } + else { + r->rt_flags |= RTF_LEAF_TIMING; + } + } + } + } + } + else if (r->rt_timer >= ROUTE_DISCARD_TIME) { + /* + * Time to garbage-collect the route entry. + */ + 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() +{ + 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() +{ + register struct rtentry *r; + + r = (struct rtentry *)&routing_table; + + 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; + char *p; + int datalen; + u_long 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); +} + +struct newrt { + u_long mask; + u_long origin; + int metric; + int pad; +}; + +int compare_rts(r1, r2) + register struct newrt *r1; + register struct newrt *r2; +{ + register unsigned long m1 = ntohl(r1->mask); + register unsigned long m2 = ntohl(r2->mask); + register unsigned long o1, o2; + + 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); + return (0); +} + +/* + * Process an incoming route report message. + */ +void accept_report(src, dst, p, datalen, level) + u_long 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; + 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", + 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; + datalen -= 3; + + do { /* Loop through (origin, metric) pairs */ + if (datalen < width + 1) { + log(LOG_WARNING, 0, + "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; + ++nrt; + } while (!(metric & 0x80)); + } + qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts); + start_route_updates(); + for (i = 0; i < nrt; ++i) + update_route(rt[i].origin, rt[i].mask, (rt[i].metric & 0x7f), + 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) + int which_routes; + vifi_t vifi; + u_long dst; +{ + register struct rtentry *r; + register char *p; + register int i; + int datalen; + int width; + u_long mask; + u_long src; + + 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) { + + 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); + + p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN; + datalen = 0; + mask = 0; + } + + if(r->rt_originmask != mask) { + 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); + } +} + + +/* + * Send a route report message to all neighboring routers. + * 'which_routes' specifies ALL_ROUTES or CHANGED_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) + register struct rtentry *start_rt; + vifi_t vifi; + u_long 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; + + 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) { + /* + * 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); + return (nrt); + } + if(r->rt_originmask != mask) { + 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); + } + return (nrt); +} + +/* + * send the next chunk of our routing table to all neighbors. + */ +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; + 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; + } + /* + * 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) { + n = report_chunk(sr, vifi, + (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr + : dvmrp_group); + } + } + if (debug) + printf("update %d starting at %d of %d\n", n, 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) + FILE *fp; +{ + register struct rtentry *r; + register int i; + + fprintf(fp, + "Multicast Routing Table (%u %s)\n%s", + nroutes, (nroutes == 1) ? "entry" : "entries", + " Origin-Subnet From-Gateway Metric In-Vif Out-Vifs\n"); + + for (r = routing_table; r != NULL; r = r->rt_next) { + + fprintf(fp, " %-15s %-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); + + 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 *rt; + + for (rt = routing_table; rt != NULL; rt = rt->rt_next) { + if (rt->rt_origin == (src & rt->rt_originmask)) + break; + } + return rt; +} + diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h new file mode 100644 index 0000000..2e7aa33 --- /dev/null +++ b/usr.sbin/mrouted/route.h @@ -0,0 +1,50 @@ +/* + * 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 $ + */ + +/* + * 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. + * 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 */ + 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 */ + 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 */ +}; + +#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() */ diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c new file mode 100644 index 0000000..bf7b867 --- /dev/null +++ b/usr.sbin/mrouted/vif.c @@ -0,0 +1,1136 @@ +/* + * 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.8 1994/08/24 23:54:45 thyagara Exp $ + */ + + +#include "defs.h" + + +/* + * 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 */ +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. */ + +/* + * Forward declarations. + */ +static void start_vif(); +static void stop_vif(); + +/* + * Initialize the virtual interfaces. + */ +void init_vifs() +{ + vifi_t vifi; + struct uvif *v; + int enabled_vifs, enabled_phyints; + + numvifs = 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"); + config_vifs_from_kernel(); + config_vifs_from_file(); + + /* + * Quit if there are fewer than two enabled vifs. + */ + enabled_vifs = 0; + enabled_phyints = 0; + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (!(v->uv_flags & VIFF_DISABLED)) { + ++enabled_vifs; + if (!(v->uv_flags & VIFF_TUNNEL)) + ++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. + */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (!(v->uv_flags & VIFF_DISABLED)) { + if (!(v->uv_flags & VIFF_DOWN)) + start_vif(vifi); + 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() +{ + 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; + } + } + } +} + + +/* + * Start routing on the specified virtual interface. + */ +static void start_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + u_long src, dst; + int i; + char *p; + int datalen; + struct listaddr *nbr; + + 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. + */ + 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); + + /* + * 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); + + /* + * 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, + GROUP_EXPIRE_TIME * 10 / ROUTE_MAX_REPORT_DELAY, 0, 0); + age_old_hosts(); + } + + /* + * 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); +} + + +/* + * Stop routing on the specified virtual interface. + */ +static void stop_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + struct listaddr *a; + + 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); + + /* + * 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); + + /* + * 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. + */ + 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() +{ + 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; +{ + register vifi_t vifi; + register struct uvif *v; + + 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) + return(vifi); + } + } + } + return (NO_VIF); +} + + +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() +{ + 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, + GROUP_EXPIRE_TIME * 10 / ROUTE_MAX_REPORT_DELAY, 0, 0); + } + } + age_old_hosts(); +} + + +/* + * Process an incoming group membership report. + */ +void accept_group_report(src, dst, group, r_type) + u_long 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); + 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){ + 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; + 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; +{ + 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) { + log(LOG_DEBUG, 0, + "[vif.c, _leave_group_message] %d %d \n", + g->al_old, g->al_query); + + if (g->al_old) + 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; + send_igmp(v->uv_lcl_addr, g->al_addr, + IGMP_HOST_MEMBERSHIP_QUERY, + IGMP_MAX_HOST_REPORT_DELAY * 10 / (2*TIMER_INTERVAL), + 0, 0); + g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3 , + IGMP_MAX_HOST_REPORT_DELAY / 2); + 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() +{ + 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 a list of all of our neighbors to the requestor, `src'. + */ +void accept_neighbor_request(src, dst) + u_long src, dst; +{ + vifi_t vifi; + struct uvif *v; + u_char *p, *ncount; + struct listaddr *la; + int datalen; + u_long 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; + 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; + +#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 = (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; +{ + vifi_t vifi; + struct uvif *v; + u_char *p, *ncount; + struct listaddr *la; + int datalen; + u_long 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; + 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; + + 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; + 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; + 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; + 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) + vifi_t vifi; + u_long addr; + int msgtype; + char *p; + int datalen; + u_long level; +{ + register struct uvif *v; + register struct listaddr *n; + u_long genid; + u_long router; + int he_hears_me = TRUE; + + v = &uvifs[vifi]; + + /* + * 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. + */ + + if (msgtype == DVMRP_REPORT && v->uv_neighbors == NULL) + report(ALL_ROUTES, vifi, + (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group); + + /* + * Check if the router gen-ids are the same. + * Need to reset the prune state of the router if not. + */ + if (msgtype == DVMRP_PROBE) { + int i; + + if (datalen < 4) { + log(LOG_WARNING, 0, + "received truncated probe message from %s", + inet_fmt(addr, s1)); + return; + } + + for (i = 0; i < 4; i++) + ((char *)&genid)[i] = *p++; + datalen -=4; + + /* + * loop through router list and check for one-way ifs. + */ + + he_hears_me = FALSE; + + while (datalen > 0) { + if (datalen < 4) { + log(LOG_WARNING, 0, + "received truncated probe message from %s", + inet_fmt(addr, s1)); + return; + } + for (i = 0; i < 4; i++) + ((char *)&router)[i] = *p++; + datalen -= 4; + if (router == v->uv_lcl_addr) { + he_hears_me = TRUE; + break; + } + } + } + /* + * 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; + /* need to do a full route report here */ + break; + } + + /* recurring probe - so no need to do a route report */ + return FALSE; + } + break; + } + } + + /* + * 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; + } + + return (TRUE); +} + + +/* + * On every timer interrupt, advance the timer in each neighbor and + * group entry on every vif. + */ +void age_vifs() +{ + register vifi_t vifi; + register struct uvif *v; + register struct listaddr *a, *prev_a, *n; + register u_long addr; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) { + + 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_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; + } + } + } + } + } +} + + +/* + * Print the contents of the uvifs array on file 'fp'. + */ +void dump_vifs(fp) + FILE *fp; +{ + register vifi_t vifi; + register struct uvif *v; + register struct listaddr *a; + struct sioc_vif_req v_req; + + fprintf(fp, + "\nVirtual Interface Table\n%s", + "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 ", + 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"); + fprintf(fp, "\n"); + + if (v->uv_neighbors != NULL) { + fprintf(fp, " peers: %s (%d.%d)\n", + inet_fmt(v->uv_neighbors->al_addr, s1), + v->uv_neighbors->al_pv, v->uv_neighbors->al_mv); + 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); + } + } + + 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", + 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", + 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", + v_req.icount); + fprintf(fp, " pkts out: %d\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; + +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); + delete_lclgrp(vifi, a->al_addr); + + prev_a->al_next = a->al_next; + free((char *)a); + a = prev_a; + } + + free(cbk); +} + + +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); +} + +DeleteTimer( id) +int id; +{ + timer_clearTimer(id); + return 0; +} + +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 * 10 / TIMER_INTERVAL, 0, 0); + cbk->g->al_query = 0; + free(cbk); +} + +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); +} + diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h new file mode 100644 index 0000000..3cfa2e5 --- /dev/null +++ b/usr.sbin/mrouted/vif.h @@ -0,0 +1,62 @@ +/* + * 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 $ + */ + +/* + * 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) */ + 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 */ +}; + +#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 */ + +struct vif_acl { + struct vif_acl *acl_next; /* next acl member */ + u_long acl_addr; /* Group address */ + u_long 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_long al_timer; /* for timing out group or neighbor */ + u_long 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 */ +}; + + +#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */ -- cgit v1.1