summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pim6dd
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>2000-01-28 05:10:56 +0000
committershin <shin@FreeBSD.org>2000-01-28 05:10:56 +0000
commit417b54f8df0382bc71e299bb3cf35e74b2579e6c (patch)
tree4d242bbd1d32138424f41ea1ee419cb9768308bf /usr.sbin/pim6dd
parentb223e32eeea5f93619a53d365ddc73c6a3c16bf4 (diff)
downloadFreeBSD-src-417b54f8df0382bc71e299bb3cf35e74b2579e6c.zip
FreeBSD-src-417b54f8df0382bc71e299bb3cf35e74b2579e6c.tar.gz
IPv6 multicast routing.
kernel IPv6 multicast routing support. pim6 dense mode daemon pim6 sparse mode daemon netstat support of IPv6 multicast routing statistics Merging to the current and testing with other existing multicast routers is done by Tatsuya Jinmei <jinmei@kame.net>, who writes and maintainances the base code in KAME distribution. Make world check and kernel build check was also successful.
Diffstat (limited to 'usr.sbin/pim6dd')
-rw-r--r--usr.sbin/pim6dd/LICENSE.mrouted49
-rw-r--r--usr.sbin/pim6dd/LICENSE.pimd48
-rw-r--r--usr.sbin/pim6dd/Makefile14
-rw-r--r--usr.sbin/pim6dd/VERSION2
-rw-r--r--usr.sbin/pim6dd/callout.c256
-rw-r--r--usr.sbin/pim6dd/config.c681
-rw-r--r--usr.sbin/pim6dd/debug.c500
-rw-r--r--usr.sbin/pim6dd/debug.h121
-rw-r--r--usr.sbin/pim6dd/defs.h597
-rw-r--r--usr.sbin/pim6dd/inet6.c288
-rw-r--r--usr.sbin/pim6dd/kern.c415
-rw-r--r--usr.sbin/pim6dd/main.c719
-rw-r--r--usr.sbin/pim6dd/mld6.c537
-rw-r--r--usr.sbin/pim6dd/mld6.h45
-rw-r--r--usr.sbin/pim6dd/mld6_proto.c532
-rw-r--r--usr.sbin/pim6dd/mrt.c803
-rw-r--r--usr.sbin/pim6dd/mrt.h228
-rw-r--r--usr.sbin/pim6dd/pathnames.h63
-rw-r--r--usr.sbin/pim6dd/pim6.c436
-rw-r--r--usr.sbin/pim6dd/pim6_proto.c1598
-rw-r--r--usr.sbin/pim6dd/pim6dd.8112
-rw-r--r--usr.sbin/pim6dd/pim6dd.conf.5157
-rw-r--r--usr.sbin/pim6dd/pimdd.h553
-rw-r--r--usr.sbin/pim6dd/route.c659
-rw-r--r--usr.sbin/pim6dd/routesock.c373
-rw-r--r--usr.sbin/pim6dd/timer.c322
-rw-r--r--usr.sbin/pim6dd/trace.c540
-rw-r--r--usr.sbin/pim6dd/trace.h204
-rw-r--r--usr.sbin/pim6dd/vers.c2
-rw-r--r--usr.sbin/pim6dd/vif.c528
-rw-r--r--usr.sbin/pim6dd/vif.h292
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(&sectime);
+ 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 */
+};
OpenPOWER on IntegriCloud