diff options
Diffstat (limited to 'usr.sbin/pim6dd')
31 files changed, 11674 insertions, 0 deletions
diff --git a/usr.sbin/pim6dd/LICENSE.mrouted b/usr.sbin/pim6dd/LICENSE.mrouted new file mode 100644 index 0000000..e03eb82 --- /dev/null +++ b/usr.sbin/pim6dd/LICENSE.mrouted @@ -0,0 +1,49 @@ +$FreeBSD$ + +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/pim6dd/LICENSE.pimd b/usr.sbin/pim6dd/LICENSE.pimd new file mode 100644 index 0000000..384ad23 --- /dev/null +++ b/usr.sbin/pim6dd/LICENSE.pimd @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: LICENSE.pimd,v 1.1.1.1 1999/08/08 23:30:50 itojun Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ diff --git a/usr.sbin/pim6dd/Makefile b/usr.sbin/pim6dd/Makefile new file mode 100644 index 0000000..b0fdcd3 --- /dev/null +++ b/usr.sbin/pim6dd/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 +# $FreeBSD$ + +PROG= pim6dd +MAN5= pim6dd.conf.5 +MAN8= pim6dd.8 + +SRCS= mld6.c mld6_proto.c\ + inet6.c kern.c main.c config.c debug.c routesock.c vers.c callout.c\ + route.c vif.c timer.c mrt.c pim6.c pim6_proto.c trace.c + +CFLAGS+= -DINET6 -DPIM -DIOCTL_OK_ON_RAW_SOCKET + +.include <bsd.prog.mk> diff --git a/usr.sbin/pim6dd/VERSION b/usr.sbin/pim6dd/VERSION new file mode 100644 index 0000000..69e5262 --- /dev/null +++ b/usr.sbin/pim6dd/VERSION @@ -0,0 +1,2 @@ +# $FreeBSD$ +0.2.1.0-alpha15 diff --git a/usr.sbin/pim6dd/callout.c b/usr.sbin/pim6dd/callout.c new file mode 100644 index 0000000..6820dcd --- /dev/null +++ b/usr.sbin/pim6dd/callout.c @@ -0,0 +1,256 @@ +/* + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". 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. + * + * + * callout.c,v 3.8.4.5 1997/05/16 20:18:25 fenner Exp + * + * $FreeBSD$ + */ + +#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 */ + +struct timeout_q { + struct timeout_q *next; /* next event */ + int id; + cfunc_t func; /* function to call */ + void *data; /* func's data */ + int time; /* time offset to next event*/ +}; + +#ifdef CALLOUT_DEBUG +static void print_Q __P((void)); +#else +#define print_Q() +#endif + +void +callout_init() +{ + if (Q) { + log(LOG_ERR, 0, "timer used before callout_init()"); + exit(1); + } + Q = (struct timeout_q *) 0; +} + +void +free_all_callouts() +{ + struct timeout_q *p; + + while (Q) { + p = Q; + Q = Q->next; + free(p); + } +} + + +/* + * elapsed_time seconds have passed; perform all the events that should + * happen. + */ +void +age_callout_queue(elapsed_time) + int elapsed_time; +{ + struct timeout_q *ptr, *expQ; + +#ifdef CALLOUT_DEBUG + IF_DEBUG(DEBUG_TIMEOUT) + log(LOG_DEBUG, 0, "aging queue (elapsed time %d):", elapsed_time); + print_Q(); +#endif + + expQ = Q; + ptr = NULL; + + while (Q) { + if (Q->time > elapsed_time) { + Q->time -= elapsed_time; + if (ptr) { + ptr->next = NULL; + break; + } + return; + } else { + elapsed_time -= Q->time; + ptr = Q; + Q = Q->next; + } + } + + /* handle queue of expired timers */ + while (expQ) { + ptr = expQ; + if (ptr->func) + ptr->func(ptr->data); + expQ = expQ->next; + free(ptr); + } +} + +/* + * Return in how many seconds age_callout_queue() would like to be called. + * Return -1 if there are no events pending. + */ +int +timer_nextTimer() +{ + if (Q) { + if (Q->time < 0) { + log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d", + Q->time); + return 0; + } + return Q->time; + } + return -1; +} + +/* + * 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 */ + void *data; /* what to call the timeout function with */ +{ + struct timeout_q *ptr, *node, *prev; + +#ifdef CALLOUT_DEBUG + IF_DEBUG(DEBUG_TIMEOUT) + log(LOG_DEBUG, 0, "setting timer:"); + print_Q(); +#endif + + /* create a node */ + node = (struct timeout_q *)malloc(sizeof(struct timeout_q)); + if (node == 0) { + log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); + 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; + return node->id; + } else { + /* keep moving */ + + delay -= ptr->time; node->time = delay; + prev = ptr; + ptr = ptr->next; + } + } + prev->next = node; + } + return node->id; +} + +/* returns the time until the timer is scheduled */ +int +timer_leftTimer(timer_id) + int timer_id; +{ + struct timeout_q *ptr; + int left = 0; + + if (!timer_id) + return -1; + + for (ptr = Q; ptr; ptr = ptr->next) { + left += ptr->time; + if (ptr->id == timer_id) + return left; + } + return -1; +} + +/* clears the associated timer */ +void +timer_clearTimer(timer_id) + int timer_id; +{ + struct timeout_q *ptr, *prev; + + if (!timer_id) + return; + + prev = ptr = Q; + + /* + * find the right node, delete it. the subsequent node's time + * gets bumped up + */ + + while (ptr) { + if (ptr->id == timer_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; + + if (ptr->data) + free(ptr->data); + free(ptr); + return; + } + prev = ptr; + ptr = ptr->next; + } +} + +#ifdef CALLOUT_DEBUG +/* + * debugging utility + */ +static void +print_Q() +{ + struct timeout_q *ptr; + + IF_DEBUG(DEBUG_TIMEOUT) + for (ptr = Q; ptr; ptr = ptr->next) + log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time); +} +#endif /* CALLOUT_DEBUG */ diff --git a/usr.sbin/pim6dd/config.c b/usr.sbin/pim6dd/config.c new file mode 100644 index 0000000..c52e06f --- /dev/null +++ b/usr.sbin/pim6dd/config.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: config.c,v 1.3 1999/12/30 09:23:08 itojun Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + + +/* + * Forward declarations. + */ +static char *next_word __P((char **)); +static int parse_phyint __P((char *s)); +static void add_phaddr __P((struct uvif *v, struct sockaddr_in6 *addr, + struct in6_addr *mask)); +static mifi_t ifname2mifi __P((char *ifname)); +static int wordToOption __P((char *)); +static int parse_filter __P((char *s)); +#if 0 /* not used */ +static int parse_default_source_metric __P((char *)); +static int parse_default_source_preference __P((char *)); +#endif + +/* + * 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 *ifrp, *ifend; + register struct uvif *v; + register vifi_t vifi; + int n; + struct sockaddr_in6 addr; + struct in6_addr mask, prefix; + short flags; + int num_ifreq = 64; + struct ifconf ifc; + + total_interfaces = 0; /* The total number of physical interfaces */ + + ifc.ifc_len = num_ifreq * sizeof(struct ifreq); + ifc.ifc_buf = calloc(ifc.ifc_len, sizeof(char)); + while (ifc.ifc_buf) { + caddr_t newbuf; + + if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFCONF"); + + /* + * If the buffer was large enough to hold all the addresses + * then break out, otherwise increase the buffer size and + * try again. + * + * The only way to know that we definitely had enough space + * is to know that there was enough space for at least one + * more struct ifreq. ??? + */ + if ((num_ifreq * sizeof(struct ifreq)) >= + ifc.ifc_len + sizeof(struct ifreq)) + break; + + num_ifreq *= 2; + ifc.ifc_len = num_ifreq * sizeof(struct ifreq); + newbuf = realloc(ifc.ifc_buf, ifc.ifc_len); + if (newbuf == NULL) + free(ifc.ifc_buf); + ifc.ifc_buf = newbuf; + } + if (ifc.ifc_buf == NULL) + log(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory"); + + ifrp = (struct ifreq *)ifc.ifc_buf; + ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); + /* + * Loop through all of the interfaces. + */ + for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) { + struct ifreq ifr; + struct in6_ifreq ifr6; +#ifdef HAVE_SA_LEN + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); +#else + n = sizeof(*ifrp); +#endif /* HAVE_SA_LEN */ + + /* + * Ignore any interface for an address family other than IPv6. + */ + if (ifrp->ifr_addr.sa_family != AF_INET6) { + total_interfaces++; /* Eventually may have IPv6 address later */ + continue; + } + + memcpy(&addr, &ifrp->ifr_addr, sizeof(struct sockaddr_in6)); + + /* + * 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.) + */ + memcpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); + memcpy(ifr6.ifr_name, ifrp->ifr_name, sizeof(ifr6.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; + + /* + * Get netmask of the address. + */ + ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr; + if (ioctl(udp_socket, SIOCGIFNETMASK_IN6, (char *)&ifr6) < 0) + log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK_IN6 for %s", + ifr6.ifr_name); + memcpy(&mask, &ifr6.ifr_addr.sin6_addr, sizeof(mask)); + + if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr)) { + addr.sin6_scope_id = if_nametoindex(ifrp->ifr_name); +#ifdef __KAME__ + /* + * Hack for KAME kernel. Set sin6_scope_id field of a + * link local address and clear the index embedded in + * the address. + */ + /* clear interface index */ + addr.sin6_addr.s6_addr[2] = 0; + addr.sin6_addr.s6_addr[3] = 0; +#endif + } + + /* + * If the address is connected to the same subnet as one already + * installed in the uvifs array, just add the address to the list + * of addresses of the uvif. + */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (strcmp(v->uv_name, ifr.ifr_name) == 0) { + add_phaddr(v, &addr, &mask); + break; + } + } + if (vifi != numvifs) + continue; + + /* + * If there is room in the uvifs array, install this interface. + */ + if (numvifs == MAXMIFS) { + log(LOG_WARNING, 0, "too many ifs, ignoring %s", ifr.ifr_name); + continue; + } + + /* + * Everyone below is a potential vif interface. + * We don't care if it has wrong configuration or not configured + * at all. + */ + total_interfaces++; + + v = &uvifs[numvifs]; + v->uv_flags = 0; + v->uv_metric = DEFAULT_METRIC; + v->uv_admetric = 0; +#if 0 + v->uv_threshold = DEFAULT_THRESHOLD; +#endif + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; +#if 0 + v->uv_rmt_addr = INADDR_ANY_N; +#endif + v->uv_dst_addr = allpim6routers_group; + v->uv_prefix.sin6_addr = prefix; + v->uv_subnetmask = mask; + strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ); + v->uv_ifindex = if_nametoindex(v->uv_name); + v->uv_groups = (struct listaddr *)NULL; + v->uv_dvmrp_neighbors = (struct listaddr *)NULL; + NBRM_CLRALL(v->uv_nbrmap); + v->uv_querier = (struct listaddr *)NULL; + v->uv_prune_lifetime = 0; + v->uv_acl = (struct vif_acl *)NULL; + v->uv_leaf_timer = 0; + v->uv_addrs = (struct phaddr *)NULL; + v->uv_filter = (struct vif_filter *)NULL; + v->uv_pim_hello_timer = 0; + v->uv_gq_timer = 0; + v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL; + v->uv_local_pref = default_source_preference; + v->uv_local_metric = default_source_metric; + add_phaddr(v, &addr, &mask); + + if (flags & IFF_POINTOPOINT) + v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT); + log(LOG_INFO, 0, + "installing %s as if #%u - rate=%d", + v->uv_name, 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 void +add_phaddr(v, addr, mask) + struct uvif *v; + struct sockaddr_in6 *addr; + struct in6_addr *mask; +{ + struct phaddr *pa; + int i; + + if ((pa = malloc(sizeof(*pa))) == NULL) + log(LOG_ERR, 0, "add_phaddr: memory exhausted"); + + memset(pa, 0, sizeof(*pa)); + pa->pa_addr = *addr; + pa->pa_subnetmask = *mask; + + /* + * install the prefix of the address derived from the address + * and the mask. + */ + for (i = 0; i < sizeof(struct in6_addr); i++) + pa->pa_prefix.sin6_addr.s6_addr[i] = + addr->sin6_addr.s6_addr[i] & mask->s6_addr[i]; + pa->pa_prefix.sin6_scope_id = addr->sin6_scope_id; + + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { + if (v->uv_linklocal) { + log(LOG_WARNING, 0, + "add_phaddr: found more than one link-local " + "address on %s", + v->uv_name); + } + v->uv_linklocal = pa; /* relace anyway */ + } + + /* link into chain */ + pa->pa_next = v->uv_addrs; + v->uv_addrs = pa; +} + +static mifi_t +ifname2mifi(ifname) + char *ifname; +{ + mifi_t mifi; + struct uvif *v; + + for (mifi = 0, v = uvifs; mifi < numvifs; ++mifi, ++v) { + if (strcmp(v->uv_name, ifname) == 0) + break; + } + return(mifi); +} + +#define UNKNOWN -1 +#define EMPTY 1 +#define PHYINT 2 +#define DEFAULT_SOURCE_METRIC 3 +#define DEFAULT_SOURCE_PREFERENCE 4 +#define FILTER 5 + +/* + * function name: wordToOption + * input: char *word, a pointer to the word + * output: int; a number corresponding to the code of the word + * operation: converts the result of the string comparisons into numerics. + * comments: called by config_vifs_from_file() + */ +static int +wordToOption(word) + char *word; +{ + if (EQUAL(word, "")) + return EMPTY; + if (EQUAL(word, "phyint")) + return PHYINT; + if (EQUAL(word, "default_source_metric")) + return DEFAULT_SOURCE_METRIC; + if (EQUAL(word, "default_source_preference")) + return DEFAULT_SOURCE_PREFERENCE; + if (EQUAL(word, "filter")) + return FILTER; + return UNKNOWN; +} + +/* + * function name: parse_phyint + * input: char *s, pointing to the parsing point of the file + * output: int (TRUE if the parsing was successful, o.w. FALSE) + * operation: parses the physical interface file configurations, if any. + * The general form is: + * phyint <ifname> [disable] + */ +static int +parse_phyint(s) + char *s; +{ + char *w, c, *ifname; + vifi_t vifi; + struct uvif *v; + u_int n; + + if (EQUAL((w = next_word(&s)), "")) { + log(LOG_WARNING, 0, "Missing phyint in %s", configfilename); + return(FALSE); + } /* if empty */ + ifname = w; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (vifi == numvifs) { + log(LOG_WARNING, 0, + "phyint %s in %s is not a configured interface", + ifname, configfilename); + return(FALSE); + } /* if vifi == numvifs */ + + if (strcmp(v->uv_name, ifname)) + continue; + + while (!EQUAL((w = next_word(&s)), "")) { + if (EQUAL(w, "disable")) + v->uv_flags |= VIFF_DISABLED; + else if (EQUAL(w, "nolistener")) + v->uv_flags |= VIFF_NOLISTENER; + else if(EQUAL(w, "preference")) { + if(EQUAL((w = next_word(&s)), "")) + log(LOG_WARNING, 0, + "Missing preference for phyint %s in %s", + ifname, configfilename); + else if (sscanf(w, "%u%c", &n, &c) != 1 || + n < 1 || n > 255 ) + log(LOG_WARNING, 0, + "Invalid preference '%s' for phyint %s in %s", + w, ifname, + configfilename); + else { + IF_DEBUG(DEBUG_ASSERT) + log(LOG_DEBUG, 0, + "Config setting default local preference on %s to %d.", + ifname, n); + v->uv_local_pref = n; + } + + } else if(EQUAL(w, "metric")) { + if(EQUAL((w = next_word(&s)), "")) + log(LOG_WARNING, 0, + "Missing metric for phyint %s in %s", + ifname, configfilename); + else if (sscanf(w, "%u%c", &n, &c) != 1 || + n < 1 || n > 1024 ) + log(LOG_WARNING, 0, + "Invalid metric '%s' for phyint %s in %s", + w, ifname, + configfilename); + else { + IF_DEBUG(DEBUG_ASSERT) + log(LOG_DEBUG, 0, + "Config setting default local metric on %s to %d.", + ifname, n); + v->uv_local_metric = n; + } + } + } /* if not empty */ + break; + } + return(TRUE); +} + +static int +parse_filter(s) + char *s; +{ + char *w, *groups, *p; + mifi_t mifi; + struct in6_addr grp1, grp2; + if_set filterset; + struct mrtfilter *filter; + int plen = 0, filtertype; + + if (EQUAL((groups = next_word(&s)), "")) { + log(LOG_WARNING, 0, "Missing multicast group in %s", + configfilename); + return(FALSE); + } + + /* + * Group address specification. Valid format are the followings. + * - Group1-Group2: specifies a numerical range of a scope. + * - GroupPrefix/Prefixlen: specifies a prefix of a scope. If the + * Prefixlen is omitted, it means the exact match. + */ + if ((p = strchr(groups, '-')) != NULL) { /* Group1-Group2 */ + char *maddr1, *maddr2; + + maddr1 = groups; + maddr2 = p + 1; + *p = '\0'; + if (inet_pton(AF_INET6, maddr1, (void *)&grp1) != 1 || + !IN6_IS_ADDR_MULTICAST(&grp1)) { + log(LOG_WARNING, 0, "invalid group address %s", maddr1); + return(FALSE); + } + if (inet_pton(AF_INET6, maddr2, (void *)&grp2) != 1 || + !IN6_IS_ADDR_MULTICAST(&grp2)) { + log(LOG_WARNING, 0, "invalid group address %s", maddr2); + return(FALSE); + } + filtertype = FILTER_RANGE; + } + else if ((p = strchr(groups, '/')) != NULL) { /* GroupPrefix/Plen */ + char *mprefix = groups; + int plen = atoi(p + 1); + *p = '\0'; + if (inet_pton(AF_INET6, mprefix, (void *)&grp1) != 1 || + !IN6_IS_ADDR_MULTICAST(&grp1)) { + log(LOG_WARNING, 0, "invalid group prefix %s", mprefix); + return(FALSE); + } + if (plen < 0 || plen > 128) { + log(LOG_WARNING, 0, "invalid prefix length %s", p + 1); + return(FALSE); + } + filtertype = FILTER_PREFIX; + } + else { + if (inet_pton(AF_INET6, groups, (void *)&grp1) != 1) { + log(LOG_WARNING, 0, "invalid group address %s", groups); + return(FALSE); + } + plen = 128; /* exact match */ + filtertype = FILTER_PREFIX; + } + + IF_ZERO(&filterset); + while (!EQUAL((w = next_word(&s)), "")) { + if ((mifi = ifname2mifi(w)) == MAXMIFS) { + /* XXX: scope consideration?? */ + log(LOG_WARNING, 0, + "phyint %s in %s is not a configured interface", + w, configfilename); + return(FALSE); + } + + IF_SET(mifi, &filterset); + } + if (IF_ISEMPTY(&filterset)) { + log(LOG_WARNING, 0, + "filter set is empty. ignore it."); + return(FALSE); + } + + filter = add_filter(filtertype, &grp1, &grp2, plen); + IF_COPY(&filterset, &filter->ifset); + + return(TRUE); +} + +#if 0 /* not used */ +/* + * function name: parse_default_source_metric + * input: char *s + * output: int + * operation: reads and assigns the default source metric, if no reliable + * unicast routing information available. + * General form: + * 'default_source_metric <number>'. + * default pref and metric statements should precede all phyint + * statements in the config file. + */ +static int +parse_default_source_metric(s) + char *s; +{ + char *w; + u_int value; + vifi_t vifi; + struct uvif *v; + + value = DEFAULT_LOCAL_METRIC; + if (EQUAL((w = next_word(&s)), "")) { + log(LOG_WARNING, 0, + "Missing default source metric; set to default %u", + DEFAULT_LOCAL_METRIC); + } else if (sscanf(w, "%u", &value) != 1) { + log(LOG_WARNING, 0, + "Invalid default source metric; set to default %u", + DEFAULT_LOCAL_METRIC); + value = DEFAULT_LOCAL_METRIC; + } + default_source_metric = value; + log(LOG_INFO, 0, "default_source_metric is %u", value); + + for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) { + v->uv_local_metric = default_source_metric; + } + + return(TRUE); +} + + +/* + * function name: parse_default_source_preference + * input: char *s + * output: int + * operation: reads and assigns the default source preference, if no reliable + * unicast routing information available. + * General form: + * 'default_source_preference <number>'. + * default pref and metric statements should precede all phyint + * statements in the config file. +*/ +static int +parse_default_source_preference(s) + char *s; +{ + char *w; + u_int value; + vifi_t vifi; + struct uvif *v; + + value = DEFAULT_LOCAL_PREF; + if (EQUAL((w = next_word(&s)), "")) { + log(LOG_WARNING, 0, + "Missing default source preference; set to default %u", + DEFAULT_LOCAL_PREF); + } else if (sscanf(w, "%u", &value) != 1) { + log(LOG_WARNING, 0, + "Invalid default source preference; set to default %u", + DEFAULT_LOCAL_PREF); + value = DEFAULT_LOCAL_PREF; + } + default_source_preference = value; + log(LOG_INFO, 0, "default_source_preference is %u", value); + + for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) { + v->uv_local_pref = default_source_preference; + } + + return(TRUE); +} +#endif + +void +config_vifs_from_file() +{ + FILE *f; + char linebuf[100]; + char *w, *s; + struct ifconf ifc; + int option; + char ifbuf[BUFSIZ]; + + if ((f = fopen(configfilename, "r")) == NULL) { + if (errno != ENOENT) log(LOG_WARNING, errno, "can't open %s", + configfilename); + return; + } + + ifc.ifc_buf = 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; + w = next_word(&s); + option = wordToOption(w); + switch(option) { + case EMPTY: + continue; + break; + case PHYINT: + parse_phyint(s); + break; + case FILTER: + parse_filter(s); + break; + default: + log(LOG_WARNING, 0, "unknown command '%s' in %s", + w, configfilename); + } + } + fclose(f); +} + +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/pim6dd/debug.c b/usr.sbin/pim6dd/debug.c new file mode 100644 index 0000000..0894d5c --- /dev/null +++ b/usr.sbin/pim6dd/debug.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: debug.c,v 1.4 1999/09/20 14:28:08 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +extern int haveterminal; +extern char *progname; + +int log_nmsgs = 0; +unsigned long debug = 0x00000000; /* If (long) is smaller than + * 4 bytes, then we are in + * trouble. + */ +static char dumpfilename[] = _PATH_PIM6D_DUMP; +static char cachefilename[] = _PATH_PIM6D_CACHE; /* TODO: notused */ + + +char * +packet_kind(proto, type, code) + u_int proto, type, code; +{ + static char unknown[60]; + + switch (proto) { + case IPPROTO_ICMPV6: + switch (type) { + case MLD6_LISTENER_QUERY: return "Multicast Listener Query "; + case MLD6_LISTENER_REPORT: return "Multicast Listener Report "; + case MLD6_LISTENER_DONE: return "Multicast Listener Done "; + default: + sprintf(unknown, + "UNKNOWN ICMPv6 message: type = 0x%02x, code = 0x%02x ", + type, code); + return unknown; + } + + case IPPROTO_PIM: /* PIM v2 */ + switch (type) { + case PIM_V2_HELLO: return "PIM v2 Hello "; + case PIM_V2_REGISTER: return "PIM v2 Register "; + case PIM_V2_REGISTER_STOP: return "PIM v2 Register_Stop "; + case PIM_V2_JOIN_PRUNE: return "PIM v2 Join/Prune "; + case PIM_V2_BOOTSTRAP: return "PIM v2 Bootstrap "; + case PIM_V2_ASSERT: return "PIM v2 Assert "; + case PIM_V2_GRAFT: return "PIM-DM v2 Graft "; + case PIM_V2_GRAFT_ACK: return "PIM-DM v2 Graft_Ack "; + case PIM_V2_CAND_RP_ADV: return "PIM v2 Cand. RP Adv. "; + default: + sprintf(unknown, "UNKNOWN PIM v2 message type =%3d ", type); + return unknown; + } + default: + sprintf(unknown, "UNKNOWN proto =%3d ", proto); + return unknown; + } +} + + +/* + * Used for debugging particular type of messages. + */ +int +debug_kind(proto, type, code) + u_int proto, type, code; +{ + switch (proto) { + case IPPROTO_ICMPV6: + switch (type) { + case MLD6_LISTENER_QUERY: return DEBUG_MLD; + case MLD6_LISTENER_REPORT: return DEBUG_MLD; + case MLD6_LISTENER_DONE: return DEBUG_MLD; + default: return DEBUG_MLD; + } + case IPPROTO_PIM: /* PIM v2 */ + /* TODO: modify? */ + switch (type) { + case PIM_V2_HELLO: return DEBUG_PIM; + case PIM_V2_REGISTER: return DEBUG_PIM_REGISTER; + case PIM_V2_REGISTER_STOP: return DEBUG_PIM_REGISTER; + case PIM_V2_JOIN_PRUNE: return DEBUG_PIM; + case PIM_V2_BOOTSTRAP: return DEBUG_PIM_BOOTSTRAP; + case PIM_V2_ASSERT: return DEBUG_PIM; + case PIM_V2_GRAFT: return DEBUG_PIM; + case PIM_V2_GRAFT_ACK: return DEBUG_PIM; + case PIM_V2_CAND_RP_ADV: return DEBUG_PIM_CAND_RP; + default: return DEBUG_PIM; + } + default: return 0; + } + return 0; +} + + +/* + * Some messages are more important than others. This routine + * determines the logging level at which to log a send error (often + * "No route to host"). This is important when there is asymmetric + * reachability and someone is trying to, i.e., mrinfo me periodically. + */ +int +log_level(proto, type, code) + u_int proto, type, code; +{ + switch (proto) { + case IPPROTO_ICMPV6: + switch (type) { + default: + return LOG_WARNING; + } + + case IPPROTO_PIM: + /* PIM v2 */ + switch (type) { + default: + return LOG_INFO; + } + default: + return LOG_WARNING; + } + return LOG_WARNING; +} + + +/* + * Dump internal data structures to stderr. + */ +/* TODO: currently not used +void +dump(int i) +{ + dump_vifs(stderr); + dump_pim_mrt(stderr); +} +*/ + +/* + * Dump internal data structures to a file. + */ +void +fdump(i) + int i; +{ + FILE *fp; + fp = fopen(dumpfilename, "w"); + if (fp != NULL) { + dump_vifs(fp); + dump_pim_mrt(fp); + dump_lcl_grp(fp); + (void) fclose(fp); + } +} + +/* TODO: dummy, to be used in the future. */ +/* + * Dump local cache contents to a file. + */ +void +cdump(i) + int i; +{ + FILE *fp; + + fp = fopen(cachefilename, "w"); + if (fp != NULL) { + /* TODO: implement it: + dump_cache(fp); + */ + (void) fclose(fp); + } +} + +void +dump_vifs(fp) + FILE *fp; +{ + vifi_t vifi; + register struct uvif *v; + pim_nbr_entry_t *n; + struct phaddr *pa; + int width; + int i; + + fprintf(fp, "\nMulticast Interface Table\n %-4s %-6s %-50s %-14s %s", + "Mif", " PhyIF", "Local-Address/Prefixlen", "Flags", + "Neighbors\n"); + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + int firstaddr = 1; + for (pa = v->uv_addrs; pa; pa = pa->pa_next) { + if (!firstaddr) { + fprintf(fp, " %3s %6s %-50s\n", "", "", + net6name(&pa->pa_addr.sin6_addr, + &pa->pa_subnetmask)); + continue; + } + + firstaddr = 0; + fprintf(fp, " %-3u %6s %-50s", vifi, v->uv_name, + net6name(&pa->pa_addr.sin6_addr, + &pa->pa_subnetmask)); + firstaddr = 0; +#if 0 + if (v->uv_flags & MIFF_REGISTER) + fprintf(fp, "%-16s ", v->uv_name); + else + fprintf(fp,"%-16.16s ", + net6name(&v->uv_prefix.sin6_addr, + &v->uv_subnetmask)); +#endif + width = 0; + if (v->uv_flags & VIFF_DISABLED) + fprintf(fp, " DISABLED"); + if (v->uv_flags & VIFF_NOLISTENER) + fprintf(fp, " NOLISTENER"); + if (v->uv_flags & VIFF_DOWN) + fprintf(fp, " DOWN"); + if (v->uv_flags & VIFF_DR) { + fprintf(fp, " DR"); + width += 3; + } + if (v->uv_flags & VIFF_PIM_NBR) { + fprintf(fp, " PIM"); + width += 4; + } +#if 0 /* impossible */ + if (v->uv_flags & VIFF_DVMRP_NBR) { + fprintf(fp, " DVMRP"); + width += 6; + } +#endif + if (v->uv_flags & VIFF_NONBRS) { + fprintf(fp, " %-12s", "NO-NBR"); + width += 6; + } + + if ((n = v->uv_pim_neighbors) != NULL) { + /* Print the first neighbor on the same line */ + for (i = width; i <= 15; i++) + fprintf(fp, " "); + fprintf(fp, "%-12s\n", + inet6_fmt(&n->address.sin6_addr)); + for (n = n->next; n != NULL; n = n->next) + fprintf(fp, "%64s %-15s\n", "", + inet6_fmt(&n->address.sin6_addr)); + + } + else + fprintf(fp, "\n"); + } + } + fprintf(fp, "\n"); +} + + +/* + * 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. + */ +#ifdef __STDC__ +void +log(int severity, int syserr, char *format, ...) +{ + va_list ap; + static char fmt[211] = "warning - "; + char *msg; + struct timeval now; + struct tm *thyme; + + va_start(ap, format); +#else +/*VARARGS3*/ +void +log(severity, syserr, format, va_alist) + int severity, syserr; + char *format; + va_dcl +{ + va_list ap; + static char fmt[311] = "warning - "; + char *msg; + char tbuf[20]; + struct timeval now; + struct tm *thyme; + + va_start(ap); +#endif + vsprintf(&fmt[10], format, ap); + va_end(ap); + msg = (severity == LOG_WARNING) ? fmt : &fmt[10]; + + /* + * Log to stderr if we haven't forked yet and it's a warning or worse, + * or if we're debugging. + */ + if (haveterminal && (debug || severity <= LOG_WARNING)) { + time_t sectime; + gettimeofday(&now,NULL); + sectime = (time_t) now.tv_sec; + thyme = localtime(§ime); + if (!debug) + fprintf(stderr, "%s: ", progname); + fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour, + thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg); + 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); + } + + /* + * Always log things that are worse than warnings, no matter what + * the log_nmsgs rate limiter says. + * Only count things worse than debugging in the rate limiter + * (since if you put daemon.debug in syslog.conf you probably + * actually want to log the debugging messages so they shouldn't + * be rate-limited) + */ + if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) { + if (severity < LOG_DEBUG) + log_nmsgs++; + if (syserr != 0) { + errno = syserr; + syslog(severity, "%s: %m", msg); + } else + syslog(severity, "%s", msg); + } + + if (severity <= LOG_ERR) exit(-1); +} + +/* TODO: format the output for better readability */ +void +dump_pim_mrt(fp) + FILE *fp; +{ + grpentry_t *g; + register mrtentry_t *r; + register vifi_t vifi; + u_int number_of_groups = 0; + char oifs[(sizeof(if_set)<<3)+1]; + char pruned_oifs[(sizeof(if_set)<<3)+1]; + char asserted_oifs[(sizeof(if_set)<<3)+1]; + char leaves_oifs[(sizeof(if_set)<<3)+1]; + char filter_oifs[(sizeof(if_set)<<3)+1]; + char incoming_iif[(sizeof(if_set)<<3)+1]; + + fprintf(fp, "Multicast Routing Table\n%s", + " Source Group Flags\n"); + + /* TODO: remove the dummy 0:: group (first in the chain) */ + for (g = grplist->next; g != (grpentry_t *)NULL; g = g->next) { + number_of_groups++; + /* Print all (S,G) routing info */ + for (r = g->mrtlink; r != (mrtentry_t *)NULL; r = r->grpnext) { + fprintf(fp, "---------------------------(S,G)----------------------------\n"); + /* Print the routing info */ + fprintf(fp, " %-15s", inet6_fmt(&r->source->address.sin6_addr)); + fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr)); + + for (vifi = 0; vifi < numvifs; vifi++) { + oifs[vifi] = + IF_ISSET(vifi, &r->oifs) ? 'o' : '.'; + pruned_oifs[vifi] = + IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.'; + asserted_oifs[vifi] = + IF_ISSET(vifi, &r->pruned_oifs) ? 'a' : '.'; + filter_oifs[vifi] = + IF_ISSET(vifi, &r->filter_oifs) ? 'f' : '.'; + leaves_oifs[vifi] = + IF_ISSET(vifi, &r->leaves) ? 'l' : '.'; + incoming_iif[vifi] = '.'; + } + oifs[vifi] = 0x0; /* End of string */ + pruned_oifs[vifi] = 0x0; + leaves_oifs[vifi] = 0x0; + filter_oifs[vifi] = 0x0; + incoming_iif[vifi] = 0x0; + incoming_iif[r->incoming] = 'I'; + + /* TODO: don't need some of the flags */ + if (r->flags & MRTF_SPT) fprintf(fp, " SPT"); + if (r->flags & MRTF_WC) fprintf(fp, " WC"); + if (r->flags & MRTF_RP) fprintf(fp, " RP"); + if (r->flags & MRTF_REGISTER) fprintf(fp, " REG"); + if (r->flags & MRTF_IIF_REGISTER) fprintf(fp, " IIF_REG"); + if (r->flags & MRTF_NULL_OIF) fprintf(fp, " NULL_OIF"); + if (r->flags & MRTF_KERNEL_CACHE) fprintf(fp, " CACHE"); + if (r->flags & MRTF_ASSERTED) fprintf(fp, " ASSERTED"); + if (r->flags & MRTF_REG_SUPP) fprintf(fp, " REG_SUPP"); + if (r->flags & MRTF_SG) fprintf(fp, " SG"); + if (r->flags & MRTF_PMBR) fprintf(fp, " PMBR"); + fprintf(fp, "\n"); + + fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs); + fprintf(fp, "Asserted oifs: %-20s\n", pruned_oifs); + fprintf(fp, "Filtered oifs: %-20s\n", filter_oifs); + fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs); + fprintf(fp, "Outgoing oifs: %-20s\n", oifs); + fprintf(fp, "Incoming : %-20s\n", incoming_iif); + + fprintf(fp, "Upstream nbr: %s\n", + r->upstream ? inet6_fmt(&r->upstream->address.sin6_addr) : "NONE"); + fprintf(fp, "\nTIMERS: Entry Prune VIFS:"); + for (vifi = 0; vifi < numvifs; vifi++) + fprintf(fp, " %2d", vifi); + fprintf(fp, "\n %3d ", + r->timer); + for (vifi = 0; vifi < numvifs; vifi++) + fprintf(fp, " %3d", r->prune_timers[vifi]); + fprintf(fp, "\n"); + } + }/* for all groups */ + + fprintf(fp, "Number of Groups: %u\n", number_of_groups); +} + +void +dump_lcl_grp(fp) + FILE *fp; +{ + vifi_t vifi; + struct uvif *v; + struct listaddr *g; + + fprintf(fp, "\nList of local listener information per interface\n"); + for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { + int first = 1; + + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (first) { + fprintf(fp, " Mif %d(%s)\n", vifi, v->uv_name); + first = 0; + } + fprintf(fp, " %s", inet6_fmt(&g->al_addr.sin6_addr)); + if (g->al_timerid) + fprintf(fp, " timeout: %d", + timer_leftTimer(g->al_timerid)); + if (g->al_query) + fprintf(fp, " querytimer: %d", + timer_leftTimer(g->al_timerid)); + fputc('\n', fp); + } + } +} diff --git a/usr.sbin/pim6dd/debug.h b/usr.sbin/pim6dd/debug.h new file mode 100644 index 0000000..fbcc708 --- /dev/null +++ b/usr.sbin/pim6dd/debug.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: debug.h,v 1.1.1.1 1999/08/08 23:30:52 itojun Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +extern unsigned long debug; +extern int log_nmsgs; +#define IF_DEBUG(l) if (debug && debug & (l)) + +#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */ +#define LOG_SHUT_UP 600 /* shut up for 10 minutes */ + + +/* Debug values definition */ +/* DVMRP reserved for future use */ +#define DEBUG_DVMRP_PRUNE 0x00000001 +#define DEBUG_DVMRP_ROUTE 0x00000002 +#define DEBUG_DVMRP_PEER 0x00000004 +#define DEBUG_DVMRP_TIMER 0x00000008 +#define DEBUG_DVMRP_DETAIL 0x01000000 +#define DEBUG_DVMRP ( DEBUG_DVMRP_PRUNE | DEBUG_DVMRP_ROUTE | \ + DEBUG_DVMRP_PEER ) + +/* MLD related */ +#define DEBUG_MLD_PROTO 0x00000010 +#define DEBUG_MLD_TIMER 0x00000020 +#define DEBUG_MLD_MEMBER 0x00000040 +#define DEBUG_MEMBER DEBUG_MLD_MEMBER +#define DEBUG_MLD ( DEBUG_MLD_PROTO | DEBUG_MLD_TIMER | \ + DEBUG_MLD_MEMBER ) + +/* Misc */ +#define DEBUG_TRACE 0x00000080 +#define DEBUG_TIMEOUT 0x00000100 +#define DEBUG_PKT 0x00000200 + + +/* Kernel related */ +#define DEBUG_IF 0x00000400 +#define DEBUG_KERN 0x00000800 +#define DEBUG_MFC 0x00001000 +#define DEBUG_RSRR 0x00002000 + +/* PIM related */ +#define DEBUG_PIM_GRAFT 0x02000000 +#define DEBUG_PIM_HELLO 0x00004000 +#define DEBUG_PIM_REGISTER 0x00008000 +#define DEBUG_PIM_JOIN_PRUNE 0x00010000 +#define DEBUG_PIM_BOOTSTRAP 0x00020000 +#define DEBUG_PIM_ASSERT 0x00040000 +#define DEBUG_PIM_CAND_RP 0x00080000 +#define DEBUG_PIM_MRT 0x00100000 +#define DEBUG_PIM_TIMER 0x00200000 +#define DEBUG_PIM_RPF 0x00400000 +#define DEBUG_RPF DEBUG_PIM_RPF +#define DEBUG_PIM_DETAIL 0x00800000 +#define DEBUG_PIM ( DEBUG_PIM_HELLO | DEBUG_PIM_REGISTER | \ + DEBUG_PIM_JOIN_PRUNE | DEBUG_PIM_BOOTSTRAP | \ + DEBUG_PIM_ASSERT | DEBUG_PIM_CAND_RP | \ + DEBUG_PIM_MRT | DEBUG_PIM_TIMER | \ + DEBUG_PIM_RPF | DEBUG_PIM_GRAFT ) + +#define DEBUG_MRT ( DEBUG_DVMRP_ROUTE | DEBUG_PIM_MRT ) +#define DEBUG_NEIGHBORS ( DEBUG_DVMRP_PEER | DEBUG_PIM_HELLO ) +#define DEBUG_TIMER ( DEBUG_MLD_TIMER | DEBUG_DVMRP_TIMER | \ + DEBUG_PIM_TIMER ) +#define DEBUG_ASSERT ( DEBUG_PIM_ASSERT ) +#define DEBUG_ALL 0xffffffff + + +#define DEBUG_DEFAULT 0xffffffff/* default if "-d" given without value */ + + + + + + diff --git a/usr.sbin/pim6dd/defs.h b/usr.sbin/pim6dd/defs.h new file mode 100644 index 0000000..5970c74 --- /dev/null +++ b/usr.sbin/pim6dd/defs.h @@ -0,0 +1,597 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: defs.h,v 1.6 1999/12/10 06:09:13 itojun Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <syslog.h> +#include <signal.h> +#include <string.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#if ((defined(SYSV)) || (defined(__bsdi__)) || ((defined SunOS) && (SunOS < 50))) +#include <sys/sockio.h> +#endif /* SYSV || __bsdi__ || SunOS 4.x */ +#include <sys/time.h> +#include <net/if.h> +#include <net/if_var.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/icmp6.h> + +#include <netinet6/in6_var.h> +#include <netinet/ip6.h> + +#include <arpa/inet.h> +#include <osreldate.h> +#define rtentry kernel_rtentry +#include <net/route.h> +#undef rtentry +#include <netinet/ip_mroute.h> +#include <netinet6/ip6_mroute.h> +#include <strings.h> +#ifdef RSRR +#include <sys/un.h> +#endif /* RSRR */ + +typedef u_int u_int32; +typedef u_short u_int16; +typedef u_char u_int8; + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +typedef void (*cfunc_t) __P((void *)); +typedef void (*ihfunc_t) __P((int, fd_set *)); + +#include "pimdd.h" +#include "mrt.h" +#include "mld6.h" +#include "vif.h" +#include "debug.h" +#include "pathnames.h" +#ifdef RSRR +#include "rsrr.h" +#include "rsrr_var.h" +#endif /* RSRR */ + +/* + * Miscellaneous constants and macros + */ +/* #if (!(defined(__bsdi__)) && !(defined(KERNEL))) */ +#ifndef KERNEL +#define max(a, b) ((a) < (b) ? (b) : (a)) +#define min(a, b) ((a) > (b) ? (b) : (a)) +#endif + +/* + * Various definitions to make it working for different platforms + */ +/* The old style sockaddr definition doesn't have sa_len */ +#if (defined(BSD) && (BSD >= 199006)) /* sa_len was added with 4.3-Reno */ +#define HAVE_SA_LEN +#endif + +/* Versions of Solaris older than 2.6 don't have routing sockets. */ +/* XXX TODO: check FreeBSD version and add all other platforms */ +#if ((defined(SunOS) && SunOS >=56) || (defined __FreeBSD__) || (defined IRIX) || (defined __bsdi__) || defined(__NetBSD__)) +#define HAVE_ROUTING_SOCKETS +#endif + +#define TRUE 1 +#define FALSE 0 + +#define CREATE TRUE +#define DONT_CREATE FALSE + +#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) + +/* obnoxious gcc gives an extraneous warning about this constant... */ +#if defined(__STDC__) || defined(__GNUC__) +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +#else +#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ +#define const /**/ +#endif + + +#define MINHLIM 1 /* min hoplim in the packets send locally */ + +#define MAX_IP_PACKET_LEN 576 +#define MIN_IP_HEADER_LEN 20 +#define MAX_IP_HEADER_LEN 60 + + +/* + * The IGMPv2 <netinet/in.h> defines INADDR_ALLRTRS_GROUP, but earlier + * ones don't, so we define it conditionally here. + */ +#ifndef INADDR_ALLRTRS_GROUP + /* address for multicast mtrace msg */ +#define INADDR_ALLRTRS_GROUP (u_int32)0xe0000002 /* 224.0.0.2 */ +#endif + +#ifndef INADDR_MAX_LOCAL_GROUP +define INADDR_MAX_LOCAL_GROUP (u_int32)0xe00000ff /* 224.0.0.255 */ +#endif + +#define INADDR_ANY_N (u_int32)0x00000000 /* INADDR_ANY in + * network order */ +#define CLASSD_PREFIX (u_int32)0xe0000000 /* 224.0.0.0 */ +#define ALL_MCAST_GROUPS_ADDR (u_int32)0xe0000000 /* 224.0.0.0 */ +#define ALL_MCAST_GROUPS_LENGTH 4 + +/* Used by DVMRP */ +#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */ +#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */ + +#define TIMER_INTERVAL 5 /* 5 sec virtual timer granularity */ + +#ifdef RSRR +#define BIT_ZERO(X) ((X) = 0) +#define BIT_SET(X,n) ((X) |= 1 << (n)) +#define BIT_CLR(X,n) ((X) &= ~(1 << (n))) +#define BIT_TST(X,n) ((X) & 1 << (n)) +#endif /* RSRR */ + +#ifdef SYSV +#define bcopy(a, b, c) memcpy((b), (a), (c)) +#define bzero(s, n) memset((s), 0, (n)) +#define setlinebuf(s) setvbuf((s), (NULL), (_IOLBF), 0) +#define RANDOM() lrand48() +#else +#define RANDOM() random() +#endif /* SYSV */ + +/* + * External declarations for global variables and functions. + */ +#define RECV_BUF_SIZE 64*1024 /* Maximum buff size to send + * or receive packet */ +#define SO_RECV_BUF_SIZE_MAX 256*1024 +#define SO_RECV_BUF_SIZE_MIN 48*1024 + +/* TODO: describe the variables and clean up */ +extern char *mld6_recv_buf; +extern char *mld6_send_buf; +extern char *pim6_recv_buf; +extern char *pim6_send_buf; +extern int mld6_socket; +extern int pim6_socket; +extern struct sockaddr_in6 allnodes_group; +extern struct sockaddr_in6 allrouters_group; +extern struct sockaddr_in6 allpim6routers_group; +extern if_set nbr_mifs; + +#ifdef RSRR +extern int rsrr_socket; +#endif /* RSRR */ + +extern u_long virtual_time; +extern char configfilename[]; + +extern u_int32 default_source_metric; +extern u_int32 default_source_preference; + +extern srcentry_t *srclist; +extern grpentry_t *grplist; + +extern struct uvif uvifs[]; +extern vifi_t numvifs; +extern int total_interfaces; +extern int phys_vif; +extern int udp_socket; + +extern int vifs_down; + +extern char s1[]; +extern char s2[]; +extern char s3[]; +extern char s4[]; + +#if !(defined(BSD) && (BSD >= 199103)) +extern int errno; +extern int sys_nerr; +extern char * sys_errlist[]; +#endif + + +#ifndef IGMP_MEMBERSHIP_QUERY +#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY +#if !(defined(__NetBSD__)) +#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT +#else +#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT +#endif +#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#endif + +#if defined(__NetBSD__) +#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY +#define IGMP_MTRACE IGMP_MTRACE_QUERY +#endif + +/* For timeout. The timers count down */ +#define SET_TIMER(timer, value) (timer) = (value) +#define IF_TIMER_SET(timer) if ((timer) > 0) +#define IF_TIMER_NOT_SET(timer) if ((timer) <= 0) +#define FIRE_TIMER(timer) (timer) = 0 + +#define IF_TIMEOUT(value) \ + if (!(((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL))) + +#define IF_NOT_TIMEOUT(value) \ + if (((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL)) + +#define TIMEOUT(value) \ + (!(((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL))) + +#define NOT_TIMEOUT(value) \ + (((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL)) + +#define ELSE else /* To make emacs cc-mode happy */ + + +/* + * External function definitions + */ + +/* callout.c */ +extern void callout_init __P((void)); +extern void free_all_callouts __P((void)); +extern void age_callout_queue __P((int)); +extern int timer_nextTimer __P((void)); +extern int timer_setTimer __P((int, cfunc_t, void *)); +extern void timer_clearTimer __P((int)); +extern int timer_leftTimer __P((int)); + +/* config.c */ +extern void config_vifs_from_kernel __P((void)); +extern void config_vifs_from_file __P((void)); + +/* debug.c */ +extern char *packet_kind __P((u_int proto, u_int type, u_int code)); +extern int debug_kind __P((u_int proto, u_int type, u_int code)); +extern void log __P((int, int, char *, ...)); +extern int log_level __P((u_int proto, u_int type, u_int code)); +extern void dump __P((int i)); +extern void fdump __P((int i)); +extern void cdump __P((int i)); +extern void dump_vifs __P((FILE *fp)); +extern void dump_pim_mrt __P((FILE *fp)); +extern void dump_lcl_grp __P((FILE *fp)); + +/* dvmrp_proto.c */ +extern void dvmrp_accept_probe __P((u_int32 src, u_int32 dst, + char *p, int datalen, + u_int32 level)); +extern void dvmrp_accept_report __P((u_int32 src, u_int32 dst, + char *p, int datalen, + u_int32 level)); +extern void dvmrp_accept_info_request __P((u_int32 src, u_int32 dst, + u_char *p, int datalen)); +extern void dvmrp_accept_info_reply __P((u_int32 src, u_int32 dst, + u_char *p, int datalen)); +extern void dvmrp_accept_neighbors __P((u_int32 src, u_int32 dst, + u_char *p, int datalen, + u_int32 level)); +extern void dvmrp_accept_neighbors2 __P((u_int32 src, u_int32 dst, + u_char *p, int datalen, + u_int32 level)); +extern void dvmrp_accept_prune __P((u_int32 src, u_int32 dst, + char *p, int datalen)); +extern void dvmrp_accept_graft __P((u_int32 src, u_int32 dst, + char *p, int datalen)); +extern void dvmrp_accept_g_ack __P((u_int32 src, u_int32 dst, + char *p, int datalen)); +/* mld6.c */ +void init_mld6 __P((void)); +void send_mld6 __P((int type, int code, struct sockaddr_in6 *src, + struct sockaddr_in6 *dst, struct in6_addr *group, + int index, int delay, int datalen, int alert)); + +/* mld6_proto.c */ +extern void query_groups __P((struct uvif *v)); +extern int check_grp_membership __P((struct uvif *v, + struct sockaddr_in6 *group)); +extern void accept_listener_query __P((struct sockaddr_in6 *src, + struct in6_addr *dst, + struct in6_addr *group, + int tmo)); +extern void accept_listener_report __P((struct sockaddr_in6 *src, + struct in6_addr *dst, + struct in6_addr *group)); +extern void accept_listener_done __P((struct sockaddr_in6 *src, + struct in6_addr *dst, + struct in6_addr *group)); +extern int check_multicast_listener __P((struct uvif *v, + struct sockaddr_in6 *group)); + +/* igmp.c */ +#if 0 +extern void init_igmp __P((void)); +extern void send_igmp __P((char *buf, u_int32 src, u_int32 dst, + int type, int code, u_int32 group, + int datalen)); + +/* igmp_proto.c */ +extern void query_groups __P((struct uvif *v)); +extern void accept_membership_query __P((u_int32 src, u_int32 dst, + u_int32 group, int tmo)); +extern void accept_group_report __P((u_int32 src, u_int32 dst, + u_int32 group, int r_type)); +extern void accept_leave_message __P((u_int32 src, u_int32 dst, + u_int32 group)); +#endif + +#if 0 +/* inet.c */ +extern int inet_cksum __P((u_int16 *addr, u_int len)); +extern int inet_valid_host __P((u_int32 naddr)); +extern int inet_valid_mask __P((u_int32 mask)); +extern int inet_valid_subnet __P((u_int32 nsubnet, u_int32 nmask)); +extern char *inet_fmt __P((u_int32, char *s)); +#ifdef NOSUCHDEF +extern char *inet_fmts __P((u_int32 addr, u_int32 mask, char *s)); +#endif /* NOSUCHDEF */ +extern char *netname __P((u_int32 addr, u_int32 mask)); +extern u_int32 inet_parse __P((char *s, int n)); +#endif + +/* inet6.c */ +extern int inet6_equal __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2)); +extern int inet6_lessoreq __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2)); +extern int inet6_lessthan __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2)); +extern int inet6_localif_address __P((struct sockaddr_in6 *sa, + struct uvif *v)); +extern int inet6_greaterthan __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2)); +extern int inet6_greateroreq __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2)); +extern int inet6_match_prefix __P((struct sockaddr_in6 *sa1, + struct sockaddr_in6 *sa2, + struct in6_addr *mask)); +extern int inet6_mask2plen __P((struct in6_addr *mask)); +extern int inet6_uvif2scopeid __P((struct sockaddr_in6 *sa, struct uvif *v)); +extern int inet6_valid_host __P((struct sockaddr_in6 *addr)); +extern char *inet6_fmt __P((struct in6_addr *addr)); +extern char *ifindex2str __P((int ifindex)); +extern char *net6name __P((struct in6_addr *prefix, + struct in6_addr *mask)); + +/* kern.c */ +extern void k_set_rcvbuf __P((int socket, int bufsize, int minsize)); +extern void k_hdr_include __P((int socket, int bool)); +extern void k_set_hlim __P((int socket, int t)); +extern void k_set_loop __P((int socket, int l)); +extern void k_set_if __P((int socket, u_int ifindex)); +extern void k_join __P((int socket, struct in6_addr *grp, + u_int ifindex)); +extern void k_leave __P((int socket, struct in6_addr *grp, + u_int ifindex)); +extern void k_init_pim __P((int)); +extern void k_stop_pim __P((int)); +extern int k_del_mfc __P((int socket, struct sockaddr_in6 *source, + struct sockaddr_in6 *group)); +extern int k_chg_mfc __P((int socket, struct sockaddr_in6 *source, + struct sockaddr_in6 *group, vifi_t iif, + if_set *oifs)); +extern void k_add_vif __P((int socket, vifi_t vifi, struct uvif *v)); +extern void k_del_vif __P((int socket, vifi_t vifi)); +extern int k_get_vif_count __P((vifi_t vifi, struct vif_count *retval)); +extern int k_get_sg_cnt __P((int socket, struct sockaddr_in6 *source, + struct sockaddr_in6 *group, + struct sg_count *retval)); + +/* main.c */ +extern int register_input_handler __P((int fd, ihfunc_t func)); + +/* mrt.c */ +extern void init_pim6_mrt __P((void)); +extern mrtentry_t *find_route __P((struct sockaddr_in6 *source, + struct sockaddr_in6 *group, + u_int16 flags, char create)); +extern grpentry_t *find_group __P((struct sockaddr_in6 *group)); +extern srcentry_t *find_source __P((struct sockaddr_in6 *source)); +extern void delete_mrtentry __P((mrtentry_t *mrtentry_ptr)); +extern void delete_srcentry __P((srcentry_t *srcentry_ptr)); +extern void delete_grpentry __P((grpentry_t *grpentry_ptr)); +extern struct mrtfilter *search_filter __P((struct in6_addr *)); +extern struct mrtfilter *add_filter __P((int, struct in6_addr *, + struct in6_addr *, int)); + +/* pim6.c */ +extern void init_pim6 __P((void)); +extern void send_pim6 __P((char *buf, struct sockaddr_in6 *src, + struct sockaddr_in6 *dst, + int type, int datalen)); + +/* pim6_poto.c */ +extern int receive_pim6_hello __P((struct sockaddr_in6 *src, + char *pim_message, int datalen)); +extern int send_pim6_hello __P((struct uvif *v, u_int16 holdtime)); +extern void delete_pim6_nbr __P((pim_nbr_entry_t *nbr_delete)); +extern int receive_pim6_join_prune __P((struct sockaddr_in6 *src, + char *pim_message, int datalen)); +extern int send_pim6_jp __P((mrtentry_t *mrtentry_ptr, int action, + mifi_t mifi, + struct sockaddr_in6 *target_addr, + u_int16 holdtime, int echo)); + +extern int receive_pim6_assert __P((struct sockaddr_in6 *src, + char *pim_message, int datalen)); +extern int send_pim6_assert __P((struct sockaddr_in6 *source, + struct sockaddr_in6 *group, + mifi_t mifi, + mrtentry_t *mrtentry_ptr)); +extern void delete_pim6_graft_entry __P((mrtentry_t *mrtentry_ptr)); +extern int receive_pim6_graft __P((struct sockaddr_in6 *src, + char *pim_message, int datalen, + int pimtype)); +extern int send_pim6_graft __P((mrtentry_t *mrtentry_ptr)); + +#if 0 +/* pim.c */ +extern void init_pim __P((void)); +extern void send_pim __P((char *buf, u_int32 src, u_int32 dst, + int type, int datalen)); +extern void send_pim_unicast __P((char *buf, u_int32 src, u_int32 dst, + int type, int datalen)); +/* pim_proto.c */ +extern int receive_pim_hello __P((u_int32 src, u_int32 dst, + char *pim_message, int datalen)); +extern int send_pim_hello __P((struct uvif *v, u_int16 holdtime)); +extern void delete_pim_nbr __P((pim_nbr_entry_t *nbr_delete)); +extern int receive_pim_join_prune __P((u_int32 src, u_int32 dst, + char *pim_message, int datalen)); +extern int send_pim_jp __P((mrtentry_t *mrtentry_ptr, int action, + vifi_t vifi, u_int32 target_addr, + u_int16 holdtime)); +extern int receive_pim_assert __P((u_int32 src, u_int32 dst, + char *pim_message, int datalen)); +extern int send_pim_assert __P((u_int32 source, u_int32 group, + vifi_t vifi, + mrtentry_t *mrtentry_ptr)); +extern void delete_pim_graft_entry __P((mrtentry_t *mrtentry_ptr)); +extern int receive_pim_graft __P((u_int32 src, u_int32 dst, + char *pim_message, int datalen, + int pimtype)); +#endif + +/* route.c */ +extern int set_incoming __P((srcentry_t *srcentry_ptr, + int srctype)); +extern vifi_t get_iif __P((struct sockaddr_in6 *source)); +extern pim_nbr_entry_t *find_pim6_nbr __P((struct sockaddr_in6 *source)); +extern int add_sg_oif __P((mrtentry_t *mrtentry_ptr, + vifi_t vifi, + u_int16 holdtime, + int update_holdtime)); +extern void add_leaf __P((vifi_t vifi, + struct sockaddr_in6 *source, + struct sockaddr_in6 *group)); +extern void delete_leaf __P((vifi_t vifi, + struct sockaddr_in6 *source, + struct sockaddr_in6 *group)); +extern void set_leaves __P((mrtentry_t *mrtentry_ptr)); +extern int change_interfaces __P((mrtentry_t *mrtentry_ptr, + vifi_t new_iif, + if_set *new_pruned_oifs, + if_set *new_leaves_, + if_set *new_asserted_oifs)); +extern void calc_oifs __P((mrtentry_t *mrtentry_ptr, + if_set *oifs_ptr)); +extern void process_kernel_call __P((void)); +extern int delete_vif_from_mrt __P((vifi_t vifi)); +extern void trigger_join_alert __P((mrtentry_t *mrtentry_ptr)); +extern void trigger_prune_alert __P((mrtentry_t *mrtentry_ptr)); + + +/* routesock.c */ +extern int k_req_incoming __P((struct sockaddr_in6 *source, + struct rpfctl *rpfp)); +#ifdef HAVE_ROUTING_SOCKETS +extern int init_routesock __P((void)); +#endif /* HAVE_ROUTING_SOCKETS */ + +#ifdef RSRR +#define gtable mrtentry +#define RSRR_NOTIFICATION_OK TRUE +#define RSRR_NOTIFICATION_FALSE FALSE + +/* rsrr.c */ +extern void rsrr_init __P((void)); +extern void rsrr_clean __P((void)); +extern void rsrr_cache_send __P((struct gtable *, int)); +extern void rsrr_cache_clean __P((struct gtable *)); +extern void rsrr_cache_bring_up __P((struct gtable *)); +#endif /* RSRR */ + +/* timer.c */ +extern void init_timers __P((void)); +extern void age_vifs __P((void)); +extern void age_routes __P((void)); +extern int clean_srclist __P((void)); + +/* trace.c */ +/* u_int is promoted u_char */ +extern void accept_mtrace __P((struct sockaddr_in6 *src, + struct in6_addr *dst, struct in6_addr *group, + int ifindex, char *data, u_int no, int datalen)); + +/* vif.c */ +extern void init_vifs __P((void)); +extern void stop_all_vifs __P((void)); +extern void check_vif_state __P((void)); +extern vifi_t local_address __P((struct sockaddr_in6 *src)); +extern vifi_t find_vif_direct __P((struct sockaddr_in6 *src)); +extern vifi_t find_vif_direct_local __P((struct sockaddr_in6 *src)); +extern struct sockaddr_in6 *max_global_address __P((void)); +extern struct sockaddr_in6 *uv_global __P((vifi_t vifi)); diff --git a/usr.sbin/pim6dd/inet6.c b/usr.sbin/pim6dd/inet6.c new file mode 100644 index 0000000..7dc9ace --- /dev/null +++ b/usr.sbin/pim6dd/inet6.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "defs.h" + +int +inet6_uvif2scopeid(struct sockaddr_in6 *sa, struct uvif *v) +{ + if (IN6_IS_ADDR_MULTICAST(&sa->sin6_addr)) { + if (IN6_IS_ADDR_MC_LINKLOCAL(&sa->sin6_addr)) + return(v->uv_ifindex); + if (IN6_IS_ADDR_MC_SITELOCAL(&sa->sin6_addr)) + return(v->uv_siteid); + } + else { + if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) + return(v->uv_ifindex); + + if (IN6_IS_ADDR_SITELOCAL(&sa->sin6_addr)) + return(v->uv_siteid); + } + + return(0); +} + +int +inet6_localif_address(struct sockaddr_in6 *sa, struct uvif *v) +{ + struct phaddr *pa; + + for (pa = v->uv_addrs; pa; pa = pa->pa_next) + if (inet6_equal(sa, &pa->pa_addr)) + return(TRUE); + + return(FALSE); +} + +int +inet6_valid_host(struct sockaddr_in6 *addr) +{ + if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) + return(FALSE); + + return(TRUE); +} + +int +inet6_equal(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2) +{ + if (sa1->sin6_scope_id == sa2->sin6_scope_id && + IN6_ARE_ADDR_EQUAL(&sa1->sin6_addr, &sa2->sin6_addr)) + return(1); + + return(0); +} + +int +inet6_lessthan(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2) +{ + u_int32_t s32_1, s32_2; + int i; + + if (sa1->sin6_scope_id < sa2->sin6_scope_id) + return(1); + if (sa1->sin6_scope_id == sa2->sin6_scope_id) { + for (i = 0; i < 4; i++) { + s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]); + s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]); + + if (s32_1 > s32_2) + return(0); + if (s32_1 < s32_2) + return(1); + + /* otherwide, continue to compare */ + } + } + + return(0); +} + +int +inet6_lessoreq(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2) +{ + u_int32_t s32_1, s32_2; + int i; + + if (sa1->sin6_scope_id < sa2->sin6_scope_id) + return(1); + if (sa1->sin6_scope_id == sa2->sin6_scope_id) { + for (i = 0; i < 4; i++) { + s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]); + s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]); + + if (s32_1 > s32_2) + return(0); + if (s32_1 < s32_2) + return(1); + + /* otherwide, continue to compare */ + } + /* sa1 == sa2 */ + return(1); + } + + return(0); +} + +int +inet6_greaterthan(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2) +{ + u_int32_t s32_1, s32_2; + int i; + + if (sa1->sin6_scope_id > sa2->sin6_scope_id) + return(1); + if (sa1->sin6_scope_id == sa2->sin6_scope_id) { + for (i = 0; i < 4; i++) { + s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]); + s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]); + + if (s32_1 < s32_2) + return(0); + if (s32_1 > s32_2) + return(1); + + /* otherwide, continue to compare */ + } + } + + return(0); +} + +int +inet6_greateroreq(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2) +{ + u_int32_t s32_1, s32_2; + int i; + + if (sa1->sin6_scope_id > sa2->sin6_scope_id) + return(1); + if (sa1->sin6_scope_id == sa2->sin6_scope_id) { + for (i = 0; i < 4; i++) { + s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]); + s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]); + + if (s32_1 < s32_2) + return(0); + if (s32_1 > s32_2) + return(1); + + /* otherwide, continue to compare */ + } + /* sa1 == sa2 */ + return(1); + } + + return(0); +} + +int +inet6_match_prefix(sa1, sa2, mask) + struct sockaddr_in6 *sa1, *sa2; + struct in6_addr *mask; +{ + int i; + + if (sa1->sin6_scope_id != sa2->sin6_scope_id) + return(0); + + for (i = 0; i < 16; i++) { + if ((sa1->sin6_addr.s6_addr[i] ^ sa2->sin6_addr.s6_addr[i]) & + mask->s6_addr[i]) + return(0); + } + + return(1); +} + +char * +inet6_fmt(struct in6_addr *addr) +{ + static char ip6buf[8][INET6_ADDRSTRLEN]; + static int ip6round = 0; + char *cp; + + ip6round = (ip6round + 1) & 7; + cp = ip6buf[ip6round]; + + inet_ntop(AF_INET6, addr, cp, INET6_ADDRSTRLEN); + return(cp); +} + +char * +ifindex2str(int ifindex) +{ + static char ifname[IFNAMSIZ]; + + return(if_indextoname(ifindex, ifname)); +} + +int +inet6_mask2plen(struct in6_addr *mask) +{ + int masklen; + u_char *p = (u_char *)mask; + u_char *lim = p + 16; + + for (masklen = 0; p < lim; p++) { + switch (*p) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + break; + case 0xfc: + masklen += 6; + break; + case 0xf8: + masklen += 5; + break; + case 0xf0: + masklen += 4; + break; + case 0xe0: + masklen += 3; + break; + case 0xc0: + masklen += 2; + break; + case 0x80: + masklen += 1; + break; + case 0x00: + break; + } + } + + return(masklen); +} + +char * +net6name(struct in6_addr *prefix, struct in6_addr *mask) +{ + static char ip6buf[8][INET6_ADDRSTRLEN + 4]; /* length of addr/plen */ + static int ip6round = 0; + char *cp; + + ip6round = (ip6round + 1) & 7; + cp = ip6buf[ip6round]; + + inet_ntop(AF_INET6, prefix, cp, INET6_ADDRSTRLEN); + cp += strlen(cp); + *cp = '/'; + cp++; + sprintf(cp, "%d", inet6_mask2plen(mask)); + + return(ip6buf[ip6round]); +} diff --git a/usr.sbin/pim6dd/kern.c b/usr.sbin/pim6dd/kern.c new file mode 100644 index 0000000..170e85e --- /dev/null +++ b/usr.sbin/pim6dd/kern.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: kern.c,v 1.1.1.1 1999/08/08 23:30:52 itojun Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +#ifdef RAW_OUTPUT_IS_RAW +int curttl = 0; +#endif + + +/* + * Open/init the multicast routing in the kernel and sets the MRT_ASSERT + * flag in the kernel. + * + */ +void +k_init_pim(socket) + int socket; +{ + int v = 1; + + if (setsockopt(socket, IPPROTO_IPV6, + MRT6_INIT, (char *)&v, sizeof(int)) < 0) + log(LOG_ERR, errno, "cannot enable multicast routing in kernel"); + + if(setsockopt(socket, IPPROTO_IPV6, + MRT6_PIM, (char *)&v, sizeof(int)) < 0) + log(LOG_ERR, errno, "cannot set ASSERT flag in kernel"); +} + + +/* + * Stops the multicast routing in the kernel and resets the MRT_ASSERT + * flag in the kernel. + */ +void +k_stop_pim(socket) + int socket; +{ + int v = 0; + + if(setsockopt(socket, IPPROTO_IPV6, MRT6_PIM, + (char *)&v, sizeof(int)) < 0) + log(LOG_ERR, errno, "cannot reset ASSERT flag in kernel"); + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DONE, (char *)NULL, 0) < 0) + log(LOG_ERR, errno, "cannot disable multicast routing in kernel"); + +} + + +/* + * Set the socket receiving buffer. `bufsize` is the preferred size, + * `minsize` is the smallest acceptable size. + */ +void k_set_rcvbuf(socket, bufsize, minsize) + int socket; + int bufsize; + int minsize; +{ + int delta = bufsize / 2; + int iter = 0; + + /* + * Set the socket buffer. If we can't set it as large as we + * want, search around to try to find the highest acceptable + * value. The highest acceptable value being smaller than + * minsize is a fatal error. + */ + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, + (char *)&bufsize, sizeof(bufsize)) < 0) { + bufsize -= delta; + while (1) { + iter++; + if (delta > 1) + delta /= 2; + + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, + (char *)&bufsize, sizeof(bufsize)) < 0) { + bufsize -= delta; + } else { + if (delta < 1024) + break; + bufsize += delta; + } + } + if (bufsize < minsize) { + log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u", + bufsize, minsize); + /*NOTREACHED*/ + } + } + IF_DEBUG(DEBUG_KERN) + log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations", + bufsize, iter); +} + +#if 0 /* there is no HDRINCL option in IPv6 */ +/* + * Set/reset the IP_HDRINCL option. My guess is we don't need it for raw + * sockets, but having it here won't hurt. Well, unless you are running + * an older version of FreeBSD (older than 2.2.2). If the multicast + * raw packet is bigger than 208 bytes, then IP_HDRINCL triggers a bug + * in the kernel and "panic". The kernel patch for netinet/ip_raw.c + * coming with this distribution fixes it. + */ +void k_hdr_include(socket, bool) + int socket; + int bool; +{ +#ifdef IP_HDRINCL + if (setsockopt(socket, IPPROTO_IP, IP_HDRINCL, + (char *)&bool, sizeof(bool)) < 0) + log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool); +#endif +} +#endif /* 0 */ + +/* + * Set the default Hop Limit for the multicast packets outgoing from this + * socket. + */ +void k_set_hlim(socket, h) + int socket; + int h; +{ +#ifdef RAW_OUTPUT_IS_RAW + curttl = h; +#else + int hlim = h; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (char *)&hlim, sizeof(hlim)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_HOPS %u", hlim); +#endif +} + + +/* + * Set/reset the IPV6_MULTICAST_LOOP. Set/reset is specified by "flag". + */ +void k_set_loop(socket, flag) + int socket; + int flag; +{ + u_int loop; + + loop = flag; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_LOOP %u", loop); +} + + +/* + * Set the IPV6_MULTICAST_IF option on local interface which has the + * specified index. + */ +void k_set_if(socket, ifindex) + int socket; + u_int ifindex; +{ + if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (char *)&ifindex, sizeof(ifindex)) < 0) + log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_IF for %s", + ifindex2str(ifindex)); +} + + +/* + * Join a multicast grp group on local interface ifa. + */ +void k_join(socket, grp, ifindex) + int socket; + struct in6_addr *grp; + u_int ifindex; +{ + struct ipv6_mreq mreq; + + mreq.ipv6mr_multiaddr = *grp; + mreq.ipv6mr_interface = ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "cannot join group %s on interface %s", + inet6_fmt(grp), ifindex2str(ifindex)); +} + + +/* + * Leave a multicats grp group on local interface ifa. + */ +void k_leave(socket, grp, ifindex) + int socket; + struct in6_addr *grp; + u_int ifindex; +{ + struct ipv6_mreq mreq; + + mreq.ipv6mr_multiaddr = *grp; + mreq.ipv6mr_interface = ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (char *)&mreq, sizeof(mreq)) < 0) + log(LOG_WARNING, errno, "cannot leave group %s on interface %s", + inet6_fmt(grp), ifindex2str(ifindex)); +} + + +/* + * Add a virtual interface in the kernel. + */ +void k_add_vif(socket, vifi, v) + int socket; + vifi_t vifi; + struct uvif *v; +{ + struct mif6ctl mc; + + mc.mif6c_mifi = vifi; + /* TODO: only for DVMRP tunnels? + mc.mif6c_flags = v->uv_flags & VIFF_KERNEL_FLAGS; + */ + mc.mif6c_flags = v->uv_flags; +#ifdef notyet + mc.mif6c_rate_limit = v->uv_rate_limit; +#endif + mc.mif6c_pifi = v->uv_ifindex; + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MIF, + (char *)&mc, sizeof(mc)) < 0) + log(LOG_ERR, errno, "setsockopt MRT6_ADD_MIF on mif %d", vifi); +} + +/* + * Delete a virtual interface in the kernel. + */ +void k_del_vif(socket, vifi) + int socket; + vifi_t vifi; +{ + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MIF, + (char *)&vifi, sizeof(vifi)) < 0) + log(LOG_ERR, errno, "setsockopt MRT6_DEL_MIF on mif %d", vifi); +} + + +/* + * Delete all MFC entries for particular routing entry from the kernel. + */ +int +k_del_mfc(socket, source, group) + int socket; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; +{ + struct mf6cctl mc; + + mc.mf6cc_origin = *source; + mc.mf6cc_mcastgrp = *group; + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MFC, (char *)&mc, + sizeof(mc)) < 0) { + log(LOG_WARNING, errno, "setsockopt MRT6_DEL_MFC"); + return FALSE; + } + + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, "Deleted MFC entry: src %s, grp %s", + inet6_fmt(&source->sin6_addr), + inet6_fmt(&group->sin6_addr)); + + return(TRUE); +} + + +/* + * Install/modify a MFC entry in the kernel + */ +int +k_chg_mfc(socket, source, group, iif, oifs) + int socket; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + vifi_t iif; + if_set *oifs; +{ + struct mf6cctl mc; + vifi_t vifi; + + mc.mf6cc_origin = *source; + mc.mf6cc_mcastgrp = *group; + mc.mf6cc_parent = iif; + + IF_ZERO(&mc.mf6cc_ifset); + for (vifi = 0; vifi < numvifs; vifi++) { + if (IF_ISSET(vifi, oifs)) + IF_SET(vifi, &mc.mf6cc_ifset); + else + IF_CLR(vifi, &mc.mf6cc_ifset); + } + + if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MFC, (char *)&mc, + sizeof(mc)) < 0) { + log(LOG_WARNING, errno, + "setsockopt MRT6_ADD_MFC for source %s and group %s", + inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr)); + return(FALSE); + } + return(TRUE); +} + + +/* + * Get packet counters for particular interface + */ +/* + * XXX: TODO: currently not used, but keep just in case we need it later. + */ +int k_get_vif_count(vifi, retval) + vifi_t vifi; + struct vif_count *retval; +{ + struct sioc_mif_req6 mreq; + + mreq.mifi = vifi; + if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) < 0) { + log(LOG_WARNING, errno, "SIOCGETMIFCNT_IN6 on vif %d", vifi); + retval->icount = retval->ocount = retval->ibytes = + retval->obytes = 0xffffffff; + return (1); + } + retval->icount = mreq.icount; + retval->ocount = mreq.ocount; + retval->ibytes = mreq.ibytes; + retval->obytes = mreq.obytes; + return (0); +} + + +/* + * Gets the number of packets, bytes, and number of packets arrived + * on wrong if in the kernel for particular (S,G) entry. + */ +int +k_get_sg_cnt(socket, source, group, retval) + int socket; /* udp_socket */ + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + struct sg_count *retval; +{ + struct sioc_sg_req6 sgreq; + + sgreq.src = *source; + sgreq.grp = *group; + if (ioctl(socket, SIOCGETSGCNT_IN6, (char *)&sgreq) < 0) { + log(LOG_WARNING, errno, "SIOCGETSGCNT_IN6 on (%s %s)", + inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr)); + retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0; /* XXX */ + return(1); + } + retval->pktcnt = sgreq.pktcnt; + retval->bytecnt = sgreq.bytecnt; + retval->wrong_if = sgreq.wrong_if; + return(0); +} + + + diff --git a/usr.sbin/pim6dd/main.c b/usr.sbin/pim6dd/main.c new file mode 100644 index 0000000..7efc30e --- /dev/null +++ b/usr.sbin/pim6dd/main.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: main.c,v 1.2 1999/08/13 09:20:13 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +#ifdef SNMP +#include "snmp.h" +#endif + +char configfilename[256] = _PATH_PIM6D_CONF; +char versionstring[100]; + +static char pidfilename[] = _PATH_PIM6D_PID; +/* TODO: not used +static char genidfilename[] = _PATH_PIM6D_GENID; +*/ + +int haveterminal = 1; +char *progname; + +static int sighandled = 0; +#define GOT_SIGINT 0x01 +#define GOT_SIGHUP 0x02 +#define GOT_SIGUSR1 0x04 +#define GOT_SIGUSR2 0x08 +#define GOT_SIGALRM 0x10 + + +#ifdef SNMP +#define NHANDLERS 34 +#else +#define NHANDLERS 3 +#endif + +static struct ihandler { + int fd; /* File descriptor */ + ihfunc_t func; /* Function to call with &fd_set */ +} ihandlers[NHANDLERS]; +static int nhandlers = 0; + +static struct debugname { + char *name; + int level; + int nchars; +} debugnames[] = { +#if 0 + { "dvmrp_detail", DEBUG_DVMRP_DETAIL, 5 }, + { "dvmrp_prunes", DEBUG_DVMRP_PRUNE, 8 }, + { "dvmrp_pruning", DEBUG_DVMRP_PRUNE, 8 }, + { "dvmrp_mrt", DEBUG_DVMRP_ROUTE, 7 }, + { "dvmrp_routes", DEBUG_DVMRP_ROUTE, 7 }, + { "dvmrp_routing", DEBUG_DVMRP_ROUTE, 7 }, + { "dvmrp_neighbors", DEBUG_DVMRP_PEER, 7 }, + { "dvmrp_peers", DEBUG_DVMRP_PEER, 8 }, + { "dvmrp_hello", DEBUG_DVMRP_PEER, 7 }, + { "dvmrp_timers", DEBUG_DVMRP_TIMER, 7 }, + { "dvmrp", DEBUG_DVMRP, 1 }, + { "igmp_proto", DEBUG_IGMP_PROTO, 6 }, + { "igmp_timers", DEBUG_IGMP_TIMER, 6 }, + { "igmp_members", DEBUG_IGMP_MEMBER, 6 }, + { "groups", DEBUG_MEMBER, 1 }, + { "membership", DEBUG_MEMBER, 2 }, + { "igmp", DEBUG_IGMP, 1 }, +#endif + { "trace", DEBUG_TRACE, 2 }, + { "mtrace", DEBUG_TRACE, 2 }, + { "traceroute", DEBUG_TRACE, 2 }, + { "timeout", DEBUG_TIMEOUT, 2 }, + { "callout", DEBUG_TIMEOUT, 3 }, + { "pkt", DEBUG_PKT, 2 }, + { "packets", DEBUG_PKT, 2 }, + { "interfaces", DEBUG_IF, 2 }, + { "vif", DEBUG_IF, 1 }, + { "kernel", DEBUG_KERN, 2 }, + { "cache", DEBUG_MFC, 1 }, + { "mfc", DEBUG_MFC, 2 }, + { "k_cache", DEBUG_MFC, 2 }, + { "k_mfc", DEBUG_MFC, 2 }, + { "rsrr", DEBUG_RSRR, 2 }, + { "pim_detail", DEBUG_PIM_DETAIL, 5 }, + { "pim_hello", DEBUG_PIM_HELLO, 5 }, + { "pim_neighbors", DEBUG_PIM_HELLO, 5 }, + { "pim_register", DEBUG_PIM_REGISTER, 5 }, + { "registers", DEBUG_PIM_REGISTER, 2 }, + { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 }, + { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 }, + { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 }, + { "pim_graft", DEBUG_PIM_GRAFT, 5 }, + { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 }, + { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 }, + { "bsr", DEBUG_PIM_BOOTSTRAP, 1 }, + { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 }, + { "pim_asserts", DEBUG_PIM_ASSERT, 5 }, + { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 }, + { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 }, + { "pim_rp", DEBUG_PIM_CAND_RP, 6 }, + { "rp", DEBUG_PIM_CAND_RP, 2 }, + { "pim_routes", DEBUG_PIM_MRT, 6 }, + { "pim_routing", DEBUG_PIM_MRT, 6 }, + { "pim_mrt", DEBUG_PIM_MRT, 5 }, + { "pim_timers", DEBUG_PIM_TIMER, 5 }, + { "pim_rpf", DEBUG_PIM_RPF, 6 }, + { "rpf", DEBUG_RPF, 3 }, + { "pim", DEBUG_PIM, 1 }, + { "routes", DEBUG_MRT, 1 }, + { "routing", DEBUG_MRT, 1 }, + { "mrt", DEBUG_MRT, 1 }, + { "routers", DEBUG_NEIGHBORS, 6 }, + { "mrouters", DEBUG_NEIGHBORS, 7 }, + { "neighbors", DEBUG_NEIGHBORS, 1 }, + { "timers", DEBUG_TIMER, 1 }, + { "asserts", DEBUG_ASSERT, 1 }, + { "all", DEBUG_ALL, 2 }, + { "3", 0xffffffff, 1 } /* compat. */ +}; + +/* + * Forward declarations. + */ +static void handler __P((int)); +static void timer __P((void *)); +static void cleanup __P((void)); +static void restart __P((int)); +static void cleanup __P((void)); +static void resetlogging __P((void *)); + + +/* To shut up gcc -Wstrict-prototypes */ +int main __P((int argc, char **argv)); + +int +register_input_handler(fd, func) + int fd; + ihfunc_t func; +{ + if (nhandlers >= NHANDLERS) + return -1; + + ihandlers[nhandlers].fd = fd; + ihandlers[nhandlers++].func = func; + + return 0; +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int dummy, dummysigalrm; + FILE *fp; + struct timeval tv, difftime, curtime, lasttime, *timeout; + fd_set rfds, readers; + int nfds, n, i, secs; + extern char todaysversion[]; + struct sigaction sa; + struct debugname *d; + char c; + int tmpd; + + + setlinebuf(stderr); + + if (geteuid() != 0) { + fprintf(stderr, "pim6dd: must be root\n"); + exit(1); + } + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + + argv++; + argc--; + while (argc > 0 && *argv[0] == '-') { + if (strcmp(*argv, "-d") == 0) { + if (argc > 1 && *(argv + 1)[0] != '-') { + char *p,*q; + int i, len; + struct debugname *d; + + argv++; + argc--; + debug = 0; + p = *argv; q = NULL; + while (p) { + q = strchr(p, ','); + if (q) + *q++ = '\0'; + len = strlen(p); + for (i = 0, d = debugnames; + i < sizeof(debugnames) / sizeof(debugnames[0]); + i++, d++) + if (len >= d->nchars && strncmp(d->name, p, len) == 0) + break; + if (i == sizeof(debugnames) / sizeof(debugnames[0])) { + int j = 0xffffffff; + int k = 0; + fprintf(stderr, "Valid debug levels: "); + for (i = 0, d = debugnames; + i < sizeof(debugnames) / sizeof(debugnames[0]); + i++, d++) { + if ((j & d->level) == d->level) { + if (k++) + putc(',', stderr); + fputs(d->name, stderr); + j &= ~d->level; + } + } + putc('\n', stderr); + goto usage; + } + debug |= d->level; + p = q; + } + } + else + debug = DEBUG_DEFAULT; + } + else if (strcmp(*argv, "-c") == 0) { + if (argc > 1) { + argv++; argc--; + strcpy(configfilename, *argv); + } + else + goto usage; +/* TODO: not implemented */ +#ifdef SNMP + } + else if (strcmp(*argv, "-P") == 0) { + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + dest_port = atoi(*argv); + } + else + dest_port = DEFAULT_PORT; +#endif + } + else + goto usage; + argv++; argc--; + } + + if (argc > 0) { + usage: + tmpd = 0xffffffff; + fprintf(stderr, "usage: pim6dd [-c configfile] [-d [debug_level][,debug_level]]\n"); + + fprintf(stderr, "debug levels: "); + c = '('; + for (d = debugnames; d < debugnames + + sizeof(debugnames) / sizeof(debugnames[0]); d++) { + if ((tmpd & d->level) == d->level) { + tmpd &= ~d->level; + fprintf(stderr, "%c%s", c, d->name); + c = ','; + } + } + fprintf(stderr, ")\n"); + exit(1); + } + + if (debug != 0) { + tmpd = debug; + fprintf(stderr, "debug level 0x%lx ", debug); + c = '('; + for (d = debugnames; d < debugnames + + sizeof(debugnames) / sizeof(debugnames[0]); d++) { + if ((tmpd & d->level) == d->level) { + tmpd &= ~d->level; + fprintf(stderr, "%c%s", c, d->name); + c = ','; + } + } + fprintf(stderr, ")\n"); + } + +#ifdef LOG_DAEMON + (void)openlog("pim6dd", LOG_PID, LOG_DAEMON); + (void)setlogmask(LOG_UPTO(LOG_NOTICE)); +#else + (void)openlog("pim6dd", LOG_PID); +#endif /* LOG_DAEMON */ + sprintf(versionstring, "pim6dd version %s", todaysversion); + + log(LOG_DEBUG, 0, "%s starting", versionstring); + +/* TODO: XXX: use a combination of time and hostid to initialize the random + * generator. + */ +#ifdef SYSV + srand48(time(NULL)); +#else + { + struct timeval tm; + gettimeofday(&tm, NULL); + srandom(tm.tv_usec + gethostid()); + } +#endif + + callout_init(); + + /* Start up the log rate-limiter */ + resetlogging(NULL); + + init_mld6(); +#if 0 + k_stop_pim(mld6_socket); + exit(0); /* XXX */ +#endif + init_pim6(); + + init_pim6_mrt(); + init_timers(); + + /* TODO: check the kernel DVMRP/MROUTED/PIM support version */ + +#ifdef SNMP + if (i = snmp_init()) + return i; +#endif /* SNMP */ + init_vifs(); + +#ifdef RSRR + rsrr_init(); +#endif /* RSRR */ + + sa.sa_handler = handler; + sa.sa_flags = 0; /* Interrupt system calls */ + sigemptyset(&sa.sa_mask); + sigaction(SIGALRM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + FD_ZERO(&readers); + FD_SET(mld6_socket, &readers); + nfds = mld6_socket + 1; + for (i = 0; i < nhandlers; i++) { + FD_SET(ihandlers[i].fd, &readers); + if (ihandlers[i].fd >= nfds) + nfds = ihandlers[i].fd + 1; + } + + IF_DEBUG(DEBUG_IF) + dump_vifs(stderr); + IF_DEBUG(DEBUG_PIM_MRT) + dump_pim_mrt(stderr); + + /* schedule first timer interrupt */ + timer_setTimer(TIMER_INTERVAL, timer, NULL); + + if (debug == 0) { + /* Detach from the terminal */ +#ifdef TIOCNOTTY + int t; +#endif /* TIOCNOTTY */ + + haveterminal = 0; + if (fork()) + exit(0); + (void)close(0); + (void)close(1); + (void)close(2); + (void)open("/", 0); + (void)dup2(0, 1); + (void)dup2(0, 2); +#if defined(SYSV) || defined(linux) + (void)setpgrp(); +#else +#ifdef TIOCNOTTY + t = open("/dev/tty", 2); + if (t >= 0) { + (void)ioctl(t, TIOCNOTTY, (char *)0); + (void)close(t); + } +#else + if (setsid() < 0) + perror("setsid"); +#endif /* TIOCNOTTY */ +#endif /* SYSV */ + } /* End of child process code */ + +#ifdef HAVE_ROUTING_SOCKETS + init_routesock(); +#endif /* HAVE_ROUTING_SOCKETS */ + + fp = fopen(pidfilename, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", (int)getpid()); + (void) fclose(fp); + } + + /* + * Main receive loop. + */ + dummy = 0; + dummysigalrm = SIGALRM; + difftime.tv_usec = 0; + gettimeofday(&curtime, NULL); + lasttime = curtime; + for(;;) { + bcopy((char *)&readers, (char *)&rfds, sizeof(rfds)); + secs = timer_nextTimer(); + if (secs == -1) + timeout = NULL; + else { + timeout = &tv; + timeout->tv_sec = secs; + timeout->tv_usec = 0; + } + + if (sighandled) { + if (sighandled & GOT_SIGINT) { + sighandled &= ~GOT_SIGINT; + break; + } + if (sighandled & GOT_SIGHUP) { + sighandled &= ~GOT_SIGHUP; + restart(SIGHUP); + } + if (sighandled & GOT_SIGUSR1) { + sighandled &= ~GOT_SIGUSR1; + fdump(SIGUSR1); + } + if (sighandled & GOT_SIGUSR2) { + sighandled &= ~GOT_SIGUSR2; + cdump(SIGUSR2); + } + if (sighandled & GOT_SIGALRM) { + sighandled &= ~GOT_SIGALRM; + timer(&dummysigalrm); + } + } + if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { + if (errno != EINTR) /* SIGALRM is expected */ + log(LOG_WARNING, errno, "select failed"); + continue; + } + + /* + * Handle timeout queue. + * + * If select + packet processing took more than 1 second, + * or if there is a timeout pending, age the timeout queue. + * + * If not, collect usec in difftime to make sure that the + * time doesn't drift too badly. + * + * If the timeout handlers took more than 1 second, + * age the timeout queue again. XXX This introduces the + * potential for infinite loops! + */ + do { + /* + * If the select timed out, then there's no other + * activity to account for and we don't need to + * call gettimeofday. + */ + if (n == 0) { + curtime.tv_sec = lasttime.tv_sec + secs; + curtime.tv_usec = lasttime.tv_usec; + n = -1; /* don't do this next time through the loop */ + } else + gettimeofday(&curtime, NULL); + difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; + difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; +#ifdef TIMERDEBUG + IF_DEBUG(DEBUG_TIMEOUT) + log(LOG_DEBUG, 0, "TIMEOUT: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec ); +#endif + while (difftime.tv_usec > 1000000) { + difftime.tv_sec++; + difftime.tv_usec -= 1000000; + } + if (difftime.tv_usec < 0) { + difftime.tv_sec--; + difftime.tv_usec += 1000000; + } + lasttime = curtime; + if (secs == 0 || difftime.tv_sec > 0) { +#ifdef TIMERDEBUG + IF_DEBUG(DEBUG_TIMEOUT) + log(LOG_DEBUG, 0, "\taging callouts: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec ); +#endif + age_callout_queue(difftime.tv_sec); + } + secs = -1; + } while (difftime.tv_sec > 0); + + /* Handle sockets */ + if (n > 0) { + /* TODO: shall check first mld6_socket for better performance? */ + for (i = 0; i < nhandlers; i++) { + if (FD_ISSET(ihandlers[i].fd, &rfds)) { + (*ihandlers[i].func)(ihandlers[i].fd, &rfds); + } + } + } + + } /* Main loop */ + + log(LOG_NOTICE, 0, "%s exiting", versionstring); + cleanup(); + exit(0); +} + +/* + * The 'virtual_time' variable is initialized to a value that will cause the + * first invocation of timer() to send a probe or route report to all vifs + * and send group membership queries to all subnets for which this router is + * querier. This first invocation occurs approximately TIMER_INTERVAL seconds + * after the router starts up. Note that probes for neighbors and queries + * for group memberships are also sent at start-up time, as part of initial- + * ization. This repetition after a short interval is desirable for quickly + * building up topology and membership information in the presence of possible + * packet loss. + * + * 'virtual_time' advances at a rate that is only a crude approximation of + * real time, because it does not take into account any time spent processing, + * and because the timer intervals are sometimes shrunk by a random amount to + * avoid unwanted synchronization with other routers. + */ + +u_long virtual_time = 0; + +/* + * Timer routine. Performs all perodic functions: + * aging interfaces, quering neighbors and members, etc... The granularity + * is equal to TIMER_INTERVAL. + */ +static void +timer(i) + void *i; +{ + age_vifs(); /* Timeout neighbors and groups */ + age_routes(); /* Timeout routing entries */ + + virtual_time += TIMER_INTERVAL; + timer_setTimer(TIMER_INTERVAL, timer, NULL); +} + +/* + * Performs all necessary functions to quit gracefully + */ +/* TODO: implement all necessary stuff */ +static void +cleanup() +{ + +#ifdef RSRR + rsrr_clean(); +#endif /* RSRR */ + + k_stop_pim(mld6_socket); + + /* TODO: XXX (not in the spec) + */ +} + + +/* + * Signal handler. Take note of the fact that the signal arrived + * so that the main loop can take care of it. + */ +static void +handler(sig) + int sig; +{ + switch (sig) { + case SIGALRM: + sighandled |= GOT_SIGALRM; + case SIGINT: + case SIGTERM: + sighandled |= GOT_SIGINT; + break; + + case SIGHUP: + sighandled |= GOT_SIGHUP; + break; + + case SIGUSR1: + sighandled |= GOT_SIGUSR1; + break; + + case SIGUSR2: + sighandled |= GOT_SIGUSR2; + break; + } +} + + +/* TODO: not verified */ +/* PIMDM TODO */ +/* + * Restart the daemon + */ +static void +restart(i) + int i; +{ +#ifdef SNMP + int s; +#endif /* SNMP */ + + log(LOG_NOTICE, 0, "% restart", versionstring); + + /* + * reset all the entries + */ + /* + * TODO: delete? + * free_all_routes(); + */ + free_all_callouts(); + stop_all_vifs(); + nhandlers=0; + k_stop_pim(mld6_socket); + close(mld6_socket); + close(pim6_socket); + close(udp_socket); + + /* + * start processing again + */ + init_mld6(); + init_pim6(); +#ifdef HAVE_ROUTING_SOCKETS + init_routesock(); +#endif /* HAVE_ROUTING_SOCKETS */ + init_pim6_mrt(); +#ifdef SNMP + if ( s = snmp_init()) + exit(s); +#endif /* SNMP */ + init_vifs(); + +#ifdef RSRR + rsrr_init(); +#endif /* RSRR */ + + /* schedule timer interrupts */ + timer_setTimer(TIMER_INTERVAL, timer, NULL); +} + + +static void +resetlogging(arg) + void *arg; +{ + int nxttime = 60; + void *narg = NULL; + + if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) { + nxttime = LOG_SHUT_UP; + narg = (void *)&log_nmsgs; /* just need some valid void * */ + syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes", + LOG_SHUT_UP / 60); + } else { + log_nmsgs = 0; + } + + timer_setTimer(nxttime, resetlogging, narg); +} diff --git a/usr.sbin/pim6dd/mld6.c b/usr.sbin/pim6dd/mld6.c new file mode 100644 index 0000000..9185935 --- /dev/null +++ b/usr.sbin/pim6dd/mld6.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: mld6.c,v 1.7 2000/01/04 17:17:21 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" +#include <sys/uio.h> + +/* + * Exported variables. + */ + +char *mld6_recv_buf; /* input packet buffer */ +char *mld6_send_buf; /* output packet buffer */ +int mld6_socket; /* socket for all network I/O */ +struct sockaddr_in6 allrouters_group = {sizeof(struct sockaddr_in6), AF_INET6}; +struct sockaddr_in6 allnodes_group = {sizeof(struct sockaddr_in6), AF_INET6}; + +/* Extenals */ + +extern struct in6_addr in6addr_linklocal_allnodes; + +/* local variables. */ +static struct sockaddr_in6 dst = {sizeof(dst), AF_INET6}; +static struct msghdr sndmh, + rcvmh; +static struct iovec sndiov[2]; +static struct iovec rcviov[2]; +static struct sockaddr_in6 from; +static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + + CMSG_SPACE(sizeof(int))]; +#ifndef USE_RFC2292BIS +u_int8_t raopt[IP6OPT_RTALERT_LEN]; +#endif +static char *sndcmsgbuf; +static int ctlbuflen = 0; +static u_short rtalert_code; + +/* local functions */ + +static void mld6_read __P((int i, fd_set * fds)); +static void accept_mld6 __P((int len)); +static void make_mld6_msg __P((int, int, struct sockaddr_in6 *, + struct sockaddr_in6 *, struct in6_addr *, int, int, int, int)); + +#ifndef IP6OPT_ROUTER_ALERT +#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT +#endif + +/* + * Open and initialize the MLD socket. + */ +void +init_mld6() +{ + struct icmp6_filter filt; + int on; + + rtalert_code = htons(IP6OPT_RTALERT_MLD); + if (!mld6_recv_buf && (mld6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL) + log(LOG_ERR, 0, "malloca failed"); + if (!mld6_send_buf && (mld6_send_buf = malloc(RECV_BUF_SIZE)) == NULL) + log(LOG_ERR, 0, "malloca failed"); + + IF_DEBUG(DEBUG_KERN) + log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE); + + if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) + log(LOG_ERR, errno, "MLD6 socket"); + + k_set_rcvbuf(mld6_socket, SO_RECV_BUF_SIZE_MAX, + SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */ + k_set_hlim(mld6_socket, MINHLIM); /* restrict multicasts to one hop */ + k_set_loop(mld6_socket, FALSE); /* disable multicast loopback */ + + /* address initialization */ + allnodes_group.sin6_addr = in6addr_linklocal_allnodes; + if (inet_pton(AF_INET6, "ff02::2", + (void *) &allrouters_group.sin6_addr) != 1) + log(LOG_ERR, 0, "inet_pton failed for ff02::2"); + + /* filter all non-MLD ICMP messages */ + ICMP6_FILTER_SETBLOCKALL(&filt); + ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt); + ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt); + ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt); + ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filt); + ICMP6_FILTER_SETPASS(MLD6_MTRACE, &filt); + if (setsockopt(mld6_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, + sizeof(filt)) < 0) + log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)"); + + /* specify to tell receiving interface */ + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) + log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)"); +#else /* old adv. API */ + if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) + log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)"); +#endif + on = 1; + /* specify to tell value of hoplimit field of received IP6 hdr */ +#ifdef IPV6_RECVHOPLIMIT + if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, + sizeof(on)) < 0) + log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)"); +#else /* old adv. API */ + if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, + sizeof(on)) < 0) + log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)"); +#endif + /* initialize msghdr for receiving packets */ + rcviov[0].iov_base = (caddr_t) mld6_recv_buf; + rcviov[0].iov_len = RECV_BUF_SIZE; + rcvmh.msg_name = (caddr_t) & from; + rcvmh.msg_namelen = sizeof(from); + rcvmh.msg_iov = rcviov; + rcvmh.msg_iovlen = 1; + rcvmh.msg_control = (caddr_t) rcvcmsgbuf; + rcvmh.msg_controllen = sizeof(rcvcmsgbuf); + + /* initialize msghdr for sending packets */ + sndiov[0].iov_base = (caddr_t)mld6_send_buf; + sndmh.msg_namelen = sizeof(struct sockaddr_in6); + sndmh.msg_iov = sndiov; + sndmh.msg_iovlen = 1; + /* specifiy to insert router alert option in a hop-by-hop opt hdr. */ +#ifndef USE_RFC2292BIS + raopt[0] = IP6OPT_ROUTER_ALERT; + raopt[1] = IP6OPT_RTALERT_LEN - 2; + memcpy(&raopt[2], (caddr_t) & rtalert_code, sizeof(u_short)); +#endif + + /* register MLD message handler */ + if (register_input_handler(mld6_socket, mld6_read) < 0) + log(LOG_ERR, 0, + "Couldn't register mld6_read as an input handler"); +} + +/* Read an MLD message */ +static void +mld6_read(i, rfd) + int i; + fd_set *rfd; +{ + register int mld6_recvlen; + + mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0); + + if (mld6_recvlen < 0) + { + if (errno != EINTR) + log(LOG_ERR, errno, "MLD6 recvmsg"); + return; + } + + /* TODO: make it as a thread in the future releases */ + accept_mld6(mld6_recvlen); +} + +/* + * Process a newly received MLD6 packet that is sitting in the input packet + * buffer. + */ +static void +accept_mld6(recvlen) +int recvlen; +{ + struct in6_addr *group, *dst = NULL; + struct mld6_hdr *mldh; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + int *hlimp = NULL; + int ifindex = 0; + struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name; + + /* + * If control length is zero, it must be an upcall from the kernel + * multicast forwarding engine. + * XXX: can we trust it? + */ + if (rcvmh.msg_controllen == 0) { + /* XXX: msg_controllen must be reset in this case. */ + rcvmh.msg_controllen = sizeof(rcvcmsgbuf); + + process_kernel_call(); + return; + } + + if (recvlen < sizeof(struct mld6_hdr)) + { + log(LOG_WARNING, 0, + "received packet too short (%u bytes) for MLD header", + recvlen); + return; + } + mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base; + group = &mldh->mld6_addr; + + /* extract optional information via Advanced API */ + for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh); + cm; + cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm)) + { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) + { + pi = (struct in6_pktinfo *) (CMSG_DATA(cm)); + ifindex = pi->ipi6_ifindex; + dst = &pi->ipi6_addr; + } + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_HOPLIMIT && + cm->cmsg_len == CMSG_LEN(sizeof(int))) + hlimp = (int *) CMSG_DATA(cm); + } + if (hlimp == NULL) + { + log(LOG_WARNING, 0, + "failed to get receiving hop limit"); + return; + } + + /* TODO: too noisy. Remove it? */ +//#define NOSUCHDEF +#ifdef NOSUCHDEF + IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type, + mldh->mld6_code)) + log(LOG_DEBUG, 0, "RECV %s from %s to %s", + packet_kind(IPPROTO_ICMPV6, + mldh->mld6_type, mldh->mld6_code), + inet6_fmt(&src->sin6_addr), inet6_fmt(dst)); +#endif /* NOSUCHDEF */ + + /* for an mtrace message, we don't need strict checks */ + if (mldh->mld6_type == MLD6_MTRACE) { + accept_mtrace(src, dst, group, ifindex, (char *)(mldh + 1), + mldh->mld6_code, recvlen - sizeof(struct mld6_hdr)); + return; + } + + /* hop limit check */ + if (*hlimp != 1) + { + log(LOG_WARNING, 0, + "received an MLD6 message with illegal hop limit(%d) from %s", + *hlimp, inet6_fmt(&src->sin6_addr)); + /* but accept the packet */ + } + if (ifindex == 0) + { + log(LOG_WARNING, 0, "failed to get receiving interface"); + return; + } + + /* scope check */ + if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr)) + { + log(LOG_INFO, 0, + "RECV %s with an invalid scope: %s from %s", + inet6_fmt(&mldh->mld6_addr), + inet6_fmt(&src->sin6_addr)); + return; /* discard */ + } + + /* source address check */ + if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) + { + log(LOG_INFO, 0, + "RECV %s from a non link local address: %s", + packet_kind(IPPROTO_ICMPV6, mldh->mld6_type, + mldh->mld6_code), + inet6_fmt(&src->sin6_addr)); + return; + } + + switch (mldh->mld6_type) + { + case MLD6_LISTENER_QUERY: + accept_listener_query(src, dst, group, + ntohs(mldh->mld6_maxdelay)); + return; + + case MLD6_LISTENER_REPORT: + accept_listener_report(src, dst, group); + return; + + case MLD6_LISTENER_DONE: + accept_listener_done(src, dst, group); + return; + + default: + /* This must be impossible since we set a type filter */ + log(LOG_INFO, 0, + "ignoring unknown ICMPV6 message type %x from %s to %s", + mldh->mld6_type, inet6_fmt(&src->sin6_addr), + inet6_fmt(dst)); + return; + } +} + +static void +make_mld6_msg(type, code, src, dst, group, ifindex, delay, datalen, alert) + int type, code, ifindex, delay, datalen, alert; + struct sockaddr_in6 *src, *dst; + struct in6_addr *group; +{ + static struct sockaddr_in6 dst_sa = {sizeof(dst_sa), AF_INET6}; + struct mld6_hdr *mhp = (struct mld6_hdr *)mld6_send_buf; + int ctllen, hbhlen = 0; + + switch(type) { + case MLD6_MTRACE: + case MLD6_MTRACE_RESP: + sndmh.msg_name = (caddr_t)dst; + break; + default: + if (IN6_IS_ADDR_UNSPECIFIED(group)) + dst_sa.sin6_addr = allnodes_group.sin6_addr; + else + dst_sa.sin6_addr = *group; + sndmh.msg_name = (caddr_t)&dst_sa; + datalen = sizeof(struct mld6_hdr); + break; + } + + bzero(mhp, sizeof(*mhp)); + mhp->mld6_type = type; + mhp->mld6_code = code; + mhp->mld6_maxdelay = htons(delay); + mhp->mld6_addr = *group; + + sndiov[0].iov_len = datalen; + + /* estimate total ancillary data length */ + ctllen = 0; + if (ifindex != -1 || src) + ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + if (alert) { +#ifdef USE_RFC2292BIS + if ((hbhlen = inet6_opt_init(NULL, 0)) == -1) + log(LOG_ERR, 0, "inet6_opt_init(0) failed"); + if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2, + 2, NULL)) == -1) + log(LOG_ERR, 0, "inet6_opt_append(0) failed"); + if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1) + log(LOG_ERR, 0, "inet6_opt_finish(0) failed"); +#else /* old advanced API */ + hbhlen = inet6_option_space(sizeof(raopt)); +#endif + ctllen += CMSG_SPACE(hbhlen); + } + /* extend ancillary data space (if necessary) */ + if (ctlbuflen < ctllen) { + if (sndcmsgbuf) + free(sndcmsgbuf); + if ((sndcmsgbuf = malloc(ctllen)) == NULL) + log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */ + ctlbuflen = ctllen; + } + /* store ancillary data */ + if ((sndmh.msg_controllen = ctllen) > 0) { + struct cmsghdr *cmsgp; + + sndmh.msg_control = sndcmsgbuf; + cmsgp = CMSG_FIRSTHDR(&sndmh); + + if (ifindex != -1 || src) { + struct in6_pktinfo *pktinfo; + + cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_PKTINFO; + pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp); + memset((caddr_t)pktinfo, 0, sizeof(*pktinfo)); + if (ifindex != -1) + pktinfo->ipi6_ifindex = ifindex; + if (src) + pktinfo->ipi6_addr = src->sin6_addr; + cmsgp = CMSG_NXTHDR(&sndmh, cmsgp); + } + if (alert) { +#ifdef USE_RFC2292BIS + int currentlen; + void *hbhbuf, *optp = NULL; + + cmsgp->cmsg_len = CMSG_SPACE(hbhlen); + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_HOPOPTS; + hbhbuf = CMSG_DATA(cmsgp); + + if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1) + log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed", + hbhlen); + if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, + currentlen, + IP6OPT_ROUTER_ALERT, 2, + 2, &optp)) == -1) + log(LOG_ERR, 0, + "inet6_opt_append(len = %d) failed", + currentlen, hbhlen); + (void)inet6_opt_set_val(optp, 0, &rtalert_code, + sizeof(rtalert_code)); + if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1) + log(LOG_ERR, 0, "inet6_opt_finish(buf) failed"); +#else /* old advanced API */ + if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) + log(LOG_ERR, 0, /* assert */ + "make_mld6_msg: inet6_option_init failed"); + if (inet6_option_append(cmsgp, raopt, 4, 0)) + log(LOG_ERR, 0, /* assert */ + "make_mld6_msg: inet6_option_append failed"); +#endif + cmsgp = CMSG_NXTHDR(&sndmh, cmsgp); + } + } + else + sndmh.msg_control = NULL; /* clear for safety */ +} + +void +send_mld6(type, code, src, dst, group, index, delay, datalen, alert) + int type; + int code; /* for trace packets only */ + struct sockaddr_in6 *src; + struct sockaddr_in6 *dst; /* may be NULL */ + struct in6_addr *group; + int index, delay, alert; + int datalen; /* for trace packets only */ +{ + int setloop = 0; + struct sockaddr_in6 *dstp; + + make_mld6_msg(type, code, src, dst, group, index, delay, datalen, alert); + dstp = (struct sockaddr_in6 *)sndmh.msg_name; + if (IN6_ARE_ADDR_EQUAL(&dstp->sin6_addr, &allnodes_group.sin6_addr)) { + setloop = 1; + k_set_loop(mld6_socket, TRUE); + } + if (sendmsg(mld6_socket, &sndmh, 0) < 0) { + if (errno == ENETDOWN) + check_vif_state(); + else + log(log_level(IPPROTO_ICMPV6, type, 0), errno, + "sendmsg to %s with src %s on %s", + inet6_fmt(&dstp->sin6_addr), + src ? inet6_fmt(&src->sin6_addr) : "(unspec)", + ifindex2str(index)); + + if (setloop) + k_set_loop(mld6_socket, FALSE); + return; + } + + IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, 0)) + log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", + packet_kind(IPPROTO_ICMPV6, type, 0), + src ? inet6_fmt(&src->sin6_addr) : "unspec", + inet6_fmt(&dstp->sin6_addr)); +} diff --git a/usr.sbin/pim6dd/mld6.h b/usr.sbin/pim6dd/mld6.h new file mode 100644 index 0000000..21453a3 --- /dev/null +++ b/usr.sbin/pim6dd/mld6.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Constans for Multicast Listener Discovery protocol for IPv6. + */ +#define MLD6_ROBUSTNESS_VARIABLE 2 +#define MLD6_QUERY_INTERVAL 125 /* in seconds */ +#define MLD6_QUERY_RESPONSE_INTERVAL 10000 /* in milliseconds */ +#ifndef MLD6_TIMER_SCALE +#define MLD6_TIMER_SCALE 1000 +#endif +#define MLD6_LISTENER_INTERVAL (MLD6_ROBUSTNESS_VARIABLE * \ + MLD6_QUERY_INTERVAL + \ + MLD6_QUERY_RESPONSE_INTERVAL / MLD6_TIMER_SCALE) +#define MLD6_LAST_LISTENER_QUERY_INTERVAL 1000 /* in milliseconds */ +#define MLD6_LAST_LISTENER_QUERY_COUNT MLD6_ROBUSTNESS_VARIABLE diff --git a/usr.sbin/pim6dd/mld6_proto.c b/usr.sbin/pim6dd/mld6_proto.c new file mode 100644 index 0000000..97cbea1 --- /dev/null +++ b/usr.sbin/pim6dd/mld6_proto.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: mld6_proto.c,v 1.2 1999/09/12 17:00:09 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +extern struct in6_addr in6addr_any; + +typedef struct { + mifi_t mifi; + struct listaddr *g; + int q_time; +} cbk_t; + + +/* + * Forward declarations. + */ +static void DelVif __P((void *arg)); +static int SetTimer __P((int mifi, struct listaddr *g)); +static int DeleteTimer __P((int id)); +static void SendQuery __P((void *arg)); +static int SetQueryTimer __P((struct listaddr *g, int mifi, int to_expire, + int q_time)); + +/* + * Send group membership queries on that interface if I am querier. + */ +void +query_groups(v) + register struct uvif *v; +{ + register struct listaddr *g; + + v->uv_gq_timer = MLD6_QUERY_INTERVAL; + if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) + send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr, + NULL, (struct in6_addr *)&in6addr_any, + v->uv_ifindex, MLD6_QUERY_RESPONSE_INTERVAL, 0, 1); + + /* + * Decrement the old-hosts-present timer for each + * active group on that vif. + */ + for (g = v->uv_groups; g != NULL; g = g->al_next) + if (g->al_old > TIMER_INTERVAL) + g->al_old -= TIMER_INTERVAL; + else + g->al_old = 0; +} + + +/* + * Process an incoming host membership query + */ +void +accept_listener_query(src, dst, group, tmo) + struct sockaddr_in6 *src; + struct in6_addr *dst, *group; + int tmo; +{ + register int mifi; + register struct uvif *v; + struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; + + /* Ignore my own membership query */ + if (local_address(src) != NO_VIF) + return; + + if ((mifi = find_vif_direct(src)) == NO_VIF) { + IF_DEBUG(DEBUG_MLD) + log(LOG_INFO, 0, + "accept_listener_query: can't find a mif"); + return; + } + + v = &uvifs[mifi]; + + if (v->uv_querier == NULL || inet6_equal(&v->uv_querier->al_addr, src)) + { + /* + * This might be: + * - A query from a new querier, with a lower source address + * than the current querier (who might be me) + * - A query from a new router that just started up and doesn't + * know who the querier is. + * - A query from the current querier + */ + if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr + : &v->uv_linklocal->pa_addr))) { + IF_DEBUG(DEBUG_MLD) + log(LOG_DEBUG, 0, "new querier %s (was %s) " + "on mif %d", + inet6_fmt(&src->sin6_addr), + v->uv_querier ? + inet6_fmt(&v->uv_querier->al_addr.sin6_addr) : + "me", mifi); + if (!v->uv_querier) { + v->uv_querier = (struct listaddr *) + malloc(sizeof(struct listaddr)); + v->uv_querier->al_next = (struct listaddr *)NULL; + v->uv_querier->al_timer = 0; + v->uv_querier->al_genid = 0; + v->uv_querier->al_pv = 0; + v->uv_querier->al_mv = 0; + v->uv_querier->al_old = 0; + v->uv_querier->al_index = 0; + v->uv_querier->al_timerid = 0; + v->uv_querier->al_query = 0; + v->uv_querier->al_flags = 0; + + v->uv_flags &= ~VIFF_QUERIER; + } + v->uv_querier->al_addr = *src; + time(&v->uv_querier->al_ctime); + } + } + + /* + * Reset the timer since we've received a query. + */ + if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr)) + v->uv_querier->al_timer = 0; + + /* + * If this is a Group-Specific query which we did not source, + * we must set our membership timer to [Last Member Query Count] * + * the [Max Response Time] in the packet. + */ + if (!IN6_IS_ADDR_UNSPECIFIED(group) && + inet6_equal(src, &v->uv_linklocal->pa_addr)) { + register struct listaddr *g; + + IF_DEBUG(DEBUG_MLD) + log(LOG_DEBUG, 0, + "%s for %s from %s on mif %d, timer %d", + "Group-specific membership query", + inet6_fmt(group), + inet6_fmt(&src->sin6_addr), mifi, tmo); + + group_sa.sin6_addr = *group; + group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (inet6_equal(&group_sa, &g->al_addr) + && g->al_query == 0) { + /* setup a timeout to remove the group membership */ + if (g->al_timerid) + g->al_timerid = DeleteTimer(g->al_timerid); + g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT * + tmo / MLD6_TIMER_SCALE; + /* + * use al_query to record our presence + * in last-member state + */ + g->al_query = -1; + g->al_timerid = SetTimer(mifi, g); + IF_DEBUG(DEBUG_MLD) + log(LOG_DEBUG, 0, + "timer for grp %s on mif %d " + "set to %d", + inet6_fmt(group), + mifi, g->al_timer); + break; + } + } + } +} + + +/* + * Process an incoming group membership report. + */ +void +accept_listener_report(src, dst, group) + struct sockaddr_in6 *src; + struct in6_addr *dst, *group; +{ + register mifi_t mifi; + register struct uvif *v; + register struct listaddr *g; + struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; + + if (IN6_IS_ADDR_MC_LINKLOCAL(group)) { + IF_DEBUG(DEBUG_MLD) + log(LOG_DEBUG, 0, + "accept_listener_report: group(%s) has the " + "link-local scope. discard", inet6_fmt(group)); + return; + } + + if ((mifi = find_vif_direct_local(src)) == NO_VIF) { + IF_DEBUG(DEBUG_MLD) + log(LOG_INFO, 0, + "accept_listener_report: can't find a mif"); + return; + } + + IF_DEBUG(DEBUG_MLD) + log(LOG_INFO, 0, + "accepting multicast listener report: " + "src %s, dst% s, grp %s", + inet6_fmt(&src->sin6_addr), inet6_fmt(dst), + inet6_fmt(group)); + + v = &uvifs[mifi]; + + /* + * Look for the group in our group list; if found, reset its timer. + */ + group_sa.sin6_addr = *group; + group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (inet6_equal(&group_sa, &g->al_addr)) { + g->al_reporter = *src; + + /* delete old timers, set a timer for expiration */ + g->al_timer = MLD6_LISTENER_INTERVAL; + if (g->al_query) + g->al_query = DeleteTimer(g->al_query); + if (g->al_timerid) + g->al_timerid = DeleteTimer(g->al_timerid); + g->al_timerid = SetTimer(mifi, g); + add_leaf(mifi, NULL, &group_sa); + break; + } + } + + /* + * If not found, add it to the list and update kernel cache. + */ + if (g == NULL) { + g = (struct listaddr *)malloc(sizeof(struct listaddr)); + if (g == NULL) + log(LOG_ERR, 0, "ran out of memory"); /* fatal */ + + g->al_addr = group_sa; + g->al_old = 0; + + /** set a timer for expiration **/ + g->al_query = 0; + g->al_timer = MLD6_LISTENER_INTERVAL; + g->al_reporter = *src; + g->al_timerid = SetTimer(mifi, g); + g->al_next = v->uv_groups; + v->uv_groups = g; + time(&g->al_ctime); + + add_leaf(mifi, NULL, &group_sa); + } +} + + +/* TODO: send PIM prune message if the last member? */ +void +accept_listener_done(src, dst, group) + struct sockaddr_in6 *src; + struct in6_addr *dst, *group; +{ + register mifi_t mifi; + register struct uvif *v; + register struct listaddr *g; + struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6}; + + if ((mifi = find_vif_direct_local(src)) == NO_VIF) { + IF_DEBUG(DEBUG_MLD) + log(LOG_INFO, 0, + "accept_listener_done: can't find a mif"); + return; + } + + IF_DEBUG(DEBUG_MLD) + log(LOG_INFO, 0, + "accepting listener done message: src %s, dst% s, grp %s", + inet6_fmt(&src->sin6_addr), + inet6_fmt(dst), inet6_fmt(group)); + + v = &uvifs[mifi]; + + if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))) + return; + + /* + * Look for the group in our group list in order to set up a + * short-timeout query. + */ + group_sa.sin6_addr = *group; + group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v); + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (inet6_equal(&group_sa, &g->al_addr)) { + IF_DEBUG(DEBUG_MLD) + log(LOG_DEBUG, 0, + "[accept_done_message] %d %d \n", + g->al_old, g->al_query); + + /* + * Ignore the done message if there are old + * hosts present + */ + if (g->al_old) + return; + + /* + * still waiting for a reply to a query, + * ignore the done + */ + if (g->al_query) + return; + + /** delete old timer set a timer for expiration **/ + if (g->al_timerid) + g->al_timerid = DeleteTimer(g->al_timerid); + + /** send a group specific querry **/ + g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE) * + (MLD6_LAST_LISTENER_QUERY_COUNT + 1); + if (v->uv_flags & VIFF_QUERIER && + (v->uv_flags & VIFF_NOLISTENER) == 0) + send_mld6(MLD6_LISTENER_QUERY, 0, + &v->uv_linklocal->pa_addr, NULL, + &g->al_addr.sin6_addr, + v->uv_ifindex, + MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1); + g->al_query = SetQueryTimer(g, mifi, + MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE, + MLD6_LAST_LISTENER_QUERY_INTERVAL); + g->al_timerid = SetTimer(mifi, g); + break; + } + } +} + + +/* + * Time out record of a group membership on a vif + */ +static void +DelVif(arg) + void *arg; +{ + cbk_t *cbk = (cbk_t *)arg; + mifi_t mifi = cbk->mifi; + struct uvif *v = &uvifs[mifi]; + struct listaddr *a, **anp, *g = cbk->g; + + /* + * Group has expired + * delete all kernel cache entries with this group + */ + if (g->al_query) + DeleteTimer(g->al_query); + + delete_leaf(mifi, NULL, &g->al_addr); + + anp = &(v->uv_groups); + while ((a = *anp) != NULL) { + if (a == g) { + *anp = a->al_next; + free((char *)a); + } else { + anp = &a->al_next; + } + } + + free(cbk); +} + + +/* + * Set a timer to delete the record of a group membership on a vif. + */ +static int +SetTimer(mifi, g) + mifi_t mifi; + struct listaddr *g; +{ + cbk_t *cbk; + + cbk = (cbk_t *) malloc(sizeof(cbk_t)); + cbk->mifi = mifi; + cbk->g = g; + return timer_setTimer(g->al_timer, DelVif, cbk); +} + + +/* + * Delete a timer that was set above. + */ +static int +DeleteTimer(id) + int id; +{ + timer_clearTimer(id); + return 0; +} + + +/* + * Send a group-specific query. + */ +static void +SendQuery(arg) + void *arg; +{ + cbk_t *cbk = (cbk_t *)arg; + register struct uvif *v = &uvifs[cbk->mifi]; + + if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) + send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr, + NULL, &cbk->g->al_addr.sin6_addr, + v->uv_ifindex, cbk->q_time, 0, 1); + cbk->g->al_query = 0; + free(cbk); +} + + +/* + * Set a timer to send a group-specific query. + */ +static int +SetQueryTimer(g, mifi, to_expire, q_time) + struct listaddr *g; + mifi_t mifi; + int to_expire; + int q_time; +{ + cbk_t *cbk; + + cbk = (cbk_t *) malloc(sizeof(cbk_t)); + cbk->g = g; + cbk->q_time = q_time; + cbk->mifi = mifi; + return timer_setTimer(to_expire, SendQuery, cbk); +} + +/* Checks for MLD listener: returns TRUE if there is a receiver for the + * group on the given uvif, or returns FALSE otherwise. + */ +int +check_multicast_listener(v, group) + struct uvif *v; + struct sockaddr_in6 *group; +{ + register struct listaddr *g; + + /* + * Look for the group in our listener list; + */ + for (g = v->uv_groups; g != NULL; g = g->al_next) { + if (inet6_equal(group, &g->al_addr)) + return TRUE; + } + return FALSE; +} diff --git a/usr.sbin/pim6dd/mrt.c b/usr.sbin/pim6dd/mrt.c new file mode 100644 index 0000000..13d5149 --- /dev/null +++ b/usr.sbin/pim6dd/mrt.c @@ -0,0 +1,803 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: mrt.c,v 1.2 1999/08/24 10:04:56 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +srcentry_t *srclist; +grpentry_t *grplist; + +/* + * Local functions definition + */ +static srcentry_t *create_srcentry __P((struct sockaddr_in6 *source)); +static int search_srclist __P((struct sockaddr_in6 *source, + srcentry_t **sourceEntry)); +static int search_srcmrtlink __P((srcentry_t *srcentry_ptr, + struct sockaddr_in6 *group, + mrtentry_t **mrtPtr)); +static void insert_srcmrtlink __P((mrtentry_t *elementPtr, + mrtentry_t *insertPtr, + srcentry_t *srcListPtr)); +static grpentry_t *create_grpentry __P((struct sockaddr_in6 *group)); +static int search_grplist __P((struct sockaddr_in6 *group, + grpentry_t **groupEntry)); +static int search_grpmrtlink __P((grpentry_t *grpentry_ptr, + struct sockaddr_in6 *source, + mrtentry_t **mrtPtr)); +static void insert_grpmrtlink __P((mrtentry_t *elementPtr, + mrtentry_t *insertPtr, + grpentry_t *grpListPtr)); +static mrtentry_t *alloc_mrtentry __P((srcentry_t *srcentry_ptr, + grpentry_t *grpentry_ptr)); +static mrtentry_t *create_mrtentry __P((srcentry_t *srcentry_ptr, + grpentry_t *grpentry_ptr, + u_int16 flags)); + + +void +init_pim6_mrt() +{ + + /* TODO: delete any existing routing table */ + + /* Initialize the source list */ + /* The first entry has the unspecified address and is not used */ + /* The order is the smallest address first. */ + srclist = (srcentry_t *)malloc(sizeof(srcentry_t)); + srclist->next = (srcentry_t *)NULL; + srclist->prev = (srcentry_t *)NULL; + memset(&srclist->address, 0, sizeof(struct sockaddr_in6)); + srclist->address.sin6_len = sizeof(struct sockaddr_in6); + srclist->address.sin6_family = AF_INET6; + srclist->mrtlink = (mrtentry_t *)NULL; + srclist->incoming = NO_VIF; + srclist->upstream = (pim_nbr_entry_t *)NULL; + srclist->metric = 0; + srclist->preference = 0; + srclist->timer = 0; + + /* Initialize the group list */ + /* The first entry has the unspecified address and is not used */ + /* The order is the smallest address first. */ + grplist = (grpentry_t *)malloc(sizeof(grpentry_t)); + grplist->next = (grpentry_t *)NULL; + grplist->prev = (grpentry_t *)NULL; + memset(&grplist->group, 0, sizeof(struct sockaddr_in6)); + grplist->group.sin6_len = sizeof(struct sockaddr_in6); + grplist->group.sin6_family = AF_INET6; + grplist->mrtlink = (mrtentry_t *)NULL; +} + + +grpentry_t* +find_group(group) + struct sockaddr_in6 *group; +{ + grpentry_t *grpentry_ptr; + + if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr)) + return (grpentry_t *)NULL; + + if (search_grplist(group, &grpentry_ptr) == TRUE) { + /* Group found! */ + return (grpentry_ptr); + } + return (grpentry_t *)NULL; +} + + +srcentry_t * +find_source(source) + struct sockaddr_in6 *source; +{ + srcentry_t *srcentry_ptr; + + if (!inet6_valid_host(source)) + return (srcentry_t *)NULL; + + if (search_srclist(source, &srcentry_ptr) == TRUE) { + /* Source found! */ + return (srcentry_ptr); + } + return (srcentry_t *)NULL; +} + + +mrtentry_t * +find_route(source, group, flags, create) + struct sockaddr_in6 *source, *group; + u_int16 flags; + char create; +{ + srcentry_t *srcentry_ptr; + grpentry_t *grpentry_ptr; + mrtentry_t *mrtentry_ptr; + + if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr)) + return (mrtentry_t *)NULL; + + if (!inet6_valid_host(source)) + return (mrtentry_t *)NULL; + + if (create == DONT_CREATE) { + if (search_grplist(group, &grpentry_ptr) == FALSE) + return (mrtentry_t *)NULL; + /* Search for the source */ + if (search_grpmrtlink(grpentry_ptr, source, + &mrtentry_ptr) == TRUE) { + /* Exact (S,G) entry found */ + return (mrtentry_ptr); + } + return (mrtentry_t *)NULL; + } + + + /* Creation allowed */ + + grpentry_ptr = create_grpentry(group); + if (grpentry_ptr == (grpentry_t *)NULL) { + return (mrtentry_t *)NULL; + } + + /* Setup the (S,G) routing entry */ + srcentry_ptr = create_srcentry(source); + if (srcentry_ptr == (srcentry_t *)NULL) { + if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) { + /* New created grpentry. Delete it. */ + delete_grpentry(grpentry_ptr); + } + return (mrtentry_t *)NULL; + } + + mrtentry_ptr = create_mrtentry(srcentry_ptr, grpentry_ptr, MRTF_SG); + if (mrtentry_ptr == (mrtentry_t *)NULL) { + if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) { + /* New created grpentry. Delete it. */ + delete_grpentry(grpentry_ptr); + } + if (srcentry_ptr->mrtlink == (mrtentry_t *)NULL) { + /* New created srcentry. Delete it. */ + delete_srcentry(srcentry_ptr); + } + return (mrtentry_t *)NULL; + } + + if (mrtentry_ptr->flags & MRTF_NEW) { + struct mrtfilter *f; + /* The mrtentry pref/metric should be the pref/metric of the + * _upstream_ assert winner. Since this isn't known now, + * set it to the config'ed default + */ + mrtentry_ptr->incoming = srcentry_ptr->incoming; + mrtentry_ptr->upstream = srcentry_ptr->upstream; + mrtentry_ptr->metric = srcentry_ptr->metric; + mrtentry_ptr->preference = srcentry_ptr->preference; + + if ((f = search_filter(&group->sin6_addr))) + IF_COPY(&f->ifset, &mrtentry_ptr->filter_oifs); + } + + return (mrtentry_ptr); +} + + +void +delete_srcentry(srcentry_ptr) + srcentry_t *srcentry_ptr; +{ + mrtentry_t *mrtentry_ptr; + mrtentry_t *mrtentry_next; + + if (srcentry_ptr == (srcentry_t *)NULL) + return; + /* TODO: XXX: the first entry is unused and always there */ + srcentry_ptr->prev->next = srcentry_ptr->next; + if (srcentry_ptr->next != (srcentry_t *)NULL) + srcentry_ptr->next->prev = srcentry_ptr->prev; + + for (mrtentry_ptr = srcentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + mrtentry_ptr = mrtentry_next) { + mrtentry_next = mrtentry_ptr->srcnext; + if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL) + mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext; + else { + mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext; + if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) { + /* Delete the group entry */ + delete_grpentry(mrtentry_ptr->group); + } + } + if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL) + mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev; + FREE_MRTENTRY(mrtentry_ptr); + } + free((char *)srcentry_ptr); +} + + +void +delete_grpentry(grpentry_ptr) + grpentry_t *grpentry_ptr; +{ + mrtentry_t *mrtentry_ptr; + mrtentry_t *mrtentry_next; + + if (grpentry_ptr == (grpentry_t *)NULL) + return; + /* TODO: XXX: the first entry is unused and always there */ + grpentry_ptr->prev->next = grpentry_ptr->next; + if (grpentry_ptr->next != (grpentry_t *)NULL) + grpentry_ptr->next->prev = grpentry_ptr->prev; + + for (mrtentry_ptr = grpentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + mrtentry_ptr = mrtentry_next) { + mrtentry_next = mrtentry_ptr->grpnext; + if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL) + mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext; + else { + mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext; + if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) { + /* Delete the srcentry if this was the last routing entry */ + delete_srcentry(mrtentry_ptr->source); + } + } + if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL) + mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev; + FREE_MRTENTRY(mrtentry_ptr); + } + free((char *)grpentry_ptr); +} + + +void +delete_mrtentry(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + if (mrtentry_ptr == (mrtentry_t *)NULL) + return; + + /* Delete the kernel cache first */ + k_del_mfc(mld6_socket, &mrtentry_ptr->source->address, + &mrtentry_ptr->group->group); + +#ifdef RSRR + /* Tell the reservation daemon */ + rsrr_cache_clean(mrtentry_ptr); +#endif /* RSRR */ + + /* (S,G) mrtentry */ + /* Delete from the grpentry MRT chain */ + if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL) + mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext; + else { + mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext; + if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) { + /* Delete the group entry */ + delete_grpentry(mrtentry_ptr->group); + } + } + + if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL) + mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev; + + /* Delete from the srcentry MRT chain */ + if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL) + mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext; + else { + mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext; + if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) { + /* Delete the srcentry if this was the last routing entry */ + delete_srcentry(mrtentry_ptr->source); + } + } + if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL) + mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev; + + FREE_MRTENTRY(mrtentry_ptr); +} + + +static int +search_srclist(source, sourceEntry) + struct sockaddr_in6 *source; + register srcentry_t **sourceEntry; +{ + register srcentry_t *s_prev,*s; + + for (s_prev = srclist, s = s_prev->next; s != (srcentry_t *)NULL; + s_prev = s, s = s->next) { + /* The srclist is ordered with the smallest addresses first. + * The first entry is not used. + */ + if (inet6_lessthan(&s->address, source)) + continue; + if (inet6_equal(&s->address, source)) { + *sourceEntry = s; + return(TRUE); + } + break; + } + *sourceEntry = s_prev; /* The insertion point is between s_prev and s */ + return(FALSE); +} + + +static int +search_grplist(group, groupEntry) + struct sockaddr_in6 *group; + register grpentry_t **groupEntry; +{ + register grpentry_t *g_prev, *g; + + for (g_prev = grplist, g = g_prev->next; g != (grpentry_t *)NULL; + g_prev = g, g = g->next) { + /* The grplist is ordered with the smallest address first. + * The first entry is not used. + */ + if (inet6_lessthan(&g->group, group)) + continue; + if (inet6_equal(&g->group, group)) { + *groupEntry = g; + return(TRUE); + } + break; + } + *groupEntry = g_prev; /* The insertion point is between g_prev and g */ + return(FALSE); +} + +static srcentry_t * +create_srcentry(source) + struct sockaddr_in6 *source; +{ + register srcentry_t *srcentry_ptr; + srcentry_t *srcentry_prev; + + if (search_srclist(source, &srcentry_prev) == TRUE) + return (srcentry_prev); + + srcentry_ptr = (srcentry_t *)malloc(sizeof(srcentry_t)); + if (srcentry_ptr == (srcentry_t *)NULL) { + log(LOG_WARNING, 0, "Memory allocation error for srcentry %s", + inet6_fmt(&source->sin6_addr)); + return (srcentry_t *)NULL; + } + + srcentry_ptr->address = *source; + /* + * Free the memory if there is error getting the iif and + * the next hop (upstream) router. + */ + if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) { + free((char *)srcentry_ptr); + return (srcentry_t *)NULL; + } + srcentry_ptr->mrtlink = (mrtentry_t *)NULL; + srcentry_ptr->timer = 0; + srcentry_ptr->next = srcentry_prev->next; + srcentry_prev->next = srcentry_ptr; + srcentry_ptr->prev = srcentry_prev; + if (srcentry_ptr->next != (srcentry_t *)NULL) + srcentry_ptr->next->prev = srcentry_ptr; + + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, "create source entry, source %s", + inet6_fmt(&source->sin6_addr)); + return (srcentry_ptr); +} + + +static grpentry_t * +create_grpentry(group) + struct sockaddr_in6 *group; +{ + register grpentry_t *grpentry_ptr; + grpentry_t *grpentry_prev; + + if (search_grplist(group, &grpentry_prev) == TRUE) + return (grpentry_prev); + + grpentry_ptr = (grpentry_t *)malloc(sizeof(grpentry_t)); + if (grpentry_ptr == (grpentry_t *)NULL) { + log(LOG_WARNING, 0, "Memory allocation error for grpentry %s", + inet6_fmt(&group->sin6_addr)); + return (grpentry_t *)NULL; + } + + grpentry_ptr->group = *group; + grpentry_ptr->mrtlink = (mrtentry_t *)NULL; + + /* Now it is safe to include the new group entry */ + grpentry_ptr->next = grpentry_prev->next; + grpentry_prev->next = grpentry_ptr; + grpentry_ptr->prev = grpentry_prev; + if (grpentry_ptr->next != (grpentry_t *)NULL) + grpentry_ptr->next->prev = grpentry_ptr; + + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, "create group entry, group %s", + inet6_fmt(&group->sin6_addr)); + return(grpentry_ptr); +} + + +/* + * Return TRUE if the entry is found and then *mrtPtr is set to point to that + * entry. Otherwise return FALSE and *mrtPtr points the the previous entry + * (or NULL if first in the chain. + */ +static int +search_srcmrtlink(srcentry_ptr, group, mrtPtr) + srcentry_t *srcentry_ptr; + struct sockaddr_in6 *group; + mrtentry_t **mrtPtr; +{ + register mrtentry_t *mrtentry_ptr; + register mrtentry_t *m_prev = (mrtentry_t *)NULL; + + for(mrtentry_ptr = srcentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->srcnext) { + /* The entries are ordered with the smaller group address first. + * The addresses are in network order. + */ + if (inet6_lessthan(&mrtentry_ptr->group->group, group)) + continue; + if (inet6_equal(&mrtentry_ptr->group->group, group)) { + *mrtPtr = mrtentry_ptr; + return(TRUE); + } + break; + } + *mrtPtr = m_prev; + return(FALSE); +} + + +/* + * Return TRUE if the entry is found and then *mrtPtr is set to point to that + * entry. Otherwise return FALSE and *mrtPtr points the the previous entry + * (or NULL if first in the chain. + */ +static int +search_grpmrtlink(grpentry_ptr, source, mrtPtr) + grpentry_t *grpentry_ptr; + struct sockaddr_in6 *source; + mrtentry_t **mrtPtr; +{ + register mrtentry_t *mrtentry_ptr; + register mrtentry_t *m_prev = (mrtentry_t *)NULL; + + for (mrtentry_ptr = grpentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->grpnext) { + /* The entries are ordered with the smaller source address first. + * The addresses are in network order. + */ + if (inet6_lessthan(&mrtentry_ptr->source->address, source)) + continue; + if (inet6_equal(source, &mrtentry_ptr->source->address)) { + *mrtPtr = mrtentry_ptr; + return(TRUE); + } + break; + } + *mrtPtr = m_prev; + return(FALSE); +} + + +static void +insert_srcmrtlink(mrtentry_new, mrtentry_prev, srcentry_ptr) + mrtentry_t *mrtentry_new; + mrtentry_t *mrtentry_prev; + srcentry_t *srcentry_ptr; +{ + if (mrtentry_prev == (mrtentry_t *)NULL) { + /* Has to be insert as the head entry for this source */ + mrtentry_new->srcnext = srcentry_ptr->mrtlink; + mrtentry_new->srcprev = (mrtentry_t *)NULL; + srcentry_ptr->mrtlink = mrtentry_new; + } + else { + /* Insert right after the mrtentry_prev */ + mrtentry_new->srcnext = mrtentry_prev->srcnext; + mrtentry_new->srcprev = mrtentry_prev; + mrtentry_prev->srcnext = mrtentry_new; + } + if (mrtentry_new->srcnext != (mrtentry_t *)NULL) + mrtentry_new->srcnext->srcprev = mrtentry_new; +} + + +static void +insert_grpmrtlink(mrtentry_new, mrtentry_prev, grpentry_ptr) + mrtentry_t *mrtentry_new; + mrtentry_t *mrtentry_prev; + grpentry_t *grpentry_ptr; +{ + if (mrtentry_prev == (mrtentry_t *)NULL) { + /* Has to be insert as the head entry for this group */ + mrtentry_new->grpnext = grpentry_ptr->mrtlink; + mrtentry_new->grpprev = (mrtentry_t *)NULL; + grpentry_ptr->mrtlink = mrtentry_new; + } + else { + /* Insert right after the mrtentry_prev */ + mrtentry_new->grpnext = mrtentry_prev->grpnext; + mrtentry_new->grpprev = mrtentry_prev; + mrtentry_prev->grpnext = mrtentry_new; + } + if (mrtentry_new->grpnext != (mrtentry_t *)NULL) + mrtentry_new->grpnext->grpprev = mrtentry_new; +} + + +static mrtentry_t * +alloc_mrtentry(srcentry_ptr, grpentry_ptr) + srcentry_t *srcentry_ptr; + grpentry_t *grpentry_ptr; +{ + register mrtentry_t *mrtentry_ptr; + u_int16 i, *i_ptr; + u_long *il_ptr; + u_int8 vif_numbers; + + mrtentry_ptr = (mrtentry_t *)malloc(sizeof(mrtentry_t)); + if (mrtentry_ptr == (mrtentry_t *)NULL) { + log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory"); + return (mrtentry_t *)NULL; + } + + /* + * grpnext, grpprev, srcnext, srcprev will be setup when we link the + * mrtentry to the source and group chains + */ + mrtentry_ptr->source = srcentry_ptr; + mrtentry_ptr->group = grpentry_ptr; + mrtentry_ptr->incoming = NO_VIF; + IF_ZERO(&mrtentry_ptr->leaves); + IF_ZERO(&mrtentry_ptr->pruned_oifs); + IF_ZERO(&mrtentry_ptr->oifs); + IF_ZERO(&mrtentry_ptr->filter_oifs); + IF_ZERO(&mrtentry_ptr->asserted_oifs); + mrtentry_ptr->upstream = (pim_nbr_entry_t *)NULL; + mrtentry_ptr->metric = 0; + mrtentry_ptr->preference = 0; +#ifdef RSRR + mrtentry_ptr->rsrr_cache = (struct rsrr_cache *)NULL; +#endif /* RSRR */ + +/* + * XXX: TODO: if we are short in memory, we can reserve as few as possible + * space for vif timers (per group and/or routing entry), but then everytime + * when a new interfaces is configured, the router will be restarted and + * will delete the whole routing table. The "memory is cheap" solution is + * to reserve timer space for all potential vifs in advance and then no + * need to delete the routing table and disturb the forwarding. + */ +#ifdef SAVE_MEMORY + mrtentry_ptr->prune_timers = (u_int16 *)malloc(sizeof(u_int16) * numvifs); + mrtentry_ptr->prune_delay_timerids = + (u_long *)malloc(sizeof(u_long) * numvifs); + mrtentry_ptr->last_assert = (u_long *)malloc(sizeof(u_long) * numvifs); + mrtentry_ptr->last_prune = (u_long *)malloc(sizeof(u_long) * numvifs); + vif_numbers = numvifs; +#else + mrtentry_ptr->prune_timers = + (u_int16 *)malloc(sizeof(u_int16) * total_interfaces); + mrtentry_ptr->prune_delay_timerids = + (u_long *)malloc(sizeof(u_long) * total_interfaces); + mrtentry_ptr->last_assert = + (u_long *)malloc(sizeof(u_long) * total_interfaces); + mrtentry_ptr->last_prune = + (u_long *)malloc(sizeof(u_long) * total_interfaces); + vif_numbers = total_interfaces; +#endif /* SAVE_MEMORY */ + if ((mrtentry_ptr->prune_timers == (u_int16 *)NULL) || + (mrtentry_ptr->prune_delay_timerids == (u_long *)NULL) || + (mrtentry_ptr->last_assert == (u_long *)NULL) || + (mrtentry_ptr->last_prune == (u_long *)NULL)) { + log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory"); + FREE_MRTENTRY(mrtentry_ptr); + return (mrtentry_t *)NULL; + } + /* Reset the timers */ + for (i = 0, i_ptr = mrtentry_ptr->prune_timers; i < vif_numbers; + i++, i_ptr++) + *i_ptr = 0; + for (i = 0, il_ptr = mrtentry_ptr->prune_delay_timerids; i < vif_numbers; + i++, il_ptr++) + *il_ptr = 0; + for (i = 0, il_ptr = mrtentry_ptr->last_assert; i < vif_numbers; + i++, il_ptr++) + *il_ptr = 0; + for (i = 0, il_ptr = mrtentry_ptr->last_prune; i < vif_numbers; + i++, il_ptr++) + *il_ptr = 0; + + mrtentry_ptr->flags = MRTF_NEW; + mrtentry_ptr->timer = 0; + mrtentry_ptr->join_delay_timerid = 0; + mrtentry_ptr->assert_timer = 0; + mrtentry_ptr->graft = (pim_graft_entry_t *)NULL; + + return(mrtentry_ptr); +} + + +static mrtentry_t * +create_mrtentry(srcentry_ptr, grpentry_ptr, flags) + srcentry_t *srcentry_ptr; + grpentry_t *grpentry_ptr; + u_int16 flags; +{ + mrtentry_t *r_new; + mrtentry_t *r_grp_insert, *r_src_insert; /* pointers to insert */ + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + + /* (S,G) entry */ + source = &srcentry_ptr->address; + group = &grpentry_ptr->group; + + if (search_grpmrtlink(grpentry_ptr, source, &r_grp_insert) == TRUE) { + return(r_grp_insert); + } + if (search_srcmrtlink(srcentry_ptr, group, &r_src_insert) == TRUE) { + /* + * Hmmm, search_grpmrtlink() didn't find the entry, but + * search_srcmrtlink() did find it! Shoudn't happen. Panic! + */ + log(LOG_ERR, 0, "MRT inconsistency for src %s and grp %s\n", + inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr)); + /* not reached but to make lint happy */ + return (mrtentry_t *)NULL; + } + /* + * Create and insert in group mrtlink and source mrtlink chains. + */ + r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr); + if (r_new == (mrtentry_t *)NULL) + return (mrtentry_t *)NULL; + /* + * r_new has to be insert right after r_grp_insert in the + * grp mrtlink chain and right after r_src_insert in the + * src mrtlink chain + */ + insert_grpmrtlink(r_new, r_grp_insert, grpentry_ptr); + insert_srcmrtlink(r_new, r_src_insert, srcentry_ptr); + r_new->flags |= MRTF_SG; + return (r_new); +} + +/* ======================== */ +/* filter related functions */ +struct mrtfilter *filterlist; + +/* + * Search for a filter entry in the filter list. + */ +struct mrtfilter * +search_filter(maddr) + struct in6_addr *maddr; +{ + struct mrtfilter *f; + struct sockaddr_in6 msa6; + + for (f = filterlist; f; f = f->next) { + switch(f->type) { + case FILTER_RANGE: + msa6.sin6_scope_id = 0; /* XXX: scope consideration */ + msa6.sin6_addr = *maddr; + if (inet6_greateroreq(&msa6, &f->mrtf_from) && + inet6_lessoreq(&msa6, &f->mrtf_to)) + return(f); + break; + case FILTER_PREFIX: + msa6.sin6_scope_id = 0; /* XXX: scope consideration */ + if (inet6_match_prefix(&msa6, &f->mrtf_prefix, + &f->mrtf_mask)) + return(f); + break; + } + } + + return(NULL); +} + +/* + * Make a new filter entry. + * This function assumes + */ +struct mrtfilter * +add_filter(type, maddr1, maddr2, plen) + struct in6_addr *maddr1, *maddr2; + int type, plen; +{ + struct mrtfilter *f; + struct sockaddr_in6 from, to; + + if ((f = malloc(sizeof(*f))) == NULL) + log(LOG_ERR, 0, "add_filter: malloc failed"); /* assert */ + memset((void *)f, 0, sizeof(*f)); + + f->type = type; + switch(type) { + case FILTER_RANGE: + memset((void *)&from, 0, sizeof(from)); + memset((void *)&to, 0, sizeof(to)); + from.sin6_addr = *maddr1; + to.sin6_addr = *maddr2; + if (inet6_lessthan(&from, &to)) { + f->mrtf_from = from; + f->mrtf_to = to; + } + else { + f->mrtf_from = to; + f->mrtf_to = from; + } + break; + case FILTER_PREFIX: + f->mrtf_prefix.sin6_addr = *maddr1; + MASKLEN_TO_MASK6(plen, f->mrtf_mask); + break; + } + f->next = filterlist; + filterlist = f; + + return(f); +} diff --git a/usr.sbin/pim6dd/mrt.h b/usr.sbin/pim6dd/mrt.h new file mode 100644 index 0000000..156476d --- /dev/null +++ b/usr.sbin/pim6dd/mrt.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: mrt.h,v 1.2 1999/08/24 10:04:56 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#define MRTF_SPT 0x0001 /* iif toward source */ +#define MRTF_WC 0x0002 /* (*,G) entry */ +#define MRTF_RP 0x0004 /* iif toward RP */ +#define MRTF_NEW 0x0008 /* new created routing entry */ +#define MRTF_IIF_REGISTER 0x0020 /* ??? */ +#define MRTF_REGISTER 0x0080 /* ??? */ +#define MRTF_KERNEL_CACHE 0x0200 /* a mirror for the kernel cache */ +#define MRTF_NULL_OIF 0x0400 /* null oif cache.. ??? */ +#define MRTF_REG_SUPP 0x0800 /* register suppress ??? */ +#define MRTF_ASSERTED 0x1000 /* upstream is not that of src ??? */ +#define MRTF_SG 0x2000 /* (S,G) pure, not hanging off of (*,G)*/ +#define MRTF_PMBR 0x4000 /* (*,*,RP) entry (for interop) */ + +/* Macro to duplicate oif info (oif bits, timers): XXX: unused */ +#define VOIF_COPY(from, to) \ + do { \ + VIFM_COPY((from)->joined_oifs, (to)->joined_oifs); \ + VIFM_COPY((from)->oifs, (to)->oifs); \ + VIFM_COPY((from)->leaves, (to)->leaves); \ + VIFM_COPY((from)->pruned_oifs, (to)->pruned_oifs); \ + bcopy((from)->prune_timers, (to)->prune_timers, \ + numvifs*sizeof((from)->prune_timers[0])); \ + bcopy((from)->prune_delay_timerids, \ + (to)->prune_delay_timerids, \ + numvifs*sizeof((from)->prune_delay_timerids[0])); \ + (to)->join_delay_timerid = (from)->join_delay_timerid; \ + } while (0) + +#ifdef SAVE_MEMORY +#define FREE_MRTENTRY(mrtentry_ptr) \ + do { \ + u_int16 i; \ + u_long *il_ptr; \ + free((char *)((mrtentry_ptr)->prune_timers)); \ + for(i=0, il_ptr=(mrtentry_ptr)->prune_delay_timerids; \ + i<numvifs; i++, il_ptr++) \ + timer_clearTimer(*il_ptr); \ + free((char *)((mrtentry_ptr)->prune_delay_timerids)); \ + timer_clearTimer((mrtentry_ptr)->join_delay_timerid); \ + delete_pim6_graft_entry((mrtentry_ptr)); \ + free((char *)(mrtentry_ptr)); \ + } while (0) +#else +#define FREE_MRTENTRY(mrtentry_ptr) \ + do { \ + u_int16 i; \ + u_long *il_ptr; \ + free((char *)((mrtentry_ptr)->prune_timers)); \ + for(i=0, il_ptr=(mrtentry_ptr)->prune_delay_timerids; \ + i<total_interfaces; i++, il_ptr++) \ + timer_clearTimer(*il_ptr); \ + free((char *)((mrtentry_ptr)->prune_delay_timerids)); \ + free((char *)((mrtentry_ptr)->last_assert)); \ + free((char *)((mrtentry_ptr)->last_prune)); \ + timer_clearTimer((mrtentry_ptr)->join_delay_timerid); \ + delete_pim6_graft_entry((mrtentry_ptr)); \ + free((char *)(mrtentry_ptr)); \ + } while (0) +#endif /* SAVE_MEMORY */ + +typedef struct pim_nbr_entry { + struct pim_nbr_entry *next; /* link to next neighbor */ + struct pim_nbr_entry *prev; /* link to prev neighbor */ + struct sockaddr_in6 address; /* neighbor address */ + vifi_t vifi; /* which interface */ + u_int16 timer; /* for timing out neighbor */ +} pim_nbr_entry_t; + + +/* + * Used to get forwarded data related counts (number of packet, number of + * bits, etc) + */ +struct sg_count { + u_long pktcnt; /* Number of packets for (s,g) */ + u_long bytecnt; /* Number of bytes for (s,g) */ + u_long wrong_if; /* Number of packets received on wrong iif for (s,g) */ +}; + +typedef struct mrtentry mrtentry_t; +typedef struct pim_graft_entry { + struct pim_graft_entry *next; + struct pim_graft_entry *prev; + mrtentry_t *mrtlink; +} pim_graft_entry_t; + + +typedef struct srcentry { + struct srcentry *next; /* link to next entry */ + struct srcentry *prev; /* link to prev entry */ + struct sockaddr_in6 address; /* source or RP address */ + struct mrtentry *mrtlink; /* link to routing entries */ + vifi_t incoming; /* incoming vif */ + struct pim_nbr_entry *upstream; /* upstream router */ + u_int32 metric; /* Unicast Routing Metric to the source */ + u_int32 preference; /* The metric preference (for assers)*/ + u_int16 timer; /* Entry timer??? Delete? */ +} srcentry_t; + + +typedef struct grpentry { + struct grpentry *next; /* link to next entry */ + struct grpentry *prev; /* link to prev entry */ + struct sockaddr_in6 group; /* subnet group of multicasts */ + struct mrtentry *mrtlink; /* link to (S,G) routing entries */ +} grpentry_t; + +struct mrtentry { + struct mrtentry *grpnext; /* next entry of same group */ + struct mrtentry *grpprev; /* prev entry of same group */ + struct mrtentry *srcnext; /* next entry of same source */ + struct mrtentry *srcprev; /* prev entry of same source */ + struct grpentry *group; /* pointer to group entry */ + struct srcentry *source; /* pointer to source entry (or RP) */ + vifi_t incoming; /* the iif (either toward S or RP) */ + + if_set oifs; /* The current result oifs */ + if_set pruned_oifs; /* The pruned oifs (Prune received) */ + if_set asserted_oifs; /* The asserted oifs (Lost Assert) */ + if_set filter_oifs; /* The filtered oifs */ + if_set leaves; /* Has directly connected members */ + struct pim_nbr_entry *upstream; /* upstream router, needed because + * of the asserts it may be different + * than the source (or RP) upstream + * router. + */ + u_int32 metric; /* Metric for the upstream */ + u_int32 preference; /* preference for the upstream */ + u_int16 *prune_timers; /* prune timer list */ + u_long *prune_delay_timerids; /* timers for LAN prunes */ + u_long join_delay_timerid; /* timer for delay joins */ + u_int16 flags; /* The MRTF_* flags */ + u_int16 timer; /* entry timer */ + u_int assert_timer; + struct sg_count sg_count; + u_long *last_assert; /* time for last data-driven assert */ + u_long *last_prune; /* time for last data-driven prune */ + pim_graft_entry_t *graft; /* Pointer into graft entry list */ +#ifdef RSRR + struct rsrr_cache *rsrr_cache; /* Used to save RSRR requests for + * routes change notification. + */ +#endif /* RSRR */ +}; + + +struct vif_count { + u_long icount; /* Input packet count on vif */ + u_long ocount; /* Output packet count on vif */ + u_long ibytes; /* Input byte count on vif */ + u_long obytes; /* Output byte count on vif */ +}; + +#define FILTER_RANGE 0 +#define FILTER_PREFIX 1 +struct mrtfilter { + struct mrtfilter *next; /* link to the next entry */ + int type; /* filter type: RANGE or PREFIX */ + union { /* type specific data structure */ + struct { + struct sockaddr_in6 from; + struct sockaddr_in6 to; + } mrtfu_range; + struct { + struct sockaddr_in6 prefix; + struct in6_addr mask; + } mrtfu_prefix; + } mrtu; + if_set ifset; /* interface list */ +}; +#define mrtf_from mrtu.mrtfu_range.from +#define mrtf_to mrtu.mrtfu_range.to +#define mrtf_prefix mrtu.mrtfu_prefix.prefix +#define mrtf_mask mrtu.mrtfu_prefix.mask diff --git a/usr.sbin/pim6dd/pathnames.h b/usr.sbin/pim6dd/pathnames.h new file mode 100644 index 0000000..487d6a37 --- /dev/null +++ b/usr.sbin/pim6dd/pathnames.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: pathnames.h,v 1.2 1999/12/16 05:36:37 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + + +#define _PATH_PIM6D_CONF "/usr/local/v6/etc/pim6dd.conf" + +#if (defined(BSD) && (BSD >= 199103)) +#define _PATH_PIM6D_PID "/var/run/pim6dd.pid" +#define _PATH_PIM6D_GENID "/var/run/pim6dd.genid" +#define _PATH_PIM6D_DUMP "/var/run/pim6dd.dump" +#define _PATH_PIM6D_CACHE "/var/run/pim6dd.cache" +#else +#define _PATH_PIM6D_PID "/etc/pim6dd.pid" +#define _PATH_PIM6D_GENID "/etc/pim6dd.genid" +#define _PATH_PIM6D_DUMP "/etc/pim6dd.dump" +#define _PATH_PIM6D_CACHE "/etc/pim6dd.cache" +#endif diff --git a/usr.sbin/pim6dd/pim6.c b/usr.sbin/pim6dd/pim6.c new file mode 100644 index 0000000..f15d6b1 --- /dev/null +++ b/usr.sbin/pim6dd/pim6.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: pim6.c,v 1.3 1999/10/26 08:39:19 itojun Exp $ + * $FreeBSD$ + */ + +#include "defs.h" +#include <sys/uio.h> + +/* + * Exported variables. + */ +char *pim6_recv_buf; /* input packet buffer */ +char *pim6_send_buf; /* output packet buffer */ + +struct sockaddr_in6 allpim6routers_group; /* ALL_PIM_ROUTERS group */ +int pim6_socket; /* socket for PIM control msgs */ + +/* + * Local variables. + */ +static struct sockaddr_in6 from; +static struct msghdr sndmh; +static struct iovec sndiov[2]; +static struct in6_pktinfo *sndpktinfo; + +/* + * Local function definitions. + */ +static void pim6_read __P((int f, fd_set *rfd)); +static void accept_pim6 __P((int recvlen)); +static int pim6_cksum __P((u_short *, struct in6_addr *, + struct in6_addr *, int)); + +void +init_pim6() +{ + static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct cmsghdr *cmsgp = (struct cmsghdr *)sndcmsgbuf; + + if ((pim6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_PIM)) < 0) + log(LOG_ERR, errno, "PIM6 socket"); + + k_set_rcvbuf(pim6_socket, SO_RECV_BUF_SIZE_MAX, + SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */ + k_set_hlim(pim6_socket, MINHLIM); /* restrict multicasts to one hop */ + k_set_loop(pim6_socket, FALSE); /* disable multicast loopback */ + + allpim6routers_group.sin6_len = sizeof(allpim6routers_group); + allpim6routers_group.sin6_family = AF_INET6; + if (inet_pton(AF_INET6, "ff02::d", + (void *)&allpim6routers_group.sin6_addr) != 1) + log(LOG_ERR, 0, "inet_pton failed for ff02::d"); + + if ((pim6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL || + (pim6_send_buf = malloc(RECV_BUF_SIZE)) == NULL) { + log(LOG_ERR, 0, "init_pim6: malloc failed\n"); + } + + /* initialize msghdr for sending packets */ + sndmh.msg_namelen = sizeof(struct sockaddr_in6); + sndmh.msg_iov = sndiov; + sndmh.msg_iovlen = 1; + sndmh.msg_control = (caddr_t)sndcmsgbuf; + sndmh.msg_controllen = sizeof(sndcmsgbuf); + /* initilization cmsg for specifing outgoing interfaces and source */ + sndpktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp); + cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo)); + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_PKTINFO; + + if (register_input_handler(pim6_socket, pim6_read) < 0) + log(LOG_ERR, 0, + "cannot register pim6_read() as an input handler"); + + IF_ZERO(&nbr_mifs); +} + +/* Read a PIM message */ +static void +pim6_read(f, rfd) + int f; + fd_set *rfd; +{ + register int pim6_recvlen; + int fromlen = sizeof(from); +#ifdef SYSV + sigset_t block, oblock; +#else + register int omask; +#endif + + pim6_recvlen = recvfrom(pim6_socket, pim6_recv_buf, RECV_BUF_SIZE, + 0, (struct sockaddr *)&from, &fromlen); + + if (pim6_recvlen < 0) { + if (errno != EINTR) + log(LOG_ERR, errno, "PIM6 recvmsg"); + return; + } + +#ifdef SYSV + (void)sigemptyset(&block); + (void)sigaddset(&block, SIGALRM); + if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0) + log(LOG_ERR, errno, "sigprocmask"); +#else + /* Use of omask taken from main() */ + omask = sigblock(sigmask(SIGALRM)); +#endif /* SYSV */ + + accept_pim6(pim6_recvlen); + +#ifdef SYSV + (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL); +#else + (void)sigsetmask(omask); +#endif /* SYSV */ +} + + +static void +accept_pim6(pimlen) + int pimlen; +{ + register struct pim *pim; + struct sockaddr_in6 *src = &from; + + /* sanity check */ + if (pimlen < sizeof(pim)) { + log(LOG_WARNING, 0, + "data field too short (%u bytes) for PIM header, from %s", + pimlen, inet6_fmt(&src->sin6_addr)); + return; + } + pim = (struct pim *)pim6_recv_buf; + +#ifdef NOSUCHDEF /* TODO: delete. Too noisy */ + IF_DEBUG(DEBUG_PIM_DETAIL) { + IF_DEBUG(DEBUG_PIM) { + log(LOG_DEBUG, 0, "Receiving %s from %s", + packet_kind(IPPROTO_PIM, pim->pim_type, 0), + inet6_fmt(&src->sin6_addr)); + log(LOG_DEBUG, 0, "PIM type is %u", pim->pim_type); + } + } +#endif /* NOSUCHDEF */ + + + /* Check of PIM version is already done in the kernel */ + /* + * TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address) + * is it necessary? + */ + /* Checksum verification is done in the kernel. */ + + switch (pim->pim_type) { + case PIM_HELLO: + receive_pim6_hello(src, (char *)(pim), pimlen); + break; + case PIM_REGISTER: + log(LOG_INFO, 0, "ignore %s from %s", + packet_kind(IPPROTO_PIM, pim->pim_type, 0), + inet6_fmt(&src->sin6_addr)); + break; + case PIM_REGISTER_STOP: + log(LOG_INFO, 0, "ignore %s from %s", + packet_kind(IPPROTO_PIM, pim->pim_type, 0), + inet6_fmt(&src->sin6_addr)); + break; + case PIM_JOIN_PRUNE: + receive_pim6_join_prune(src, (char *)(pim), pimlen); + break; + case PIM_BOOTSTRAP: + log(LOG_INFO, 0, "ignore %s from %s", + packet_kind(IPPROTO_PIM, pim->pim_type, 0), + inet6_fmt(&src->sin6_addr)); + break; + case PIM_ASSERT: + receive_pim6_assert(src, (char *)(pim), pimlen); + break; + case PIM_GRAFT: + case PIM_GRAFT_ACK: + receive_pim6_graft(src, (char *)(pim), pimlen, pim->pim_type); + break; + case PIM_CAND_RP_ADV: + log(LOG_INFO, 0, "ignore %s from %s", + packet_kind(IPPROTO_PIM, pim->pim_type, 0), + inet6_fmt(&src->sin6_addr)); + break; + default: + log(LOG_INFO, 0, + "ignore unknown PIM message code %u from %s", + pim->pim_type, + inet6_fmt(&src->sin6_addr)); + break; + } +} + + +/* + * Send a multicast PIM packet from src to dst, PIM message type = "type" + * and data length (after the PIM header) = "datalen" + */ +void +send_pim6(buf, src, dst, type, datalen) + char *buf; + struct sockaddr_in6 *src, *dst; + int type, datalen; +{ + struct pim *pim; + int setloop = 0; + int ifindex = 0, sendlen = sizeof(struct pim) + datalen; + + /* Prepare the PIM packet */ + pim = (struct pim *)buf; + pim->pim_type = type; + pim->pim_ver = PIM_PROTOCOL_VERSION; + pim->pim_rsv = 0; + pim->pim_cksum = 0; + /* + * TODO: XXX: if start using this code for PIM_REGISTERS, exclude the + * encapsulated packet from the checksum. + */ + pim->pim_cksum = pim6_cksum((u_int16 *)pim, + &src->sin6_addr, &dst->sin6_addr, + sendlen); + + /* + * Specify the source address of the packet. Also, specify the + * outgoing interface and the source address if possible. + */ + memcpy(&sndpktinfo->ipi6_addr, &src->sin6_addr, + sizeof(src->sin6_addr)); + if ((ifindex = src->sin6_scope_id) != 0) { + sndpktinfo->ipi6_ifindex = ifindex; + } + else { + sndpktinfo->ipi6_ifindex = 0; /* make sure to be cleared */ + log(LOG_WARNING, 0, + "send_pim6: could not determine the outgoint IF; send anyway"); + } + + if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) { + k_set_if(pim6_socket, ifindex); + if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, + &allnodes_group.sin6_addr) || + IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, + &allrouters_group.sin6_addr) || + IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, + &allpim6routers_group.sin6_addr)) { + setloop = 1; + k_set_loop(pim6_socket, TRUE); + } + } + + sndmh.msg_name = (caddr_t)dst; + sndiov[0].iov_base = (caddr_t)buf; + sndiov[0].iov_len = sendlen; + if (sendmsg(pim6_socket, &sndmh, 0) < 0) { + if (errno == ENETDOWN) + check_vif_state(); + else + log(LOG_WARNING, errno, "sendto from %s to %s", + inet6_fmt(&src->sin6_addr), + inet6_fmt(&dst->sin6_addr)); + if (setloop) + k_set_loop(pim6_socket, FALSE); + return; + } + + if (setloop) + k_set_loop(pim6_socket, FALSE); + + IF_DEBUG(DEBUG_PIM_DETAIL) { + IF_DEBUG(DEBUG_PIM) { + char ifname[IFNAMSIZ]; + + log(LOG_DEBUG, 0, "SENT %s from %-15s to %s on %s", + packet_kind(IPPROTO_PIM, type, 0), + inet6_fmt(&src->sin6_addr), + inet6_fmt(&dst->sin6_addr), + ifindex ? if_indextoname(ifindex, ifname) : "?"); + } + } +} + +u_int pim_send_cnt = 0; +#define SEND_DEBUG_NUMBER 50 + +/* ============================== */ + +/* + * Checksum routine for Internet Protocol family headers (Portable Version). + * + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + */ + +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} + +static union { + u_short phs[4]; + struct { + u_long ph_len; + u_char ph_zero[3]; + u_char ph_nxt; + } ph; +} uph; + +/* + * 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. + */ +static int +pim6_cksum(addr, src, dst, len) + u_short *addr; + struct in6_addr *src, *dst; + int len; +{ + register int nleft = len; + register u_short *w; + register int sum = 0; + u_short answer = 0; + + /* + * First create IP6 pseudo header and calculate a summary. + */ + w = (u_short *)src; + uph.ph.ph_len = htonl(len); + uph.ph.ph_nxt = IPPROTO_PIM; + + /* IPv6 source address */ + sum += w[0]; + /* XXX: necessary? */ + if (!(IN6_IS_ADDR_LINKLOCAL(src) || IN6_IS_ADDR_MC_LINKLOCAL(src))) + sum += w[1]; + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; + sum += w[6]; sum += w[7]; + /* IPv6 destination address */ + w = (u_short *)dst; + sum += w[0]; + /* XXX: necessary? */ + if (!(IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst))) + sum += w[1]; + sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; + sum += w[6]; sum += w[7]; + /* Payload length and upper layer identifier */ + sum += uph.phs[0]; sum += uph.phs[1]; + sum += uph.phs[2]; sum += uph.phs[3]; + + /* + * Secondly calculate a summary of the first mbuf excluding offset. + */ + w = addr; + 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/pim6dd/pim6_proto.c b/usr.sbin/pim6dd/pim6_proto.c new file mode 100644 index 0000000..2be8f5c --- /dev/null +++ b/usr.sbin/pim6dd/pim6_proto.c @@ -0,0 +1,1598 @@ +/* + * Copyright (C) 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: pim6_proto.c,v 1.4 1999/10/27 11:40:30 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +/* + * Local functions definitions. + */ +static int parse_pim6_hello __P((char *pktPtr, int datalen, + struct sockaddr_in6 *src, + u_int16 *holdtime)); +static void delayed_join_job __P((void *)); +static void schedule_delayed_join __P((mrtentry_t *, struct sockaddr_in6 *)); +static void delayed_prune_job __P((void *)); +static void schedule_delayed_prune __P((mrtentry_t *, mifi_t, u_int16)); +static int compare_metrics __P((u_int32 local_preference, + u_int32 local_metric, + struct sockaddr_in6 *local_address, + u_int32 remote_preference, + u_int32 remote_metric, + struct sockaddr_in6 *remote_address)); +static int retransmit_pim6_graft __P((mrtentry_t *)); +static void retransmit_all_pim6_grafts __P((void *)); + +if_set nbr_mifs; /* Mifs that have one or more neighbors attached */ + +/************************************************************************ + * PIM_HELLO + ************************************************************************/ +int +receive_pim6_hello(src, pim_message, datalen) + struct sockaddr_in6 *src; + register char *pim_message; + int datalen; +{ + mifi_t mifi; + struct uvif *v; + register pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr; + u_int16 holdtime; + u_int8 *data_ptr; + int state_change; + srcentry_t *srcentry_ptr; + srcentry_t *srcentry_ptr_next; + mrtentry_t *mrtentry_ptr; + u_long random_delay; + + if ((mifi = find_vif_direct(src)) == NO_VIF) { + /* Either a local vif or somehow received PIM_HELLO from + * non-directly connected router. Ignore it. + */ + if (local_address(src) == NO_VIF) + log(LOG_INFO, 0, + "Ignoring PIM_HELLO from non-neighbor router %s", + inet6_fmt(&src->sin6_addr)); + return(FALSE); + } + + v = &uvifs[mifi]; + if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED)) + return(FALSE); /* Shoudn't come on this interface */ + data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); + + /* Get the Holdtime (in seconds) from the message. Return if error. */ + if (parse_pim6_hello(pim_message, datalen, src, &holdtime) == FALSE) + return(FALSE); + IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) + log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u", + inet6_fmt(&src->sin6_addr), holdtime); + + for (prev_nbr = (pim_nbr_entry_t *)NULL, nbr = v->uv_pim_neighbors; + nbr != (pim_nbr_entry_t *)NULL; + prev_nbr = nbr, nbr = nbr->next) { + /* The PIM neighbors are sorted in decreasing order of the + * network addresses (note that to be able to compare them + * correctly we must translate the addresses in host order. + */ + if (inet6_lessthan(src, &nbr->address)) + continue; + if (inet6_equal(src, &nbr->address)) { + /* We already have an entry for this host */ + if (0 == holdtime) { + /* + * Looks like we have a nice neighbor who is + * going down and wants to inform us by sending + * "holdtime=0". Thanks buddy and see you again! + */ + log(LOG_INFO, 0, + "PIM HELLO received: neighbor %s going down", + inet6_fmt(&src->sin6_addr)); + delete_pim6_nbr(nbr); + return(TRUE); + } + /* Set the timer */ + nbr->timer = holdtime; + return(TRUE); + } + else + /* + * No entry for this neighbor. Exit the loop and create an + * entry for it. + */ + break; + } + + /* + * This is a new neighbor. Create a new entry for it. + * It must be added right after `prev_nbr` + */ + new_nbr = (pim_nbr_entry_t *)malloc(sizeof(pim_nbr_entry_t)); + new_nbr->address = *src; + new_nbr->vifi = mifi; + new_nbr->timer = holdtime; + new_nbr->next = nbr; + new_nbr->prev = prev_nbr; + + if (prev_nbr != (pim_nbr_entry_t *)NULL) + prev_nbr->next = new_nbr; + else + v->uv_pim_neighbors = new_nbr; + if (new_nbr->next != (pim_nbr_entry_t *)NULL) + new_nbr->next->prev = new_nbr; + + v->uv_flags &= ~VIFF_NONBRS; + v->uv_flags |= VIFF_PIM_NBR; + IF_SET(mifi, &nbr_mifs); + + /* Elect a new DR */ + if (inet6_lessthan(&v->uv_linklocal->pa_addr, + &v->uv_pim_neighbors->address)) { + /* The first address is the new potential remote + * DR address and it wins (is >) over the local address. + */ + v->uv_flags &= ~VIFF_DR; + v->uv_flags &= ~VIFF_QUERIER; + } + + /* + * Since a new neighbour has come up, let it know your existence ASAP; + * compute a random value, and reset the value to the hello timer + * if it's smaller than the rest of the timer. + * XXX: not in the spec... + */ + random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1)); + if (random_delay < v->uv_pim_hello_timer) + v->uv_pim_hello_timer = random_delay; + + /* Update the source entries */ + for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL; + srcentry_ptr = srcentry_ptr_next) { + srcentry_ptr_next = srcentry_ptr->next; + + if (srcentry_ptr->incoming == mifi) + continue; + + for (mrtentry_ptr = srcentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + mrtentry_ptr = mrtentry_ptr->srcnext) { + + if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs))) { + state_change = + change_interfaces(mrtentry_ptr, + srcentry_ptr->incoming, + &mrtentry_ptr->pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + if(state_change == 1) + trigger_join_alert(mrtentry_ptr); + } + } + } + + IF_DEBUG(DEBUG_PIM_HELLO) + dump_vifs(stderr); /* Show we got a new neighbor */ + return(TRUE); +} + +void +delete_pim6_nbr(nbr_delete) + pim_nbr_entry_t *nbr_delete; +{ + srcentry_t *srcentry_ptr; + srcentry_t *srcentry_ptr_next; + mrtentry_t *mrtentry_ptr; + struct uvif *v; + int state_change; + + v = &uvifs[nbr_delete->vifi]; + + /* Delete the entry from the pim_nbrs chain */ + if (nbr_delete->prev != (pim_nbr_entry_t *)NULL) + nbr_delete->prev->next = nbr_delete->next; + else + v->uv_pim_neighbors = nbr_delete->next; + if (nbr_delete->next != (pim_nbr_entry_t *)NULL) + nbr_delete->next->prev = nbr_delete->prev; + + if (v->uv_pim_neighbors == (pim_nbr_entry_t *)NULL) { + /* This was our last neighbor. */ + v->uv_flags &= ~VIFF_PIM_NBR; + v->uv_flags |= (VIFF_NONBRS | VIFF_DR | VIFF_QUERIER); + IF_CLR(nbr_delete->vifi, &nbr_mifs); + } + else { + if (inet6_greaterthan(&v->uv_linklocal->pa_addr, + &v->uv_pim_neighbors->address)) { + /* The first address is the new potential remote + * DR address, but the local address is the winner. + */ + v->uv_flags |= VIFF_DR; + v->uv_flags |= VIFF_QUERIER; + } + } + + /* Update the source entries: + * If the deleted nbr was my upstream, then reset incoming and + * update all (S,G) entries for sources reachable through it. + * If the deleted nbr was the last on a non-iif vif, then recalcuate + * outgoing interfaces. + */ + for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL; + srcentry_ptr = srcentry_ptr_next) { + srcentry_ptr_next = srcentry_ptr->next; + + /* The only time we don't need to scan all mrtentries is + * when the nbr was on the iif, but not the upstream nbr! + */ + if (nbr_delete->vifi == srcentry_ptr->incoming && + srcentry_ptr->upstream != nbr_delete) + continue; + + /* Reset the next hop (PIM) router */ + if(srcentry_ptr->upstream == nbr_delete) + if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) { + /* + * Couldn't reset it. Sorry, the next hop router + * toward that source is probably not + * a PIM router, or cannot find route at all, + * hence I cannot handle this source and have to + * delete it. + */ + delete_srcentry(srcentry_ptr); + free((char *)nbr_delete); + return; + } + + for (mrtentry_ptr = srcentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + mrtentry_ptr = mrtentry_ptr->srcnext) { + mrtentry_ptr->incoming = srcentry_ptr->incoming; + mrtentry_ptr->upstream = srcentry_ptr->upstream; + mrtentry_ptr->metric = srcentry_ptr->metric; + mrtentry_ptr->preference = srcentry_ptr->preference; + state_change = + change_interfaces(mrtentry_ptr, + srcentry_ptr->incoming, + &mrtentry_ptr->pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + if(state_change == -1) { + trigger_prune_alert(mrtentry_ptr); + } else if(state_change == 1) { + trigger_join_alert(mrtentry_ptr); + } + } + } + + free((char *)nbr_delete); +} + + +/* TODO: simplify it! */ +static int +parse_pim6_hello(pim_message, datalen, src, holdtime) + char *pim_message; + int datalen; + struct sockaddr_in6 *src; + u_int16 *holdtime; +{ + u_int8 *pim_hello_message; + u_int8 *data_ptr; + u_int16 option_type; + u_int16 option_length; + int holdtime_received_ok = FALSE; + int option_total_length; + + pim_hello_message = (u_int8 *)(pim_message + sizeof(struct pim)); + datalen -= sizeof(struct pim); + for ( ; datalen >= sizeof(pim_hello_t); ) { + /* Ignore any data if shorter than (pim_hello header) */ + data_ptr = pim_hello_message; + GET_HOSTSHORT(option_type, data_ptr); + GET_HOSTSHORT(option_length, data_ptr); + switch (option_type) { + case PIM_MESSAGE_HELLO_HOLDTIME: + if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) { + IF_DEBUG(DEBUG_PIM_HELLO) + log(LOG_DEBUG, 0, + "PIM HELLO Holdtime from %s: " + "invalid OptionLength = %u", + inet6_fmt(&src->sin6_addr), + option_length); + return (FALSE); + } + GET_HOSTSHORT(*holdtime, data_ptr); + holdtime_received_ok = TRUE; + break; + default: + /* Ignore any unknown options */ + break; + } + + /* Move to the next option */ + /* XXX: TODO: If we are padding to the end of the 32 bit boundary, + * use the first method to move to the next option, otherwise + * simply (sizeof(pim_hello_t) + option_length). + */ +#ifdef BOUNDARY_32_BIT + option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) + + ((option_length & 0x3) ? 4 : 0)); +#else + option_total_length = (sizeof(pim_hello_t) + option_length); +#endif /* BOUNDARY_32_BIT */ + datalen -= option_total_length; + pim_hello_message += option_total_length; + } + return (holdtime_received_ok); +} + + +int +send_pim6_hello(v, holdtime) + struct uvif *v; + u_int16 holdtime; +{ + char *buf; + u_int8 *data_ptr; + + int datalen; + + buf = pim6_send_buf + sizeof(struct pim); + data_ptr = (u_int8 *)buf; + PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr); + PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr); + PUT_HOSTSHORT(holdtime, data_ptr); + + datalen = data_ptr - (u_int8 *)buf; + + send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr, + &allpim6routers_group, PIM_HELLO, datalen); + v->uv_pim_hello_timer = PIM_TIMER_HELLO_PERIOD; + return(TRUE); +} + + +/************************************************************************ + * PIM_JOIN_PRUNE + ************************************************************************/ + +typedef struct { + struct sockaddr_in6 source; + struct sockaddr_in6 group; + struct sockaddr_in6 target; +} join_delay_cbk_t; + +typedef struct { + mifi_t mifi; + struct sockaddr_in6 source; + struct sockaddr_in6 group; + u_int16 holdtime; +} prune_delay_cbk_t; + +static void +delayed_join_job(arg) + void *arg; +{ + mrtentry_t *mrtentry_ptr; + + join_delay_cbk_t *cbk = (join_delay_cbk_t *)arg; + + mrtentry_ptr = find_route(&cbk->source, &cbk->group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + return; + + if(mrtentry_ptr->join_delay_timerid) + timer_clearTimer(mrtentry_ptr->join_delay_timerid); + + if(mrtentry_ptr->upstream) + send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, + mrtentry_ptr->incoming, + &mrtentry_ptr->upstream->address, 0, 0); + + free(cbk); +} + +static void +schedule_delayed_join(mrtentry_ptr, target) + mrtentry_t *mrtentry_ptr; + struct sockaddr_in6 *target; +{ + u_long random_delay; + join_delay_cbk_t *cbk; + + /* Delete existing timer */ + if(mrtentry_ptr->join_delay_timerid) + timer_clearTimer(mrtentry_ptr->join_delay_timerid); + +#ifdef SYSV + random_delay = lrand48() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT; +#else + random_delay = random() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT; +#endif + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, "Scheduling join for src %s, grp %s, delay %d", + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), + random_delay); + + if(random_delay == 0 && mrtentry_ptr->upstream) { + send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, + mrtentry_ptr->incoming, + &mrtentry_ptr->upstream->address, 0, 0); + return; + } + + cbk = (join_delay_cbk_t *)malloc(sizeof(join_delay_cbk_t)); + cbk->source = mrtentry_ptr->source->address; + cbk->group = mrtentry_ptr->group->group; + cbk->target = *target; + + mrtentry_ptr->join_delay_timerid = + timer_setTimer(random_delay, delayed_join_job, cbk); +} + + +static void +delayed_prune_job(arg) + void *arg; +{ + mrtentry_t *mrtentry_ptr; + if_set new_pruned_oifs; + int state_change; + + prune_delay_cbk_t *cbk = (prune_delay_cbk_t *)arg; + + mrtentry_ptr = find_route(&cbk->source, &cbk->group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + return; + + if(mrtentry_ptr->prune_delay_timerids[cbk->mifi]) + timer_clearTimer(mrtentry_ptr->prune_delay_timerids[cbk->mifi]); + + if(IF_ISSET(cbk->mifi, &mrtentry_ptr->oifs)) { + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "Deleting pruned mif %d for src %s, grp %s", + cbk->mifi, + inet6_fmt(&cbk->source.sin6_addr), + inet6_fmt(&cbk->group.sin6_addr)); + + IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); + IF_SET(cbk->mifi, &new_pruned_oifs); + SET_TIMER(mrtentry_ptr->prune_timers[cbk->mifi], cbk->holdtime); + + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &new_pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + + /* Handle transition to negative cache */ + if(state_change == -1) + trigger_prune_alert(mrtentry_ptr); + } + + free(cbk); +} + +static void +schedule_delayed_prune(mrtentry_ptr, mifi, holdtime) + mrtentry_t *mrtentry_ptr; + mifi_t mifi; + u_int16 holdtime; +{ + prune_delay_cbk_t *cbk; + + /* Delete existing timer */ + if(mrtentry_ptr->prune_delay_timerids[mifi]) + timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); + + cbk = (prune_delay_cbk_t *)malloc(sizeof(prune_delay_cbk_t)); + cbk->mifi = mifi; + cbk->source = mrtentry_ptr->source->address; + cbk->group = mrtentry_ptr->group->group; + cbk->holdtime = holdtime; + + mrtentry_ptr->prune_delay_timerids[mifi] = + timer_setTimer((u_int16)PIM_RANDOM_DELAY_JOIN_TIMEOUT, + delayed_prune_job, cbk); +} + + +/* TODO: when parsing, check if we go beyong message size */ +int +receive_pim6_join_prune(src, pim_message, datalen) + struct sockaddr_in6 *src; + char *pim_message; + register int datalen; +{ + mifi_t mifi; + struct uvif *v; + pim6_encod_uni_addr_t uni_target_addr; + pim6_encod_grp_addr_t encod_group; + pim6_encod_src_addr_t encod_src; + u_int8 *data_ptr; + u_int8 num_groups; + u_int16 holdtime; + u_int16 num_j_srcs, num_p_srcs; + struct sockaddr_in6 source, group, target; + struct in6_addr s_mask, g_mask; + u_int8 s_flags; + u_int8 reserved; + mrtentry_t *mrtentry_ptr; + pim_nbr_entry_t *upstream_router; + if_set new_pruned_oifs; + int state_change; + + if ((mifi = find_vif_direct(src)) == NO_VIF) { + /* + * Either a local vif or somehow received PIM_JOIN_PRUNE from + * non-directly connected router. Ignore it. + */ + if (local_address(src) == NO_VIF) + log(LOG_INFO, 0, + "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s", + inet6_fmt(&src->sin6_addr)); + return(FALSE); + } + + v = &uvifs[mifi]; + if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) + return(FALSE); /* Shoudn't come on this interface */ + data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); + + /* Get the target address */ + GET_EUADDR6(&uni_target_addr, data_ptr); + GET_BYTE(reserved, data_ptr); + GET_BYTE(num_groups, data_ptr); + if (num_groups == 0) + return (FALSE); /* No indication for groups in the message */ + GET_HOSTSHORT(holdtime, data_ptr); + target.sin6_len = sizeof(target); + target.sin6_family = AF_INET6; + target.sin6_addr = uni_target_addr.unicast_addr; + target.sin6_scope_id = inet6_uvif2scopeid(&target, v); + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "PIM Join/Prune received from %s : target %s, holdtime %d", + inet6_fmt(&src->sin6_addr), + inet6_fmt(&target.sin6_addr), + holdtime); + + if (!inet6_localif_address(&target, v) && + !IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr)) { + /* if I am not the target of the join or prune message */ + /* + * Join Suppression: when receiving a join not addressed to me, + * if I am delaying a join for this (S,G) then cancel the delayed + * join. + * Prune Soliticiting Joins: when receiving a prune not + * addressed to me on a LAN, schedule delayed join if I have + * downstream receivers. + */ + upstream_router = find_pim6_nbr(&target); + if (upstream_router == (pim_nbr_entry_t *)NULL) + return (FALSE); /* I have no such neighbor */ + group.sin6_len = sizeof(group); + group.sin6_family = AF_INET6; + source.sin6_len = sizeof(source); + source.sin6_family = AF_INET6; + while (num_groups--) { + GET_EGADDR6(&encod_group, data_ptr); + GET_HOSTSHORT(num_j_srcs, data_ptr); + GET_HOSTSHORT(num_p_srcs, data_ptr); + if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) + continue; + MASKLEN_TO_MASK6(encod_group.masklen, g_mask); + group.sin6_addr = encod_group.mcast_addr; + group.sin6_scope_id = inet6_uvif2scopeid(&group, v); + if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { + data_ptr += + (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); + continue; /* Ignore this group and jump to the next */ + } + + while (num_j_srcs--) { + GET_ESADDR6(&encod_src, data_ptr); + source.sin6_addr = encod_src.src_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, + v); + + /* sanity checks */ + if (!inet6_valid_host(&source)) + continue; + if (encod_src.masklen > + (sizeof(struct in6_addr) << 3)) + continue; + + s_flags = encod_src.flags; + MASKLEN_TO_MASK6(encod_src.masklen, s_mask); + + /* (S,G) Join suppresion */ + mrtentry_ptr = find_route(&source, &group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + continue; + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "\tJOIN src %s, group %s - canceling " + "delayed join", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + /* Cancel the delayed join */ + if(mrtentry_ptr->join_delay_timerid) { + timer_clearTimer(mrtentry_ptr->join_delay_timerid); + mrtentry_ptr->join_delay_timerid = 0; + } + } + + while (num_p_srcs--) { + GET_ESADDR6(&encod_src, data_ptr); + source.sin6_addr = encod_src.src_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, + v); + /* sanity checks */ + if (!inet6_valid_host(&source)) + continue; + if (encod_src.masklen > + (sizeof(struct in6_addr) << 3)) + continue; + + s_flags = encod_src.flags; + + /* if P2P link (not addressed to me) ignore + */ + if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) + continue; + + /* + * if non-null oiflist then schedule delayed join. + */ + mrtentry_ptr = find_route(&source, &group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + continue; + + if(!(IF_ISEMPTY(&mrtentry_ptr->oifs))) { + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "\tPRUNE src %s, group %s " + "- scheduling delayed join", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + schedule_delayed_join(mrtentry_ptr, + &target); + } + } + + } /* while groups */ + + return(TRUE); + } /* if not unicast target */ + + /* I am the target of this join/prune: + * For joins, cancel delayed prunes that I have scheduled. + * For prunes, echo the prune and schedule delayed prunes on LAN or + * prune immediately on point-to-point links. + */ + else { + while (num_groups--) { + GET_EGADDR6(&encod_group, data_ptr); + GET_HOSTSHORT(num_j_srcs, data_ptr); + GET_HOSTSHORT(num_p_srcs, data_ptr); + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "PIM Join/Prune received: grp: %s plen: %d, " + "%d jsrc, %d psrc", + inet6_fmt(&encod_group.mcast_addr), + encod_group.masklen, num_j_srcs, num_p_srcs); + if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) + continue; /* Ignore this group */ + MASKLEN_TO_MASK6(encod_group.masklen, g_mask); + group.sin6_addr = encod_group.mcast_addr; + group.sin6_scope_id = inet6_uvif2scopeid(&group, v); + if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { + data_ptr += + (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); + continue; /* Ignore this group and jump to the next */ + } + + while (num_j_srcs--) { + GET_ESADDR6(&encod_src, data_ptr); + source.sin6_addr = encod_src.src_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, + v); + if (!inet6_valid_host(&source)) + continue; + if (encod_src.masklen > + (sizeof(struct in6_addr) << 3)) + continue; + + s_flags = encod_src.flags; + MASKLEN_TO_MASK6(encod_src.masklen, s_mask); + + mrtentry_ptr = find_route(&source, &group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + continue; + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "\tJOIN src %s, group %s - canceling " + "delayed prune", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + /* Cancel the delayed prune */ + if(mrtentry_ptr->prune_delay_timerids[mifi]) { + timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); + mrtentry_ptr->prune_delay_timerids[mifi] = 0; + } + } + + while (num_p_srcs--) { + GET_ESADDR6(&encod_src, data_ptr); + source.sin6_addr = encod_src.src_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, + v); + if (!inet6_valid_host(&source)) + continue; + s_flags = encod_src.flags; + + + mrtentry_ptr = find_route(&source, &group, + MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + continue; + + /* if P2P link (addressed to me) prune immediately + */ + if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) { + if(IF_ISSET(mifi, &mrtentry_ptr->oifs)) { + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "\tPRUNE(P2P) src %s," + "group %s - pruning " + "mif", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + IF_DEBUG(DEBUG_MRT) + log(LOG_DEBUG, 0, "Deleting pruned mif %d for src %s, grp %s", + mifi, + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); + IF_SET(mifi, &new_pruned_oifs); + SET_TIMER(mrtentry_ptr->prune_timers[mifi], holdtime); + + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &new_pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + + /* Handle transition to negative cache */ + if(state_change == -1) + trigger_prune_alert(mrtentry_ptr); + } /* if is pruned */ + } /* if p2p */ + + /* if LAN link, echo the prune and schedule delayed + * oif deletion + */ + else { + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "\tPRUNE(LAN) src %s, group " + "%s - scheduling delayed prune", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + + send_pim6_jp(mrtentry_ptr, + PIM_ACTION_PRUNE, mifi, + &target, holdtime, 1); + schedule_delayed_prune(mrtentry_ptr, + mifi, holdtime); + } + } + } /* while groups */ + } /* else I am unicast target */ + + return(TRUE); +} + + +int +send_pim6_jp(mrtentry_ptr, action, mifi, target_addr, holdtime, echo) + mrtentry_t *mrtentry_ptr; + int action; /* PIM_ACTION_JOIN or PIM_ACTION_PRUNE */ + mifi_t mifi; /* vif to send join/prune on */ + struct sockaddr_in6 *target_addr; /* encoded unicast target neighbor */ + u_int16 holdtime; /* holdtime */ + int echo; +{ + u_int8 *data_ptr, *data_start_ptr; + + data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); + data_start_ptr = data_ptr; + + if(echo == 0 && mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) { + /* No upstream neighbor - don't send */ + return(FALSE); + } + + IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) + log(LOG_DEBUG, 0, + "Sending %s: vif %s, src %s, group %s, " + "target %s, holdtime %d", + action==PIM_ACTION_JOIN ? "JOIN" : "PRUNE", + inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr), + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), + inet6_fmt(&target_addr->sin6_addr), holdtime); + + PUT_EUADDR6(target_addr->sin6_addr, data_ptr); /* encoded unicast target addr */ + PUT_BYTE(0, data_ptr); /* Reserved */ + *data_ptr++ = (u_int8)1; /* number of groups */ + PUT_HOSTSHORT(holdtime, data_ptr); /* holdtime */ + + /* data_ptr points at the first, and only encoded mcast group */ + PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr, + SINGLE_GRP_MSK6LEN, 0, data_ptr); + + /* set the number of join and prune sources */ + if(action == PIM_ACTION_JOIN) { + PUT_HOSTSHORT(1, data_ptr); + PUT_HOSTSHORT(0, data_ptr); + } else if(action == PIM_ACTION_PRUNE) { + PUT_HOSTSHORT(0, data_ptr); + PUT_HOSTSHORT(1, data_ptr); + } + + PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN, + 0, data_ptr); + + /* Cancel active graft */ + if (echo == 0) + delete_pim6_graft_entry(mrtentry_ptr); + + send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, + &allpim6routers_group, PIM_JOIN_PRUNE, + data_ptr - data_start_ptr); + + return(TRUE); +} + + +/************************************************************************ + * PIM_ASSERT + ************************************************************************/ + +/* Notes on assert prefs/metrics + * - For downstream routers, compare pref/metric previously received from + * winner against those in message. + * ==> store assert winner's pref/metric in mrtentry + * - For upstream router compare my actualy pref/metric for the source + * against those received in message. + * ==> store my actual pref/metric in srcentry + */ + +int +receive_pim6_assert(src, pim_message, datalen) + struct sockaddr_in6 *src; + register char *pim_message; + int datalen; +{ + mifi_t mifi; + pim6_encod_uni_addr_t eusaddr; + pim6_encod_grp_addr_t egaddr; + struct sockaddr_in6 source, group; + mrtentry_t *mrtentry_ptr; + u_int8 *data_ptr; + struct uvif *v; + u_int32 assert_preference; + u_int32 assert_metric; + u_int32 local_metric; + u_int32 local_preference; + u_int8 local_wins; + if_set new_pruned_oifs, new_leaves; + int state_change; + + if ((mifi = find_vif_direct(src)) == NO_VIF) { + /* Either a local vif or somehow received PIM_ASSERT from + * non-directly connected router. Ignore it. + */ + if (local_address(src) == NO_VIF) + log(LOG_INFO, 0, + "Ignoring PIM_ASSERT from non-neighbor router %s", + inet6_fmt(&src->sin6_addr)); + return(FALSE); + } + + v = &uvifs[mifi]; + if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) + return(FALSE); /* Shoudn't come on this interface */ + data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); + + /* Get the group and source addresses */ + GET_EGADDR6(&egaddr, data_ptr); + GET_EUADDR6(&eusaddr, data_ptr); + + /* Get the metric related info */ + GET_HOSTLONG(assert_preference, data_ptr); + GET_HOSTLONG(assert_metric, data_ptr); + + source.sin6_addr = eusaddr.unicast_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, v); + group.sin6_addr = egaddr.mcast_addr; + group.sin6_scope_id = inet6_uvif2scopeid(&group, v); + + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "PIM Assert received from %s: src %s, grp %s, " + "pref %d, metric %d", + inet6_fmt(&src->sin6_addr), + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr), + assert_preference, assert_metric); + + if ((mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE)) + == NULL) { + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_INFO, 0, + "\tFailed to create a mrtentry src:%s grp:%s", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr)); + return(FALSE); + } + if(mrtentry_ptr->flags & MRTF_NEW) { + /* For some reason, it's possible for asserts to be processed + * before the data alerts a cache miss. Therefore, when an + * assert is received, create (S,G) state and continue, since + * we know by the assert that there are upstream forwarders. + */ + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, "\tNo MRT entry - creating..."); + + mrtentry_ptr->flags &= ~MRTF_NEW; + + /* Set oifs */ + set_leaves(mrtentry_ptr); + calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs)); + + /* Add it to the kernel */ + k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming, + &mrtentry_ptr->oifs); + +#ifdef RSRR + rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK); +#endif /* RSRR */ + + /* No need to call change_interfaces, but check for NULL oiflist */ + if(IF_ISEMPTY(&mrtentry_ptr->oifs)) + trigger_prune_alert(mrtentry_ptr); + } + + /* If arrived on iif, I'm downstream of the asserted LAN. + * If arrived on oif, I'm upstream of the asserted LAN. + */ + if (mifi == mrtentry_ptr->incoming) { + /* assert arrived on iif ==> I'm a downstream router */ + + /* Determine local (really that of upstream nbr!) pref/metric */ + local_metric = mrtentry_ptr->metric; + local_preference = mrtentry_ptr->preference; + + if(mrtentry_ptr->upstream && + inet6_equal(&mrtentry_ptr->upstream->address, src) && + assert_preference == local_preference && + assert_metric == local_metric) + + /* if assert from previous winner w/ same pref/metric, + * then assert sender wins again */ + local_wins = FALSE; + + else + /* assert from someone else or something changed */ + local_wins = compare_metrics(local_preference, + local_metric, + &mrtentry_ptr->upstream->address, + assert_preference, + assert_metric, src); + + /* + * This is between the assert sender and previous winner or rpf + * (who is the "local" in this case). + */ + if(local_wins == TRUE) { + /* the assert-sender loses, so discard the assert */ + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "\tAssert sender %s loses", + inet6_fmt(&src->sin6_addr)); + return(TRUE); + } + + /* The assert sender wins: upstream must be changed to the winner */ + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, "\tAssert sender %s wins", + inet6_fmt(&src->sin6_addr)); + if(inet6_equal(&mrtentry_ptr->upstream->address, src)) { + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "\tChanging upstream nbr to %s", + inet6_fmt(&src->sin6_addr)); + mrtentry_ptr->preference = assert_preference; + mrtentry_ptr->metric = assert_metric; + mrtentry_ptr->upstream = find_pim6_nbr(src); + } + SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT); + mrtentry_ptr->flags |= MRTF_ASSERTED; + + /* Send a join for the S,G if oiflist is non-empty */ + if(!(IF_ISEMPTY(&mrtentry_ptr->oifs))) + send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, + mrtentry_ptr->incoming, src, 0, 0); + + } /* if assert on iif */ + + /* If the assert arrived on an oif: */ + else { + if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs))) + return(FALSE); + /* assert arrived on oif ==> I'm a upstream router */ + + /* Determine local pref/metric */ + local_metric = mrtentry_ptr->source->metric; + local_preference = mrtentry_ptr->source->preference; + + local_wins = compare_metrics(local_preference, local_metric, + &v->uv_linklocal->pa_addr, + assert_preference, + assert_metric, src); + + if(local_wins == FALSE) { + + /* Assert sender wins - prune the interface */ + + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "\tAssert sender %s wins - pruning...", + inet6_fmt(&src->sin6_addr)); + + IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); + IF_SET(mifi, &new_pruned_oifs); + IF_SET(mifi, &mrtentry_ptr->asserted_oifs); + SET_TIMER(mrtentry_ptr->prune_timers[mifi], + PIM_JOIN_PRUNE_HOLDTIME); + + if (IF_ISSET(mifi, &mrtentry_ptr->leaves)) { + IF_COPY(&mrtentry_ptr->leaves, &new_leaves); + IF_CLR(mifi, &new_leaves); + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &new_pruned_oifs, + &mrtentry_ptr->leaves, + &new_leaves); + } + else { + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &new_pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + } + + /* Handle transition to negative cache */ + if(state_change == -1) + trigger_prune_alert(mrtentry_ptr); + + } /* assert sender wins */ + + else { + + /* Local wins (assert sender loses): + * send assert and schedule prune + */ + + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "\tAssert sender %s loses - " + "sending assert and scheuling prune", + inet6_fmt(&src->sin6_addr)); + + if(!(IF_ISSET(mifi, &mrtentry_ptr->leaves))) { + /* + * No directly connected receivers - delay prune + */ + send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, + mifi, &v->uv_linklocal->pa_addr, + PIM_JOIN_PRUNE_HOLDTIME, 0); + schedule_delayed_prune(mrtentry_ptr, mifi, + PIM_JOIN_PRUNE_HOLDTIME); + } + send_pim6_assert(&source, &group, mifi, mrtentry_ptr); + } + + } /* if assert on oif */ + + return(TRUE); +} + + +int +send_pim6_assert(source, group, mifi, mrtentry_ptr) + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; + mifi_t mifi; + mrtentry_t *mrtentry_ptr; +{ + u_int8 *data_ptr; + u_int8 *data_start_ptr; + u_int32 local_preference; + u_int32 local_metric; + + data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); + data_start_ptr = data_ptr; + PUT_EGADDR6(group->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr); + PUT_EUADDR6(source->sin6_addr, data_ptr); + + local_metric = mrtentry_ptr->source->metric; + local_preference = mrtentry_ptr->source->preference; + + PUT_HOSTLONG(local_preference, data_ptr); + PUT_HOSTLONG(local_metric, data_ptr); + + IF_DEBUG(DEBUG_PIM_ASSERT) + log(LOG_DEBUG, 0, + "PIM Assert sending: src %s, grp %s, " + "pref %d, metric %d", + inet6_fmt(&source->sin6_addr), + inet6_fmt(&group->sin6_addr), + local_metric, local_preference); + + send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, + &allpim6routers_group, PIM_ASSERT, + data_ptr - data_start_ptr); + + return(TRUE); +} + + +/* Return TRUE if the local win, otherwise FALSE */ +static int +compare_metrics(local_preference, local_metric, local_address, + remote_preference, remote_metric, remote_address) + u_int32 local_preference; + u_int32 local_metric; + struct sockaddr_in6 *local_address; + u_int32 remote_preference; + u_int32 remote_metric; + struct sockaddr_in6 *remote_address; +{ + /* Now lets see who has a smaller gun (aka "asserts war") */ + /* FYI, the smaller gun...err metric wins, but if the same + * caliber, then the bigger network address wins. The order of + * treatment is: preference, metric, address. + */ + /* The RPT bits are already included as the most significant bits + * of the preferences. + */ + if (remote_preference > local_preference) + return TRUE; + if (remote_preference < local_preference) + return FALSE; + if (remote_metric > local_metric) + return TRUE; + if (remote_metric < local_metric) + return FALSE; + if (inet6_greaterthan(local_address, remote_address)) + return TRUE; + return FALSE; +} + + + + +/************************************************************************ + * PIM_GRAFT + ************************************************************************/ + + +u_long graft_retrans_timer; /* Graft retransmission timer */ +pim_graft_entry_t *graft_list; /* Active grafting entries */ + + +void +delete_pim6_graft_entry(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + pim_graft_entry_t *graft_entry; + + if(mrtentry_ptr->graft == (pim_graft_entry_t *)NULL) + return; + graft_entry = mrtentry_ptr->graft; + + if(graft_entry->prev) + graft_entry->prev->next = graft_entry->next; + else + graft_list = graft_entry->next; + if(graft_entry->next) + graft_entry->next->prev = graft_entry->prev; + mrtentry_ptr->graft = (pim_graft_entry_t *)NULL; + free(graft_entry); + + /* Stop the timer if there are no more entries */ + if(!graft_list) { + timer_clearTimer(graft_retrans_timer); + graft_retrans_timer = 0; + } +} + + +static int +retransmit_pim6_graft(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + u_int8 *data_ptr, *data_start_ptr; + + data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); + data_start_ptr = data_ptr; + + if (mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) { + /* No upstream neighbor - don't send */ + return(FALSE); + } + + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, + "Sending GRAFT: vif %s, src %s, grp %s, dst %s", + inet6_fmt(&uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr.sin6_addr), + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), + inet6_fmt(&mrtentry_ptr->upstream->address.sin6_addr)); + + + /* unicast target */ + PUT_EUADDR6(mrtentry_ptr->upstream->address.sin6_addr, data_ptr); + PUT_BYTE(0, data_ptr); /* Reserved */ + *data_ptr++ = (u_int8)1; /* number of groups */ + PUT_HOSTSHORT(0, data_ptr); /* no holdtime */ + + /* data_ptr points at the first, and only encoded mcast group */ + PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr, + SINGLE_GRP_MSK6LEN, 0, data_ptr); + + /* set the number of join(graft) and prune sources */ + PUT_HOSTSHORT(1, data_ptr); + PUT_HOSTSHORT(0, data_ptr); + + PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN, + 0, data_ptr); + + send_pim6(pim6_send_buf, + &uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr, + &mrtentry_ptr->upstream->address, + PIM_GRAFT, data_ptr - data_start_ptr); + + return(TRUE); +} + + +static void +retransmit_all_pim6_grafts(arg) + void *arg; /* UNUSED */ +{ + pim_graft_entry_t *graft_ptr; + + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, "Retransmitting all pending PIM-Grafts"); + + + for(graft_ptr = graft_list; + graft_ptr != NULL; + graft_ptr = graft_ptr->next) { + + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, "\tGRAFT src %s, grp %s", + inet6_fmt(&graft_ptr->mrtlink->source->address.sin6_addr), + inet6_fmt(&graft_ptr->mrtlink->group->group.sin6_addr)); + + retransmit_pim6_graft(graft_ptr->mrtlink); + } + + if (graft_list) + timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, + retransmit_all_pim6_grafts, (void *)NULL); +} + + +int +receive_pim6_graft(src, pim_message, datalen, pimtype) + struct sockaddr_in6 *src; + register char *pim_message; + int datalen; + int pimtype; +{ + mifi_t mifi; + struct uvif *v; + pim6_encod_uni_addr_t uni_target_addr; + pim6_encod_grp_addr_t encod_group; + pim6_encod_src_addr_t encod_src; + u_int8 *data_ptr; + u_int8 num_groups; + u_int16 holdtime; + u_int16 num_j_srcs; + u_int16 num_p_srcs; + struct sockaddr_in6 source, group; + struct in6_addr s_mask, g_mask; + u_int8 s_flags; + u_int8 reserved; + mrtentry_t *mrtentry_ptr; + int state_change; + + if ((mifi = find_vif_direct(src)) == NO_VIF) { + /* Either a local vif or somehow received PIM_GRAFT from + * non-directly connected router. Ignore it. + */ + if (local_address(src) == NO_VIF) + log(LOG_INFO, 0, + "Ignoring PIM_GRAFT from non-neighbor router %s", + inet6_fmt(&src->sin6_addr)); + return(FALSE); + } + + v = &uvifs[mifi]; + if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) + return(FALSE); /* Shoudn't come on this interface */ + data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); + + /* Get the target address */ + GET_EUADDR6(&uni_target_addr, data_ptr); + GET_BYTE(reserved, data_ptr); + GET_BYTE(num_groups, data_ptr); + if (num_groups == 0) + return (FALSE); /* No indication for groups in the message */ + GET_HOSTSHORT(holdtime, data_ptr); + + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, + "PIM %s received from %s on mif %d, grps: %d", + pimtype == PIM_GRAFT ? "GRAFT" : "GRAFT-ACK", + inet6_fmt(&src->sin6_addr), mifi, num_groups); + + group.sin6_len = sizeof(group); + group.sin6_family = AF_INET6; + source.sin6_len = sizeof(source); + source.sin6_family = AF_INET6; + while (num_groups--) { + GET_EGADDR6(&encod_group, data_ptr); + GET_HOSTSHORT(num_j_srcs, data_ptr); + GET_HOSTSHORT(num_p_srcs, data_ptr); + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, + " PIM graft: grp: %s, plen: %d, %d jsrcs, %d psrcs", + inet6_fmt(&encod_group.mcast_addr), + encod_group.masklen, num_j_srcs, num_p_srcs); + if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) + continue; + MASKLEN_TO_MASK6(encod_group.masklen, g_mask); + group.sin6_addr = encod_group.mcast_addr; + group.sin6_scope_id = inet6_uvif2scopeid(&group, v); + if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { + data_ptr += + (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); + continue; /* Ignore this group and jump to the next */ + } + + while (num_j_srcs--) { + GET_ESADDR6(&encod_src, data_ptr); + if (encod_src.masklen > (sizeof(struct in6_addr) << 3)) + continue; + source.sin6_addr = encod_src.src_addr; + source.sin6_scope_id = inet6_uvif2scopeid(&source, v); + if (!inet6_valid_host(&source)) + continue; + s_flags = encod_src.flags; + MASKLEN_TO_MASK6(encod_src.masklen, s_mask); + + mrtentry_ptr = find_route(&source, &group, MRTF_SG, + DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + continue; + + if(pimtype == PIM_GRAFT) { + /* Graft */ + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, + "\tGRAFT src %s, group %s - " + "forward data on mif %d", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr), + mifi); + + /* Cancel any delayed prune */ + if(mrtentry_ptr->prune_delay_timerids[mifi]) { + timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); + mrtentry_ptr->prune_delay_timerids[mifi] = 0; + } + + /* Add to oiflist (unprune) */ + if (IF_ISSET(mifi, &mrtentry_ptr->pruned_oifs)) { + IF_CLR(mifi, &mrtentry_ptr->pruned_oifs); + IF_CLR(mifi, &mrtentry_ptr->asserted_oifs); + SET_TIMER(mrtentry_ptr->prune_timers[mifi], 0); + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &mrtentry_ptr->pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + if(state_change == 1) + trigger_join_alert(mrtentry_ptr); + } + } /* Graft */ + else { + /* Graft-Ack */ + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, + "\tGRAFT-ACK src %s, group %s - " + "forward data on mif %d", + inet6_fmt(&source.sin6_addr), + inet6_fmt(&group.sin6_addr), + mifi); + if(mrtentry_ptr->graft) + delete_pim6_graft_entry(mrtentry_ptr); + } + } + /* Ignore anything in the prune portion of the message! */ + } + + /* Respond to graft with a graft-ack */ + if(pimtype == PIM_GRAFT) { + IF_DEBUG(DEBUG_PIM_GRAFT) + log(LOG_DEBUG, 0, "Sending GRAFT-ACK: mif %s, dst %s", + inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr), + inet6_fmt(&src->sin6_addr)); + bcopy(pim_message, pim6_send_buf, datalen); + send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, + src, PIM_GRAFT_ACK, datalen - sizeof(struct pim)); + } + + return(TRUE); +} + +int +send_pim6_graft(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + pim_graft_entry_t *new_graft; + int was_sent = 0; + + if(mrtentry_ptr->graft != (pim_graft_entry_t *)NULL) + /* Already sending grafts */ + return(FALSE); + + /* Send the first graft */ + was_sent = retransmit_pim6_graft(mrtentry_ptr); + if(!was_sent) + return(FALSE); + + /* Set up retransmission */ + new_graft = (pim_graft_entry_t *)malloc(sizeof(pim_graft_entry_t)); + if (new_graft == (pim_graft_entry_t *)NULL) { + log(LOG_WARNING, 0, + "Memory allocation error for graft entry src %s, grp %s", + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr)); + return(FALSE); + } + new_graft->next = graft_list; + new_graft->prev = (pim_graft_entry_t *)NULL; + new_graft->mrtlink = mrtentry_ptr; + if(graft_list) + graft_list->prev = new_graft; + graft_list = new_graft; + mrtentry_ptr->graft = new_graft; + + /* Set up timer if not running */ + if(!graft_retrans_timer) + graft_retrans_timer = timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, + retransmit_all_pim6_grafts, + (void *)NULL); + + return(TRUE); +} diff --git a/usr.sbin/pim6dd/pim6dd.8 b/usr.sbin/pim6dd/pim6dd.8 new file mode 100644 index 0000000..6109b91 --- /dev/null +++ b/usr.sbin/pim6dd/pim6dd.8 @@ -0,0 +1,112 @@ +.\" Copyright (C) 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: pim6dd.8,v 1.3 1999/08/13 09:20:43 jinmei Exp $ +.\" $FreeBSD$ +.\" +.Dd Nov 17, 1998 +.Dt PIM6DD 8 +.Os KAME +.Sh NAME +.Nm pim6dd +.Nd PIM for IPv6 dense mode daemon +.Sh SYNOPSIS +.Nm +.Op Fl c Ar configfile +.Op Fl d Op debug_level Op ,debug_level +.Sh DESCRIPTION +.Nm Pim6dd +is an IPv6 multicast routing daemon, which supports +PIMv2(Protocol Independent Multicast Version 2) dense mode +for IPv6. +.Pp +Options supported by +.Nm pim6dd : +.Bl -tag -width Ds +.It Fl c Ar configfile +Specify alternate location, +.Ar configfile , +for configuration file. +By default, +.Pa /usr/local/v6/etc/pim6dd.conf +is used. +.It Fl d +Specify debug levels. If this option is specified without any arguments, +all debug messages will be printed out. +A subset of the messages to be printed out can be specified +as arguments of the option. +Valid debug levels are +.Ic timeout, packets, interfaces, kernel, mfc, pim_detail, pim_hello, +.Ic kernel, mfc, pim_detail, pim_hello, pim_jp, pim_graft, pim_asserts, +.Ic pim_routes, pim_timers, rpf, pim, routes, routers, timers, +and +.Ic asserts. +.El +.Pp +.Nm Pim6dd +automatically configures itself to forward on all multicast-capable +interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding +the "loopback interface"). +To override the default configuration, +configuration commands may be placed in +.Pa /usr/local/v6/etc/pim6dd.conf +(or an alternative file, specified by the "\-c" option). +.\" +.Sh FILES +.Bl -tag -width /usr/local/v6/etc/pim6dd.conf -compact +.It Pa /usr/local/v6/etc/pim6dd.conf +The default configuration file. +.El +.Sh SEE ALSO +.Xr daemon 3 , +.Xr pim6dd.conf 5 +.Sh HISTORY +The +.Nm +command is based on +.Nm pimdd, +which is an IPv4 multicast routing daemon +developed at the University of Oregon. +.Nm Pimdd +has been derived from PIM sparse-mode +.Nm pimd +developed at University of Southern California. +Part of these two programs above has also been derived from +.Nm mrouted. +.Nm Mrouted +is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. +.\" +.Sh BUGS +.Nm Pim6dd +does not contain any unicast routing engine, so a unicast routing +daemon needs to run on the system. +.Pp +The kernel unicast routing table is periodically polled by +.Nm +in order to follow changes of existing unicast routes. +.\" diff --git a/usr.sbin/pim6dd/pim6dd.conf.5 b/usr.sbin/pim6dd/pim6dd.conf.5 new file mode 100644 index 0000000..a526c59 --- /dev/null +++ b/usr.sbin/pim6dd/pim6dd.conf.5 @@ -0,0 +1,157 @@ +.\" Copyright (C) 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: pim6dd.conf.5,v 1.2 1999/12/10 06:08:49 itojun Exp $ +.\" $FreeBSD$ +.\" +.Dd Nov 17, 1998 +.Dt PIM6DD.CONF 5 +.Os KAME +.Sh NAME +.Nm pim6dd.conf +.Nd "config file for pim6dd, PIM for IPv6 dense mode daemon" +.\" +.Sh DESCRIPTION +The file describes how the +.Nm pim6dd +daemon treats each interface on the system. +By default(including the case where there is no configuration file), +PIM will be activated on all interfaces, which can be overridden +by the file. +Lines beginning with +.Ql # +are comments. +.Pp +There are four types of configuration commands: +.Bl -tag -width Ds -compact +.It Xo +.Ic default_source_preference Ar preference +.Xc +Specifies a default preference value when sending a PIM assert message. +Preferences are used by assert elections to determine upstream routers. +Currently +.Nm +cannot reliably obtain preferences and metrics from the +unicast routing protocols, so a default value may be configured. +.\" +.It Ic default_source_metric Ar metric +Specifies a default metric value when sending a PIM assert message. +It is recommended that preferences be set such that metrics are never +consulted. However, default metrics may also be set and will default to +1024. +.\" +.It Xo +.Ic phyint Ar interface +.Op disable +.Xc +Specifies +.Nm +to ignore the interface even if the interface is multicast-capable. +Interfaces are specified in the form of "name unit", such as +.Ar gif0 +and +.Ar ep1. +.\" +.It Xo +.Ic phyint Ar interface +.Op preference Ar preference +.Op metric Ar metric +.Xc +Specifies the preference and/or metric values when sending a PIM +assert message on the interface. +.\" +.It Xo +.Ic filter Ar groupaddrs Ar interfaces... +.Xc +Specifies an output filter. If an incoming multicast packet's destination +matches the specified +.Ar groupaddrs, +the packet is not sent on the +.Ar interfaces. +Moreover, if there is no other interface than the specified +interfaces, +.Nm pim6dd +sends a prune message to the upstream neighbor. +Valid formats of +.Ar groupaddrs +are as follows. +.Bl -tag -width Ds -compact +.It Ar multicastaddr1-multicastaddr2 +specifies a numerical range of a scope. +Multicast addresses +from +.Ar multicastaddr1 +to +.Ar multicastaddr2 +will be filtered out. +Note that neither a white space nor a tab character must not be +inserted before nor after +.Ql - . +.It Ar multicastaddr/prefixlen +specifies a group prefix of a scope. +Multicast addresses which match the specified prefix will be filtered +out. +If +.Ar prefixlen +is omitted, it means the exact match for +.Ar multicastaddr. +.El +.Ar interfaces +are specified as a blank separated list of interfaces. Each interface is +specified in the form of "name unit". +.El +.\" +.Sh EXAMPLE +.Bd -literal -offset +#phyint gif0 disable +#phyint ep0 preference 101 +phyint de0 disable +filter ff15::4000/120 gif1 gif2 +filter ff18::1000-ff18::1050 gif3 +.Ed +.Sh SEE ALSO +.Xr pim6dd 8 +.Sh HISTORY +The +.Nm pim6dd +command and the configuration file +.Nm +are based on +.Nm pimdd, +which is an IPv4 multicast routing daemon +developed at the University of Oregon. +.Nm Pimdd +has been derived from PIM sparse-mode +.Nm pimd +developed at University of Southern California. +Part of these two programs above has also been derived from +.Nm mrouted. +.Nm Mrouted +is COPYRIGHT 1989 by The Board of Trustees of +Leland Stanford Junior University. +.\" .Sh BUGS +.\" (to be written) diff --git a/usr.sbin/pim6dd/pimdd.h b/usr.sbin/pim6dd/pimdd.h new file mode 100644 index 0000000..31b46d5 --- /dev/null +++ b/usr.sbin/pim6dd/pimdd.h @@ -0,0 +1,553 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: pimdd.h,v 1.1.1.1 1999/08/08 23:30:53 itojun Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include <netinet6/pim6.h> + +#define PIM_PROTOCOL_VERSION 2 +#define PIMD_VERSION PIM_PROTOCOL_VERSION +#define PIMD_SUBVERSION 1 +#if 0 +#define PIM_CONSTANT 0x000eff00 /* constant portion of 'group' field */ +#endif +#define PIM_CONSTANT 0 +#define PIMD_LEVEL (PIM_CONSTANT | PIMD_VERSION | (PIMD_SUBVERSION << 8)) + +#define INADDR_ALL_PIM_ROUTERS (u_int32)0xe000000D /* 224.0.0.13 */ + + +/* PIM protocol timers (in seconds) */ +#ifndef TIMER_INTERVAL +#define TIMER_INTERVAL 5 /* virtual timer granularity */ +#endif /* TIMER_INTERVAL */ + +#define PIM_DATA_TIMEOUT 210 +#define PIM_TIMER_HELLO_PERIOD 30 +#define PIM_JOIN_PRUNE_HOLDTIME 210 +#define PIM_RANDOM_DELAY_JOIN_TIMEOUT 3 +#define PIM_GRAFT_RETRANS_PERIOD 3 +#define PIM_TIMER_HELLO_HOLDTIME (3.5 * PIM_TIMER_HELLO_PERIOD) +#define PIM_ASSERT_TIMEOUT 210 + + +/* Misc definitions */ +#define SINGLE_SRC_MSKLEN 32 /* the single source mask length */ +#define SINGLE_GRP_MSKLEN 32 /* the single group mask length */ + +#define SINGLE_SRC_MSK6LEN 128 /* the single source mask length for IPv6*/ +#define SINGLE_GRP_MSK6LEN 128 /* the single group mask length for IPv6*/ + +/* TODO: change? */ +#define PIM_GROUP_PREFIX_DEFAULT_MASKLEN 16 /* The default group masklen if + * omitted in the config file. + */ + +#define UCAST_ROUTING_CHECK_INTERVAL 20 /* Unfortunately, if the unicast + * routing changes, the kernel + * or any of the existing + * unicast routing daemons + * don't send us a signal. + * Have to ask periodically the + * kernel for any route changes. + * Default: every 20 seconds. + * Sigh. + */ + + +#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */ + +#define DEFAULT_LOCAL_PREF 101 /* Default assert preference */ +#define DEFAULT_LOCAL_METRIC 1024 /* Default assert metric */ + +/************************************************************************** + * PIM Encoded-Unicast, Encoded-Group and Encoded-Source Address formats * + *************************************************************************/ +/* Address families definition */ +#define ADDRF_RESERVED 0 +#define ADDRF_IPv4 1 +#define ADDRF_IPv6 2 +#define ADDRF_NSAP 3 +#define ADDRF_HDLC 4 +#define ADDRF_BBN1822 5 +#define ADDRF_802 6 +#define ADDRF_ETHERNET ADDRF_802 +#define ADDRF_E163 7 +#define ADDRF_E164 8 +#define ADDRF_SMDS ADDRF_E164 +#define ADDRF_ATM ADDRF_E164 +#define ADDRF_F69 9 +#define ADDRF_TELEX ADDRF_F69 +#define ADDRF_X121 10 +#define ADDRF_X25 ADDRF_X121 +#define ADDRF_IPX 11 +#define ADDRF_APPLETALK 12 +#define ADDRF_DECNET_IV 13 +#define ADDRF_BANYAN 14 +#define ADDRF_E164_NSAP 15 + +/* Addresses Encoding Type (specific for each Address Family */ +#define ADDRT_IPv4 0 +#define ADDRT_IPv6 0 + + +#if 0 /* XXX: the definition is for IPv4 only */ +/* Encoded-Unicast: 6 bytes long */ +typedef struct pim_encod_uni_addr_ { + u_int8 addr_family; + u_int8 encod_type; + u_int32 unicast_addr; /* XXX: Note the 32-bit boundary + * misalignment for the unicast + * address when placed in the + * memory. Must read it byte-by-byte! + */ +} pim_encod_uni_addr_t; +#endif +/* Encoded-Unicast: 18 bytes long */ +typedef struct pim6_encod_uni_addr_ { + u_int8 addr_family; + u_int8 encod_type; + struct in6_addr unicast_addr; /* XXX: Note the 32-bit boundary + * misalignment for the unicast + * address when placed in the + * memory. Must read it byte-by-byte! + */ +} pim6_encod_uni_addr_t; + +#if 0 /* XXX: the definition is for IPv4 only */ +/* Encoded-Group */ +typedef struct pim_encod_grp_addr_ { + u_int8 addr_family; + u_int8 encod_type; + u_int8 reserved; + u_int8 masklen; + u_int32 mcast_addr; +} pim_encod_grp_addr_t; +#endif +/* Encoded-Group */ +typedef struct pim6_encod_grp_addr_ { + u_int8 addr_family; + u_int8 encod_type; + u_int8 reserved; + u_int8 masklen; + struct in6_addr mcast_addr; +} pim6_encod_grp_addr_t; + +#if 0 /* XXX: the definition is for IPv4 only */ +/* Encoded-Source */ +typedef struct pim_encod_src_addr_ { + u_int8 addr_family; + u_int8 encod_type; + u_int8 flags; + u_int8 masklen; + u_int32 src_addr; +} pim_encod_src_addr_t; +#endif +/* Encoded-Source */ +typedef struct pim6_encod_src_addr_ { + u_int8 addr_family; + u_int8 encod_type; + u_int8 flags; + u_int8 masklen; + struct in6_addr src_addr; +} pim6_encod_src_addr_t; +#define USADDR_RP_BIT 0x1 +#define USADDR_WC_BIT 0x2 +#define USADDR_S_BIT 0x4 + +/************************************************************************** + * PIM Messages formats * + *************************************************************************/ +/* TODO: XXX: some structures are probably not used at all */ + +typedef struct pim pim_header_t; + +/* PIM Hello */ +typedef struct pim_hello_ { + u_int16 option_type; /* Option type */ + u_int16 option_length; /* Length of the Option Value field in bytes */ +} pim_hello_t; + +#if 0 +/* PIM Join/Prune: XXX: all 32-bit addresses misaligned! */ +typedef struct pim_jp_header_ { + pim_encod_uni_addr_t encod_upstream_nbr; + u_int8 reserved; + u_int8 num_groups; + u_int16 holdtime; +} pim_jp_header_t; + +typedef struct pim_jp_encod_grp_ { + pim_encod_grp_addr_t encod_grp; + u_int16 number_join_src; + u_int16 number_prune_src; +} pim_jp_encod_grp_t; +#endif + +#define PIM_ACTION_NOTHING 0 +#define PIM_ACTION_JOIN 1 +#define PIM_ACTION_PRUNE 2 + +#define PIM_IIF_SOURCE 1 +#define PIM_IIF_RP 2 + +#define PIM_ASSERT_RPT_BIT 0x80000000 + + +/* PIM messages type */ +#define PIM_HELLO 0 +#ifndef PIM_REGISTER +#define PIM_REGISTER 1 +#endif +#define PIM_REGISTER_STOP 2 +#define PIM_JOIN_PRUNE 3 +#define PIM_BOOTSTRAP 4 +#define PIM_ASSERT 5 +#define PIM_GRAFT 6 +#define PIM_GRAFT_ACK 7 +#define PIM_CAND_RP_ADV 8 + +#define PIM_V2_HELLO PIM_HELLO +#define PIM_V2_REGISTER PIM_REGISTER +#define PIM_V2_REGISTER_STOP PIM_REGISTER_STOP +#define PIM_V2_JOIN_PRUNE PIM_JOIN_PRUNE +#define PIM_V2_BOOTSTRAP PIM_BOOTSTRAP +#define PIM_V2_ASSERT PIM_ASSERT +#define PIM_V2_GRAFT PIM_GRAFT +#define PIM_V2_GRAFT_ACK PIM_GRAFT_ACK +#define PIM_V2_CAND_RP_ADV PIM_CAND_RP_ADV + +#define PIM_V1_QUERY 0 +#define PIM_V1_REGISTER 1 +#define PIM_V1_REGISTER_STOP 2 +#define PIM_V1_JOIN_PRUNE 3 +#define PIM_V1_RP_REACHABILITY 4 +#define PIM_V1_ASSERT 5 +#define PIM_V1_GRAFT 6 +#define PIM_V1_GRAFT_ACK 7 + +/* Vartious options from PIM messages definitions */ +/* PIM_HELLO definitions */ +#define PIM_MESSAGE_HELLO_HOLDTIME 1 +#define PIM_MESSAGE_HELLO_HOLDTIME_LENGTH 2 +#define PIM_MESSAGE_HELLO_HOLDTIME_FOREVER 0xffff + + +#define MASK_TO_MASKLEN(mask, masklen) \ + do { \ + register u_int32 tmp_mask = ntohl((mask)); \ + register u_int8 tmp_masklen = sizeof((mask)) << 3; \ + for ( ; tmp_masklen > 0; tmp_masklen--, tmp_mask >>= 1) \ + if (tmp_mask & 0x1) \ + break; \ + (masklen) = tmp_masklen; \ + } while (0) + +#define MASKLEN_TO_MASK(masklen, mask) \ +do { \ + (mask) = (masklen)? htonl(~0 << ((sizeof((mask)) << 3) - (masklen))) : 0;\ +} while (0) + +#define MASKLEN_TO_MASK6(masklen, mask6) \ + do {\ + u_char maskarray[8] = \ + {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \ + int bytelen, bitlen, i; \ + memset(&(mask6), 0, sizeof(mask6));\ + bytelen = (masklen) / 8;\ + bitlen = (masklen) % 8;\ + for (i = 0; i < bytelen; i++) \ + (mask6).s6_addr[i] = 0xff;\ + if (bitlen) \ + (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \ + }while(0); + +/* + * A bunch of macros because of the lack of 32-bit boundary alignment. + * All because of one misalligned address format. Hopefully this will be + * fixed in PIMv3. (cp) must be (u_int8 *) . + */ +/* Originates from Eddy Rusty's (eddy@isi.edu) PIM-SM implementation for + * gated. + */ + +/* PUT_NETLONG puts "network ordered" data to the datastream. + * PUT_HOSTLONG puts "host ordered" data to the datastream. + * GET_NETLONG gets the data and keeps it in "network order" in the memory + * GET_HOSTLONG gets the data, but in the memory it is in "host order" + * The same for all {PUT,GET}_{NET,HOST}{SHORT,LONG} + */ +#define GET_BYTE(val, cp) ((val) = *(cp)++) +#define PUT_BYTE(val, cp) (*(cp)++ = (u_int8)(val)) + +#define GET_HOSTSHORT(val, cp) \ + do { \ + register u_int16 Xv; \ + Xv = (*(cp)++) << 8; \ + Xv |= *(cp)++; \ + (val) = Xv; \ + } while (0) + +#define PUT_HOSTSHORT(val, cp) \ + do { \ + register u_int16 Xv; \ + Xv = (u_int16)(val); \ + *(cp)++ = (u_int8)(Xv >> 8); \ + *(cp)++ = (u_int8)Xv; \ + } while (0) + +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) +#define GET_NETSHORT(val, cp) \ + do { \ + register u_int16 Xv; \ + Xv = *(cp)++; \ + Xv |= (*(cp)++) << 8; \ + (val) = Xv; \ + } while (0) +#define PUT_NETSHORT(val, cp) \ + do { \ + register u_int16 Xv; \ + Xv = (u_int16)(val); \ + *(cp)++ = (u_int8)Xv; \ + *(cp)++ = (u_int8)(Xv >> 8); \ + } while (0) +#else +#define GET_NETSHORT(val, cp) GET_HOSTSHORT(val, cp) +#define PUT_NETSHORT(val, cp) PUT_HOSTSHORT(val, cp) +#endif /* {GET,PUT}_NETSHORT */ + +#define GET_HOSTLONG(val, cp) \ + do { \ + register u_long Xv; \ + Xv = (*(cp)++) << 24; \ + Xv |= (*(cp)++) << 16; \ + Xv |= (*(cp)++) << 8; \ + Xv |= *(cp)++; \ + (val) = Xv; \ + } while (0) + +#define PUT_HOSTLONG(val, cp) \ + do { \ + register u_int32 Xv; \ + Xv = (u_int32)(val); \ + *(cp)++ = (u_int8)(Xv >> 24); \ + *(cp)++ = (u_int8)(Xv >> 16); \ + *(cp)++ = (u_int8)(Xv >> 8); \ + *(cp)++ = (u_int8)Xv; \ + } while (0) + +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) +#define GET_NETLONG(val, cp) \ + do { \ + register u_long Xv; \ + Xv = *(cp)++; \ + Xv |= (*(cp)++) << 8; \ + Xv |= (*(cp)++) << 16; \ + Xv |= (*(cp)++) << 24; \ + (val) = Xv; \ + } while (0) + +#define PUT_NETLONG(val, cp) \ + do { \ + register u_int32 Xv; \ + Xv = (u_int32)(val); \ + *(cp)++ = (u_int8)Xv; \ + *(cp)++ = (u_int8)(Xv >> 8); \ + *(cp)++ = (u_int8)(Xv >> 16); \ + *(cp)++ = (u_int8)(Xv >> 24); \ + } while (0) +#else +#define GET_NETLONG(val, cp) GET_HOSTLONG(val, cp) +#define PUT_NETLONG(val, cp) PUT_HOSTLONG(val, cp) +#endif /* {GET,PUT}_HOSTLONG */ + + +#define GET_ESADDR(esa, cp) \ + do { \ + (esa)->addr_family = *(cp)++; \ + (esa)->encod_type = *(cp)++; \ + (esa)->flags = *(cp)++; \ + (esa)->masklen = *(cp)++; \ + GET_NETLONG((esa)->src_addr, (cp)); \ + } while(0) + +#define GET_ESADDR6(esa, cp) /* XXX: hard coding */ \ + do { \ + (esa)->addr_family = *(cp)++; \ + (esa)->encod_type = *(cp)++; \ + (esa)->flags = *(cp)++; \ + (esa)->masklen = *(cp)++; \ + memcpy(&(esa)->src_addr, (cp), sizeof(struct in6_addr)); \ + (cp) += sizeof(struct in6_addr); \ + } while(0) + +#define PUT_ESADDR(addr, masklen, flags, cp) \ + do { \ + u_int32 mask; \ + MASKLEN_TO_MASK((masklen), mask); \ + *(cp)++ = ADDRF_IPv4; /* family */ \ + *(cp)++ = ADDRT_IPv4; /* type */ \ + *(cp)++ = (flags); /* flags */ \ + *(cp)++ = (masklen); \ + PUT_NETLONG((addr) & mask, (cp)); \ + } while(0) + +#define PUT_ESADDR6(addr, masklen, flags, cp) \ + do { \ + int i; \ + struct in6_addr maskaddr; \ + MASKLEN_TO_MASK6(masklen, maskaddr); \ + *(cp)++ = ADDRF_IPv6; /* family */ \ + *(cp)++ = ADDRT_IPv6; /* type */ \ + *(cp)++ = (flags); /* flags */ \ + *(cp)++ = (masklen); \ + for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \ + *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \ + } while(0) + +#define GET_EGADDR(ega, cp) \ + do { \ + (ega)->addr_family = *(cp)++; \ + (ega)->encod_type = *(cp)++; \ + (ega)->reserved = *(cp)++; \ + (ega)->masklen = *(cp)++; \ + GET_NETLONG((ega)->mcast_addr, (cp)); \ + } while(0) + +#define GET_EGADDR6(ega, cp) /* XXX: hard coding */ \ + do { \ + (ega)->addr_family = *(cp)++; \ + (ega)->encod_type = *(cp)++; \ + (ega)->reserved = *(cp)++; \ + (ega)->masklen = *(cp)++; \ + memcpy(&(ega)->mcast_addr, (cp), sizeof(struct in6_addr)); \ + (cp) += sizeof(struct in6_addr); \ + } while(0) + +#define PUT_EGADDR(addr, masklen, reserved, cp) \ + do { \ + u_int32 mask; \ + MASKLEN_TO_MASK((masklen), mask); \ + *(cp)++ = ADDRF_IPv4; /* family */ \ + *(cp)++ = ADDRT_IPv4; /* type */ \ + *(cp)++ = (reserved); /* reserved; should be 0 */ \ + *(cp)++ = (masklen); \ + PUT_NETLONG((addr) & mask, (cp)); \ + } while(0) + +#define PUT_EGADDR6(addr, masklen, reserved, cp) \ + do { \ + int i; \ + struct in6_addr maskaddr; \ + MASKLEN_TO_MASK6(masklen, maskaddr); \ + *(cp)++ = ADDRF_IPv6; /* family */ \ + *(cp)++ = ADDRT_IPv6; /* type */ \ + *(cp)++ = (reserved); /* reserved; should be 0 */ \ + *(cp)++ = (masklen); \ + for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \ + *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \ + } while(0) + +#define GET_EUADDR(eua, cp) \ + do { \ + (eua)->addr_family = *(cp)++; \ + (eua)->encod_type = *(cp)++; \ + GET_NETLONG((eua)->unicast_addr, (cp)); \ + } while(0) + +#define GET_EUADDR6(eua, cp) /* XXX hard conding */ \ + do { \ + (eua)->addr_family = *(cp)++; \ + (eua)->encod_type = *(cp)++; \ + memcpy(&(eua)->unicast_addr, (cp), sizeof(struct in6_addr)); \ + (cp) += sizeof(struct in6_addr); \ + } while(0) + +#define PUT_EUADDR(addr, cp) \ + do { \ + *(cp)++ = ADDRF_IPv4; /* family */ \ + *(cp)++ = ADDRT_IPv4; /* type */ \ + PUT_NETLONG((addr), (cp)); \ + } while(0) + +#define PUT_EUADDR6(addr, cp) \ + do { \ + *(cp)++ = ADDRF_IPv6; /* family */ \ + *(cp)++ = ADDRT_IPv6; /* type */ \ + memcpy((cp), &(addr), sizeof(struct in6_addr)); \ + (cp) += sizeof(struct in6_addr); \ + } while(0) + +/* TODO: Currently not used. Probably not need at all. Delete! */ +#ifdef NOSUCHDEF +/* This is completely IGMP related stuff? */ +#define PIM_LEAF_TIMEOUT (3.5 * IGMP_QUERY_INTERVAL) +#endif /* NOSUCHDEF */ + +#if defined(__bsdi__) || defined(__NetBSD__) +/* + * Struct used to communicate from kernel to multicast router + * note the convenient similarity to an IP packet + */ +struct igmpmsg { + u_long unused1; + u_long unused2; + u_char im_msgtype; /* what type of message */ +#define IGMPMSG_NOCACHE 1 +#define IGMPMSG_WRONGVIF 2 +#define IGMPMSG_WHOLEPKT 3 /* used for user level encap*/ + u_char im_mbz; /* must be zero */ + u_char im_vif; /* vif rec'd on */ + u_char unused3; + struct in_addr im_src, im_dst; +}; +#endif diff --git a/usr.sbin/pim6dd/route.c b/usr.sbin/pim6dd/route.c new file mode 100644 index 0000000..6684f2c --- /dev/null +++ b/usr.sbin/pim6dd/route.c @@ -0,0 +1,659 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: route.c,v 1.3 1999/10/27 11:40:30 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + + +static u_int16 max_prune_timeout __P((mrtentry_t *)); +static void process_cache_miss __P((struct mrt6msg *im)); +static void process_wrong_iif __P((struct mrt6msg *im)); + +u_int32 default_source_preference = DEFAULT_LOCAL_PREF; +u_int32 default_source_metric = DEFAULT_LOCAL_METRIC; + + +/* Return the iif for given address */ +vifi_t +get_iif(address) + struct sockaddr_in6 *address; +{ + struct rpfctl rpfc; + + k_req_incoming(address, &rpfc); + if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) + return (NO_VIF); + return (rpfc.iif); +} + +/* Return the PIM neighbor toward a source */ +/* If route not found or if a local source or if a directly connected source, + * but is not PIM router, or if the first hop router is not a PIM router, + * then return NULL. + */ +pim_nbr_entry_t * +find_pim6_nbr(source) + struct sockaddr_in6 *source; +{ + struct rpfctl rpfc; + pim_nbr_entry_t *pim_nbr; + struct sockaddr_in6 *next_hop_router_addr; + + if (local_address(source) != NO_VIF) + return (pim_nbr_entry_t *)NULL; + k_req_incoming(source, &rpfc); + if ((IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) + || (rpfc.iif == NO_VIF)) + return (pim_nbr_entry_t *)NULL; + next_hop_router_addr = &rpfc.rpfneighbor; + for (pim_nbr = uvifs[rpfc.iif].uv_pim_neighbors; + pim_nbr != (pim_nbr_entry_t *)NULL; + pim_nbr = pim_nbr->next) + if (inet6_equal(&pim_nbr->address, next_hop_router_addr)) + return(pim_nbr); + return (pim_nbr_entry_t *)NULL; +} + +/* TODO: check again the exact setup if the source is local or directly + * connected!!! + */ +/* TODO: XXX: change the metric and preference for all (S,G) entries per + * source? + */ +/* PIMDM TODO - If possible, this would be the place to correct set the + * source's preference and metric to that obtained from the kernel + * and/or unicast routing protocol. For now, set it to the configured + * default for local pref/metric. + */ +/* + * Set the iif, upstream router, preference and metric for the route + * toward the source. Return TRUE is the route was found, othewise FALSE. + * If srctype==PIM_IIF_SOURCE and if the source is directly connected + * then the "upstream" is set to NULL. + * Note that srctype is a hold-over from the PIM-SM daemon and is unused. + */ +int +set_incoming(srcentry_ptr, srctype) + srcentry_t *srcentry_ptr; + int srctype; +{ + struct rpfctl rpfc; + struct sockaddr_in6 *source = &srcentry_ptr->address; + struct sockaddr_in6 *neighbor_addr; + register struct uvif *v; + register pim_nbr_entry_t *n; + + /* Preference will be 0 if directly connected */ + srcentry_ptr->preference = 0; + srcentry_ptr->metric = 0; + + if ((srcentry_ptr->incoming = local_address(source)) != NO_VIF) { + /* The source is a local address */ + /* TODO: set the upstream to myself? */ + srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL; + return (TRUE); + } + + if ((srcentry_ptr->incoming = find_vif_direct(source)) == NO_VIF) { + /* TODO: probably need to check the case if the iif is disabled */ + /* Use the lastest resource: the kernel unicast routing table */ + k_req_incoming(source, &rpfc); + if ((rpfc.iif == NO_VIF) || + IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) { + /* couldn't find a route */ + IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF) + log(LOG_DEBUG, 0, "NO ROUTE found for %s", + inet6_fmt(&source->sin6_addr)); + return(FALSE); + } + srcentry_ptr->incoming = rpfc.iif; + neighbor_addr = &rpfc.rpfneighbor; + } + else { + /* The source is directly connected. + */ + srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL; + return (TRUE); + } + + /* set the preference for sources that aren't directly connected. */ + v = &uvifs[srcentry_ptr->incoming]; + srcentry_ptr->preference = v->uv_local_pref; + srcentry_ptr->metric = v->uv_local_metric; + + /* + * The upstream router must be a (PIM router) neighbor, otherwise we + * are in big trouble ;-) + */ + for (n = v->uv_pim_neighbors; n != NULL; n = n->next) { + if (inet6_lessthan(neighbor_addr, &n->address)) + continue; + if (inet6_equal(neighbor_addr, &n->address)) { + /* + *The upstream router is found in the list of neighbors. + * We are safe! + */ + srcentry_ptr->upstream = n; + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, + "For src %s, iif is %d, next hop router is %s", + inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming, + inet6_fmt(&neighbor_addr->sin6_addr)); + return(TRUE); + } + else break; + } + + /* TODO: control the number of messages! */ + log(LOG_INFO, 0, + "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER", + inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming, + inet6_fmt(&neighbor_addr->sin6_addr)); + srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL; + + return(FALSE); +} + + +/* Set the leaves in a new mrtentry */ +void set_leaves(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + vifi_t vifi; + struct uvif *v; + + /* Check for a group report on each vif */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) + if(check_multicast_listener(v, &mrtentry_ptr->group->group)) + IF_SET(vifi, &mrtentry_ptr->leaves); +} + + +/* Handle new receiver + * + * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where + * we have source-specific Join/Prune. + */ +void +add_leaf(vifi, source, group) + vifi_t vifi; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; +{ + grpentry_t *grpentry_ptr; + mrtentry_t *mrtentry_srcs; + if_set new_leaves; + int state_change; + + grpentry_ptr = find_group(group); + if (grpentry_ptr == (grpentry_t *)NULL) + return; + + /* walk the source list for the group and add vif to oiflist */ + for (mrtentry_srcs = grpentry_ptr->mrtlink; + mrtentry_srcs != (mrtentry_t *)NULL; + mrtentry_srcs = mrtentry_srcs->grpnext) { + + /* if applicable, add the vif to the leaves */ + if (mrtentry_srcs->incoming == vifi) + continue; + + if(!(IF_ISSET(vifi, &mrtentry_srcs->leaves))) { + + IF_DEBUG(DEBUG_MRT) + log(LOG_DEBUG, 0, "Adding leaf vif %d for src %s group %s", + vifi, + inet6_fmt(&mrtentry_srcs->source->address.sin6_addr), + inet6_fmt(&group->sin6_addr)); + + IF_COPY(&mrtentry_srcs->leaves, &new_leaves); + IF_SET(vifi, &new_leaves); /* Add the leaf */ + + state_change = + change_interfaces(mrtentry_srcs, + mrtentry_srcs->incoming, + &mrtentry_srcs->pruned_oifs, + &new_leaves, + &mrtentry_srcs->asserted_oifs); + + /* Handle transition from negative cache */ + if(state_change == 1) + trigger_join_alert(mrtentry_srcs); + } + } +} + + +/* + * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where + * we have source-specific joins/prunes. + */ +void +delete_leaf(vifi, source, group) + vifi_t vifi; + struct sockaddr_in6 *source; + struct sockaddr_in6 *group; +{ + grpentry_t *grpentry_ptr; + mrtentry_t *mrtentry_srcs; + if_set new_leaves; + int state_change; + + /* mrtentry_t *mrtentry_ptr; + * mrtentry_t *mrtentry_srcs; + * vifbitmap_t new_oifs; + * vifbitmap_t old_oifs; + * vifbitmap_t new_leaves; + */ + + grpentry_ptr = find_group(group); + if (grpentry_ptr == (grpentry_t *)NULL) + return; + + /* walk the source list for the group and delete vif to leaves */ + for (mrtentry_srcs = grpentry_ptr->mrtlink; + mrtentry_srcs != (mrtentry_t *)NULL; + mrtentry_srcs = mrtentry_srcs->grpnext) { + + /* if applicable, delete the vif from the leaves */ + if (mrtentry_srcs->incoming == vifi) + continue; + + if(IF_ISSET(vifi, &mrtentry_srcs->leaves)) { + + IF_DEBUG(DEBUG_MRT) + log(LOG_DEBUG, 0, "Deleting leaf vif %d for src %s, group %s", + vifi, + inet6_fmt(&mrtentry_srcs->source->address.sin6_addr), + inet6_fmt(&group->sin6_addr)); + + IF_COPY(&mrtentry_srcs->leaves, &new_leaves); + IF_CLR(vifi, &new_leaves); /* Remove the leaf */ + + state_change = + change_interfaces(mrtentry_srcs, + mrtentry_srcs->incoming, + &mrtentry_srcs->pruned_oifs, + &new_leaves, + &mrtentry_srcs->asserted_oifs); + + /* Handle transition to negative cache */ + if(state_change == -1) + trigger_prune_alert(mrtentry_srcs); + } + } +} + +void +calc_oifs(mrtentry_ptr, oifs_ptr) + mrtentry_t *mrtentry_ptr; + if_set *oifs_ptr; +{ + if_set oifs; + + /* + * oifs = + * ((nbr_ifs - my_prune) + my_leaves) - my_filters - incoming_interface, + * i.e.`leaves` have higher priority than `prunes`, but lower than `filters'. + * Asserted oifs (those that lost assert) are handled as pruned oifs. + * The incoming interface is always deleted from the oifs + */ + + if (mrtentry_ptr == (mrtentry_t *)NULL) { + IF_ZERO(oifs_ptr); + return; + } + + IF_COPY(&nbr_mifs, &oifs); + IF_CLR_MASK(&oifs, &mrtentry_ptr->pruned_oifs); + IF_MERGE(&oifs, &mrtentry_ptr->leaves, &oifs); + IF_CLR_MASK(&oifs, &mrtentry_ptr->asserted_oifs); + IF_CLR_MASK(&oifs, &mrtentry_ptr->filter_oifs); + IF_CLR(mrtentry_ptr->incoming, &oifs); + IF_COPY(&oifs, oifs_ptr); +} + + +/* + * Set the iif, join/prune/leaves/asserted interfaces. Calculate and + * set the oifs. + * Return 1 if oifs change from NULL to not-NULL. + * Return -1 if oifs change from non-NULL to NULL + * else return 0 + * If the iif change or if the oifs change from NULL to non-NULL + * or vice-versa, then schedule that mrtentry join/prune timer to + * timeout immediately. + */ +int +change_interfaces(mrtentry_ptr, new_iif, new_pruned_oifs, + new_leaves_, new_asserted_oifs) + mrtentry_t *mrtentry_ptr; + vifi_t new_iif; + if_set *new_pruned_oifs; + if_set *new_leaves_; + if_set *new_asserted_oifs; +{ + if_set old_pruned_oifs; /* unnecessary? */ + if_set old_leaves; /* unnecessary? */ + if_set new_leaves; + if_set new_real_oifs; /* The result oifs */ + if_set old_real_oifs; + if_set old_asserted_oifs; /* unnecessary? */ + vifi_t old_iif; + int return_value; + + if (mrtentry_ptr == (mrtentry_t *)NULL) + return (0); + + IF_COPY(new_leaves_, &new_leaves); + + old_iif = mrtentry_ptr->incoming; + IF_COPY(&mrtentry_ptr->leaves, &old_leaves); + IF_COPY(&mrtentry_ptr->pruned_oifs, &old_pruned_oifs); + IF_COPY(&mrtentry_ptr->asserted_oifs, &old_asserted_oifs); + + IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs); + + mrtentry_ptr->incoming = new_iif; + IF_COPY(new_pruned_oifs, &mrtentry_ptr->pruned_oifs); + IF_COPY(&new_leaves, &mrtentry_ptr->leaves); + IF_COPY(new_asserted_oifs, &mrtentry_ptr->asserted_oifs); + calc_oifs(mrtentry_ptr, &new_real_oifs); + + if (IF_ISEMPTY(&old_real_oifs)) { + if (IF_ISEMPTY(&new_real_oifs)) + return_value = 0; + else + return_value = 1; + } else { + if (IF_ISEMPTY(&new_real_oifs)) + return_value = -1; + else + return_value = 0; + } + + if ((IF_SAME(&new_real_oifs, &old_real_oifs)) + && (new_iif == old_iif)) + return 0; /* Nothing to change */ + + IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs); + + k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address, + &mrtentry_ptr->group->group, new_iif, &new_real_oifs); + +#ifdef RSRR + rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK); +#endif /* RSRR */ + + return (return_value); +} + + +/* TODO: implement it. Required to allow changing of the physical interfaces + * configuration without need to restart pimd. + */ +int +delete_vif_from_mrt(vifi) +vifi_t vifi; +{ + return TRUE; +} + + +static u_int16 +max_prune_timeout(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + vifi_t vifi; +#if 0 + /* XXX: I don't understand how the variable works...(jinmei@kame.net) */ + u_int16 time_left = 0; +#endif + u_int16 max_holdtime = 0; + + for(vifi=0; vifi < numvifs; ++vifi) + if(IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs) && + mrtentry_ptr->prune_timers[vifi]) + /* XXX - too expensive ? */ + if(mrtentry_ptr->prune_timers[vifi] > max_holdtime) + max_holdtime = mrtentry_ptr->prune_timers[vifi]; +#if 0 + /* XXX: This is original. But does it have any meaning? */ + max_holdtime = time_left; +#endif + + if(max_holdtime == 0) + max_holdtime = (u_int16)PIM_JOIN_PRUNE_HOLDTIME; + + return(max_holdtime); +} + + +void +process_kernel_call() +{ + register struct mrt6msg *im; /* igmpmsg control struct */ + + im = (struct mrt6msg *) mld6_recv_buf; + + switch (im->im6_msgtype) { + case MRT6MSG_NOCACHE: + process_cache_miss(im); + break; + case MRT6MSG_WRONGMIF: + process_wrong_iif(im); + break; + default: + IF_DEBUG(DEBUG_KERN) + log(LOG_DEBUG, 0, "Unknown kernel_call code, %d", im->im6_msgtype); + break; + } +} + + +/* + * Protocol actions: + * 1. Create (S,G) entry (find_route(CREATE)) + * a. set iif and oifs + */ +static void +process_cache_miss(im) + struct mrt6msg *im; +{ + static struct sockaddr_in6 source = {sizeof(source), AF_INET6}; + static struct sockaddr_in6 group = {sizeof(group), AF_INET6}; + mrtentry_t *mrtentry_ptr; + + /* + * When there is a cache miss, we check only the header of the packet + * (and only it should be sent up by the kernel. + */ + + group.sin6_addr = im->im6_dst; + source.sin6_addr = im->im6_src; + group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]); + source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]); + + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s", + inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); + + /* Don't create routing entries for the LAN scoped addresses */ + if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */ + IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr)) + return; + + /* Create the (S,G) entry */ + mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE); + if (mrtentry_ptr == (mrtentry_t *)NULL) + return; + mrtentry_ptr->flags &= ~MRTF_NEW; + + /* Set oifs */ + set_leaves(mrtentry_ptr); + calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs)); + + /* Add it to the kernel */ + k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming, + &mrtentry_ptr->oifs); + +#ifdef RSRR + rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK); +#endif /* RSRR */ + + /* No need to call change_interfaces, but check for NULL oiflist */ + if(IF_ISEMPTY(&mrtentry_ptr->oifs)) + trigger_prune_alert(mrtentry_ptr); +} + + +/* + * A multicast packet has been received on wrong iif by the kernel. + * If the packet was received on a point-to-point interface, rate-limit + * prunes. if the packet was received on a LAN interface, rate-limit + * asserts. + */ +static void +process_wrong_iif(im) + struct mrt6msg *im; +{ + static struct sockaddr_in6 source = {sizeof(source), AF_INET6}; + static struct sockaddr_in6 group = {sizeof(group), AF_INET6}; + mifi_t mifi; + mrtentry_t *mrtentry_ptr; + + group.sin6_addr = im->im6_dst; + source.sin6_addr = im->im6_src; + mifi = (mifi_t)im->im6_mif; + group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[mifi]); + source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[mifi]); + + /* PIMDM TODO Don't create routing entries for the LAN scoped addresses */ + if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */ + IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr)) + return; + + mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); + if(mrtentry_ptr == (mrtentry_t *)NULL) + return; + + /* Ratelimit prunes or asserts */ +#ifdef notyet + if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) { + + /* Wrong vif on P2P interface - rate-limit prunes */ + + if(mrtentry_ptr->last_prune[mifi] == virtual_time) + /* Skip due to rate-limiting */ + return; + mrtentry_ptr->last_prune[mifi] = virtual_time; + + if(uvifs[mifi].uv_rmt_addr) + send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi, + uvifs[mifi].uv_rmt_addr, + max_prune_timeout(mrtentry_ptr), 0); + else + log(LOG_WARNING, 0, + "Can't send wrongvif prune on p2p %s: no remote address", + uvifs[mifi].uv_lcl_addr); + } else +#endif + { + + /* Wrong vif on LAN interface - rate-limit asserts */ + + if(mrtentry_ptr->last_assert[mifi] == virtual_time) + /* Skip due to rate-limiting */ + return; + mrtentry_ptr->last_assert[mifi] = virtual_time; + + /* Send the assert */ + send_pim6_assert(&source, &group, mifi, mrtentry_ptr); + } +} + + +void +trigger_prune_alert(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + IF_DEBUG(DEBUG_MRT) + log(LOG_DEBUG, 0, "Now negative cache for src %s, grp %s - pruning", + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr)); + + /* Set the entry timer to the max of the prune timers */ + SET_TIMER(mrtentry_ptr->timer, max_prune_timeout(mrtentry_ptr)); + + /* Send a prune */ + if(mrtentry_ptr->upstream) + send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mrtentry_ptr->incoming, + &mrtentry_ptr->upstream->address, + max_prune_timeout(mrtentry_ptr), 0); +} + +void +trigger_join_alert(mrtentry_ptr) + mrtentry_t *mrtentry_ptr; +{ + IF_DEBUG(DEBUG_MRT) + log(LOG_DEBUG, 0, "Now forwarding state for src %s, grp %s - grafting", + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr)); + + /* Refresh the entry timer */ + SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT); + + /* Send graft */ + send_pim6_graft(mrtentry_ptr); +} diff --git a/usr.sbin/pim6dd/routesock.c b/usr.sbin/pim6dd/routesock.c new file mode 100644 index 0000000..bb2d590 --- /dev/null +++ b/usr.sbin/pim6dd/routesock.c @@ -0,0 +1,373 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: routesock.c,v 1.4 1999/11/19 04:05:48 sumikawa Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/file.h> +#include "defs.h" +#include <sys/socket.h> +#include <net/route.h> +#ifdef HAVE_ROUTING_SOCKETS +#include <net/if_dl.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> +#include <stdlib.h> + +#ifdef HAVE_ROUTING_SOCKETS +union sockunion { + struct sockaddr sa; + struct sockaddr_in6 sin6; + struct sockaddr_dl sdl; +} so_dst, so_ifp; +typedef union sockunion *sup; +int routing_socket; +int rtm_addrs, pid; +struct rt_metrics rt_metrics; +u_long rtm_inits; + +/* + * Local functions definitions. + */ +static int getmsg __P((register struct rt_msghdr *, int, + struct rpfctl *rpfinfo)); + +/* + * TODO: check again! + */ +#ifdef IRIX +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ + : sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif /* IRIX */ + +#ifdef HAVE_SA_LEN +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) +#else +#define ADVANCE(x, n) (x += ROUNDUP(4)) /* TODO: a hack!! */ +#endif + +/* Open and initialize the routing socket */ +int +init_routesock() +{ + pid = getpid(); + routing_socket = socket(PF_ROUTE, SOCK_RAW, AF_INET6); + if (routing_socket < 0) { + log(LOG_ERR, 0, "\nRouting socket error"); + return -1; + } + if (fcntl(routing_socket, F_SETFL, O_NONBLOCK) == -1){ + log(LOG_ERR, 0, "\n Routing socket error"); + return -1; + } +#if 0 + { + int off; + + off = 0; + if (setsockopt(routing_socket, SOL_SOCKET, + SO_USELOOPBACK, (char *)&off, + sizeof(off)) < 0){ + log(LOG_ERR, 0 , "\n setsockopt(SO_USELOOPBACK,0)"); + return -1; + } + } +#endif + return 0; +} + + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + + +/* get the rpf neighbor info */ +int +k_req_incoming(source, rpfp) + struct sockaddr_in6 *source; + struct rpfctl *rpfp; +{ + int flags = RTF_STATIC; + register sup su; + static int seq; + int rlen; + register char *cp = m_rtmsg.m_space; + register int l; + struct rpfctl rpfinfo; + +/* TODO: a hack!!!! */ +#ifdef HAVE_SA_LEN +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) { \ + l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\ + } +#else +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) { \ + l = ROUNDUP(4); bcopy((char *)&(u), cp, l); cp += l;\ + } +#endif /* HAVE_SA_LEN */ + + /* initialize */ + memset(&rpfp->rpfneighbor, 0, sizeof(rpfp->rpfneighbor)); + rpfp->source = *source; + + /* check if local address or directly connected before calling the + * routing socket + */ + + if ((rpfp->iif = find_vif_direct_local(source)) != NO_VIF) { + rpfp->rpfneighbor = *source; + return(TRUE); + } + + /* prepare the routing socket params */ + rtm_addrs |= RTA_DST; + rtm_addrs |= RTA_IFP; + su = &so_dst; + su->sin6.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + su->sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif + su->sin6.sin6_addr = source->sin6_addr; + su->sin6.sin6_scope_id = source->sin6_scope_id; + so_ifp.sa.sa_family = AF_LINK; +#ifdef HAVE_SA_LEN + so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); +#endif + flags |= RTF_UP; + flags |= RTF_HOST; + flags |= RTF_GATEWAY; + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_IFP, so_ifp); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + + if ((rlen = write(routing_socket, (char *)&m_rtmsg, l)) < 0) { + IF_DEBUG(DEBUG_RPF | DEBUG_KERN) { + if (errno == ESRCH) + log(LOG_DEBUG, 0, + "Writing to routing socket: no such route\n"); + else + log(LOG_DEBUG, 0, "Error writing to routing socket"); + } + return(FALSE); + } + + do { + l = read(routing_socket, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + + if (l < 0) { + IF_DEBUG(DEBUG_RPF | DEBUG_KERN) + log(LOG_DEBUG, 0, "Read from routing socket failed: %s", strerror(errno)); + return(FALSE); + } + + if (getmsg(&rtm, l, &rpfinfo)){ + rpfp->rpfneighbor = rpfinfo.rpfneighbor; + rpfp->iif = rpfinfo.iif; + } +#undef rtm + return (TRUE); +} + + +/* + * Returns TRUE on success, FALSE otherwise. rpfinfo contains the result. + */ +int +getmsg(rtm, msglen, rpfinfop) + register struct rt_msghdr *rtm; + int msglen; + struct rpfctl *rpfinfop; +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + register struct sockaddr *sa; + register char *cp; + register int i; + struct sockaddr_in6 *sin6; + vifi_t vifi; + struct uvif *v; + char in6txt[INET6_ADDRSTRLEN]; + + if (rpfinfop == (struct rpfctl *)NULL) + return(FALSE); + + sin6 = (struct sockaddr_in6 *)&so_dst; + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, "route to: %s", + inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN)); + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + + if (!ifp){ /* No incoming interface */ + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, + "No incoming interface for destination %s", + inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN)); + return(FALSE); + } + if (dst && mask) + mask->sa_family = dst->sa_family; + if (dst) { + sin6 = (struct sockaddr_in6 *)dst; + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, " destination is: %s", + inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN)); + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) { + sin6 = (struct sockaddr_in6 *)gate; + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, " gateway is: %s", + inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN)); + rpfinfop->rpfneighbor = *sin6; + + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { +#if 0 + rpfinfop->rpfneighbor.sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); +#endif + rpfinfop->rpfneighbor.sin6_scope_id = ifp->sdl_index; + /* + * XXX: KAME kernel embeds the interface index to the address. + * Clear the index for safety. + */ + rpfinfop->rpfneighbor.sin6_addr.s6_addr[2] = 0; + rpfinfop->rpfneighbor.sin6_addr.s6_addr[3] = 0; + } + } + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) + /* get the number of the interface by matching the name */ + if ((strlen(v->uv_name) == ifp->sdl_nlen) && + !(strncmp(v->uv_name,ifp->sdl_data,ifp->sdl_nlen))) + break; + + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, " iif is %d", vifi); + + rpfinfop->iif = vifi; + + if (vifi >= numvifs){ + IF_DEBUG(DEBUG_RPF) + log(LOG_DEBUG, 0, + "Invalid incoming interface for destination %s, because of invalid virtual interface", + inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN)); + return(FALSE);/* invalid iif */ + } + + return(TRUE); +} + + +#else /* HAVE_ROUTING_SOCKETS */ + + +/* + * Return in rpfcinfo the incoming interface and the next hop router + * toward source. + */ +/* TODO: check whether next hop router address is in network or host order */ +int +k_req_incoming(source, rpfcinfo) + struct sockaddr_in6 *source; + struct rpfctl *rpfcinfo; +{ + rpfcinfo->source = *source; + rpfcinfo->iif = NO_VIF; /* just initialized, will be */ + /* changed in kernel */ + memset(&rpfcinfo->rpfneighbor, 0, sizeof(rpfcinfo->rpfneighbor)); /* initialized */ + + if (ioctl(udp_socket, SIOCGETRPF, (char *) rpfcinfo) < 0){ + log(LOG_ERR, errno, "ioctl SIOCGETRPF k_req_incoming"); + return(FALSE); + } + return (TRUE); +} + +#endif /* HAVE_ROUTING_SOCKETS */ + diff --git a/usr.sbin/pim6dd/timer.c b/usr.sbin/pim6dd/timer.c new file mode 100644 index 0000000..410183f --- /dev/null +++ b/usr.sbin/pim6dd/timer.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1998 by the University of Oregon. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Oregon. + * The name of the University of Oregon may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Kurt Windisch (kurtw@antc.uoregon.edu) + * + * $Id: timer.c,v 1.3 1999/09/15 07:45:12 jinmei Exp $ + */ +/* + * Part of this program has been derived from PIM sparse-mode pimd. + * The pimd program is covered by the license in the accompanying file + * named "LICENSE.pimd". + * + * The pimd program is COPYRIGHT 1998 by University of Southern California. + * + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + + +/* + * Global variables + */ + +/* + * Local functions definitions. + */ + + +/* + * Local variables + */ +u_int16 unicast_routing_timer; /* Used to check periodically for any + * change in the unicast routing. + */ +u_int16 unicast_routing_check_interval; +u_int8 ucast_flag; /* Used to indicate there was a timeout */ + + +/* to request and compare any route changes */ +srcentry_t srcentry_save; + +/* + * Init some timers + */ +void +init_timers() +{ + unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL; + unicast_routing_timer = unicast_routing_check_interval; + + /* Initialize the srcentry used to save the old routes + * during unicast routing change discovery process. + */ + srcentry_save.prev = (srcentry_t *)NULL; + srcentry_save.next = (srcentry_t *)NULL; + memset(&srcentry_save.address, 0, sizeof(struct sockaddr_in6)); + srcentry_save.address.sin6_len = sizeof(struct sockaddr_in6); + srcentry_save.address.sin6_family= AF_INET6; + srcentry_save.mrtlink = (mrtentry_t *)NULL; + srcentry_save.incoming = NO_VIF; + srcentry_save.upstream = (pim_nbr_entry_t *)NULL; + srcentry_save.metric = ~0; + srcentry_save.preference = ~0; + srcentry_save.timer = 0; + +} + +/* + * On every timer interrupt, advance (i.e. decrease) the timer for each + * neighbor and group entry for each vif. + */ +void +age_vifs() +{ + vifi_t vifi; + register struct uvif *v; + register pim_nbr_entry_t *next_nbr, *curr_nbr; + +/* XXX: TODO: currently, sending to qe* interface which is DOWN + * doesn't return error (ENETDOWN) on my Solaris machine, + * so have to check periodically the + * interfaces status. If this is fixed, just remove the defs around + * the "if (vifs_down)" line. + */ + +#if (!((defined SunOS) && (SunOS >= 50))) + if (vifs_down) +#endif /* Solaris */ + check_vif_state(); + + /* Age many things */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) + continue; + /* Timeout neighbors */ + for (curr_nbr = v->uv_pim_neighbors; curr_nbr != NULL; + curr_nbr = next_nbr) { + next_nbr = curr_nbr->next; + /* + * Never timeout neighbors with holdtime = 0xffff. + * This may be used with ISDN lines to avoid keeping the + * link up with periodic Hello messages. + */ + if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer) + continue; + IF_NOT_TIMEOUT(curr_nbr->timer) + continue; + + delete_pim6_nbr(curr_nbr); + } + + /* PIM_HELLO periodic */ + IF_TIMEOUT(v->uv_pim_hello_timer) + send_pim6_hello(v, PIM_TIMER_HELLO_HOLDTIME); + + /* MLD query periodic */ + IF_TIMEOUT(v->uv_gq_timer) + query_groups(v); + } + + IF_DEBUG(DEBUG_IF) { + dump_vifs(stderr); + dump_lcl_grp(stderr); + } +} + + +/* + * Scan the whole routing table and timeout a bunch of timers: + * - prune timers + * - Join/Prune delay timer + * - routing entry + * - Assert timer + */ +void +age_routes() +{ + mrtentry_t *mrtentry_ptr, *mrtentry_next; + grpentry_t *grpentry_ptr, *grpentry_next; + vifi_t vifi; + int change_flag, state_change; + int update_src_iif; + u_long curr_bytecnt; + + /* + * Timing out of the global `unicast_routing_timer` and data rate timer + */ + IF_TIMEOUT(unicast_routing_timer) { + ucast_flag = TRUE; + unicast_routing_timer = unicast_routing_check_interval; + } + ELSE { + ucast_flag = FALSE; + } + + /* Walk the the (S,G) entries */ + if(grplist == (grpentry_t *)NULL) + return; + for(grpentry_ptr = grplist; + grpentry_ptr != (grpentry_t *)NULL; + grpentry_ptr = grpentry_next) { + grpentry_next = grpentry_ptr->next; + + for(mrtentry_ptr = grpentry_ptr->mrtlink; + mrtentry_ptr != (mrtentry_t *)NULL; + mrtentry_ptr = mrtentry_next) { + mrtentry_next = mrtentry_ptr->grpnext; + + /* Refresh entry timer if data forwarded */ + curr_bytecnt = mrtentry_ptr->sg_count.bytecnt; + if (k_get_sg_cnt(udp_socket, + &mrtentry_ptr->source->address, + &mrtentry_ptr->group->group, + &mrtentry_ptr->sg_count)) { + /* No such routing entry in kernel */ + delete_mrtentry(mrtentry_ptr); + continue; + } + if(!(IF_ISEMPTY(&mrtentry_ptr->oifs)) && + curr_bytecnt != mrtentry_ptr->sg_count.bytecnt) { + /* Packets have been forwarded - refresh timer + * Note that these counters count packets received, + * not packets forwarded. So only refresh if packets + * received and non-null oiflist. + */ + IF_DEBUG(DEBUG_MFC) + log(LOG_DEBUG, 0, + "Refreshing src %s, dst %s after %d bytes forwarded", + inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), + inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), + mrtentry_ptr->sg_count.bytecnt); + SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT); + } + + /* Time out the entry */ + IF_TIMEOUT(mrtentry_ptr->timer) { + delete_mrtentry(mrtentry_ptr); + continue; + } + + /* Time out asserts */ + if(mrtentry_ptr->flags & MRTF_ASSERTED) + IF_TIMEOUT(mrtentry_ptr->assert_timer) { + mrtentry_ptr->flags &= ~MRTF_ASSERTED; + mrtentry_ptr->upstream = mrtentry_ptr->source->upstream; + mrtentry_ptr->metric = mrtentry_ptr->source->metric; + mrtentry_ptr->preference = mrtentry_ptr->source->preference; + } + + /* Time out Pruned interfaces */ + change_flag = FALSE; + for (vifi = 0; vifi < numvifs; vifi++) { + if (IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs)) + IF_TIMEOUT(mrtentry_ptr->prune_timers[vifi]) { + IF_CLR(vifi, &mrtentry_ptr->pruned_oifs); + SET_TIMER(mrtentry_ptr->prune_timers[vifi], 0); + change_flag = TRUE; + } + } + + /* Unicast Route changes */ + update_src_iif = FALSE; + if (ucast_flag == TRUE) { + /* iif toward the source */ + srcentry_save.incoming = mrtentry_ptr->source->incoming; + srcentry_save.upstream = mrtentry_ptr->source->upstream; + srcentry_save.preference = mrtentry_ptr->source->preference; + srcentry_save.metric = mrtentry_ptr->source->metric; + + if (set_incoming(mrtentry_ptr->source, + PIM_IIF_SOURCE) != TRUE) { + /* + * XXX: not in the spec! + * Cannot find route toward that source. + * This is bad. Delete the entry. + */ + delete_mrtentry(mrtentry_ptr); + continue; + } + else { + /* iif info found */ + if (!(mrtentry_ptr->flags & MRTF_ASSERTED) && + ((srcentry_save.incoming != + mrtentry_ptr->incoming) + || (srcentry_save.upstream != + mrtentry_ptr->upstream))) { + /* Route change has occur */ + update_src_iif = TRUE; + mrtentry_ptr->incoming = + mrtentry_ptr->source->incoming; + mrtentry_ptr->upstream = + mrtentry_ptr->source->upstream; + /* mrtentry should have pref/metric of upstream + * assert winner, but we dont have that info, + * so use the source pref/metric, which will be + * larger and thus the correct assert winner + * from upstream will be chosen. + */ + mrtentry_ptr->preference = + mrtentry_ptr->source->preference; + mrtentry_ptr->metric = + mrtentry_ptr->source->metric; + } + } + } + + if ((change_flag == TRUE) || (update_src_iif == TRUE)) { + /* Flush the changes */ + state_change = + change_interfaces(mrtentry_ptr, + mrtentry_ptr->incoming, + &mrtentry_ptr->pruned_oifs, + &mrtentry_ptr->leaves, + &mrtentry_ptr->asserted_oifs); + if(state_change == -1) + trigger_prune_alert(mrtentry_ptr); + } + } + } + + IF_DEBUG(DEBUG_PIM_MRT) + dump_pim_mrt(stderr); + return; +} diff --git a/usr.sbin/pim6dd/trace.c b/usr.sbin/pim6dd/trace.c new file mode 100644 index 0000000..1556097 --- /dev/null +++ b/usr.sbin/pim6dd/trace.c @@ -0,0 +1,540 @@ +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * non-commercial purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: trace.c,v 1.5 1999/09/16 08:46:00 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + + +#include "defs.h" +#include "trace.h" + +/* TODO */ +/* + * Traceroute function which returns traceroute replies to the requesting + * router. Also forwards the request to downstream routers. + */ +void +accept_mtrace(src, dst, group, ifindex, data, no, datalen) + struct sockaddr_in6 *src; + struct in6_addr *dst; + struct in6_addr *group; + int ifindex; + char *data; + u_int no; /* promoted u_char */ + int datalen; +{ + u_char type; + mrtentry_t *mrt; + struct tr6_query *qry; + struct tr6_resp *resp; + int vifi, ovifi; + char *p; + int rcount; + int errcode = TR_NO_ERR; + int resptype; + struct timeval tp; + struct sioc_mif_req6 mreq; + struct in6_addr parent_address; + struct sockaddr_in6 src_sa6 = {sizeof(src_sa6), AF_INET6}; + struct sockaddr_in6 dst_sa6 = {sizeof(dst_sa6), AF_INET6}; + struct sockaddr_in6 resp_sa6 = {sizeof(resp_sa6), AF_INET6}; + struct sockaddr_in6 grp_sa6 = {sizeof(grp_sa6), AF_INET6}; + struct sockaddr_in6 *sa_global; +#ifdef SM_ONLY + rpentry_t *rpentry_ptr; +#endif + + /* Remember qid across invocations */ + static u_int32 oqid = 0; + + /* timestamp the request/response */ + gettimeofday(&tp, 0); + + /* + * Check if it is a query or a response + */ + if (datalen == QLEN) { + type = QUERY; + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "Initial traceroute query rcvd " + "from %s to %s", + inet6_fmt(&src->sin6_addr), + inet6_fmt(dst)); + } + else if ((datalen - QLEN) % RLEN == 0) { + type = RESP; + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "In-transit traceroute query rcvd " + "from %s to %s", + inet6_fmt(&src->sin6_addr), + inet6_fmt(dst)); + if (IN6_IS_ADDR_MULTICAST(dst)) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "Dropping multicast response"); + return; + } + } + else { + log(LOG_WARNING, 0, "%s from %s to %s", + "Non decipherable traceroute request recieved", + inet6_fmt(&src->sin6_addr), inet6_fmt(dst)); + return; + } + + qry = (struct tr6_query *)data; + src_sa6.sin6_addr = qry->tr_src; + src_sa6.sin6_scope_id = + (IN6_IS_ADDR_LINKLOCAL(&qry->tr_src) + || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_src)) ? ifindex : 0; + dst_sa6.sin6_addr = qry->tr_dst; + dst_sa6.sin6_scope_id = + (IN6_IS_ADDR_LINKLOCAL(&qry->tr_dst) + || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_dst)) ? ifindex : 0; + grp_sa6.sin6_addr = *group; + grp_sa6.sin6_scope_id = 0; + + /* + * if it is a packet with all reports filled, drop it + */ + if ((rcount = (datalen - QLEN)/RLEN) == no) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "packet with all reports filled in"); + return; + } + + IF_DEBUG(DEBUG_TRACE) { + log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", + inet6_fmt(&qry->tr_src), + inet6_fmt(group), inet6_fmt(&qry->tr_dst)); + log(LOG_DEBUG, 0, "rhlim: %d rd: %s", qry->tr_rhlim, + inet6_fmt(&qry->tr_raddr)); + log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid); + } + + /* determine the routing table entry for this traceroute */ + mrt = find_route(&src_sa6, &grp_sa6, MRTF_SG | MRTF_WC | MRTF_PMBR, + DONT_CREATE); + IF_DEBUG(DEBUG_TRACE) { + if (mrt != (mrtentry_t *)NULL) { + if (mrt->upstream != (pim_nbr_entry_t *)NULL) + parent_address = mrt->upstream->address.sin6_addr; + else + parent_address = in6addr_any; + log(LOG_DEBUG, 0, + "mrt parent mif: %d rtr: %s metric: %d", + mrt->incoming, + inet6_fmt(&parent_address), mrt->metric); + /* TODO + log(LOG_DEBUG, 0, "mrt origin %s", + RT_FMT(rt, s1)); + */ + } else + log(LOG_DEBUG, 0, "...no route"); + } + + /* + * 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 (oqid == qry->tr_qid) { + /* + * If the multicast router is a member of the group + * being queried, and the query is multicasted, + * then the router can recieve multiple copies of + * the same query. If we have already replied to + * this traceroute, just ignore it this time. + * + * This is not a total solution, but since if this + * fails you only get N copies, N <= the number of + * interfaces on the router, it is not fatal. + */ + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "ignoring duplicate traceroute packet"); + return; + } + + if (mrt == (mrtentry_t *)NULL) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Mcast traceroute: no route entry %s", + inet6_fmt(&qry->tr_src)); +#if 0 + if (IN6_IS_ADDR_MULTICAST(dst)) + return; +#endif + } + vifi = find_vif_direct(&dst_sa6); + + if (vifi == NO_VIF) { + /* + * The traceroute destination is not on one of + * my subnet vifs. + */ + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Destination %s not an interface", + inet6_fmt(&qry->tr_dst)); + if (IN6_IS_ADDR_MULTICAST(dst)) + return; + errcode = TR_WRONG_IF; + } else if (mrt != (mrtentry_t *)NULL && + !IF_ISSET(vifi, &mrt->oifs)) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Destination %s not on forwarding tree " + "for src %s", + inet6_fmt(&qry->tr_dst), + inet6_fmt(&qry->tr_src)); + if (IN6_IS_ADDR_MULTICAST(dst)) + return; + errcode = TR_WRONG_IF; + } + } + else { + /* + * determine which interface the packet came in on + * RESP packets travel hop-by-hop so this either traversed + * a tunnel or came from a directly attached mrouter. + */ + if ((vifi = find_vif_direct(src)) == NO_VIF) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Wrong interface for packet"); + errcode = TR_WRONG_IF; + } + } + + /* Now that we've decided to send a response, save the qid */ + oqid = qry->tr_qid; + + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "Sending traceroute response"); + + /* copy the packet to the sending buffer */ + p = mld6_send_buf + sizeof(struct mld6_hdr); + + bcopy(data, p, datalen); + + p += datalen; + + /* + * If there is no room to insert our reply, coopt the previous hop + * error indication to relay this fact. + */ + if (p + sizeof(struct tr6_resp) > mld6_send_buf + RECV_BUF_SIZE) { + resp = (struct tr6_resp *)p - 1; + resp->tr_rflags = TR_NO_SPACE; + mrt = NULL; + goto sendit; + } + + /* + * fill in initial response fields + */ + resp = (struct tr6_resp *)p; + bzero(resp, sizeof(struct tr6_resp)); + datalen += (RLEN + sizeof(struct mld6_hdr)); + + resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) + + ((tp.tv_usec << 10) / 15625)); + + resp->tr_rproto = PROTO_PIM; + resp->tr_outifid = (vifi == NO_VIF) ? TR_NO_VIF : htonl(vifi); + resp->tr_rflags = errcode; + if ((sa_global = max_global_address()) == NULL) /* impossible */ + log(LOG_ERR, 0, "acept_mtrace: max_global_address returns NULL"); + resp->tr_lcladdr = sa_global->sin6_addr; + + /* + * obtain # of packets out on interface + */ + mreq.mifi = vifi; + if (vifi != NO_VIF && + ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0) + resp->tr_vifout = htonl(mreq.ocount); + else + resp->tr_vifout = 0xffffffff; + + /* + * fill in scoping & pruning information + */ + /* TODO */ +#if 0 + if (mrt != (mrtentry_t *)NULL) + for (gt = rt->rt_groups; gt; gt = gt->gt_next) { + if (gt->gt_mcastgrp >= group) + break; + } + else + gt = NULL; + + if (gt && gt->gt_mcastgrp == group) { + struct stable *st; + + for (st = gt->gt_srctbl; st; st = st->st_next) + if (qry->tr_src == st->st_origin) + break; + + sg_req.src.s_addr = qry->tr_src; + sg_req.grp.s_addr = group; + if (st && st->st_ctime != 0 && + ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0) + resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt); + else + resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff); + + if (VIFM_ISSET(vifi, gt->gt_scope)) + resp->tr_rflags = TR_SCOPED; + else if (gt->gt_prsent_timer) + resp->tr_rflags = TR_PRUNED; + else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) + if (VIFM_ISSET(vifi, rt->rt_children) && + NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap, + rt->rt_subordinates)) /*XXX*/ + resp->tr_rflags = TR_OPRUNED; + else + resp->tr_rflags = TR_NO_FWD; + } else { + if (scoped_addr(vifi, group)) + resp->tr_rflags = TR_SCOPED; + else if (rt && !VIFM_ISSET(vifi, rt->rt_children)) + resp->tr_rflags = TR_NO_FWD; + } +#endif /* 0 */ + + /* + * if no rte exists, set NO_RTE error + */ + if (mrt == (mrtentry_t *)NULL) { + src->sin6_addr = *dst; /* the dst address of resp. pkt */ + resp->tr_inifid = TR_NO_VIF; + resp->tr_rflags = TR_NO_RTE; + memset(&resp->tr_rmtaddr, 0, sizeof(struct in6_addr)); + } else { + /* get # of packets in on interface */ + mreq.mifi = mrt->incoming; + if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0) + resp->tr_vifin = htonl(mreq.icount); + else + resp->tr_vifin = 0xffffffff; + + /* + * TODO + * MASK_TO_VAL(rt->rt_originmask, resp->tr_smask); + */ + resp->tr_inifid = htonl(mrt->incoming); + if (mrt->upstream != (pim_nbr_entry_t *)NULL) + parent_address = mrt->upstream->address.sin6_addr; + else + parent_address = in6addr_any; + + resp->tr_rmtaddr = parent_address; + if (!IF_ISSET(vifi, &mrt->oifs)) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Destination %s not on forwarding tree " + "for src %s", + inet6_fmt(&qry->tr_dst), + inet6_fmt(&qry->tr_src)); + resp->tr_rflags = TR_WRONG_IF; + } +#if 0 + if (rt->rt_metric >= UNREACHABLE) { + resp->tr_rflags = TR_NO_RTE; + /* Hack to send reply directly */ + rt = NULL; + } +#endif /* 0 */ + } + +#ifdef SM_ONLY + /* + * If we're the RP for the trace group, note it. + */ + rpentry_ptr = rp_match(&grp_sa6); + if (rpentry_ptr && local_address(&rpentry_ptr->address) != NO_VIF) + resp->tr_rflags = TR_RP; +#endif /* SM_ONLY */ + + sendit: + /* + * if metric is 1 or no. of reports is 1, send response to requestor + * else send to upstream router. If the upstream router can't handle + * mtrace, set an error code and send to requestor anyway. + */ + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no); + + ovifi = NO_VIF; /* unspecified */ + if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) { + resptype = MLD6_MTRACE_RESP; + resp_sa6.sin6_addr = qry->tr_raddr; + if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) { + if ((ovifi = find_vif_direct(&dst_sa6)) == NO_VIF) { + log(LOG_INFO, 0, + "can't determine outgoing i/f for mtrace " + "response."); + return; + } + } + } else + /* TODO */ + { +#if 0 + if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) { + resp_sa6.sin6_addr = qry->tr_raddr; + resp->tr_rflags = TR_OLD_ROUTER; + resptype = MLD6_MTRACE_RESP; + } else +#endif /* 0 */ +#ifdef SM_ONLY + if (mrt->incoming && + (uvifs[mrt->incoming].uv_flags & MIFF_REGISTER)) { + log(LOG_DEBUG, 0, + "incoming i/f is for register. " + "Can't be forwarded anymore."); + resp_sa6.sin6_addr = qry->tr_raddr; + resptype = MLD6_MTRACE_RESP; + } else +#endif /* SM_ONLY */ + { + if (mrt->upstream != (pim_nbr_entry_t *)NULL) + parent_address = + mrt->upstream->address.sin6_addr; + else + parent_address = allrouters_group.sin6_addr; + resp_sa6.sin6_addr = parent_address; + ovifi = mrt->incoming; + resptype = MLD6_MTRACE; + } + } + + if (IN6_IS_ADDR_MULTICAST(&resp_sa6.sin6_addr)) { + struct sockaddr_in6 *sa6; + + /* + * Send the reply on a known multicast capable vif. + * If we don't have one, we can't source any + * multicasts anyway. + */ + if (IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) { + sa6 = &uvifs[ovifi].uv_linklocal->pa_addr; + ifindex = uvifs[ovifi].uv_ifindex; + } + else { + if (phys_vif != -1 && + (sa6 = uv_global(phys_vif)) != NULL) { + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, + "Sending reply to %s from %s", + inet6_fmt(dst), + inet6_fmt(&sa6->sin6_addr)); + ifindex = uvifs[phys_vif].uv_ifindex; + } + else { + log(LOG_INFO, 0, "No enabled phyints -- %s", + "dropping traceroute reply"); + return; + } + } + k_set_hlim(mld6_socket, qry->tr_rhlim); + send_mld6(resptype, no, sa6, &resp_sa6, group, + ifindex, 0, datalen, 0); + k_set_hlim(mld6_socket, 1); + } else { + struct sockaddr_in6 *sa6 = NULL; + ifindex = -1; /* unspecified by default */ + + if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr)) { + /* ovifi must be valid in this case */ + ifindex = uvifs[ovifi].uv_ifindex; + sa6 = &uvifs[ovifi].uv_linklocal->pa_addr; + } + + IF_DEBUG(DEBUG_TRACE) + log(LOG_DEBUG, 0, "Sending %s to %s from %s", + resptype == MLD6_MTRACE_RESP ? + "reply" : "request on", + inet6_fmt(dst), + sa6 ? inet6_fmt(&sa6->sin6_addr) : "unspecified"); + + send_mld6(resptype, no, sa6, &resp_sa6, group, ifindex, + 0, datalen, 0); + } + return; +} diff --git a/usr.sbin/pim6dd/trace.h b/usr.sbin/pim6dd/trace.h new file mode 100644 index 0000000..31e79c7 --- /dev/null +++ b/usr.sbin/pim6dd/trace.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: trace.h,v 1.2 1999/09/12 17:00:10 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + + +/* + * The packet format for a traceroute request. + */ +struct tr6_query { + struct in6_addr tr_src; /* traceroute source */ + struct in6_addr tr_dst; /* traceroute destination */ + struct in6_addr tr_raddr; /* traceroute response address */ +#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) + struct { + u_int32_t qid : 24; /* traceroute query id */ + u_int32_t rhlim : 8; /* traceroute response ttl */ + } q; +#else + struct { + u_int32_t rhlim : 8; /* traceroute response ttl */ + u_int32_t qid : 24; /* traceroute query id */ + } q; +#endif /* BYTE_ORDER */ +}; + +#define tr_rhlim q.rhlim +#define tr_qid q.qid + +/* + * Traceroute response format. A traceroute response has a tr_query at the + * beginning, followed by one tr_resp for each hop taken. + */ +struct tr6_resp { + u_int32_t tr_qarr; /* query arrival time */ +#if 0 + struct in6_addr tr_inaddr; /* incoming interface address */ + struct in6_addr tr_outaddr; /* outgoing interface address */ +#endif + u_int32_t tr_inifid; /* incoming interface identifier */ + u_int32_t tr_outifid; /* outgoing interface identifier */ + struct in6_addr tr_lcladdr; /* router's address(must have largest scope) */ + struct in6_addr tr_rmtaddr; /* parent address in source tree */ + u_int32_t tr_vifin; /* input packet count on interface */ + u_int32_t tr_vifout; /* output packet count on interface */ + u_int32_t tr_pktcnt; /* total incoming packets for src-grp */ + u_char tr_rproto; /* routing protocol deployed on router */ +#if 0 + u_char tr_fhlim; /* hop limit required to forward on outvif */ +#endif + u_char tr_flags; /* flags */ + u_char tr_plen; /* prefix length for src addr */ + u_char tr_rflags; /* forwarding error codes */ +}; + +/* defs within mtrace */ +#define QUERY 1 +#define RESP 2 +#define QLEN sizeof(struct tr6_query) +#define RLEN sizeof(struct tr6_resp) + +/* fields for tr_inifid and tr_outifid */ +#define TR_NO_VIF 0xffffffff/* interface can't be determined */ + +/* fields for tr_rflags (forwarding error codes) */ +#define TR_NO_ERR 0 /* No error */ +#define TR_WRONG_IF 1 /* traceroute arrived on non-oif */ +#define TR_PRUNED 2 /* router has sent a prune upstream */ +#define TR_OPRUNED 3 /* stop forw. after request from next hop rtr*/ +#define TR_SCOPED 4 /* group adm. scoped at this hop */ +#define TR_NO_RTE 5 /* no route for the source */ +#define TR_NO_LHR 6 /* not the last-hop router */ +#define TR_NO_FWD 7 /* not forwarding for this (S,G). Reason = ? */ +#define TR_RP 8 /* I am the RP/Core */ +#define TR_IIF 9 /* request arrived on the iif */ +#define TR_NO_MULTI 0x0a /* multicast disabled on that interface */ +#define TR_NO_SPACE 0x81 /* no space to insert responce data block */ +#define TR_OLD_ROUTER 0x82 /* previous hop does not support traceroute */ +#define TR_ADMIN_PROHIB 0x83 /* traceroute adm. prohibited */ + +/* fields for tr_flags */ +#define TR_SUBNET_COUNT 0x80 /* pkt count for (S,G) is for source network */ + +/* fields for r_plen */ +#define TR_GROUP_ONLY 0xff /* forwarding solely on group state */ + +/* fields for packets count */ +#define TR_CANT_COUNT 0xffffffff /* no count can be reported */ + +/* fields for tr_rproto (routing protocol) */ +#define PROTO_DVMRP 1 +#define PROTO_MOSPF 2 +#define PROTO_PIM 3 +#define PROTO_CBT 4 +#define PROTO_PIM_SPECIAL 5 +#define PROTO_PIM_STATIC 6 +#define PROTO_DVMRP_STATIC 7 + +#define MASK_TO_VAL(x, i) { \ + u_int32_t _x = ntohl(x); \ + (i) = 1; \ + while ((_x) <<= 1) \ + (i)++; \ + }; + +#define VAL_TO_MASK(x, i) { \ + x = htonl(~((1 << (32 - (i))) - 1)); \ + }; + +#define MASKLEN_TO_MASK6(masklen, mask6) \ + do {\ + u_char maskarray[8] = \ + {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \ + int bytelen, bitlen, i; \ + memset(&(mask6), 0, sizeof(mask6));\ + bytelen = (masklen) / 8;\ + bitlen = (masklen) % 8;\ + for (i = 0; i < bytelen; i++) \ + (mask6).s6_addr[i] = 0xff;\ + if (bitlen) \ + (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \ + }while(0); + +/* obnoxious gcc gives an extraneous warning about this constant... */ +#if defined(__STDC__) || defined(__GNUC__) +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +#else +#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ +#define const /**/ +#endif + +#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv) diff --git a/usr.sbin/pim6dd/vers.c b/usr.sbin/pim6dd/vers.c new file mode 100644 index 0000000..72668af --- /dev/null +++ b/usr.sbin/pim6dd/vers.c @@ -0,0 +1,2 @@ +/* $FreeBSD$ */ +char todaysversion[]="0.2.1.0-alpha15"; diff --git a/usr.sbin/pim6dd/vif.c b/usr.sbin/pim6dd/vif.c new file mode 100644 index 0000000..55bdb1f --- /dev/null +++ b/usr.sbin/pim6dd/vif.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: vif.c,v 1.3 1999/09/12 17:00:11 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + +#include "defs.h" + +/* + * Exported variables. + */ +struct uvif uvifs[MAXMIFS]; /* array of all virtual interfaces */ +vifi_t numvifs; /* Number of vifs in use */ +int vifs_down; /* 1=>some interfaces are down */ +int phys_vif; /* An enabled vif */ +int udp_socket; /* Since the honkin' kernel doesn't support */ + /* ioctls on raw IP sockets, we need a UDP */ + /* socket as well as our IGMP (raw) socket. */ + /* How dumb. */ +int total_interfaces; /* Number of all interfaces: including the + * non-configured, but excluding the + * loopback interface and the non-multicast + * capable interfaces. + */ +if_set if_nullset; /* an interface face that has all-0 bit + * (for comparison) + */ + +/* + * Forward declarations. + */ +static void start_vif __P((vifi_t vifi)); +static void stop_vif __P((vifi_t vifi)); +static void start_all_vifs __P((void)); + + +void +init_vifs() +{ + vifi_t vifi; + struct uvif *v; + int enabled_vifs; + + numvifs = 0; + vifs_down = FALSE; + + /* + * Configure the vifs based on the interface configuration of + * the kernel and the contents of the configuration file. + * (Open a UDP socket for ioctl use in the config procedures if + * the kernel can't handle IOCTL's on the MLD socket.) + */ +#ifdef IOCTL_OK_ON_RAW_SOCKET + udp_socket = mld6_socket; +#else + if ((udp_socket = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + log(LOG_ERR, errno, "UDP6 socket"); +#endif + + /* + * Clean up all vifs + */ + for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) { + memset(v, 0, sizeof(*v)); + v->uv_flags = 0; + v->uv_metric = DEFAULT_METRIC; + v->uv_admetric = 0; + v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT; + strncpy(v->uv_name, "", IFNAMSIZ); + v->uv_groups = (struct listaddr *)NULL; + v->uv_dvmrp_neighbors = (struct listaddr *)NULL; + NBRM_CLRALL(v->uv_nbrmap); + v->uv_querier = (struct listaddr *)NULL; + v->uv_prune_lifetime = 0; + v->uv_acl = (struct vif_acl *)NULL; + v->uv_leaf_timer = 0; + v->uv_addrs = (struct phaddr *)NULL; + v->uv_filter = (struct vif_filter *)NULL; + v->uv_pim_hello_timer = 0; + v->uv_gq_timer = 0; + v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL; + v->uv_local_pref = default_source_preference; + v->uv_local_metric = default_source_metric; + } + + log(LOG_INFO, 0, "Getting ifs from kernel"); + config_vifs_from_kernel(); + log(LOG_INFO, 0, "Getting ifs from %s", configfilename); + config_vifs_from_file(); + + /* + * Quit if there are fewer than two enabled vifs or there is a vif + * which has no link local address. + */ + enabled_vifs = 0; + phys_vif = -1; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) + continue; + if (v->uv_linklocal == NULL) + log(LOG_ERR, 0, + "there is no link-local address on vif#%d", vifi); + if (phys_vif == -1) { + struct phaddr *p; + + /* + * If this vif has a global address, set its id + * to phys_vif. + */ + for(p = v->uv_addrs; p; p = p->pa_next) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) { + phys_vif = vifi; + break; + } + } + } + enabled_vifs++; + } + + if (enabled_vifs < 2) + log(LOG_ERR, 0, "can't forward: %s", + enabled_vifs == 0 ? "no enabled ifs" : "only one enabled if"); + + memset(&if_nullset, 0, sizeof(if_nullset)); + k_init_pim(mld6_socket); /* Call to kernel to initiliaze structures */ + + start_all_vifs(); +} + + +static void +start_all_vifs() +{ + vifi_t vifi; + struct uvif *v; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + /* Start vif if not DISABLED or DOWN */ + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) { + if (v->uv_flags & VIFF_DISABLED) + log(LOG_INFO, 0, + "%s is DISABLED; if #%u out of service", + v->uv_name, vifi); + else + log(LOG_INFO, 0, + "%s is DOWN; if #%u out of service", + v->uv_name, vifi); + } + else + start_vif(vifi); + } +} + + + +/* + * stop all vifs + */ +void +stop_all_vifs() +{ + vifi_t vifi; + struct uvif *v; + + for (vifi = 0, v=uvifs; vifi < numvifs; ++vifi, ++v) { + if (!(v->uv_flags & VIFF_DOWN)) { + stop_vif(vifi); + } + } +} + + +/* + * Initialize the vif and add to the kernel. The vif can be either + * physical, tunnel (tunnels will be used in the future + * when this code becomes PIM multicast boarder router.) + */ +static void +start_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + u_long random_delay; + + v = &uvifs[vifi]; + /* Initialy no router on any vif */ + v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~VIFF_DOWN; + v->uv_pim_hello_timer = 1 + RANDOM() % PIM_TIMER_HELLO_PERIOD; + /* TODO: CHECK THE TIMERS!!!!! Set or reset? */ + v->uv_gq_timer = 0; + v->uv_pim_neighbors = (pim_nbr_entry_t *)NULL; + + /* Tell kernel to add, i.e. start this vif */ + k_add_vif(mld6_socket, vifi, &uvifs[vifi]); + log(LOG_INFO, 0, "%s comes up; if #%u now in service", v->uv_name, vifi); + + /* + * Join the PIM multicast group on the interface. + */ + k_join(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex); + + /* + * Join the ALL-ROUTERS multicast group on the interface. + * This allows mtrace requests to loop back if they are run + * on the multicast router. + */ + k_join(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex); + + /* + * Until neighbors are discovered, assume responsibility for sending + * periodic group membership queries to the subnet. Send the first + * query. + */ + v->uv_flags |= VIFF_QUERIER; + query_groups(v); + + /* + * To avoid synchronization among routers booting simultaneously, set + * the hello timer to a random value between 1 to PIM_TIMER_HELLO_PERIOD. + */ + random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1)); + v->uv_pim_hello_timer = random_delay; +} + + +/* + * Stop a vif (either physical interface or tunnel). + * If we are running only PIM we don't have tunnels. + */ +static void +stop_vif(vifi) + vifi_t vifi; +{ + struct uvif *v; + struct listaddr *a; + register pim_nbr_entry_t *n, *next; + struct vif_acl *acl; + + /* + * TODO: make sure that the kernel viftable is + * consistent with the daemon table + */ + v = &uvifs[vifi]; + k_leave(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex); + k_leave(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex); + /* + * Discard all group addresses. (No need to tell kernel; + * the k_del_vif() call will clean up kernel state.) + */ + while (v->uv_groups != NULL) { + a = v->uv_groups; + v->uv_groups = a->al_next; + free((char *)a); + } + + /* + * TODO: inform (eventually) the neighbors I am going down by sending + * PIM_HELLO with holdtime=0 so someone else should become a DR. + */ + + /* TODO: dummy! Implement it!! Any problems if don't use it? */ + delete_vif_from_mrt(vifi); + + /* + * Delete the interface from the kernel's vif structure. + */ + k_del_vif(mld6_socket, vifi); + v->uv_flags = (v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS ) + | VIFF_DOWN; + v->uv_pim_hello_timer = 0; + v->uv_gq_timer = 0; + for (n = v->uv_pim_neighbors; n != NULL; n = next) { + next = n->next; /* Free the space for each neighbour */ + free((char *)n); + } + v->uv_pim_neighbors = NULL; + + /* TODO: currently not used */ + /* The Access Control List (list with the scoped addresses) */ + while (v->uv_acl != NULL) { + acl = v->uv_acl; + v->uv_acl = acl->acl_next; + free((char *)acl); + } + + vifs_down = TRUE; + log(LOG_INFO, 0, + "%s goes down; if #%u out of service", v->uv_name, vifi); +} + +/* + * return the max global Ipv6 address of an UP and ENABLED interface + * other than the MIFF_REGISTER interface. +*/ +struct sockaddr_in6 * +max_global_address() +{ + vifi_t vifi; + struct uvif *v; + struct phaddr *p; + struct phaddr *pmax = NULL; + + for(vifi=0,v=uvifs;vifi< numvifs;++vifi,++v) + { + if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER)) + continue; + /* + * take first the max global address of the interface + * (without link local) => aliasing + */ + for(p=v->uv_addrs;p!=NULL;p=p->pa_next) + { + /* + * If this is the first global address, take it anyway. + */ + if (pmax == NULL) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + pmax = p; + } + else { + if (inet6_lessthan(&pmax->pa_addr, + &p->pa_addr) && + !IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + pmax=p; + } + } + } + + return(pmax ? &pmax->pa_addr : NULL); +} + +struct sockaddr_in6 * +uv_global(vifi) + vifi_t vifi; +{ + struct uvif *v = &uvifs[vifi]; + struct phaddr *p; + + for (p = v->uv_addrs; p; p = p->pa_next) { + if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) + return(&p->pa_addr); + } + + return(NULL); +} + +/* + * 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; + static int checking_vifs = 0; + + /* + * XXX: TODO: True only for DVMRP?? Check. + * If we get an error while checking, (e.g. two interfaces go down + * at once, and we decide to send a prune out one of the failed ones) + * then don't go into an infinite loop! + */ + if (checking_vifs) + return; + + vifs_down = FALSE; + checking_vifs = 1; + /* TODO: Check all potential interfaces!!! */ + /* Check the physical interfaces only */ + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & VIFF_DISABLED) + continue; + + strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ); + /* get the interface flags */ + if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) + log(LOG_ERR, errno, + "check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); + + if (v->uv_flags & VIFF_DOWN) { + if (ifr.ifr_flags & IFF_UP) { + start_vif(vifi); + } + else vifs_down = TRUE; + } + else { + if (!(ifr.ifr_flags & IFF_UP)) { + log(LOG_NOTICE, 0, + "%s has gone down; if #%u taken out of service", + v->uv_name, vifi); + stop_vif(vifi); + vifs_down = TRUE; + } + } + } + checking_vifs = 0; +} + + +/* + * If the source is directly connected to us, find the vif number for + * the corresponding physical interface (tunnels excluded). + * Local addresses are excluded. + * Return the vif number or NO_VIF if not found. + */ +vifi_t +find_vif_direct(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr)) + return(NO_VIF); + if (inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask)) + return(vifi); + } + } + return (NO_VIF); +} + + +/* + * Checks if src is local address. If "yes" return the vif index, + * otherwise return value is NO_VIF. + */ +vifi_t +local_address(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr)) + return(vifi); + } + } + /* Returning NO_VIF means not a local address */ + return (NO_VIF); +} + +/* + * If the source is directly connected, or is local address, + * find the vif number for the corresponding physical interface + * (tunnels excluded). + * Return the vif number or NO_VIF if not found. + */ +vifi_t +find_vif_direct_local(src) + struct sockaddr_in6 *src; +{ + vifi_t vifi; + register struct uvif *v; + register struct phaddr *p; + + for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { + if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL)) + continue; + for (p = v->uv_addrs; p; p = p->pa_next) { + if (inet6_equal(src, &p->pa_addr) || + inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask)) + return(vifi); + } + } + return (NO_VIF); +} diff --git a/usr.sbin/pim6dd/vif.h b/usr.sbin/pim6dd/vif.h new file mode 100644 index 0000000..8dd44fc --- /dev/null +++ b/usr.sbin/pim6dd/vif.h @@ -0,0 +1,292 @@ +/* + * Copyright (c) 1998 by the University of Southern California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation in source and binary forms for lawful + * purposes and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both + * the copyright notice and this permission notice appear in supporting + * documentation, and that any documentation, advertising materials, + * and other materials related to such distribution and use acknowledge + * that the software was developed by the University of Southern + * California and/or Information Sciences Institute. + * The name of the University of Southern California may not + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS + * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS + * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND + * NON-INFRINGEMENT. + * + * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, + * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, + * THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Other copyrights might apply to parts of this software and are so + * noted when applicable. + */ +/* + * Questions concerning this software should be directed to + * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) + * + * $Id: vif.h,v 1.2 1999/08/24 16:45:23 jinmei Exp $ + */ +/* + * Part of this program has been derived from mrouted. + * The mrouted program is covered by the license in the accompanying file + * named "LICENSE.mrouted". + * + * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of + * Leland Stanford Junior University. + * + * $FreeBSD$ + */ + + +/* + * Bitmap handling functions. + * These should be fast but generic. bytes can be slow to zero and compare, + * words are hard to make generic. Thus two sets of macros (yuk). + */ + +/* + * The VIFM_ functions should migrate out of <netinet/ip_mroute.h>, since + * the kernel no longer uses vifbitmaps. + */ +#ifndef VIFM_SET + +typedef u_int32 vifbitmap_t; + +#define VIFM_SET(n, m) ((m) |= (1 << (n))) +#define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) +#define VIFM_ISSET(n, m) ((m) & (1 << (n))) +#define VIFM_CLRALL(m) ((m) = 0x00000000) +#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) +#define VIFM_SAME(m1, m2) ((m1) == (m2)) +#endif +/* + * And <netinet/ip_mroute.h> was missing some required functions anyway + */ +#if !defined(__NetBSD__) +#define VIFM_SETALL(m) ((m) = ~0) +#endif +#define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n))) +#define VIFM_ISEMPTY(m) ((m) == 0) +#define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask)) +#define VIFM_SET_MASK(m, mask) ((m) |= (mask)) +#define VIFM_MERGE(m1, m2, result) ((result) = (m1) | (m2)) + +/* + * And <netinet6/ip6_mroute.h> was missing some required functions anyway + */ +extern if_set if_nullset; +#define IF_ISEMPTY(p) (memcmp((p), &if_nullset, sizeof(if_nullset)) == 0) +#define IF_CLR_MASK(p, mask) \ + {\ + int idx;\ + for (idx = 0; idx < sizeof(*(p))/sizeof(fd_mask); idx++) {\ + (p)->ifs_bits[idx] &= ~((mask)->ifs_bits[idx]);\ + }\ + } +#define IF_MERGE(p1, p2, result) \ + {\ + int idx;\ + for (idx = 0; idx < sizeof(*(p1))/sizeof(fd_mask); idx++) {\ + (result)->ifs_bits[idx] = (p1)->ifs_bits[idx]|(p2)->ifs_bits[idx]; \ + }\ + } +#define IF_SAME(p1, p2) (memcmp((p1),(p2),sizeof(*(p1))) == 0) + +/* Check whether I am the forwarder on some LAN */ +#define VIFM_FORWARDER(leaves, oifs) ((leaves) & (oifs)) + +/* + * Neighbor bitmaps are, for efficiency, implemented as a struct + * containing two variables of a native machine type. If you + * have a native type that's bigger than a long, define it below. + */ +#define NBRTYPE u_long +#define NBRBITS sizeof(NBRTYPE) * 8 + +typedef struct { + NBRTYPE hi; + NBRTYPE lo; +} nbrbitmap_t; +#define MAXNBRS 2 * NBRBITS + +#define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \ + ((m).hi |= (1 << (n - NBRBITS)))) +#define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \ + ((m).hi &= ~(1 << (n - NBRBITS)))) +#define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \ + ((m).hi & (1 << ((n) - NBRBITS)))) +#define NBRM_CLRALL(m) ((m).lo = (m).hi = 0) +#define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi) +#define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi)) +#define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0)) +#define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi)) +#define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi)) +#define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi)) +#define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi)) +#define NBRM_ISSETALLMASK(m, mask)\ + ((((m).lo & (mask).lo) == (mask).lo) && \ + (((m).hi & (mask).hi) == (mask).hi)) +/* + * This macro is TRUE if all the subordinates have been pruned, or if + * there are no subordinates on this vif. + * The arguments is the map of subordinates, the map of neighbors on the + * vif, and the map of received prunes. + */ +#define SUBS_ARE_PRUNED(sub, vifmask, prunes) \ + (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \ + ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi)) + +/* + * User level Virtual Interface structure + * + * A "virtual interface" is either a physical, multicast-capable interface + * (called a "phyint"), a virtual point-to-point link (called a "tunnel") + * or a "register vif" used by PIM. The register vif is used by the + * Designated Router (DR) to send encapsulated data packets to the + * Rendevous Point (RP) for a particular group. The data packets are + * encapsulated in PIM messages (IPPROTO_PIM = 103) and then unicast to + * the RP. + * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) + */ +struct uvif { + u_int uv_flags; /* VIFF_ flags defined below */ + u_char uv_metric; /* cost of this vif */ + u_char uv_admetric; /* advertised cost of this vif */ +#if 0 /* unused for IPv6? */ + u_char uv_threshold; /* min ttl required to forward on vif */ +#endif + u_int uv_rate_limit; /* rate limit on this vif */ + struct sockaddr_in6 uv_lcl_addr;/* local address of this vif */ + struct phaddr *uv_linklocal;/* link-local address of this vif */ +#if 0 + u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */ +#endif + struct sockaddr_in6 uv_dst_addr; /* destination for PIM messages */ + struct sockaddr_in6 uv_prefix; /* prefix (phyints only) */ + struct in6_addr uv_subnetmask; /* subnet mask (phyints only) */ + char uv_name[IFNAMSIZ]; /* interface name */ + u_int uv_ifindex; /* index of the interface */ + u_int uv_siteid; /* index of the site on the interface */ + struct listaddr *uv_groups; /* list of local groups (phyints only) */ + struct listaddr *uv_dvmrp_neighbors; /* list of neighboring routers */ + nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */ + struct listaddr *uv_querier; /* MLD querier on vif */ + int uv_prune_lifetime; /* Prune lifetime or 0 for default */ + struct vif_acl *uv_acl; /* access control list of groups */ + int uv_leaf_timer; /* time until this vif is considrd leaf */ + struct phaddr *uv_addrs; /* Additional addresses on this vif */ + struct vif_filter *uv_filter; /* Route filters on this vif */ + u_int16 uv_pim_hello_timer;/* timer for sending PIM hello msgs */ + u_int16 uv_gq_timer; /* Group Query timer */ + int uv_local_pref; /* default local preference for assert */ + int uv_local_metric; /* default local metric for assert */ + struct pim_nbr_entry *uv_pim_neighbors; /* list of PIM neighbor routers */ +}; + +/* TODO: define VIFF_KERNEL_FLAGS */ +#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL | VIFF_SRCRT) +#define VIFF_DOWN 0x000100 /* kernel state of interface */ +#define VIFF_DISABLED 0x000200 /* administratively disabled */ +#define VIFF_QUERIER 0x000400 /* I am the subnet's querier */ +#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */ +#define VIFF_LEAF 0x001000 /* all neighbors are leaves */ +#define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */ +#define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */ +#define VIFF_PASSIVE 0x008000 /* passive tunnel */ +#define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */ +#define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */ +#define VIFF_DR 0x040000 /* designated router */ +/* TODO: VIFF_NONBRS == VIFF_ONEWAY? */ +#define VIFF_NONBRS 0x080000 /* no neighbor on vif */ +#define VIFF_POINT_TO_POINT 0x100000 /* point-to-point link */ +#define VIFF_PIM_NBR 0x200000 /* PIM neighbor */ +#define VIFF_DVMRP_NBR 0x400000 /* DVMRP neighbor */ +#define VIFF_NOLISTENER 0x800000 /* no listener on the link */ + +struct phaddr { + struct phaddr *pa_next; + struct sockaddr_in6 pa_addr; /* extra address */ + struct sockaddr_in6 pa_prefix; /* prefix of the extra address */ + struct in6_addr pa_subnetmask; /* netmask */ +}; + +/* The Access Control List (list with scoped addresses) member */ +struct vif_acl { + struct vif_acl *acl_next; /* next acl member */ + u_int32 acl_addr; /* Group address */ + u_int32 acl_mask; /* Group addr. mask */ +}; + +struct vif_filter { + int vf_type; +#define VFT_ACCEPT 1 +#define VFT_DENY 2 + int vf_flags; +#define VFF_BIDIR 1 + struct vf_element *vf_filter; +}; + +struct vf_element { + struct vf_element *vfe_next; + struct sockaddr_in6 *vfe_addr; + struct in6_addr vfe_mask; + int vfe_flags; +#define VFEF_EXACT 0x0001 +}; + +struct listaddr { + struct listaddr *al_next; /* link to next addr, MUST BE FIRST */ + struct sockaddr_in6 al_addr; /* local group or neighbor address */ + u_long al_timer; /* for timing out group or neighbor */ + time_t al_ctime; /* entry creation time */ + union { + u_int32 alu_genid; /* generation id for neighbor */ + struct sockaddr_in6 alu_reporter;/* a host which reported membership */ + } al_alu; + u_char al_pv; /* router protocol version */ + u_char al_mv; /* router mrouted version */ + u_char al_old; /* time since heard old report: unnecessary for mld */ + u_char al_index; /* neighbor index */ + u_long al_timerid; /* timer for group membership */ + u_long al_query; /* timer for repeated leave query */ + u_int16 al_flags; /* flags related to this neighbor */ +}; +#define al_genid al_alu.alu_genid +#define al_reporter al_alu.alu_reporter + +#define NBRF_LEAF 0x0001 /* This neighbor is a leaf */ +#define NBRF_GENID 0x0100 /* I know this neighbor's genid */ +#define NBRF_WAITING 0x0200 /* Waiting for peering to come up */ +#define NBRF_ONEWAY 0x0400 /* One-way peering */ +#define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */ +#define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */ +#define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */ + +/* + * Don't peer with neighbors with any of these flags set + */ +#define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \ + NBRF_TOOMANYROUTES|NBRF_NOTPRUNING) + +#define NO_VIF ((vifi_t)MAXMIFS) /* An invalid vif index */ + + +/* + * Used to get the RPF neighbor and IIF info + * for a given source from the unicast routing table. + */ +struct rpfctl { + struct sockaddr_in6 source; /* the source for which we want iif and rpfnbr */ + struct sockaddr_in6 rpfneighbor;/* next hop towards the source */ + vifi_t iif; /* the incoming interface to reach the next hop */ +}; |