summaryrefslogtreecommitdiffstats
path: root/usr.sbin
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
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')
-rw-r--r--usr.sbin/Makefile2
-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
-rw-r--r--usr.sbin/pim6sd/BUGS.TODO126
-rw-r--r--usr.sbin/pim6sd/BUGS.V610
-rw-r--r--usr.sbin/pim6sd/LICENSE.mrouted49
-rw-r--r--usr.sbin/pim6sd/LICENSE.pim6dd32
-rw-r--r--usr.sbin/pim6sd/LICENSE.pim6sd47
-rw-r--r--usr.sbin/pim6sd/LICENSE.pimd48
-rw-r--r--usr.sbin/pim6sd/Makefile49
-rw-r--r--usr.sbin/pim6sd/README85
-rw-r--r--usr.sbin/pim6sd/README.first15
-rw-r--r--usr.sbin/pim6sd/callout.c316
-rw-r--r--usr.sbin/pim6sd/callout.h62
-rw-r--r--usr.sbin/pim6sd/cfparse.h55
-rw-r--r--usr.sbin/pim6sd/cfparse.y960
-rw-r--r--usr.sbin/pim6sd/cftoken.l657
-rw-r--r--usr.sbin/pim6sd/config.c1609
-rw-r--r--usr.sbin/pim6sd/config.h73
-rw-r--r--usr.sbin/pim6sd/crc.c97
-rw-r--r--usr.sbin/pim6sd/crc.h48
-rw-r--r--usr.sbin/pim6sd/debug.c910
-rw-r--r--usr.sbin/pim6sd/debug.h153
-rw-r--r--usr.sbin/pim6sd/defs.h82
-rw-r--r--usr.sbin/pim6sd/inet6.c283
-rw-r--r--usr.sbin/pim6sd/inet6.h76
-rw-r--r--usr.sbin/pim6sd/kern.c413
-rw-r--r--usr.sbin/pim6sd/kern.h78
-rw-r--r--usr.sbin/pim6sd/main.c767
-rw-r--r--usr.sbin/pim6sd/mld6.c555
-rw-r--r--usr.sbin/pim6sd/mld6.h90
-rw-r--r--usr.sbin/pim6sd/mld6_proto.c632
-rw-r--r--usr.sbin/pim6sd/mld6_proto.h72
-rw-r--r--usr.sbin/pim6sd/mrt.c1495
-rw-r--r--usr.sbin/pim6sd/mrt.h341
-rw-r--r--usr.sbin/pim6sd/mtrace6/Makefile86
-rw-r--r--usr.sbin/pim6sd/mtrace6/mtrace6.8115
-rw-r--r--usr.sbin/pim6sd/mtrace6/mtrace6.c661
-rw-r--r--usr.sbin/pim6sd/pathnames.h81
-rw-r--r--usr.sbin/pim6sd/pim6.c507
-rw-r--r--usr.sbin/pim6sd/pim6.h70
-rw-r--r--usr.sbin/pim6sd/pim6_proto.c4126
-rw-r--r--usr.sbin/pim6sd/pim6_proto.h103
-rw-r--r--usr.sbin/pim6sd/pim6sd.8149
-rw-r--r--usr.sbin/pim6sd/pim6sd.conf.5330
-rw-r--r--usr.sbin/pim6sd/pim6sd.conf.sample107
-rwxr-xr-xusr.sbin/pim6sd/pim6stat89
-rw-r--r--usr.sbin/pim6sd/pim6stat.192
-rw-r--r--usr.sbin/pim6sd/pimd.h527
-rw-r--r--usr.sbin/pim6sd/route.c1180
-rw-r--r--usr.sbin/pim6sd/route.h85
-rw-r--r--usr.sbin/pim6sd/routesock.c428
-rw-r--r--usr.sbin/pim6sd/routesock.h57
-rw-r--r--usr.sbin/pim6sd/rp.c1210
-rw-r--r--usr.sbin/pim6sd/rp.h123
-rw-r--r--usr.sbin/pim6sd/timer.c1286
-rw-r--r--usr.sbin/pim6sd/timer.h102
-rw-r--r--usr.sbin/pim6sd/trace.c559
-rw-r--r--usr.sbin/pim6sd/trace.h211
-rw-r--r--usr.sbin/pim6sd/var.h62
-rw-r--r--usr.sbin/pim6sd/vers.c2
-rw-r--r--usr.sbin/pim6sd/vif.c824
-rw-r--r--usr.sbin/pim6sd/vif.h277
-rw-r--r--usr.sbin/pim6sd/vmbuf.h49
93 files changed, 35459 insertions, 0 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 5e7433d..f2a1e55 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -59,6 +59,8 @@ SUBDIR= IPXrouted \
pccard \
pciconf \
periodic \
+ pim6dd \
+ pim6sd \
pkg_install \
pnpinfo \
portmap \
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 */
+};
diff --git a/usr.sbin/pim6sd/BUGS.TODO b/usr.sbin/pim6sd/BUGS.TODO
new file mode 100644
index 0000000..4a9e684
--- /dev/null
+++ b/usr.sbin/pim6sd/BUGS.TODO
@@ -0,0 +1,126 @@
+ $Id: BUGS.TODO,v 1.1.1.1 1999/08/08 23:30:57 itojun Exp $
+ $FreeBSD$
+
+THIS LIST IS FAR AWAY FROM BEING COMPLETE, so these are the few things
+that came up at the right moment to be written down.
+
+ * Experimental kernel MFC (*,G) related:
+ If the (S,G) iif or oifs are different from the (*,G) or (*,*,RP)
+ iifs/oifs, the resp. (*,G) or (*,*,RP) will delete and disallow
+ creating (*,G) MFC. Only after all MRT (S,G) are deleted, the
+ corresponding (*,G) or (*,*,RP) will create (*,G) MFC.
+
+ * Experimental kernel MFC (*,G) related:
+ Right now when the MFC (*,G) total datarate is above the SPT switch
+ threshold, the (*,G) MFC will be deleted, and any further cache miss
+ will result in (S,G) MFC (the problem is that we must do (S,G)
+ monitoring for eventually high datagate sources). Only after all
+ (S,G) MFCs expire, the daemon's MRT will stop creating (S,G) MFCs
+ (i.e. the next cache miss will result in (*,G) kernel MFC).
+ A better selection should be applied to sort out the higher
+ datarate sources, and at the same time to have (*,G)MFC as well.
+ For example, create few (S,G), and after that create the (*,G). If some
+ of the created (S,G) MFC entries have very low datarate, delete them.
+
+ * Use NetBSD's definition for IPADDR (netinet/in.h):
+#ifdef _KERNEL
+#define __IPADDR(x) ((u_int32_t) htonl((u_int32_t)(x)))
+#else
+#define __IPADDR(x) ((u_int32_t)(x))
+#endif
+
+
+ * The (S,G)RPbit in the DR for the sender and the (S,G)SPT in the
+ downstream router won't timeout and will refresh each other even
+ if the sender is not active:
+
+ S--DR-----------------R1------------RP
+ (S,G)RPbit (S,G)
+ iif toward S
+
+ * Check whether the kernel code sends CACHE_MISS and WRONG_IIF for
+ the LAN-scoped addresses
+
+ * If the RP for a group changes, the DR should cancel any PIM-register-stop
+ timers (XXX: not in the spec, but should be there)
+
+ * If a new interface is configured, include it automatically
+
+ * Don't create routing entries for local link scoped groups
+
+ * Implement adm. scoped filters
+
+ * Do precise check of the timer events to speed up the propagation of the
+ Cand-RP messages + Cand-BSR messages and the election of the BSR.
+
+ * Fix the bug for messing up the things when the receiver is on the
+ same host as the RP for the multicast group (probably was fixed with alpha6,
+ because I cannot reproduce it anymore)
+
+ * Do more precise error check for the received PIM messages. In most cases,
+ the whole message must be parsed completely before starting processing it.
+
+ * Clean up the debugging messages.
+
+ * Use Patricia tree to search the routing table
+ (There is a nice paper in Sigcomm '97 about fast routing tables
+ implementation, so need to check it as well)
+
+ * Do switch back to the Shared Tree by timing out the SPT if the rate
+ is too low (not in the spec, but Ahmed pointed out some complications if
+ this happens)
+
+ * Change all countdown timers to events timeout (callout.c)
+ (The current implementation is very unefficient if the routing table becomes
+ very large)
+
+ * Send immediately Join/Prune, instead of relying of Join/Prune timer = 0
+
+ * Fix the code allowing interface UP/DOWN without restarting pimd.
+
+ * Do more testings for SPT switch, Join/Prune, asserts, etc...
+
+ * Test the (*,*,RP) code (need PIM/DVMRP border router to do so)
+
+ * Test the RSRR (RSVP support) code
+
+ * Send Initial_Reply RSRR message if the interfaces detected by pimd change
+
+ * SNMP support
+
+===TODO by function name===
+igmp_proto.c:
+ * accept_group_report():
+ - add a leaf if DR or forwarder (currently only if DR)???
+ * accept_leave_message():
+ - send immediately PIM prune message if the last member has left
+
+main.c
+ * main():
+ - use a combination of time and hostid to initialize the random generator.
+ * restart():
+ - check the implementation
+
+pim_proto.c
+ * pim_register():
+ - IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM USER SPACE
+ AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE KERNEL.
+ * register_stop():
+ - REGISTER_STOP rate limiting
+
+route.c
+ * process_cache_miss()
+ - use negative cache.
+
+rp.c
+ * add_rp_grp_entry():
+ - FIX THE BUG when adding an RP for different prefix requires remapping
+ for some groups!!!
+ (Intentionally left, waiting to come up with an idea how to implement
+ it simple and efficient. If you configure all RPs to advertise the
+ same prefix, the bug won't "show up")
+
+================DONE=====================
+
+ * When receive PIM_REGISTER, check whether I am the chosen RP
+
diff --git a/usr.sbin/pim6sd/BUGS.V6 b/usr.sbin/pim6sd/BUGS.V6
new file mode 100644
index 0000000..cdc1903
--- /dev/null
+++ b/usr.sbin/pim6sd/BUGS.V6
@@ -0,0 +1,10 @@
+ $FreeBSD$
+
+Write your bugs reports here
+
+BSR and Cand_rp discovery (base mecanism ): fixed
+Encap/decap and unicast register send : fixed.
+Join/Prune messages: They are sent but the RP don't create the (*,G) entry: fixed.
+Check with the sockaddr6_any and sockaddr_d (not sure it works because of the scope..)
+Pb with filling the upstream neighbor:fixed
+to query groups at boot time not efficient because there is no RP
diff --git a/usr.sbin/pim6sd/LICENSE.mrouted b/usr.sbin/pim6sd/LICENSE.mrouted
new file mode 100644
index 0000000..9cf01b6
--- /dev/null
+++ b/usr.sbin/pim6sd/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/pim6sd/LICENSE.pim6dd b/usr.sbin/pim6sd/LICENSE.pim6dd
new file mode 100644
index 0000000..0055e6e
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.pim6dd
@@ -0,0 +1,32 @@
+/*
+ * 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$
+ */
+
+
diff --git a/usr.sbin/pim6sd/LICENSE.pim6sd b/usr.sbin/pim6sd/LICENSE.pim6sd
new file mode 100644
index 0000000..853f9e0
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.pim6sd
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ * $FreeBSD$
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
diff --git a/usr.sbin/pim6sd/LICENSE.pimd b/usr.sbin/pim6sd/LICENSE.pimd
new file mode 100644
index 0000000..96791e5
--- /dev/null
+++ b/usr.sbin/pim6sd/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:57 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/pim6sd/Makefile b/usr.sbin/pim6sd/Makefile
new file mode 100644
index 0000000..9cda3ce
--- /dev/null
+++ b/usr.sbin/pim6sd/Makefile
@@ -0,0 +1,49 @@
+# 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.
+# $FreeBSD$
+
+PROG= pim6sd
+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 rp.c crc.c trace.c\
+ cfparse.y cftoken.l
+SRCS+= y.tab.h
+y.tab.h: cfparse.y
+CLEANFILES+= lex.yy.c y.tab.h y.tab.c
+CFLAGS+= -g
+CFLAGS+=-DINET6 -DPIM -DIOCTL_OK_ON_RAW_SOCKET -I${.OBJDIR}
+CFLAGS+=-DHAVE_STDARG_H
+LDADD+= -ly -ll
+
+MAN1= pim6stat.1
+MAN5= pim6sd.conf.5
+MAN8= pim6sd.8
+
+afterinstall:
+ install ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${INSTALLFLAGS} ${SRCDIR}/pim6stat ${DESTDIR}${BINDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pim6sd/README b/usr.sbin/pim6sd/README
new file mode 100644
index 0000000..49dfc17
--- /dev/null
+++ b/usr.sbin/pim6sd/README
@@ -0,0 +1,85 @@
+ $Id: README,v 1.1.1.1 1999/08/08 23:30:57 itojun Exp $
+ $FreeBSD$
+
+WARNING! WARNING! WARNING!
+THIS RELEASE IS VERY ALPHA, SO PLEASE DO NOT REDISTRIBUTE AND
+DO NOT TRY IT OUTSIDE OF YOUR TESTBED.
+
+This is README for pimd, the PIM multicast daemon.
+PIM-SM version: 2
+Check ftp://catarina.usc.edu/pub/pim/pimd/ for lastest version.
+
+SUPPORTED PLATFORMS: FreeBSD-2.2.*, SunOS-4.1.3, Solaris-2.5.1 and 2.6,
+SGI, BSDI 3.0/3.1, NetBSD-1.3
+
+AVAILABLE PIM kernel patches: FreeBSD-2.2.1, FreeBSD-2.2.2, FreeBSD-2.2,5,
+ SunOS-4.1.3, SGI, BSDI-3.0, BSDI-3.1, NetBSD-1.3
+
+Linux: pimd compiles under Linux, and linux-2.1.103 seems to have PIM-SMv2
+kernel support, but I haven't tested whether the kernel patches really work.
+
+FAST START (read "fast explanation" :))
+
+1. Apply the PIM kernel patches, recompile, reboot
+
+2. Copy pimd.conf to /etc and edit as appropriate. Disable the interfaces
+you don't need. Note that you need at least 2 physical interfaces enabled.
+
+3. Edit Makefile by uncommenting the line(s) corresponding to your platform.
+
+4. Recompile pimd
+
+5. Run pimd as a root. It is highly recommended to run it in debug mode.
+Because there are many debug messages, you can specify only a subset of
+the messages to be printed out:
+
+usage: pimd [-c configfile] [-d [debug_level][,debug_level]]
+
+Valid debug levels: dvmrp_prunes,dvmrp_mrt,dvmrp_neighbors,dvmrp_timers,igmp_proto,igmp_timers,igmp_members,trace,timeout,pkt,interfaces,kernel,cache,rsrr,pim_hello,pim_register,pim_join_prune,pim_bootstrap,pim_asserts,pim_cand_rp,pim_routes,pim_timers,pim_rpf
+
+If you want to see all messages, use "pimd -d" only.
+
+6. Note that it takes of the order of 30 seconds to 1 minute until the
+Bootstrap router is elected and the RP-set distributed to the PIM routers,
+and without the RP-set in the routers the multicast packets cannot be
+forwarded.
+
+7. There are plenty of bugs, some of them known (check BUGS.TODO),
+some of them unknown, so your bug reports are more than welcome.
+
+
+Pavlin Ivanov Radoslavov
+pavlin@catarina.usc.edu
+
+ACKNOWLEDGEMENTS:
+
+ * The PIM kernel modifications and pimd itself were originally
+ written by Ahmed Helmy (ahelmy@catarina.usc.edu) as a summer intern in SGI.
+
+ * The "up to the March '97 I-D spec" + RSVP support pimd version was done
+ during my summer'97 intern in Sun Microsystems under Michael Speer's
+ supervision.
+
+ * BSDI 3.0/3.1 support + various improvements and bug reports
+ by Hitoshi Asaeda (asaeda@yamato.ibm.co.jp).
+
+ * Bug reports and SGI tests by Nidhi Bhaskar (nidhi@cho-oyu.engr.sgi.com).
+
+ * Bug reports and SunOS tests by Isabelle Girard (girardi@rc.bel.alcatel.be)
+ and Dirk Ooms (oomsd@rc.bel.alcatel.be)
+
+ * NetBSD-1.3 compilation support (both for pimd and the kernel mods) and
+ bug reports by Heiko W.Rupp <hwr@pilhuhn.de>
+
+ * Bug reports by Chirayu Shah (shahzad@torrentnet.com)
+
+ * A number of changes copied back from pimdd (PIM-DM) stand-alone
+ implementation by Kurt Windisch (kurtw@antc.uoregon.edu)
+
+ * Linux patches by "Jonathan Day" <jd9812@my-dejanews.com> and
+ Fred Griffoul <griffoul@ccrle.nec.de>
+
+ * Thanks to the FreeBSD team and particularly to the
+ freebsd-hackers mailing list participants for the help
+ with the real-time debugging of the FreeBSD kernel.
+
diff --git a/usr.sbin/pim6sd/README.first b/usr.sbin/pim6sd/README.first
new file mode 100644
index 0000000..b58efbc
--- /dev/null
+++ b/usr.sbin/pim6sd/README.first
@@ -0,0 +1,15 @@
+ $FreeBSD$
+
+WARNING WARNING WARNING:
+
+This is Pim Sparse Mode for IPv6. BUT IT HAS STILL SOME PROBLEMS !
+So , before trying to run it , take a look to the code.
+
+
+THIS VERSION NEEDS :
+
+lots of debugging work...
+AND : more debugging work :)
+
+
+Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
diff --git a/usr.sbin/pim6sd/callout.c b/usr.sbin/pim6sd/callout.c
new file mode 100644
index 0000000..52eda4f
--- /dev/null
+++ b/usr.sbin/pim6sd/callout.c
@@ -0,0 +1,316 @@
+/*
+ * 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
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "debug.h"
+#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 */
+};
+
+#if 0
+#define CALLOUT_DEBUG 1
+#define CALLOUT_DEBUG2 1
+#endif /* 0 */
+
+#ifdef CALLOUT_DEBUG2
+static void print_Q __P((void));
+#else
+#define print_Q()
+#endif
+
+void
+callout_init()
+{
+ 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;
+ print_Q();
+ return node->id;
+ }
+ else
+ {
+ /* keep moving */
+
+ delay -= ptr->time;
+ node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+ prev->next = node;
+ }
+ print_Q();
+ 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
+ */
+
+ print_Q();
+ 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);
+ print_Q();
+ return;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ print_Q();
+}
+
+#ifdef CALLOUT_DEBUG2
+/*
+ * 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_DEBUG2 */
diff --git a/usr.sbin/pim6sd/callout.h b/usr.sbin/pim6sd/callout.h
new file mode 100644
index 0000000..61c4de3
--- /dev/null
+++ b/usr.sbin/pim6sd/callout.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+
+#ifndef CALLOUT_H
+#define CALLOUT_H
+
+#include "defs.h"
+
+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));
+
+#endif
diff --git a/usr.sbin/pim6sd/cfparse.h b/usr.sbin/pim6sd/cfparse.h
new file mode 100644
index 0000000..256b3f1
--- /dev/null
+++ b/usr.sbin/pim6sd/cfparse.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#if defined(YIPS_DEBUG)
+# define DP(str) YIPSDEBUG(DEBUG_CONF, cfdebug_print(str, yytext, yyleng))
+# define YYD_ECHO \
+ { YIPSDEBUG(DEBUG_CONF, printf("<%d>", yy_start); ECHO ; printf("\n");); }
+# define YIPSDP(cmd) YIPSDEBUG(DEBUG_CONF, cmd)
+# define PLOG printf
+#else
+# define DP(str)
+# define YYD_ECHO
+# define YIPSDP(cmd)
+# define PLOG(cmd)
+#endif /* defined(YIPS_DEBUG) */
+
+/* cfparse.y */
+extern void cf_init __P((int, int));
+#ifdef notyet
+extern int re_cfparse __P((void));
+#endif
+extern int cf_post_config __P((void));
+extern int yyparse __P((void));
+
+/* cftoken.l */
+extern void yyerror __P((char *, ...));
+extern void yywarn __P((char *, ...));
+extern int cfparse __P((int, int));
diff --git a/usr.sbin/pim6sd/cfparse.y b/usr.sbin/pim6sd/cfparse.y
new file mode 100644
index 0000000..3bc4341
--- /dev/null
+++ b/usr.sbin/pim6sd/cfparse.y
@@ -0,0 +1,960 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+%{
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <syslog.h>
+
+#include "defs.h"
+#include "rp.h"
+#include "vif.h"
+
+#include "var.h"
+#include "vmbuf.h"
+#include "cfparse.h"
+#include "debug.h"
+#include "pimd.h"
+#include "timer.h"
+#include "inet6.h"
+
+#define set_param(var,val,p) \
+ do {\
+ if ((var) != -1) {\
+ yywarn("%s doubly defined(ignore %d)", (p), (val));\
+ }\
+ else {\
+ (var) = val;\
+ }\
+ } while(0)
+
+struct in6_prefix {
+ struct in6_addr paddr;
+ int plen;
+};
+
+struct attr_list {
+ struct attr_list *next;
+ int type;
+ union {
+ unsigned int flags;
+ double number;
+ struct in6_prefix prefix;
+ }attru;
+};
+
+enum {IFA_FLAG, IFA_PREFERENCE, IFA_METRIC, RPA_PRIORITY, RPA_TIME,
+ BSRA_PRIORITY, BSRA_TIME, IN6_PREFIX, THRESA_RATE, THRESA_INTERVAL};
+
+static int strict; /* flag if the grammer check is strict */
+static struct attr_list *rp_attr, *bsr_attr, *grp_prefix, *regthres_attr,
+ *datathres_attr;
+static char *cand_rp_ifname, *cand_bsr_ifname;
+static int srcmetric, srcpref, helloperiod, jpperiod, granularity,
+ datatimo, regsuptimo, probetime, asserttimo;
+static double helloperiod_coef, jpperiod_coef;
+
+static int debugonly;
+%}
+
+%union {
+ unsigned long num;
+ double fl;
+ vchar_t val;
+ struct attr_list *attr;
+}
+
+%token EOS
+%token LOGGING LOGLEV NOLOGLEV
+%token YES NO
+%token REVERSELOOKUP
+%token PHYINT IFNAME DISABLE PREFERENCE METRIC NOLISTENER
+%token GRPPFX
+%token CANDRP CANDBSR TIME PRIORITY
+%token NUMBER STRING SLASH
+%token REGTHRES DATATHRES RATE INTERVAL
+%token SRCMETRIC SRCPREF HELLOPERIOD GRANULARITY JPPERIOD
+%token DATATIME REGSUPTIME PROBETIME ASSERTTIME
+
+%type <num> LOGLEV NOLOGLEV
+%type <fl> NUMBER
+%type <val> STRING IFNAME
+%type <attr> if_attributes rp_substatement rp_attributes
+%type <attr> bsr_substatement bsr_attributes thres_attributes
+
+%%
+statements:
+ /* empty */
+ | statements statement
+ ;
+
+statement:
+ logging_statement
+ | reverselookup_statement
+ | phyint_statement
+ | candrp_statement
+ | candbsr_statement
+ | grppfx_statement
+ | regthres_statement
+ | datathres_statement
+ | param_statement
+ ;
+
+/* logging */
+logging_statement:
+ LOGGING log_specs EOS
+ ;
+
+log_specs:
+ /* empty */
+ | log_specs LOGLEV {debug |= $2;}
+ | log_specs NOLOGLEV {debug &= ~($2);}
+ ;
+
+/* reverselookup */
+reverselookup_statement:
+ REVERSELOOKUP YES EOS { numerichost = FALSE; }
+ | REVERSELOOKUP NO EOS { numerichost = TRUE; }
+ ;
+
+/* phyint */
+phyint_statement:
+ PHYINT IFNAME if_attributes EOS {
+ struct uvif *v;
+
+ v = find_vif($2.v);
+ free($2.v); /* XXX */
+ if (v == NULL) {
+ yywarn("unknown interface: %s", $2.v);
+ free_attr_list($3);
+ if (strict)
+ return(-1);
+ }
+ else {
+ struct attr_list *p;
+
+ for (p = (struct attr_list *)v->config_attr;
+ p && p->next; p = p->next)
+ ;
+ if (p)
+ p->next = (void *)$3;
+ else
+ v->config_attr = (void *)$3;
+ }
+ }
+ ;
+
+if_attributes:
+ { $$ = NULL; }
+ | if_attributes DISABLE
+ {
+ if (($$ = add_attribute_flag($1, IFA_FLAG,
+ VIFF_DISABLED)) == NULL)
+ return(-1);
+ }
+ | if_attributes NOLISTENER
+ {
+ if (($$ = add_attribute_flag($1, IFA_FLAG,
+ VIFF_NOLISTENER)) == NULL)
+ return(-1);
+ }
+ | if_attributes PREFERENCE NUMBER
+ {
+ if (($$ = add_attribute_num($1, IFA_PREFERENCE, $3))
+ == NULL)
+ return(-1);
+ }
+ | if_attributes METRIC NUMBER
+ {
+ if (($$ = add_attribute_num($1, IFA_METRIC, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* cand_rp */
+candrp_statement:
+ CANDRP rp_substatement EOS {
+ if (cand_rp_flag == TRUE) {
+ yywarn("cand_rp doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else {
+ cand_rp_flag = TRUE;
+ rp_attr = $2;
+ }
+ }
+ ;
+/* XXX: intermediate rule to avoid shift-reduce conflict */
+rp_substatement:
+ IFNAME rp_attributes
+ {
+ if (cand_rp_ifname) {
+ yywarn("ifname for cand_rp doubly defined");
+ if (strict)
+ return(-1);
+ }
+ else
+ cand_rp_ifname = $1.v;
+ $$ = $2;
+ }
+ | rp_attributes
+ ;
+rp_attributes:
+ { $$ = NULL; }
+ | rp_attributes PRIORITY NUMBER
+ {
+ if (($$ = add_attribute_num($1, RPA_PRIORITY, $3))
+ == NULL)
+ return(-1);
+ }
+ | rp_attributes TIME NUMBER
+ {
+ if (($$ = add_attribute_num($1, RPA_TIME, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* cand_bootstrap_router */
+candbsr_statement:
+ CANDBSR bsr_substatement EOS {
+ if (cand_bsr_flag == TRUE) {
+ yywarn("cand_bsr doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else {
+ cand_bsr_flag = TRUE;
+ bsr_attr = $2;
+ }
+ }
+ ;
+/* XXX: intermediate rule to avoid shift-reduce conflict */
+bsr_substatement:
+ IFNAME bsr_attributes
+ {
+ if (cand_bsr_ifname) {
+ yywarn("ifname for cand_bsr doubly defined");
+ if (strict)
+ return(-1);
+ }
+ else
+ cand_bsr_ifname = $1.v;
+ $$ = $2;
+ }
+ | bsr_attributes
+ ;
+
+bsr_attributes:
+ { $$ = NULL; }
+ | bsr_attributes PRIORITY NUMBER
+ {
+ if (($$ = add_attribute_num($1, BSRA_PRIORITY, $3))
+ == NULL)
+ return(-1);
+ }
+ | bsr_attributes TIME NUMBER
+ {
+ if (($$ = add_attribute_num($1, BSRA_TIME, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* group_prefix <group-addr>/<prefix_len> */
+grppfx_statement:
+ GRPPFX STRING SLASH NUMBER EOS {
+ struct in6_prefix prefix;
+ int prefixok = 1;
+
+ if (inet_pton(AF_INET6, $2.v, &prefix.paddr) != 1) {
+ yywarn("invalid IPv6 address: %s (ignored)", $2);
+ prefixok = 0;
+ }
+ free($2.v); /* XXX: which was allocated dynamically */
+
+ prefix.plen = $4;
+ if (prefix.plen < 0 || prefix.plen > 128) {
+ yywarn("invalid prefix length: %d (ignored)",
+ prefix.plen);
+ prefixok = 0;
+ }
+
+ if (prefixok) {
+ struct attr_list *new;
+
+ if ((new = malloc(sizeof(*new))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset(new, 0, sizeof(*new));
+
+ new->type = IN6_PREFIX;
+ new->attru.prefix = prefix;
+ new->next = grp_prefix;
+
+ grp_prefix = new;
+ }
+ }
+ ;
+
+/*
+ * switch_register_threshold [rate <number> interval <number>]
+ * Operation: reads and assigns the switch to the spt threshold
+ * due to registers for the router, if used as RP.
+ * Maybe extended to support different thresholds for different
+ * groups(prefixes).
+ */
+regthres_statement:
+ REGTHRES thres_attributes EOS {
+ if (regthres_attr) {
+ yywarn("switch_register_threshold doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else
+ regthres_attr = $2;
+ }
+ ;
+
+thres_attributes:
+ { $$ = NULL; }
+ | thres_attributes RATE NUMBER
+ {
+ if (($$ = add_attribute_num($1, THRESA_RATE, $3))
+ == NULL)
+ return(-1);
+ }
+ | thres_attributes INTERVAL NUMBER
+ {
+ if (($$ = add_attribute_num($1, THRESA_INTERVAL, $3))
+ == NULL)
+ return(-1);
+ }
+
+/*
+ * switch_data_threshold [rate <number> interval <number>]
+ * Operation: reads and assigns the switch to the spt threshold due to
+ * data packets, if used as DR.
+ */
+datathres_statement:
+ DATATHRES thres_attributes EOS {
+ if (datathres_attr) {
+ yywarn("switch_data_threshold doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else
+ datathres_attr = $2;
+ }
+ ;
+
+param_statement:
+ SRCMETRIC NUMBER EOS
+ {
+ set_param(srcmetric, $2, "default_source_metric");
+ }
+ | SRCPREF NUMBER EOS
+ {
+ set_param(srcpref, $2, "default_source_preference");
+ }
+ | HELLOPERIOD NUMBER EOS
+ {
+ set_param(helloperiod, $2, "hello_period");
+ }
+ | HELLOPERIOD NUMBER NUMBER EOS
+ {
+ set_param(helloperiod, $2, "hello_period");
+ set_param(helloperiod_coef, $3, "hello_period(coef)");
+ }
+ | JPPERIOD NUMBER EOS
+ {
+ set_param(jpperiod, $2, "join_prune_period");
+ }
+ | JPPERIOD NUMBER NUMBER EOS
+ {
+ set_param(jpperiod, $2, "join_prune_period");
+ set_param(jpperiod_coef, $3, "join_prune_period(coef)");
+ }
+ | GRANULARITY NUMBER EOS
+ {
+ set_param(granularity, $2, "granularity");
+ }
+ | REGSUPTIME NUMBER EOS
+ {
+ set_param(regsuptimo, $2, "register_suppression_timeout");
+ }
+ | PROBETIME NUMBER EOS
+ {
+ set_param(probetime, $2, "probe_time");
+ }
+ | ASSERTTIME NUMBER EOS
+ {
+ set_param(asserttimo, $2, "assert_timeout");
+ }
+ ;
+%%
+
+static struct attr_list *
+add_attribute_flag(list, type, flag)
+ struct attr_list *list;
+ int type;
+ unsigned int flag;
+{
+ struct attr_list *p;
+
+ if ((p = malloc(sizeof(*p))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset((void *)p, 0, sizeof(*p));
+ p->type = type;
+ p->attru.flags = flag;
+ p->next = list;
+
+ return(p);
+}
+
+/* XXX: too many dup code... */
+static struct attr_list *
+add_attribute_num(list, type, num)
+ struct attr_list *list;
+ int type;
+ double num;
+{
+ struct attr_list *p;
+
+ if ((p = malloc(sizeof(*p))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset((void *)p, 0, sizeof(*p));
+ p->type = type;
+ p->attru.number = num;
+ p->next = list;
+
+ return(p);
+}
+
+static void
+free_attr_list(list)
+ struct attr_list *list;
+{
+ struct attr_list *p, *next;
+
+ for(p = list; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+}
+
+int
+param_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ /* at first, set the default values to all the undefined variables */
+ if (srcmetric == -1) srcmetric = DEFAULT_LOCAL_METRIC;
+ if (srcpref == -1) srcpref = DEFAULT_LOCAL_PREF;
+ if (helloperiod == -1) helloperiod = PIM_TIMER_HELLO_PERIOD;
+ if (helloperiod_coef == -1) helloperiod_coef = 3.5;
+ if (jpperiod == -1) jpperiod = PIM_JOIN_PRUNE_PERIOD;
+ if (jpperiod_coef == -1) jpperiod_coef = 3.5;
+ if (granularity == -1) granularity = DEFAULT_TIMER_INTERVAL;
+ if (datatimo == -1) datatimo = PIM_DATA_TIMEOUT;
+ if (regsuptimo == -1) regsuptimo = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ if (probetime == -1) probetime = PIM_REGISTER_PROBE_TIME;
+ if (asserttimo == -1) asserttimo = PIM_ASSERT_TIMEOUT;
+
+ /* set protocol parameters using the configuration variables */
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) {
+ v->uv_local_metric = srcmetric;
+ v->uv_local_pref = srcpref;
+ }
+ pim_hello_period = helloperiod;
+ pim_hello_holdtime = helloperiod * helloperiod_coef;
+ pim_join_prune_period = jpperiod;
+ pim_join_prune_holdtime = jpperiod * jpperiod_coef;
+ timer_interval = granularity;
+ pim_data_timeout = datatimo;
+ pim_register_suppression_timeout = regsuptimo;
+ pim_register_probe_time = probetime;
+ pim_assert_timeout = asserttimo;
+
+ IF_DEBUG(DEBUG_PIM_HELLO) {
+ log(LOG_DEBUG, 0, "pim_hello_period set to: %u",
+ pim_hello_period);
+ log(LOG_DEBUG, 0, "pim_hello_holdtime set to: %u",
+ pim_hello_holdtime);
+ }
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) {
+ log(LOG_DEBUG,0 , "pim_join_prune_period set to: %u",
+ pim_join_prune_period);
+ log(LOG_DEBUG, 0, "pim_join_prune_holdtime set to: %u",
+ pim_join_prune_holdtime);
+ }
+
+ return(0);
+}
+
+int
+phyint_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+ struct attr_list *al;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) {
+ for (al = (struct attr_list *)v->config_attr; al; al = al->next) {
+ switch(al->type) {
+ case IFA_FLAG:
+ v->uv_flags |= al->attru.flags;
+ break;
+ case IFA_PREFERENCE:
+ if (al->attru.number < 1 ||
+ al->attru.number > 255)
+ yywarn("invalid phyint preference(%d)",
+ (int)al->attru.number);
+ else {
+ v->uv_local_pref = al->attru.number;
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "default localpref for %s "
+ "is %d",
+ v->uv_name,
+ v->uv_local_pref);
+ }
+ break;
+ case IFA_METRIC:
+ if (al->attru.number < 1 ||
+ al->attru.number > 1024)
+ yywarn("invalid metric(%d)",
+ al->attru.number);
+ else {
+ v->uv_metric = al->attru.number;
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "default local metric for %s "
+ "is %d",
+ v->uv_name,
+ v->uv_metric);
+ }
+ break;
+ }
+ }
+ }
+
+ return(0);
+}
+
+int
+rp_config()
+{
+ struct sockaddr_in6 *sa6_rp = NULL;
+ struct attr_list *al;
+ u_int8 *data_ptr;
+
+ /* initialization by default values */
+ my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ my_cand_rp_priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+
+ if (cand_rp_ifname) {
+ sa6_rp = local_iface(cand_rp_ifname);
+ if (!sa6_rp)
+ log(LOG_WARNING, 0,
+ "cand_rp '%s' is not configured. "
+ "take the max local address the router..",
+ cand_rp_ifname);
+ }
+
+ for (al = rp_attr; al; al = al->next) {
+ switch(al->type) {
+ case RPA_PRIORITY:
+ if (al->attru.number < 0)
+ my_cand_rp_priority =
+ PIM_DEFAULT_CAND_RP_PRIORITY;
+ else
+ my_cand_rp_priority = al->attru.number;
+ break;
+ case RPA_TIME:
+ if (al->attru.number < 10)
+ my_cand_rp_adv_period = 10;
+ else if (al->attru.number > PIM_DEFAULT_CAND_RP_ADV_PERIOD)
+ my_cand_rp_adv_period =
+ PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ else
+ my_cand_rp_adv_period = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for RP", al->type);
+ break;
+ }
+ }
+
+ if (!sa6_rp)
+ sa6_rp = max_global_address(); /* this MUST suceed */
+ my_cand_rp_address = *sa6_rp;
+
+ /*
+ * initialize related parameters
+ */
+
+ /*
+ * Note that sizeof(pim6_enocd_uni_addr_t) might be larger than
+ * the length of the Encoded-Unicast-address field(18 byte) due to
+ * some padding put in the compiler. However, it doesn't matter
+ * since we use the space just as a buffer(i.e not as the message).
+ */
+ cand_rp_adv_message.buffer = (u_int8 *)malloc(4 +
+ sizeof(pim6_encod_uni_addr_t) +
+ 255*sizeof(pim6_encod_grp_addr_t));
+ if(cand_rp_adv_message.buffer == NULL)
+ log(LOG_ERR, 0, "Candrpadv Buffer allocation");
+
+ cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer;
+
+ /*
+ * By default, if no group_prefix configured, then prefix_cnt == 0
+ * implies group_prefix = ff00::/8 and masklen = 8.
+ */
+ *cand_rp_adv_message.prefix_cnt_ptr = 0;
+ cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer;
+
+ /* TODO: XXX: HARDCODING!!! */
+ cand_rp_adv_message.insert_data_ptr += (4 + 18);
+ cand_rp_adv_message.message_size =
+ cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+
+ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period;
+
+ /* TODO: HARDCODING! */
+ data_ptr = cand_rp_adv_message.buffer + 1; /* WARNING */
+ PUT_BYTE(my_cand_rp_priority,data_ptr);
+ PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr);
+ PUT_EUADDR6(my_cand_rp_address.sin6_addr,data_ptr);
+ IF_DEBUG(DEBUG_PIM_CAND_RP) {
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP address is : %s",
+ inet6_fmt(&my_cand_rp_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP priority is : %u",my_cand_rp_priority);
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP advertisement period is : %u sec.",
+ my_cand_rp_adv_period);
+ }
+
+ return(0);
+}
+
+int
+bsr_config()
+{
+ struct sockaddr_in6 *sa6_bsr = NULL;
+ struct attr_list *al;
+
+ /* initialization by default values */
+ my_bsr_period = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ my_bsr_priority = PIM_DEFAULT_BSR_PRIORITY;
+
+ if (cand_bsr_ifname) {
+ sa6_bsr = local_iface(cand_bsr_ifname);
+ if (!sa6_bsr)
+ log(LOG_WARNING, 0,
+ "bsr '%s' is not configured. "
+ "take the max local address the router..",
+ cand_bsr_ifname);
+ }
+
+ for (al = bsr_attr; al; al = al->next) {
+ switch(al->type) {
+ case BSRA_PRIORITY:
+ if (al->attru.number >= 0)
+ my_bsr_priority = al->attru.number;
+ break;
+ case BSRA_TIME:
+ if (al->attru.number < 10)
+ my_bsr_period = 10;
+ else if (al->attru.number > PIM_DEFAULT_BOOTSTRAP_PERIOD)
+ my_bsr_period =
+ PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ else
+ my_bsr_period = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for BSR", al->type);
+ break;
+ }
+ }
+
+ if (!sa6_bsr)
+ sa6_bsr = max_global_address(); /* this MUST suceed */
+ my_bsr_address = *sa6_bsr;
+
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP) {
+ log(LOG_DEBUG, 0,
+ "Local BSR address: %s",
+ inet6_fmt(&my_bsr_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local BSR priority : %u",my_bsr_priority);
+ log(LOG_DEBUG,0,
+ "Local BSR period is : %u sec.",
+ my_bsr_period);
+ }
+
+ return(0);
+}
+
+int
+grp_prefix_config()
+{
+ struct attr_list *pl;
+
+ if (cand_rp_flag != TRUE) {
+ log(LOG_WARNING, 0,
+ "group_prefix was specified without cand_rp(ignored)");
+ return(0);
+ }
+
+ for (pl = grp_prefix; pl; pl = pl->next) {
+ if (!IN6_IS_ADDR_MULTICAST(&pl->attru.prefix.paddr)) {
+ log(LOG_WARNING, 0,
+ "Config error: %s is not a mulicast address(ignored)",
+ inet6_fmt(&pl->attru.prefix.paddr));
+ continue;
+ }
+
+ if (!(~(*cand_rp_adv_message.prefix_cnt_ptr))) {
+ log(LOG_WARNING, 0,
+ "Too many group_prefix configured. Truncating...");
+ break;
+ }
+
+ /* validation for plen has almost done */
+ if (pl->attru.prefix.plen < PIM_GROUP_PREFIX_DEFAULT_MASKLEN)
+ pl->attru.prefix.plen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+
+ PUT_EGADDR6(pl->attru.prefix.paddr,
+ (u_int8)pl->attru.prefix.plen, 0,
+ cand_rp_adv_message.insert_data_ptr);
+ (*cand_rp_adv_message.prefix_cnt_ptr)++;
+ }
+
+ /* finally, adjust the data size */
+ cand_rp_adv_message.message_size =
+ cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+
+ return(0);
+}
+
+int
+regthres_config()
+{
+ struct attr_list *al;
+ int rate = -1;
+ int interval = -1;
+
+ if (cand_rp_flag != TRUE) {
+ log(LOG_WARNING, 0,
+ "register_threshold was specified without cand_rp");
+ }
+
+ for (al = regthres_attr; al; al = al->next) {
+ switch(al->type) {
+ case THRESA_RATE:
+ if (al->attru.number < 0)
+ yywarn("invalid regthres rate: %d(ignored)",
+ al->attru.number);
+ else if (rate != -1)
+ yywarn("regthres rate is doubly defined(ignored)");
+ else
+ rate = al->attru.number;
+ break;
+ case THRESA_INTERVAL:
+ if (al->attru.number < 0)
+ yywarn("invalid regthres interval: %d(ignored)",
+ al->attru.number);
+ else if (interval != -1)
+ yywarn("regthres interval is doubly defined(ignored)");
+ else
+ interval = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for regthres", al->type);
+ break;
+ }
+ }
+
+ /* set default values if not specified */
+ if (rate == -1)
+ rate = PIM_DEFAULT_REG_RATE;
+ if (interval == -1)
+ interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+
+ pim_reg_rate_bytes = (rate * interval ) /10;
+ pim_reg_rate_check_interval = interval;
+
+ return(0);
+}
+
+int
+datathres_config()
+{
+ struct attr_list *al;
+ int rate = -1;
+ int interval = -1;
+
+ for (al = datathres_attr; al; al = al->next) {
+ switch(al->type) {
+ case THRESA_RATE:
+ if (al->attru.number < 0)
+ yywarn("invalid datathres rate: %d(ignored)",
+ al->attru.number);
+ else if (rate != -1)
+ yywarn("datathres rate is doubly defined(ignored)");
+ else
+ rate = al->attru.number;
+ break;
+ case THRESA_INTERVAL:
+ if (al->attru.number < 0)
+ yywarn("invalid datathres interval: %d(ignored)",
+ al->attru.number);
+ else if (interval != -1)
+ yywarn("datathres interval is doubly defined(ignored)");
+ else
+ interval = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for datathres", al->type);
+ break;
+ }
+ }
+
+ /* set default values if not specified */
+ if (rate == -1)
+ rate = PIM_DEFAULT_DATA_RATE;
+ if (interval == -1)
+ interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+
+ pim_data_rate_bytes = (rate * interval ) /10;
+ pim_data_rate_check_interval = interval;
+
+ return(0);
+}
+
+int
+cf_post_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ if (debugonly)
+ goto cleanup;
+
+ param_config(); /* must be called before phyint_conifg() */
+
+ phyint_config();
+
+ if (cand_bsr_flag == TRUE)
+ bsr_config();
+
+ if (cand_rp_flag == TRUE)
+ rp_config();
+
+ if (grp_prefix) /* this must be called after rp_config() */
+ grp_prefix_config();
+
+ if (cand_rp_flag == TRUE)
+ regthres_config();
+
+ datathres_config();
+
+ IF_DEBUG(DEBUG_SWITCH) {
+ log(LOG_DEBUG, 0, "reg_rate_limit set to %u (bits/s)",
+ pim_reg_rate_bytes);
+ log(LOG_DEBUG, 0, "reg_rate_interval set to %u s.",
+ pim_reg_rate_check_interval);
+ log(LOG_DEBUG, 0, "data_rate_limit set to %u (bits/s)",
+ pim_data_rate_bytes);
+ log(LOG_DEBUG, 0, "data_rate_interval set to %u s.",
+ pim_data_rate_check_interval);
+ }
+
+ cleanup:
+ /* cleanup temporary variables */
+ if (cand_rp_ifname) free(cand_rp_ifname);
+ if (cand_bsr_ifname) free(cand_bsr_ifname);
+ if (rp_attr) free_attr_list(rp_attr);
+ if (bsr_attr) free_attr_list(bsr_attr);
+ if (grp_prefix) free_attr_list(grp_prefix);
+ if (regthres_attr) free_attr_list(regthres_attr);
+ if (datathres_attr) free_attr_list(datathres_attr);
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v)
+ free_attr_list((struct attr_list *)v->config_attr);
+
+ return(0);
+}
+
+/* initialize all the temporary variables */
+void
+cf_init(s, d)
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ strict = s;
+ debugonly = d;
+
+ debug = 0;
+
+ rp_attr = bsr_attr = grp_prefix = regthres_attr = datathres_attr = NULL;
+
+ cand_rp_ifname = cand_bsr_ifname = NULL;
+
+ srcmetric = srcpref = helloperiod = jpperiod = jpperiod_coef
+ = granularity = datatimo = regsuptimo = probetime
+ = asserttimo = -1;
+ helloperiod_coef = jpperiod_coef = -1;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v)
+ v->config_attr = NULL;
+}
diff --git a/usr.sbin/pim6sd/cftoken.l b/usr.sbin/pim6sd/cftoken.l
new file mode 100644
index 0000000..09725d3
--- /dev/null
+++ b/usr.sbin/pim6sd/cftoken.l
@@ -0,0 +1,657 @@
+%{
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "var.h"
+#include "vmbuf.h"
+#include "debug.h"
+#include "cfparse.h"
+#include "y.tab.h"
+
+static int yyerrorcount = 0;
+int lineno = 1;
+int yy_first_time = 1;
+
+extern char configfilename[];
+
+static void cfdebug_print __P((char *, char *, int));
+%}
+
+/* common seciton */
+nl \n
+ws [ \t]+
+comment \#.*
+semi \;
+string [a-zA-Z0-9:\._][a-zA-Z0-9:\._]*
+digit [0-9]
+integer {digit}+
+number {integer}|({digit}*\.{integer})
+hexdigit [0-9A-Fa-f]
+hexpair {hexdigit}{hexdigit}
+hexstring 0[xX]{hexpair}+
+ifname [a-zA-Z]+[0-9]+
+slash \/
+
+%s S_CNF
+%s S_LOG
+%s S_PHYINT S_IFCONF S_CANDRP S_CANDBSR S_PREFIX
+%s S_THRES
+
+%%
+%{
+ if (yy_first_time) {
+ BEGIN S_CNF;
+ yy_first_time = 0;
+ }
+%}
+
+ /* logging */
+<S_CNF>log { DP("begin logging"); BEGIN S_LOG; return(LOGGING); }
+<S_LOG>(no)?mld_proto {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_PROTO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld_timer {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld_member {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_MEMBER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?switch {
+ YYD_ECHO;
+ yylval.num = DEBUG_SWITCH;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?m?trace {
+ YYD_ECHO;
+ yylval.num = DEBUG_TRACE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?traceroute {
+ YYD_ECHO;
+ yylval.num = DEBUG_TRACE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?timeout {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMEOUT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?callout {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMEOUT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pkt {
+ YYD_ECHO;
+ yylval.num = DEBUG_PKT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?packets {
+ YYD_ECHO;
+ yylval.num = DEBUG_PKT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?interfaces {
+ YYD_ECHO;
+ yylval.num = DEBUG_IF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?vif {
+ YYD_ECHO;
+ yylval.num = DEBUG_IF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?kernel {
+ YYD_ECHO;
+ yylval.num = DEBUG_KERN;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?cache {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mfc {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?k_cache {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?k_mfc {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rsrr {
+ YYD_ECHO;
+ yylval.num = DEBUG_RSRR;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_detail {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_DETAIL;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_hello {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_HELLO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_neighbors {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_HELLO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_register {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_REGISTER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?registers {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_REGISTER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_join_prune {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_j_p {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_jp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_bootstrap {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_bsr {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?bsr {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?bootstrap {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_asserts {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_ASSERT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_cand_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_c_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_routes {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_routing {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_mrt {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_timers {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_rpf {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_RPF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rpf {
+ YYD_ECHO;
+ yylval.num = DEBUG_RPF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routes {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routing {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mrt {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routers {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mrouters {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?neighbors {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?timers {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?asserts {
+ YYD_ECHO;
+ yylval.num = DEBUG_ASSERT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>all { YYD_ECHO; yylval.num = DEBUG_ALL; return(LOGLEV); }
+<S_LOG>3 { YYD_ECHO; yylval.num = DEBUG_ALL; return(LOGLEV); }
+<S_LOG>{semi} { DP("end logging"); BEGIN S_CNF; return(EOS); }
+<S_LOG>{string} { yywarn("unknown log type: %s (ignored)", yytext); }
+
+ /* yes-or-no */
+[yY][eE][sS] { YYD_ECHO; return(YES); }
+[nN][oO] { YYD_ECHO; return(NO); }
+
+ /* reverselookup */
+<S_CNF>reverselookup { YYD_ECHO; return(REVERSELOOKUP); }
+
+ /* phyint */
+<S_CNF>phyint { DP("begin phyint"); BEGIN S_PHYINT; return(PHYINT); }
+<S_PHYINT>{string} {
+ YYD_ECHO;
+ BEGIN S_IFCONF;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_PHYINT>{semi} { yyerror("phyint was specified without interface."); return(EOS); }
+<S_IFCONF>disable { YYD_ECHO; return(DISABLE); }
+<S_IFCONF>preference { YYD_ECHO; return(PREFERENCE); }
+<S_IFCONF>metric { YYD_ECHO; return(METRIC); }
+<S_IFCONF>nolistener { YYD_ECHO; return(NOLISTENER); }
+<S_IFCONF>{semi} { DP("end phyint"); BEGIN S_CNF; return(EOS); }
+
+ /* cand_rp */
+<S_CNF>cand_rp { DP("begin cand_rp"); BEGIN S_CANDRP; return(CANDRP); }
+<S_CANDRP>priority { YYD_ECHO; return(PRIORITY); }
+<S_CANDRP>time { YYD_ECHO; return(TIME); }
+<S_CANDRP>{ifname} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_CANDRP>{semi} { DP("end cand_rp"); BEGIN S_CNF; return(EOS); }
+
+ /* cand_bootstrap_router */
+<S_CNF>cand_bootstrap_router { DP("begin cand_bsr"); BEGIN S_CANDBSR; return(CANDBSR); }
+<S_CANDBSR>priority { YYD_ECHO; return(PRIORITY); }
+<S_CANDBSR>time { YYD_ECHO; return(TIME); }
+<S_CANDBSR>{ifname} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_CANDBSR>{semi} { DP("end cand_bsr"); BEGIN S_CNF; return(EOS); }
+
+ /* group_prefix */
+<S_CNF>group_prefix { YYD_ECHO; return(GRPPFX); }
+
+ /* switch_register_threshold */
+<S_CNF>switch_register_threshold {
+ YYD_ECHO; BEGIN S_THRES; return(REGTHRES);
+}
+ /* switch_data_threshold */
+<S_CNF>switch_data_threshold {
+ YYD_ECHO; BEGIN S_THRES; return(DATATHRES);
+}
+<S_THRES>rate { YYD_ECHO; return(RATE); }
+<S_THRES>interval { YYD_ECHO; return(INTERVAL); }
+<S_THRES>{semi} { DP("end thres"); BEGIN S_CNF; return(EOS); }
+
+ /* various parameters */
+<S_CNF>default_source_metric { YYD_ECHO; return(SRCMETRIC); }
+<S_CNF>default_source_preference { YYD_ECHO; return(SRCPREF); }
+<S_CNF>hello_period { YYD_ECHO; return(HELLOPERIOD); }
+<S_CNF>granularity { YYD_ECHO; return(GRANULARITY); }
+<S_CNF>join_prune_period { YYD_ECHO; return(JPPERIOD); }
+<S_CNF>data_timeout { YYD_ECHO; return(DATATIME); }
+<S_CNF>register_suppression_timeout { YYD_ECHO; return(REGSUPTIME); }
+<S_CNF>probe_time { YYD_ECHO; return(PROBETIME); }
+<S_CNF>assert_timeout { YYD_ECHO; return(ASSERTTIME); }
+
+ /* misc */
+{ws} { ; }
+{nl} { lineno++; }
+{comment} { DP("comment"); }
+{number} { YYD_ECHO; yylval.fl = atof(yytext); return(NUMBER); }
+{slash} { YYD_ECHO; return(SLASH); }
+{semi} { DP("end cnf"); return(EOS); }
+
+ /* last resort */
+{string} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(STRING);
+ }
+%%
+
+static void
+cfdebug_print(w, t, l)
+ char *w, *t;
+ int l;
+{
+ printf("<%d>%s [%s] (%d)\n", yy_start, w, t, l);
+}
+
+static void
+yyerror0(char *s, va_list ap)
+{
+ fprintf(stderr, "%s %d: ", configfilename, lineno);
+ vfprintf(stderr, s, ap);
+ fprintf(stderr, "\n");
+}
+
+void
+yyerror(char *s, ...)
+{
+ va_list ap;
+#ifdef HAVE_STDARG_H
+ va_start(ap, s);
+#else
+ va_start(ap);
+#endif
+ yyerror0(s, ap);
+ va_end(ap);
+ yyerrorcount++;
+}
+
+void
+yywarn(char *s, ...)
+{
+ va_list ap;
+#ifdef HAVE_STDARG_H
+ va_start(ap, s);
+#else
+ va_start(ap);
+#endif
+ yyerror0(s, ap);
+ va_end(ap);
+}
+
+int
+cfparse(strict, debugonly)
+ int strict, debugonly;
+{
+ if ((yyin = fopen(configfilename, "r")) == NULL) {
+ fprintf(stderr, "cfparse: fopen(%s)", configfilename);
+ return(-1);
+ }
+
+ cf_init(strict, debugonly);
+
+ if ((yyparse() || yyerrorcount) && strict) {
+ if (yyerrorcount) {
+ yyerror("fatal parse failure: exiting (%d errors)",
+ yyerrorcount);
+ } else
+ yyerror("fatal parse failure: exiting");
+ return(-1);
+ }
+
+ YIPSDP(PLOG("parse successed.\n"));
+
+ return cf_post_config();
+}
diff --git a/usr.sbin/pim6sd/config.c b/usr.sbin/pim6sd/config.c
new file mode 100644
index 0000000..04689ca
--- /dev/null
+++ b/usr.sbin/pim6sd/config.c
@@ -0,0 +1,1609 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "vif.h"
+#include "pim6.h"
+#include "inet6.h"
+#include "rp.h"
+#include "pimd.h"
+#include "timer.h"
+#include "route.h"
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "config.h"
+#include <arpa/inet.h>
+#include <stdio.h>
+#include "debug.h"
+
+void add_phaddr(struct uvif *v , struct sockaddr_in6 *addr,struct in6_addr *mask);
+char *next_word(char **s);
+int wordToOption(char *word);
+int parse_phyint(char *s);
+int parse_candidateRP(char *s);
+int parse_group_prefix(char *s);
+int parseBSR(char *s);
+int parse_reg_threshold(char *s);
+int parse_data_threshold(char *s);
+int parse_default_source_metric(char *s);
+int parse_default_source_preference(char *s);
+int parse_hello_period(char *s);
+int parse_granularity(char *s);
+int parse_jp_period(char *s);
+int parse_data_timeout(char *s);
+int parse_register_suppression_timeout(char *s);
+int parse_probe_time(char *s);
+int parse_assert_timeout(char *s);
+
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp,*ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n,i;
+ struct sockaddr_in6 addr;
+ struct in6_addr mask;
+ 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
+
+ /*
+ * Ignore any interface for an address family other than IPv6.
+ */
+ if ( ifrp->ifr_addr.sa_family != AF_INET6)
+ {
+ /* Eventually may have IP address later */
+ total_interfaces++;
+ 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));
+
+ if(ioctl(udp_socket,SIOCGIFFLAGS,(char *)&ifr) <0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+ flags = ifr.ifr_flags;
+
+#if 0
+ /*
+ * Ignore loopback interfaces and interfaces that do not
+ * support multicast.
+ */
+ if((flags & (IFF_LOOPBACK | IFF_MULTICAST ))!= IFF_MULTICAST)
+ continue;
+#endif
+
+ /*
+ * 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",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr));
+ memcpy(&mask,&ifr6.ifr_addr.sin6_addr,sizeof(mask));
+
+ /*
+ * Get IPv6 specific flags, and ignore an anycast address.
+ * XXX: how about a deprecated, tentative, duplicated or
+ * detached address?
+ */
+ ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr;
+ if (ioctl(udp_socket, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ log(LOG_ERR, errno, "ioctl SIOCGIFAFLAG_IN6 for %s",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr));
+ }
+ else {
+ if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) {
+ log(LOG_DEBUG, 0, "config_vifs_from_kernel: "
+ "%s on %s is an anycast address, ignored",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr),
+ ifr.ifr_name);
+ continue;
+ }
+ }
+
+ 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 vifs, 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_dst_addr = allpim6routers_group;
+ v->uv_subnetmask = mask;
+ strncpy ( v->uv_name , ifr.ifr_name,IFNAMSIZ);
+ v->uv_ifindex = if_nametoindex(v->uv_name);
+ add_phaddr(v,&addr,&mask);
+
+ /* prefix local calc. (and what about add_phaddr?...) */
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ v->uv_prefix.sin6_addr.s6_addr[i] =
+ addr.sin6_addr.s6_addr[i] & mask.s6_addr[i];
+
+ if(flags & IFF_POINTOPOINT)
+ v->uv_flags |=(VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT);
+
+ /*
+ * Disable multicast routing on loopback interfaces and
+ * interfaces that do not support multicast. But they are
+ * still necessary, since global addresses maybe assigned only
+ * on such interfaces.
+ */
+ if ((flags & IFF_LOOPBACK) != 0 || (flags & IFF_MULTICAST) == 0)
+ v->uv_flags |= VIFF_DISABLED;
+
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,
+ "Installing %s (%s on subnet %s) ,"
+ "as vif #%u - rate = %d",
+ v->uv_name,inet6_fmt(&addr.sin6_addr),
+ net6name(&v->uv_prefix.sin6_addr,&mask),
+ numvifs,v->uv_rate_limit);
+
+ ++numvifs;
+
+
+ if( !(flags & IFF_UP))
+ {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+
+ }
+}
+
+void
+add_phaddr(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;
+
+ 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;
+ }
+
+ pa->pa_next = v->uv_addrs;
+ v->uv_addrs = pa;
+}
+
+void
+config_vifs_from_file()
+{
+ FILE *f;
+ char linebuf[100];
+ char *w,*s;
+ struct ifconf ifc;
+ int option;
+ char ifbuf[BUFSIZ];
+ u_int8 *data_ptr;
+
+ if((f=fopen(configfilename,"r"))==NULL)
+ {
+ if( errno != ENOENT)
+ log(LOG_ERR,errno,"Can't open %s",configfilename);
+ log(LOG_WARNING,errno,"Can't open %s",configfilename);
+ return;
+ }
+ /*
+ * Note that sizeof(pim6_enocd_uni_addr_t) might be larger than
+ * the length of the Encoded-Unicast-address field(18 byte) due to
+ * some padding put in the compiler. However, it doesn't matter
+ * since we use the space just as a buffer(i.e not as the message).
+ */
+ cand_rp_adv_message.buffer = (u_int8 *)malloc( 4 + sizeof(pim6_encod_uni_addr_t) +
+ 255*sizeof(pim6_encod_grp_addr_t));
+ if(cand_rp_adv_message.buffer == NULL)
+ log(LOG_ERR,errno,"Candrpadv Buffer allocation");
+
+ cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer;
+
+ /* By default, if no group_prefix configured, then prefix_cnt == 0
+ * implies group_prefix = ff::/8 and masklen = 8.
+ */
+
+ *cand_rp_adv_message.prefix_cnt_ptr = 0;
+ cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer;
+
+ /* TODO: XXX: HARDCODING!!! */
+ cand_rp_adv_message.insert_data_ptr += (4 + 18);
+
+ 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 CANDIDATE_RP:
+ parse_candidateRP(s);
+ break;
+ case GROUP_PREFIX:
+ parse_group_prefix(s);
+ break;
+ case BOOTSTRAP_RP:
+ parseBSR(s);
+ break;
+ case REG_THRESHOLD:
+ parse_reg_threshold(s);
+ break;
+ case DATA_THRESHOLD:
+ parse_data_threshold(s);
+ break;
+ case DEFAULT_SOURCE_METRIC:
+ parse_default_source_metric(s);
+ break;
+ case DEFAULT_SOURCE_PREFERENCE :
+ parse_default_source_preference(s);
+ break;
+ case HELLO_PERIOD :
+ parse_hello_period(s);
+ break;
+ case GRANULARITY :
+ parse_granularity(s);
+ break;
+ case JOIN_PRUNE_PERIOD :
+ parse_jp_period(s);
+ break;
+ case DATA_TIMEOUT :
+ parse_data_timeout(s);
+ break;
+ case REGISTER_SUPPRESSION_TIMEOUT :
+ parse_register_suppression_timeout(s);
+ break;
+ case PROBE_TIME :
+ parse_probe_time(s);
+ break;
+ case ASSERT_TIMEOUT:
+ parse_assert_timeout(s);
+ break;
+ default:
+ log(LOG_WARNING, 0, "unknown command '%s' in %s",
+ w, configfilename);
+
+ }
+ }
+ cand_rp_adv_message.message_size = cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+ if (cand_rp_flag != FALSE)
+ {
+ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period;
+
+ /* TODO: HARDCODING! */
+ data_ptr = cand_rp_adv_message.buffer + 1; /* WARNING */
+ PUT_BYTE(my_cand_rp_priority,data_ptr);
+ PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr);
+ PUT_EUADDR6(my_cand_rp_address.sin6_addr,data_ptr);
+ IF_DEBUG(DEBUG_PIM_CAND_RP)
+ {
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP address is : %s",
+ inet6_fmt(&my_cand_rp_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP priority is : %u",my_cand_rp_priority);
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP advertisement period is : %u sec.",
+ my_cand_rp_adv_period);
+ }
+ }
+
+
+ if( cand_bsr_flag!=FALSE)
+ {
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP)
+ {
+ log(LOG_DEBUG, 0,
+ "Local BSR address: %s",
+ inet6_fmt(&my_bsr_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local BSR priority : %u",my_bsr_priority);
+ log(LOG_DEBUG,0,
+ "Local BSR period is : %u sec.",
+ my_bsr_period);
+
+ }
+
+ }
+
+ IF_DEBUG(DEBUG_SWITCH)
+ {
+ log(LOG_DEBUG,0,"reg_rate_limit set to %u (bits/s)" , pim_reg_rate_bytes);
+ log(LOG_DEBUG,0,"reg_rate_interval set to %u s.",pim_reg_rate_check_interval);
+ log(LOG_DEBUG,0,"data_rate_limit set to %u (bits/s)" , pim_data_rate_bytes);
+ log(LOG_DEBUG,0,"data_rate_interval set to %u s.",pim_data_rate_check_interval);
+ }
+
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ {
+ log(LOG_DEBUG,0, "pim_hello_period set to: %u", pim_hello_period);
+ log(LOG_DEBUG,0, "pim_hello_holdtime set to: %u", pim_hello_holdtime);
+ }
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG,0, "pim_join_prune_period set to: %u", pim_join_prune_period);
+ log(LOG_DEBUG,0, "pim_join_prune_holdtime set to: %u", pim_join_prune_holdtime);
+ }
+
+ fclose(f);
+
+}
+/*
+ * 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()
+ */
+
+int wordToOption(char *word)
+{
+ if (EQUAL(word, ""))
+ return EMPTY;
+ if (EQUAL(word, "phyint"))
+ return PHYINT;
+ if (EQUAL(word, "cand_rp"))
+ return CANDIDATE_RP;
+ if (EQUAL(word, "group_prefix"))
+ return GROUP_PREFIX;
+ if (EQUAL(word, "cand_bootstrap_router"))
+ return BOOTSTRAP_RP;
+ if (EQUAL(word, "switch_register_threshold"))
+ return REG_THRESHOLD;
+ if (EQUAL(word, "switch_data_threshold"))
+ return DATA_THRESHOLD;
+ if (EQUAL(word, "default_source_metric"))
+ return DEFAULT_SOURCE_METRIC;
+ if (EQUAL(word, "default_source_preference"))
+ return DEFAULT_SOURCE_PREFERENCE;
+ if (EQUAL(word, "hello_period"))
+ return HELLO_PERIOD;
+ if (EQUAL(word, "granularity"))
+ return GRANULARITY;
+ if (EQUAL(word, "join_prune_period"))
+ return JOIN_PRUNE_PERIOD;
+ if (EQUAL(word, "data_timeout"))
+ return DATA_TIMEOUT;
+ if (EQUAL(word, "register_suppression_timeout"))
+ return REGISTER_SUPPRESSION_TIMEOUT;
+ if (EQUAL(word, "probe_time"))
+ return PROBE_TIME;
+ if (EQUAL(word, "assert_timeout"))
+ return PROBE_TIME;
+ 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] [preference <p>] [metric <m>]
+ */
+
+
+int parse_phyint(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 name in %s", configfilename);
+ return FALSE;
+ }
+ ifname = w;
+
+ for (vifi = 0,v=uvifs;vifi <= numvifs ; ++vifi , ++v)
+ {
+ if(vifi == numvifs)
+ {
+ log(LOG_WARNING, 0,
+ "Invalid phyint name (maybe not configured..) '%s' "
+ "in %s", w, configfilename);
+ return FALSE;
+ }
+
+ 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 %d to %s",n,ifname);
+ 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 %d to %s.",
+ n,ifname);
+ v->uv_local_metric = n;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ return(TRUE);
+}
+
+/*
+ * function name: parse_candidateRP
+ * input: char *s
+ * output: int (TRUE if the parsing was successful, o.w. FALSE)
+ * operation: parses the candidate RP information.
+ * The general form is:
+ * 'cand_rp <ifname> [priority <number>] [time <number>]'.
+ */
+int
+parse_candidateRP(char *s)
+{
+ char *w;
+ struct sockaddr_in6 *sa6_rp;
+ u_int time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ u_int priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+
+ sa6_rp = NULL;
+ cand_rp_flag = FALSE;
+
+ my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+
+ while(!EQUAL((w = next_word(&s)),""))
+ {
+ if((!EQUAL(w,"priority")) && (!EQUAL(w,"time")))
+ {
+ /*
+ * if the interface is specified and is valid
+ * we take the max global address of the interface
+ * (aliasing) else look at the end of the function.
+ */
+ sa6_rp = local_iface(w);
+ if(!sa6_rp)
+ log(LOG_WARNING, 0,
+ "cand_rp '%s' in %s is not configured."
+ "take the max local address the router..",
+ w, configfilename);
+ }
+ else
+ {
+ if (EQUAL(w,"priority"))
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing priority ; set to default "
+ ": %d (0 is highest )",priority);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&priority)!= 1 )
+ {
+ priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+ log(LOG_WARNING, 0,
+ "Invalid priority '%' "
+ "for cand_rp;set to default "
+ "(0 is highest) : %d",
+ w, priority);
+ }
+ }
+ }
+ else
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0,
+ "Missing cand_adv period ;"
+ "set to default : %d",time);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&time)!= 1 )
+ {
+ time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ log(LOG_WARNING, 0,
+ "Invalid cand_adv_period "
+ "'%s';set to default : %d",
+ w,time);
+
+ }
+ else
+ {
+ if( time > (my_cand_rp_adv_period = ~0))
+ time = my_cand_rp_adv_period;
+ else
+ if(time <10)
+ time = 10;
+ else
+ if (time > PIM_DEFAULT_CAND_RP_ADV_PERIOD)
+ time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ my_cand_rp_adv_period = time;
+ }
+ }
+ }
+ }
+ }
+
+ if(!sa6_rp)
+ sa6_rp= max_global_address();
+
+ my_cand_rp_address=*sa6_rp;
+ my_cand_rp_priority = priority;
+ my_cand_rp_adv_period = time;
+ cand_rp_flag = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * function name: parse_group_prefix
+ * input: char *s
+ * output: int
+ * operation: parse group_prefix configured information.
+ * General form: 'group_prefix <group-addr>/<prefix_len>'.
+ */
+int
+parse_group_prefix(char *s)
+{
+ char *w;
+ struct in6_addr group_addr;
+ u_int32 masklen=PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+
+ w=next_word(&s);
+ if (EQUAL(w,""))
+ {
+ log(LOG_WARNING, 0,
+ "Configuration error for 'group_prefix' in %s: no group_addr. "
+ "Ignoring...", configfilename);
+ return FALSE;
+ }
+
+ w=strtok(w,"/");
+
+ if ( inet_pton(AF_INET6,w,(void *)&group_addr) != 1 )
+ {
+ log(LOG_WARNING, 0,
+ "Config error in %s : Bad ddress formatt.Ignoring..",
+ configfilename);
+ return FALSE;
+ }
+ if (!IN6_IS_ADDR_MULTICAST(&group_addr))
+ {
+ log(LOG_WARNING,0,
+ "Config error in %s: '%s' is not a mcast addr.Ignoring",
+ configfilename,
+ inet6_fmt(&group_addr));
+ return FALSE;
+ }
+ if (!(~(*cand_rp_adv_message.prefix_cnt_ptr)))
+ {
+ log(LOG_WARNING, 0,
+ "Too many group_prefix configured. Truncating...");
+ return FALSE;
+ }
+
+ w=strtok(NULL,"/");
+ if(w==NULL)
+ {
+ log(LOG_WARNING,0,
+ "Config error in %s : missing group prefix.Ignoring..",
+ configfilename);
+ return FALSE;
+ }
+ if ( sscanf(w,"%u",&masklen) ==1 )
+ {
+ if (masklen > (sizeof(group_addr) * 8))
+ masklen = (sizeof(group_addr)*8);
+ else
+ if (masklen <PIM_GROUP_PREFIX_DEFAULT_MASKLEN)
+ masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+ }
+
+
+ PUT_EGADDR6(group_addr, (u_int8)masklen, 0,
+ cand_rp_adv_message.insert_data_ptr);
+ (*cand_rp_adv_message.prefix_cnt_ptr)++;
+
+ return TRUE;
+
+}
+/*
+ * function name: parseBSR
+ * input: char *s
+ * output: int
+ * operation: parse the candidate BSR configured information.
+ * General form:
+ * 'cand_bootstrap_router <ifname> [priority <number>]'.
+ * this function is similar to parse_candrp
+ */
+
+int
+parseBSR(char *s)
+{
+ char *w;
+ struct sockaddr_in6 *sa6_bsr;
+ u_int32 priority = PIM_DEFAULT_BSR_PRIORITY;
+ u_int time = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ my_bsr_period = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+
+ sa6_bsr = NULL;
+ cand_bsr_flag = FALSE;
+
+ while(!EQUAL((w = next_word(&s)),""))
+ {
+ if((!EQUAL(w,"priority")) && (!EQUAL(w,"time")))
+ {
+
+ sa6_bsr = local_iface(w);
+ if(!sa6_bsr)
+ {
+ log(LOG_WARNING,0,
+ "cand_bootstrap_router '%s' in %s is not "
+ "configured.Take the max router address.",
+ w,configfilename);
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"priority"))
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0,
+ "Missing priority for the bsr;set to "
+ "default (0 is lowest): %d",priority);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&priority)!= 1 )
+ {
+ priority = PIM_DEFAULT_BSR_PRIORITY;
+ log(LOG_WARNING, 0,
+ "Invalid priority '%s'for "
+ "the bsr;set to default : %d",
+ w, priority);
+ }
+ else
+ {
+ if( priority > (my_bsr_priority = ~0))
+ priority = my_bsr_priority;
+ my_bsr_priority = (u_int8)priority;
+ }
+ }
+ }
+ else
+ {
+ if( EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing bsr period ;"
+ "set to default : %d ",time);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid bsr period"
+ "'%s';set to default : %d",
+ w,time);
+ }
+ else
+ my_bsr_period=time;
+ }
+ }
+ }
+ }
+
+ if(!sa6_bsr)
+ sa6_bsr = max_global_address();
+
+ my_bsr_address=*sa6_bsr;
+ my_bsr_priority = priority;
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN,my_bsr_hash_mask);
+ cand_bsr_flag = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * function name: parse_reg_threshold
+ * input: char *s
+ * output: int (TRUE if successful, FALSE o.w.)
+ * operation: reads and assigns the switch to the spt threshold
+ * due to registers for the router, if used as RP.
+ * Maybe extended to support different thresholds
+ * for different groups(prefixes).
+ * General form:
+ * 'switch_register_threshold [rate <number> interval <number>]'.
+ * comments: called by config_vifs_from_file()
+ */
+
+
+int parse_reg_threshold(char *s)
+{
+ char *w;
+ u_int rate=PIM_DEFAULT_REG_RATE;
+ u_int interval= PIM_DEFAULT_REG_RATE_INTERVAL;
+
+ while(!EQUAL((w=next_word(&s)),""))
+ {
+ if(EQUAL(w,"rate"))
+ {
+ if(EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "switch_register_threshold : missing rate ; "
+ "set to default : %u (bits/s)",
+ rate);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&rate)!=1)
+ {
+ rate = PIM_DEFAULT_REG_RATE;
+ log(LOG_WARNING, 0,
+ "switch_register_threshold : "
+ "Invalid rate '%s' , set to defaut :"
+ " %u (bits/s)",
+ w,rate);
+ }
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"interval"))
+ {
+ if(EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_register_threshold : missing interval ; set to default : %u s.",
+ interval);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&interval) != 1)
+ {
+ interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+ log(LOG_WARNING,0,"switch_register_threshold : Invalid interval '%s' ; set to default : %u s.",
+ w,interval);
+ }
+ }
+ }
+ else
+ {
+ log(LOG_WARNING,0,"swhitch_register_threshold : Invalid parameter %s",w);
+ }
+ }
+ }
+
+ if( interval < timer_interval)
+ {
+ interval = timer_interval;
+ log(LOG_WARNING,0,"switch_register_threshold : Interval too short , set to default : %u s.",
+ interval);
+ }
+
+ pim_reg_rate_bytes = (rate * interval ) /10;
+ pim_reg_rate_check_interval = interval;
+
+ return TRUE;
+
+}
+/*
+ * function name: parse_data_threshold
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the switch to the spt threshold
+ * due to data packets, if used as DR.
+ * General form:
+ * 'switch_data_threshold [rate <number> interval <number>]'.
+ * similar to register_threshold...
+ */
+
+int parse_data_threshold(char *s)
+{
+ char *w;
+ u_int rate=PIM_DEFAULT_DATA_RATE;
+ u_int interval= PIM_DEFAULT_DATA_RATE_INTERVAL;
+
+ while(!EQUAL((w=next_word(&s)),""))
+ {
+ if(EQUAL(w,"rate"))
+ {
+ if(EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_data_threshold : missing rate value ; set to defaut : %u (bits/s)",
+ rate);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&rate)!=1)
+ {
+ rate = PIM_DEFAULT_DATA_RATE;
+ log(LOG_WARNING,0,"switch_data_threshold : Invalid rate '%s' ; set to default : %u (bits/s)",
+ w,rate);
+ }
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"interval"))
+ {
+ if(EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_data_threshold : missing interval value ; set to default : %u s.",
+ interval);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&interval) != 1)
+ {
+ interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+ log(LOG_WARNING,0,"switch_data_threshold : Invalid interval '%s' ; set to default : %u s.",
+ w,interval);
+ }
+ }
+ }
+ else
+ {
+ log(LOG_WARNING,0,"swhitch_data_threshold :Invalid Parameter %s",w);
+ }
+ }
+ }
+
+ if( interval < timer_interval)
+ {
+ interval = timer_interval;
+ log(LOG_WARNING,0,"switch_data_threshold : interval too short set to default : %u s.",
+ interval);
+ }
+
+ pim_data_rate_bytes = (rate * interval ) /10;
+ pim_data_rate_check_interval = interval;
+
+ return TRUE;
+
+}
+
+/*
+ * 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.
+ */
+
+
+int parse_default_source_metric(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 source metric value ; set to default %u",
+ value);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &value) != 1)
+ {
+ value = DEFAULT_LOCAL_METRIC;
+ log(LOG_WARNING,0,
+ "Invalid source metric value '%s' ;set to default %u",
+ w,value);
+ }
+ default_source_metric = value;
+ log(LOG_INFO,0, "Default_source_metric is : %u", default_source_metric);
+
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++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.
+ */
+
+int parse_default_source_preference(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 source preference ; set to default %u",
+ value);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &value) != 1)
+ {
+ value = DEFAULT_LOCAL_PREF;
+ log(LOG_WARNING,0,
+ "Invalid source preference value '%s' ;set to default %u",
+ w,value);
+ }
+ default_source_preference = value;
+ log(LOG_INFO,0, "default_source_preference set to: %u", default_source_preference);
+
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v)
+ {
+ v->uv_local_pref = default_source_preference;
+ }
+
+
+ }
+ return(TRUE);
+}
+
+/*
+ * function name: parse_hello_period
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the hello period for a pim router
+ * General form:
+ * 'hello_period <number> <coef>'.
+ * number is the period in second between 2 hello messages
+ * and coef is the coef to deterimine the hello holdtime
+ * default : 3.5
+ */
+
+int parse_hello_period(char *s)
+{
+ char *w;
+ u_int hellop;
+ float coef;
+
+ hellop = PIM_TIMER_HELLO_PERIOD;
+ coef = 3.5;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing hello period ; set to default %u",
+ hellop);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &hellop) != 1)
+ {
+ hellop = PIM_TIMER_HELLO_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid hello period value '%s' ;set to default %u",
+ w,hellop);
+ }
+ pim_hello_period = hellop;
+
+ if (!EQUAL((w=next_word(&s)),""))
+ {
+ if (sscanf(w, "%f", &coef) != 1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "Invalid hello period coef '%s' ;set to default %.1f",
+ w,coef);
+ }
+ if(coef<=1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "for hello period coef must be > 1;set to default %.1f",
+ coef);
+ }
+
+ }
+
+
+ }
+ pim_hello_holdtime = coef*pim_hello_period;
+ return(TRUE);
+}
+/*
+ * function name: parse_jp_period
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the join/prune period for a pim router
+ * General form:
+ * 'join_prune_period <number> <coef>'.
+ * number is the period in second between 2 join/prune messages
+ * and coef is the coef to deterimine the join/prune holdtime
+ * default : 3.5
+ * This function is similar to the function above
+ */
+
+int parse_jp_period(char *s)
+{
+ char *w;
+ u_int jpp;
+ float coef;
+
+ jpp = PIM_JOIN_PRUNE_PERIOD;
+ coef = 3.5;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing join/prune period ; set to default %u",
+ jpp);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &jpp) != 1)
+ {
+ jpp = PIM_JOIN_PRUNE_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid join/prune period value '%s' ;set to default %u",
+ w,jpp);
+ }
+
+ pim_join_prune_period = jpp;
+
+ if (!EQUAL((w=next_word(&s)),""))
+ {
+ if (sscanf(w, "%f", &coef) != 1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "Invalid join/prune period coef '%s' ;set to default %.1f",
+ w,coef);
+ }
+ if(coef<=1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "for join/prune period coef must be > 1;set to default %.1f",
+ coef);
+ }
+
+ }
+
+
+ }
+ pim_join_prune_holdtime = coef*pim_join_prune_period;
+ return(TRUE);
+}
+
+
+/* function name : parse_granularity
+ * input char *s
+ * output int
+ * operation : reads and assigns the granularity of the demon's timer
+ * General form :
+ * 'granularity <number>
+ * number is the period in seconds between each "tics" of the virtual time.
+ * default : 5 s.
+ */
+int parse_granularity(char *s)
+{
+ char *w;
+ u_int granu;
+
+ granu = DEFAULT_TIMER_INTERVAL;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing timer granularity ; set to default %u",
+ granu);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&granu)!=1)
+ {
+ granu=DEFAULT_TIMER_INTERVAL;
+ log(LOG_WARNING,0,
+ "Invalid timer granularity value '%s' ; set to default %u",
+ w,granu);
+ }
+ timer_interval = granu;
+ if(granu < 1)
+ {
+ granu = DEFAULT_TIMER_INTERVAL;
+ log(LOG_WARNING,0,
+ "Timer granularity MUST be > 1! ; set to default %u",
+ granu);
+ }
+ }
+
+ timer_interval = granu;
+ return TRUE;
+}
+
+/* function name : parse_data_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the data_timeout of each (S,G)
+ * General form :
+ * 'data_timeout <number>
+ * default : 210 s.
+ */
+int parse_data_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_DATA_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing data timeout ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_DATA_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid data timeout value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_data_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_DATA_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Data timeout must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_data_timeout = time;
+ return TRUE;
+}
+
+/* function name : parse_register_suppression_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the register_suppression_timeout
+ * General form :
+ * 'register_suppression_timeout <number>
+ * default : 60 s.
+ */
+int parse_register_suppression_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing register suppression timeout ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid register suppression timeout value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_register_suppression_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Register suppression timeout must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_register_suppression_timeout = time;
+ return TRUE;
+}
+
+/* function name : parse_probe_time
+ * input char *s
+ * output int
+ * operation : reads and assigns the probe_time for null-register
+ * General form :
+ * 'probe_time <number>
+ * default : 5 s.
+ */
+int parse_probe_time(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_REGISTER_PROBE_TIME;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing register probe time ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_REGISTER_PROBE_TIME;
+ log(LOG_WARNING,0,
+ "Invalid register probe time value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_register_probe_time = time;
+ if(time < 1)
+ {
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Register probe time must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_register_probe_time = time;
+ return TRUE;
+}
+
+/* function name : parse_assert_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the assert timeout
+ * General form :
+ * 'assert_timeout <number>
+ * default : 180 s.
+ */
+int parse_assert_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_ASSERT_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing assert time out; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_ASSERT_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid assert time out value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_assert_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_ASSERT_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Assert time out must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_assert_timeout = time;
+ return TRUE;
+}
+
+
+
+
+char *next_word(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/pim6sd/config.h b/usr.sbin/pim6sd/config.h
new file mode 100644
index 0000000..deffe3f3
--- /dev/null
+++ b/usr.sbin/pim6sd/config.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define UNKNOWN -1
+#define EMPTY 1
+#define PHYINT 2
+#define CANDIDATE_RP 3
+#define GROUP_PREFIX 4
+#define BOOTSTRAP_RP 5
+#define REG_THRESHOLD 6
+#define DATA_THRESHOLD 7
+#define DEFAULT_SOURCE_METRIC 8
+#define DEFAULT_SOURCE_PREFERENCE 9
+#define HELLO_PERIOD 10
+#define GRANULARITY 11
+#define JOIN_PRUNE_PERIOD 12
+#define DATA_TIMEOUT 13
+#define REGISTER_SUPPRESSION_TIMEOUT 14
+#define PROBE_TIME 15
+#define ASSERT_TIMEOUT 16
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+void config_vifs_from_kernel();
+void config_vifs_from_file();
+
+#endif
diff --git a/usr.sbin/pim6sd/crc.c b/usr.sbin/pim6sd/crc.c
new file mode 100644
index 0000000..917371e
--- /dev/null
+++ b/usr.sbin/pim6sd/crc.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+/* CRC implantation : stolen from RFC 2083 section 15.*/
+
+/* Table of CRCs of all 8-bit messages. */
+ unsigned long crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+ int crc_table_computed = 0;
+
+
+/* Make the table for a fast CRC. */
+
+void make_crc_table(void)
+{
+ unsigned long c;
+ int n, k;
+ for (n = 0; n < 256; n++)
+ {
+ c = (unsigned long) n;
+ for (k = 0; k < 8; k++)
+ {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+ crc_table_computed = 1;
+}
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+unsigned long update_crc(unsigned long crc, unsigned char *buf,
+ int len)
+{
+ unsigned long c = crc;
+ int n;
+
+ if (!crc_table_computed)
+ make_crc_table();
+ for (n = 0; n < len; n++)
+ {
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+ }
+ return c;
+}
+
+
+/* Return the CRC of the bytes buf[0..len-1]. */
+
+unsigned long crc(unsigned char *buf, int len)
+{
+ return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
+}
diff --git a/usr.sbin/pim6sd/crc.h b/usr.sbin/pim6sd/crc.h
new file mode 100644
index 0000000..e55dbc8
--- /dev/null
+++ b/usr.sbin/pim6sd/crc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#ifndef CRC_H
+#define CRC_H
+
+extern long crc __P((unsigned char *buf, int len));
+
+#endif
diff --git a/usr.sbin/pim6sd/debug.c b/usr.sbin/pim6sd/debug.c
new file mode 100644
index 0000000..9349cce
--- /dev/null
+++ b/usr.sbin/pim6sd/debug.c
@@ -0,0 +1,910 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/icmp6.h>
+#include <netinet6/pim6.h>
+#include "pathnames.h"
+#include "defs.h"
+#include "pimd.h"
+#include "debug.h"
+#include "mrt.h"
+#include "vif.h"
+#include "rp.h"
+#include "inet6.h"
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+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 */
+static char statfilename[] = _PATH_PIM6D_STAT;
+
+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_nbrs(fp);
+ dump_pim_mrt(fp);
+ dump_rp_set(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_stat()
+{
+ FILE *fp;
+ vifi_t vifi;
+ register struct uvif *v;
+
+ fp = fopen(statfilename, "w");
+ if (fp == NULL) {
+ log(LOG_WARNING, errno, "dump_stat: can't open file(%s)",
+ statfilename);
+ return;
+ }
+
+ fprintf(fp, "pim6sd per-interface statistics\n");
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+#if 0 /* is it better to skip them? */
+ if ((v->uv_flags & (VIFF_DISABLED|VIFF_DOWN)) != 0)
+ continue;
+#endif
+ fprintf(fp, " Mif=%d, PhyIF=%s\n", vifi, v->uv_name);
+ fprintf(fp, "\t%qu pim6 hello received\n", v->uv_in_pim6_hello);
+ fprintf(fp, "\t%qu pim6 join-prune received\n",
+ v->uv_in_pim6_join_prune);
+ fprintf(fp, "\t%qu pim6 bootstrap received\n",
+ v->uv_in_pim6_bootsrap);
+ fprintf(fp, "\t%qu pim6 assert received\n", v->uv_in_pim6_assert);
+
+ fprintf(fp, "\t%qu pim6 hello sent\n", v->uv_out_pim6_hello);
+ fprintf(fp, "\t%qu pim6 join-prune sent\n",
+ v->uv_out_pim6_join_prune);
+ fprintf(fp, "\t%qu pim6 bootstrap sent\n",
+ v->uv_out_pim6_bootsrap);
+ fprintf(fp, "\t%qu pim6 assert sent\n", v->uv_out_pim6_assert);
+
+ fprintf(fp, "\t%qu MLD query received\n", v->uv_in_mld_query);
+ fprintf(fp, "\t%qu MLD report received\n", v->uv_in_mld_report);
+ fprintf(fp, "\t%qu MLD done received\n", v->uv_in_mld_done);
+
+ fprintf(fp, "\t%qu MLD query sent\n", v->uv_out_mld_query);
+ fprintf(fp, "\t%qu MLD report sent\n", v->uv_out_mld_report);
+ fprintf(fp, "\t%qu MLD done sent\n", v->uv_out_mld_done);
+
+ fprintf(fp, "\t%qu forwarding cache miss\n", v->uv_cache_miss);
+ fprintf(fp, "\t%qu forwarding cache miss and not created\n",
+ v->uv_cache_notcreated);
+
+ fprintf(fp, "\t%qu PIM neighbor timeouts\n", v->uv_pim6_nbr_timo);
+ fprintf(fp, "\t%qu MLD listener timeouts\n", v->uv_listener_timo);
+ fprintf(fp, "\t%qu out-I/F timeouts\n", v->uv_outif_timo);
+ }
+
+ fprintf(fp, "\npim6sd interface independent statistics\n");
+
+ fprintf(fp, "\t%qu pim6 register received\n", pim6dstat.in_pim6_register);
+ fprintf(fp, "\t%qu pim6 register-stop received\n",
+ pim6dstat.in_pim6_register_stop);
+ fprintf(fp, "\t%qu pim6 cand-RP received\n", pim6dstat.in_pim6_cand_rp);
+ fprintf(fp, "\t%qu pim6 graft received\n", pim6dstat.in_pim6_graft);
+ fprintf(fp, "\t%qu pim6 graft ack received\n",
+ pim6dstat.in_pim6_graft_ack);
+
+ fprintf(fp, "\t%qu pim6 register sent\n", pim6dstat.out_pim6_register);
+ fprintf(fp, "\t%qu pim6 register-stop sent\n",
+ pim6dstat.out_pim6_register_stop);
+ fprintf(fp, "\t%qu pim6 cand-RP sent\n", pim6dstat.out_pim6_cand_rp);
+
+ fprintf(fp, "\t%qu transitions of forwarder initiated SPT\n",
+ pim6dstat.pim6_trans_spt_forward);
+ fprintf(fp, "\t%qu transitions of RP initiated SPT\n",
+ pim6dstat.pim6_trans_spt_rp);
+
+ fprintf(fp, "\t%qu pim6 bootstrap timeouts\n",
+ pim6dstat.pim6_bootstrap_timo);
+ fprintf(fp, "\t%qu pim6 RP group entry timeouts\n",
+ pim6dstat.pim6_rpgrp_timo);
+ fprintf(fp, "\t%qu pim6 routing entry timeouts\n",
+ pim6dstat.pim6_rtentry_timo);
+
+ fprintf(fp, "\t%qu kernel cache additions\n", pim6dstat.kern_add_cache);
+ fprintf(fp, "\t%qu kernel cache addition failures\n",
+ pim6dstat.kern_add_cache_fail);
+ fprintf(fp, "\t%qu kernel cache deletions\n", pim6dstat.kern_del_cache);
+ fprintf(fp, "\t%qu kernel cache deletion failures\n",
+ pim6dstat.kern_del_cache_fail);
+ fprintf(fp, "\t%qu failures of getting kernel cache\n",
+ pim6dstat.kern_sgcnt_fail);
+
+ fclose(fp);
+}
+
+void
+dump_vifs(fp)
+ FILE *fp;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ struct phaddr *pa;
+
+ fprintf(fp, "\nMulticast Interface Table\n %-4s %-6s %-43s %5s %-14s\n",
+ "Mif", " PhyIF", "Local-Address/Prefixlen","Scope", "Flags");
+
+ 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 %-43s", "", "",
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ fprintf(fp," %-5d\n", pa->pa_addr.sin6_scope_id);
+ continue;
+ }
+
+ firstaddr = 0;
+ fprintf(fp, " %-3u %6s %-43s", vifi,
+ (v->uv_flags & MIFF_REGISTER)?"regist":v->uv_name,
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ fprintf(fp," %-5d", pa->pa_addr.sin6_scope_id);
+
+ if (v->uv_flags & MIFF_REGISTER)
+ fprintf(fp, " REGISTER");
+ 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");
+ if (v->uv_flags & VIFF_PIM_NBR)
+ fprintf(fp, " PIM");
+#if 0 /* impossible */
+ if (v->uv_flags & VIFF_DVMRP_NBR)
+ {
+ fprintf(fp, " DVMRP");
+ }
+#endif
+ if (v->uv_flags & VIFF_NONBRS)
+ fprintf(fp, " %-12s", "NO-NBR");
+
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp, " %3s %6s ", "", "");
+ fprintf(fp, "Timers: PIM hello = %d:%02d, MLD query = %d:%02d\n",
+ v->uv_pim_hello_timer / 60, v->uv_pim_hello_timer % 60,
+ v->uv_gq_timer / 60, v->uv_gq_timer % 60);
+ }
+ fprintf(fp, "\n");
+}
+
+void
+dump_nbrs(fp)
+ FILE *fp;
+{
+ struct uvif *v;
+ vifi_t vifi;
+ pim_nbr_entry_t *n;
+
+ fprintf(fp, "PIM Neighbor List\n");
+ fprintf(fp, " %-3s %6s %-40s %-5s\n",
+ "Mif", "PhyIF", "Address", "Timer");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if ((n = v->uv_pim_neighbors) != NULL) {
+ int first = 1;
+
+ fprintf(fp, " %-3u %6s", vifi,
+ (v->uv_flags & MIFF_REGISTER)?"regist":
+ v->uv_name);
+ for (; n != NULL; n = n->next) {
+ if (first)
+ first = 0;
+ else
+ fprintf(fp, " %3s %6s", "", "");
+ fprintf(fp, " %-40s %-5u\n",
+ inet6_fmt(&n->address.sin6_addr),
+ n->timer);
+ }
+ }
+ }
+
+ 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 (debug || severity <= LOG_WARNING)
+ {
+ time_t t;
+ FILE *fp = log_fp ? log_fp : stderr;
+
+ gettimeofday(&now, NULL);
+ t = (time_t)now.tv_sec;
+ thyme = localtime(&t);
+ if (!debug)
+ fprintf(fp, "%s: ", progname);
+ fprintf(fp, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ if (syserr == 0)
+ fprintf(fp, "\n");
+ else
+ if (syserr < sys_nerr)
+ fprintf(fp, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(fp, ": 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;
+ int i;
+ u_int number_of_cache_mirrors = 0;
+ u_int number_of_groups = 0;
+ char joined_oifs[(sizeof(if_set) << 3) + 1];
+ char asserted_oifs[(sizeof(if_set) << 3) + 1];
+ cand_rp_t *rp;
+ kernel_cache_t *kernel_cache;
+ char oifs[(sizeof(if_set) << 3) + 1];
+ char pruned_oifs[(sizeof(if_set) << 3) + 1];
+ char leaves_oifs[(sizeof(if_set) << 3) + 1];
+ char incoming_iif[(sizeof(if_set) << 3) + 1];
+
+ fprintf(fp, "Multicast Routing Table\n%s",
+ " Source Group RP-addr 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++;
+ if ((r = g->grp_route) != (mrtentry_t *) NULL)
+ {
+ if (r->flags & MRTF_KERNEL_CACHE)
+ {
+ for (kernel_cache = r->kernel_cache;
+ kernel_cache != (kernel_cache_t *) NULL;
+ kernel_cache = kernel_cache->next)
+ number_of_cache_mirrors++;
+ }
+
+ /* Print the (*,G) routing info */
+ fprintf(fp, "---------------------------(*,G)----------------------------\n");
+ fprintf(fp, " %-15s", "IN6ADDR_ANY");
+ fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr));
+ fprintf(fp, " %-15s",
+ g->active_rp_grp ? inet6_fmt(&g->rpaddr.sin6_addr) : "NULL");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_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, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_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=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+
+ /* Print all (S,G) routing info */
+
+ for (r = g->mrtlink; r != (mrtentry_t *) NULL; r = r->grpnext)
+ {
+ fprintf(fp, "---------------------------(S,G)----------------------------\n");
+ if (r->flags & MRTF_KERNEL_CACHE)
+ number_of_cache_mirrors++;
+
+ /* Print the routing info */
+ fprintf(fp, " %-15s", inet6_fmt(&r->source->address.sin6_addr));
+ fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr));
+ fprintf(fp, " %-15s",
+ g->active_rp_grp ? inet6_fmt(&g->rpaddr.sin6_addr) : "NULL");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_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, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_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=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+ } /* for all groups */
+
+ /* Print the (*,*,R) routing entries */
+ fprintf(fp, "--------------------------(*,*,RP)--------------------------\n");
+ for (rp = cand_rp_list; rp != (cand_rp_t *) NULL; rp = rp->next)
+ {
+ if ((r = rp->rpentry->mrtlink) != (mrtentry_t *) NULL)
+ {
+ if (r->flags & MRTF_KERNEL_CACHE)
+ {
+ for (kernel_cache = r->kernel_cache;
+ kernel_cache != (kernel_cache_t *) NULL;
+ kernel_cache = kernel_cache->next)
+ number_of_cache_mirrors++;
+ }
+
+ /* Print the (*,*,RP) routing info */
+ fprintf(fp, " RP = %-15s", inet6_fmt(&r->source->address.sin6_addr));
+ fprintf(fp, " %-15s", "IN6ADDR_ANY");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_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, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs);
+ fprintf(fp, "Outgoing oifs: %-20s\n", oifs);
+ fprintf(fp, "Incoming : %-20s\n", incoming_iif);
+
+ fprintf(fp, "\nTIMERS: Entry=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+ } /* For all (*,*,RP) */
+
+ fprintf(fp, "Number of Groups: %u\n", number_of_groups);
+ fprintf(fp, "Number of Cache MIRRORs: %u\n\n", number_of_cache_mirrors);
+}
+
+
+/* TODO: modify the output for better redability */
+/*
+ * Dumps the local Cand-RP-set
+ */
+int
+dump_rp_set(fp)
+ FILE *fp;
+{
+ cand_rp_t *rp;
+ rp_grp_entry_t *rp_grp_entry;
+ grp_mask_t *grp_mask;
+
+ fprintf(fp, "---------------------------RP-Set----------------------------\n");
+ fprintf(fp, "Current BSR address: %s Prio: %d Timeout: %d\n",
+ inet6_fmt(&curr_bsr_address.sin6_addr), curr_bsr_priority,
+ pim_bootstrap_timer);
+ fprintf(fp, "%-40s %-3s Group prefix Prio Hold Age\n",
+ "RP-address", "IN");
+
+ for (rp = cand_rp_list; rp != (cand_rp_t *) NULL; rp = rp->next)
+ {
+
+ fprintf(fp, "%-40s %-3d ",
+ inet6_fmt(&rp->rpentry->address.sin6_addr),
+ rp->rpentry->incoming);
+ if ((rp_grp_entry = rp->rp_grp_next) != (rp_grp_entry_t *) NULL)
+ {
+ grp_mask = rp_grp_entry->group;
+ fprintf(fp, "%-16.16s %-4u %-4u %-3u\n",
+ net6name(&grp_mask->group_addr.sin6_addr,
+ &grp_mask->group_mask),
+ rp_grp_entry->priority, rp_grp_entry->advholdtime,
+ rp_grp_entry->holdtime);
+
+ for (rp_grp_entry = rp_grp_entry->rp_grp_next;
+ rp_grp_entry != (rp_grp_entry_t *) NULL;
+ rp_grp_entry = rp_grp_entry->rp_grp_next)
+ {
+ grp_mask = rp_grp_entry->group;
+ fprintf(fp, "%59.16s %-4u %-4u %-3u\n", /* XXX: hardcoding */
+ net6name(&grp_mask->group_addr.sin6_addr,
+ &grp_mask->group_mask),
+ rp_grp_entry->priority,
+ rp_grp_entry->advholdtime, rp_grp_entry->holdtime);
+ }
+ }
+ }
+ return (TRUE);
+}
diff --git a/usr.sbin/pim6sd/debug.h b/usr.sbin/pim6sd/debug.h
new file mode 100644
index 0000000..094ffd1
--- /dev/null
+++ b/usr.sbin/pim6sd/debug.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+
+#ifndef DEBUG_H
+#define DEBUG_H
+#include <sys/types.h>
+#include <stdio.h>
+
+extern unsigned long debug;
+extern int log_nmsgs;
+extern FILE *log_fp;
+#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_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 )
+
+#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 )
+
+/* CONFIG related */
+#define DEBUG_CONF 0x01000000
+
+#define DEBUG_ALL 0xffffffff
+#define DEBUG_SWITCH 0x80000000
+
+#define DEBUG_DEFAULT 0xffffffff/* default if "-d" given without value */
+
+#if defined(YIPS_DEBUG)
+#define YIPSDEBUG(lev,arg) if ((debug & (lev)) == (lev)) { arg; }
+#else
+#define YIPSDEBUG(lev,arg)
+#endif /* defined(YIPS_DEBUG) */
+
+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_nbrs __P((FILE *fp));
+extern void dump_pim_mrt __P((FILE *fp));
+extern int dump_rp_set __P((FILE *fp));
+extern void dump_stat __P(());
+
+#endif
diff --git a/usr.sbin/pim6sd/defs.h b/usr.sbin/pim6sd/defs.h
new file mode 100644
index 0000000..b9c3389
--- /dev/null
+++ b/usr.sbin/pim6sd/defs.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#include <sys/types.h>
+
+#define TRUE 1
+#define FALSE 0
+#define ELSE else /* To make emacs cc-mode happy */
+
+#define max( a , b ) ( ( a )<( b )?( b ):( a ) )
+
+typedef void ( *ihfunc_t ) __P( ( int , fd_set * ) );
+typedef void ( *cfunc_t ) __P( ( void * ) );
+
+int register_input_handler __P((int fd,ihfunc_t func));
+
+/* CONFIGCONFIGCONFIGCONFIG */
+
+#define HAVE_ROUTING_SOCKETS
+#define HAVE_SA_LEN
+#define RANDOM() random()
+
+#define PRINTF printf
+#define ALL_MCAST_GROUPS_LENGTH 8
+
+
+typedef u_int u_int32;
+typedef u_short u_int16;
+typedef u_char u_int8;
+
+
+extern char configfilename[];
+
+#endif
diff --git a/usr.sbin/pim6sd/inet6.c b/usr.sbin/pim6sd/inet6.c
new file mode 100644
index 0000000..ff34532
--- /dev/null
+++ b/usr.sbin/pim6sd/inet6.c
@@ -0,0 +1,283 @@
+/*
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include "defs.h"
+#include "vif.h"
+#include "inet6.h"
+#include <arpa/inet.h>
+
+/* flag if address to hostname resolution should be perfomed */
+int numerichost = TRUE;
+
+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_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_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][MAXHOSTNAMELEN];
+ static int ip6round = 0;
+ char *cp;
+ struct sockaddr_in6 sa6;
+ int flags = NI_WITHSCOPEID;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_len = sizeof(sa6);
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_addr = *addr;
+ sa6.sin6_scope_id = 0; /* XXX */
+
+ if (numerichost)
+ flags |= NI_NUMERICHOST;
+ getnameinfo((struct sockaddr *)&sa6, sa6.sin6_len, cp, MAXHOSTNAMELEN,
+ NULL, 0, flags);
+
+ 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/pim6sd/inet6.h b/usr.sbin/pim6sd/inet6.h
new file mode 100644
index 0000000..21ba6dc
--- /dev/null
+++ b/usr.sbin/pim6sd/inet6.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef INET6_H
+#define INET6_H
+#include "vif.h"
+
+extern int numerichost;
+
+extern int inet6_equal __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_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));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/kern.c b/usr.sbin/pim6sd/kern.c
new file mode 100644
index 0000000..73f610d
--- /dev/null
+++ b/usr.sbin/pim6sd/kern.c
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet6/ip6_mroute.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "inet6.h"
+#include "vif.h"
+#include "mrt.h"
+#include "debug.h"
+
+
+/*
+ * Open/init the multicast routing in the kernel and sets the MRT_ASSERT
+ * flag in the kernel.
+ *
+ */
+
+
+void
+k_init_pim(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, "Pim kernel initialization");
+}
+
+/*
+ * 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 PIM 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(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,"Buffer reception size for socket %d : %d in %d iterations",socket, bufsize, iter);
+}
+
+/*
+ * Set the default Hop Limit for the multicast packets outgoing from this
+ * socket.
+ */
+
+void
+k_set_hlim(int socket, int h)
+{
+ int hlim = h;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &hlim, sizeof(hlim)) < 0)
+ log(LOG_ERR,errno,"k_set_hlim");
+
+}
+
+/*
+ * Set/reset the IPV6_MULTICAST_LOOP. Set/reset is specified by "flag".
+ */
+
+
+void
+k_set_loop(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,"k_set_loop");
+}
+
+/*
+ * Set the IPV6_MULTICAST_IF option on local interface which has the
+ * specified index.
+ */
+
+
+void
+k_set_if(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(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)
+ syslog(LOG_WARNING, "Cannot join group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+/*
+ * Leave a multicats grp group on local interface ifa.
+ */
+
+void
+k_leave(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)
+ syslog(LOG_WARNING, "Cannot leave group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+/*
+ * Add a virtual interface in the kernel.
+ */
+
+void
+k_add_vif(int socket, vifi_t vifi, struct uvif * v)
+{
+ struct mif6ctl mc;
+
+ mc.mif6c_mifi = vifi;
+ mc.mif6c_flags = v->uv_flags;
+
+ mc.mif6c_pifi = v->uv_ifindex;
+
+ if ((v->uv_flags & MIFF_REGISTER))
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ log(LOG_DEBUG,0,"register vifi : %d , register pifi : %d ", vifi, 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(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(int socket, struct sockaddr_in6 * source, struct sockaddr_in6 * group)
+{
+ struct mf6cctl mc;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+
+ pim6dstat.kern_del_cache++;
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MFC, (char *) &mc, sizeof(mc)) < 0)
+ {
+ pim6dstat.kern_del_cache_fail++;
+ log(LOG_WARNING, errno, "setsockopt MRT6_DEL_MFC");
+ return FALSE;
+ }
+
+ syslog(LOG_DEBUG, "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, rp_addr)
+ int socket;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ vifi_t iif;
+ if_set *oifs;
+ struct sockaddr_in6 *rp_addr;
+{
+ struct mf6cctl mc;
+ vifi_t vifi;
+ struct uvif *v;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+ mc.mf6cc_parent = iif;
+
+
+ IF_ZERO(&mc.mf6cc_ifset);
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
+ {
+ if (IF_ISSET(vifi, oifs))
+ IF_SET(vifi, &mc.mf6cc_ifset);
+ else
+ IF_CLR(vifi, &mc.mf6cc_ifset);
+ }
+
+#ifdef PIM_REG_KERNEL_ENCAP
+ mc.mf6cc_rp_addr.s_addr = rp_addr;
+#endif
+
+ pim6dstat.kern_add_cache++;
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MFC, (char *) &mc,
+ sizeof(mc)) < 0)
+ {
+ pim6dstat.kern_add_cache_fail++;
+ log(LOG_WARNING, errno,
+ "setsockopt MRT_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)
+ {
+ pim6dstat.kern_sgcnt_fail++;
+ 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/pim6sd/kern.h b/usr.sbin/pim6sd/kern.h
new file mode 100644
index 0000000..f58503c
--- /dev/null
+++ b/usr.sbin/pim6sd/kern.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef KERN_H
+#define KERN_H
+#include "vif.h"
+#include "mrt.h"
+
+extern void k_set_rcvbuf __P((int socket, int bufsize, int minsize));
+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(());
+extern void k_stop_pim __P(());
+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, struct sockaddr_in6 *rp_addr));
+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));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/main.c b/usr.sbin/pim6sd/main.c
new file mode 100644
index 0000000..3ad1e7a
--- /dev/null
+++ b/usr.sbin/pim6sd/main.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include "pathnames.h"
+#include "defs.h"
+#include "debug.h"
+#include "mld6.h"
+#include "pim6.h"
+#include "vif.h"
+#include "routesock.h"
+#include "callout.h"
+#include "mrt.h"
+#include "timer.h"
+#include "rp.h"
+#include "kern.h"
+#include "cfparse.h"
+
+char configfilename[256] = _PATH_PIM6D_CONF;
+char versionstring[100];
+char logfilename[256] = _PATH_PIM6D_LOGFILE;
+
+/* TODO: not used
+static char genidfilename[] = _PATH_PIM6D_GENID;
+*/
+static char pidfilename[] = _PATH_PIM6D_PID;
+
+FILE *log_fp = stderr;
+char *progname;
+
+static int foreground = 0;
+static int sighandled = 0;
+
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+#define GOT_SIGALRM 0x10
+#define GOT_SIGINFO 0x20
+
+
+#define NHANDLERS 3
+
+static struct ihandler
+{
+ int fd; /* File descriptor */
+ ihfunc_t func; /* Function to call with &fd_set */
+} ihandlers[NHANDLERS];
+
+static int nhandlers = 0;
+
+static struct debugname
+{
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+ { "mld_proto", DEBUG_MLD_PROTO, 5 },
+ { "mld_timer", DEBUG_MLD_TIMER, 5 },
+ { "mld_member", DEBUG_MLD_MEMBER, 5 },
+ { "mld", DEBUG_MLD, 3 },
+ { "switch", DEBUG_SWITCH, 2 },
+ { "trace", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "timeout", DEBUG_TIMEOUT, 2 },
+ { "callout", DEBUG_TIMEOUT, 3 },
+ { "pkt", DEBUG_PKT, 2 },
+ { "packets", DEBUG_PKT, 2 },
+ { "interfaces", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "kernel", DEBUG_KERN, 2 },
+ { "cache", DEBUG_MFC, 1 },
+ { "mfc", DEBUG_MFC, 2 },
+ { "k_cache", DEBUG_MFC, 2 },
+ { "k_mfc", DEBUG_MFC, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "pim_detail", DEBUG_PIM_DETAIL, 5 },
+ { "pim_hello", DEBUG_PIM_HELLO, 5 },
+ { "pim_neighbors", DEBUG_PIM_HELLO, 5 },
+ { "pim_register", DEBUG_PIM_REGISTER, 5 },
+ { "registers", DEBUG_PIM_REGISTER, 2 },
+ { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "bsr", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "pim_asserts", DEBUG_PIM_ASSERT, 5 },
+ { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_rp", DEBUG_PIM_CAND_RP, 6 },
+ { "rp", DEBUG_PIM_CAND_RP, 2 },
+ { "pim_routes", DEBUG_PIM_MRT, 6 },
+ { "pim_routing", DEBUG_PIM_MRT, 6 },
+ { "pim_mrt", DEBUG_PIM_MRT, 5 },
+ { "pim_timers", DEBUG_PIM_TIMER, 5 },
+ { "pim_rpf", DEBUG_PIM_RPF, 6 },
+ { "rpf", DEBUG_RPF, 3 },
+ { "pim", DEBUG_PIM, 1 },
+ { "routes", DEBUG_MRT, 1 },
+ { "routing", DEBUG_MRT, 1 },
+ { "mrt", DEBUG_MRT, 1 },
+ { "routers", DEBUG_NEIGHBORS, 6 },
+ { "mrouters", DEBUG_NEIGHBORS, 7 },
+ { "neighbors", DEBUG_NEIGHBORS, 1 },
+ { "timers", DEBUG_TIMER, 1 },
+ { "asserts", DEBUG_ASSERT, 1 },
+ { "all", DEBUG_ALL, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
+
+/*
+ * Forward declarations.
+ */
+
+static void handler __P((int));
+static void timer __P((void *));
+static void cleanup __P((void));
+static void restart __P((int));
+static void cleanup __P((void));
+
+
+/* To shut up gcc -Wstrict-prototypes */
+
+int main __P((int argc, char **argv));
+
+int
+register_input_handler(fd, func)
+ int fd;
+ ihfunc_t func;
+{
+ if (nhandlers >= NHANDLERS)
+ return -1;
+
+ ihandlers[nhandlers].fd = fd;
+ ihandlers[nhandlers++].func = func;
+
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int dummy,
+ dummysigalrm;
+ FILE *fp;
+ struct timeval tv,
+ difftime,
+ curtime,
+ lasttime,
+ *timeout;
+ fd_set rfds,
+ readers;
+ int nfds=0,
+ n,
+ i,
+ secs;
+ extern char todaysversion[];
+ struct sigaction sa;
+ struct debugname *d;
+ char c;
+ int tmpd;
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0)
+ {
+ fprintf(stderr, "pim6sd: must be root\n");
+ exit(1);
+ }
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ argv++;
+ argc--;
+ while (argc > 0 && *argv[0] == '-')
+ {
+ if (strcmp(*argv, "-d") == 0)
+ {
+ if (argc > 1 && *(argv + 1)[0] != '-')
+ {
+ char *p,
+ *q;
+ int i,
+ len;
+ struct debugname *d;
+ int no=0;
+
+ argv++;
+ argc--;
+ debug = 0;
+ p = *argv;
+ q = NULL;
+ while (p)
+ {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ if(p[0]=='-')
+ {
+ no=1;
+ p++;
+ }
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0]))
+ {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ {
+ if ((j & d->level) == d->level)
+ {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ goto usage;
+ }
+ if(no)
+ {
+ debug &=~d->level;
+ no=0;
+ }
+ else
+ debug |= d->level;
+ p = q;
+ }
+ }
+ else
+ debug = DEBUG_DEFAULT;
+ }
+ else if (strcmp(*argv, "-c") == 0) {
+ if (argc > 1)
+ {
+ argv++;
+ argc--;
+ strcpy(configfilename, *argv);
+ }
+ else
+ goto usage;
+ }
+ else if (strcmp(*argv, "-f") == 0)
+ foreground = 1;
+ else
+ goto usage;
+
+ argv++;
+ argc--;
+ }
+
+ if (argc > 0)
+ {
+usage:
+ tmpd = 0xffffffff;
+ fprintf(stderr, "usage: pim6sd [-c configfile] [-d [debug_level][,debug_level]]\n");
+
+ fprintf(stderr, "debug levels: ");
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++)
+ {
+ if ((tmpd & d->level) == d->level)
+ {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ exit(1);
+ }
+
+ if (debug != 0)
+ {
+ tmpd = debug;
+ fprintf(stderr, "debug level 0x%lx ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++)
+ {
+ if ((tmpd & d->level) == d->level)
+ {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef LOG_DAEMON
+ (void) openlog("pim6sd", LOG_PID, LOG_DAEMON);
+// (void) setlogmask(LOG_UPTO(LOG_NOTICE));
+#else
+ (void) openlog("pim6sd", LOG_PID);
+#endif /* LOG_DAEMON */
+ /* open a log file */
+ if ((log_fp = fopen(logfilename, "w")) == NULL)
+ log(LOG_ERR, errno, "fopen(%s)", logfilename);
+ setlinebuf(log_fp);
+
+ sprintf(versionstring, "pim6sd version %s", todaysversion);
+
+ log(LOG_INFO, 0, "%s starting", versionstring);
+
+ /*
+ * TODO: XXX: use a combination of time and hostid to initialize the
+ * random generator.
+ */
+
+#ifdef SYSV
+ srand48(time(NULL));
+#else
+ srandom(time(NULL));
+#endif
+
+ callout_init();
+ init_mld6();
+ init_pim6();
+
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+
+ init_pim6_mrt();
+ init_timers();
+
+ /* TODO: check the kernel DVMRP/MROUTED/PIM support version */
+
+ init_vifs();
+ init_rp6_and_bsr6(); /* Must be after init_vifs() */
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
+ sigaction(SIGINFO, &sa, NULL);
+
+ FD_ZERO(&readers);
+ FD_SET(mld6_socket, &readers);
+ for (i = 0; i < nhandlers; i++)
+ {
+ FD_SET(ihandlers[i].fd, &readers);
+ if (ihandlers[i].fd >= nfds)
+ nfds = ihandlers[i].fd + 1;
+ }
+
+ IF_DEBUG(DEBUG_IF)
+ dump_vifs(log_fp);
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(log_fp);
+
+ /* schedule first timer interrupt */
+ timer_setTimer(timer_interval, timer, NULL);
+
+ if (foreground == 0)
+ {
+ /* Detach from the terminal */
+#ifdef TIOCNOTTY
+ int t;
+#endif /* TIOCNOTTY */
+
+ if (fork())
+ exit(0);
+
+#ifdef HAVE_ROUTING_SOCKETS
+ pid = getpid();
+#endif
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+#if defined(SYSV) || defined(linux)
+ (void) setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open("/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 */
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", (int) getpid());
+ (void) fclose(fp);
+ }
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ dummysigalrm = SIGALRM;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
+ for (;;)
+ {
+ bcopy((char *) &readers, (char *) &rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else
+ {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
+
+ if (sighandled)
+ {
+ if (sighandled & GOT_SIGINT)
+ {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP)
+ {
+ sighandled &= ~GOT_SIGHUP;
+ restart(SIGHUP);
+ }
+ if (sighandled & GOT_SIGINFO)
+ {
+ sighandled &= ~GOT_SIGINFO;
+ dump_stat();
+ }
+ if (sighandled & GOT_SIGUSR1)
+ {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump(SIGUSR1);
+ }
+ if (sighandled & GOT_SIGUSR2)
+ {
+ sighandled &= ~GOT_SIGUSR2;
+#ifdef notyet
+ cdump(SIGUSR2);
+#else
+ cfparse(0, 1); /* reset debug level */
+#endif
+ }
+ if (sighandled & GOT_SIGALRM)
+ {
+ sighandled &= ~GOT_SIGALRM;
+ timer(&dummysigalrm);
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0)
+ {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second, or if there is
+ * a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the time doesn't
+ * drift too badly.
+ *
+ * If the timeout handlers took more than 1 second, age the timeout
+ * queue again. XXX This introduces the potential for infinite
+ * loops!
+ */
+ do
+ {
+ /*
+ * If the select timed out, then there's no other activity to
+ * account for and we don't need to call gettimeofday.
+ */
+ if (n == 0)
+ {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ }
+ else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "TIMEOUT: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec);
+#endif
+ while (difftime.tv_usec > 1000000)
+ {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0)
+ {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0)
+ if (secs == 0 || difftime.tv_sec > 0)
+ {
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "\taging callouts: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec);
+#endif
+ age_callout_queue(difftime.tv_sec);
+ }
+ secs = -1;
+ } while (difftime.tv_sec > 0);
+
+ /* Handle sockets */
+ if (n > 0)
+ {
+ for (i = 0; i < nhandlers; i++)
+ {
+ if (FD_ISSET(ihandlers[i].fd, &rfds))
+ {
+ (*ihandlers[i].func) (ihandlers[i].fd, &rfds);
+ }
+ }
+ }
+
+ } /* Main loop */
+
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
+}
+
+/*
+ * The 'virtual_time' variable is initialized to a value that will cause the
+ * first invocation of timer() to send a probe or route report to all vifs
+ * and send group membership queries to all subnets for which this router is
+ * querier. This first invocation occurs approximately timer_interval
+ * seconds after the router starts up. Note that probes for neighbors and
+ * queries for group memberships are also sent at start-up time, as part of
+ * initial- ization. This repetition after a short interval is desirable for
+ * quickly building up topology and membership information in the presence of
+ * possible packet loss.
+ *
+ * 'virtual_time' advances at a rate that is only a crude approximation of real
+ * time, because it does not take into account any time spent processing, and
+ * because the timer intervals are sometimes shrunk by a random amount to
+ * avoid unwanted synchronization with other routers.
+ */
+
+u_long virtual_time = 0;
+
+/*
+ * Timer routine. Performs all perodic functions: aging interfaces, quering
+ * neighbors and members, etc... The granularity is equal to timer_interval.
+ * this granularity is configurable ( see file pim6sd.conf.sample)
+ */
+
+static void
+timer(i)
+ void *i;
+{
+ age_vifs(); /* Timeout neighbors and groups */
+ age_routes(); /* Timeout routing entries */
+ age_misc(); /* Timeout the rest (Cand-RP list, etc) */
+
+ virtual_time += timer_interval;
+ timer_setTimer(timer_interval, timer, NULL);
+}
+
+/*
+ * Performs all necessary functions to quit gracefully
+ */
+/* TODO: implement all necessary stuff */
+
+static void
+cleanup()
+{
+
+ /*
+ * TODO: XXX (not in the spec): if I am the BSR, somehow inform the other
+ * routers I am going down and need to elect another BSR? (probably by
+ * sending a the Cand-RP-set with my_priority=LOWEST?)
+ *
+ */
+
+ k_stop_pim(mld6_socket);
+}
+
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived so that the
+ * main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig)
+ {
+ case SIGALRM:
+ sighandled |= GOT_SIGALRM;
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
+
+ case SIGINFO:
+ sighandled |= GOT_SIGINFO;
+ break;
+ }
+}
+
+
+/* TODO: not verified */
+/*
+ * Restart the daemon
+ */
+
+static void
+restart(i)
+ int i;
+{
+
+ log(LOG_NOTICE, 0, "% restart", versionstring);
+
+ /*
+ * reset all the entries
+ */
+ /*
+ * TODO: delete?
+ free_all_routes();
+ */
+
+ free_all_callouts();
+ stop_all_vifs();
+ nhandlers=0;
+ k_stop_pim(mld6_socket);
+ close(mld6_socket);
+ close(pim6_socket);
+ close(udp_socket);
+
+ /*
+ * start processing again
+ */
+
+ init_mld6();
+ init_pim6();
+
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+
+ init_pim6_mrt();
+ init_vifs();
+
+ /* schedule timer interrupts */
+ timer_setTimer(timer_interval, timer, NULL);
+}
diff --git a/usr.sbin/pim6sd/mld6.c b/usr.sbin/pim6sd/mld6.c
new file mode 100644
index 0000000..9c9c109
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6.c
@@ -0,0 +1,555 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "mld6.h"
+#include "kern.h"
+#include "defs.h"
+#include "inet6.h"
+#include "debug.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "trace.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));
+
+#ifndef IP6OPT_ROUTER_ALERT /* XXX to be compatible older systems */
+#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/pim6sd/mld6.h b/usr.sbin/pim6sd/mld6.h
new file mode 100644
index 0000000..dcc43f1
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6.h
@@ -0,0 +1,90 @@
+/*
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MLD6_H
+#define MLD6_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#define RECV_BUF_SIZE 64*1024
+#define SO_RECV_BUF_SIZE_MAX 256*1024
+#define SO_RECV_BUF_SIZE_MIN 48*1024
+#define MINHLIM 1
+
+
+/*
+ * 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
+
+extern int mld6_socket;
+extern char *mld6_recv_buf;
+extern struct sockaddr_in6 allrouters_group;
+extern struct sockaddr_in6 allnodes_group;
+extern char *mld6_send_buf;
+
+void init_mld6 __P(());
+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));
+
+#endif
diff --git a/usr.sbin/pim6sd/mld6_proto.c b/usr.sbin/pim6sd/mld6_proto.c
new file mode 100644
index 0000000..4533aac
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6_proto.c
@@ -0,0 +1,632 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet/icmp6.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "mld6.h"
+#include "vif.h"
+#include "debug.h"
+#include "inet6.h"
+#include "route.h"
+#include "callout.h"
+#include "timer.h"
+
+extern struct in6_addr in6addr_any;
+
+typedef struct
+{
+ mifi_t mifi;
+ struct listaddr *g;
+ int q_time;
+} cbk_t;
+
+
+/*
+ * Forward declarations.
+ */
+static void DelVif __P((void *arg));
+static int SetTimer __P((int mifi, struct listaddr * g));
+static int DeleteTimer __P((int id));
+static void SendQuery __P((void *arg));
+static int SetQueryTimer
+__P((struct listaddr * g, int mifi, int to_expire,
+ int q_time));
+
+/*
+ * Send group membership queries on that interface if I am querier.
+ */
+void
+query_groups(v)
+ register struct uvif *v;
+{
+ register struct listaddr *g;
+
+ v->uv_gq_timer = MLD6_QUERY_INTERVAL;
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, (struct in6_addr *)&in6addr_any, v->uv_ifindex,
+ MLD6_QUERY_RESPONSE_INTERVAL, 0, 1);
+ v->uv_out_mld_query++;
+ }
+
+ /*
+ * Decrement the old-hosts-present timer for each active group on that
+ * vif.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (g->al_old > timer_interval)
+ g->al_old -= timer_interval;
+ else
+ g->al_old = 0;
+}
+
+
+/*
+ * Process an incoming host membership query
+ */
+void
+accept_listener_query(src, dst, group, tmo)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst,
+ *group;
+ int tmo;
+{
+ register int mifi;
+ register struct uvif *v;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ /* Ignore my own 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;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accepting multicast listener query: "
+ "src %s, dst %s, grp %s",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
+ inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_query++;
+
+ 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_DEBUG, 0,
+ "accepting multicast listener report: "
+ "src %s,dst %s, grp %s",
+ inet6_fmt(&src->sin6_addr),inet6_fmt(dst),
+ inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_report++;
+
+ /*
+ * Look for the group in our group list; if found, reset its timer.
+ */
+
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(&group_sa, &g->al_addr))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG,0,
+ "The group already exist");
+
+ g->al_reporter = *src;
+
+ /* delete old timers, set a timer for expiration */
+
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ if (g->al_query)
+ g->al_query = DeleteTimer(g->al_query);
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timerid = SetTimer(mifi, g);
+ add_leaf(mifi, NULL, &group_sa);
+ break;
+ }
+ }
+
+ /*
+ * If not found, add it to the list and update kernel cache.
+ */
+ if (g == NULL)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG,0,
+ "The group don't exist , trying to add it");
+
+ g = (struct listaddr *) malloc(sizeof(struct listaddr));
+ if (g == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ g->al_addr = group_sa;
+ g->al_old = 0;
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ g->al_reporter = *src;
+ g->al_timerid = SetTimer(mifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+ time(&g->al_ctime);
+
+ add_leaf(mifi, NULL, &group_sa);
+ }
+}
+
+
+/* TODO: send PIM prune message if the last member? */
+void
+accept_listener_done(src, dst, group)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst,
+ *group;
+{
+ register mifi_t mifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ /* Don't create routing entries for the LAN scoped addresses */
+
+ if (IN6_IS_ADDR_MC_NODELOCAL(group)) /* sanity? */
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_done: address multicast node local(%s),"
+ " ignore it...", inet6_fmt(group));
+ return;
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(group))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_done: address multicast link local(%s), "
+ "ignore it ...", inet6_fmt(group));
+ return;
+ }
+
+ if ((mifi = find_vif_direct_local(src)) == NO_VIF)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_done: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accepting listener done message: src %s, dst% s, grp %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst), inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_done++;
+
+ if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
+ return;
+
+ /*
+ * Look for the group in our group list in order to set up a
+ * short-timeout query.
+ */
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(&group_sa, &g->al_addr))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "[accept_done_message] %d %d \n",
+ g->al_old, g->al_query);
+
+ /*
+ * Ignore the done message if there are old hosts present
+ */
+ if (g->al_old)
+ return;
+
+ /*
+ * still waiting for a reply to a query, ignore the done
+ */
+ if (g->al_query)
+ return;
+
+ /** delete old timer set a timer for expiration **/
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+
+ /** send a group specific querry **/
+ g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE) *
+ (MLD6_LAST_LISTENER_QUERY_COUNT + 1);
+ if (v->uv_flags & VIFF_QUERIER &&
+ (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0,
+ &v->uv_linklocal->pa_addr, NULL,
+ &g->al_addr.sin6_addr,
+ v->uv_ifindex,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
+ v->uv_out_mld_query++;
+ }
+ g->al_query = SetQueryTimer(g, mifi,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL);
+ g->al_timerid = SetTimer(mifi, g);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Time out record of a group membership on a vif
+ */
+static void
+DelVif(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *) arg;
+ mifi_t mifi = cbk->mifi;
+ struct uvif *v = &uvifs[mifi];
+ struct listaddr *a,
+ **anp,
+ *g = cbk->g;
+
+ /*
+ * Group has expired delete all kernel cache entries with this group
+ */
+ if (g->al_query)
+ DeleteTimer(g->al_query);
+
+ delete_leaf(mifi, NULL, &g->al_addr);
+
+ /* increment statistics */
+ v->uv_listener_timo++;
+
+ anp = &(v->uv_groups);
+ while ((a = *anp) != NULL)
+ {
+ if (a == g)
+ {
+ *anp = a->al_next;
+ free((char *) a);
+ }
+ else
+ {
+ anp = &a->al_next;
+ }
+ }
+
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to delete the record of a group membership on a vif.
+ */
+static int
+SetTimer(mifi, g)
+ mifi_t mifi;
+ struct listaddr *g;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->mifi = mifi;
+ cbk->g = g;
+ return timer_setTimer(g->al_timer, DelVif, cbk);
+}
+
+
+/*
+ * Delete a timer that was set above.
+ */
+static int
+DeleteTimer(id)
+ int id;
+{
+ timer_clearTimer(id);
+ return 0;
+}
+
+
+/*
+ * Send a group-specific query.
+ */
+static void
+SendQuery(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *) arg;
+ register struct uvif *v = &uvifs[cbk->mifi];
+
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, &cbk->g->al_addr.sin6_addr, v->uv_ifindex,
+ cbk->q_time, 0, 1);
+ v->uv_out_mld_query++;
+ }
+ cbk->g->al_query = 0;
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to send a group-specific query.
+ */
+static int
+SetQueryTimer(g, mifi, to_expire, q_time)
+ struct listaddr *g;
+ mifi_t mifi;
+ int to_expire;
+ int q_time;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->q_time = q_time;
+ cbk->mifi = mifi;
+ return timer_setTimer(to_expire, SendQuery, cbk);
+}
+
+/*
+ * Checks for MLD listener: returns TRUE if there is a receiver for the group
+ * on the given uvif, or returns FALSE otherwise.
+ */
+int
+check_multicast_listener(v, group)
+ struct uvif *v;
+ struct sockaddr_in6 *group;
+{
+ register struct listaddr *g;
+
+ /*
+ * Look for the group in our listener list;
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(group, &g->al_addr))
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/usr.sbin/pim6sd/mld6_proto.h b/usr.sbin/pim6sd/mld6_proto.h
new file mode 100644
index 0000000..644eb8d
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6_proto.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MLD6_PROTO_H
+#define MLD6_PROTO_H
+
+#include "vif.h"
+
+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));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/mrt.c b/usr.sbin/pim6sd/mrt.c
new file mode 100644
index 0000000..ae21aa3
--- /dev/null
+++ b/usr.sbin/pim6sd/mrt.c
@@ -0,0 +1,1495 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mrt.h"
+#include "vif.h"
+#include "rp.h"
+#include "pimd.h"
+#include "debug.h"
+#include "mld6.h"
+#include "inet6.h"
+#include "timer.h"
+#include "route.h"
+#include "kern.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));
+
+static void move_kernel_cache __P((mrtentry_t * mrtentry_ptr,
+ u_int16 flags));
+
+void
+init_pim6_mrt()
+{
+
+ /* TODO: delete any existing routing table */
+
+ /* Initialize the source list */
+ /* The first entry has address 'IN6ADDR_ANY' 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;
+ RESET_TIMER(srclist->timer);
+ srclist->cand_rp = (cand_rp_t *) NULL;
+
+ /* Initialize the group list */
+ /* The first entry has address 'IN6ADDR_ANY' 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;
+ grplist->rpnext = (grpentry_t *) NULL;
+ grplist->rpprev = (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;
+ memset(&grplist->rpaddr, 0, sizeof(struct sockaddr_in6));
+ grplist->rpaddr.sin6_len = sizeof(struct sockaddr_in6);
+ grplist->rpaddr.sin6_family = AF_INET6;
+ grplist->mrtlink = (mrtentry_t *) NULL;
+ grplist->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grplist->grp_route = (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;
+ mrtentry_t *mrtentry_ptr_wc;
+ mrtentry_t *mrtentry_ptr_pmbr;
+ mrtentry_t *mrtentry_ptr_2;
+ rpentry_t *rpentry_ptr=NULL;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+ if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
+ return (mrtentry_t *) NULL;
+ }
+
+ if (flags & MRTF_SG)
+ if (!inet6_valid_host(source))
+ return (mrtentry_t *) NULL;
+
+ if (create == DONT_CREATE)
+ {
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+ if (search_grplist(group, &grpentry_ptr) == FALSE)
+ {
+ /* Group not found. Return the (*,*,RP) entry */
+ if (flags & MRTF_PMBR)
+ {
+ rpentry_ptr = rp_match(group);
+ if (rpentry_ptr != (rpentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ }
+ return (mrtentry_t *) NULL;
+ }
+ /* Search for the source */
+ if (flags & MRTF_SG)
+ {
+ if (search_grpmrtlink(grpentry_ptr, source,
+ &mrtentry_ptr) == TRUE)
+ {
+ /* Exact (S,G) entry found */
+ return (mrtentry_ptr);
+ }
+ }
+ /* No (S,G) entry. Return the (*,G) entry (if exist) */
+ if ((flags & MRTF_WC) &&
+ (grpentry_ptr->grp_route != (mrtentry_t *) NULL))
+ return (grpentry_ptr->grp_route);
+ }
+
+ /* Return the (*,*,RP) entry */
+
+ if (flags & MRTF_PMBR)
+ {
+ rpentry_ptr = (rpentry_t *) NULL;
+ if (group != NULL)
+ rpentry_ptr = rp_match(group);
+ else
+ if (source != NULL)
+ rpentry_ptr = rp_find(source);
+ if (rpentry_ptr != (rpentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+
+ /* Creation allowed */
+
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+
+ grpentry_ptr = create_grpentry(group);
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ {
+ return (mrtentry_t *) NULL;
+ }
+
+ if (grpentry_ptr->active_rp_grp == (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr = rp_grp_match(group);
+
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ {
+ if ((grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route == (mrtentry_t *) NULL))
+ {
+ /* New created grpentry. Delete it. */
+
+ delete_grpentry(grpentry_ptr);
+ }
+
+ return (mrtentry_t *) NULL;
+ }
+
+ rpentry_ptr = rp_grp_entry_ptr->rp->rpentry;
+ grpentry_ptr->active_rp_grp = rp_grp_entry_ptr;
+ grpentry_ptr->rpaddr = rpentry_ptr->address;
+
+ /* Link to the top of the rp_grp_chain */
+
+ grpentry_ptr->rpnext = rp_grp_entry_ptr->grplink;
+ rp_grp_entry_ptr->grplink = grpentry_ptr;
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr;
+ }
+ else
+ rpentry_ptr = grpentry_ptr->active_rp_grp->rp->rpentry;
+ }
+
+ mrtentry_ptr_wc = mrtentry_ptr_pmbr = (mrtentry_t *) NULL;
+
+ if (flags & MRTF_WC)
+ {
+ /* Setup the (*,G) routing entry */
+
+ mrtentry_ptr_wc = create_mrtentry((srcentry_t *) NULL, grpentry_ptr,
+ MRTF_WC);
+
+ if (mrtentry_ptr_wc == (mrtentry_t *) NULL)
+ {
+ if (grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ {
+ /* New created grpentry. Delete it. */
+
+ delete_grpentry(grpentry_ptr);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+ if (mrtentry_ptr_wc->flags & MRTF_NEW)
+ {
+ mrtentry_ptr_pmbr = rpentry_ptr->mrtlink;
+
+ /* Copy the oif list from the (*,*,RP) entry */
+
+ if (mrtentry_ptr_pmbr != (mrtentry_t *) NULL)
+ {
+ VOIF_COPY(mrtentry_ptr_pmbr, mrtentry_ptr_wc);
+ }
+
+ mrtentry_ptr_wc->incoming = rpentry_ptr->incoming;
+ mrtentry_ptr_wc->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr_wc->metric = rpentry_ptr->metric;
+ mrtentry_ptr_wc->preference = rpentry_ptr->preference;
+ move_kernel_cache(mrtentry_ptr_wc, 0);
+
+#ifdef RSRR
+ rsrr_cache_bring_up(mrtentry_ptr_wc);
+#endif /* RSRR */
+
+ }
+
+ if (!(flags & MRTF_SG))
+ {
+ return (mrtentry_ptr_wc);
+ }
+ }
+
+ if (flags & MRTF_SG)
+ {
+ /* Setup the (S,G) routing entry */
+ srcentry_ptr = create_srcentry(source);
+ if (srcentry_ptr == (srcentry_t *) NULL)
+ {
+ /* TODO: XXX: The MRTF_NEW flag check may be misleading?? check */
+
+ if (((grpentry_ptr->grp_route == (mrtentry_t *) NULL)
+ || ((grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route->flags & MRTF_NEW)))
+ && (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->grp_route == (mrtentry_t *) NULL)
+ || ((grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route->flags & MRTF_NEW)))
+ && (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)
+ {
+ if ((mrtentry_ptr_2 = grpentry_ptr->grp_route)
+ == (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr_2 = rpentry_ptr->mrtlink;
+ }
+ /* Copy the oif list from the existing (*,G) or (*,*,RP) entry */
+ if (mrtentry_ptr_2 != (mrtentry_t *) NULL)
+ {
+ VOIF_COPY(mrtentry_ptr_2, mrtentry_ptr);
+ if (flags & MRTF_RP)
+ {
+ /* ~(S,G) prune entry */
+ mrtentry_ptr->incoming = mrtentry_ptr_2->incoming;
+ mrtentry_ptr->upstream = mrtentry_ptr_2->upstream;
+ mrtentry_ptr->metric = mrtentry_ptr_2->metric;
+ mrtentry_ptr->preference = mrtentry_ptr_2->preference;
+ mrtentry_ptr->flags |= MRTF_RP;
+ }
+ }
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ mrtentry_ptr->incoming = srcentry_ptr->incoming;
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+ }
+ move_kernel_cache(mrtentry_ptr, 0);
+#ifdef RSRR
+ rsrr_cache_bring_up(mrtentry_ptr);
+#endif /* RSRR */
+ }
+ return (mrtentry_ptr);
+ }
+
+ if (flags & MRTF_PMBR)
+ {
+ /* Get/return the (*,*,RP) routing entry */
+
+ if (group != NULL)
+ rpentry_ptr = rp_match(group);
+ else
+ if (source != NULL)
+ {
+ rpentry_ptr = rp_find(source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ {
+ return (mrtentry_t *) NULL;
+ }
+ }
+ else
+ return (mrtentry_t *) NULL; /* source == group ==
+ * IN6ADDR_ANY */
+
+ if (rpentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ mrtentry_ptr = create_mrtentry(rpentry_ptr, (grpentry_t *) NULL,
+ MRTF_PMBR);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ mrtentry_ptr->incoming = rpentry_ptr->incoming;
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ return (mrtentry_ptr);
+ }
+
+ return (mrtentry_t *) NULL;
+}
+
+
+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->flags & MRTF_KERNEL_CACHE)
+ /* Delete the kernel cache first */
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ 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)
+ && (mrtentry_ptr->group->grp_route == (mrtentry_t *) NULL))
+ {
+ /* Delete the group entry if it has no (*,G) routing 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;
+
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ {
+ if (grpentry_ptr->grp_route->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(grpentry_ptr->grp_route);
+ FREE_MRTENTRY(grpentry_ptr->grp_route);
+ }
+
+ /* Delete from the rp_grp_entry chain */
+ if (grpentry_ptr->active_rp_grp != (rp_grp_entry_t *) NULL)
+ {
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr->rpprev;
+ if (grpentry_ptr->rpprev != (grpentry_t *) NULL)
+ grpentry_ptr->rpprev->rpnext = grpentry_ptr->rpnext;
+ else
+ grpentry_ptr->active_rp_grp->grplink = grpentry_ptr->rpnext;
+ }
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_next)
+ {
+ mrtentry_next = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ /* Delete the kernel cache first */
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ 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;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_wc;
+ mrtentry_t *mrtentry_rp;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ /* Delete the kernel cache first */
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+
+#ifdef RSRR
+ /* Tell the reservation daemon */
+ rsrr_cache_clean(mrtentry_ptr);
+#endif /* RSRR */
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) mrtentry */
+ mrtentry_ptr->source->mrtlink = (mrtentry_t *) NULL;
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (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)
+ {
+ /*
+ * All (S,G) MRT entries are gone. Allow creating (*,G)
+ * MFC entries.
+ */
+ mrtentry_rp
+ = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ mrtentry_wc = mrtentry_ptr->group->grp_route;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ mrtentry_rp->flags &= ~MRTF_MFC_CLONE_SG;
+ if (mrtentry_wc != (mrtentry_t *) NULL)
+ mrtentry_wc->flags &= ~MRTF_MFC_CLONE_SG;
+ else
+ {
+ /*
+ * Delete the group entry if it has no (*,G) routing
+ * 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;
+ }
+ else
+ {
+ /* This mrtentry should be (*,G) */
+ grpentry_ptr = mrtentry_ptr->group;
+ grpentry_ptr->grp_route = (mrtentry_t *) NULL;
+
+ if (grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ /* Delete the group entry if it has no (S,G) entries */
+ delete_grpentry(grpentry_ptr);
+ }
+
+ 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;
+ RESET_TIMER(srcentry_ptr->timer);
+ srcentry_ptr->cand_rp = (cand_rp_t *) NULL;
+ 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;
+ }
+
+ /*
+ * TODO: XXX: Note that this is NOT a (*,G) routing entry, but simply a
+ * group entry, probably used to search the routing table (to find (S,G)
+ * entries for example.) To become (*,G) routing entry, we must setup
+ * grpentry_ptr->grp_route
+ */
+
+ grpentry_ptr->group = *group;
+ memset(&grpentry_ptr->rpaddr, 0, sizeof(struct sockaddr_in6));
+ grpentry_ptr->rpaddr.sin6_len = sizeof(struct sockaddr_in6);
+ grpentry_ptr->rpaddr.sin6_family = AF_INET6;
+ grpentry_ptr->mrtlink = (mrtentry_t *) NULL;
+ grpentry_ptr->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grpentry_ptr->grp_route = (mrtentry_t *) NULL;
+ grpentry_ptr->rpnext = (grpentry_t *) NULL;
+ grpentry_ptr->rpprev = (grpentry_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_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->joined_oifs);
+ IF_ZERO(&mrtentry_ptr->leaves);
+ IF_ZERO(&mrtentry_ptr->pruned_oifs);
+ IF_ZERO(&mrtentry_ptr->asserted_oifs);
+ IF_ZERO(&mrtentry_ptr->oifs);
+ mrtentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ mrtentry_ptr->metric = 0;
+ mrtentry_ptr->preference = 0;
+ mrtentry_ptr->pmbr_addr.sin6_addr = in6addr_any;
+ mrtentry_ptr->pmbr_addr.sin6_len = sizeof(struct sockaddr_in6);
+ mrtentry_ptr->pmbr_addr.sin6_family = AF_INET6;
+
+#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->vif_timers = (u_int16 *) malloc(sizeof(u_int16) * numvifs);
+ mrtentry_ptr->vif_deletion_delay =
+ (u_int16 *) malloc(sizeof(u_int16) * numvifs);
+ vif_numbers = numvifs;
+#else
+ mrtentry_ptr->vif_timers =
+ (u_int16 *) malloc(sizeof(u_int16) * total_interfaces);
+ mrtentry_ptr->vif_deletion_delay =
+ (u_int16 *) malloc(sizeof(u_int16) * total_interfaces);
+ vif_numbers = total_interfaces;
+#endif /* SAVE_MEMORY */
+ if ((mrtentry_ptr->vif_timers == (u_int16 *) NULL) ||
+ (mrtentry_ptr->vif_deletion_delay == (u_int16 *) 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->vif_timers; i < vif_numbers;
+ i++, i_ptr++)
+ RESET_TIMER(*i_ptr);
+ for (i = 0, i_ptr = mrtentry_ptr->vif_deletion_delay; i < vif_numbers;
+ i++, i_ptr++)
+ RESET_TIMER(*i_ptr);
+
+ mrtentry_ptr->flags = MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->timer);
+ RESET_TIMER(mrtentry_ptr->jp_timer);
+ RESET_TIMER(mrtentry_ptr->rs_timer);
+ RESET_TIMER(mrtentry_ptr->assert_timer);
+ RESET_TIMER(mrtentry_ptr->assert_rate_timer);
+ mrtentry_ptr->kernel_cache = (kernel_cache_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;
+
+
+ if (flags & MRTF_SG)
+ {
+ /* (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);
+ }
+
+ if (flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ return (grpentry_ptr->grp_route);
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ grpentry_ptr->grp_route = r_new;
+ r_new->flags |= (MRTF_WC | MRTF_RP);
+ return (r_new);
+ }
+
+ if (flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+
+ if (srcentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ return (srcentry_ptr->mrtlink);
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ srcentry_ptr->mrtlink = r_new;
+ r_new->flags |= (MRTF_PMBR | MRTF_RP);
+ return (r_new);
+ }
+
+ return (mrtentry_t *) NULL;
+}
+
+
+/*
+ * Delete all kernel cache for this mrtentry
+ */
+void
+delete_mrtentry_all_kernel_cache(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ kernel_cache_t *kernel_cache_prev;
+ kernel_cache_t *kernel_cache_ptr;
+
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ {
+ return;
+ }
+
+ /* Free all kernel_cache entries */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;)
+ {
+ kernel_cache_prev = kernel_cache_ptr;
+ kernel_cache_ptr = kernel_cache_ptr->next;
+ k_del_mfc(mld6_socket, &kernel_cache_prev->source,
+ &kernel_cache_prev->group);
+ free((char *) kernel_cache_prev);
+ }
+ mrtentry_ptr->kernel_cache = (kernel_cache_t *) NULL;
+
+ /* turn off the cache flag(s) */
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+}
+
+
+void
+delete_single_kernel_cache(mrtentry_ptr, kernel_cache_ptr)
+ mrtentry_t *mrtentry_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+{
+ if (kernel_cache_ptr->prev == (kernel_cache_t *) NULL)
+ {
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr->next;
+ if (mrtentry_ptr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ }
+ else
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s",
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr),
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr));
+ k_del_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ free((char *) kernel_cache_ptr);
+}
+
+
+void
+delete_single_kernel_cache_addr(mrtentry_ptr, source, group)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ kernel_cache_t *kernel_cache_ptr;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+
+ /* Find the exact (S,G) kernel_cache entry */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ if (inet6_lessthan(&kernel_cache_ptr->group, group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->group, group))
+ return; /* Not found */
+ if (inet6_lessthan(&kernel_cache_ptr->source, source))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->source, source))
+ return; /* Not found */
+ /* Found exact match */
+ break;
+ }
+
+ if (kernel_cache_ptr == (kernel_cache_t *) NULL)
+ return;
+
+ /* Found. Delete it */
+ if (kernel_cache_ptr->prev == (kernel_cache_t *) NULL)
+ {
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr->next;
+ if (mrtentry_ptr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ }
+ else
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s",
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr),
+ inet6_fmt(&kernel_cache_ptr->group.sin6_addr));
+ k_del_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ free((char *) kernel_cache_ptr);
+}
+
+
+/*
+ * Installs kernel cache for (source, group). Assumes mrtentry_ptr is the
+ * correct entry.
+ */
+void
+add_kernel_cache(mrtentry_ptr, source, group, flags)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ u_int16 flags;
+{
+ kernel_cache_t *kernel_cache_next;
+ kernel_cache_t *kernel_cache_prev;
+ kernel_cache_t *kernel_cache_new;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ move_kernel_cache(mrtentry_ptr, flags);
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) */
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ return;
+ kernel_cache_new = (kernel_cache_t *) malloc(sizeof(kernel_cache_t));
+ kernel_cache_new->next = (kernel_cache_t *) NULL;
+ kernel_cache_new->prev = (kernel_cache_t *) NULL;
+ kernel_cache_new->source = *source;
+ kernel_cache_new->group = *group;
+ kernel_cache_new->sg_count.pktcnt = 0;
+ kernel_cache_new->sg_count.bytecnt = 0;
+ kernel_cache_new->sg_count.wrong_if = 0;
+ mrtentry_ptr->kernel_cache = kernel_cache_new;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+ return;
+ }
+
+ kernel_cache_prev = (kernel_cache_t *) NULL;
+
+ for (kernel_cache_next = mrtentry_ptr->kernel_cache;
+ kernel_cache_next != (kernel_cache_t *) NULL;
+ kernel_cache_prev = kernel_cache_next,
+ kernel_cache_next = kernel_cache_next->next)
+ {
+ if (inet6_lessthan(&kernel_cache_next->group , group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_next->group , group))
+ break;
+ if (inet6_lessthan(&kernel_cache_next->source , source))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_next->source , source))
+ break;
+ /* Found exact match. Nothing to change. */
+ return;
+ }
+
+ /*
+ * The new entry must be placed between kernel_cache_prev and
+ * kernel_cache_next
+ */
+ kernel_cache_new = (kernel_cache_t *) malloc(sizeof(kernel_cache_t));
+ if (kernel_cache_prev != (kernel_cache_t *) NULL)
+ kernel_cache_prev->next = kernel_cache_new;
+ else
+ mrtentry_ptr->kernel_cache = kernel_cache_new;
+ if (kernel_cache_next != (kernel_cache_t *) NULL)
+ kernel_cache_next->prev = kernel_cache_new;
+ kernel_cache_new->prev = kernel_cache_prev;
+ kernel_cache_new->next = kernel_cache_next;
+ kernel_cache_new->source = *source;
+ kernel_cache_new->group = *group;
+ kernel_cache_new->sg_count.pktcnt = 0;
+ kernel_cache_new->sg_count.bytecnt = 0;
+ kernel_cache_new->sg_count.wrong_if = 0;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+}
+
+/*
+ * Bring the kernel cache "UP": from the (*,*,RP) to (*,G) or (S,G)
+ */
+static void
+move_kernel_cache(mrtentry_ptr, flags)
+ mrtentry_t *mrtentry_ptr;
+ u_int16 flags;
+{
+ kernel_cache_t *kernel_cache_ptr;
+ kernel_cache_t *insert_kernel_cache_ptr;
+ kernel_cache_t *first_kernel_cache_ptr;
+ kernel_cache_t *last_kernel_cache_ptr;
+ kernel_cache_t *prev_kernel_cache_ptr;
+ mrtentry_t *mrtentry_pmbr;
+ mrtentry_t *mrtentry_rp;
+ int found;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ return;
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* Move the cache info from (*,*,RP) to (*,G) */
+ mrtentry_pmbr =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_pmbr == (mrtentry_t *) NULL)
+ return; /* Nothing to move */
+
+ first_kernel_cache_ptr = last_kernel_cache_ptr =
+ (kernel_cache_t *) NULL;
+ for (kernel_cache_ptr = mrtentry_pmbr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ /*
+ * The order is: (1) smaller group; (2) smaller source within
+ * group
+ */
+ if (inet6_lessthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ continue;
+ if (!inet6_equal(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ break;
+ /* Select the kernel_cache entries to move */
+ if (first_kernel_cache_ptr == (kernel_cache_t *) NULL)
+ {
+ first_kernel_cache_ptr = last_kernel_cache_ptr =
+ kernel_cache_ptr;
+ }
+ else
+ last_kernel_cache_ptr = kernel_cache_ptr;
+ }
+
+ if (first_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ {
+ /* Fix the old chain */
+ if (first_kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ {
+ first_kernel_cache_ptr->prev->next =
+ last_kernel_cache_ptr->next;
+ }
+ else
+ mrtentry_pmbr->kernel_cache = last_kernel_cache_ptr->next;
+ if (last_kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ last_kernel_cache_ptr->next->prev =
+ first_kernel_cache_ptr->prev;
+ if (mrtentry_pmbr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_pmbr->flags
+ &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+
+ /* Insert in the new place */
+ prev_kernel_cache_ptr = (kernel_cache_t *) NULL;
+ last_kernel_cache_ptr->next = (kernel_cache_t *) NULL;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;)
+ {
+ if (first_kernel_cache_ptr == (kernel_cache_t *) NULL)
+ break; /* All entries have been inserted */
+ if (inet6_greaterthan(&kernel_cache_ptr->source,&first_kernel_cache_ptr->source))
+ {
+ /* Insert the entry before kernel_cache_ptr */
+ insert_kernel_cache_ptr = first_kernel_cache_ptr;
+ first_kernel_cache_ptr = first_kernel_cache_ptr->next;
+ if (kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->prev->next =
+ insert_kernel_cache_ptr;
+ else
+ mrtentry_ptr->kernel_cache =
+ insert_kernel_cache_ptr;
+ insert_kernel_cache_ptr->prev =
+ kernel_cache_ptr->prev;
+ insert_kernel_cache_ptr->next = kernel_cache_ptr;
+ kernel_cache_ptr->prev = insert_kernel_cache_ptr;
+ }
+ prev_kernel_cache_ptr = kernel_cache_ptr;
+ kernel_cache_ptr = kernel_cache_ptr->next;
+ }
+ if (first_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ {
+ /* Place all at the end after prev_kernel_cache_ptr */
+ if (prev_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ prev_kernel_cache_ptr->next = first_kernel_cache_ptr;
+ else
+ mrtentry_ptr->kernel_cache = first_kernel_cache_ptr;
+ first_kernel_cache_ptr->prev = prev_kernel_cache_ptr;
+ }
+ }
+ return;
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /*
+ * (S,G) entry. Move the whole group cache from (*,*,RP) to (*,G) and
+ * then get the necessary entry from (*,G). TODO: Not optimized! The
+ * particular entry is moved first to (*,G), then we have to search
+ * again (*,G) to find it and move to (S,G).
+ */
+ /* TODO: XXX: No need for this? Thinking.... */
+ /* move_kernel_cache(mrtentry_ptr->group->grp_route, flags); */
+
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp == (mrtentry_t *) NULL)
+ return;
+
+ if (mrtentry_rp->incoming != mrtentry_ptr->incoming)
+ {
+ /*
+ * XXX: the (*,*,RP) (or (*,G)) iif is different from the (S,G)
+ * iif. No need to move the cache, because (S,G) don't need it.
+ * After the first packet arrives on the shortest path, the
+ * correct cache entry will be created. If (flags &
+ * MFC_MOVE_FORCE) then we must move the cache. This usually
+ * happens when switching to the shortest path. The calling
+ * function will immediately call k_chg_mfc() to modify the
+ * kernel cache.
+ */
+ if (!(flags & MFC_MOVE_FORCE))
+ return;
+ }
+
+ /* Find the exact entry */
+
+ found = FALSE;
+ for (kernel_cache_ptr = mrtentry_rp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ if (inet6_lessthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ break;
+ if (inet6_lessthan(&kernel_cache_ptr->source, &mrtentry_ptr->source->address))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->source, &mrtentry_ptr->source->address))
+ break;
+ /* We found it! */
+ if (kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ else
+ {
+ mrtentry_rp->kernel_cache = kernel_cache_ptr->next;
+ }
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ found = TRUE;
+ break;
+ }
+
+ if (found == TRUE)
+ {
+ if (mrtentry_rp->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_rp->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ if (mrtentry_ptr->kernel_cache != (kernel_cache_t *) NULL)
+ free((char *) mrtentry_ptr->kernel_cache);
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr;
+ kernel_cache_ptr->prev = (kernel_cache_t *) NULL;
+ kernel_cache_ptr->next = (kernel_cache_t *) NULL;
+ }
+ }
+}
diff --git a/usr.sbin/pim6sd/mrt.h b/usr.sbin/pim6sd/mrt.h
new file mode 100644
index 0000000..dbe31ca
--- /dev/null
+++ b/usr.sbin/pim6sd/mrt.h
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MRT_H
+#define MRT_H
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet6/ip6_mroute.h>
+#include <string.h>
+#include "defs.h"
+
+
+/* flags for the mrt entries */
+
+#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) */
+#define MRTF_MFC_CLONE_SG 0x8000 /* clone (S,G) MFC from (*,G) or (*,*,RP) */
+
+#define CREATE TRUE
+#define DONT_CREATE FALSE
+
+
+#define MFC_MOVE_FORCE 0x1
+#define MFC_UPDATE_FORCE 0x2
+
+
+/* Macro to duplicate oif info (oif bits, timers) */
+
+#define VOIF_COPY(from , to ) \
+ do { \
+ IF_COPY(&from->joined_oifs , &to->joined_oifs); \
+ IF_COPY(&from->oifs ,&to->oifs ); \
+ IF_COPY(&from->leaves , &to->leaves); \
+ IF_COPY(&from->pruned_oifs , &to->leaves ); \
+ IF_COPY(&from->asserted_oifs ,&to->asserted_oifs); \
+ bcopy(from->vif_timers , to->vif_timers , \
+ numvifs*sizeof(from->vif_timers[0])); \
+ bcopy(from->vif_deletion_delay , to->vif_deletion_delay , \
+ numvifs*sizeof(from->vif_deletion_delay[0])); \
+ } while (0)
+
+
+#define FREE_MRTENTRY(mrtentry_ptr) \
+ do { \
+ kernel_cache_t *prev; \
+ kernel_cache_t *next; \
+ \
+ free( (char *)( (mrtentry_ptr)->vif_timers ) ); \
+ free( (char *)( (mrtentry_ptr)->vif_deletion_delay ) ); \
+ for( next=(mrtentry_ptr)->kernel_cache ; next!=(kernel_cache_t *)NULL ; ) \
+ { \
+ prev=next; \
+ next=next->next; \
+ free(prev); \
+ } \
+ free( (char *)( (mrtentry_ptr)->kernel_cache ) ); \
+ free( (char *)(mrtentry_ptr) ); \
+ } while (0)
+
+
+/*
+ * The complicated structure used by the more complicated Join/Prune
+ * message building
+ */
+
+
+typedef struct build_jp_message {
+ struct build_jp_message *next; /* Used to chain the free entries */
+ u_int8 *jp_message; /* The Join/Prune message */
+ u_int32 jp_message_size; /* Size of the Join/Prune message (in bytes) */
+ u_int16 holdtime; /* Join/Prune message holdtime field */
+ struct sockaddr_in6 curr_group; /* Current group address */
+ u_int8 curr_group_msklen; /* Current group masklen */
+ u_int8 *join_list; /* The working area for the join addresses */
+ u_int32 join_list_size; /* The size of the join_list (in bytes) */
+ u_int16 join_addr_number; /* Number of the join addresses in join_list */
+ u_int8 *prune_list; /* The working area for the prune addresses */
+ u_int32 prune_list_size; /* The size of the prune_list (in bytes) */
+ u_int16 prune_addr_number; /* Number of the prune addresses in prune_list*/
+ u_int8 *rp_list_join; /* The working area for RP join addresses */
+ u_int32 rp_list_join_size; /* The size of the rp_list_join (in bytes) */
+ u_int16 rp_list_join_number; /* Number of RP addresses in rp_list_join */
+ u_int8 *rp_list_prune; /* The working area for RP prune addresses */
+ u_int32 rp_list_prune_size; /* The size of the rp_list_prune (in bytes) */
+ u_int16 rp_list_prune_number; /* Number of RP addresses in rp_list_prune */
+ u_int8 *num_groups_ptr; /* Pointer to number_of_groups in jp_message */
+} build_jp_message_t;
+
+
+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 */
+ build_jp_message_t *build_jp_message; /* A structure for fairly
+ * complicated Join/Prune
+ * message construction.
+ */
+
+} pim_nbr_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? */
+ struct cand_rp *cand_rp; /* Used if this is rpentry_t */
+} srcentry_t;
+typedef srcentry_t rpentry_t;
+
+/* (RP<->group) matching table related structures */
+
+typedef struct cand_rp {
+ struct cand_rp *next; /* Next candidate RP */
+ struct cand_rp *prev; /* Previous candidate RP */
+ struct rp_grp_entry *rp_grp_next; /* The rp_grp_entry chain for that RP*/
+ rpentry_t *rpentry; /* Pointer to the RP entry */
+} cand_rp_t;
+
+typedef struct grp_mask {
+ struct grp_mask *next;
+ struct grp_mask *prev;
+ struct rp_grp_entry *grp_rp_next;
+ struct sockaddr_in6 group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr hash_mask;
+ u_int16 fragment_tag; /* Used for garbage collection */
+ u_int8 group_rp_number; /* Used when assembling segments */
+} grp_mask_t;
+
+typedef struct rp_grp_entry {
+ struct rp_grp_entry *rp_grp_next; /* Next entry for same RP */
+ struct rp_grp_entry *rp_grp_prev; /* Prev entry for same RP */
+ struct rp_grp_entry *grp_rp_next; /* Next entry for same grp prefix */
+ struct rp_grp_entry *grp_rp_prev; /* Prev entry for same grp prefix */
+ struct grpentry *grplink; /* Link to all grps via this entry*/
+ u_int16 advholdtime; /* The advertised holdtime */
+ u_int16 holdtime; /* The RP holdtime (will be aged) */
+ u_int16 fragment_tag; /* The fragment tag from the
+ * received BSR message
+ */
+
+ u_int8 priority; /* The RP priority */
+ grp_mask_t *group; /* Pointer to (group,mask) entry */
+ cand_rp_t *rp; /* Pointer to the RP */
+} rp_grp_entry_t;
+
+typedef struct grpentry {
+ struct grpentry *next; /* link to next entry */
+ struct grpentry *prev; /* link to prev entry */
+ struct grpentry *rpnext; /* next grp for the same RP */
+ struct grpentry *rpprev; /* prev grp for the same RP */
+ struct sockaddr_in6 group; /* subnet group of multicasts */
+ struct sockaddr_in6 rpaddr; /* The IPv6 address of the RP */
+ struct mrtentry *mrtlink; /* link to (S,G) routing entries */
+ rp_grp_entry_t *active_rp_grp; /* Pointer to the active rp_grp entry*/
+ struct mrtentry *grp_route; /* Pointer to the (*,G) routing entry*/
+} grpentry_t;
+
+typedef 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 joined_oifs; /* The joined oifs (Join received) */
+ if_set pruned_oifs; /* The pruned oifs (Prune received) */
+ if_set asserted_oifs; /* The asserted oifs (lost Assert) */
+ 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; /* Routing Metric for this entry */
+ u_int32 preference; /* The metric preference value */
+ struct sockaddr_in6 pmbr_addr; /* The PMBR address (for interop) */
+ u_int16 *vif_timers; /* vifs timer list */
+ u_int16 *vif_deletion_delay; /* vifs deletion delay list */
+
+ u_int16 flags; /* The MRTF_* flags */
+ u_int16 timer; /* entry timer */
+ u_int16 jp_timer; /* The Join/Prune timer */
+ u_int16 rs_timer; /* Register-Suppression Timer */
+ u_int assert_timer;
+ u_int assert_rate_timer;
+ struct kernel_cache *kernel_cache; /* List of the kernel cache entries */
+#ifdef RSRR
+ struct rsrr_cache *rsrr_cache; /* Used to save RSRR requests for
+ * routes change notification.
+ */
+#endif /* RSRR */
+
+} mrtentry_t;
+
+
+/*
+ * Used to get forwarded data related counts (number of packet, number of
+ * bits, etc)
+ */
+
+struct sg_count {
+ u_quad_t pktcnt; /* Number of packets for (s,g) */
+ u_quad_t bytecnt; /* Number of bytes for (s,g) */
+ u_quad_t wrong_if; /* Number of packets received on wrong iif for (s,g) */
+};
+
+/*
+ * Structure to keep track of existing (S,G) MFC entries in the kernel
+ * for particular (*,G) or (*,*,RP) entry. We must keep track for
+ * each active source which doesn't have (S,G) entry in the daemon's
+ * routing table. We need to keep track of such sources for two reasons:
+ *
+ * (1) If the kernel does not support (*,G) MFC entries (currently, the
+ * "official" mcast code doesn't), we must know all installed (s,G) entries
+ * in the kernel and modify them if the iif or oif for the (*,G) changes.
+ *
+ * (2) By checking periodically the traffic coming from the shared tree,
+ * we can either delete the idle sources or switch to the shortest path.
+ *
+ * Note that even if we have (*,G) implemented in the kernel, we still
+ * need to have this structure because of (2)
+ */
+
+
+typedef struct kernel_cache {
+ struct kernel_cache *next;
+ struct kernel_cache *prev;
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ struct sg_count sg_count; /* The (s,g) data retated counters (see above) */
+} kernel_cache_t;
+
+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 */
+};
+
+/* globals and functions exportations */
+
+extern srcentry_t *srclist;
+extern grpentry_t *grplist;
+
+extern void init_pim6_mrt __P(());
+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 void delete_mrtentry_all_kernel_cache __P((mrtentry_t *mrtentry_ptr));
+extern void delete_single_kernel_cache __P((mrtentry_t *mrtentry_ptr,
+ kernel_cache_t *kernel_cache_ptr));
+extern void delete_single_kernel_cache_addr __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern void add_kernel_cache __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *source, struct sockaddr_in6 *group,
+ u_int16 flags));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/mtrace6/Makefile b/usr.sbin/pim6sd/mtrace6/Makefile
new file mode 100644
index 0000000..2c198b8
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/Makefile
@@ -0,0 +1,86 @@
+# 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 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: Makefile,v 1.2 1999/09/13 01:23:22 jinmei Exp $
+# $FreeBSD$
+#
+#
+#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.
+
+PROG= mtrace6
+
+CFLAGS+=-Wall
+CFLAGS+= -I$(.CURDIR)/..
+
+MAN8= mtrace6.8
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/pim6sd/mtrace6/mtrace6.8 b/usr.sbin/pim6sd/mtrace6/mtrace6.8
new file mode 100644
index 0000000..1beab02
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/mtrace6.8
@@ -0,0 +1,115 @@
+.\" 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.
+.\"
+.\" $Id: mtrace6.8,v 1.3 1999/09/12 17:03:18 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd Sep 12, 1999
+.Dt MTRACE6 8
+.Os KAME
+.Sh NAME
+.Nm mtrace6
+.Nd print IPv6 multicast path from a source to
+a receiver
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar destination
+.Op Fl g Ar gateway
+.Op Fl h Ar hops
+.Op Fl i Ar interface
+.Op Fl m Ar maxhops
+.Op Fl n
+.Op Fl r Ar response_addr
+.Op Fl w Ar waittime
+.Ar source
+.Ar group
+.Sh DESCRIPTION
+.Nm
+utilizes a tracing feature implemented in multicast routers that is
+accessed via an extension to the MLD protocol. A trace query is
+passed hop-by-hop along the reverse path from the
+.Ar destination
+to the
+.Ar source ,
+collecting hop addresses, packet counts, and routing error conditions
+along the path, and then the response is returned to the requestor.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d Ar destination
+Specifies the multicast receiver that the query wants to trace.
+It is the host running
+.Nm mtrace6
+by default.
+.It Fl g Ar gateway
+Send the trace query via unicast directly to the multicast router
+.Ar gateway .
+The unicast router must be the last-hop router on the path from the
+intended source to the receiver.
+.Ar gateway
+can also be a multicast address that the last hop router joins.
+.It Fl h Ar hops
+Set
+.Ar hops
+to the IPv6 hop limit field of query packets. The default is 64.
+.It Fl i Ar interface
+Specifies the local interface (on a multi-homed host) for sending
+the trace query and as the default for the receiver and the response
+destination.
+.It Fl m Ar maxhops
+Set to
+.Ar maxhops
+to the maximum number of hops that will be traced from the receiver
+back toward the source. The default is 127 hops.
+.It Fl n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each router found on
+the path).
+.It Fl r Ar response_addr
+Specify the host that the trace response sends to.
+By default, the response will send to the host running
+.Nm mtrace6 .
+.It Fl w Ar waittime
+Set the time to wait for a trace response to
+.Ar waittime
+seconds. The default is 3 seconds.
+.El
+.Sh SEE ALSO
+.Xr pim6dd 8 ,
+.Xr pim6sd 8 ,
+.Xr mtrace 8
+.Sh BUGS
+Multicast trace for IPv6 is experimental. MLD types for query and
+response, and packet format are not officially defined.
+.Pp
+.Ar waittime
+specified by the
+.Fl w
+option is currently meaningless.
+.Sh HISTORY
+The
+.Nm mtrace6
+command first appeared in WIDE/KAME IPv6 protocol stack kit.
diff --git a/usr.sbin/pim6sd/mtrace6/mtrace6.c b/usr.sbin/pim6sd/mtrace6/mtrace6.c
new file mode 100644
index 0000000..f2befc6
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/mtrace6.c
@@ -0,0 +1,661 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/icmp6.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <err.h>
+
+#include "trace.h"
+
+static void usage(), open_socket(), make_packet(), mtrace_loop(), show_result();
+
+static char *gateway, *intface, *source, *group, *receiver, *destination;
+static int mldsoc, hops = 64, maxhops = 127, waittime = 3, querylen, opt_n;
+static struct sockaddr *gw_sock, *src_sock, *grp_sock, *dst_sock, *rcv_sock;
+static char *querypacket;
+static char frombuf[1024]; /* XXX: enough size? */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int op;
+
+ /* get parameters */
+ while((op = getopt(argc, argv, "d:g:h:i:m:nr:w:")) != -1) {
+ switch(op) {
+ case 'd':
+ destination = optarg;
+ break;
+ case 'g':
+ gateway = optarg;
+ break;
+ case 'h':
+ hops = atoi(optarg);
+ if (hops < 0 || hops > 255) {
+ warnx("query/response hops must be between 0 and 255");
+ usage();
+ }
+ break;
+ case 'i':
+ intface = optarg;
+ break;
+ case 'm':
+ maxhops = atoi(optarg);
+ if (maxhops < 0 || maxhops > 255) {
+ warnx("maxhops must be between 0 and 255");
+ usage();
+ }
+ break;
+ case 'n':
+ opt_n = 1;
+ break;
+ case 'r':
+ receiver = optarg;
+ break;
+ case 'w':
+ waittime = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage();
+ source = argv[0];
+ group = argv[1];
+
+ /* examine addresses and open a socket */
+ open_socket();
+
+ /* construct a query packet according to the specified parameters */
+ make_packet();
+
+ mtrace_loop();
+}
+
+static char *
+proto_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case PROTO_DVMRP:
+ return ("DVMRP");
+ case PROTO_MOSPF:
+ return ("MOSPF");
+ case PROTO_PIM:
+ return ("PIM");
+ case PROTO_CBT:
+ return ("CBT");
+ case PROTO_PIM_SPECIAL:
+ return ("PIM/Special");
+ case PROTO_PIM_STATIC:
+ return ("PIM/Static");
+ case PROTO_DVMRP_STATIC:
+ return ("DVMRP/Static");
+ case 0:
+ return ("None");
+ default:
+ (void) sprintf(buf, "Unknown protocol code %d", type);
+ return (buf);
+ }
+}
+
+static char *
+pr_addr(addr, numeric)
+ struct sockaddr_in6 *addr;
+ int numeric;
+{
+ static char buf[MAXHOSTNAMELEN];
+ int flag = 0;
+
+ if (numeric)
+ flag |= NI_NUMERICHOST;
+ flag |= NI_WITHSCOPEID;
+
+ getnameinfo((struct sockaddr *)addr, addr->sin6_len, buf, sizeof(buf),
+ NULL, 0, flag);
+
+ return (buf);
+}
+
+static void
+setqid(family, query)
+ int family;
+ char *query;
+{
+ struct tr6_query *q6;
+
+ switch(family) {
+ case AF_INET6:
+ q6 = (struct tr6_query *)((struct mld6_hdr *)query + 1);
+ q6->tr_qid = (u_int32_t)random();
+ }
+}
+
+static void
+mtrace_loop()
+{
+ int nsoc, fromlen, rcvcc;
+ struct timeval tv, tv_wait;
+ struct fd_set fds;
+ struct sockaddr_storage from_ss;
+ struct sockaddr *from_sock = (struct sockaddr *)&from_ss;
+
+ /* initializa random number of query ID */
+ gettimeofday(&tv, 0);
+ srandom(tv.tv_usec);
+
+ while(1) { /* XXX */
+ setqid(gw_sock->sa_family, querypacket);
+
+ if (sendto(mldsoc, (void *)querypacket, querylen, 0, gw_sock,
+ gw_sock->sa_len) < 0)
+ err(1, "sendto");
+
+ tv_wait.tv_sec = waittime;
+ tv_wait.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(mldsoc, &fds);
+
+ if ((nsoc = select(mldsoc + 1, &fds, NULL, NULL, &tv_wait)) < 0)
+ err(1, "select");
+
+ if (nsoc == 0) {
+ printf("Timeout\n");
+ exit(0); /* XXX try again? */
+ }
+
+ fromlen = sizeof(from_ss);
+ if ((rcvcc = recvfrom(mldsoc, frombuf, sizeof(frombuf), 0,
+ from_sock, &fromlen))
+ < 0)
+ err(1, "recvfrom");
+
+ show_result(from_sock, rcvcc);
+ exit(0); /* XXX */
+ }
+}
+
+char *fwd_code[] = {"NOERR", "WRONGIF", "SPRUNE", "RPRUNE", "SCOPED", "NORT",
+ "WRONGLH", "NOFWD", "RP", "RPFIF", "NOMC", "HIDDEN"};
+char *fwd_errcode[] = {"", "NOSPC", "OLD", "ADMIN"};
+
+static char *
+str_rflags(flag)
+ int flag;
+{
+ if (0x80 & flag) { /* fatal error */
+ flag &= ~0x80;
+ if (flag >= sizeof(fwd_errcode) / sizeof(char *) ||
+ flag == 0) {
+ warnx("unknown error code(%d)", flag);
+ return("UNKNOWN");
+ }
+ return(fwd_errcode[flag]);
+ }
+
+ /* normal code */
+ if (flag >= sizeof(fwd_code) / sizeof(char *)) {
+ warnx("unknown forward code(%d)", flag);
+ return("UNKNOWN");
+ }
+ return(fwd_code[flag]);
+}
+
+static void
+show_ip6_result(from6, datalen)
+ struct sockaddr_in6 *from6;
+ int datalen;
+{
+ struct mld6_hdr *mld6_tr_resp = (struct mld6_hdr *)frombuf;
+ struct mld6_hdr *mld6_tr_query = (struct mld6_hdr *)querypacket;
+ struct tr6_query *tr6_rquery = (struct tr6_query *)(mld6_tr_resp + 1);
+ struct tr6_query *tr6_query = (struct tr6_query *)(mld6_tr_query + 1);
+ struct tr6_resp *tr6_resp = (struct tr6_resp *)(tr6_rquery + 1),
+ *rp, *rp_end;
+ int i;
+
+ if (datalen < sizeof(*mld6_tr_resp) + sizeof(*tr6_rquery) +
+ sizeof(*tr6_resp)) {
+ warnx("show_ip6_result: receive data length(%d) is short",
+ datalen);
+ return;
+ }
+
+ switch(mld6_tr_resp->mld6_type) {
+ case MLD6_MTRACE_RESP:
+ if ((datalen - sizeof(*mld6_tr_resp) - sizeof(*tr6_rquery)) %
+ sizeof(*tr6_resp)) {
+ warnx("show_ip6_result: incomplete response (%d bytes)",
+ datalen);
+ return;
+ }
+ rp_end = (struct tr6_resp *)((char *)mld6_tr_resp + datalen);
+
+ /* sanity check for the response */
+ if (tr6_query->tr_qid != tr6_rquery->tr_qid ||
+ !IN6_ARE_ADDR_EQUAL(&tr6_query->tr_src, &tr6_rquery->tr_src) ||
+ !IN6_ARE_ADDR_EQUAL(&tr6_query->tr_dst, &tr6_rquery->tr_dst))
+ return; /* XXX: bark here? */
+
+ for (i = 0, rp = tr6_resp; rp < rp_end; i++, rp++) {
+ struct sockaddr_in6 sa_resp, sa_upstream;
+
+ /* reinitialize the sockaddr. paranoid? */
+ memset((void *)&sa_resp, 0, sizeof(sa_resp));
+ sa_resp.sin6_family = AF_INET6;
+ sa_resp.sin6_len = sizeof(sa_resp);
+ memset((void *)&sa_upstream, 0, sizeof(sa_upstream));
+ sa_upstream.sin6_family = AF_INET6;
+ sa_upstream.sin6_len = sizeof(sa_upstream);
+
+ sa_resp.sin6_addr = rp->tr_lcladdr;
+ sa_upstream.sin6_addr = rp->tr_rmtaddr;
+
+ /* print information for the router */
+ printf("%3d ", -i);/* index */
+ /* router address and incoming/outgoing interface */
+ printf("%s", pr_addr((struct sockaddr *)&sa_resp, opt_n));
+ printf("(%s/%ld->%ld) ",
+ pr_addr((struct sckaddr *)&sa_upstream),
+ ntohl(rp->tr_inifid), ntohl(rp->tr_outifid));
+ /* multicast routing protocol type */
+ printf("%s ", proto_type(rp->tr_rproto));
+ /* forwarding error code */
+ printf("%s", str_rflags(rp->tr_rflags & 0xff));
+
+ putchar('\n');
+ }
+
+ break;
+ default: /* impossible... */
+ warnx("show_ip6_result: invalid ICMPv6 type(%d)",
+ mld6_tr_resp->mld6_type); /* assert? */
+ break;
+ }
+}
+
+static void
+show_result(from, datalen)
+ struct sockaddr *from;
+ int datalen;
+{
+ switch(from->sa_family) {
+ case AF_INET6:
+ show_ip6_result((struct sockaddr_in6 *)from, datalen);
+ break;
+ default:
+ errx(1, "show_result: illegal AF(%d) on recv", from->sa_family);
+ }
+}
+
+static void
+set_sockaddr(addrname, hints, sap)
+ char *addrname;
+ struct addrinfo *hints;
+ struct sockaddr *sap;
+{
+ struct addrinfo *res;
+ int ret_ga;
+
+ ret_ga = getaddrinfo(addrname, NULL, hints, &res);
+ if (ret_ga)
+ errx(1, "getaddrinfo faild: %s", gai_strerror(ret_ga));
+ if (!res->ai_addr)
+ errx(1, "getaddrinfo failed");
+ memcpy((void *)sap, (void *)res->ai_addr, res->ai_addr->sa_len);
+
+ freeaddrinfo(res);
+}
+
+static int
+is_multicast(sa)
+ struct sockaddr *sa;
+{
+ switch(sa->sa_family) {
+ case AF_INET6:
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sa)->sin6_addr))
+ return 1;
+ else
+ return 0;
+ break;
+ default:
+ return 0; /* XXX: support IPv4? */
+ }
+}
+
+static char *
+all_routers_str(family)
+ int family;
+{
+ switch(family) {
+ case AF_INET6:
+ return("ff02::1");
+ default:
+ errx(1, "all_routers_str: unknown AF(%d)", family);
+ }
+}
+
+int
+ip6_validaddr(ifname, addr)
+ char *ifname;
+ struct sockaddr_in6 *addr;
+{
+ int s;
+ struct in6_ifreq ifr6;
+ u_int32_t flags6;
+
+ /* we need a global address only...XXX: should be flexible? */
+ if (IN6_IS_ADDR_LOOPBACK(&addr->sin6_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&addr->sin6_addr))
+ return(0);
+
+ /* get IPv6 dependent flags and examine them */
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "ip6_validaddr: socket");
+
+ strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = *addr;
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0)
+ err(1, "ioctl(SIOCGIFAFLAG_IN6)");
+ close(s);
+ flags6 = ifr6.ifr_ifru.ifru_flags6;
+ if (flags6 & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE |
+ IN6_IFF_DUPLICATED | IN6_IFF_DETACHED))
+ return(0);
+
+ return(1);
+}
+
+int
+get_my_sockaddr(family, addrp)
+ int family;
+ struct sockaddr *addrp;
+{
+#define IF_BUFSIZE 8192 /* XXX: adhoc...should be customizable? */
+ int i, s;
+ struct ifconf ifconf;
+ struct ifreq *ifrp;
+ static char ifbuf[IF_BUFSIZE];
+
+ if ((s = socket(family, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ ifconf.ifc_buf = ifbuf;
+ ifconf.ifc_len = sizeof(ifbuf);
+
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifconf) < 0)
+ err(1, "ioctl(SIOCGIFCONF)");
+ close(s);
+
+ for (i = 0; i < ifconf.ifc_len; ) {
+ ifrp = (struct ifreq *)(ifbuf + i);
+ if (ifrp->ifr_addr.sa_family == family) {
+ switch(family) {
+ case AF_INET6:
+ if (ip6_validaddr(ifrp->ifr_name,
+ (struct sockaddr_in6 *)&ifrp->ifr_addr))
+ goto found;
+ }
+ }
+
+ i += IFNAMSIZ;
+ /* i += max(sizeof(sockaddr), ifr_addr.sa_len) */
+ if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr))
+ i += ifrp->ifr_addr.sa_len;
+ else
+ i += sizeof(struct sockaddr);
+ }
+
+ return(-1); /* not found */
+
+ found:
+ memcpy((void *)addrp, (void *)&ifrp->ifr_addr, ifrp->ifr_addr.sa_len);
+ return(0);
+#undef IF_BUFSIZE
+}
+
+static void
+set_hlim(s, addr, hops)
+ int s, hops;
+ struct sockaddr *addr;
+{
+ struct sockaddr_in6 *sin6;
+ int opt;
+
+ switch(addr->sa_family) {
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)addr;
+ opt = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) ?
+ IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS;
+ if (setsockopt(s, IPPROTO_IPV6, opt, (char *)&hops,
+ sizeof(hops)) == -1)
+ err(1, "setsockopt(%s)",
+ (opt == IPV6_MULTICAST_HOPS) ?
+ "IPV6_MULTICAST_HOPS" : "IPV6_UNICAST_HOPS");
+ break;
+ }
+}
+
+static void
+set_join(s, ifname, group)
+ int s;
+ char *ifname;
+ struct sockaddr *group;
+{
+ struct ipv6_mreq mreq6;
+ u_int ifindex;
+
+ switch(group->sa_family) {
+ case AF_INET6:
+ if ((ifindex = if_nametoindex(ifname)) == 0)
+ err(1, "set_join: if_nametoindex failed for %s", ifname);
+ mreq6.ipv6mr_multiaddr =
+ ((struct sockaddr_in6 *)group)->sin6_addr;
+ mreq6.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
+ sizeof(mreq6)) < 0)
+ err(1, "setsockopt(IPV6_JOIN_GROUP)");
+ break;
+ }
+}
+
+static void
+set_filter(s, family)
+{
+ struct icmp6_filter filter6;
+
+ switch(family) {
+ case AF_INET6:
+ ICMP6_FILTER_SETBLOCKALL(&filter6);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filter6);
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter6,
+ sizeof(filter6)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+ break;
+ }
+}
+
+static void
+open_socket()
+{
+ struct addrinfo hints;
+ static struct sockaddr_storage gw_ss, src_ss, grp_ss, dst_ss, rcv_ss;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6; /* to be independent of AF? */
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ /* multicast group(must be specified) */
+ grp_sock = (struct sockaddr *)&grp_ss;
+ set_sockaddr(group, &hints, grp_sock);
+ if (!is_multicast(grp_sock))
+ errx(1, "group(%s) is not a multicast address", group);
+
+ /* multicast source(must be specified) */
+ src_sock = (struct sockaddr *)&src_ss;
+ set_sockaddr(source, &hints, src_sock);
+ if (is_multicast(src_sock))
+ errx(1, "source(%s) is not a unicast address", source);
+
+ /* last hop gateway for the destination(if specified) */
+ gw_sock = (struct sockaddr *)&gw_ss;
+ if (gateway) /* can be either multicast or unicast */
+ set_sockaddr(gateway, &hints, gw_sock);
+ else {
+ char *r = all_routers_str(grp_sock->sa_family);
+
+ set_sockaddr(r, &hints, gw_sock);
+ }
+
+ /* destination address for the trace */
+ dst_sock = (struct sockaddr *)&dst_ss;
+ if (destination) {
+ set_sockaddr(destination, &hints, dst_sock);
+ if (is_multicast(dst_sock))
+ errx(1, "destination(%s) is not a unicast address",
+ destination);
+ }
+ else {
+ /* XXX: consider interface? */
+ get_my_sockaddr(grp_sock->sa_family, dst_sock);
+ }
+
+ /* response receiver(if specified) */
+ rcv_sock = (struct sockaddr *)&rcv_ss;
+ if (receiver) { /* can be either multicast or unicast */
+ set_sockaddr(receiver, &hints, rcv_sock);
+ if (is_multicast(rcv_sock) &&
+ intface == NULL) {
+#ifdef notyet
+ warnx("receive I/F is not specified for multicast"
+ "response(%s)", receiver);
+ intface = default_intface;
+#else
+ errx(1, "receive I/F is not specified for multicast"
+ "response(%s)", receiver);
+#endif
+ }
+ }
+ else {
+ /* XXX: consider interface? */
+ get_my_sockaddr(grp_sock->sa_family, rcv_sock);
+ }
+
+ if ((mldsoc = socket(hints.ai_family, hints.ai_socktype,
+ hints.ai_protocol)) < 0)
+ err(1, "socket");
+
+ /* set necessary socket options */
+ if (hops)
+ set_hlim(mldsoc, gw_sock, hops);
+ if (receiver && is_multicast(rcv_sock))
+ set_join(mldsoc, intface, rcv_sock);
+ set_filter(mldsoc, grp_sock->sa_family);
+}
+
+static void
+make_ip6_packet()
+{
+ struct mld6_hdr *mld6_tr_query;
+ struct tr6_query *tr6_query;
+
+ querylen = sizeof(*mld6_tr_query) + sizeof(*tr6_query);
+ if ((querypacket = malloc(querylen)) == NULL)
+ errx(1, "make_ip6_packet: malloc failed");
+ memset(querypacket, 0, querylen);
+
+ /* fill in MLD header */
+ mld6_tr_query = (struct mld6_hdr *)querypacket;
+ mld6_tr_query->mld6_type = MLD6_MTRACE;
+ mld6_tr_query->mld6_code = maxhops & 0xff;
+ mld6_tr_query->mld6_addr = ((struct sockaddr_in6 *)grp_sock)->sin6_addr;
+
+ /* fill in mtrace query fields */
+ tr6_query = (struct tr6_query *)(mld6_tr_query + 1);
+ tr6_query->tr_src = ((struct sockaddr_in6 *)src_sock)->sin6_addr;
+ tr6_query->tr_dst = ((struct sockaddr_in6 *)dst_sock)->sin6_addr;
+ tr6_query->tr_raddr = ((struct sockaddr_in6 *)rcv_sock)->sin6_addr;
+ tr6_query->tr_rhlim = 0xff & hops;
+}
+
+static void
+make_packet()
+{
+ switch(grp_sock->sa_family) {
+ case AF_INET6:
+ make_ip6_packet();
+ break;
+ default:
+ errx(1, "make_packet: unsupported AF(%d)", grp_sock->sa_family);
+ }
+}
+
+static void
+usage()
+{
+ errx(1,
+ "[-d destination] [-g gateway] [-h hops] [-i interface] "
+ "[-m maxhops] [-n] [-r response_addr] [-w waittime] source group");
+}
diff --git a/usr.sbin/pim6sd/pathnames.h b/usr.sbin/pim6sd/pathnames.h
new file mode 100644
index 0000000..bacd832
--- /dev/null
+++ b/usr.sbin/pim6sd/pathnames.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#define _PATH_PIM6D_CONF "/usr/local/v6/etc/pim6sd.conf"
+#define _PATH_PIM6D_LOGFILE "/var/log/pim6sd.log"
+
+#if (defined(BSD) && (BSD >= 199103))
+ #define _PATH_PIM6D_PID "/var/run/pim6sd.pid"
+ #define _PATH_PIM6D_GENID "/var/run/pim6sd.genid"
+ #define _PATH_PIM6D_DUMP "/var/run/pim6sd.dump"
+ #define _PATH_PIM6D_CACHE "/var/run/pim6sd.cache"
+ #define _PATH_PIM6D_STAT "/var/run/pim6sd.stat"
+#else
+ #define _PATH_PIM6D_PID "/etc/pim6sd.pid"
+ #define _PATH_PIM6D_GENID "/etc/pim6sd.genid"
+ #define _PATH_PIM6D_DUMP "/etc/pim6sd.dump"
+ #define _PATH_PIM6D_CACHE "/etc/pim6sd.cache"
+ #define _PATH_PIM6D_STAT "/etc/pim6sd.stat"
+#endif
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6.c b/usr.sbin/pim6sd/pim6.c
new file mode 100644
index 0000000..117179d
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/pim6.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <signal.h>
+#include "mld6.h"
+#include "defs.h"
+#include "kern.h"
+#include "pim6.h"
+#include "pimd.h"
+#include "pim6_proto.h"
+#include "inet6.h"
+#include "debug.h"
+
+struct sockaddr_in6 allpim6routers_group;
+int pim6_socket;
+char *pim6_recv_buf;
+char *pim6_send_buf;
+
+static struct sockaddr_in6 from;
+static struct iovec sndiovpim[2];
+static struct iovec rcviovpim[2];
+static struct msghdr sndmhpim,
+ rcvmhpim;
+static u_char sndcmsgbufpim[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+static u_char rcvcmsgbufpim[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+
+
+/*
+ * 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()
+{
+ struct cmsghdr *cmsgp;
+ int on;
+
+ if ( (pim6_recv_buf = malloc( RECV_BUF_SIZE)) == NULL ||
+ (pim6_send_buf = malloc (RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR,errno,"pim6 buffer allocation");
+
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer pim6",RECV_BUF_SIZE);
+
+ 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);
+ k_set_hlim(pim6_socket,MINHLIM);
+ k_set_loop(pim6_socket,FALSE);
+
+ memset(&allpim6routers_group, 0, sizeof(allpim6routers_group));
+ 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");
+ memset(&sockaddr6_d, 0, sizeof(sockaddr6_d));
+ sockaddr6_d.sin6_len = sizeof(sockaddr6_d);
+ sockaddr6_d.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, "ff00::",
+ (void *)&sockaddr6_d.sin6_addr) != 1)
+ log(LOG_ERR, 0, "inet_pton failed for ff00::");
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(pim6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
+#else
+ if (setsockopt(pim6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
+#endif
+
+ /* initialize msghdr for receiving packets */
+ rcviovpim[0].iov_base = (caddr_t) pim6_recv_buf;
+ rcviovpim[0].iov_len = RECV_BUF_SIZE;
+ rcvmhpim.msg_name = (caddr_t ) &from;
+ rcvmhpim.msg_namelen = sizeof (from);
+ rcvmhpim.msg_iov = rcviovpim;
+ rcvmhpim.msg_iovlen = 1;
+ rcvmhpim.msg_control = (caddr_t ) rcvcmsgbufpim;
+ rcvmhpim.msg_controllen = sizeof (rcvcmsgbufpim);
+
+
+ sndmhpim.msg_namelen=sizeof(struct sockaddr_in6);
+ sndmhpim.msg_iov=sndiovpim;
+ sndmhpim.msg_iovlen=1;
+ sndmhpim.msg_control=(caddr_t)sndcmsgbufpim;
+ sndmhpim.msg_controllen=sizeof(sndcmsgbufpim);
+ cmsgp=(struct cmsghdr *)sndcmsgbufpim;
+ 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,"Registering pim6 socket");
+
+ /* Initialize the building Join/Prune messages working area */
+ build_jp_message_pool = (build_jp_message_t *)NULL;
+ build_jp_message_pool_counter = 0;
+}
+
+/* Read a PIM message */
+
+static void
+pim6_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int pim6_recvlen;
+
+#ifdef SYSV
+ sigset_t block, oblock;
+#else
+ register int omask;
+#endif
+
+ pim6_recvlen = recvmsg(pim6_socket,&rcvmhpim,0);
+
+ 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 dst;
+ struct in6_pktinfo *pi=NULL;
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *)rcvmhpim.msg_name;
+ struct cmsghdr *cm;
+ int ifindex=0;
+
+ /* 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 *)rcvmhpim.msg_iov[0].iov_base;
+
+ /* extract vital information via Advanced API */
+ for(cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhpim);
+ cm;
+ cm =(struct cmsghdr *)CMSG_NXTHDR(&rcvmhpim , 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));
+ dst.sin6_addr=pi->ipi6_addr;
+ ifindex = pi->ipi6_ifindex;
+ if (IN6_IS_ADDR_LINKLOCAL(&dst.sin6_addr))
+ dst.sin6_scope_id = ifindex;
+ else
+ dst.sin6_scope_id = 0;
+ }
+ }
+
+ if(pi==NULL)
+ log(LOG_ERR,0,"pim6_socket : unable to get destination packet");
+
+ if(ifindex==0)
+ log(LOG_ERR,0,"pim6_socket : unable to get ifindex");
+
+#define NOSUCHDEF
+#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));
+ }
+ }
+#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:
+ receive_pim6_register(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_REGISTER_STOP:
+ receive_pim6_register_stop(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_JOIN_PRUNE:
+ receive_pim6_join_prune(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_BOOTSTRAP:
+ receive_pim6_bootstrap(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_ASSERT:
+ receive_pim6_assert(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_GRAFT:
+ pim6dstat.in_pim6_graft++;
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_GRAFT_ACK:
+ pim6dstat.in_pim6_graft_ack++;
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_CAND_RP_ADV:
+ receive_pim6_cand_rp_adv(src, &dst, (char *)(pim), pimlen);
+ break;
+ default:
+ log(LOG_INFO, 0,
+ "ignore unknown PIM message code %u from %s",
+ pim->pim_type,
+ inet6_fmt(&src->sin6_addr));
+ break;
+ }
+}
+
+void
+send_pim6(char *buf, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, int type, int datalen)
+{
+ struct pim *pim;
+ int setloop=0;
+ int ifindex=0;
+ int sendlen=sizeof(struct pim)+datalen;
+ struct cmsghdr *cmsgp;
+ struct in6_pktinfo *sndpktinfo;
+
+ sndiovpim[0].iov_base=(caddr_t)buf;
+ sndiovpim[0].iov_len=datalen+sizeof(struct pim);
+ cmsgp=(struct cmsghdr *)sndcmsgbufpim;
+ sndpktinfo=(struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ sndmhpim.msg_name=(caddr_t)dst;
+
+ pim = (struct pim *)buf;
+ pim->pim_type = type;
+ pim->pim_ver = PIM_PROTOCOL_VERSION;
+ pim->pim_rsv = 0;
+ pim->pim_cksum = 0;
+
+ if(pim->pim_type == PIM_REGISTER)
+ {
+ sendlen = sizeof(struct pim)+sizeof(pim_register_t);
+
+ }
+
+ pim->pim_cksum = pim6_cksum((u_int16 *)pim,
+ &src->sin6_addr, &dst->sin6_addr,
+ sendlen);
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ {
+ if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ {
+ log(LOG_WARNING, 0,
+ "trying to send pim multicast packet "
+ "with non linklocal src(%s), ignoring",
+ inet6_fmt(&src->sin6_addr));
+ return;
+ }
+ sndmhpim.msg_control=NULL;
+ sndmhpim.msg_controllen=0;
+ ifindex=src->sin6_scope_id;
+
+ 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);
+ }
+ }
+ else
+ {
+ sndmhpim.msg_control=(caddr_t)sndcmsgbufpim;
+ sndmhpim.msg_controllen=sizeof(sndcmsgbufpim);
+ sndpktinfo->ipi6_ifindex=src->sin6_scope_id;
+ memcpy(&sndpktinfo->ipi6_addr, &src->sin6_addr,
+ sizeof(sndpktinfo->ipi6_addr));
+ }
+ if( sendmsg(pim6_socket, &sndmhpim, 0) <0 )
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno, "sendmsg from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&dst->sin6_addr));
+
+ if(setloop)
+ k_set_loop(pim6_socket, FALSE);
+
+ return;
+}
+
+/* ============================== */
+
+/*
+ * 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.
+ */
+int pim6_cksum(u_short *addr, struct in6_addr *src ,struct in6_addr *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/pim6sd/pim6.h b/usr.sbin/pim6sd/pim6.h
new file mode 100644
index 0000000..d57bf4b
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIM6_H
+#define PIM6_H
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netinet6/ip6_mroute.h>
+
+extern struct sockaddr_in6 allpim6routers_group;
+extern char *pim6_send_buf;
+extern int pim6_socket;
+
+void init_pim6();
+extern void send_pim6 __P((char *buf, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, int type,
+ int datalen));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6_proto.c b/usr.sbin/pim6sd/pim6_proto.c
new file mode 100644
index 0000000..9de795e
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6_proto.c
@@ -0,0 +1,4126 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet6/pim6.h>
+#include <netinet/ip6.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "mrt.h"
+#include "defs.h"
+#include "vif.h"
+#include "debug.h"
+#include "pim6.h"
+#include "pim6_proto.h"
+#include "pimd.h"
+#include "rp.h"
+#include "mld6.h"
+#include "timer.h"
+#include "route.h"
+#include "inet6.h"
+#include "kern.h"
+#include "routesock.h"
+
+/*
+ * Local functions definitions.
+ */
+
+static int parse_pim6_hello __P((char *pktPtr , int datalen , struct sockaddr_in6 *src,
+ u_int16 *holdtime));
+
+static int send_pim6_register_stop __P((struct sockaddr_in6 *reg_src , struct sockaddr_in6 *reg_dst ,
+ struct sockaddr_in6 *inner_source,
+ struct sockaddr_in6 *inner_grp));
+
+static build_jp_message_t *get_jp6_working_buff __P(());
+static void return_jp6_working_buff __P((pim_nbr_entry_t * pim_nbr));
+static void pack_jp6_message __P((pim_nbr_entry_t * pim_nbr));
+static void send_jp6_message __P((pim_nbr_entry_t * pim_nbr));
+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));
+
+build_jp_message_t *build_jp_message_pool;
+int build_jp_message_pool_counter;
+struct sockaddr_in6 sockaddr6_any = {sizeof(struct sockaddr_in6) , AF_INET6 ,0,0, IN6ADDR_ANY_INIT};
+struct sockaddr_in6 sockaddr6_d;
+
+struct pim6dstat pim6dstat;
+
+/************************************************************************
+ * 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;
+ int bsr_length;
+ u_int8 *data_ptr;
+ srcentry_t *srcentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+
+
+ 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];
+ v->uv_in_pim6_hello++; /* increment statistacs */
+ if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
+ 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_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;
+ SET_TIMER(new_nbr->timer, holdtime);
+ new_nbr->build_jp_message = (build_jp_message_t *) NULL;
+ 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;
+
+ /* Since a new neighbour has come up, let it know your existence */
+ /*
+ * XXX: TODO: not in the spec, but probably should send the message after
+ * a short random period?
+ */
+
+ send_pim6_hello(v, pim_hello_holdtime);
+
+ if (v->uv_flags & VIFF_DR)
+ {
+ /*
+ * If I am the current DR on that interface, so send an RP-Set
+ * message to the new neighbor.
+ */
+
+ if ((bsr_length = create_pim6_bootstrap_message(pim6_send_buf)))
+ send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr , src , PIM_BOOTSTRAP,
+ bsr_length);
+
+
+ /* The router with highest network address is the elected DR */
+ if (inet6_lessthan(&v->uv_linklocal->pa_addr,&v->uv_pim_neighbors->address))
+ {
+ /*
+ * I was the DR, but not anymore. Remove all register_vif from
+ * oif list for all directly connected sources (for vifi).
+ */
+ /* TODO: XXX: first entry is not used! */
+ for (srcentry_ptr = srclist->next;
+ srcentry_ptr != (srcentry_t *) NULL;
+ srcentry_ptr = srcentry_ptr->next)
+ {
+ /* If not directly connected source for vifi */
+
+ if ((srcentry_ptr->incoming != mifi)
+ || (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL))
+ continue;
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_SG))
+ continue; /* This is not (S,G) entry */
+ /* Remove the register oif */
+ IF_CLR(reg_vif_num, &mrtentry_ptr->joined_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+ v->uv_flags &= ~VIFF_DR;
+ v->uv_flags &= ~VIFF_QUERIER;
+
+ }
+ }
+
+ /*
+ * TODO: XXX: does a new neighbor change any routing entries info? Need
+ * to trigger joins?
+ */
+
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG,0,"I'have got a new neighbor %s on vif %s",inet6_fmt(&src->sin6_addr),v->uv_name);
+ 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;
+ mrtentry_t *mrtentry_srcs;
+ grpentry_t *grpentry_ptr;
+ pim_nbr_entry_t *new_nbr;
+ cand_rp_t *cand_rp_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rpentry_t *rpentry_ptr;
+ struct uvif *v;
+
+ 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;
+
+ return_jp6_working_buff(nbr_delete);
+
+ 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);
+ }
+ 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 */
+ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *) NULL;
+ srcentry_ptr = srcentry_ptr_next)
+ {
+ srcentry_ptr_next = srcentry_ptr->next;
+
+ if (srcentry_ptr->upstream != nbr_delete)
+ continue;
+
+ /* Reset the next hop (PIM) router */
+
+ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE)
+ {
+ /*
+ * Coudn't reset it. Sorry, the hext 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);
+ }
+ else
+ if (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL)
+ {
+ /* Ignore the local or directly connected sources */
+ /*
+ * Browse all MRT entries for this source and reset the
+ * upstream router. Note that the upstream router is not
+ * always toward the source: it could be toward the RP for
+ * example.
+ */
+ new_nbr = srcentry_ptr->upstream;
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+ change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+ }
+ }
+
+ /* Update the RP entries */
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if (cand_rp_ptr->rpentry->upstream != nbr_delete)
+ continue;
+ rpentry_ptr = cand_rp_ptr->rpentry;
+ /* Reset the RP entry iif */
+ /* TODO: check if error setting the iif! */
+ if (local_address(&rpentry_ptr->address) == NO_VIF)
+ {
+ set_incoming(rpentry_ptr, PIM_IIF_RP);
+ }
+ else
+ {
+ rpentry_ptr->incoming = reg_vif_num;
+ rpentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ }
+ mrtentry_ptr = rpentry_ptr->mrtlink;
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr,
+ rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ /* Update the group entries for this RP */
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ mrtentry_ptr = grpentry_ptr->grp_route;
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr,
+ rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ /* Update only the (S,G)RPbit entries for this group */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_srcs,
+ rpentry_ptr->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ SET_TIMER(v->uv_pim_hello_timer, pim_hello_period);
+
+ v->uv_out_pim6_hello++;
+ return (TRUE);
+}
+
+/************************************************************************
+ * PIM_REGISTER
+ ************************************************************************/
+/*
+ * TODO: XXX: IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM
+ * USER SPACE AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE
+ * KERNEL.
+ */
+
+int
+receive_pim6_register(reg_src, reg_dst, pim_message, datalen)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ char *pim_message;
+ int datalen;
+{
+ struct sockaddr_in6 inner_src,
+ inner_grp;
+ pim_register_t *register_p;
+ struct ip6_hdr *ip;
+ u_int32 borderBit,
+ nullRegisterBit;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_ptr2;
+ if_set oifs;
+
+ pim6dstat.in_pim6_register++;
+
+ register_p = (pim_register_t *) (pim_message + sizeof(struct pim));
+
+ borderBit = ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_BORDER_BIT;
+ nullRegisterBit =
+ ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT;
+
+ /* initialize the pointer to the encapsulated packet */
+ ip = (struct ip6_hdr *) (register_p + 1);
+
+ /*
+ * We are keeping all addresses in network order,
+ * so no need for byte order translation.
+ */
+ inner_src.sin6_addr = ip->ip6_src;
+ inner_grp.sin6_addr = ip->ip6_dst;
+
+ /* scope validation of the inner source and destination addresses */
+ if (IN6_IS_ADDR_LINKLOCAL(&ip->ip6_src)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_register: inner source(%s) has invalid scope",
+ inet6_fmt(&ip->ip6_src));
+ }
+#ifdef notyet
+ if (IN6_IS_ADDR_SITELOCAL)
+ inner_src.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+ inner_src.sin6_scope_id = 0;
+
+ if (IN6_IS_ADDR_MC_NODELOCAL(&ip->ip6_dst) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&ip->ip6_dst)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_register: inner group(%s) has invalid scope",
+ inet6_fmt(&ip->ip6_dst));
+ return(FALSE); /* XXX: can we discard it? */
+ }
+#ifdef notyet
+ if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
+ inner_grp.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+ inner_grp.sin6_scope_id = 0;
+#endif
+ inner_grp.sin6_scope_id = 0;
+
+ mrtentry_ptr = find_route(&inner_src, &inner_grp,
+ MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+
+ /* No routing entry. Send REGISTER_STOP and return. */
+
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ log(LOG_DEBUG, 0,
+ "No routing entry for source %s and/or group %s",
+ inet6_fmt(&inner_src.sin6_addr), inet6_fmt(&inner_grp.sin6_addr));
+
+ /* TODO: XXX: shouldn't be inner_src=IN6ADDR_ANY? Not in the spec. */
+
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+
+ /* XXX: not in the spec: check if I am the RP for that group */
+ if (!inet6_equal(&my_cand_rp_address, reg_dst)
+ || (check_mrtentry_rp(mrtentry_ptr, &my_cand_rp_address) == FALSE))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+
+ /* (S,G) found */
+ /* TODO: check the timer again */
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout); /* restart timer */
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ /* The SPT bit is not set */
+
+ if (!nullRegisterBit)
+ {
+ calc_oifs(mrtentry_ptr, &oifs);
+ if (IF_ISEMPTY(&oifs)
+ && (mrtentry_ptr->incoming == reg_vif_num))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp,
+ &inner_src);
+ return (TRUE);
+ }
+
+ /*
+ * TODO: XXX: BUG!!! The data will be forwarded by the kernel
+ * MFC!!! Need to set a special flag for this routing entry
+ * so after a cache miss occur, the multicast packet will be
+ * forwarded from user space and won't install entry in the
+ * kernel MFC. The problem is that the kernel MFC doesn't
+ * know the PMBR address and simply sets the multicast
+ * forwarding cache to accept/forward all data coming from
+ * the register_vif.
+ */
+
+ if (borderBit)
+ {
+ if (!inet6_equal(&mrtentry_ptr->pmbr_addr,reg_src))
+ {
+ send_pim6_register_stop(reg_dst, reg_src,
+ &inner_grp, &inner_src);
+ return (TRUE);
+
+ }
+ }
+ return (TRUE);
+ }
+ /* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..? */
+ return (TRUE);
+ }
+ else
+ {
+ /* The SPT bit is set */
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+ }
+ if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))
+ {
+ if (borderBit)
+ {
+ /*
+ * Create (S,G) state. The oifs will be the copied from the
+ * existing (*,G) or (*,*,RP) entry.
+ */
+
+ mrtentry_ptr2 = find_route(&inner_src, &inner_grp, MRTF_SG,
+ CREATE);
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr2->pmbr_addr = *reg_src;
+
+ /* Clear the SPT flag */
+
+ mrtentry_ptr2->flags &= ~(MRTF_SPT | MRTF_NEW);
+ SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
+
+ /* TODO: explicitly call the Join/Prune send function? */
+
+ FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Send the Join
+ * immediately */
+ /*
+ * TODO: explicitly call this function?
+ * send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
+ * mrtentry_ptr2->upstream, pim_join_prune_holdtime);
+ */
+ }
+ }
+ }
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+
+ calc_oifs(mrtentry_ptr, &oifs);
+ if (IF_ISEMPTY(&oifs))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &sockaddr6_any);
+ return (FALSE);
+ }
+
+ /* XXX: TODO: check with the spec again */
+
+ else
+ {
+ if (!nullRegisterBit)
+ {
+ /* Install cache entry in the kernel */
+ /*
+ * TODO: XXX: probably redundant here, because the
+ * decapsulated mcast packet in the kernel will result in
+ * CACHE_MISS
+ */
+
+ struct sockaddr_in6 *mfc_source = &inner_src;
+
+
+#ifdef KERNEL_MFC_WC_G
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = NULL;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
+ k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
+ mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
+ &mrtentry_ptr->group->rpaddr);
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+ if (!nullRegisterBit)
+ {
+ struct sockaddr_in6 *mfc_source = &inner_src;
+
+ /*
+ * XXX: have to create either (S,G) or (*,G). The choice below is
+ * (*,G)
+ */
+
+ mrtentry_ptr2 = find_route(NULL, &inner_grp, MRTF_WC,
+ CREATE);
+ if (mrtentry_ptr2 == (mrtentry_t *) NULL)
+ return (FALSE);
+ if (mrtentry_ptr2->flags & MRTF_NEW)
+ {
+ /* TODO: something else? Have the feeling sth is missing */
+
+ mrtentry_ptr2->flags &= ~MRTF_NEW;
+
+ /* TODO: XXX: copy the timer from the (*,*,RP) entry? */
+
+ COPY_TIMER(mrtentry_ptr->timer, mrtentry_ptr2->timer);
+ }
+ /* Install cache entry in the kernel */
+
+#ifdef KERNEL_MFC_WC_G
+
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = NULL;
+#endif /* KERNEL_MFC_WC_G */
+ add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
+ k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
+ mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
+ &mrtentry_ptr2->group->rpaddr);
+
+ return (TRUE);
+ }
+ }
+
+ /* Shoudn't happen: invalid routing entry? */
+ /* XXX: TODO: shoudn't be inner_src=IN6ADDR_ANY? Not in the spec. */
+
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+}
+
+
+int
+send_pim6_register(pkt)
+ char *pkt;
+{
+ register struct ip6_hdr *ip6;
+ static struct sockaddr_in6 source= {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 group= {sizeof(group) , AF_INET6 };
+ mifi_t mifi;
+ rpentry_t *rpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_ptr2;
+
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ int pktlen = 0;
+ char *buf;
+
+ ip6=(struct ip6_hdr *)pkt;
+
+ group.sin6_addr = ip6->ip6_dst;
+ source.sin6_addr = ip6->ip6_src;
+
+ if ((mifi = find_vif_direct_local(&source)) == NO_VIF)
+ return (FALSE);
+
+ if (!(uvifs[mifi].uv_flags & VIFF_DR))
+ return (FALSE); /* I am not the DR for that subnet */
+
+
+
+ rpentry_ptr = rp_match(&group);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ return (FALSE); /* No RP for this group */
+ if (local_address(&rpentry_ptr->address) != NO_VIF)
+ /* TODO: XXX: not sure it is working! */
+ return (FALSE); /* I am the RP for this group */
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (FALSE); /* Cannot create (S,G) state */
+
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ {
+ /* A new entry */
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->rs_timer); /* Reset the
+ * Register-Suppression timer */
+ if ((mrtentry_ptr2 = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_ptr2 =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Timeout the
+ * Join/Prune timer */
+ /*
+ * TODO: explicitly call this function?
+ * send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
+ * mrtentry_ptr2->upstream, pim_join_prune_holdtime);
+ */
+ }
+ }
+ /* Restart the (S,G) Entry-timer */
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+
+ IF_TIMER_NOT_SET(mrtentry_ptr->rs_timer)
+ {
+ /*
+ * The Register-Suppression Timer is not running. Encapsulate the
+ * data and send to the RP.
+ */
+ buf = pim6_send_buf + sizeof(struct pim);
+
+ bzero(buf, sizeof(pim_register_t)); /* No flags set */
+ buf += sizeof(pim_register_t);
+
+ /* Copy the data packet at the back of the register packet */
+ /* TODO: check pktlen. ntohs? */
+
+ pktlen = ntohs(ip6->ip6_plen);
+ pktlen +=sizeof(struct ip6_hdr); /* XXX */
+
+ bcopy((char *) ip6, buf, pktlen);
+ pktlen += sizeof(pim_register_t);
+ reg_src = max_global_address();
+ reg_dst = &mrtentry_ptr->group->rpaddr;
+
+ send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER,
+ pktlen);
+
+ pim6dstat.out_pim6_register++;
+
+ return (TRUE);
+ }
+ return (TRUE);
+}
+
+int
+send_pim6_null_register(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ struct ip6_hdr *ip;
+ pim_register_t *pim_register;
+ int pktlen = 0;
+ mifi_t mifi;
+ struct sockaddr_in6 *reg_source,
+ *dest;
+
+ /* No directly connected source; no local address */
+
+ if ((mifi = find_vif_direct_local(&mrtentry_ptr->source->address)) == NO_VIF)
+ return (FALSE);
+
+ pim_register = (pim_register_t *) (pim6_send_buf + sizeof(struct pim));
+ bzero((char *) pim_register, sizeof(pim_register_t));
+ pim_register->reg_flags = htonl(pim_register->reg_flags
+ | PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT);
+
+ /* include the dummy ip header */
+ ip = (struct ip6_hdr *) (pim_register +1);
+ ip->ip6_plen= 0;
+ ip->ip6_flow=0;
+ ip->ip6_vfc = 0x60;
+ ip->ip6_hlim = MINHLIM;
+ ip->ip6_nxt = IPPROTO_NONE;
+ ip->ip6_src = mrtentry_ptr->source->address.sin6_addr;
+ ip->ip6_dst = mrtentry_ptr->group->group.sin6_addr;
+
+ pktlen = sizeof(pim_register_t) + sizeof(struct ip6_hdr);
+
+ dest = &mrtentry_ptr->group->rpaddr;
+ reg_source = max_global_address();
+
+ send_pim6(pim6_send_buf, reg_source , dest, PIM_REGISTER,
+ pktlen);
+ pim6dstat.out_pim6_register++; /* should be counted separately? */
+
+ return (TRUE);
+}
+
+
+/************************************************************************
+ * PIM_REGISTER_STOP
+ ************************************************************************/
+int
+receive_pim6_register_stop(reg_src, reg_dst, pim_message, datalen)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ char *pim_message;
+ register int datalen;
+{
+ pim_register_stop_t *pim_regstop_p;
+ pim6_encod_grp_addr_t encod_grp;
+ pim6_encod_uni_addr_t encod_unisrc;
+ struct sockaddr_in6 source,
+ group;
+ u_int8 *data_ptr;
+ mrtentry_t *mrtentry_ptr;
+ if_set pruned_oifs;
+ mifi_t mifi;
+ struct uvif *v;
+
+ pim6dstat.in_pim6_register_stop++;
+
+ pim_regstop_p = (pim_register_stop_t *) (pim_message +
+ sizeof(struct pim));
+ data_ptr = (u_int8 *) & pim_regstop_p->encod_grp;
+ GET_EGADDR6(&encod_grp, data_ptr);
+ GET_EUADDR6(&encod_unisrc, data_ptr);
+
+ group.sin6_addr = encod_grp.mcast_addr;
+
+ /* scope validation of the inner source and destination addresses */
+
+#ifdef notyet
+ if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
+ group.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+
+ group.sin6_scope_id = 0;
+ source.sin6_addr = encod_unisrc.unicast_addr;
+
+ /* the source address must be global...but is it always true? */
+
+#ifdef notyet
+ if (IN6_IS_ADDR_SITELOCAL)
+ source.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+
+ source.sin6_scope_id = 0;
+
+ if((mifi= find_vif_direct_local(&source))==NO_VIF)
+ {
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ {
+ log(LOG_WARNING,0,
+ "Received PIM_REGISTER_STOP from RP %s for a non "
+ "direct-connect source %s",
+ inet6_fmt(&reg_src->sin6_addr),
+ inet6_fmt(&encod_unisrc.unicast_addr));
+ }
+ return FALSE;
+ }
+
+ v=&uvifs[mifi];
+
+
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ {
+ log(LOG_DEBUG, 0,
+ "Received PIM_REGISTER_STOP from RP %s to %s "
+ "source : %s group : %s",
+ inet6_fmt(&reg_src->sin6_addr),
+ inet6_fmt(&reg_dst->sin6_addr),
+ inet6_fmt(&encod_unisrc.unicast_addr),
+ inet6_fmt(&encod_grp.mcast_addr));
+ }
+
+ /* TODO: apply the group mask and do register_stop for all grp addresses */
+ /* TODO: check for SourceAddress == 0 */
+
+
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ return (FALSE);
+ }
+
+ /*
+ * XXX: not in the spec: check if the PIM_REGISTER_STOP originator is
+ * really the RP
+ */
+
+
+ if (check_mrtentry_rp(mrtentry_ptr, reg_src) == FALSE)
+ {
+ return (FALSE);
+ }
+
+ /* restart the Register-Suppression timer */
+
+ SET_TIMER(mrtentry_ptr->rs_timer, (0.5 * pim_register_suppression_timeout)
+ + (RANDOM() % (pim_register_suppression_timeout + 1)));
+ /* Prune the register_vif from the outgoing list */
+
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &pruned_oifs);
+ IF_SET(reg_vif_num, &pruned_oifs);
+ change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs, &pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ return (TRUE);
+}
+
+
+/* TODO: optional rate limiting is not implemented yet */
+/* Unicasts a REGISTER_STOP message to the DR */
+
+static int
+send_pim6_register_stop(reg_src, reg_dst, inner_grp, inner_src)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst,
+ *inner_grp,
+ *inner_src;
+{
+ char *buf;
+ u_int8 *data_ptr;
+
+ buf = pim6_send_buf + sizeof(struct pim);
+ data_ptr = (u_int8 *) buf;
+ PUT_EGADDR6(inner_grp->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
+ PUT_EUADDR6(inner_src->sin6_addr, data_ptr);
+
+ send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER_STOP,
+ data_ptr-(u_int8 *) buf);
+ pim6dstat.out_pim6_register_stop++;
+
+ return (TRUE);
+}
+
+/************************************************************************
+ * PIM_JOIN_PRUNE
+ ************************************************************************/
+int
+join_or_prune(mrtentry_ptr, upstream_router)
+ mrtentry_t *mrtentry_ptr;
+ pim_nbr_entry_t *upstream_router;
+{
+ if_set entry_oifs;
+ mrtentry_t *mrtentry_grp;
+
+ if ((mrtentry_ptr == (mrtentry_t *) NULL))
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join_or_prune : mrtentry_ptr is null");
+ return (PIM_ACTION_NOTHING);
+ }
+ if( upstream_router == (pim_nbr_entry_t *) NULL)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join_or_prune : upstream_router is null");
+ return (PIM_ACTION_NOTHING);
+ }
+
+ calc_oifs(mrtentry_ptr, &entry_oifs);
+ if (mrtentry_ptr->flags & (MRTF_PMBR | MRTF_WC))
+ {
+ /* (*,*,RP) or (*,G) entry */
+ /* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */
+
+ if (upstream_router != mrtentry_ptr->upstream)
+ return (PIM_ACTION_NOTHING);
+
+ /* TODO: XXX: Can we have (*,*,RP) prune? */
+
+ if (IF_ISEMPTY(&entry_oifs))
+ {
+ /* NULL oifs */
+ if (!(uvifs[mrtentry_ptr->incoming].uv_flags & VIFF_DR))
+ {
+ /* I am not the DR for that subnet. */
+ return (PIM_ACTION_PRUNE);
+ }
+ if (IF_ISSET(mrtentry_ptr->incoming, &mrtentry_ptr->leaves))
+ /* I am the DR and have local leaves */
+ return (PIM_ACTION_JOIN);
+ /* Probably the last local member hast timeout */
+ return (PIM_ACTION_PRUNE);
+ }
+ return (PIM_ACTION_JOIN);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry */
+ /* TODO: check again */
+ if (mrtentry_ptr->upstream == upstream_router)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ /* Upstream router toward S */
+ if (IF_ISEMPTY(&entry_oifs))
+ {
+ if (mrtentry_ptr->group->active_rp_grp != (rp_grp_entry_t *)NULL &&
+ inet6_equal(&mrtentry_ptr->group->rpaddr,
+ &my_cand_rp_address))
+ {
+ /*
+ * (S,G) at the RP. Don't send Join/Prune (see the
+ * end of Section 3.3.2)
+ */
+
+ return (PIM_ACTION_NOTHING);
+ }
+ return (PIM_ACTION_PRUNE);
+ }
+ else
+ return (PIM_ACTION_JOIN);
+ }
+ else
+ {
+ /* Upstream router toward RP */
+ if (IF_ISEMPTY(&entry_oifs))
+ return (PIM_ACTION_PRUNE);
+ }
+ }
+
+ /*
+ * Looks like the case when the upstream router toward S is different
+ * from the upstream router toward RP
+ */
+
+ if (mrtentry_ptr->group->active_rp_grp == (rp_grp_entry_t *) NULL)
+ return (PIM_ACTION_NOTHING);
+ mrtentry_grp = mrtentry_ptr->group->grp_route;
+ if (mrtentry_grp == (mrtentry_t *) NULL)
+ mrtentry_grp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_grp == (mrtentry_t *) NULL)
+ return (PIM_ACTION_NOTHING);
+ if (mrtentry_grp->upstream != upstream_router)
+ return (PIM_ACTION_NOTHING); /* XXX: shoudn't happen */
+
+ if ((!(mrtentry_ptr->flags & MRTF_RP))
+ && (mrtentry_ptr->flags & MRTF_SPT))
+ {
+ return (PIM_ACTION_PRUNE);
+ }
+ }
+ return (PIM_ACTION_NOTHING);
+}
+
+/* TODO: too long, simplify it! */
+#define PIM6_JOIN_PRUNE_MINLEN (4 + PIM6_ENCODE_UNI_ADDR_LEN + 4)
+
+int
+receive_pim6_join_prune(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ 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 *data_ptr_start;
+ u_int8 num_groups;
+ u_int8 num_groups_tmp;
+ int star_star_rp_found;
+ u_int16 holdtime;
+ u_int16 num_j_srcs;
+ u_int16 num_j_srcs_tmp;
+ u_int16 num_p_srcs;
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ struct sockaddr_in6 target;
+ struct in6_addr s_mask;
+ struct in6_addr g_mask;
+ u_int8 s_flags;
+ u_int8 reserved;
+ rpentry_t *rpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_rp;
+ grpentry_t *grpentry_ptr;
+ u_int16 jp_value;
+ pim_nbr_entry_t *upstream_router;
+ int my_action;
+ int ignore_group;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ u_int8 *data_ptr_group_j_start;
+ u_int8 *data_ptr_group_p_start;
+
+ 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];
+ v->uv_in_pim6_join_prune++;
+ if (uvifs[mifi].uv_flags &
+ (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
+ {
+ return (FALSE); /* Shoudn't come on this interface */
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_JOIN_PRUNE_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message size(%u) is"
+ " too short from %s on %s",
+ datalen, inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= PIM6_JOIN_PRUNE_MINLEN;
+ 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);
+
+ /* Sanity check for the message length through all the groups */
+ num_groups_tmp = num_groups;
+ data_ptr_start = data_ptr;
+ while (num_groups_tmp--) {
+ int srclen;
+
+ /* group addr + #join + #src */
+ if (datalen < PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t)) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message from %s on %s is"
+ " too short to contain enough data",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= (PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t));
+ data_ptr += PIM6_ENCODE_GRP_ADDR_LEN;
+
+ /* joined source addresses and pruned source addresses */
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ srclen = (num_j_srcs + num_p_srcs) * PIM6_ENCODE_SRC_ADDR_LEN;
+ if (datalen < srclen) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message from %s on %s is"
+ " too short to contain enough data",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= srclen;
+ data_ptr += srclen;
+ }
+ data_ptr = data_ptr_start;
+ num_groups_tmp = num_groups;
+
+ if (!inet6_localif_address(&target, v) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr))
+ {
+
+ /* if I am not the targer of the join message */
+ /*
+ * Join/Prune suppression code. This either modifies the J/P timers
+ * or triggers an overriding Join.
+ */
+ /*
+ * Note that if we have (S,G) prune and (*,G) Join, we must send them
+ * in the same message. We don't bother to modify both timers here.
+ * The Join/Prune sending function will take care of that.
+ */
+
+ 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 */
+ }
+
+ if (inet6_equal(&group,&sockaddr6_d) &&
+ (encod_src.masklen == STAR_STAR_RP_MSK6LEN))
+ {
+ /* (*,*,RP) Join suppression */
+
+ 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);
+ if ((s_flags & USADDR_RP_BIT) &&
+ (s_flags & USADDR_WC_BIT))
+ {
+ /* This is the RP address. */
+ rpentry_ptr = rp_find(&source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue; /* Don't have such RP. Ignore */
+ mrtentry_rp = rpentry_ptr->mrtlink;
+ my_action = join_or_prune(mrtentry_rp,
+ upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_rp->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_rp->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+
+ /*
+ * Set the Join/Prune suppression timer for this
+ * routing entry by increasing the current Join/Prune
+ * timer.
+ */
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_rp->jp_timer < jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ } /* num_j_srcs */
+
+ while (num_p_srcs--)
+ {
+ /*
+ * TODO: XXX: Can we have (*,*,RP) prune message? Not in
+ * the spec, but anyway, the code below can handle them:
+ * either suppress the local (*,*,RP) prunes or override
+ * the prunes by sending (*,*,RP) and/or (*,G) and/or
+ * (S,G) Join.
+ */
+
+ 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);
+ if ((s_flags & USADDR_RP_BIT) &&
+ (s_flags & USADDR_WC_BIT))
+ {
+ /* This is the RP address. */
+ rpentry_ptr = rp_find(&source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue; /* Don't have such RP. Ignore */
+ mrtentry_rp = rpentry_ptr->mrtlink;
+ my_action = join_or_prune(mrtentry_rp,
+ upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_rp->jp_timer < holdtime)
+ || ((mrtentry_rp->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ /* Suppress the Prune */
+ jp_value = pim_join_prune_period+
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_rp->jp_timer < jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_rp->jp_timer > jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ /*
+ * Check all (*,G) and (S,G) matching to this RP. If
+ * my_action == JOIN, then send a Join and override
+ * the (*,*,RP) Prune.
+ */
+ for (grpentry_ptr =
+ rpentry_ptr->cand_rp->rp_grp_next->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ my_action = join_or_prune(grpentry_ptr->grp_route,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (grpentry_ptr->grp_route->jp_timer >
+ jp_value)
+ SET_TIMER(grpentry_ptr->grp_route->jp_timer, jp_value);
+ }
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ my_action = join_or_prune(mrtentry_srcs,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_srcs->jp_timer > jp_value)
+ SET_TIMER(mrtentry_srcs->jp_timer, jp_value);
+ }
+ } /* For all (S,G) */
+ } /* For all (*,G) */
+ }
+ } /* num_p_srcs */
+ continue; /* This was (*,*,RP) suppression */
+ }
+
+ /* (*,G) or (S,G) suppression */
+ /*
+ * TODO: XXX: currently, accumulated groups (i.e. group_masklen <
+ * group_address_lengt) are not implemented. Just need to create
+ * a loop and apply the procedure below for all groups matching
+ * the prefix.
+ */
+
+ 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);
+
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) JOIN_REQUEST (toward the RP) */
+ mrtentry_ptr = find_route(&sockaddr6_any , &group, MRTF_WC,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+ /* (*,G) Join suppresion */
+ if (!inet6_equal(&source,&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address))
+ continue; /* The RP address doesn't match.
+ * Ignore. */
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ continue;
+ } /* End of (*,G) Join suppression */
+
+ /* (S,G) Join suppresion */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ continue;
+ }
+
+ /* Prunes suppression */
+ 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 (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) prune suppression */
+ rpentry_ptr = rp_match(&source);
+ if ((rpentry_ptr == (rpentry_t *) NULL)
+ || (!inet6_equal(&rpentry_ptr->address , &source)))
+ continue; /* No such RP or it is different.
+ * Ignore */
+ mrtentry_ptr = find_route(&sockaddr6_any, &group, MRTF_WC,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->jp_timer < holdtime)
+ || ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ /* Suppress the Prune */
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+
+ /*
+ * Check all (S,G) entries for this group. If my_action
+ * == JOIN, then send the Join and override the (*,G)
+ * Prune.
+ */
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ my_action = join_or_prune(mrtentry_srcs,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ } /* For all (S,G) */
+ continue; /* End of (*,G) prune suppression */
+ }
+
+ /* (S,G) prune suppression */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* Suppress the (S,G) Prune */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->jp_timer < holdtime)
+ || ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ } /* while (num_p_srcs--) */
+ } /* while (num_groups--) */
+ return (TRUE);
+ } /* End of Join/Prune suppression code */
+
+ /* I am the target of this join, so process the message */
+
+ /*
+ * The spec says that if there is (*,G) Join, it has priority over old
+ * existing ~(S,G) prunes in the routing table. However, if the (*,G)
+ * Join and the ~(S,G) prune are in the same message, ~(S,G) has the
+ * priority. The spec doesn't say it, but I think the same is true for
+ * (*,*,RP) and ~(S,G) prunes.
+ *
+ * The code below do: (1) Check the whole message for (*,*,RP) Joins. (1.1)
+ * If found, clean all pruned_oifs for all (*,G) and all (S,G) for each
+ * RP in the list, but do not update the kernel cache. Then go back to
+ * the beginning of the message and start processing for each group: (2)
+ * Check for Prunes. If no prunes, process the Joins. (3) If there are
+ * Prunes: (3.1) Scan the Join part for existing (*,G) Join. (3.1.1) If
+ * there is (*,G) Join, clear join interface from the pruned_oifs for all
+ * (S,G), but DO NOT flush the change to the kernel (by using
+ * change_interfaces() for example) (3.2) After the pruned_oifs are
+ * eventually cleared in (3.1.1), process the Prune part of the message
+ * normally (setting the prune_oifs and flashing the changes to the
+ * (kernel). (3.3) After the Prune part is processed, process the Join
+ * part normally (by applying any changes to the kernel) (4) If there
+ * were (*,*,RP) Join/Prune, process them.
+ *
+ * If the Join/Prune list is too long, it may result in long processing
+ * overhead. The idea above is not to place any wrong info in the kernel,
+ * because it may result in short-time existing traffic forwarding on
+ * wrong interface. Hopefully, in the future will find a better way to
+ * implement it.
+ */
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"I'm the target of the JOIN/PRUNE message");
+
+ num_groups_tmp = num_groups;
+ data_ptr_start = data_ptr;
+ star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Number of groups to process : %d",num_groups_tmp);
+
+ while (num_groups_tmp--)
+ {
+ /* Search for (*,*,RP) Join */
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG, 0,
+ "Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
+ log(LOG_DEBUG, 0,
+ "Number of join : %d",num_j_srcs );
+ log(LOG_DEBUG, 0,
+ "Number of prune : %d",num_p_srcs );
+ }
+
+ if (!(inet6_equal(&group,&sockaddr6_d))
+ || (encod_src.masklen != STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is not (*,*,RP). Jump to the next group. */
+ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG, 0,
+ "I'm looking for the (*,*,RP) entry , skip to next entry");
+ }
+ continue;
+ }
+
+ /*
+ * (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear
+ * the pruned oif, but do not update the kernel.
+ */
+
+ star_star_rp_found = TRUE;
+ while (num_j_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ rpentry_ptr = rp_find(&source);
+
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue;
+ for (rp_grp_entry_ptr = rpentry_ptr->cand_rp->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ IF_CLR(mifi, &grpentry_ptr->grp_route->pruned_oifs);
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ }
+ }
+ }
+ data_ptr += (num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ }
+
+ /*
+ * Start processing the groups. If this is (*,*,RP), skip it, but process
+ * it at the end.don't forget to reinit data_ptr!
+ */
+
+ data_ptr = data_ptr_start;
+ num_groups_tmp = num_groups;
+
+ while (num_groups_tmp--)
+ {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG,0,"Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
+ log(LOG_DEBUG,0,"Number of join : %d",num_j_srcs );
+ log(LOG_DEBUG,0,"Number of prune : %d",num_p_srcs );
+ }
+
+ 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 one */
+ }
+
+
+ if (inet6_equal(&group, &sockaddr6_d)
+ && (encod_group.masklen == STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is (*,*,RP). Jump to the next group. */
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) {
+ log(LOG_DEBUG, 0, "This is (*,*,RP). Jump to next.");
+ }
+ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue;
+ }
+
+ rpentry_ptr = rp_match(&group);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue;
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"The rp for this JOIN/PRUNE is %s",inet6_fmt(&rpentry_ptr->address.sin6_addr));
+
+ data_ptr_group_j_start = data_ptr;
+ data_ptr_group_p_start = data_ptr + num_j_srcs * sizeof(pim6_encod_src_addr_t);
+
+ /*
+ * Scan the Join part for (*,G) Join and then clear the particular
+ * interface from pruned_oifs for all (S,G). If the RP address in the
+ * Join message is different from the local match, ignore the whole
+ * group.
+ */
+
+ num_j_srcs_tmp = num_j_srcs;
+ ignore_group = FALSE;
+
+ while (num_j_srcs_tmp--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr=encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,v);
+
+ if ((encod_src.flags & USADDR_RP_BIT)
+ && (encod_src.flags & USADDR_WC_BIT))
+ {
+ /*
+ * This is the RP address, i.e. (*,G) Join. Check if the
+ * RP-mapping is consistent and if "yes", then Reset the
+ * pruned_oifs for all (S,G) entries.
+ */
+
+ if(!inet6_equal(&rpentry_ptr->address, &source))
+ {
+ ignore_group = TRUE;
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"And I'm not the RP for this address");
+ break;
+ }
+
+ mrtentry_ptr = find_route(&sockaddr6_any, &group,
+ MRTF_WC, DONT_CREATE);
+
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ IF_CLR(mifi, &mrtentry_srcs->pruned_oifs);
+ }
+ break;
+ }
+ }
+
+ if (ignore_group == TRUE)
+ continue;
+
+ data_ptr = data_ptr_group_p_start;
+
+ /* Process the Prune part first */
+
+ 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;
+ if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
+ {
+ /* (S,G) prune sent toward S */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue; /* I don't have (S,G) to prune. Ignore. */
+ /*
+ * If the link is point-to-point, timeout the oif
+ * immediately, otherwise decrease the timer to allow other
+ * downstream routers to override the prune.
+ */
+ /* TODO: XXX: increase the entry timer? */
+
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ if ((s_flags & USADDR_RP_BIT)
+ && (!(s_flags & USADDR_WC_BIT)))
+ {
+ /* ~(S,G)RPbit prune sent toward the RP */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+ /* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_RP,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
+
+ /*
+ * TODO: XXX: The spec doens't say what value to use for
+ * the entry time. Use the J/P holdtime.
+ */
+
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+
+ /*
+ * TODO: XXX: The spec says to delete the oif. However,
+ * its timer only should be lowered, so the prune can be
+ * overwritten on multiaccess LAN. Spec BUG.
+ */
+
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) Prune */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /*
+ * TODO: XXX: Should check the whole Prune list in
+ * advance for (*,G) prune and if the RP address does
+ * not match the local RP-map, then ignore the whole
+ * group, not only this particular (*,G) prune.
+ */
+ if (!inet6_equal(&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address, &source ))
+ continue; /* The RP address doesn't match. */
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+ /* No (*,G) entry, but found (*,*,RP). Create (*,G) */
+ if (!inet6_equal(&mrtentry_ptr->source->address, &source))
+ continue; /* The RP address doesn't match. */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
+
+ /*
+ * TODO: XXX: should only lower the oif timer, so it can
+ * be overwritten on multiaccess LAN. Spec bug.
+ */
+
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ } /* (*,G) or (*,*,RP) found */
+ } /* (*,G) prune */
+ } /* while(num_p_srcs--) */
+
+ /* End of (S,G) and (*,G) Prune handling */
+
+ /* Jump back to the Join part and process it */
+ data_ptr = data_ptr_group_j_start;
+ 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;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_WC_BIT)
+ && (s_flags & USADDR_RP_BIT))
+ {
+ /* (*,G) Join toward RP */
+ /*
+ * It has been checked already that this RP address is the
+ * same as the local RP-maping.
+ */
+ mrtentry_ptr = find_route(NULL, &group, MRTF_WC,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ /*
+ * Need to update the (S,G) entries, because of the previous
+ * cleaning of the pruned_oifs. The reason is that if the
+ * oifs for (*,G) weren't changed, the (S,G) entries won't be
+ * updated by change_interfaces()
+ */
+
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ continue;
+ }
+
+ if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
+ {
+ /* (S,G) Join toward S */
+ if (mifi == get_iif(&source))
+ continue; /* Ignore this (S,G) Join */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ /*
+ * TODO: if this is a new entry, send immediately the Join
+ * message toward S. The Join/Prune timer for new entries is
+ * 0, but it does not means the message will be sent
+ * immediately.
+ */
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ /*
+ * Note that we must create (S,G) without the RPbit set. If
+ * we already had such entry, change_interfaces() will reset
+ * the RPbit propertly.
+ */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->source->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ continue;
+ }
+ } /* while(num_j_srcs--) */
+ } /* for all groups */
+
+ /* Now process the (*,*,RP) Join/Prune */
+
+ if (star_star_rp_found == TRUE)
+ return (TRUE);
+ data_ptr = data_ptr_start;
+ while (num_groups--)
+ {
+ /*
+ * The conservative approach is to scan again the whole message, just
+ * in case if we have more than one (*,*,RP) requests.
+ */
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ if (!inet6_equal(&group,&sockaddr6_d)
+ || (encod_group.masklen != STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is not (*,*,RP). Jump to the next group. */
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue;
+ }
+ /* (*,*,RP) found */
+ while (num_j_srcs--)
+ {
+ /* TODO: XXX: check that the iif is different from the Join oifs */
+ 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;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ mrtentry_ptr = find_route(&source, NULL, MRTF_PMBR,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+
+ /*
+ * Need to update the (S,G) and (*,G) entries, because of the
+ * previous cleaning of the pruned_oifs. The reason is that if
+ * the oifs for (*,*,RP) weren't changed, the (*,G) and (S,G)
+ * entries won't be updated by change_interfaces()
+ */
+
+ for (rp_grp_entry_ptr = mrtentry_ptr->source->cand_rp->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ /* Update the (*,G) entry */
+ change_interfaces(grpentry_ptr->grp_route,
+ grpentry_ptr->grp_route->incoming,
+ &grpentry_ptr->grp_route->joined_oifs,
+ &grpentry_ptr->grp_route->pruned_oifs,
+ &grpentry_ptr->grp_route->leaves,
+ &grpentry_ptr->grp_route->asserted_oifs, 0);
+ /* Update the (S,G) entries */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ while (num_p_srcs--)
+ {
+ /* TODO: XXX: can we have (*,*,RP) Prune? */
+ 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;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ mrtentry_ptr = find_route(&source, NULL , MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ /*
+ * If the link is point-to-point, timeout the oif immediately,
+ * otherwise decrease the timer to allow other downstream routers
+ * to override the prune.
+ */
+ /* TODO: XXX: increase the entry timer? */
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+
+ }
+ } /* For all groups processing (*,*,R) */
+
+ return (TRUE);
+}
+
+
+/*
+ * TODO: NOT USED, probably buggy, but may need it in the future.
+ */
+/*
+ * TODO: create two functions: periodic which timeout the timers and
+ * non-periodic which only check but don't timeout the timers.
+ */
+/*
+ * Create and send Join/Prune messages per interface. Only the entries which
+ * have the Join/Prune timer expired are included. In the special case when
+ * we have ~(S,G)RPbit Prune entry, we must include any (*,G) or (*,*,RP)
+ * Currently the whole table is scanned. In the future will have all routing
+ * entries linked in a chain with the corresponding upstream pim_nbr_entry.
+ *
+ * If pim_nbr is not NULL, then send to only this particular PIM neighbor,
+ */
+int
+send_periodic_pim6_join_prune(mifi, pim_nbr, holdtime)
+ mifi_t mifi;
+ pim_nbr_entry_t *pim_nbr;
+ u_int16 holdtime;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ rpentry_t *rpentry_ptr;
+ struct sockaddr_in6 src_addr;
+ struct uvif *v;
+ pim_nbr_entry_t *pim_nbr_ptr;
+ cand_rp_t *cand_rp_ptr;
+
+ /*
+ * Walk through all routing entries. The iif must match to include the
+ * entry. Check first the (*,G) entry and then all associated (S,G). At
+ * the end of the message will add any (*,*,RP) entries. TODO: check
+ * other PIM-SM implementations and decide the more appropriate place to
+ * put the (*,*,RP) entries: in the beginning of the message or at the
+ * end.
+ */
+
+ v = &uvifs[mifi];
+
+ /* Check the (*,G) and (S,G) entries */
+ for (grpentry_ptr = grplist; grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->next)
+ {
+ mrtentry_ptr = grpentry_ptr->grp_route;
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr != (mrtentry_t *) NULL)
+ && (mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (mrtentry_ptr->upstream != pim_nbr))
+ continue;
+
+ /* TODO: XXX: The J/P suppression timer is not in the spec! */
+ if (!IF_ISEMPTY(&mrtentry_ptr->joined_oifs) ||
+ (v->uv_flags & VIFF_DR))
+ {
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group,
+ SINGLE_GRP_MSK6LEN,
+ &grpentry_ptr->rpaddr,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
+ }
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
+ && (!(v->uv_flags & VIFF_DR))
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ {
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &grpentry_ptr->rpaddr,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
+ }
+ }
+
+ /* Check the (S,G) entries */
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (mrtentry_ptr->upstream != pim_nbr))
+ continue;
+
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /* RPbit set */
+
+ src_addr = mrtentry_ptr->source->address;
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
+ || ((find_vif_direct_local(&src_addr) != NO_VIF)
+ && grpentry_ptr->grp_route != (mrtentry_t *) NULL))
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((grpentry_ptr->grp_route->incoming == mifi)
+ && (grpentry_ptr->grp_route->jp_timer
+ <= timer_interval))
+ /* S is directly connected. Send toward RP */
+ add_jp_entry(grpentry_ptr->grp_route->upstream,
+ holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &src_addr, SINGLE_SRC_MSK6LEN,
+ MRTF_RP, PIM_ACTION_PRUNE);
+ }
+ else
+ {
+ /* RPbit cleared */
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs))
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
+ }
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->flags & MRTF_SPT)
+ && (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (mrtentry_ptr->incoming !=
+ grpentry_ptr->grp_route->incoming)
+ && (grpentry_ptr->grp_route->incoming == mifi)
+ && (grpentry_ptr->grp_route->jp_timer
+ <= timer_interval))
+ add_jp_entry(grpentry_ptr->grp_route->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, MRTF_RP,
+ PIM_ACTION_PRUNE);
+ }
+ }
+ }
+
+ /* Check the (*,*,RP) entries */
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ rpentry_ptr = cand_rp_ptr->rpentry;
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (rpentry_ptr->upstream != pim_nbr))
+ continue;
+
+
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((rpentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ && (rpentry_ptr->incoming == mifi)
+ && (rpentry_ptr->mrtlink->jp_timer <= timer_interval))
+ {
+ add_jp_entry(rpentry_ptr->upstream, holdtime,
+ &sockaddr6_d, STAR_STAR_RP_MSK6LEN,
+ &rpentry_ptr->address,
+ SINGLE_SRC_MSK6LEN, MRTF_RP | MRTF_WC,
+ PIM_ACTION_JOIN);
+ }
+ }
+
+ /* Send all pending Join/Prune messages */
+ for (pim_nbr_ptr = v->uv_pim_neighbors;
+ pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
+ pim_nbr_ptr = pim_nbr->next)
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (pim_nbr_ptr != pim_nbr))
+ continue;
+
+ pack_and_send_jp6_message(pim_nbr_ptr);
+ }
+
+ return (TRUE);
+}
+
+
+int
+add_jp_entry(pim_nbr, holdtime, group, grp_msklen, source, src_msklen,
+ addr_flags, join_prune)
+ pim_nbr_entry_t *pim_nbr;
+ u_int16 holdtime;
+ struct sockaddr_in6 *group;
+ u_int8 grp_msklen;
+ struct sockaddr_in6 *source;
+ u_int8 src_msklen;
+ u_int16 addr_flags;
+ u_int8 join_prune;
+{
+ build_jp_message_t *bjpm;
+ u_int8 *data_ptr;
+ u_int8 flags = 0;
+ int rp_flag;
+
+
+ bjpm = pim_nbr->build_jp_message;
+
+ if (bjpm != (build_jp_message_t *) NULL)
+ {
+ if ((bjpm->jp_message_size + bjpm->join_list_size +
+ bjpm->prune_list_size + bjpm->rp_list_join_size +
+ bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE)
+ || (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE)
+ || (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE)
+ || (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE)
+ || (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE))
+ {
+ /*
+ * TODO: XXX: BUG: If the list is getting too large, must be
+ * careful with the fragmentation.
+ */
+ pack_and_send_jp6_message(pim_nbr);
+ bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
+ }
+ }
+
+ if (bjpm != (build_jp_message_t *) NULL)
+ {
+ if ((!inet6_equal(&bjpm->curr_group, group)
+ || (bjpm->curr_group_msklen != grp_msklen)
+ || (bjpm->holdtime != holdtime)))
+ {
+ pack_jp6_message(pim_nbr);
+ }
+ }
+
+ if (bjpm == (build_jp_message_t *) NULL)
+ {
+ bjpm = get_jp6_working_buff();
+ pim_nbr->build_jp_message = bjpm;
+ data_ptr = bjpm->jp_message;
+ PUT_EUADDR6(pim_nbr->address.sin6_addr, data_ptr);
+ PUT_BYTE(0, data_ptr); /* Reserved */
+ bjpm->num_groups_ptr = data_ptr++; /* The pointer for numgroups */
+ *(bjpm->num_groups_ptr) = 0; /* Zero groups */
+ PUT_HOSTSHORT(holdtime, data_ptr);
+ bjpm->holdtime = holdtime;
+ bjpm->jp_message_size = data_ptr - bjpm->jp_message;
+ }
+
+ /* TODO: move somewhere else, only when it is a new group */
+ bjpm->curr_group = *group;
+ bjpm->curr_group_msklen = grp_msklen;
+
+ if (inet6_equal(group, &sockaddr6_d) &&
+ (grp_msklen == STAR_STAR_RP_MSK6LEN))
+ rp_flag = TRUE;
+ else
+ rp_flag = FALSE;
+
+ switch (join_prune)
+ {
+ case PIM_ACTION_JOIN:
+ if (rp_flag == TRUE)
+ data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
+ else
+ data_ptr = bjpm->join_list + bjpm->join_list_size;
+ break;
+ case PIM_ACTION_PRUNE:
+ if (rp_flag == TRUE)
+ data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
+ else
+ data_ptr = bjpm->prune_list + bjpm->prune_list_size;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */
+ if (addr_flags & MRTF_RP)
+ flags |= USADDR_RP_BIT;
+ if (addr_flags & MRTF_WC)
+ flags |= USADDR_WC_BIT;
+ PUT_ESADDR6(source->sin6_addr, src_msklen, flags, data_ptr);
+
+ switch (join_prune)
+ {
+ case PIM_ACTION_JOIN:
+ if (rp_flag == TRUE)
+ {
+ bjpm->rp_list_join_size = data_ptr - bjpm->rp_list_join;
+ bjpm->rp_list_join_number++;
+ }
+ else
+ {
+ bjpm->join_list_size = data_ptr - bjpm->join_list;
+ bjpm->join_addr_number++;
+ }
+ break;
+ case PIM_ACTION_PRUNE:
+ if (rp_flag == TRUE)
+ {
+ bjpm->rp_list_prune_size = data_ptr - bjpm->rp_list_prune;
+ bjpm->rp_list_prune_number++;
+ }
+ else
+ {
+ bjpm->prune_list_size = data_ptr - bjpm->prune_list;
+ bjpm->prune_addr_number++;
+ }
+ break;
+ default:
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/* TODO: check again the size of the buffers */
+
+static build_jp_message_t *
+get_jp6_working_buff()
+{
+ build_jp_message_t *bjpm_ptr;
+
+ if (build_jp_message_pool_counter == 0)
+ {
+ bjpm_ptr = (build_jp_message_t *) malloc(sizeof(build_jp_message_t));
+ bjpm_ptr->next = (build_jp_message_t *) NULL;
+ bjpm_ptr->jp_message =
+ (u_int8 *) malloc(MAX_JP_MESSAGE_SIZE +
+ sizeof(pim_jp_encod_grp_t) +
+ 2 * sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->jp_message_size = 0;
+ bjpm_ptr->join_list_size = 0;
+ bjpm_ptr->join_addr_number = 0;
+ bjpm_ptr->join_list = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->prune_list_size = 0;
+ bjpm_ptr->prune_addr_number = 0;
+ bjpm_ptr->prune_list = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->rp_list_join_size = 0;
+ bjpm_ptr->rp_list_join_number = 0;
+ bjpm_ptr->rp_list_join = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->rp_list_prune_size = 0;
+ bjpm_ptr->rp_list_prune_number = 0;
+ bjpm_ptr->rp_list_prune = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->curr_group = sockaddr6_any;
+ bjpm_ptr->curr_group_msklen = 0;
+ bjpm_ptr->holdtime = 0;
+ return bjpm_ptr;
+ }
+ else
+ {
+ bjpm_ptr = build_jp_message_pool;
+ build_jp_message_pool = build_jp_message_pool->next;
+ build_jp_message_pool_counter--;
+ bjpm_ptr->jp_message_size = 0;
+ bjpm_ptr->join_list_size = 0;
+ bjpm_ptr->join_addr_number = 0;
+ bjpm_ptr->prune_list_size = 0;
+ bjpm_ptr->prune_addr_number = 0;
+ bjpm_ptr->curr_group = sockaddr6_any;
+ bjpm_ptr->curr_group_msklen = 0;
+ return (bjpm_ptr);
+ }
+}
+
+
+static void
+return_jp6_working_buff(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ build_jp_message_t *bjpm_ptr = pim_nbr->build_jp_message;
+
+ if (bjpm_ptr == (build_jp_message_t *) NULL)
+ return;
+ /* Don't waste memory by keeping too many free buffers */
+ /* TODO: check/modify the definitions for POOL_NUMBER and size */
+ if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER)
+ {
+ free((void *) bjpm_ptr->jp_message);
+ free((void *) bjpm_ptr->join_list);
+ free((void *) bjpm_ptr->prune_list);
+ free((void *) bjpm_ptr->rp_list_join);
+ free((void *) bjpm_ptr->rp_list_prune);
+ free((void *) bjpm_ptr);
+ }
+ else
+ {
+ bjpm_ptr->next = build_jp_message_pool;
+ build_jp_message_pool = bjpm_ptr;
+ build_jp_message_pool_counter++;
+ }
+ pim_nbr->build_jp_message = (build_jp_message_t *) NULL;
+}
+
+
+/*
+ * TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the Join/Prune
+ * message. However, this particular implementation of PIM processes the
+ * Join/Prune messages faster if (*,*,RP) is at the beginning. Modify some of
+ * the functions below such that the outgoing messages place (*,*,RP) at the
+ * beginning, not at the end.
+ */
+
+static void
+pack_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ build_jp_message_t *bjpm;
+ u_int8 *data_ptr;
+
+ bjpm = pim_nbr->build_jp_message;
+ if ((bjpm == (build_jp_message_t *) NULL)
+ || (inet6_equal(&bjpm->curr_group,&sockaddr6_any)))
+ return;
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+ PUT_EGADDR6(bjpm->curr_group.sin6_addr, bjpm->curr_group_msklen, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->join_addr_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->prune_addr_number, data_ptr);
+ bcopy(bjpm->join_list, data_ptr, bjpm->join_list_size);
+ data_ptr += bjpm->join_list_size;
+ bcopy(bjpm->prune_list, data_ptr, bjpm->prune_list_size);
+ data_ptr += bjpm->prune_list_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->join_list_size = 0;
+ bjpm->join_addr_number = 0;
+#if 0 /* isn't this necessary? */
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+#endif
+ bjpm->prune_list_size = 0;
+ bjpm->prune_addr_number = 0;
+#if 0 /* isn't this necessary? */
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+#endif
+ (*bjpm->num_groups_ptr)++;
+ bjpm->curr_group = sockaddr6_any;
+ bjpm->curr_group_msklen = 0;
+ if (*bjpm->num_groups_ptr == ((u_int8) ~ 0 - 1))
+ {
+ if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
+ {
+ /* Add the (*,*,RP) at the end */
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+ PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
+ bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
+ data_ptr += bjpm->rp_list_join_size;
+ bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
+ data_ptr += bjpm->rp_list_prune_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+ (*bjpm->num_groups_ptr)++;
+ }
+ send_jp6_message(pim_nbr);
+ }
+}
+
+void
+pack_and_send_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ u_int8 *data_ptr;
+ build_jp_message_t *bjpm;
+
+
+ if ((pim_nbr == (pim_nbr_entry_t *) NULL)
+ || ((bjpm = pim_nbr->build_jp_message) == (build_jp_message_t *) NULL))
+ {
+ return;
+ }
+ pack_jp6_message(pim_nbr);
+
+
+ if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
+ {
+ /* Add the (*,*,RP) at the end */
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+
+ PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
+ bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
+ data_ptr += bjpm->rp_list_join_size;
+ bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
+ data_ptr += bjpm->rp_list_prune_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+ (*bjpm->num_groups_ptr)++;
+ }
+ send_jp6_message(pim_nbr);
+}
+
+static void
+send_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ u_int16 datalen;
+ mifi_t mifi;
+
+ datalen = pim_nbr->build_jp_message->jp_message_size;
+ mifi = pim_nbr->vifi;
+ bcopy(pim_nbr->build_jp_message->jp_message,
+ pim6_send_buf+sizeof(struct pim), datalen);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group , PIM_JOIN_PRUNE, datalen);
+ uvifs[mifi].uv_out_pim6_join_prune++;
+ return_jp6_working_buff(pim_nbr);
+}
+
+/************************************************************************
+ * PIM_ASSERT
+ ************************************************************************/
+int
+receive_pim6_assert(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ 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,
+ *mrtentry_ptr2;
+ u_int8 *data_ptr;
+ struct uvif *v;
+ u_int32 assert_preference;
+ u_int32 assert_metric;
+ u_int32 assert_rptbit;
+ u_int32 local_metric;
+ u_int32 local_preference;
+ u_int8 local_rptbit;
+ u_int8 local_wins;
+ pim_nbr_entry_t *original_upstream_router;
+
+
+ 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];
+ v->uv_in_pim6_assert++;
+ if (uvifs[mifi].uv_flags &
+ (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
+ 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);
+ assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT;
+
+ 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);
+
+ /* Find the longest "active" entry, i.e. the one with a kernel mirror */
+ if (assert_rptbit)
+ {
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR, DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ mrtentry_ptr =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ }
+ else
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
+ if ((mrtentry_ptr != (mrtentry_t *) NULL))
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ {
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ mrtentry_ptr2 = mrtentry_ptr->group->grp_route;
+ if ((mrtentry_ptr2 != (mrtentry_t *) NULL)
+ && (mrtentry_ptr2->flags & MRTF_KERNEL_CACHE))
+ mrtentry_ptr = mrtentry_ptr2;
+ else
+ mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_WC)
+ mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ }
+ if ((mrtentry_ptr == (mrtentry_t *) NULL)
+ || (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)))
+ /* No routing entry or not "active" entry. Ignore the assert */
+ return (FALSE);
+
+ /* Prepare the local preference and metric */
+ if ((mrtentry_ptr->flags & MRTF_PMBR)
+ || ((mrtentry_ptr->flags & MRTF_SG)
+ && (!(mrtentry_ptr->flags & MRTF_RP))))
+ {
+ /* Either (S,G) (toward S) or (*,*,RP). */
+ /* TODO: XXX: get the info from mrtentry, or source or from kernel ? */
+ /*
+ * local_metric = mrtentry_ptr->source->metric; local_preference =
+ * mrtentry_ptr->source->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+ else
+ {
+ /*
+ * Should be (*,G) or (S,G)RPbit entry. Get what we need from the RP
+ * info.
+ */
+ /* TODO: get the info from mrtentry, RP-entry or kernel? */
+ /*
+ * local_metric =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
+ * local_preference =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference;
+ */
+
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+
+ local_rptbit = (mrtentry_ptr->flags & MRTF_RP);
+ if (local_rptbit)
+ /* Make the RPT bit the most significant one */
+ local_preference |= PIM_ASSERT_RPT_BIT;
+
+
+ if (IF_ISSET(mifi, &mrtentry_ptr->oifs))
+ {
+ /* The ASSERT has arrived on oif */
+
+ /*
+ * TODO: XXX: here the processing order is different from the spec.
+ * The spec requires first eventually to create a routing entry (see
+ * 3.5.2.1(1) and then compare the metrics. Here we compare first the
+ * metrics with the existing longest match entry and if we lose then
+ * create a new entry and compare again. This saves us the
+ * unnecessary creating of a routing entry if we anyway are going to
+ * lose: for example the local (*,*,RP) vs the remote (*,*,RP) or
+ * (*,G)
+ */
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &v->uv_linklocal->pa_addr, assert_preference,
+ assert_metric, src);
+
+ if (local_wins == TRUE)
+ {
+ /* TODO: verify the parameters */
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ return (TRUE);
+ }
+
+ /* Create a "better" routing entry and try again */
+
+ if ((assert_rptbit) && (mrtentry_ptr->flags & MRTF_PMBR))
+ {
+ /* The matching entry was (*,*,RP). Create (*,G) */
+ mrtentry_ptr2 = find_route(NULL, &group, MRTF_WC, CREATE);
+ }
+ else
+ if ((!assert_rptbit) &&
+ (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR)))
+ {
+ /* create (S,G) */
+ mrtentry_ptr2 = find_route(&source, &group, MRTF_SG, CREATE);
+ }
+ else
+ {
+ /* We have no chance to win. Give up and prune the oif */
+ mrtentry_ptr2 = (mrtentry_t *) NULL;
+ }
+
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr2->flags &= ~MRTF_NEW;
+
+ /*
+ * TODO: XXX: The spec doesn't say what entry timer value to use
+ * when the routing entry is created because of asserts.
+ */
+
+ SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
+ if (mrtentry_ptr2->flags & MRTF_RP)
+ {
+ /*
+ * Either (*,G) or (S,G)RPbit entry. Get what we need from
+ * the RP info.
+ */
+ /* TODO: where to get the metric+preference from? */
+ /*
+ * local_metric =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
+ * local_preference =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference
+ * ;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ local_preference |= PIM_ASSERT_RPT_BIT;
+ }
+ else
+ {
+ /* (S,G) toward the source */
+ /* TODO: where to get the metric from ? */
+ /*
+ * local_metric = mrtentry_ptr->source->metric;
+ * local_preference = mrtentry_ptr->source->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &v->uv_linklocal->pa_addr, assert_preference,
+ assert_metric, src);
+
+ if (local_wins == TRUE)
+ {
+ /* TODO: verify the parameters */
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ return (TRUE);
+ }
+ /* We lost, but have created the entry which has to be pruned */
+ mrtentry_ptr = mrtentry_ptr2;
+ }
+
+ /* Have to remove that outgoing vifi from mrtentry_ptr */
+ IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->timer < pim_assert_timeout)
+ SET_TIMER(mrtentry_ptr->timer, pim_assert_timeout);
+ /*
+ * TODO: XXX: check that the timer of all affected routing entries
+ * has been restarted.
+ */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ return (FALSE); /* Doesn't matter the return value */
+ } /* End of assert received on oif */
+
+
+ if (mrtentry_ptr->incoming == mifi)
+ {
+ /* Assert received on iif */
+ if (assert_rptbit)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ return (TRUE); /* The locally used upstream router will win
+ * the assert, so don't change it. */
+ }
+
+ /*
+ * TODO: where to get the local metric and preference from? system
+ * call or mrtentry is fine?
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ if (mrtentry_ptr->flags & MRTF_RP)
+ local_preference |= PIM_ASSERT_RPT_BIT;
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &mrtentry_ptr->upstream->address,
+ assert_preference, assert_metric, src);
+
+ if (local_wins == TRUE)
+ return (TRUE); /* return whatever */
+
+ /* The upstream must be changed to the winner */
+ mrtentry_ptr->preference = assert_preference;
+ mrtentry_ptr->metric = assert_metric;
+ mrtentry_ptr->upstream = find_pim6_nbr(src);
+
+ /* Check if the upstream router is different from the original one */
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ original_upstream_router = mrtentry_ptr->source->upstream;
+ else
+ if (mrtentry_ptr->flags & MRTF_RP)
+ original_upstream_router =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->upstream;
+ else
+ original_upstream_router = mrtentry_ptr->source->upstream;
+ if (mrtentry_ptr->upstream != original_upstream_router)
+ {
+ mrtentry_ptr->flags |= MRTF_ASSERTED;
+ SET_TIMER(mrtentry_ptr->assert_timer, pim_assert_timeout);
+ }
+ else
+ mrtentry_ptr->flags &= ~MRTF_ASSERTED;
+ }
+
+ 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;
+ srcentry_t *srcentry_ptr;
+
+ /* Don't send assert if the outgoing interface a tunnel or register vif */
+ if (uvifs[mifi].uv_flags & (MIFF_REGISTER | VIFF_TUNNEL))
+ return (FALSE);
+
+ 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);
+
+ /*
+ * TODO: XXX: where to get the metric from: srcentry_ptr or mrtentry_ptr
+ * or from the kernel?
+ */
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) */
+ srcentry_ptr = mrtentry_ptr->source;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
+ */
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /* (*,G) or (S,G)RPbit (iif toward RP) */
+ srcentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
+ */
+ }
+ else
+ {
+ /* (S,G) toward S */
+ srcentry_ptr = mrtentry_ptr->source;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_SOURCE);
+ */
+ }
+
+ /*
+ * TODO: check again! local_metric = srcentry_ptr->metric;
+ * local_preference = srcentry_ptr->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+
+ if (mrtentry_ptr->flags & MRTF_RP)
+ local_preference |= PIM_ASSERT_RPT_BIT;
+ PUT_HOSTLONG(local_preference, data_ptr);
+ PUT_HOSTLONG(local_metric, data_ptr);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_ASSERT,
+ data_ptr - data_start_ptr);
+ uvifs[mifi].uv_out_pim6_assert++;
+
+ 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 threatment 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_BOOTSTRAP
+ ************************************************************************/
+#define PIM6_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
+
+int
+receive_pim6_bootstrap(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ char *pim_message;
+ int datalen;
+{
+ u_int8 *data_ptr;
+ u_int8 *max_data_ptr;
+ u_int16 new_bsr_fragment_tag;
+ u_int8 new_bsr_hash_masklen;
+ u_int8 new_bsr_priority;
+ pim6_encod_uni_addr_t new_bsr_uni_addr;
+ struct sockaddr_in6 new_bsr_address;
+ struct rpfctl rpfc;
+ pim_nbr_entry_t *n,
+ *rpf_neighbor;
+ struct sockaddr_in6 neighbor_addr;
+ mifi_t mifi,
+ incoming = NO_VIF;
+ int min_datalen;
+ pim6_encod_grp_addr_t curr_group_addr;
+ pim6_encod_uni_addr_t curr_rp_addr;
+ u_int8 curr_rp_count;
+ u_int8 curr_frag_rp_count;
+ u_int16 reserved_short;
+ u_int16 curr_rp_holdtime;
+ u_int8 curr_rp_priority;
+ u_int8 reserved_byte;
+ struct in6_addr curr_group_mask;
+ grp_mask_t *grp_mask_ptr;
+ grp_mask_t *grp_mask_next;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+ struct sockaddr_in6 prefix_h,
+ prefix_h2,
+ group_,
+ rpp_;
+ int i;
+ struct uvif *v;
+
+
+ if ((mifi=find_vif_direct(src)) == NO_VIF)
+ {
+ /*
+ * Either a local vif or somehow received PIM_BOOTSTRAP from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_BOOTSTRAP from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_BOOTSTRAP_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap message size(%u) is"
+ " too short from %s",
+ datalen, inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ v->uv_in_pim6_bootsrap++;
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+
+ /* Parse the PIM_BOOTSTRAP message */
+ GET_HOSTSHORT(new_bsr_fragment_tag, data_ptr);
+ GET_BYTE(new_bsr_hash_masklen, data_ptr);
+ GET_BYTE(new_bsr_priority, data_ptr);
+ GET_EUADDR6(&new_bsr_uni_addr, data_ptr);
+
+ /*
+ * BSR address must be a global unicast address.
+ * [draft-ietf-pim-ipv6-01.txt sec 4.5]
+ */
+ if (IN6_IS_ADDR_MULTICAST(&new_bsr_uni_addr.unicast_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&new_bsr_uni_addr.unicast_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&new_bsr_uni_addr.unicast_addr)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_bootstrap: invalid BSR address: %s",
+ inet6_fmt(&new_bsr_uni_addr.unicast_addr));
+ return(FALSE);
+ }
+
+ new_bsr_address.sin6_addr = new_bsr_uni_addr.unicast_addr;
+ new_bsr_address.sin6_len = sizeof(new_bsr_address);
+ new_bsr_address.sin6_family = AF_INET6;
+ new_bsr_address.sin6_scope_id = inet6_uvif2scopeid(&new_bsr_address, v);
+
+ if (local_address(&new_bsr_address) != NO_VIF)
+ {
+ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
+ log(LOG_DEBUG, 0,
+ "receive_pim6_bootstrap: Bootstrap from myself(%s), ignored.",
+ inet6_fmt(&new_bsr_address.sin6_addr));
+ return (FALSE); /* The new BSR is one of my local addresses */
+ }
+
+ /*
+ * Compare the current BSR priority with the priority of the BSR included
+ * in the message.
+ */
+ /*
+ * TODO: if I am just starting and will become the BSR, I should accept
+ * the message coming from the current BSR and get the current
+ * Cand-RP-Set.
+ */
+
+ if ((curr_bsr_priority > new_bsr_priority) ||
+ ((curr_bsr_priority == new_bsr_priority)
+ && (inet6_greaterthan(&curr_bsr_address, &new_bsr_address))))
+ {
+ /* The message's BSR is less preferred than the current BSR */
+ log(LOG_DEBUG, 0,
+ "receive_pim6_bootstrap: BSR(%s, prio=%d) is less preferred"
+ " than the current BSR(%s, prio=%d)",
+ inet6_fmt(&new_bsr_address.sin6_addr), new_bsr_priority,
+ inet6_fmt(&curr_bsr_address.sin6_addr), curr_bsr_priority);
+ return (FALSE); /* Ignore the received BSR message */
+ }
+
+ /* Check the iif, if this was PIM-ROUTERS multicast */
+ if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &allpim6routers_group.sin6_addr))
+ {
+ k_req_incoming(&new_bsr_address, &rpfc);
+ if ((rpfc.iif == NO_VIF) ||
+ IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ {
+ /* coudn't find a route to the BSR */
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: can't find a route to the BSR(%s)",
+ inet6_fmt(&new_bsr_address.sin6_addr));
+ return (FALSE);
+ }
+
+ neighbor_addr = *src;
+ incoming = rpfc.iif;
+
+ if (uvifs[incoming].uv_flags &
+ (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from an invalid interface(%s)",
+ uvifs[incoming].uv_name);
+ return (FALSE); /* Shoudn't arrive on that interface */
+ }
+
+ /* Find the upstream router */
+
+ for (n = uvifs[incoming].uv_pim_neighbors; n != NULL; n = n->next)
+ {
+ if (inet6_lessthan(&neighbor_addr, &n->address))
+ continue;
+ if (inet6_equal(&neighbor_addr, &n->address))
+ {
+ rpf_neighbor = n;
+ break;
+ }
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from an unrecognized "
+ "neighbor(%s) on %s",
+ inet6_fmt(&neighbor_addr.sin6_addr), uvifs[incoming].uv_name);
+ return (FALSE); /* No neighbor toward BSR found */
+ }
+
+ /* redundant checks? */
+ if ((n == (pim_nbr_entry_t *) NULL ))
+ {
+ return (FALSE); /* Sender of this message is not the RPF*/
+ }
+ /* neighbor */
+ if(!(inet6_equal(&n->address, src)))
+ {
+ return (FALSE);
+ }
+ }
+ else
+ {
+ if (local_address(dst) == NO_VIF)
+ /*
+ * TODO: XXX: this situation should be handled earlier: The
+ * destination is neither ALL_PIM_ROUTERS nor me
+ */
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap with an invalid dst(%s)",
+ inet6_fmt(&dst->sin6_addr));
+ return (FALSE);
+
+ /* Probably unicasted from the current DR */
+ if (cand_rp_list != (cand_rp_t *) NULL)
+ {
+ /*
+ * Hmmm, I do have a Cand-RP-list, but some neighbor has a
+ * different opinion and is unicasting it to me. Ignore this guy.
+ */
+ log(LOG_INFO, 0,
+ "receive_pim6_bootstrap: Bootstrap received but we already "
+ "have RPs. ignored.");
+ return (FALSE);
+ }
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER))
+ continue;
+ if (inet6_equal(&uvifs[mifi].uv_linklocal->pa_addr,dst))
+ {
+ incoming = mifi;
+ break;
+ }
+ }
+ if (incoming == NO_VIF)
+ {
+ /* Cannot find the receiving iif toward that DR */
+ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
+ log(LOG_DEBUG, 0,
+ "Unicast boostrap message from %s to %s ignored: "
+ "cannot find iif",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(&dst->sin6_addr));
+ return (FALSE);
+ }
+ /*
+ * TODO: check the sender is directly connected and I am really the
+ * DR.
+ */
+ }
+
+ if (cand_rp_flag == TRUE)
+ {
+ /* If change in the BSR address, send immediately Cand-RP-Adv */
+ /* TODO: use some random delay? */
+
+ if (!inet6_equal(&new_bsr_address , &curr_bsr_address))
+ {
+ send_pim6_cand_rp_adv();
+ SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
+ }
+ }
+
+ /* Forward the BSR Message first and then update the RP-set list */
+ /* XXX: should we do sanity checks before forwarding?? */
+ /* TODO: if the message was unicasted to me, resend? */
+
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (mifi == incoming)
+ continue;
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER | VIFF_TUNNEL | VIFF_NONBRS))
+ continue;
+
+ bcopy(pim_message, (char *)(pim6_send_buf), datalen);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_BOOTSTRAP,
+ datalen - sizeof(struct pim));
+ }
+
+ max_data_ptr = (u_int8 *) pim_message + datalen;
+
+ /*
+ * TODO: XXX: this 24 is HARDCODING!!! Do a bunch of definitions and make
+ * it stylish!
+ * 24 = Encoded-Group Address(20) + RP-cound(1) + Frag-RP(1) + Reserved(2)
+ */
+ min_datalen = 24;
+
+ if ((new_bsr_fragment_tag != curr_bsr_fragment_tag) ||
+ (inet6_equal(&new_bsr_address, &curr_bsr_address)))
+ {
+ /* Throw away the old segment */
+ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
+ }
+
+ curr_bsr_address = new_bsr_address;
+ curr_bsr_priority = new_bsr_priority;
+ curr_bsr_fragment_tag = new_bsr_fragment_tag;
+ MASKLEN_TO_MASK6(new_bsr_hash_masklen, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+
+ while (data_ptr + min_datalen <= max_data_ptr)
+ {
+ GET_EGADDR6(&curr_group_addr, data_ptr);
+ GET_BYTE(curr_rp_count, data_ptr);
+ GET_BYTE(curr_frag_rp_count, data_ptr);
+ GET_HOSTSHORT(reserved_short, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ if (curr_rp_count == 0)
+ {
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ delete_grp_mask(&cand_rp_list, &grp_mask_list,
+ &group_, curr_group_mask);
+ continue;
+ }
+ if (curr_rp_count == curr_frag_rp_count)
+ {
+ /* Add all RPs */
+ while (curr_frag_rp_count--)
+ {
+ /*
+ * Sanity for the data length; the data packet must contain
+ * Encoded-Unicast-RP-Address(18) + RP-Holdtime(2) +
+ * RP-Priority(1) + Reserved(1).
+ */
+ if (data_ptr + PIM6_ENCODE_UNI_ADDR_LEN + sizeof(u_int32_t)
+ > max_data_ptr) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from %s on %s "
+ "does not have enough length to contatin RP information",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+
+ /*
+ * Ignore the rest of the message.
+ * XXX: should we discard the entire message?
+ */
+ goto garbage_collect;
+ }
+
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ rpp_.sin6_len = sizeof(rpp_);
+ rpp_.sin6_family = AF_INET6;
+ /*
+ * The cand_rp address scope should be global.
+ * XXX: however, is a site-local RP sometimes useful?
+ * we currently discard such RP...
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&rpp_.sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&rpp_.sin6_addr)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_bootstrap: invalid RP address: %s",
+ inet6_fmt(&rpp_.sin6_addr));
+ continue;
+ }
+ rpp_.sin6_scope_id = 0;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_len = sizeof(group_);
+ group_.sin6_family = AF_INET6;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, curr_rp_priority,
+ curr_rp_holdtime, &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ continue;
+ }
+ /*
+ * This is a partial list of the RPs for this group prefix. Save
+ * until all segments arrive.
+ */
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ prefix_h.sin6_addr.s6_addr[i] =
+ curr_group_addr.mcast_addr.s6_addr[i]
+ & curr_group_mask.s6_addr[i];
+ }
+
+ for (grp_mask_ptr = segmented_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ prefix_h2.sin6_addr.s6_addr[i] =
+ grp_mask_ptr->group_addr.sin6_addr.s6_addr[i]
+ & grp_mask_ptr->group_mask.s6_addr[i];
+ }
+
+ if (inet6_lessthan(&prefix_h2, &prefix_h))
+ continue;
+ else
+ break;
+ }
+ if ((grp_mask_ptr != (grp_mask_t *) NULL)
+ && (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_addr.sin6_addr,
+ &curr_group_addr.mcast_addr))
+ && (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_mask, &curr_group_mask))
+ && (grp_mask_ptr->group_rp_number + curr_frag_rp_count
+ == curr_rp_count))
+ {
+ /* All missing PRs have arrived. Add all RP entries */
+ while (curr_frag_rp_count--)
+ {
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ rpp_.sin6_scope_id=0;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &rpp_,
+ curr_rp_priority,
+ curr_rp_holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ /* Add the rest from the previously saved segments */
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &grp_rp_entry_ptr->rp->rpentry->address,
+ grp_rp_entry_ptr->priority,
+ grp_rp_entry_ptr->holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ delete_grp_mask(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ &group_,
+ curr_group_mask);
+ }
+ else
+ {
+ /* Add the partially received RP-list to the group of pending RPs */
+ while (curr_frag_rp_count--)
+ {
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ &rpp_,
+ curr_rp_priority,
+ curr_rp_holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ }
+ }
+
+
+ garbage_collect:
+ /*
+ * Garbage collection. Check all group prefixes and if the fragment_tag
+ * for a group_prefix is the same as curr_bsr_fragment_tag, then remove
+ * all RPs for this group_prefix which have different fragment tag.
+ */
+
+ for (grp_mask_ptr = grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
+ {
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
+ delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ grp_rp_entry_ptr);
+ }
+ }
+ }
+
+ /* Cleanup also the list used by incompleted segments */
+
+ for (grp_mask_ptr = segmented_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
+ {
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
+ delete_rp_grp_entry(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ grp_rp_entry_ptr);
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+void
+send_pim6_bootstrap()
+{
+ int datalen;
+ mifi_t mifi;
+
+ if ((datalen = create_pim6_bootstrap_message(pim6_send_buf)))
+ {
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER | VIFF_TUNNEL))
+ continue;
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_BOOTSTRAP, datalen);
+ uvifs[mifi].uv_out_pim6_bootsrap++;
+ }
+ }
+}
+
+/************************************************************************
+ * PIM_CAND_RP_ADV
+ ************************************************************************/
+/*
+ * minimum length of a cand. RP adv. message;
+ * length of PIM header + prefix-cnt(1) + priority(1) + holdtime(2) +
+ * encoded unicast RP addr(18)
+ */
+#define PIM6_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
+
+/*
+ * If I am the Bootstrap router, process the advertisement, otherwise ignore
+ * it.
+ */
+int
+receive_pim6_cand_rp_adv(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ char *pim_message;
+ register int datalen;
+{
+ u_int8 prefix_cnt;
+ u_int8 priority;
+ u_int16 holdtime;
+ pim6_encod_uni_addr_t cand_rp_addr;
+ pim6_encod_grp_addr_t encod_grp_addr;
+ u_int8 *data_ptr;
+ struct in6_addr grp_mask;
+ struct sockaddr_in6 group_, rpp_;
+
+ pim6dstat.in_pim6_cand_rp++;
+
+ /* if I am not the bootstrap RP, then do not accept the message */
+ if ((cand_bsr_flag != FALSE) &&
+ !inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: receive cand_RP from %s "
+ "but I'm not the BSR",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_CAND_RP_ADV_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: cand_RP message size(%u) is"
+ " too short from %s",
+ datalen, inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+ datalen -= PIM6_CAND_RP_ADV_MINLEN;
+
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+ /* Parse the CAND_RP_ADV message */
+ GET_BYTE(prefix_cnt, data_ptr);
+ GET_BYTE(priority, data_ptr);
+ GET_HOSTSHORT(holdtime, data_ptr);
+ GET_EUADDR6(&cand_rp_addr, data_ptr);
+
+ /*
+ * The RP Address field is set to the globally reachable IPv6 address
+ * [draft-ietf-pim-ipv6].
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&cand_rp_addr.unicast_addr)) {
+ /* XXX: prohibit a site-local address as well? */
+ log(LOG_WARNING, 0,
+ "receive_pim6_cand_rp_adv: non global address(%s) as RP",
+ inet6_fmt(&cand_rp_addr.unicast_addr));
+ return(FALSE);
+ }
+
+ memset(&rpp_, 0, sizeof(rpp_));
+ if (prefix_cnt == 0)
+ {
+ /* The default ff:: and masklen of 8 */
+ MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
+ rpp_.sin6_addr = cand_rp_addr.unicast_addr;
+ /*
+ * note that we don't have to take care of scope id, since
+ * the address should be global(see above).
+ */
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, priority, holdtime,
+ &sockaddr6_d, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ return (TRUE);
+ }
+ while (prefix_cnt--)
+ {
+ /*
+ * Sanity check for the message length.
+ * XXX: do we have to do the check at an earlier stage and
+ * discard the whole message (instead of adopting a part of it)
+ * if it's bogus?
+ */
+ if (datalen < PIM6_ENCODE_GRP_ADDR_LEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: cand_RP message from %s is"
+ " too short to contain enough groups",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+ datalen -= PIM6_ENCODE_GRP_ADDR_LEN;
+
+ GET_EGADDR6(&encod_grp_addr, data_ptr);
+ MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
+ group_.sin6_addr = encod_grp_addr.mcast_addr;
+ group_.sin6_scope_id = 0; /* XXX: what if for scoped multicast addr? */
+ rpp_.sin6_addr = cand_rp_addr.unicast_addr;
+ /* see above note on scope id */
+
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, priority, holdtime,
+ &group_, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+
+ return (TRUE);
+}
+
+int
+send_pim6_cand_rp_adv()
+{
+ u_int8 prefix_cnt;
+ struct in6_addr grp_mask;
+ pim6_encod_grp_addr_t encod_grp_addr;
+ u_int8 *data_ptr;
+ struct sockaddr_in6 group_;
+
+ if (!inet6_valid_host(&curr_bsr_address))
+ return (FALSE); /* No BSR yet */
+
+ if( inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ /* I am the BSR and have to include my own group_prefix stuff */
+ prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr;
+ if (prefix_cnt == 0)
+ {
+ /* The default ff00:: and masklen of 8 */
+ MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &my_cand_rp_address, my_cand_rp_priority,
+ my_cand_rp_holdtime,
+ &sockaddr6_d,
+ grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ return (TRUE);
+ }
+ /* TODO: hardcoding!! */
+ /* 18 = sizeof(pim6_encod_uni_addr_t) without padding */
+ data_ptr = cand_rp_adv_message.buffer + (4 + 18);
+
+ while (prefix_cnt--)
+ {
+ GET_EGADDR6(&encod_grp_addr, data_ptr);
+ MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
+ group_.sin6_addr = encod_grp_addr.mcast_addr;
+ group_.sin6_scope_id = 0; /*XXX */
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &my_cand_rp_address, my_cand_rp_priority,
+ my_cand_rp_holdtime,
+ &group_, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ return (TRUE);
+ }
+
+ data_ptr = (u_int8 *) (pim6_send_buf + sizeof(struct pim));
+
+ bcopy((char *)cand_rp_adv_message.buffer, (char *) data_ptr,
+ cand_rp_adv_message.message_size);
+
+ send_pim6(pim6_send_buf, &my_cand_rp_address, &curr_bsr_address ,
+ PIM_CAND_RP_ADV, cand_rp_adv_message.message_size);
+ pim6dstat.out_pim6_cand_rp++;
+
+ return TRUE;
+}
diff --git a/usr.sbin/pim6sd/pim6_proto.h b/usr.sbin/pim6sd/pim6_proto.h
new file mode 100644
index 0000000..a02e3d3
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6_proto.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/* Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIM6_PROTO_H
+#define PIM6_PROTO_H
+#include "defs.h"
+#include "vif.h"
+#include "mrt.h"
+
+extern build_jp_message_t *build_jp_message_pool;
+extern int build_jp_message_pool_counter;
+extern struct sockaddr_in6 sockaddr6_any;
+extern struct sockaddr_in6 sockaddr6_d;
+
+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_register __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_null_register __P((mrtentry_t *r));
+extern int receive_pim6_register_stop __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message,
+ int datalen));
+extern int send_pim6_register __P((char *pkt));
+extern int receive_pim6_join_prune __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int join_or_prune __P((mrtentry_t *mrtentry_ptr,
+ pim_nbr_entry_t *upstream_router));
+extern int receive_pim6_assert __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_assert __P((struct sockaddr_in6 *source, struct sockaddr_in6 *group,
+ vifi_t vifi,
+ mrtentry_t *mrtentry_ptr));
+extern int send_periodic_pim6_join_prune __P((vifi_t vifi,
+ pim_nbr_entry_t *pim_nbr,
+ u_int16 holdtime));
+extern int add_jp_entry __P((pim_nbr_entry_t *pim_nbr,
+ u_int16 holdtime, struct sockaddr_in6 *group,
+ u_int8 grp_msklen, struct sockaddr_in6 *source,
+ u_int8 src_msklen,
+ u_int16 addr_flags,
+ u_int8 join_prune));
+extern void pack_and_send_jp6_message __P((pim_nbr_entry_t *pim_nbr));
+extern int receive_pim6_cand_rp_adv __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int receive_pim6_bootstrap __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_cand_rp_adv __P(());
+extern void send_pim6_bootstrap __P(());
+
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6sd.8 b/usr.sbin/pim6sd/pim6sd.8
new file mode 100644
index 0000000..5417ef8
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.8
@@ -0,0 +1,149 @@
+.\" 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.
+.\"
+.\" $Id: pim6sd.8,v 1.4 1999/12/16 05:38:06 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 10, 1999
+.Dt PIM6SD 8
+.Os KAME
+.Sh NAME
+.Nm pim6sd
+.Nd PIM for IPv6 sparse mode daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d Op debug_level Op ,debug_level
+.Op Fl f
+.Sh DESCRIPTION
+.Nm Pim6sd
+is an IPv6 multicast routing daemon, which supports
+PIMv2(Protocol Independent Multicast Version 2) sparse mode
+for IPv6.
+.Pp
+Options supported by
+.Nm pim6sd :
+.Bl -tag -width Ds
+.It Fl c Ar configfile
+Specify alternate location,
+.Ar configfile ,
+for configuration file.
+By default,
+.Pa /usr/local/v6/etc/pim6sd.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 mld_proto, mld_timer, mld_member, mld, switch, trace, mtrace, traceroute,
+.Ic timeout, callout, pkt, packets, interfaces, vif, kernel, cache, mfc,
+.Ic k_cache, k_mfc, rsrr, pim_detail, pim_hello, pim_neighbors, pim_register,
+.Ic registers, pim_join_prune, pim_j_p, pim_jp, pim_bootstrap, pim_bsr, bsr,
+.Ic bootstrap, pim_asserts, pim_cand_rp, pim_c_rp, pim_rp, rp, pim_routes,
+.Ic pim_routing, pim_mrt, pim_timers, pim_rpf, rpf, pim, routes, routing,
+.Ic mrt, routers, mrouters, neighbors, timers,
+and
+.Ic asserts.
+.It Fl f
+Do not become daemon, run in foreground. This option is for debugging
+use.
+.El
+.Pp
+.Nm Pim6sd
+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/pim6sd.conf
+(or an alternative file, specified by the "\-c" option).
+.Pp
+The
+.Nm
+program dumps its current routing information to a dump file when
+it receives a SIGUSR1 signal.
+The information includes a list of PIM neighbors,
+.Nm
+internal multicast routing table, and
+BSR and RP related information. Also, the program dumps its internal
+statistics to a file when it receives a SIGINFO signal.
+.Pp
+When
+.Nm
+receives a SIGUSR2 signal, it rereads the configuration file and
+reset its debug level.
+.Pp
+The
+.Nm
+program puts its logs to a separate file
+.Pa (/var/log/pim6sd.log).
+The log level can be configured by the
+.Fl d
+command line option or the configuration file.
+.\"
+.Sh FILES
+.Bl -tag -width /usr/local/v6/etc/pim6sd.conf -compact
+.It Pa /usr/local/v6/etc/pim6sd.conf
+The default configuration file.
+.It Pa /var/run/pim6sd.dump
+The file to which
+.Nm
+dumps its internal status.
+.It Pa /var/run/pim6sd.stat
+The file to which
+.Nm
+dumps its internal statistics.
+.It Pa /var/log/pim6sd.log
+The pim6sd specific log file.
+.El
+.Sh SEE ALSO
+.Xr daemon 3 ,
+.Xr pim6sd.conf 5
+.Sh HISTORY
+The
+.Nm
+command is developed by Mickael Hoerdt at LSIIT Laboratory.
+It is based on IPv4 PIM sparse-mode
+.Nm pimd
+developed at University of Southern California,
+which 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 Pim6sd
+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/pim6sd/pim6sd.conf.5 b/usr.sbin/pim6sd/pim6sd.conf.5
new file mode 100644
index 0000000..ea27c06
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.conf.5
@@ -0,0 +1,330 @@
+.\" 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.
+.\"
+.\" $Id: pim6sd.conf.5,v 1.7 1999/12/03 07:31:45 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd Oct 6, 1999
+.Dt PIM6SD.CONF 5
+.Os KAME
+.Sh NAME
+.Nm pim6sd.conf
+.Nd config file for pim6sd, PIM-SM daemon for IPv6
+.\"
+.Sh DESCRIPTION
+The
+.Nm pim6sd
+configuration file consists of a sequence of statements terminated
+by a semi-colon (`;'), each of which specifies how the daemon treats
+each interface on the system, specifies some parameters of the PIM
+protocol, and requires some special behavior defined by the protocol.
+.Pp
+Each statement can be constructed by multiple lines.
+.Pp
+Lines beginning with
+.Ql #
+are comments.
+.\".Pp
+.\"Note that
+.\".Nm pim6sd
+.\"works even without the configuration file, although the daemon
+.\"will warn that there is no configuration file.
+.\"In such a case, the daemon will automatically set the default value
+.\"to each configurable parameter.
+.\"
+.Pp
+The following statements can be specified in the configuration file.
+.Pp
+.Bl -tag -width Ds -compact
+.It Xo
+.Ic log
+.Ar option...
+.Ic ;
+.Xc
+Specify debug messages to be printed out. Each
+.Ar option
+usually specifies a subset of the messages to be printed.
+If an
+.Ar option
+begins with
+.Ic no ,
+it means that the set of the messages that are specified by the option
+will not be printed. For example,
+.Ic `all nomld'
+means that all the messages except MLD related ones will be printed.
+Valid options are
+.Ic mld_proto, mld_timer, mld_member, mld, switch, trace, mtrace, traceroute,
+.Ic timeout, callout, pkt, packets, interfaces, vif, kernel, cache, mfc,
+.Ic k_cache, k_mfc, rsrr, pim_detail, pim_hello, pim_neighbors, pim_register,
+.Ic registers, pim_join_prune, pim_j_p, pim_jp, pim_bootstrap, pim_bsr, bsr,
+.Ic bootstrap, pim_asserts, pim_cand_rp, pim_c_rp, pim_rp, rp, pim_routes,
+.Ic pim_routing, pim_mrt, pim_timers, pim_rpf, rpf, pim, routes, routing,
+.Ic mrt, routers, mrouters, neighbors, timers, asserts,
+and
+.Ic all .
+.\"
+.It Xo
+.Ic reverselookup (yes \(ba no);
+.Xc
+Specifies if a hostname for an IPv6 address should be resolved
+on logging.
+.Ic yes
+means a hostname should be resolved, and
+.Ic no
+means should not.
+By default, a hostname is not resolved.
+.\"
+.It Xo
+.Ic phyint Ar interface
+.Op disable
+.Ic ;
+.Xc
+Specifies
+.Nm
+to ignore the interface even if the interface is multicast-capable.
+Note that PIM will be activated on all interfaces by default(including
+the case where there is no configuration file).
+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
+.Op nolistener
+.Ic ;
+.Xc
+Specifies the preference and/or metric values when sending a PIM
+assert message on the interface.
+If another optional parameter
+.Ic nolistener
+is specified,
+.Nm pim6sd
+will not send any MLD packets on the interface.
+This option is usually meaningless but will be useful when
+MLD messages are noisy (e.g. when debugging) and there is surely no
+listner on the interface.
+.\"
+.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 pim6sd
+cannot reliably obtain preferences and metrics from the
+unicast routing protocols, so a default value may be configured.
+The default preference is 1024.
+.\"
+.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 granularity Ar second;
+.Xc
+Specifies timer granularity in seconds.
+The default value is 5.
+.\"
+.It Xo
+.Ic hello_period Ar period Ar coef;
+.Xc
+.Ar Period
+specifies the period in second between 2 hello messages.
+.Ar Coef
+is the coefficient to determine the hello holdtime;
+the holdtime will be
+.Ar period
+*
+.Ar coef .
+The default values of the period and the coefficient are 30 and 3.5,
+respectively. The default holdtime is 105 seconds as a result.
+.\"
+.It Xo
+.Ic join_prune_period Ar period Ar coef;
+.Xc
+.Ar Period
+specifies the period in second between 2 join/prune messages.
+.Ar Coef
+is the coefficient to determine the join/prune holdtime;
+the holdtime will be
+.Ar period
+*
+.Ar coef .
+The default values of the period and the coefficient are 60 and 3.5,
+respectively. Consequently, the default holdtime is 210 seconds.
+.\"
+.It Xo
+.Ic data_timeout Ar timer;
+.Xc
+Specifies the time after which (S,G) state for a silent source will be
+deleted.
+The default value is 210.
+.\"
+.It Xo
+.Ic register_suppression_timeout Ar interval;
+.Xc
+.Ar Interval
+specifies the interval between receiving a Register-Stop and allowing
+PIM Register to be send again.
+The default value is 60.
+.\"
+.It Xo
+.Ic probe_time Ar timer;
+.Xc
+.Ar Timer
+specifies the time between sending a null Register and the
+Register-Suppression-Timer expiring unless it is restarted by
+receiving a Register-Stop.
+The default value is 5.
+.\"
+.It Xo
+.Ic assert_timeout Ar interval;
+.Xc
+.Ar Interval
+specifies the interval between the last time an Assert is received and
+the time at which the assert is timeout.
+The default value is 180.
+.\"
+.It Xo
+.Ic cand_rp
+.Op Ar interface
+.Op Ic time Ar time
+.Op Ic priority Ar priority
+.Ic ;
+.Xc
+Specifies to act as a candidate Rendezvous Point(RP).
+It is recommended to specify
+.Ic cand_rp
+only in typical usage.
+All other parameters are optional and will be set automatically.
+If an
+.Ar interface
+is specified,
+.Nm pim6sd
+will search for a global address on the specified interface
+and set the address in Candidate RP Advertisements.
+An optional parameter
+.Ic time
+specifies the interval of two succeeding advertisements in seconds.
+Its default value is 60.
+2.5 *
+.Ar time
+will be set to Candidate-RP-Advertisement messages.
+Another optional parameter
+.Ic priority
+specifies the priority of the RP.
+The default value is 0, which means the highest priority.
+.\"
+.It Xo
+.Ic group_prefix Ar prefix;
+.Xc
+When acting as a Rendezvous Point(RP),
+.Ar prefix
+specifies a group prefix that the RP will handle.
+.\"
+.It Xo
+.Ic cand_bootstrap_router
+.Op Ar interface
+.Op Ic time Ar time
+.Op Ic priority Ar priority
+.Ic ;
+.Xc
+Specifies to act as a candidate bootstrap router(BSR).
+It is recommended to specify
+.Ic cand_bootstrap_router
+only in typical usage.
+All other parameters are optional and will be set automatically.
+If an
+.Ar interface
+is specified,
+.Nm pim6sd
+will search for a global address on the specified interface
+and set the address in Bootstrap messages.
+An optional parameter
+.Ic time
+specifies the interval of two succeeding bootstraps in seconds.
+Its default value is 60.
+Another optional parameter
+.Ic priority
+specifies the priority of the RP.
+The default value is 0, which means the lowest priority.
+.\"
+.It Xo
+.Ic switch_register_threshold Ic rate Ar rate Ic interval Ar interval;
+.Xc
+Specifies the threshold that a Rendezvous Point(RP) switches to a shortest
+path tree, which is valid only when acting as an RP.
+.Ic rate
+specifies the threshold in bits per second, and
+.Ic interval
+specifies the interval of checking the rate in seconds.
+The default values are 50000 and 20, respectively.
+\"
+.It Xo
+.Ic switch_data_threshold Ic rate Ar rate Ic interval Ar interval;
+.Xc
+Specifies the threshold that a last hop router switches to a shortest
+path tree.
+.Ic rate
+specifies the threshold in bits per second, and
+.Ic interval
+specifies the interval of checking the rate in seconds.
+The default values are 50000 and 20, respectively.
+.El
+.\"
+.Sh EXAMPLE
+.Bd -literal -offset
+#phyint gif0 disable;
+#phyint ep0 preference 101;
+phyint de0 disable;
+#
+#followings are for a candidate Rendezvous Point, which should usually
+#be disabled.
+cand_bootstrap_router;
+cand_rp;
+.Ed
+.Sh SEE ALSO
+.Xr pim6sd 8
+.Sh HISTORY
+The
+.Nm pim6sd
+command is developed by Mickael Hoerdt at LSIIT Laboratory.
+It is based on IPv4 PIM sparse-mode
+.Nm pimd
+developed at University of Southern California,
+which 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/pim6sd/pim6sd.conf.sample b/usr.sbin/pim6sd/pim6sd.conf.sample
new file mode 100644
index 0000000..21cf8e4
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.conf.sample
@@ -0,0 +1,107 @@
+# $FreeBSD$
+#
+#The timer granularity.
+#More this value is small,more pim6sd will be accurate
+#default if not specified : 5
+#BE SURE to have to same granularity on ALL routers,
+#otherwise....
+
+#granularity 5;
+
+#syntax : phyint <interface> <disable> <metric> [metric] <preference> [preference]
+#metric and pref are for the asserts messages
+#samples :
+
+#phyint ed1 disable;
+#phyint de0 disable;
+#phyint ed0 disable;
+#phyint gif0 disable;
+
+#---------------Protocol timer specifications---------------------------#
+#Notes : theses value are the default spec value!
+#do not touch it if you don't know what you do!!
+#you MUST change theses values according to the granularity value!
+#syntax : 'hello_period <number> <coef>'.
+# number is the period in second between 2 hello messages
+# and coef is the coef to deterimine the hello holdtime=hello_period*coef
+# default if not specified: 30 3.5
+
+#hello_period 30 3.5;
+
+#syntax : 'join_prune_period <number> <coef>'.
+# number is the period in second between 2 join/prune messages
+# and coef is the coef to deterimine the join/prune holdtime=join_prune_period*coef
+# default if not specified : 60 3.5
+
+#join_prune_period 60 3.5;
+
+#syntax : 'data_timeout <number>'.
+# number is the time after which (S,G) state for a silent source will be deleted
+# default if not specified : 210
+
+#data_timeout 210;
+
+#syntax : 'register_suppression_timeout <number>'.
+# This is the mean interval between receiving a Register-Stop and allowing
+#Register to be send again.
+# default if not specified : 60
+
+#register_suppression_timeout 60;
+
+#syntax : 'probe_time <number>'.
+#This is the time between sending a null Register and the Register-Suppression-Timer
+#expiring unless it is restarted by receiving a Register-Stop.
+#default if not specified : 5
+
+#probe_time 5;
+
+#syntax : 'assert_timeout <number>'.
+#this is the interval between the last time an Assert is received and the time at wich the
+#assert is timeout
+#default if not specified : 180
+
+#assert_timeout 180;
+
+#syntax : <cand_rp> <interface> <time> [time] <priority> [priority]
+#and time can't be < 10
+#you can just type cand_rp,
+#samples :
+#cand_rp;
+#cand_rp de0;
+#cand_rp ed0 priority 0 time 6;
+
+#syntax : <group_prefix> <multicast address>/<prefix length>
+#group_prefix ff06::15
+#default if not specified : ff00::/8
+#samples:
+#group_prefix ff1e::15/128;
+#group_prefix ff2e::/16;
+
+#syntax : <cand_bootstrap_router> <interface> <priority> [priority] <time> [time]
+#Typically, you can simply set cand_bootstrap_router for a candidate bootstrap
+#router. All other parameters are optional.
+#the bootstrap period is configurable, BUT the holdtime of a bootstrap
+#router is not in the fields of a bootstrap message : it is hardcoded
+#in the pim6sd include file!
+#So be sure to have a time < PIM_BOOSTRAP_TIMEOUT (file pimd.h )
+#cand_bootstrap_router de0 priority 15 time 5;
+
+#syntax : <switch_register_threshold> <rate> [number] <interval> [number]
+#default rate = 50000 interval = 20s
+#samples :
+#TODO : not tested
+#switch_register_threshold rate 54389 interval 45;
+#switch_register_threshold;
+
+#syntax : <switch_data_threshold> <rate> [number] <interval> [number]
+#default rate = 50000 interval = 20s
+#TODO : not tested
+#samples:
+#switch_data_threshold interval 100 rate 1000;
+
+#syntax : <default_source_metric> [number]
+
+#default_source_metric 1243;
+#syntax : <default_source_preference> [number]
+
+#default_source_preference 123 ;
diff --git a/usr.sbin/pim6sd/pim6stat b/usr.sbin/pim6sd/pim6stat
new file mode 100755
index 0000000..667ee4a
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6stat
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+# 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.
+#
+# $FreeBSD$
+
+# get options
+while getopts "df:p:sw:" option
+do
+ case $option in
+ d)
+ densemode="YES";;
+ f)
+ dumpfile="${OPTARG}";;
+ p)
+ pidfile="${OPTARG}";;
+ s)
+ statmode="YES";;
+ w)
+ waittime="${OPTARG}";;
+ *) # (error msg printed by getopts)
+ echo usage: pim6stat [-d][-f dumpfile][-p pidfile][-w waitsec]
+ exit 2;;
+ esac
+done
+
+# set parameters
+if [ X"${pidfile}" = X ]; then
+ if [ X"${densemode}" = X"YES" ]; then
+ pidfile=/var/run/pim6dd.pid
+ else
+ pidfile=/var/run/pim6sd.pid
+ fi
+fi
+
+if [ X"${waittime}" = X ]; then
+ waittime=1
+fi
+
+if [ X"${statmode}" = X"YES" ]; then
+ signame=-INFO
+else
+ signame=-USR1
+fi
+
+if [ X"${dumpfile}" = X ]; then
+ if [ X"${statmode}" = X"YES" ]; then
+ if [ X"${densemode}" = X"YES" ]; then
+ dumpfile=/var/run/pim6dd.stat
+ else
+ dumpfile=/var/run/pim6sd.stat
+ fi
+ else
+ if [ X"${densemode}" = X"YES" ]; then
+ dumpfile=/var/run/pim6dd.dump
+ else
+ dumpfile=/var/run/pim6sd.dump
+ fi
+ fi
+fi
+
+# execution
+kill ${signame} `cat ${pidfile}`
+sleep ${waittime}
+cat ${dumpfile}
diff --git a/usr.sbin/pim6sd/pim6stat.1 b/usr.sbin/pim6sd/pim6stat.1
new file mode 100644
index 0000000..fac61b1
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6stat.1
@@ -0,0 +1,92 @@
+.\" 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.
+.\"
+.\" $Id: pim6stat.1,v 1.3 1999/12/16 05:38:06 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd July 28, 1999
+.Dt PIM6STAT 1
+.Os KAME
+.Sh NAME
+.Nm pim6stat
+.Nd show PIM for IPv6 status
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f dumpfile
+.Op Fl p pidfile
+.Op Fl s
+.Op Fl w waitsec
+.Sh DESCRIPTION
+.Nm Pim6stat
+shows status or statistics of the PIM for IPv6 daemon currently running.
+When invoked, it sends a signal to let the daemon dump its internal
+status to a file, waits for dumping, and outputs the content of the file
+to standard output.
+.Pp
+Options supported by
+.Nm pim6stat :
+.Bl -tag -width Ds
+.It Fl d
+specifies to show the status of a dense mode daemon (if running).
+By default,
+.Nm
+assumes sparse mode.
+.It Fl f
+specifies the dumpfile to which the PIM daemon dumps its status.
+.It Fl p
+specifies the PID file of the currently running daemon.
+.It Fl s
+specifies to dump statistics instead of status (for sparse mode only).
+.It Fl w
+specifies the wait period in seconds between sending a signal to the
+daemon and outputs the dumpfile.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/pim6sd.pid -compact
+.It Pa /var/run/pim6sd.pid
+The default PID file for a sparse mode daemon.
+.It Pa /var/run/pim6dd.pid
+The default PID file for a dense mode daemon.
+.It Pa /var/run/pim6sd.dump
+The default dump file for a sparse mode daemon.
+.It Pa /var/run/pim6dd.dump
+The default dump file for a dense mode daemon.
+.It Pa /var/run/pim6sd.stat
+The default statistics dump file for a sparse mode daemon.
+.El
+.Sh SEE ALSO
+.Xr pim6sd 8 ,
+.Xr pim6dd 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in KAME IPv6 protocol stack kit.
+.Sh BUGS
+.Nm Pim6stat
+needs superuser privilege.
+.\"
diff --git a/usr.sbin/pim6sd/pimd.h b/usr.sbin/pim6sd/pimd.h
new file mode 100644
index 0000000..5ad8347
--- /dev/null
+++ b/usr.sbin/pim6sd/pimd.h
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/* Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIMD_H
+#define PIMD_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "defs.h"
+
+#define PIM_PROTOCOL_VERSION 2
+
+/* PIM protocol timers (in seconds) */
+#define PIM_REGISTER_SUPPRESSION_TIMEOUT 60
+#define PIM_REGISTER_PROBE_TIME 5 /* Used to send NULL_REGISTER */
+#define PIM_DATA_TIMEOUT 210
+
+#define PIM_TIMER_HELLO_PERIOD 30
+#define PIM_JOIN_PRUNE_PERIOD 60
+
+#define PIM_JOIN_PRUNE_HOLDTIME (3.5 * PIM_JOIN_PRUNE_PERIOD)
+#define PIM_RANDOM_DELAY_JOIN_TIMEOUT 4.5
+
+#define PIM_DEFAULT_CAND_RP_ADV_PERIOD 60
+#define PIM_DEFAULT_BOOTSTRAP_PERIOD 60
+
+#define PIM_BOOTSTRAP_TIMEOUT (2.5 * PIM_DEFAULT_BOOTSTRAP_PERIOD + 10)
+#define PIM_TIMER_HELLO_HOLDTIME (3.5 * PIM_TIMER_HELLO_PERIOD)
+#define PIM_ASSERT_TIMEOUT 180
+
+
+/* Misc definitions */
+#define PIM_DEFAULT_CAND_RP_PRIORITY 0 /* 0 is the highest. Don't know
+ * why this is the default.
+ * See the PS version (Mar' 97),
+ * pp.22 bottom of the spec.
+ */
+
+#define PIM_DEFAULT_BSR_PRIORITY 0 /* 0 is the lowest */
+#define RP_DEFAULT_IPV6_HASHMASKLEN 126 /* the default group msklen used
+ * by the hash function to
+ * calculate the group-to-RP
+ * mapping
+ */
+
+#define SINGLE_SRC_MSK6LEN 128 /* the single source mask length */
+#define SINGLE_GRP_MSK6LEN 128 /* the single group mask length */
+
+/* TODO: change? */
+#define PIM_GROUP_PREFIX_DEFAULT_MASKLEN 8 /* The default group masklen if
+ * omitted in the config file.
+ */
+
+/* Datarate related definitions */
+/* REG_RATE is used by the RP to switch to the shortest path instead of
+ * decapsulating Registers.
+ * DATA_RATE is the threshold for the last hop router to initiate
+ * switching to the shortest path.
+ */
+/* TODO: XXX: probably no need for two different intervals.
+ */
+
+#define PIM_DEFAULT_REG_RATE 50000 /* max # of register bits/s */
+#define PIM_DEFAULT_REG_RATE_INTERVAL 20 /* regrate probe interval */
+#define PIM_DEFAULT_DATA_RATE 50000 /* max # of data bits/s */
+#define PIM_DEFAULT_DATA_RATE_INTERVAL 20 /* datarate check interval */
+
+#define DATA_RATE_CHECK_INTERVAL 20 /* Data rate check interval */
+#define REG_RATE_CHECK_INTERVAL 20 /* PIM Reg. rate check interval*/
+
+#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_REG_RATE_LIMIT 0 /* default register_vif rate limit */
+
+/**************************************************************************
+ * 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_IPv6 0
+
+
+/* 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;
+/* XXX: sizeof(pim6_encod_uni_addr_t) does not work due to misalignment */
+#define PIM6_ENCODE_UNI_ADDR_LEN 18
+
+/* 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;
+/* XXX: sizeof(pim6_encod_grp_addr_t) MAY NOT work due to an alignment problem */
+#define PIM6_ENCODE_GRP_ADDR_LEN 20
+
+/* 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;
+/* XXX: sizeof(pim6_encod_src_addr_t) MAY NOT work due to an alignment problem */
+#define PIM6_ENCODE_SRC_ADDR_LEN 20
+
+#define USADDR_RP_BIT 0x1
+#define USADDR_WC_BIT 0x2
+#define USADDR_S_BIT 0x4
+
+/**************************************************************************
+ * PIM Messages formats *
+ *************************************************************************/
+
+/* 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;
+
+/* PIM Register */
+typedef struct pim_register_ {
+ u_int32 reg_flags;
+} pim_register_t;
+
+/* PIM Register-Stop */
+typedef struct pim_register_stop_ {
+ pim6_encod_grp_addr_t encod_grp;
+ pim6_encod_uni_addr_t encod_src; /* XXX: 18 bytes long, misaligned */
+} pim_register_stop_t;
+
+/* PIM Join/Prune: XXX: all 128-bit addresses misaligned! */
+typedef struct pim_jp_header_ {
+ pim6_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_ {
+ pim6_encod_grp_addr_t encod_grp;
+ u_int16 number_join_src;
+ u_int16 number_prune_src;
+} pim_jp_encod_grp_t;
+
+
+#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
+#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
+
+
+/* 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
+
+/* PIM_REGISTER definitions */
+#define PIM_MESSAGE_REGISTER_BORDER_BIT 0x80000000
+#define PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT 0x40000000
+
+#define MASK_TO_MASKLEN6(mask , masklen) \
+do { \
+ register u_int32 tmp_mask; \
+ register u_int8 tmp_masklen = sizeof((mask)) <<3; \
+ int i; \
+ int kl; \
+ for(i=0;i<4;i++) \
+ { \
+ tmp_mask=ntohl(*(u_int32_t *)&mask.s6_addr[i * 4]); \
+ for(kl=32; tmp_masklen >0 && kl>0 ; tmp_masklen--, kl-- , tmp_mask >>=1) \
+ if( tmp_mask & 0x1) \
+ break; \
+ } \
+ (masklen) =tmp_masklen; \
+ } 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_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_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_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_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_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_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)
+
+/* Used if no relaible unicast routing information available */
+#define UCAST_DEFAULT_SOURCE_METRIC 1024
+#define UCAST_DEFAULT_SOURCE_PREFERENCE 1024
+
+
+#define DEFAULT_LOCAL_PREF 101 /* assert pref par defaut */
+#define DEFAULT_LOCAL_METRIC 1024 /* assert metrique par default */
+
+
+/*
+ * TODO: recalculate the messages sizes, probably with regard to the MTU
+ * TODO: cleanup
+ */
+
+#define MAX_JP_MESSAGE_SIZE 8192
+#define MAX_JP_MESSAGE_POOL_NUMBER 8
+#define MAX_JOIN_LIST_SIZE 1500
+#define MAX_PRUNE_LIST_SIZE 1500
+
+#define STAR_STAR_RP_MSK6LEN 8 /* Masklen for
+ * ff00 ::
+ * to encode (*,*,RP)
+ */
+
+/* interface independent statistics */
+struct pim6dstat {
+ /* incoming PIM6 packets on this interface */
+ u_quad_t in_pim6_register;
+ u_quad_t in_pim6_register_stop;
+ u_quad_t in_pim6_cand_rp;
+ u_quad_t in_pim6_graft; /* for dense mode only */
+ u_quad_t in_pim6_graft_ack; /* for dense mode only */
+ /* outgoing PIM6 packets on this interface */
+ u_quad_t out_pim6_register;
+ u_quad_t out_pim6_register_stop;
+ u_quad_t out_pim6_cand_rp;
+ /* SPT transition */
+ u_quad_t pim6_trans_spt_forward;
+ u_quad_t pim6_trans_spt_rp;
+ /* occurrences of timeouts */
+ u_quad_t pim6_bootstrap_timo;/* pim_bootstrap_timer */
+ u_quad_t pim6_rpgrp_timo; /* rp_grp_entry_ptr->holdtime */
+ u_quad_t pim6_rtentry_timo; /* routing entry */
+ /* kernel internals */
+ u_quad_t kern_add_cache;
+ u_quad_t kern_add_cache_fail;
+ u_quad_t kern_del_cache;
+ u_quad_t kern_del_cache_fail;
+ u_quad_t kern_sgcnt_fail;
+};
+
+extern struct pim6dstat pim6dstat;
+#endif
diff --git a/usr.sbin/pim6sd/route.c b/usr.sbin/pim6sd/route.c
new file mode 100644
index 0000000..c455a58
--- /dev/null
+++ b/usr.sbin/pim6sd/route.c
@@ -0,0 +1,1180 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "vif.h"
+#include "mrt.h"
+#include "debug.h"
+#include "pim6_proto.h"
+#include "route.h"
+#include "mld6.h"
+#include "rp.h"
+#include "kern.h"
+#include "timer.h"
+#include "inet6.h"
+#include <netinet6/ip6_mroute.h>
+#include <netinet/ip6.h>
+#include "routesock.h"
+
+static void process_cache_miss __P((struct mrt6msg * im));
+static void process_wrong_iif __P((struct mrt6msg * im));
+static void process_whole_pkt __P((char *buf));
+
+u_int32 default_source_metric = UCAST_DEFAULT_SOURCE_METRIC;
+u_int32 default_source_preference = UCAST_DEFAULT_SOURCE_PREFERENCE;
+
+
+/* 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!!! Yes Really for Ipv6!!
+ */
+/*
+ * TODO: XXX: change the metric and preference for all (S,G) entries per
+ * source or RP?
+ */
+/*
+ * 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. If srcentry==PIM_IIF_RP, then "upstream" in
+ * case of directly connected "source" will be that "source" (if it is also
+ * PIM router).,
+ */
+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->metric = 0;
+ srcentry_ptr->preference = 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)
+ {
+ /*
+ * The source is directly connected. Check whether we are looking for
+ * real source or RP
+ */
+
+ if (srctype == PIM_IIF_SOURCE)
+ {
+ srcentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ return (TRUE);
+ }
+ else
+ {
+ /* PIM_IIF_RP */
+ neighbor_addr = source;
+ }
+ }
+ else
+ {
+ /* 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;
+ /* 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 ;-).
+ * Yes but the neighbors are link-local and the rp is global ipv6..
+ */
+/* WARNING WARNING WARNING WARNING */
+/* If the router is directly connected to the RP and the RP is the BSR , the next hop is
+ * the globally reachable addresse of the RP : NOT link local neighbor but
+ * a ipv6 global neighbor...
+ * the upstream router is the globally reachable router...
+ *
+ */
+/* WARNING WARNING WARNING WARNING */
+
+ v = &uvifs[srcentry_ptr->incoming];
+ if (inet6_equal(&source,&neighbor_addr))
+ {
+ srcentry_ptr->upstream=v->uv_pim_neighbors;
+ return (TRUE);
+ }
+
+ 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);
+}
+
+
+/*
+ * 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;
+{
+ mrtentry_t *mrtentry_ptr;
+ if_set old_oifs;
+ if_set new_oifs;
+ if_set new_leaves;
+
+
+ mrtentry_ptr = find_route(&sockaddr6_any, group, MRTF_WC, CREATE);
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if ((mrtentry_ptr->incoming == vifi)
+ && (!(uvifs[vifi].uv_flags & VIFF_DR)))
+ {
+ /*
+ * The report is received on the iif for this routing entry and I am
+ * not the DR for that subnet. Ignore it.
+ */
+
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ delete_mrtentry(mrtentry_ptr);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi,
+ inet6_fmt(&group->sin6_addr));
+
+ if (IF_ISSET(vifi, &mrtentry_ptr->leaves))
+ return; /* Already a leaf */
+ calc_oifs(mrtentry_ptr, &old_oifs);
+ IF_COPY(&mrtentry_ptr->leaves, &new_leaves);
+ IF_SET(vifi, &new_leaves); /* Add the leaf */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &new_leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ calc_oifs(mrtentry_ptr, &new_oifs);
+
+ if ((mrtentry_ptr->flags & MRTF_NEW)
+ || (IF_ISEMPTY(&old_oifs) && (!IF_ISEMPTY(&new_oifs))))
+ {
+
+ /*
+ * A new created entry or the oifs have changed from NULL to
+ * non-NULL.
+ */
+
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune
+ * timer */
+ /*
+ * TODO: explicitly call the function below?
+ * send_pim6_join_prune(mrtentry_ptr->upstream->vifi,
+ * mrtentry_ptr->upstream, pim_join_prune_holdtime);
+ */
+ }
+}
+
+
+/*
+ * 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;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ if_set new_oifs;
+ if_set old_oifs;
+ if_set new_leaves;
+
+ mrtentry_ptr = find_route(&sockaddr6_any, group, MRTF_WC, DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if (!IF_ISSET(vifi, &mrtentry_ptr->leaves))
+ return; /* This interface wasn't leaf */
+
+ calc_oifs(mrtentry_ptr, &old_oifs);
+ IF_COPY(&mrtentry_ptr->leaves, &new_leaves);
+ IF_CLR(vifi, &new_leaves);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &new_leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ calc_oifs(mrtentry_ptr, &new_oifs);
+ if ((!IF_ISEMPTY(&old_oifs)) && IF_ISEMPTY(&new_oifs))
+ {
+ /* The result oifs have changed from non-NULL to NULL */
+ FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune
+ * timer */
+ /*
+ * TODO: explicitly call the function?
+ * send_pim6_join_prune(mrtentry_ptr->upstream->vifi,
+ * mrtentry_ptr->upstream, pim_join_prune_holdtime);
+ */
+ }
+ /*
+ * Check all (S,G) entries and clear the inherited "leaf" flag. TODO:
+ * XXX: This won't work for IGMPv3, because there we don't know whether
+ * the (S,G) leaf oif was inherited from the (*,G) entry or was created
+ * by source specific IGMP join.
+ */
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
+ IF_CLR(vifi, &new_leaves);
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &new_leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+
+}
+
+
+void
+calc_oifs(mrtentry_ptr, oifs_ptr)
+ mrtentry_t *mrtentry_ptr;
+ if_set *oifs_ptr;
+{
+ if_set oifs;
+ mrtentry_t *grp_route;
+ mrtentry_t *rp_route;
+
+ /*
+ * oifs = (((copied_outgoing + my_join) - my_prune) + my_leaves) -
+ * my_asserted_oifs - incoming_interface, i.e. `leaves` have higher
+ * priority than `prunes`, but lower priority than `asserted`. The
+ * incoming interface is always deleted from the oifs
+ */
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ IF_ZERO(oifs_ptr);
+ return;
+ }
+ IF_ZERO(&oifs);
+ if (!(mrtentry_ptr->flags & MRTF_PMBR))
+ {
+ /* Either (*,G) or (S,G). Merge with the oifs from the (*,*,RP) */
+ if ((rp_route =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink)
+ != (mrtentry_t *) NULL)
+ {
+ IF_MERGE(&oifs, &rp_route->joined_oifs, &oifs);
+ IF_CLR_MASK(&oifs, &rp_route->pruned_oifs);
+ IF_MERGE(&oifs, &rp_route->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &rp_route->asserted_oifs);
+ }
+ }
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry. Merge with the oifs from (*,G) */
+ if ((grp_route = mrtentry_ptr->group->grp_route)
+ != (mrtentry_t *) NULL)
+ {
+ IF_MERGE(&oifs, &grp_route->joined_oifs, &oifs);
+ IF_CLR_MASK(&oifs, &grp_route->pruned_oifs);
+ IF_MERGE(&oifs, &grp_route->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &grp_route->asserted_oifs);
+ }
+ }
+
+ /* Calculate my own stuff */
+ IF_MERGE(&oifs, &mrtentry_ptr->joined_oifs, &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(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_joined_oifs_, new_pruned_oifs,
+ new_leaves_, new_asserted_oifs, flags)
+ mrtentry_t *mrtentry_ptr;
+ vifi_t new_iif;
+ if_set *new_joined_oifs_;
+ if_set *new_pruned_oifs;
+ if_set *new_leaves_;
+ if_set *new_asserted_oifs;
+ u_int16 flags;
+{
+ if_set new_joined_oifs; /* The oifs for that particular
+ * mrtentry */
+ if_set old_joined_oifs;
+ if_set old_pruned_oifs;
+ if_set old_leaves;
+ if_set new_leaves;
+ if_set old_asserted_oifs;
+ if_set new_real_oifs; /* The result oifs */
+ if_set old_real_oifs;
+ vifi_t old_iif;
+ rpentry_t *rpentry_ptr;
+ cand_rp_t *cand_rp_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_wc;
+ mrtentry_t *mrtentry_rp;
+ int delete_mrtentry_flag;
+ int return_value;
+ int fire_timer_flag;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (0);
+
+ IF_COPY(new_joined_oifs_, &new_joined_oifs);
+ IF_COPY(new_leaves_, &new_leaves);
+
+ old_iif = mrtentry_ptr->incoming;
+ IF_COPY(&mrtentry_ptr->joined_oifs, &old_joined_oifs);
+ 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_joined_oifs, &mrtentry_ptr->joined_oifs);
+ 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)
+ && !(flags & MFC_UPDATE_FORCE))
+ return 0; /* Nothing to change */
+
+ if ((return_value != 0) || (new_iif != old_iif)
+ || (flags & MFC_UPDATE_FORCE))
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+
+ IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+ rpentry_ptr = mrtentry_ptr->source;
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ return (0); /* Shoudn't happen */
+ rpentry_ptr->incoming = new_iif;
+ cand_rp_ptr = rpentry_ptr->cand_rp;
+
+ if (IF_ISEMPTY(&new_real_oifs))
+ {
+ delete_mrtentry_flag = TRUE;
+ }
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ /* Update the kernel MFC entries */
+ if (delete_mrtentry_flag == TRUE)
+ /*
+ * XXX: no need to send RSRR message. Will do it when delete
+ * the mrtentry.
+ */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ /* here mrtentry_ptr->source->address is the RP address */
+ k_chg_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group, new_iif,
+ &new_real_oifs, &mrtentry_ptr->source->address);
+ }
+ }
+
+ /*
+ * Update all (*,G) entries associated with this RP. The particular
+ * (*,G) outgoing are not changed, but the change in the (*,*,RP)
+ * oifs may have affect the real oifs.
+ */
+ fire_timer_flag = FALSE;
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ {
+ if (change_interfaces(grpentry_ptr->grp_route, new_iif,
+ &grpentry_ptr->grp_route->joined_oifs,
+ &grpentry_ptr->grp_route->pruned_oifs,
+ &grpentry_ptr->grp_route->leaves,
+ &grpentry_ptr->grp_route->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ /* Change all (S,G) entries if no (*,G) */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ if (change_interfaces(mrtentry_srcs, new_iif,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ if (change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ }
+ }
+ }
+ }
+ if (fire_timer_flag == TRUE)
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /*
+ * TODO: XXX: trigger a Prune message? Don't delete now, it will
+ * be automatically timed out. If want to delete now, don't
+ * reference to it anymore! delete_mrtentry(mrtentry_ptr);
+ */
+ }
+ return (return_value); /* (*,*,RP) */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+ if (IF_ISEMPTY(&new_real_oifs))
+ delete_mrtentry_flag = TRUE;
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ if (delete_mrtentry_flag == TRUE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ k_chg_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group, new_iif,
+ &new_real_oifs, &mrtentry_ptr->group->rpaddr);
+ }
+ }
+ /*
+ * Update all (S,G) entries for this group. For the (S,G)RPbit
+ * entries the iif is the iif toward the RP; The particular (S,G)
+ * oifs are not changed, but the change in the (*,G) oifs may affect
+ * the real oifs.
+ */
+ fire_timer_flag = FALSE;
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ if (change_interfaces(mrtentry_srcs, new_iif,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ if (change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, flags))
+ fire_timer_flag = TRUE;
+ }
+ }
+
+ if (fire_timer_flag == TRUE)
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /* TODO: XXX: the oifs are NULL. Send a Prune message? */
+ }
+ return (return_value); /* (*,G) */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry */
+#ifdef KERNEL_MFC_WC_G
+ if_set tmp_oifs;
+ mrtentry_t *mrtentry_tmp;
+#endif /* KERNEL_MFC_WC_G */
+
+ mrtentry_rp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ mrtentry_wc = mrtentry_ptr->group->grp_route;
+#ifdef KERNEL_MFC_WC_G
+ /*
+ * Check whether (*,*,RP) or (*,G) have different (iif,oifs) from the
+ * (S,G). If "yes", then forbid creating (*,G) MFC.
+ */
+ for (mrtentry_tmp = mrtentry_rp; 1; mrtentry_tmp = mrtentry_wc)
+ {
+ for (; 1;)
+ {
+ if (mrtentry_tmp == (mrtentry_t *) NULL)
+ break;
+ if (mrtentry_tmp->flags & MRTF_MFC_CLONE_SG)
+ break;
+ if (mrtentry_tmp->incoming != mrtentry_ptr->incoming)
+ {
+ delete_single_kernel_cache_addr(mrtentry_tmp, IN6ADDR_ANY_N,
+ mrtentry_ptr->group->group);
+ mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
+ break;
+ }
+ calc_oifs(mrtentry_tmp, &tmp_oifs);
+ if (!(IF_SAME(&new_real_oifs, &tmp_oifs)))
+ mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
+ break;
+ }
+ if (mrtentry_tmp == mrtentry_wc)
+ break;
+ }
+#endif /* KERNEL_MFC_WC_G */
+
+ if (IF_ISEMPTY(&new_real_oifs))
+ delete_mrtentry_flag = TRUE;
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ if (delete_mrtentry_flag == TRUE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
+ &mrtentry_ptr->group->group, new_iif, &new_real_oifs,
+ &mrtentry_ptr->group->rpaddr);
+ }
+ }
+ if (old_iif != new_iif)
+ {
+ if (new_iif == mrtentry_ptr->source->incoming)
+ {
+ /*
+ * For example, if this was (S,G)RPbit with iif toward the
+ * RP, and now switch to the Shortest Path. The setup of
+ * MRTF_SPT flag must be done by the external calling
+ * function (triggered only by receiving of a data from the
+ * source.)
+ */
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ /*
+ * TODO: XXX: delete? Check again where will be the best
+ * place to set it. mrtentry_ptr->flags |= MRTF_SPT;
+ */
+ }
+ if (((mrtentry_wc != (mrtentry_t *) NULL)
+ && (mrtentry_wc->incoming == new_iif))
+ || ((mrtentry_rp != (mrtentry_t *) NULL)
+ && (mrtentry_rp->incoming == new_iif)))
+ {
+ /*
+ * If the new iif points toward the RP, reset the SPT flag.
+ * (PIM-SM-spec-10.ps pp. 11, 2.10, last sentence of first
+ * paragraph.
+ */
+ /* TODO: XXX: check again! */
+
+ mrtentry_ptr->flags &= ~MRTF_SPT;
+ mrtentry_ptr->flags |= MRTF_RP;
+ }
+ }
+ /*
+ * TODO: XXX: if this is (S,G)RPbit entry and the oifs==(*,G)oifs,
+ * then delete the (S,G) entry?? The same if we have (*,*,RP) ?
+ */
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /* TODO: XXX: the oifs are NULL. Send a Prune message ? */
+ }
+ /* TODO: XXX: have the feeling something is missing.... */
+ return (return_value); /* (S,G) */
+ }
+ 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;
+}
+
+
+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;
+ case MRT6MSG_WHOLEPKT:
+ process_whole_pkt(mld6_recv_buf);
+ break;
+ default:
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Unknown kernel_call code");
+ break;
+ }
+}
+
+
+/*
+ * TODO: when cache miss, check the iif, because probably ASSERTS shoult take
+ * place
+ */
+
+static void
+process_cache_miss(im)
+ struct mrt6msg *im;
+{
+ static struct sockaddr_in6 source = {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 mfc_source = {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 group = {sizeof(group) , AF_INET6 };
+
+ static struct sockaddr_in6 rp_addr = {sizeof(source) , AF_INET6 };
+ vifi_t iif;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_rp;
+
+ /*
+ * 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;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]);
+ source.sin6_addr = mfc_source.sin6_addr = im->im6_src;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]);
+ iif = im->im6_mif;
+
+ uvifs[iif].uv_cache_miss++;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s, iif %d",
+ inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr), iif);
+
+ /*
+ * TODO: XXX: check whether the kernel generates cache miss for the LAN
+ * scoped addresses
+ */
+
+ /* 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))
+ goto fail;
+
+ /* TODO: check if correct in case the source is one of my addresses */
+ /*
+ * If I am the DR for this source, create (S,G) and add the register_vif
+ * to the oifs.
+ */
+ if ((uvifs[iif].uv_flags & VIFF_DR)
+ && (find_vif_direct_local(&source) == iif))
+ {
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ goto fail;
+ }
+
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+
+ /* set reg_vif_num as outgoing interface ONLY if I am not the RP */
+
+ if (!inet6_equal(&mrtentry_ptr->group->rpaddr, &my_cand_rp_address))
+ IF_SET(reg_vif_num, &mrtentry_ptr->joined_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ else
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ goto fail;
+ }
+
+ /*
+ * TODO: if there are too many cache miss for the same (S,G), install
+ * negative cache entry in the kernel (oif==NULL) to prevent too many
+ * upcalls.
+ */
+ if (mrtentry_ptr->incoming == iif)
+ {
+ if (!IF_ISEMPTY(&mrtentry_ptr->oifs))
+ {
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* TODO: check that the RPbit is not set? */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_ptr->timer < pim_data_timeout)
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ /*
+ * Check if the (S,G) iif is different from the (*,G)
+ * or (*,*,RP) iif
+ */
+ if ((mrtentry_ptr->incoming != mrtentry_rp->incoming)
+ || (mrtentry_ptr->upstream != mrtentry_rp->upstream))
+ {
+ mrtentry_ptr->flags |= MRTF_SPT;
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ }
+ }
+ }
+ }
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ rp_addr = mrtentry_ptr->source->address;
+ else
+ rp_addr = mrtentry_ptr->group->rpaddr;
+ mfc_source = source;
+// TODO
+#ifdef KERNEL_MFC_WC_G
+ if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = IN6ADDR_ANY_N;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_ptr, &mfc_source, &group, MFC_MOVE_FORCE);
+ k_chg_mfc(mld6_socket, &mfc_source, &group, iif, &mrtentry_ptr->oifs,
+ &rp_addr);
+ /*
+ * TODO: XXX: No need for RSRR message, because nothing has
+ * changed.
+ */
+ }
+ return; /* iif match */
+ }
+
+ /* The iif doesn't match */
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ if (mrtentry_ptr->flags & MRTF_SPT)
+ /* Arrived on wrong interface */
+ goto fail;
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ if (mrtentry_rp->incoming == iif)
+ {
+ /* Forward on (*,G) or (*,*,RP) */
+
+#ifdef KERNEL_MFC_WC_G
+ if (!(mrtentry_rp->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = IN6ADDR_ANY_N;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_rp, &mfc_source, &group, 0);
+ k_chg_mfc(mld6_socket, &mfc_source, &group, iif,
+ &mrtentry_rp->oifs, &mrtentry_ptr->group->rpaddr);
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_rp, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+
+ return;
+ }
+ }
+ goto fail;
+ }
+
+ fail:
+ uvifs[iif].uv_cache_notcreated++;
+}
+
+
+/*
+ * A multicast packet has been received on wrong iif by the kernel. Check for
+ * a matching entry. If there is (S,G) with reset SPTbit and the packet was
+ * received on the iif toward the source, this completes the switch to the
+ * shortest path and triggers (S,G) prune toward the RP (unless I am the RP).
+ * Otherwise, if the packet's iif is in the oiflist of the routing entry,
+ * trigger an Assert.
+ */
+
+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};
+ vifi_t iif;
+ mrtentry_t *mrtentry_ptr;
+
+ group.sin6_addr = im->im6_dst;
+ source.sin6_addr = im->im6_src;
+ iif = im->im6_mif;
+
+
+ /* 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;
+
+
+ /*
+ * Ignore if it comes on register vif. register vif is neither SPT iif,
+ * neither is used to send asserts out.
+ */
+ if (uvifs[iif].uv_flags & MIFF_REGISTER)
+ return;
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ /*
+ * TODO: check again!
+ */
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ if (mrtentry_ptr->source->incoming == iif)
+ {
+ /* Switch to the Shortest Path */
+ mrtentry_ptr->flags |= MRTF_SPT;
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ add_kernel_cache(mrtentry_ptr, &source, &group, MFC_MOVE_FORCE);
+ k_chg_mfc(mld6_socket, &source, &group, iif,
+ &mrtentry_ptr->oifs, &mrtentry_ptr->group->rpaddr);
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ return;
+ }
+ }
+ }
+
+ /* Trigger an Assert */
+ if (IF_ISSET(iif, &mrtentry_ptr->oifs))
+ send_pim6_assert(&source, &group, iif, mrtentry_ptr);
+}
+
+/*
+ * Receives whole packets from the register vif entries in the kernel, and
+ * calls the send_pim_register procedure to encapsulate the packets and
+ * unicasts them to the RP.
+ */
+static void
+process_whole_pkt(buf)
+ char *buf;
+{
+
+ send_pim6_register((char *) (buf + sizeof(struct mrt6msg)));
+
+}
+
+
+mrtentry_t *
+switch_shortest_path(source, group)
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ mrtentry_t *mrtentry_ptr;
+
+ /* TODO: XXX: prepare and send immediately the (S,G) join? */
+ if ((mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE)) !=
+ (mrtentry_t *) NULL)
+ {
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ {
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ }
+ else
+ {
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /*
+ * (S,G)RPbit with iif toward RP. Reset to (S,G) with iif
+ * toward S. Delete the kernel cache (if any), because
+ * change_interfaces() will reset it with iif toward S and no
+ * data will arrive from RP before the switch really occurs.
+ */
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ mrtentry_ptr->incoming = mrtentry_ptr->source->incoming;
+ mrtentry_ptr->upstream = mrtentry_ptr->source->upstream;
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ }
+ return (mrtentry_ptr);
+}
diff --git a/usr.sbin/pim6sd/route.h b/usr.sbin/pim6sd/route.h
new file mode 100644
index 0000000..2adb1fe
--- /dev/null
+++ b/usr.sbin/pim6sd/route.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef ROUTE_H
+#define ROUTE_H
+
+#include "mrt.h"
+
+extern u_int32 default_source_preference;
+extern u_int32 default_source_metric;
+
+int change_interfaces( mrtentry_t *mrtentry_ptr,vifi_t new_iif,
+ if_set *new_joined_oifs,if_set *new_pruned_oifs,if_set *new_leaves_ , if_set *asserted ,
+ u_int16 flags);
+
+extern void process_kernel_call __P(());
+extern int set_incoming __P((srcentry_t *srcentry_ptr,
+ int srctype));
+extern vifi_t get_iif __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 pim_nbr_entry_t *find_pim6_nbr __P((struct sockaddr_in6 *source));
+extern void calc_oifs __P((mrtentry_t *mrtentry_ptr,
+ if_set *oifs_ptr));
+extern void process_kernel_call __P(());
+extern int delete_vif_from_mrt __P((vifi_t vifi));
+extern mrtentry_t *switch_shortest_path __P((struct sockaddr_in6 *source, struct sockaddr_in6 *group));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/routesock.c b/usr.sbin/pim6sd/routesock.c
new file mode 100644
index 0000000..aced736
--- /dev/null
+++ b/usr.sbin/pim6sd/routesock.c
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "vif.h"
+#include "debug.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;
+ }
+// TODO : UTILITY ?
+#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;
+#if 0
+ default:
+ /*
+ * There are some defined flags other than above 4,
+ * but we are not interested in them.
+ */
+ log(LOG_WARNING, 0,
+ "Routesock.c (getmsg) unknown flag : %d",i);
+#endif
+ }
+ 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/pim6sd/routesock.h b/usr.sbin/pim6sd/routesock.h
new file mode 100644
index 0000000..0d3f9f7
--- /dev/null
+++ b/usr.sbin/pim6sd/routesock.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef ROUTESOCK_H
+#define ROUTESOCK_H
+
+void init_routesock();
+extern int pid;
+extern int k_req_incoming __P((struct sockaddr_in6 *source,
+ struct rpfctl *rpfp));
+
+#endif
diff --git a/usr.sbin/pim6sd/rp.c b/usr.sbin/pim6sd/rp.c
new file mode 100644
index 0000000..48936e4
--- /dev/null
+++ b/usr.sbin/pim6sd/rp.c
@@ -0,0 +1,1210 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include "rp.h"
+#include "pim6_proto.h"
+#include "pimd.h"
+#include <netinet6/pim6.h>
+#include "timer.h"
+#include "inet6.h"
+#include "route.h"
+#include "pimd.h"
+#include "debug.h"
+#include "crc.h"
+
+/*
+ * The hash function. Stollen from Eddy's (eddy@isi.edu) implementation (for
+ * compatibility ;)
+ */
+
+#define SEED1 1103515245
+#define SEED2 12345
+#define RP_HASH_VALUE(G, M, C) (((SEED1) * (((SEED1) * ((G) & (M)) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000)
+#define RP_HASH_VALUE2(P, C) (((SEED1) * (((SEED1) * (P) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000)
+
+cand_rp_t *cand_rp_list;
+grp_mask_t *grp_mask_list;
+cand_rp_t *segmented_cand_rp_list;
+grp_mask_t *segmented_grp_mask_list;
+u_int16 curr_bsr_fragment_tag;
+u_int8 curr_bsr_priority;
+struct sockaddr_in6 curr_bsr_address;
+struct in6_addr curr_bsr_hash_mask;
+u_int16 pim_bootstrap_timer; /* For electing the BSR and sending
+ * Cand-RP-set msgs */
+u_int8 my_bsr_priority;
+struct sockaddr_in6 my_bsr_address;
+struct in6_addr my_bsr_hash_mask;
+u_int8 cand_bsr_flag = FALSE; /* Set to TRUE if I am a candidate
+ * BSR */
+struct sockaddr_in6 my_cand_rp_address;
+u_int8 my_cand_rp_priority;
+u_int16 my_cand_rp_holdtime;
+u_int16 my_cand_rp_adv_period; /* The locally configured Cand-RP
+ * adv. period. */
+u_int16 my_bsr_period; /* The locally configured BSR
+ period */
+u_int16 pim_cand_rp_adv_timer;
+u_int8 cand_rp_flag = FALSE; /* Candidate RP flag */
+struct cand_rp_adv_message_ cand_rp_adv_message;
+struct in6_addr rp_my_ipv6_hashmask;
+
+
+/*
+ * Local functions definition.
+ */
+static cand_rp_t *add_cand_rp __P((cand_rp_t **used_cand_rp_list ,
+ struct sockaddr_in6 *address));
+
+static grp_mask_t *add_grp_mask __P((grp_mask_t ** used_grp_mask_list,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask,
+ struct in6_addr hash_mask));
+
+static void delete_grp_mask_entry __P((cand_rp_t ** used_cand_rp_list,
+ grp_mask_t ** used_grp_mask_list,
+ grp_mask_t * grp_mask_delete));
+
+static void delete_rp_entry __P((cand_rp_t ** used_cand_rp_list,
+ grp_mask_t ** used_grp_mask_list,
+ cand_rp_t * cand_rp_ptr));
+
+
+void
+init_rp6_and_bsr6()
+{
+ /* TODO: if the grplist is not NULL, remap all groups ASAP! */
+
+ delete_rp_list(&cand_rp_list, &grp_mask_list);
+ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
+
+ if (cand_bsr_flag == FALSE)
+ {
+ /*
+ * If I am not candidat BSR, initialize the "current BSR" as having
+ * the lowest priority.
+ */
+
+ curr_bsr_fragment_tag = 0;
+ curr_bsr_priority = 0; /* Lowest priority */
+ curr_bsr_address = sockaddr6_any; /* Lowest priority */
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+ }
+ else
+ {
+ curr_bsr_fragment_tag = RANDOM();
+ curr_bsr_priority = my_bsr_priority;
+ curr_bsr_address = my_bsr_address;
+ curr_bsr_hash_mask = my_bsr_hash_mask;
+ SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
+ }
+
+ if (cand_rp_flag != FALSE)
+ {
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, rp_my_ipv6_hashmask);
+ /* Setup the Cand-RP-Adv-Timer */
+ SET_TIMER(pim_cand_rp_adv_timer, RANDOM() % my_cand_rp_adv_period);
+ }
+}
+
+
+/*
+ * XXX: This implementation is based on section 6.2 of RFC 2362, which
+ * is highly dependent on IPv4.
+ * We'll have to rewrite the function...
+ */
+u_int16
+bootstrap_initial_delay()
+{
+// long AddrDelay;
+// long Delay;
+// long log_mask;
+// int log_of_2;
+// u_int8 bestPriority;
+
+ /*
+ * The bootstrap timer initial value (if Cand-BSR). It depends of the
+ * bootstrap router priority: higher priority has shorter value:
+ *
+ * Delay = 5 + 2*log_2(1 + bestPriority - myPriority) + AddrDelay;
+ *
+ * bestPriority = Max(storedPriority, myPriority); if (bestPriority ==
+ * myPriority) AddrDelay = log_2(bestAddr - myAddr)/16; else AddrDelay =
+ * 2 - (myAddr/2^31);
+ */
+
+// bestPriority = max(curr_bsr_priority, my_bsr_priority);
+// if (bestPriority == my_bsr_priority)
+ // {
+// AddrDelay = ntohl(curr_bsr_address) - ntohl(my_bsr_address);
+ /* Calculate the integer part of log_2 of (bestAddr - myAddr) */
+ /*
+ * To do so, have to find the position number of the first bit from
+ * left which is `1`
+ */
+// log_mask = sizeof(AddrDelay) << 3;
+// log_mask = (1 << (log_mask - 1)); /* Set the leftmost bit to
+// * `1` */
+/* for (log_of_2 = (sizeof(AddrDelay) << 3) - 1; log_of_2; log_of_2--)
+ {
+ if (AddrDelay & log_mask)
+ break;
+ else
+*/
+// log_mask >>= 1; /* Start shifting `1` on right */
+/* }
+ AddrDelay = log_of_2 / 16;
+ }
+ else
+ AddrDelay = 2 - (ntohl(my_bsr_address) / (1 << 31));
+
+ Delay = 1 + bestPriority - my_bsr_priority;
+ */
+ /* Calculate log_2(Delay) */
+// log_mask = sizeof(Delay) << 3;
+// log_mask = (1 << (log_mask - 1));
+/* Set the leftmost bit to `1`
+*/
+
+ /* for (log_of_2 = (sizeof(Delay) << 3) - 1; log_of_2; log_of_2--)
+ {
+ if (Delay & log_mask)
+ break;
+ else
+*/
+// log_mask >>= 1; /* Start shifting `1` on right */
+
+/* }
+
+ Delay = 5 + 2 * Delay + AddrDelay;
+ return (u_int16) Delay;
+*/
+
+ /* Temporary implementation */
+ return (RANDOM()%my_bsr_period);
+}
+
+
+static cand_rp_t *
+add_cand_rp(used_cand_rp_list, address)
+ cand_rp_t **used_cand_rp_list;
+ struct sockaddr_in6 *address;
+{
+ cand_rp_t *cand_rp_prev = (cand_rp_t *) NULL;
+ cand_rp_t *cand_rp;
+ cand_rp_t *cand_rp_new;
+ rpentry_t *rpentry_ptr;
+
+ /* The ordering is the bigger first */
+ for (cand_rp = *used_cand_rp_list; cand_rp != (cand_rp_t *) NULL;
+ cand_rp_prev = cand_rp, cand_rp = cand_rp->next)
+ {
+
+ if (inet6_greaterthan(&cand_rp->rpentry->address, address))
+ continue;
+ if (inet6_equal(&cand_rp->rpentry->address , address))
+ return (cand_rp);
+ else
+ break;
+ }
+
+ /* Create and insert the new entry between cand_rp_prev and cand_rp */
+ cand_rp_new = (cand_rp_t *) malloc(sizeof(cand_rp_t));
+ cand_rp_new->rp_grp_next = (rp_grp_entry_t *) NULL;
+ cand_rp_new->next = cand_rp;
+ cand_rp_new->prev = cand_rp_prev;
+ if (cand_rp != (cand_rp_t *) NULL)
+ cand_rp->prev = cand_rp_new;
+ if (cand_rp_prev == (cand_rp_t *) NULL)
+ {
+ *used_cand_rp_list = cand_rp_new;
+ }
+ else
+ {
+ cand_rp_prev->next = cand_rp_new;
+ }
+
+ rpentry_ptr = (rpentry_t *) malloc(sizeof(rpentry_t));
+ cand_rp_new->rpentry = rpentry_ptr;
+ rpentry_ptr->next = (srcentry_t *) NULL;
+ rpentry_ptr->prev = (srcentry_t *) NULL;
+ rpentry_ptr->address = *address;
+ rpentry_ptr->mrtlink = (mrtentry_t *) NULL;
+ rpentry_ptr->incoming = NO_VIF;
+ rpentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+
+ /* TODO: setup the metric and the preference as ~0 (the lowest)? */
+
+ rpentry_ptr->metric = ~0;
+ rpentry_ptr->preference = ~0;
+ RESET_TIMER(rpentry_ptr->timer);
+ rpentry_ptr->cand_rp = cand_rp_new;
+
+ /*
+ * TODO: XXX: check whether there is a route to that RP: if return value
+ * is FALSE, then no route.
+ */
+
+ if (local_address(&rpentry_ptr->address) == NO_VIF)
+ {
+ /* TODO: check for error and delete */
+ set_incoming(rpentry_ptr, PIM_IIF_RP);
+ }
+ else
+ {
+ /* TODO: XXX: CHECK!!! */
+ rpentry_ptr->incoming = reg_vif_num;
+ }
+
+ return (cand_rp_new);
+}
+
+
+static grp_mask_t *
+add_grp_mask(used_grp_mask_list, group_addr, group_mask, hash_mask)
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr hash_mask;
+{
+ grp_mask_t *grp_mask_prev = (grp_mask_t *) NULL;
+ grp_mask_t *grp_mask;
+ grp_mask_t *grp_mask_tmp;
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ /* I compare on the adresses, inet6_equal use the scope, too */
+ prefix_h.sin6_scope_id = prefix_h2.sin6_scope_id = 0;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] =
+ group_addr->sin6_addr.s6_addr[i] & group_mask.s6_addr[i];
+
+ /* The ordering is: smaller first */
+ for (grp_mask = *used_grp_mask_list; grp_mask != (grp_mask_t *) NULL;
+ grp_mask_prev = grp_mask, grp_mask = grp_mask->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ (grp_mask->group_addr.sin6_addr.s6_addr[i] &
+ grp_mask->group_mask.s6_addr[i]);
+ if (inet6_lessthan(&prefix_h2, &prefix_h) )
+ continue;
+ if (inet6_equal(&prefix_h2, &prefix_h))
+ return (grp_mask);
+ else
+ break;
+ }
+
+ grp_mask_tmp = (grp_mask_t *) malloc(sizeof(grp_mask_t));
+ grp_mask_tmp->grp_rp_next = (rp_grp_entry_t *) NULL;
+ grp_mask_tmp->next = grp_mask;
+ grp_mask_tmp->prev = grp_mask_prev;
+ if (grp_mask != (grp_mask_t *) NULL)
+ grp_mask->prev = grp_mask_tmp;
+ if (grp_mask_prev == (grp_mask_t *) NULL)
+ {
+ *used_grp_mask_list = grp_mask_tmp;
+ }
+ else
+ {
+ grp_mask_prev->next = grp_mask_tmp;
+ }
+
+ grp_mask_tmp->group_addr = *group_addr;
+ grp_mask_tmp->group_mask = group_mask;
+ grp_mask_tmp->hash_mask = hash_mask;
+ grp_mask_tmp->group_rp_number = 0;
+ grp_mask_tmp->fragment_tag = 0;
+ return (grp_mask_tmp);
+}
+
+
+/*
+ * TODO: XXX: BUG: a remapping for some groups currently using some other
+ * grp_mask may be required by the addition of the new entry!!! Remapping all
+ * groups might be a costly process...
+ */
+rp_grp_entry_t *
+add_rp_grp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_addr, rp_priority, rp_holdtime, group_addr, group_mask,
+ bsr_hash_mask,
+ fragment_tag)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *rp_addr;
+ u_int8 rp_priority;
+ u_int16 rp_holdtime;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr bsr_hash_mask;
+ u_int16 fragment_tag;
+{
+ cand_rp_t *cand_rp_ptr;
+ grp_mask_t *grp_mask_ptr;
+ rpentry_t *rpentry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+ rp_grp_entry_t *grp_rp_entry_new;
+ rp_grp_entry_t *grp_rp_entry_prev = (rp_grp_entry_t *) NULL;
+ grpentry_t *grpentry_ptr_prev;
+ grpentry_t *grpentry_ptr_next;
+ u_int8 old_highest_priority = ~0; /* Smaller value means
+ * "higher" */
+
+ /* Input data verification */
+ if (!inet6_valid_host(rp_addr))
+ return (rp_grp_entry_t *) NULL;
+
+ if (!IN6_IS_ADDR_MULTICAST(&group_addr->sin6_addr))
+ {
+ return (rp_grp_entry_t *) NULL;
+ }
+ grp_mask_ptr = add_grp_mask(used_grp_mask_list, group_addr, group_mask,
+ bsr_hash_mask);
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ /* TODO: delete */
+#if 0
+ if (grp_mask_ptr->grp_rp_next != (rp_grp_entry_t *) NULL)
+ {
+ /* Check for obsolete grp_rp chain */
+ if ((my_bsr_address != curr_bsr_address)
+ && (grp_mask_ptr->grp_rp_next->fragment_tag != fragment_tag))
+ {
+ /* This grp_rp chain is obsolete. Delete it. */
+ delete_grp_mask(used_cand_rp_list, used_grp_mask_list,
+ group_addr, group_mask);
+ grp_mask_ptr = add_grp_mask(used_grp_mask_list, group_addr,
+ group_mask, bsr_hash_mask);
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+ }
+ }
+#endif /* 0 */
+
+ cand_rp_ptr = add_cand_rp(used_cand_rp_list, rp_addr);
+ if (cand_rp_ptr == (cand_rp_t *) NULL)
+ {
+ if (grp_mask_ptr->grp_rp_next == (rp_grp_entry_t *) NULL)
+ delete_grp_mask(used_cand_rp_list, used_grp_mask_list,
+ group_addr, group_mask);
+ return (rp_grp_entry_t *) NULL;
+ }
+
+ rpentry_ptr = cand_rp_ptr->rpentry;
+ SET_TIMER(rpentry_ptr->timer, rp_holdtime);
+ grp_mask_ptr->fragment_tag = fragment_tag; /* For garbage collection */
+
+ grp_rp_entry_prev = (rp_grp_entry_t *) NULL;
+ grp_rp_entry_next = grp_mask_ptr->grp_rp_next;
+
+ /* TODO: improve it */
+
+ if (grp_rp_entry_next != (rp_grp_entry_t *) NULL)
+ old_highest_priority = grp_rp_entry_next->priority;
+ for (; grp_rp_entry_next != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_prev = grp_rp_entry_next,
+ grp_rp_entry_next = grp_rp_entry_next->grp_rp_next)
+ {
+ /*
+ * Smaller value means higher priority. The entries are sorted with
+ * the highest priority first.
+ */
+ if (grp_rp_entry_next->priority < rp_priority)
+ continue;
+ if (grp_rp_entry_next->priority > rp_priority)
+ break;
+
+ /*
+ * Here we don't care about higher/lower addresses, because higher
+ * address does not guarantee higher hash_value, but anyway we do
+ * order with the higher address first, so it will be easier to find
+ * an existing entry and update the holdtime.
+ */
+
+ if (inet6_greaterthan(&grp_rp_entry_next->rp->rpentry->address , rp_addr))
+ continue;
+ if (inet6_lessthan(&grp_rp_entry_next->rp->rpentry->address , rp_addr))
+ break;
+
+ /* We already have this entry. Update the holdtime */
+ /*
+ * TODO: We shoudn't have old existing entry, because with the
+ * current implementation all of them will be deleted (different
+ * fragment_tag). Debug and check and eventually delete.
+ */
+
+ grp_rp_entry_next->holdtime = rp_holdtime;
+ grp_rp_entry_next->advholdtime = rp_holdtime;
+ grp_rp_entry_next->fragment_tag = fragment_tag;
+ return (grp_rp_entry_next);
+ }
+
+ /* Create and link the new entry */
+
+ grp_rp_entry_new = (rp_grp_entry_t *) malloc(sizeof(rp_grp_entry_t));
+ grp_rp_entry_new->grp_rp_next = grp_rp_entry_next;
+ grp_rp_entry_new->grp_rp_prev = grp_rp_entry_prev;
+ if (grp_rp_entry_next != (rp_grp_entry_t *) NULL)
+ grp_rp_entry_next->grp_rp_prev = grp_rp_entry_new;
+ if (grp_rp_entry_prev == (rp_grp_entry_t *) NULL)
+ grp_mask_ptr->grp_rp_next = grp_rp_entry_new;
+ else
+ grp_rp_entry_prev->grp_rp_next = grp_rp_entry_new;
+
+ /*
+ * The rp_grp_entry chain is not ordered, so just plug the new entry at
+ * the head.
+ */
+
+ grp_rp_entry_new->rp_grp_next = cand_rp_ptr->rp_grp_next;
+ if (cand_rp_ptr->rp_grp_next != (rp_grp_entry_t *) NULL)
+ cand_rp_ptr->rp_grp_next->rp_grp_prev = grp_rp_entry_new;
+ grp_rp_entry_new->rp_grp_prev = (rp_grp_entry_t *) NULL;
+ cand_rp_ptr->rp_grp_next = grp_rp_entry_new;
+
+ grp_rp_entry_new->holdtime = rp_holdtime;
+ grp_rp_entry_new->advholdtime = rp_holdtime;
+ grp_rp_entry_new->fragment_tag = fragment_tag;
+ grp_rp_entry_new->priority = rp_priority;
+ grp_rp_entry_new->group = grp_mask_ptr;
+ grp_rp_entry_new->rp = cand_rp_ptr;
+ grp_rp_entry_new->grplink = (grpentry_t *) NULL;
+
+ grp_mask_ptr->group_rp_number++;
+
+ if (grp_mask_ptr->grp_rp_next->priority == rp_priority)
+ {
+ /* The first entries are with the best priority. */
+ /* Adding this rp_grp_entry may result in group_to_rp remapping */
+ for (grp_rp_entry_next = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_next != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_next = grp_rp_entry_next->grp_rp_next)
+ {
+ if (grp_rp_entry_next->priority > old_highest_priority)
+ break;
+ for (grpentry_ptr_prev = grp_rp_entry_next->grplink;
+ grpentry_ptr_prev != (grpentry_t *) NULL;)
+ {
+ grpentry_ptr_next = grpentry_ptr_prev->rpnext;
+ remap_grpentry(grpentry_ptr_prev);
+ grpentry_ptr_prev = grpentry_ptr_next;
+ }
+ }
+ }
+
+ return (grp_rp_entry_new);
+}
+
+
+void
+delete_rp_grp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ rp_grp_entry_t *rp_grp_entry_delete;
+{
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+
+ if (rp_grp_entry_delete == (rp_grp_entry_t *) NULL)
+ return;
+ rp_grp_entry_delete->group->group_rp_number--;
+ /* Free the rp_grp* and grp_rp* links */
+ if (rp_grp_entry_delete->rp_grp_prev != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->rp_grp_prev->rp_grp_next =
+ rp_grp_entry_delete->rp_grp_next;
+ else
+ rp_grp_entry_delete->rp->rp_grp_next =
+ rp_grp_entry_delete->rp_grp_next;
+ if (rp_grp_entry_delete->rp_grp_next != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->rp_grp_next->rp_grp_prev =
+ rp_grp_entry_delete->rp_grp_prev;
+
+ if (rp_grp_entry_delete->grp_rp_prev != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->grp_rp_prev->grp_rp_next =
+ rp_grp_entry_delete->grp_rp_next;
+ else
+ rp_grp_entry_delete->group->grp_rp_next =
+ rp_grp_entry_delete->grp_rp_next;
+ if (rp_grp_entry_delete->grp_rp_next != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->grp_rp_next->grp_rp_prev =
+ rp_grp_entry_delete->grp_rp_prev;
+
+ /* Delete Cand-RP or Group-prefix if useless */
+ if (rp_grp_entry_delete->group->grp_rp_next ==
+ (rp_grp_entry_t *) NULL)
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete->group);
+ if (rp_grp_entry_delete->rp->rp_grp_next ==
+ (rp_grp_entry_t *) NULL)
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete->rp);
+
+ /* Remap all affected groups */
+ for (grpentry_ptr = rp_grp_entry_delete->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+ }
+
+ free((char *) rp_grp_entry_delete);
+}
+
+/*
+ * TODO: XXX: the affected group entries will be partially setup, because may
+ * have group routing entry, but NULL pointers to RP. After the call to this
+ * function, must remap all group entries ASAP.
+ */
+
+void
+delete_rp_list(used_cand_rp_list, used_grp_mask_list)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+{
+ cand_rp_t *cand_rp_ptr,
+ *cand_rp_next;
+ grp_mask_t *grp_mask_ptr,
+ *grp_mask_next;
+ rp_grp_entry_t *rp_grp_entry_ptr,
+ *rp_grp_entry_next;
+ grpentry_t *grpentry_ptr,
+ *grpentry_ptr_next;
+
+ for (cand_rp_ptr = *used_cand_rp_list;
+ cand_rp_ptr != (cand_rp_t *) NULL;)
+ {
+ cand_rp_next = cand_rp_ptr->next;
+ /* Free the mrtentry (if any) for this RP */
+ if (cand_rp_ptr->rpentry->mrtlink != (mrtentry_t *) NULL)
+ {
+ if (cand_rp_ptr->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(cand_rp_ptr->rpentry->mrtlink);
+ FREE_MRTENTRY(cand_rp_ptr->rpentry->mrtlink);
+ }
+ free(cand_rp_ptr->rpentry);
+
+ /* Free the whole chain of rp_grp_entry for this RP */
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->rp_grp_next;
+ /* Clear the RP related invalid pointers for all group entries */
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ grpentry_ptr->rpnext = (grpentry_t *) NULL;
+ grpentry_ptr->rpprev = (grpentry_t *) NULL;
+ grpentry_ptr->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grpentry_ptr->rpaddr = sockaddr6_any;
+ }
+ free(rp_grp_entry_ptr);
+ }
+ cand_rp_ptr = cand_rp_next;
+ }
+ *used_cand_rp_list = (cand_rp_t *) NULL;
+
+ for (grp_mask_ptr = *used_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ free(grp_mask_ptr);
+ }
+ *used_grp_mask_list = (grp_mask_t *) NULL;
+}
+
+
+void
+delete_grp_mask(used_cand_rp_list, used_grp_mask_list, group_addr, group_mask)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+{
+ grp_mask_t *grp_mask_ptr;
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] = group_addr->sin6_addr.s6_addr[i]&group_mask.s6_addr[i];
+
+ for (grp_mask_ptr = *used_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ grp_mask_ptr->group_addr.sin6_addr.s6_addr[i]&grp_mask_ptr->group_mask.s6_addr[i];
+
+ if (inet6_lessthan(&prefix_h2, &prefix_h))
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_addr.sin6_addr,
+ &group_addr->sin6_addr) &&
+ IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_mask, &group_mask))
+ break;
+ else
+ return; /* Not found */
+ }
+
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return; /* Not found */
+
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ grp_mask_ptr);
+}
+
+static void
+delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list, grp_mask_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ grp_mask_t *grp_mask_delete;
+{
+ grpentry_t *grpentry_ptr,
+ *grpentry_ptr_next;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+
+ if (grp_mask_delete == (grp_mask_t *) NULL)
+ return;
+
+ /* Remove from the grp_mask_list first */
+
+ if (grp_mask_delete->prev != (grp_mask_t *) NULL)
+ grp_mask_delete->prev->next = grp_mask_delete->next;
+ else
+ *used_grp_mask_list = grp_mask_delete->next;
+
+ if (grp_mask_delete->next != (grp_mask_t *) NULL)
+ grp_mask_delete->next->prev = grp_mask_delete->prev;
+
+ /* Remove all grp_rp entries for this grp_mask */
+ for (grp_rp_entry_ptr = grp_mask_delete->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ /* Remap all related grpentry */
+ for (grpentry_ptr = grp_rp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+
+ }
+ if (grp_rp_entry_ptr->rp_grp_prev != (rp_grp_entry_t *) NULL)
+ {
+ grp_rp_entry_ptr->rp_grp_prev->rp_grp_next =
+ grp_rp_entry_ptr->rp_grp_next;
+ }
+ else
+ {
+ grp_rp_entry_ptr->rp->rp_grp_next = grp_rp_entry_ptr->rp_grp_next;
+ }
+ if (grp_rp_entry_ptr->rp_grp_next != (rp_grp_entry_t *) NULL)
+ grp_rp_entry_ptr->rp_grp_next->rp_grp_prev =
+ grp_rp_entry_ptr->rp_grp_prev;
+ if (grp_rp_entry_ptr->rp->rp_grp_next == (rp_grp_entry_t *) NULL)
+ {
+ /* Delete the RP entry */
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list,
+ grp_rp_entry_ptr->rp);
+ }
+ free(grp_rp_entry_ptr);
+ }
+}
+
+/*
+ * TODO: currently not used.
+ */
+
+void
+delete_rp(used_cand_rp_list, used_grp_mask_list, rp_addr)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *rp_addr;
+{
+ cand_rp_t *cand_rp_ptr;
+
+ for (cand_rp_ptr = *used_cand_rp_list;
+ cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if (inet6_greaterthan(&cand_rp_ptr->rpentry->address, rp_addr))
+ continue;
+ if (inet6_equal(&cand_rp_ptr->rpentry->address,rp_addr))
+ break;
+ else
+ return; /* Not found */
+ }
+
+ if (cand_rp_ptr == (cand_rp_t *) NULL)
+ return; /* Not found */
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list, cand_rp_ptr);
+}
+
+
+static void
+delete_rp_entry(used_cand_rp_list, used_grp_mask_list, cand_rp_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ cand_rp_t *cand_rp_delete;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rp_grp_entry_t *rp_grp_entry_next;
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+
+ if (cand_rp_delete == (cand_rp_t *) NULL)
+ return;
+
+ /* Remove from the cand-RP chain */
+ if (cand_rp_delete->prev != (cand_rp_t *) NULL)
+ cand_rp_delete->prev->next = cand_rp_delete->next;
+ else
+ *used_cand_rp_list = cand_rp_delete->next;
+
+ if (cand_rp_delete->next != (cand_rp_t *) NULL)
+ cand_rp_delete->next->prev = cand_rp_delete->prev;
+
+ if (cand_rp_delete->rpentry->mrtlink != (mrtentry_t *) NULL)
+ {
+ if (cand_rp_delete->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(cand_rp_delete->rpentry->mrtlink);
+ FREE_MRTENTRY(cand_rp_delete->rpentry->mrtlink);
+ }
+ free((char *) cand_rp_delete->rpentry);
+
+ /* Remove all rp_grp entries for this RP */
+ for (rp_grp_entry_ptr = cand_rp_delete->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->rp_grp_next;
+ rp_grp_entry_ptr->group->group_rp_number--;
+
+ /* First take care of the grp_rp chain */
+ if (rp_grp_entry_ptr->grp_rp_prev != (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr->grp_rp_prev->grp_rp_next =
+ rp_grp_entry_ptr->grp_rp_next;
+ }
+ else
+ {
+ rp_grp_entry_ptr->group->grp_rp_next =
+ rp_grp_entry_ptr->grp_rp_next;
+ }
+ if (rp_grp_entry_ptr->grp_rp_next != (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr->grp_rp_next->grp_rp_prev =
+ rp_grp_entry_ptr->grp_rp_prev;
+ }
+
+ if (rp_grp_entry_ptr->grp_rp_next == (rp_grp_entry_t *) NULL)
+ {
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_ptr->group);
+ }
+
+ /* Remap the related groups */
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+ }
+ free(rp_grp_entry_ptr);
+ }
+ free((char *) cand_rp_delete);
+}
+
+
+/*
+ * Rehash the RP for the group. XXX: currently, every time when
+ * remap_grpentry() is called, there has being a good reason to change the
+ * RP, so for performancy reasons no check is performed whether the RP will
+ * be really different one.
+ */
+
+int
+remap_grpentry(grpentry_ptr)
+ grpentry_t *grpentry_ptr;
+{
+ rpentry_t *rpentry_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ mrtentry_t *grp_route;
+ mrtentry_t *mrtentry_ptr;
+
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ return (FALSE);
+
+ /* Remove from the list of all groups matching to the same RP */
+ if (grpentry_ptr->rpprev != (grpentry_t *) NULL)
+ grpentry_ptr->rpprev->rpnext = grpentry_ptr->rpnext;
+ else
+ {
+ if (grpentry_ptr->active_rp_grp != (rp_grp_entry_t *) NULL)
+ grpentry_ptr->active_rp_grp->grplink = grpentry_ptr->rpnext;
+ }
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr->rpprev;
+
+ rp_grp_entry_ptr = rp_grp_match(&grpentry_ptr->group);
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ {
+ /* If cannot remap, delete the group */
+ delete_grpentry(grpentry_ptr);
+ return (FALSE);
+ }
+ rpentry_ptr = rp_grp_entry_ptr->rp->rpentry;
+
+ /* Add to the new chain of all groups mapping to the same RP */
+ grpentry_ptr->rpaddr = rpentry_ptr->address;
+ grpentry_ptr->active_rp_grp = rp_grp_entry_ptr;
+ grpentry_ptr->rpnext = rp_grp_entry_ptr->grplink;
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr;
+ grpentry_ptr->rpprev = (grpentry_t *) NULL;
+ rp_grp_entry_ptr->grplink = grpentry_ptr;
+
+ if ((grp_route = grpentry_ptr->grp_route) != (mrtentry_t *) NULL)
+ {
+ grp_route->upstream = rpentry_ptr->upstream;
+ grp_route->metric = rpentry_ptr->metric;
+ grp_route->preference = rpentry_ptr->preference;
+ change_interfaces(grp_route, rpentry_ptr->incoming,
+ &grp_route->joined_oifs,
+ &grp_route->pruned_oifs,
+ &grp_route->leaves,
+ &grp_route->asserted_oifs, MFC_UPDATE_FORCE);
+ }
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ continue;
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr, rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, MFC_UPDATE_FORCE);
+ }
+
+ return (TRUE);
+}
+
+
+rpentry_t *
+rp_match(group)
+ struct sockaddr_in6 *group;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ rp_grp_entry_ptr = rp_grp_match(group);
+ if (rp_grp_entry_ptr != (rp_grp_entry_t *) NULL)
+ return (rp_grp_entry_ptr->rp->rpentry);
+ else
+ return (rpentry_t *) NULL;
+}
+
+
+rp_grp_entry_t *
+rp_grp_match(group)
+ struct sockaddr_in6 *group;
+{
+ grp_mask_t *grp_mask_ptr;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *best_entry = (rp_grp_entry_t *) NULL;
+ u_int8 best_priority = ~0; /* Smaller is better */
+ u_int32 best_hash_value = 0; /* Bigger is better */
+ struct sockaddr_in6 best_address_h; /* Bigger is better */
+ u_int32 curr_hash_value = 0;
+ struct sockaddr_in6 curr_address_h;
+
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ if (grp_mask_list == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ /* XXX :I compare on the adresses , inet6_equal use the scope too */
+ prefix_h.sin6_scope_id = prefix_h2.sin6_scope_id = 0;
+
+ for (grp_mask_ptr = grp_mask_list; grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ (grp_mask_ptr->group_addr.sin6_addr.s6_addr[i] &
+ grp_mask_ptr->group_mask.s6_addr[i]);
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] =
+ (group->sin6_addr.s6_addr[i] &
+ grp_mask_ptr->group_mask.s6_addr[i]);
+
+ /* Search the grp_mask (group_prefix) list */
+ if ((inet6_greaterthan(&prefix_h, &prefix_h2)))
+ continue;
+ if ((inet6_lessthan(&prefix_h, &prefix_h2)))
+ break;
+
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+
+ if (best_priority < grp_rp_entry_ptr->priority)
+ break;
+
+ curr_address_h = grp_rp_entry_ptr->rp->rpentry->address;
+#if 0
+ curr_hash_value = RP_HASH_VALUE(crc((char *)&group->sin6_addr,
+ sizeof(struct in6_addr)),
+ crc((char *)&grp_mask_ptr->hash_mask,
+ sizeof(struct in6_addr)),
+ crc((char *)&curr_address_h.sin6_addr,
+ sizeof(struct in6_addr)));
+#else
+ {
+ struct in6_addr masked_grp;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ masked_grp.s6_addr[i] =
+ group->sin6_addr.s6_addr[i] &
+ grp_mask_ptr->hash_mask.s6_addr[i];
+ curr_hash_value = RP_HASH_VALUE2(crc((char *)&masked_grp,
+ sizeof(struct in6_addr)),
+ crc((char *)&curr_address_h.sin6_addr,
+ sizeof(struct in6_addr)));
+ }
+#endif
+
+ if (best_priority == grp_rp_entry_ptr->priority)
+ {
+ /* Compare the hash_value and then the addresses */
+
+ if (curr_hash_value < best_hash_value)
+ continue;
+ if (curr_hash_value == best_hash_value)
+ if (inet6_lessthan(&curr_address_h ,&best_address_h))
+ continue;
+ }
+
+ /* The current entry in the loop is preferred */
+
+ best_entry = grp_rp_entry_ptr;
+ best_priority = best_entry->priority;
+ best_address_h = curr_address_h;
+ best_hash_value = curr_hash_value;
+ }
+ }
+
+
+ if (best_entry == (rp_grp_entry_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ IF_DEBUG(DEBUG_PIM_CAND_RP)
+ log(LOG_DEBUG,0,"Rp_grp_match found %s for group %s",
+ inet6_fmt(&best_entry->rp->rpentry->address.sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ return (best_entry);
+}
+
+
+rpentry_t *
+rp_find(rp_address)
+ struct sockaddr_in6 *rp_address;
+{
+ cand_rp_t *cand_rp_ptr;
+
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if( inet6_greaterthan(&cand_rp_ptr->rpentry->address,rp_address))
+ continue;
+ if( inet6_equal(&cand_rp_ptr->rpentry->address,rp_address))
+ return (cand_rp_ptr->rpentry);
+ return (rpentry_t *) NULL;
+ }
+
+ return (rpentry_t *) NULL;
+}
+
+
+/*
+ * Create a bootstrap message in "send_buff" and returns the data size
+ * (excluding the IP header and the PIM header) Can be used both by the
+ * Bootstrap router to multicast the RP-set or by the DR to unicast it to a
+ * new neighbor. It DOES NOT change any timers.
+ */
+
+int
+create_pim6_bootstrap_message(send_buff)
+ char *send_buff;
+{
+ u_int8 *data_ptr;
+ grp_mask_t *grp_mask_ptr;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ int datalen;
+ u_int8 masklen=0;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&curr_bsr_address.sin6_addr))
+ return (0);
+
+ data_ptr = (u_int8 *) (send_buff + sizeof(struct pim));
+
+ if( inet6_equal(&curr_bsr_address , &my_bsr_address ))
+ curr_bsr_fragment_tag++;
+ PUT_HOSTSHORT(curr_bsr_fragment_tag, data_ptr);
+ MASK_TO_MASKLEN6(curr_bsr_hash_mask, masklen);
+ PUT_BYTE(masklen, data_ptr);
+ PUT_BYTE(curr_bsr_priority, data_ptr);
+ PUT_EUADDR6(curr_bsr_address.sin6_addr, data_ptr);
+
+ /* TODO: XXX: No fragmentation support (yet) */
+
+ for (grp_mask_ptr = grp_mask_list; grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ MASK_TO_MASKLEN6(grp_mask_ptr->group_mask, masklen);
+ PUT_EGADDR6(grp_mask_ptr->group_addr.sin6_addr, masklen, 0, data_ptr);
+ PUT_BYTE(grp_mask_ptr->group_rp_number, data_ptr);
+ PUT_BYTE(grp_mask_ptr->group_rp_number, data_ptr); /* TODO: if frag. */
+ PUT_HOSTSHORT(0, data_ptr);
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+ PUT_EUADDR6(grp_rp_entry_ptr->rp->rpentry->address.sin6_addr, data_ptr);
+ PUT_HOSTSHORT(grp_rp_entry_ptr->advholdtime, data_ptr);
+ PUT_BYTE(grp_rp_entry_ptr->priority, data_ptr);
+ PUT_BYTE(0, data_ptr); /* The reserved field */
+ }
+ }
+
+ datalen = (data_ptr - (u_int8 *) send_buff) - sizeof(struct pim);
+
+ return (datalen);
+}
+
+
+/*
+ * Check if the rp_addr is the RP for the group corresponding to
+ * mrtentry_ptr. Return TRUE or FALSE.
+ */
+
+int
+check_mrtentry_rp(mrtentry_ptr, rp_addr)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *rp_addr;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (FALSE);
+ if (IN6_IS_ADDR_UNSPECIFIED(&rp_addr->sin6_addr))
+ return (FALSE);
+ rp_grp_entry_ptr = mrtentry_ptr->group->active_rp_grp;
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ return (FALSE);
+ if (inet6_equal(&mrtentry_ptr->group->rpaddr,rp_addr))
+ return (TRUE);
+ return (FALSE);
+}
diff --git a/usr.sbin/pim6sd/rp.h b/usr.sbin/pim6sd/rp.h
new file mode 100644
index 0000000..56817f1
--- /dev/null
+++ b/usr.sbin/pim6sd/rp.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef RP_H
+#define RP_H
+
+#include "defs.h"
+#include "mrt.h"
+
+extern cand_rp_t *cand_rp_list;
+extern grp_mask_t *grp_mask_list;
+extern cand_rp_t *segmented_cand_rp_list;
+extern grp_mask_t *segmented_grp_mask_list;
+
+extern u_int8 cand_rp_flag;
+extern u_int8 cand_bsr_flag;
+extern u_int8 my_cand_rp_priority;
+extern u_int8 my_bsr_priority;
+extern u_int16 my_cand_rp_adv_period;
+extern u_int16 my_bsr_period;
+extern u_int16 my_cand_rp_holdtime;
+extern struct sockaddr_in6 my_cand_rp_address;
+extern struct sockaddr_in6 my_bsr_address;
+extern struct in6_addr my_bsr_hash_mask;
+extern struct in6_addr curr_bsr_hash_mask;
+extern struct sockaddr_in6 curr_bsr_address;
+extern u_int16 curr_bsr_fragment_tag;
+extern u_int8 curr_bsr_priority;
+extern u_int16 pim_bootstrap_timer;
+extern u_int16 pim_cand_rp_adv_timer;
+
+extern struct cand_rp_adv_message_ {
+ u_int8 *buffer;
+ u_int8 *insert_data_ptr;
+ u_int8 *prefix_cnt_ptr;
+ u_int16 message_size;
+} cand_rp_adv_message;
+
+
+extern void init_rp6_and_bsr6 __P(());
+void delete_rp_list( cand_rp_t **used_cand_rp_list , grp_mask_t **used_grp_mask_list );
+u_int16 bootstrap_initial_delay();
+extern rpentry_t *rp_match __P((struct sockaddr_in6 *group));
+extern rp_grp_entry_t *rp_grp_match __P((struct sockaddr_in6 *group));
+extern int create_pim6_bootstrap_message __P((char *send_buff));
+
+extern rp_grp_entry_t *add_rp_grp_entry __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *rp_addr,
+ u_int8 rp_priority,
+ u_int16 rp_holdtime,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask,
+ struct in6_addr bsr_hash_mask,
+ u_int16 fragment_tag));
+extern void delete_rp_grp_entry __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ rp_grp_entry_t *rp_grp_entry_delete));
+extern void delete_grp_mask __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask));
+extern void delete_rp __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *rp_addr));
+extern void delete_rp_list __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list));
+extern rpentry_t *rp_match __P((struct sockaddr_in6 *group));
+extern rp_grp_entry_t *rp_grp_match __P((struct sockaddr_in6 *group));
+extern rpentry_t *rp_find __P((struct sockaddr_in6 *rp_address));
+extern int remap_grpentry __P((grpentry_t *grpentry_ptr));
+extern int check_mrtentry_rp __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *rp_addr));
+
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/timer.c b/usr.sbin/pim6sd/timer.c
new file mode 100644
index 0000000..af74952
--- /dev/null
+++ b/usr.sbin/pim6sd/timer.c
@@ -0,0 +1,1286 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "mrt.h"
+#include "vif.h"
+#include <netinet6/ip6_mroute.h>
+#include "timer.h"
+#include "debug.h"
+#include "rp.h"
+#include "pim6_proto.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "kern.h"
+#include "debug.h"
+#include "inet6.h"
+
+/*
+ * Global variables
+ */
+/*
+ * XXX: The RATE is in bits/s. To include the header overhead, the
+ * approximation is 1 byte/s = 10 bits/s `whatever_bytes` is the maximum
+ * number of bytes within the test interval.
+ */
+
+u_int32 timer_interval=DEFAULT_TIMER_INTERVAL;
+u_int32 pim_reg_rate_bytes =
+(PIM_DEFAULT_REG_RATE * PIM_DEFAULT_REG_RATE_INTERVAL) / 10;
+u_int32 pim_reg_rate_check_interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+u_int32 pim_data_rate_bytes =
+(PIM_DEFAULT_DATA_RATE * PIM_DEFAULT_DATA_RATE_INTERVAL) / 10;
+u_int32 pim_data_rate_check_interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+u_int32 pim_hello_period = PIM_TIMER_HELLO_PERIOD;
+u_int32 pim_hello_holdtime = PIM_TIMER_HELLO_HOLDTIME;
+u_int32 pim_join_prune_period = PIM_JOIN_PRUNE_PERIOD;
+u_int32 pim_join_prune_holdtime = PIM_JOIN_PRUNE_HOLDTIME;
+u_int32 pim_data_timeout=PIM_DATA_TIMEOUT;
+u_int32 pim_register_suppression_timeout=PIM_REGISTER_SUPPRESSION_TIMEOUT;
+u_int32 pim_register_probe_time=PIM_REGISTER_PROBE_TIME;
+u_int32 pim_assert_timeout=PIM_ASSERT_TIMEOUT;
+
+
+/*
+ * 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 */
+
+u_int16 pim_data_rate_timer; /* Used to check periodically the
+ * datarate of the active sources and
+ * eventually switch to the shortest
+ * path (if forwarder) */
+u_int8 pim_data_rate_flag; /* Used to indicate there was a
+ * timeout */
+
+u_int16 pim_reg_rate_timer; /* The same as above, but used by the
+ * RP to switch to the shortest path
+ * and avoid the PIM registers. */
+u_int8 pim_reg_rate_flag;
+u_int8 rate_flag;
+
+/*
+ * TODO: XXX: the timers below are not used. Instead, the data rate timer is
+ * used.
+ */
+u_int16 kernel_cache_timer; /* Used to timeout the kernel cache
+ * entries for idle sources */
+u_int16 kernel_cache_check_interval;
+
+/* to request and compare any route changes */
+
+srcentry_t srcentry_save;
+rpentry_t rpentry_save;
+
+/*
+ * Init some timers
+ */
+void
+init_timers()
+{
+ unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL;
+ SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
+
+ /*
+ * The routing_check and the rate_check timers are interleaved to reduce
+ * the amount of work that has to be done at once.
+ */
+ /* XXX: for simplicity, both the intervals are the same */
+
+ if (pim_data_rate_check_interval < pim_reg_rate_check_interval)
+ pim_reg_rate_check_interval = pim_data_rate_check_interval;
+
+ SET_TIMER(pim_data_rate_timer, 3 * pim_data_rate_check_interval / 2);
+ SET_TIMER(pim_reg_rate_timer, 3 * pim_reg_rate_check_interval / 2);
+
+ /*
+ * Initialize the srcentry and rpentry 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;
+ RESET_TIMER(srcentry_save.timer);
+ srcentry_save.cand_rp = (cand_rp_t *) NULL;
+
+ rpentry_save.prev = (rpentry_t *) NULL;
+ rpentry_save.next = (rpentry_t *) NULL;
+ memset(&rpentry_save.address, 0, sizeof(struct sockaddr_in6));
+ rpentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
+ rpentry_save.address.sin6_family= AF_INET6;
+ rpentry_save.mrtlink = (mrtentry_t *) NULL;
+ rpentry_save.incoming = NO_VIF;
+ rpentry_save.upstream = (pim_nbr_entry_t *) NULL;
+ rpentry_save.metric = ~0;
+ rpentry_save.preference = ~0;
+ RESET_TIMER(rpentry_save.timer);
+ rpentry_save.cand_rp = (cand_rp_t *) NULL;
+
+}
+
+
+/*
+ * 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 | MIFF_REGISTER))
+ 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.
+ */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer)
+ continue;
+ IF_NOT_TIMEOUT(curr_nbr->timer)
+ continue;
+
+ v->uv_pim6_nbr_timo++;
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG, 0,
+ "%s on %s is dead , delete it",
+ inet6_fmt(&curr_nbr->address.sin6_addr),
+ uvifs[curr_nbr->vifi].uv_name);
+ delete_pim6_nbr(curr_nbr);
+ }
+
+ /* PIM_HELLO periodic */
+ IF_TIMEOUT(v->uv_pim_hello_timer)
+ send_pim6_hello(v, pim_hello_holdtime);
+
+ /* MLD6 query periodic */
+ IF_TIMEOUT(v->uv_gq_timer)
+ query_groups(v);
+ }
+
+ IF_DEBUG(DEBUG_IF) {
+ dump_vifs(log_fp);
+ ;
+ }
+}
+
+/*
+ * Scan the whole routing table and timeout a bunch of timers:
+ * - oifs timers
+ * - Join/Prune timer
+ * - routing entry
+ * - Assert timer
+ * - Register-Suppression timer
+ *
+ * - If the global timer for checking the unicast routing has expired, perform
+ * also iif/upstream router change verification
+ * - If the global timer for checking the data rate has expired, check the
+ * number of bytes forwarded after the lastest timeout. If bigger than
+ * a given threshold, then switch to the shortest path.
+ * If `number_of_bytes == 0`, then delete the kernel cache entry.
+ *
+ * Only the entries which have the Join/Prune timer expired are sent.
+ * In the special case when we have ~(S,G)RPbit Prune entry, we must
+ * include any (*,G) or (*,*,RP) XXX: ???? what and why?
+ *
+ * Below is a table which summarizes the segmantic rules.
+ *
+ * On the left side is "if A must be included in the J/P message".
+ * On the top is "shall/must include B?"
+ * "Y" means "MUST include"
+ * "SY" means "SHOULD include"
+ * "N" means "NO NEED to include"
+ * (G is a group that matches to RP)
+ *
+ * -----------||-----------||-----------
+ * || (*,*,RP) || (*,G) || (S,G) ||
+ * ||-----------||-----------||-----------||
+ * || J | P || J | P || J | P ||
+ * ==================================================||
+ * J || n/a | n/a || N | Y || N | Y ||
+ * (*,*,RP) -----------------------------------------||
+ * P || n/a | n/a || SY | N || SY | N ||
+ * ==================================================||
+ * J || N | N || n/a | n/a || N | Y ||
+ * (*,G) -----------------------------------------||
+ * P || N | N || n/a | n/a || SY | N ||
+ * ==================================================||
+ * J || N | N || N | N || n/a | n/a ||
+ * (S,G) -----------------------------------------||
+ * P || N | N || N | N || n/a | n/a ||
+ * ==================================================
+ *
+ */
+
+void
+age_routes()
+{
+ cand_rp_t *cand_rp_ptr;
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+ mrtentry_t *mrtentry_grp;
+ mrtentry_t *mrtentry_rp;
+ mrtentry_t *mrtentry_wide;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_srcs_next;
+ struct uvif *v;
+ vifi_t vifi;
+ pim_nbr_entry_t *pim_nbr_ptr;
+ int change_flag;
+ int rp_action,
+ grp_action,
+ src_action=0,
+ src_action_rp=0;
+ int dont_calc_action;
+ int did_switch_flag;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+ kernel_cache_t *kernel_cache_next;
+ u_long curr_bytecnt;
+ rpentry_t *rpentry_ptr;
+ int update_rp_iif;
+ int update_src_iif;
+ if_set new_pruned_oifs;
+
+ /*
+ * Timing out of the global `unicast_routing_timer` and `data_rate_timer`
+ */
+
+ IF_TIMEOUT(unicast_routing_timer)
+ {
+ ucast_flag = TRUE;
+ SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
+ }
+ ELSE
+ {
+ ucast_flag = FALSE;
+ }
+
+ IF_TIMEOUT(pim_data_rate_timer)
+ {
+ pim_data_rate_flag = TRUE;
+ SET_TIMER(pim_data_rate_timer, pim_data_rate_check_interval);
+ }
+ ELSE
+ {
+ pim_data_rate_flag = FALSE;
+ }
+
+ IF_TIMEOUT(pim_reg_rate_timer)
+ {
+ pim_reg_rate_flag = TRUE;
+ SET_TIMER(pim_reg_rate_timer, pim_reg_rate_check_interval);
+ }
+ ELSE
+ {
+ pim_reg_rate_flag = FALSE;
+ }
+
+ rate_flag = pim_data_rate_flag | pim_reg_rate_flag;
+
+ /* Scan the (*,*,RP) entries */
+
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+
+ rpentry_ptr = cand_rp_ptr->rpentry;
+
+ /*
+ * Need to save only `incoming` and `upstream` to discover unicast
+ * route changes. `metric` and `preference` are not interesting for
+ * us.
+ */
+
+ rpentry_save.incoming = rpentry_ptr->incoming;
+ rpentry_save.upstream = rpentry_ptr->upstream;
+
+ update_rp_iif = FALSE;
+ if ((ucast_flag == TRUE) &&
+ (!inet6_equal(&rpentry_ptr->address ,&my_cand_rp_address)))
+ {
+ /*
+ * I am not the RP. If I was the RP, then the iif is register_vif
+ * and no need to reset it.
+ */
+
+ if (set_incoming(rpentry_ptr, PIM_IIF_RP) != TRUE)
+ {
+ /*
+ * TODO: XXX: no route to that RP. Panic? There is a high
+ * probability the network is partitioning so immediately
+ * remapping to other RP is not a good idea. Better wait the
+ * Bootstrap mechanism to take care of it and provide me with
+ * correct Cand-RP-Set.
+ */
+ ;
+ }
+ else
+ {
+ if ((rpentry_save.upstream != rpentry_ptr->upstream)
+ || (rpentry_save.incoming != rpentry_ptr->incoming))
+ {
+ /*
+ * Routing change has occur. Update all (*,G) and
+ * (S,G)RPbit iifs mapping to that RP
+ */
+ update_rp_iif = TRUE;
+ }
+ }
+ }
+
+ rp_action = PIM_ACTION_NOTHING;
+ mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
+
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_rp->joined_oifs))
+ IF_TIMEOUT(mrtentry_rp->vif_timers[vifi])
+ {
+ uvifs[vifi].uv_outif_timo++;
+ IF_CLR(vifi, &mrtentry_rp->joined_oifs);
+ change_flag = TRUE;
+ }
+ }
+ if ((change_flag == TRUE) || (update_rp_iif == TRUE))
+ {
+ change_interfaces(mrtentry_rp,
+ rpentry_ptr->incoming,
+ &mrtentry_rp->joined_oifs,
+ &mrtentry_rp->pruned_oifs,
+ &mrtentry_rp->leaves,
+ &mrtentry_rp->asserted_oifs, 0);
+ mrtentry_rp->upstream = rpentry_ptr->upstream;
+ }
+
+ if (rate_flag == TRUE)
+ {
+ /* Check the activity for this entry */
+
+ /*
+ * XXX: the spec says to start monitoring first the total
+ * traffic for all senders for particular (*,*,RP) or (*,G)
+ * and if the total traffic exceeds some predefined
+ * threshold, then start monitoring the data traffic for each
+ * particular sender for this group: (*,G) or (*,*,RP).
+ * However, because the kernel cache/traffic info is of the
+ * form (S,G), it is easier if we are simply collecting (S,G)
+ * traffic all the time.
+ *
+ * For (*,*,RP) if the number of bytes received between the last
+ * check and now exceeds some precalculated value (based on
+ * interchecking period and datarate threshold AND if there
+ * are directly connected members (i.e. we are their last
+ * hop(e) router), then create (S,G) and start initiating
+ * (S,G) Join toward the source. The same applies for (*,G).
+ * The spec does not say that if the datarate goes below a
+ * given threshold, then will switch back to the shared tree,
+ * hence after a switch to the source-specific tree occurs, a
+ * source with low datarate, but periodically sending will
+ * keep the (S,G) states.
+ *
+ * If a source with kernel cache entry has been idle after the
+ * last time a check of the datarate for the whole routing
+ * table, then delete its kernel cache entry.
+ */
+ for (kernel_cache_ptr = mrtentry_rp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for some reason there is no such routing
+ * entry or that particular (s,g) was idle. Delete
+ * the routing entry from the kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ continue;
+ }
+ /*
+ * Check if the datarate was high enough to switch to
+ * source specific tree.
+ */
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_rp->leaves,
+ &mrtentry_rp->oifs) == TRUE)
+ {
+#ifdef KERNEL_MFC_WC_G
+// TODO (one day... :))
+ if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_forward++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+
+ /* RP initiated switch */
+
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_rp->incoming == reg_vif_num)
+
+#ifdef KERNEL_MFC_WC_G
+// TODO (one day :))
+ if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_rp++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ }
+ }
+ }
+
+ /* Join/Prune timer */
+ IF_TIMEOUT(mrtentry_rp->jp_timer)
+ {
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join/Prune timer expired");
+
+ rp_action = join_or_prune(mrtentry_rp,
+ mrtentry_rp->upstream);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"rp_action = %d",rp_action);
+
+ if (rp_action != PIM_ACTION_NOTHING)
+ add_jp_entry(mrtentry_rp->upstream,
+ pim_join_prune_holdtime,
+ &sockaddr6_d,
+ STAR_STAR_RP_MSK6LEN,
+ &mrtentry_rp->source->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP | MRTF_WC,
+ rp_action);
+ SET_TIMER(mrtentry_rp->jp_timer, pim_join_prune_period);
+ }
+
+ /* Assert timer */
+ if (mrtentry_rp->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_rp->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_rp->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is running,
+ * install a negative cache entry in the kernel?
+ */
+ /*
+ * TODO: can we have Register-Suppression timer for (*,*,RP)?
+ * Currently no...
+ */
+
+ IF_TIMEOUT(mrtentry_rp->rs_timer)
+ ;
+ /* routing entry */
+
+ if ((TIMEOUT(mrtentry_rp->timer))
+ && (IF_ISEMPTY(&mrtentry_rp->leaves)))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+ delete_mrtentry(mrtentry_rp);
+ }
+ } /* mrtentry_rp != NULL */
+
+ /* Just in case if that (*,*,RP) was deleted */
+
+ mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
+
+ /* Check the (*,G) and (S,G) entries */
+
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ mrtentry_grp = grpentry_ptr->grp_route;
+ mrtentry_srcs = grpentry_ptr->mrtlink;
+
+ grp_action = PIM_ACTION_NOTHING;
+ if (mrtentry_grp != (mrtentry_t *) NULL)
+ {
+ /* The (*,G) entry */
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_grp->joined_oifs))
+ IF_TIMEOUT(mrtentry_grp->vif_timers[vifi])
+ {
+ IF_CLR(vifi, &mrtentry_grp->joined_oifs);
+ uvifs[vifi].uv_outif_timo++;
+ change_flag = TRUE;
+ }
+ }
+
+ if ((change_flag == TRUE) || (update_rp_iif == TRUE))
+ {
+ change_interfaces(mrtentry_grp,
+ rpentry_ptr->incoming,
+ &mrtentry_grp->joined_oifs,
+ &mrtentry_grp->pruned_oifs,
+ &mrtentry_grp->leaves,
+ &mrtentry_grp->asserted_oifs, 0);
+ mrtentry_grp->upstream = rpentry_ptr->upstream;
+ }
+
+ /* Check the sources activity */
+
+ if (rate_flag == TRUE)
+ {
+ for (kernel_cache_ptr = mrtentry_grp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt =
+ kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket,
+ &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for whatever reason there is no
+ * such routing entry or that particular
+ * (s,g) was idle. Delete the routing entry
+ * from the kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_grp,
+ kernel_cache_ptr);
+ continue;
+ }
+
+ /*
+ * Check if the datarate was high enough to
+ * switch to source specific tree.
+ */
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_grp->leaves,
+ &mrtentry_grp->oifs) == TRUE)
+ {
+#ifdef KERNEL_MFC_WC_G
+// TODO
+ if (kernel_cache_ptr->source
+ == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
+ mrtentry_grp->flags
+ |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+
+ pim6dstat.pim6_trans_spt_forward++;
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+
+ /* RP initiated switch */
+
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_grp->incoming == reg_vif_num)
+#ifdef KERNEL_MFC_WC_G
+// TODO
+ if (kernel_cache_ptr->source
+ == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
+ mrtentry_grp->flags
+ |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_rp++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ }
+ }
+ }
+
+ dont_calc_action = FALSE;
+ if (rp_action != PIM_ACTION_NOTHING)
+ {
+ grp_action = join_or_prune(mrtentry_grp,
+ mrtentry_grp->upstream);
+ dont_calc_action = TRUE;
+ if (((rp_action == PIM_ACTION_JOIN)
+ && (grp_action == PIM_ACTION_PRUNE))
+ || ((rp_action == PIM_ACTION_PRUNE)
+ && (grp_action == PIM_ACTION_JOIN)))
+ FIRE_TIMER(mrtentry_grp->jp_timer);
+ }
+
+ /* Join/Prune timer */
+
+ IF_TIMEOUT(mrtentry_grp->jp_timer)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join/Prune timer expired");
+
+ if (dont_calc_action != TRUE)
+ grp_action = join_or_prune(mrtentry_grp,
+ mrtentry_grp->upstream);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"grp_action = %d",grp_action);
+
+ if (grp_action != PIM_ACTION_NOTHING)
+ {
+ add_jp_entry(mrtentry_grp->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_grp->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &cand_rp_ptr->rpentry->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP | MRTF_WC,
+ grp_action);
+ }
+
+ SET_TIMER(mrtentry_grp->jp_timer, pim_join_prune_period);
+ }
+
+ /* Assert timer */
+ if (mrtentry_grp->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_grp->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_grp->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is
+ * running, install a negative cache entry in the kernel?
+ */
+ /*
+ * TODO: currently cannot have Register-Suppression timer
+ * for (*,G) entry, but keep this around.
+ */
+ IF_TIMEOUT(mrtentry_grp->rs_timer)
+ ;
+
+ /* routing entry */
+
+ if ((TIMEOUT(mrtentry_grp->timer))
+ && (IF_ISEMPTY(&mrtentry_grp->leaves)))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+ delete_mrtentry(mrtentry_grp);
+ }
+ } /* if (mrtentry_grp != NULL) */
+
+
+ /* For all (S,G) for this group */
+ /* XXX: mrtentry_srcs was set before */
+ for (; mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs_next)
+ {
+ /* routing entry */
+ mrtentry_srcs_next = mrtentry_srcs->grpnext;
+
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_srcs->joined_oifs))
+ {
+ /* TODO: checking for reg_num_vif is slow! */
+
+ if (vifi != reg_vif_num)
+ {
+ IF_TIMEOUT(mrtentry_srcs->vif_timers[vifi])
+ {
+ IF_CLR(vifi,
+ &mrtentry_srcs->joined_oifs);
+ change_flag = TRUE;
+ uvifs[vifi].uv_outif_timo++;
+ }
+ }
+ }
+ }
+
+ update_src_iif = FALSE;
+ if (ucast_flag == TRUE)
+ {
+ if (!(mrtentry_srcs->flags & MRTF_RP))
+ {
+ /* iif toward the source */
+ srcentry_save.incoming =
+ mrtentry_srcs->source->incoming;
+ srcentry_save.upstream =
+ mrtentry_srcs->source->upstream;
+ if (set_incoming(mrtentry_srcs->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_srcs);
+ continue;
+ }
+ else
+ {
+
+ /* iif info found */
+
+ if ((srcentry_save.incoming !=
+ mrtentry_srcs->incoming)
+ || (srcentry_save.upstream !=
+ mrtentry_srcs->upstream))
+ {
+ /* Route change has occur */
+ update_src_iif = TRUE;
+ mrtentry_srcs->incoming =
+ mrtentry_srcs->source->incoming;
+ mrtentry_srcs->upstream =
+ mrtentry_srcs->source->upstream;
+ }
+ }
+ }
+ else
+ {
+ /* (S,G)RPBit with iif toward RP */
+ if ((rpentry_save.upstream !=
+ mrtentry_srcs->upstream)
+ || (rpentry_save.incoming !=
+ mrtentry_srcs->incoming))
+ {
+ update_src_iif = TRUE; /* XXX: a hack */
+ /* XXX: setup the iif now! */
+ mrtentry_srcs->incoming =
+ rpentry_ptr->incoming;
+ mrtentry_srcs->upstream =
+ rpentry_ptr->upstream;
+ }
+ }
+ }
+
+ if ((change_flag == TRUE) || (update_src_iif == TRUE))
+ /* Flush the changes */
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+
+ if (rate_flag == TRUE)
+ {
+ for (kernel_cache_ptr = mrtentry_srcs->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket,
+ &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for some reason there is no such
+ * routing entry or that particular (s,g) was
+ * idle. Delete the routing entry from the
+ * kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_srcs,
+ kernel_cache_ptr);
+ continue;
+ }
+ /*
+ * Check if the datarate was high enough to
+ * switch to source specific tree. Need to check
+ * only when we have (S,G)RPbit in the forwarder
+ * or the RP itself.
+ */
+
+ if (!(mrtentry_srcs->flags & MRTF_RP))
+ continue;
+
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_srcs->leaves,
+ &mrtentry_srcs->oifs)
+ == TRUE)
+ {
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+ /* RP initiated switch */
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_srcs->incoming == reg_vif_num)
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ }
+
+ /*
+ * XXX: currentry the spec doesn't say to switch
+ * back to the shared tree if low datarate, but
+ * if needed to implement, the check must be done
+ * here. Don't forget to check whether I am a
+ * forwarder for that source.
+ */
+ }
+ }
+
+ mrtentry_wide = mrtentry_srcs->group->grp_route;
+ if (mrtentry_wide == (mrtentry_t *) NULL)
+ mrtentry_wide = mrtentry_rp;
+
+ dont_calc_action = FALSE;
+ if ((rp_action != PIM_ACTION_NOTHING)
+ || (grp_action != PIM_ACTION_NOTHING))
+ {
+ src_action_rp = join_or_prune(mrtentry_srcs,
+ rpentry_ptr->upstream);
+ src_action = src_action_rp;
+ dont_calc_action = TRUE;
+ if (src_action_rp == PIM_ACTION_JOIN)
+ {
+ if ((grp_action == PIM_ACTION_PRUNE)
+ || (rp_action == PIM_ACTION_PRUNE))
+ FIRE_TIMER(mrtentry_srcs->jp_timer);
+ }
+ else
+ if (src_action_rp == PIM_ACTION_PRUNE)
+ {
+ if ((grp_action == PIM_ACTION_JOIN)
+ || (rp_action == PIM_ACTION_JOIN))
+ FIRE_TIMER(mrtentry_srcs->jp_timer);
+ }
+ }
+
+ /* Join/Prune timer */
+
+ IF_TIMEOUT(mrtentry_srcs->jp_timer)
+ {
+ if ((dont_calc_action != TRUE)
+ || (rpentry_ptr->upstream
+ != mrtentry_srcs->upstream))
+ src_action = join_or_prune(mrtentry_srcs,
+ mrtentry_srcs->upstream);
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"src_action = %d",src_action);
+
+ if (src_action != PIM_ACTION_NOTHING)
+ add_jp_entry(mrtentry_srcs->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_srcs->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &mrtentry_srcs->source->address,
+ SINGLE_SRC_MSK6LEN,
+ mrtentry_srcs->flags & MRTF_RP,
+ src_action);
+ if (mrtentry_wide != (mrtentry_t *) NULL)
+ {
+ /*
+ * Have both (S,G) and (*,G) (or (*,*,RP)). Check
+ * if need to send (S,G) PRUNE toward RP
+ */
+
+ if (mrtentry_srcs->upstream
+ != mrtentry_wide->upstream)
+ {
+ if (dont_calc_action != TRUE)
+ src_action_rp =
+ join_or_prune(mrtentry_srcs,
+ mrtentry_wide->upstream);
+ /*
+ * XXX: TODO: do error check if src_action ==
+ * PIM_ACTION_JOIN, which should be an error.
+ */
+
+ if (src_action_rp == PIM_ACTION_PRUNE)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"src_action = %d",src_action);
+ add_jp_entry(mrtentry_wide->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_srcs->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &mrtentry_srcs->source->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP,
+ src_action_rp);
+ }
+ }
+ }
+ SET_TIMER(mrtentry_srcs->jp_timer, pim_join_prune_period);
+ }
+ /* Assert timer */
+ if (mrtentry_srcs->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_srcs->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_srcs->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is
+ * running, install a negative cache entry in the kernel?
+ */
+
+ IF_TIMER_SET(mrtentry_srcs->rs_timer)
+ {
+ IF_TIMEOUT(mrtentry_srcs->rs_timer)
+ {
+ /* Start encapsulating the packets */
+ IF_COPY(&mrtentry_srcs->pruned_oifs,
+ &new_pruned_oifs);
+ IF_CLR(reg_vif_num, &new_pruned_oifs);
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &new_pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ ELSE
+ {
+ /*
+ * The register suppression timer is running.
+ * Check whether it is time to send
+ * PIM_NULL_REGISTER.
+ */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_srcs->rs_timer
+ <= pim_register_probe_time)
+ {
+ /* Time to send a PIM_NULL_REGISTER */
+ /*
+ * XXX: a (bad) hack! This will be sending
+ * periodically NULL_REGISTERS between
+ * pim_register_probe_time and 0. Well,
+ * because PROBE_TIME is 5 secs , it will
+ * happen only once ( if granularity is 5 and prob 5!)
+ * , so it helps to avoid
+ * adding a flag to the routing entry whether
+ * a NULL_REGISTER was sent.
+ */
+ send_pim6_null_register(mrtentry_srcs);
+ }
+ }
+ }
+
+ /* routing entry */
+ if (TIMEOUT(mrtentry_srcs->timer))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+
+ if (IF_ISEMPTY(&mrtentry_srcs->leaves))
+ {
+ delete_mrtentry(mrtentry_srcs);
+ continue;
+ }
+ /*
+ * XXX: if DR, Register suppressed, and leaf oif
+ * inherited from (*,G), the directly connected
+ * source is not active anymore, this (S,G) entry
+ * won't timeout. Check if the leaf oifs are
+ * inherited from (*,G); if true. delete the (S,G)
+ * entry.
+ */
+
+ if (mrtentry_srcs->group->grp_route
+ != (mrtentry_t *) NULL)
+ {
+ if_set r_and, r_xor;
+ vif_and(&mrtentry_srcs->group->grp_route->leaves,
+ &mrtentry_srcs->leaves,
+ &r_and);
+ vif_xor(&r_and ,&mrtentry_srcs->leaves,
+ &r_xor);
+ if (IF_ISEMPTY(&r_xor))
+ {
+ delete_mrtentry(mrtentry_srcs);
+ continue;
+ }
+ }
+ }
+ } /* End of (S,G) loop */
+ } /* End of (*,G) loop */
+ }
+ } /* For all cand RPs */
+
+ /* TODO: check again! */
+ for (vifi = 0, v = &uvifs[0]; vifi < numvifs; vifi++, v++)
+ {
+ /* Send all pending Join/Prune messages */
+ for (pim_nbr_ptr = v->uv_pim_neighbors;
+ pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
+ pim_nbr_ptr = pim_nbr_ptr->next)
+ {
+ pack_and_send_jp6_message(pim_nbr_ptr);
+ }
+ }
+
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(log_fp);
+ return;
+}
+
+
+/*
+ * TODO: timeout the RP-group mapping entries during the scan of the whole
+ * routing table?
+ */
+void
+age_misc()
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rp_grp_entry_t *rp_grp_entry_next;
+ grp_mask_t *grp_mask_ptr;
+ grp_mask_t *grp_mask_next;
+
+ /* Timeout the Cand-RP-set entries */
+ for (grp_mask_ptr = grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ /*
+ * If we timeout an entry, the grp_mask_ptr entry might be removed.
+ */
+ grp_mask_next = grp_mask_ptr->next;
+ for (rp_grp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->grp_rp_next;
+ IF_TIMEOUT(rp_grp_entry_ptr->holdtime) {
+ delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ rp_grp_entry_ptr);
+ pim6dstat.pim6_rpgrp_timo++;
+ }
+ }
+ }
+
+ /* Cand-RP-Adv timer */
+ if (cand_rp_flag == TRUE)
+ {
+ IF_TIMEOUT(pim_cand_rp_adv_timer)
+ {
+ send_pim6_cand_rp_adv();
+ SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
+ }
+ }
+
+ /* bootstrap-timer */
+
+ IF_TIMEOUT(pim_bootstrap_timer)
+ {
+ pim6dstat.pim6_bootstrap_timo++;
+
+ if (cand_bsr_flag == FALSE)
+ {
+ /*
+ * If I am not Cand-BSR, start accepting Bootstrap messages from
+ * anyone. XXX: Even if the BSR has timeout, the existing
+ * Cand-RP-Set is kept.
+ */
+ curr_bsr_fragment_tag = 0;
+ curr_bsr_priority = 0; /* Lowest priority */
+ memset(&curr_bsr_address, 0, sizeof(struct sockaddr_in6));
+ curr_bsr_address.sin6_len = sizeof(struct sockaddr_in6);
+ curr_bsr_address.sin6_family = AF_INET6;
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+ }
+ else
+ {
+ /* I am Cand-BSR, so set the current BSR to me */
+ if (inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ SET_TIMER(pim_bootstrap_timer, my_bsr_period);
+ send_pim6_bootstrap();
+ }
+ else
+ {
+ /*
+ * Short delay before becoming the BSR and start sending of
+ * the Cand-RP set (to reduce the transient control
+ * overhead).
+ */
+ SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
+ curr_bsr_fragment_tag = RANDOM();
+ curr_bsr_priority = my_bsr_priority;
+ curr_bsr_address = my_bsr_address;
+ memcpy(&curr_bsr_hash_mask , &my_bsr_hash_mask , sizeof(struct in6_addr));
+ }
+ }
+ }
+
+
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP | DEBUG_PIM_CAND_RP)
+ dump_rp_set(log_fp);
+ /* TODO: XXX: anything else to timeout */
+}
diff --git a/usr.sbin/pim6sd/timer.h b/usr.sbin/pim6sd/timer.h
new file mode 100644
index 0000000..132ff5a
--- /dev/null
+++ b/usr.sbin/pim6sd/timer.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * 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$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef TIMER_H
+#define TIMER_H
+
+/* the default granularity if not specified in the config file */
+
+#define DEFAULT_TIMER_INTERVAL 5
+
+/* For timeout. The timers count down */
+
+#define SET_TIMER(timer, value) (timer) = (value)
+#define RESET_TIMER(timer) (timer) = 0
+#define COPY_TIMER(timer_1, timer_2) (timer_2) = (timer_1)
+#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_TIMER_NOT_SET(timer) if ((timer) <= 0)
+
+#define IF_TIMEOUT(timer) \
+ if (!((timer) -= (MIN(timer, timer_interval))))
+
+#define IF_NOT_TIMEOUT(timer) \
+ if ((timer) -= (MIN(timer, timer_interval)))
+
+#define TIMEOUT(timer) \
+ (!((timer) -= (MIN(timer, timer_interval))))
+
+#define NOT_TIMEOUT(timer) \
+ ((timer) -= (MIN(timer, timer_interval)))
+
+
+extern u_int32 pim_reg_rate_bytes;
+extern u_int32 pim_reg_rate_check_interval;
+extern u_int32 pim_data_rate_bytes;
+extern u_int32 pim_data_rate_check_interval;
+extern u_int32 pim_hello_period;
+extern u_int32 pim_hello_holdtime;
+extern u_int32 timer_interval;
+extern u_int32 pim_join_prune_period;
+extern u_int32 pim_join_prune_holdtime;
+extern u_int32 pim_data_timeout;
+extern u_int32 pim_register_suppression_timeout;
+extern u_int32 pim_register_probe_time;
+extern u_int32 pim_assert_timeout;
+
+extern void init_timers __P(());
+extern void init_timers __P(());
+extern void age_vifs __P(());
+extern void age_routes __P(());
+extern void age_misc __P(());
+
+
+#endif
diff --git a/usr.sbin/pim6sd/trace.c b/usr.sbin/pim6sd/trace.c
new file mode 100644
index 0000000..7c54628
--- /dev/null
+++ b/usr.sbin/pim6sd/trace.c
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.c,v 1.7 1999/09/16 08:45:45 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.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "vif.h"
+#include "inet6.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/in6_var.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "defs.h"
+#include "mld6.h"
+#include "kern.h"
+#include "debug.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "rp.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;
+ rpentry_t *rpentry_ptr;
+
+ /* 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 */
+ }
+
+ /*
+ * 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;
+
+ 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 */
+ 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 {
+ 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/pim6sd/trace.h b/usr.sbin/pim6sd/trace.h
new file mode 100644
index 0000000..465acc6
--- /dev/null
+++ b/usr.sbin/pim6sd/trace.h
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.h,v 1.2 1999/09/09 15:47: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.
+ *
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+/*
+ * 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)
+
+void accept_mtrace __P((struct sockaddr_in6 *, struct in6_addr *,
+ struct in6_addr *, int, char *, u_int, int));
+#endif /* TRACE_H */
diff --git a/usr.sbin/pim6sd/var.h b/usr.sbin/pim6sd/var.h
new file mode 100644
index 0000000..999ba44
--- /dev/null
+++ b/usr.sbin/pim6sd/var.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 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$
+ */
+/* YIPS @(#)$Id: var.h,v 1.1 1999/10/29 09:04:54 jinmei Exp $ */
+
+#if !defined(_VAR_H_)
+#define _VAR_H_
+
+#include <sys/socket.h>
+
+#define MAX3(a,b,c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
+
+#define CALLOC(size, cast) (cast)calloc(1, (size))
+
+#define ISSET(exp, bit) (((exp) & (bit)) == (bit))
+
+#define ATOX(c) \
+ (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
+
+#define LALIGN(a) \
+ ((a) > 0 ? ((a) &~ (sizeof(long) - 1)) : sizeof(long))
+
+#define RNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#define BUFADDRSIZE 128
+#define INET_NTOP(addr, buf) \
+ inet_ntop(((struct sockaddr *)(addr))->sa_family, _INADDRBYSA(addr), buf, sizeof(buf))
+
+#define GETNAMEINFO(x, y, z) \
+ getnameinfo((x), (x)->sa_len, (y), sizeof(y), (z), sizeof(z), \
+ NI_NUMERICHOST | NI_NUMERICSERV)
+
+#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif /*!defined(_VAR_H_)*/
diff --git a/usr.sbin/pim6sd/vers.c b/usr.sbin/pim6sd/vers.c
new file mode 100644
index 0000000..5264529
--- /dev/null
+++ b/usr.sbin/pim6sd/vers.c
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+char todaysversion[]="2.1.0-alpha23";
diff --git a/usr.sbin/pim6sd/vif.c b/usr.sbin/pim6sd/vif.c
new file mode 100644
index 0000000..e7ef2d9
--- /dev/null
+++ b/usr.sbin/pim6sd/vif.c
@@ -0,0 +1,824 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "vif.h"
+#include "mld6.h"
+#include "pim6.h"
+#include "pimd.h"
+#include "route.h"
+#include "config.h"
+#include "inet6.h"
+#include "kern.h"
+#include "mld6_proto.h"
+#include "pim6_proto.h"
+#include "mrt.h"
+#include "debug.h"
+#include "timer.h"
+
+struct uvif uvifs[MAXMIFS]; /*the list of virtualsinterfaces */
+vifi_t numvifs; /*total number of interface */
+int vifs_down;
+vifi_t reg_vif_num; /*register interface*/
+int phys_vif; /* An enabled vif that has a global address */
+int udp_socket;
+int total_interfaces;
+if_set if_nullset;
+if_set if_result;
+
+int init_reg_vif();
+void start_all_vifs();
+void start_vif( vifi_t vifi );
+void stop_vif( vifi_t vivi );
+int update_reg_vif( vifi_t register_vifi);
+
+extern int cfparse(int, int);
+
+void init_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ int enabled_vifs;
+
+ numvifs = 0;
+ reg_vif_num = NO_VIF;
+
+ /*
+ * 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 all the interfaces ... */
+
+ for(vifi = 0,v=uvifs; vifi < MAXVIFS; ++ vifi, ++v)
+ {
+ memset(v,0,sizeof(*v)); /* everything is zeroed => NULL , pointer NULL , addrANY ...) */
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+ strncpy(v->uv_name,"",IFNAMSIZ);
+ v->uv_local_pref = default_source_preference;
+ v->uv_local_metric = default_source_metric;
+ }
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Interfaces world initialized...");
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Getting vifs from kernel");
+ config_vifs_from_kernel();
+ if (max_global_address() == NULL)
+ log(LOG_ERR, 0, "There's no global address");
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Getting vifs from %s",configfilename);
+
+ /* read config from file */
+ if (cfparse(1, 0) != 0)
+ log(LOG_ERR, 0, "fatal error in parsing the config file");
+
+ enabled_vifs = 0;
+ phys_vif = -1;
+
+ for( vifi = 0, v = uvifs ; vifi < numvifs ; ++ vifi,++v)
+ {
+ if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ if(v->uv_linklocal == NULL)
+ log(LOG_ERR,0,"there is no link-local address on vif %s",v->uv_name);
+ 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 vifs" : "only one enabled vif" );
+
+ memset(&if_nullset,0,sizeof(if_nullset));
+ k_init_pim(mld6_socket);
+ IF_DEBUG(DEBUG_PIM_DETAIL)
+ log(LOG_DEBUG,0,"Pim kernel initialization done");
+
+
+ /* Add a dummy virtual interface to support Registers in the kernel. */
+ init_reg_vif();
+
+ start_all_vifs();
+
+}
+int init_reg_vif()
+{
+ struct uvif *v;
+ vifi_t i;
+
+ v = &uvifs[numvifs];
+ if (( numvifs+1 ) == MAXVIFS )
+ {
+ /* Exit the program! The PIM router must have a Register vif */
+ log(LOG_ERR, 0,
+ "cannot install the Register vif: too many interfaces");
+ /* To make lint happy */
+ return (FALSE);
+ }
+
+ /*
+ * So far in PIM we need only one register vif and we save its number in
+ * the global reg_vif_num.
+ */
+
+
+ reg_vif_num = numvifs;
+
+ /* Use the address of the first available physical interface to
+ * create the register vif.
+ */
+
+ for(i =0 ; i < numvifs ; i++)
+ {
+ if(uvifs[i].uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
+ continue;
+ else
+ break;
+ }
+ if( i >= numvifs)
+ {
+ log(LOG_ERR, 0, "No physical interface enabled");
+ return -1;
+ }
+
+
+ memcpy(v,&uvifs[i],sizeof(*v));
+ strncpy(v->uv_name,"register_mif0",IFNAMSIZ);
+ v->uv_flags = MIFF_REGISTER;
+
+#ifdef PIM_EXPERIMENTAL
+ v->uv_flags |= MIFF_REGISTER_KERNEL_ENCAP;
+#endif
+
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Interface %s (subnet %s) ,installed on vif #%u - rate = %d",
+ v->uv_name,net6name(&v->uv_prefix.sin6_addr,&v->uv_subnetmask),
+ reg_vif_num,v->uv_rate_limit);
+
+ numvifs++;
+ total_interfaces++;
+ return 0;
+}
+
+void start_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_int action;
+
+
+ /* Start first the NON-REGISTER vifs */
+
+ for(action=0; ;action = MIFF_REGISTER )
+ {
+ for(vifi= 0,v = uvifs;vifi < numvifs ; ++vifi, ++v)
+ {
+ if (( v->uv_flags & MIFF_REGISTER ) ^ action )
+ /* If starting non-registers but the vif is a register
+ * or if starting registers, but the interface is not
+ * a register, then just continue.
+ */
+ continue;
+
+ if ( v->uv_flags & (VIFF_DISABLED | VIFF_DOWN ))
+ {
+ IF_DEBUG(DEBUG_IF)
+ {
+ if ( v-> uv_flags & VIFF_DISABLED)
+ log(LOG_DEBUG,0,"%s is DISABLED ; vif #%u out of service",v->uv_name,vifi);
+ else
+ log(LOG_DEBUG,0,"%s is DOWN ; vif #%u out of service",v->uv_name,vifi);
+ }
+ }
+ else
+ start_vif(vifi);
+ }
+ if ( action == MIFF_REGISTER)
+ break;
+ }
+}
+
+/*
+ * Initialize the vif and add to the kernel. The vif can be either
+ * physical, register or tunnel (tunnels will be used in the future
+ * when this code becomes PIM multicast boarder router.
+ */
+
+
+void start_vif (vifi_t vifi)
+{
+ struct uvif *v;
+
+ v = &uvifs[vifi];
+
+ /* Initialy no router on any vif */
+
+ if( v-> uv_flags & MIFF_REGISTER)
+ v->uv_flags = v->uv_flags & ~VIFF_DOWN;
+ else
+ {
+ v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~ VIFF_DOWN;
+ v->uv_pim_hello_timer = 1 + RANDOM() % pim_hello_period;
+ v->uv_jp_timer = 1 + RANDOM() % pim_join_prune_period;
+ }
+
+ /* Tell kernel to add, i.e. start this vif */
+
+ k_add_vif(mld6_socket,vifi,&uvifs[vifi]);
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"%s comes up ,vif #%u now in service",v->uv_name,vifi);
+
+ if(!(v->uv_flags & MIFF_REGISTER))
+ {
+
+ /*
+ * 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.this allow receiving mld6 messages too.
+ */
+
+ 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);
+
+ /*
+ * Send a probe via the new vif to look for neighbors.
+ */
+
+ send_pim6_hello( v , pim_hello_holdtime );
+ }
+}
+
+/*
+ * Stop a vif (either physical interface, tunnel or
+ * register.) If we are running only PIM we don't have tunnels.
+ */
+
+
+void stop_vif( vifi_t vifi )
+{
+ struct uvif *v;
+ struct listaddr *a;
+ register pim_nbr_entry_t *n;
+ register pim_nbr_entry_t *next;
+ struct vif_acl *acl;
+
+
+ /*
+ * TODO: make sure that the kernel viftable is
+ * consistent with the daemon table
+ */
+
+ v=&uvifs[vifi];
+ if( !( v->uv_flags&MIFF_REGISTER ) )
+ {
+ 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;
+ if( !(v->uv_flags & MIFF_REGISTER ))
+ {
+ RESET_TIMER(v->uv_pim_hello_timer);
+ RESET_TIMER(v->uv_jp_timer);
+ RESET_TIMER(v->uv_gq_timer);
+
+ 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;
+
+ IF_DEBUG(DEBUG_IF)
+ log( LOG_DEBUG ,0,"%s goes down , vif #%u out of service" , v->uv_name , vifi);
+}
+
+/*
+ * Update the register vif in the multicast routing daemon and the
+ * kernel because the interface used initially to get its local address
+ * is DOWN. register_vifi is the index to the Register vif which needs
+ * to be updated. As a result the Register vif has a new uv_lcl_addr and
+ * is UP (virtually :))
+ */
+int
+update_reg_vif( vifi_t register_vifi )
+{
+ register struct uvif *v;
+ register vifi_t vifi;
+
+ /* Find the first useable vif with solid physical background */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL
+ | MIFF_REGISTER))
+ continue;
+ /* Found. Stop the bogus Register vif first */
+ stop_vif(register_vifi);
+ uvifs[register_vifi].uv_linklocal->pa_addr =
+ uvifs[vifi].uv_linklocal->pa_addr;
+ start_vif(register_vifi);
+ IF_DEBUG(DEBUG_PIM_REGISTER | DEBUG_IF)
+ log(LOG_NOTICE, 0, "%s has come up; vif #%u now in service",
+ uvifs[register_vifi].uv_name, register_vifi);
+ return 0;
+ }
+ vifs_down = TRUE;
+ log(LOG_WARNING, 0, "Cannot start Register vif: %s",
+ uvifs[vifi].uv_name);
+ return(-1);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Check if the interface exists in the mif table. If true
+ * return the highest address of the interface else return NULL.
+ */
+struct sockaddr_in6 *
+local_iface(char *ifname)
+{
+ register struct uvif *v;
+ vifi_t vifi;
+ 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;
+ if(EQUAL(v->uv_name, ifname))
+ {
+ for(p=v->uv_addrs; p!=NULL; p=p->pa_next)
+ {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr)&&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
+ /*
+ * If this is the first global address
+ * or larger than the current MAX global
+ * address, remember it.
+ */
+ if (pmax == NULL ||
+ inet6_lessthan(&pmax->pa_addr,
+ &p->pa_addr))
+ pmax = p;
+ }
+ }
+ if (pmax)
+ return(&pmax->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=TRUE;
+
+ /* TODO: Check all potential interfaces!!! */
+ /* Check the physical and tunnels only */
+ for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
+ {
+ if( v->uv_flags & ( VIFF_DISABLED|MIFF_REGISTER ) )
+ 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 ; vif #%u taken out of service",
+ v->uv_name , vifi );
+ stop_vif ( vifi );
+ vifs_down = TRUE;
+ }
+ }
+ }
+
+ /* Check the register(s) vif(s) */
+ for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
+ {
+ register vifi_t vifi2;
+ register struct uvif *v2;
+ int found;
+
+ if( !(v->uv_flags & MIFF_REGISTER ) )
+ continue;
+ else
+ {
+ found=0;
+
+ /* Find a physical vif with the same IP address as the
+ * Register vif.
+ */
+ for( vifi2=0 , v2=uvifs ; vifi2<numvifs ; ++vifi2 , ++v2 )
+ {
+ if( v2->uv_flags & ( VIFF_DISABLED|VIFF_DOWN|VIFF_TUNNEL|MIFF_REGISTER ))
+ continue;
+ if( IN6_ARE_ADDR_EQUAL( &v->uv_linklocal->pa_addr.sin6_addr,
+ &v2->uv_linklocal->pa_addr.sin6_addr ))
+ {
+ found=1;
+ break;
+ }
+ }
+ if(!found)
+ /* The physical interface with the IP address as the Register
+ * vif is probably DOWN. Get a replacement.
+ */
+ update_reg_vif( vifi );
+ }
+ }
+ 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|MIFF_REGISTER))
+ 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 | MIFF_REGISTER))
+ 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 |MIFF_REGISTER))
+ 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);
+}
+
+int
+vif_forwarder(if_set *p1 , if_set *p2)
+{
+ int idx;
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ if (p1->ifs_bits[idx] & p2->ifs_bits[idx])
+ return(TRUE);
+
+ }
+
+ /* (p1 & p2) is empty. We're not the forwarder */
+ return(FALSE);
+}
+
+if_set *
+vif_and(if_set *p1 , if_set *p2, if_set *result)
+{
+ int idx;
+
+ IF_ZERO(result);
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ result->ifs_bits[idx] = p1->ifs_bits[idx] & p2->ifs_bits[idx];
+ }
+
+ return(result);
+}
+
+if_set *
+vif_xor(if_set *p1 , if_set *p2, if_set *result)
+{
+ int idx;
+
+ IF_ZERO(result);
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ result->ifs_bits[idx] =
+ p1->ifs_bits[idx] ^ p2->ifs_bits[idx];
+ }
+
+ return(result);
+}
+/*
+ * 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);
+ }
+ }
+}
+
+struct uvif *
+find_vif(ifname)
+ char *ifname;
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) {
+ if (strcasecmp(v->uv_name, ifname) == 0)
+ return(v);
+ }
+
+ return(NULL);
+}
diff --git a/usr.sbin/pim6sd/vif.h b/usr.sbin/pim6sd/vif.h
new file mode 100644
index 0000000..f3e3495
--- /dev/null
+++ b/usr.sbin/pim6sd/vif.h
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#ifndef VIF_H
+#define VIF_H
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet/ip_mroute.h>
+#include "defs.h"
+
+extern int total_interfaces;
+extern int udp_socket;
+extern struct uvif uvifs[];
+extern vifi_t numvifs;
+extern int vifs_down;
+extern int phys_vif;
+extern vifi_t reg_vif_num;
+
+#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
+#define DEFAULT_METRIC 1
+#define VIFF_DOWN 0x000100
+#define VIFF_DISABLED 0x000200
+#define VIFF_QUERIER 0x000400
+#define VIFF_REXMIT_PRUNES 0x004000
+#define VIFF_DR 0x040000
+#define VIFF_NONBRS 0x080000
+#define VIFF_PIM_NBR 0x200000
+#define VIFF_POINT_TO_POINT 0x400000
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) *8
+
+
+extern if_set if_nullset;
+#define IF_ISEMPTY(p) (memcmp((p), &if_nullset, sizeof(if_nullset)) == 0)
+#define IF_SAME(p1, p2) (memcmp((p1),(p2),sizeof(*(p1))) == 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]; \
+ }\
+ }
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ struct sockaddr_in6 *vfe_addr;
+ struct in6_addr vfe_mask;
+ int vfe_flags;
+#define VFRF_EXACT 0x0001
+};
+
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+#define VFF_BIDIR 1
+
+struct vif_filter {
+ int vf_type;
+ int vf_flags;
+ struct vf_element *vf_filter;
+};
+
+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 */
+ 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
+
+/*
+ * 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;
+ u_char uv_metric; /* VIFF_ flags defined below */
+ u_char uv_admetric; /* advertised cost of this vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
+
+ struct phaddr *uv_linklocal; /* link-local address of this vif */
+ struct sockaddr_in6 uv_rmt_addr;/* remote end-point addr (tunnels only) */
+ 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 lisaddr *uv_dvmrp_neighbors;
+ 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_leaftimer; /* time until this vif is considrd leaf */
+ struct phaddr *uv_addrs; /* Additional addresses on this vif */
+ struct vif_filter *uvfilter; /* 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 */
+ u_int16 uv_jp_timer; /* Join/Prune 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 nbr routers */
+
+ void *config_attr; /* temporary buffer while parsing config */
+
+ /* the followings are to collect statistics */
+ /* incoming PIM6 packets on this interface */
+ u_quad_t uv_in_pim6_hello;
+ u_quad_t uv_in_pim6_join_prune;
+ u_quad_t uv_in_pim6_bootsrap;
+ u_quad_t uv_in_pim6_assert;
+ /* outgoing PIM6 packets on this interface */
+ u_quad_t uv_out_pim6_hello;
+ u_quad_t uv_out_pim6_join_prune;
+ u_quad_t uv_out_pim6_bootsrap;
+ u_quad_t uv_out_pim6_assert;
+ /* incoming MLD packets on this interface */
+ u_quad_t uv_in_mld_query;
+ u_quad_t uv_in_mld_report;
+ u_quad_t uv_in_mld_done;
+ /* outgoing MLD packets on this interface */
+ u_quad_t uv_out_mld_query;
+ u_quad_t uv_out_mld_report;
+ u_quad_t uv_out_mld_done;
+ /* statistics about the forwarding cache in kernel */
+ u_quad_t uv_cache_miss;
+ u_quad_t uv_cache_notcreated;
+ /* occurrences of timeouts */
+ u_quad_t uv_pim6_nbr_timo;
+ u_quad_t uv_listener_timo;
+ u_quad_t uv_outif_timo; /* outgoing interfaces timers */
+};
+
+struct phaddr {
+ struct phaddr *pa_next;
+ struct sockaddr_in6 pa_addr;
+ struct sockaddr_in6 pa_prefix;
+ struct in6_addr pa_subnetmask;
+};
+
+
+/* The Access Control List (list with scoped addresses) member */
+#define VIFF_NOLISTENER 0x800000 /* no listener on the link */
+
+struct vif_acl {
+ struct vif_acl *acl_next;
+ struct sockaddr_in6 acl_addr;
+ struct in6_addr acl_mask;
+};
+
+/*
+ * 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 */
+};
+
+
+
+
+extern void init_vifs __P(());
+extern void stop_all_vifs __P(());
+extern void check_vif_state __P(());
+struct sockaddr_in6 * max_global_address();
+struct sockaddr_in6 * uv_global __P(());
+extern vifi_t local_address __P((struct sockaddr_in6 *src));
+struct sockaddr_in6 * local_iface( char *ifname );
+extern vifi_t find_vif_direct __P((struct sockaddr_in6 *src));
+extern vifi_t find_vif_direct_local __P((struct sockaddr_in6 *src));
+extern int vif_forwarder __P((if_set *p1 ,if_set *p2));
+extern if_set *vif_and __P((if_set *p1, if_set *p2, if_set *result));
+extern if_set *vif_xor __P((if_set *p1, if_set *p2, if_set *result));
+extern struct uvif *find_vif __P((char *ifname));
+#endif
diff --git a/usr.sbin/pim6sd/vmbuf.h b/usr.sbin/pim6sd/vmbuf.h
new file mode 100644
index 0000000..01fa8cd
--- /dev/null
+++ b/usr.sbin/pim6sd/vmbuf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 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$
+ */
+/* YIPS @(#)$Id: vmbuf.h,v 1.1 1999/10/29 09:04:55 jinmei Exp $ */
+
+typedef struct _vchar_ {
+ u_int32_t t; /* type of the value */
+ size_t l; /* length of the value */
+ caddr_t v; /* place holder to the value in buffer */
+#if 0
+ caddr_t v0; /* pointer to the buffer - not used any more */
+#endif
+} vchar_t;
+
+extern vchar_t *vmalloc(size_t);
+extern vchar_t *vrealloc(vchar_t *, size_t);
+extern void vfree(vchar_t *);
+extern vchar_t *vdup(vchar_t *);
+extern int pvdump(vchar_t *);
+
+#define VREALLOC(ptr, size) ((ptr) = vrealloc((ptr), (size)))
+
OpenPOWER on IntegriCloud