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