summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/conf/files1
-rw-r--r--sys/netinet6/in6_proto.c6
-rw-r--r--usr.bin/netstat/Makefile2
-rw-r--r--usr.bin/netstat/main.c6
-rw-r--r--usr.bin/netstat/mroute.c2
-rw-r--r--usr.bin/netstat/mroute6.c235
-rw-r--r--usr.bin/netstat/netstat.h2
-rw-r--r--usr.sbin/Makefile2
-rw-r--r--usr.sbin/pim6dd/LICENSE.mrouted49
-rw-r--r--usr.sbin/pim6dd/LICENSE.pimd48
-rw-r--r--usr.sbin/pim6dd/Makefile14
-rw-r--r--usr.sbin/pim6dd/VERSION2
-rw-r--r--usr.sbin/pim6dd/callout.c256
-rw-r--r--usr.sbin/pim6dd/config.c681
-rw-r--r--usr.sbin/pim6dd/debug.c500
-rw-r--r--usr.sbin/pim6dd/debug.h121
-rw-r--r--usr.sbin/pim6dd/defs.h597
-rw-r--r--usr.sbin/pim6dd/inet6.c288
-rw-r--r--usr.sbin/pim6dd/kern.c415
-rw-r--r--usr.sbin/pim6dd/main.c719
-rw-r--r--usr.sbin/pim6dd/mld6.c537
-rw-r--r--usr.sbin/pim6dd/mld6.h45
-rw-r--r--usr.sbin/pim6dd/mld6_proto.c532
-rw-r--r--usr.sbin/pim6dd/mrt.c803
-rw-r--r--usr.sbin/pim6dd/mrt.h228
-rw-r--r--usr.sbin/pim6dd/pathnames.h63
-rw-r--r--usr.sbin/pim6dd/pim6.c436
-rw-r--r--usr.sbin/pim6dd/pim6_proto.c1598
-rw-r--r--usr.sbin/pim6dd/pim6dd.8112
-rw-r--r--usr.sbin/pim6dd/pim6dd.conf.5157
-rw-r--r--usr.sbin/pim6dd/pimdd.h553
-rw-r--r--usr.sbin/pim6dd/route.c659
-rw-r--r--usr.sbin/pim6dd/routesock.c373
-rw-r--r--usr.sbin/pim6dd/timer.c322
-rw-r--r--usr.sbin/pim6dd/trace.c540
-rw-r--r--usr.sbin/pim6dd/trace.h204
-rw-r--r--usr.sbin/pim6dd/vers.c2
-rw-r--r--usr.sbin/pim6dd/vif.c528
-rw-r--r--usr.sbin/pim6dd/vif.h292
-rw-r--r--usr.sbin/pim6sd/BUGS.TODO126
-rw-r--r--usr.sbin/pim6sd/BUGS.V610
-rw-r--r--usr.sbin/pim6sd/LICENSE.mrouted49
-rw-r--r--usr.sbin/pim6sd/LICENSE.pim6dd32
-rw-r--r--usr.sbin/pim6sd/LICENSE.pim6sd47
-rw-r--r--usr.sbin/pim6sd/LICENSE.pimd48
-rw-r--r--usr.sbin/pim6sd/Makefile49
-rw-r--r--usr.sbin/pim6sd/README85
-rw-r--r--usr.sbin/pim6sd/README.first15
-rw-r--r--usr.sbin/pim6sd/callout.c316
-rw-r--r--usr.sbin/pim6sd/callout.h62
-rw-r--r--usr.sbin/pim6sd/cfparse.h55
-rw-r--r--usr.sbin/pim6sd/cfparse.y960
-rw-r--r--usr.sbin/pim6sd/cftoken.l657
-rw-r--r--usr.sbin/pim6sd/config.c1609
-rw-r--r--usr.sbin/pim6sd/config.h73
-rw-r--r--usr.sbin/pim6sd/crc.c97
-rw-r--r--usr.sbin/pim6sd/crc.h48
-rw-r--r--usr.sbin/pim6sd/debug.c910
-rw-r--r--usr.sbin/pim6sd/debug.h153
-rw-r--r--usr.sbin/pim6sd/defs.h82
-rw-r--r--usr.sbin/pim6sd/inet6.c283
-rw-r--r--usr.sbin/pim6sd/inet6.h76
-rw-r--r--usr.sbin/pim6sd/kern.c413
-rw-r--r--usr.sbin/pim6sd/kern.h78
-rw-r--r--usr.sbin/pim6sd/main.c767
-rw-r--r--usr.sbin/pim6sd/mld6.c555
-rw-r--r--usr.sbin/pim6sd/mld6.h90
-rw-r--r--usr.sbin/pim6sd/mld6_proto.c632
-rw-r--r--usr.sbin/pim6sd/mld6_proto.h72
-rw-r--r--usr.sbin/pim6sd/mrt.c1495
-rw-r--r--usr.sbin/pim6sd/mrt.h341
-rw-r--r--usr.sbin/pim6sd/mtrace6/Makefile86
-rw-r--r--usr.sbin/pim6sd/mtrace6/mtrace6.8115
-rw-r--r--usr.sbin/pim6sd/mtrace6/mtrace6.c661
-rw-r--r--usr.sbin/pim6sd/pathnames.h81
-rw-r--r--usr.sbin/pim6sd/pim6.c507
-rw-r--r--usr.sbin/pim6sd/pim6.h70
-rw-r--r--usr.sbin/pim6sd/pim6_proto.c4126
-rw-r--r--usr.sbin/pim6sd/pim6_proto.h103
-rw-r--r--usr.sbin/pim6sd/pim6sd.8149
-rw-r--r--usr.sbin/pim6sd/pim6sd.conf.5330
-rw-r--r--usr.sbin/pim6sd/pim6sd.conf.sample107
-rwxr-xr-xusr.sbin/pim6sd/pim6stat89
-rw-r--r--usr.sbin/pim6sd/pim6stat.192
-rw-r--r--usr.sbin/pim6sd/pimd.h527
-rw-r--r--usr.sbin/pim6sd/route.c1180
-rw-r--r--usr.sbin/pim6sd/route.h85
-rw-r--r--usr.sbin/pim6sd/routesock.c428
-rw-r--r--usr.sbin/pim6sd/routesock.h57
-rw-r--r--usr.sbin/pim6sd/rp.c1210
-rw-r--r--usr.sbin/pim6sd/rp.h123
-rw-r--r--usr.sbin/pim6sd/timer.c1286
-rw-r--r--usr.sbin/pim6sd/timer.h102
-rw-r--r--usr.sbin/pim6sd/trace.c559
-rw-r--r--usr.sbin/pim6sd/trace.h211
-rw-r--r--usr.sbin/pim6sd/var.h62
-rw-r--r--usr.sbin/pim6sd/vers.c2
-rw-r--r--usr.sbin/pim6sd/vif.c824
-rw-r--r--usr.sbin/pim6sd/vif.h277
-rw-r--r--usr.sbin/pim6sd/vmbuf.h49
100 files changed, 35703 insertions, 10 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 628b794..1df944b 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -723,6 +723,7 @@ netinet6/in6_gif.c optional gif inet6
netinet6/ip6_forward.c optional inet6
netinet6/in6_ifattach.c optional inet6
netinet6/ip6_input.c optional inet6
+netinet6/ip6_mroute.c optional inet6
netinet6/ip6_output.c optional inet6
netinet6/in6_pcb.c optional inet6
netinet6/in6_prefix.c optional inet6
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index ea43e53..efe0aea 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -215,6 +215,12 @@ struct ip6protosw inet6sw[] = {
&nousrreqs
},
#endif /* GIF */
+{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR,
+ pim6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+},
/* raw wildcard */
{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR,
rip6_input, rip6_output, 0, rip6_ctloutput,
diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile
index c1f7539..f52e792 100644
--- a/usr.bin/netstat/Makefile
+++ b/usr.bin/netstat/Makefile
@@ -3,7 +3,7 @@
PROG= netstat
SRCS= if.c inet.c inet6.c main.c mbuf.c mroute.c ipx.c route.c \
- unix.c atalk.c netgraph.c # iso.c ns.c tp_astring.c
+ unix.c atalk.c netgraph.c mroute6.c # iso.c ns.c tp_astring.c
CFLAGS+=-Wall
#CFLAGS+=-g
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
index 5f01f8e..6ce6dcd 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -133,7 +133,6 @@ static struct nlist nl[] = {
{ "_ipsecstat" },
#define N_IPSEC6STAT 31
{ "_ipsec6stat" },
-#ifdef notyet
#define N_PIM6STAT 32
{ "_pim6stat" },
#define N_MRT6PROTO 33
@@ -144,7 +143,6 @@ static struct nlist nl[] = {
{ "_mf6ctable" },
#define N_MIF6TABLE 36
{ "_mif6table" },
-#endif
{ "" },
};
@@ -481,22 +479,18 @@ main(argc, argv)
if (af == AF_INET || af == AF_UNSPEC)
mrt_stats(nl[N_MRTSTAT].n_value);
#ifdef INET6
-#ifdef notyet
if (af == AF_INET6 || af == AF_UNSPEC)
mrt6_stats(nl[N_MRT6STAT].n_value);
#endif
-#endif
} else {
if (af == AF_INET || af == AF_UNSPEC)
mroutepr(nl[N_MFCTABLE].n_value,
nl[N_VIFTABLE].n_value);
#ifdef INET6
-#ifdef notyet
if (af == AF_INET6 || af == AF_UNSPEC)
mroute6pr(nl[N_MF6CTABLE].n_value,
nl[N_MIF6TABLE].n_value);
#endif
-#endif
}
exit(0);
}
diff --git a/usr.bin/netstat/mroute.c b/usr.bin/netstat/mroute.c
index 83dbd08..74706c7 100644
--- a/usr.bin/netstat/mroute.c
+++ b/usr.bin/netstat/mroute.c
@@ -82,7 +82,7 @@ mroutepr(mfcaddr, vifaddr)
vifi_t maxvif = 0;
if (mfcaddr == 0 || vifaddr == 0) {
- printf("No multicast routing compiled into this system.\n");
+ printf("No IPv4 multicast routing compiled into this system.\n");
return;
}
diff --git a/usr.bin/netstat/mroute6.c b/usr.bin/netstat/mroute6.c
new file mode 100644
index 0000000..3730a9e
--- /dev/null
+++ b/usr.bin/netstat/mroute6.c
@@ -0,0 +1,235 @@
+/*
+ * 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) 1989 Stephen Deering
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mroute.c 8.2 (Berkeley) 4/28/95
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#define KERNEL 1
+#include <netinet6/ip6_mroute.h>
+#undef KERNEL
+
+#include "netstat.h"
+
+#define WID_ORG (lflag ? 39 : (nflag ? 29 : 18)) /* width of origin column */
+#define WID_GRP (lflag ? 18 : (nflag ? 16 : 18)) /* width of group column */
+
+extern char *routename6 __P((struct sockaddr_in6 *));
+
+void
+mroute6pr(mfcaddr, mifaddr)
+ u_long mfcaddr, mifaddr;
+{
+ struct mf6c *mf6ctable[MF6CTBLSIZ], *mfcp;
+ struct mif6 mif6table[MAXMIFS];
+ struct mf6c mfc;
+ struct rtdetq rte, *rtep;
+ register struct mif6 *mifp;
+ register mifi_t mifi;
+ register int i;
+ register int banner_printed;
+ register int saved_nflag;
+ mifi_t maxmif = 0;
+ long int waitings;
+
+ if (mfcaddr == 0 || mifaddr == 0) {
+ printf("No IPv6 multicast routing compiled into this"
+ "system.\n");
+ return;
+ }
+
+ saved_nflag = nflag;
+ nflag = 1;
+
+ kread(mifaddr, (char *)&mif6table, sizeof(mif6table));
+ banner_printed = 0;
+ for (mifi = 0, mifp = mif6table; mifi < MAXMIFS; ++mifi, ++mifp) {
+ struct ifnet ifnet;
+ char ifname[IFNAMSIZ];
+
+ if (mifp->m6_ifp == NULL)
+ continue;
+
+ kread((u_long)mifp->m6_ifp, (char *)&ifnet, sizeof(ifnet));
+ maxmif = mifi;
+ if (!banner_printed) {
+ printf("\nIPv6 Multicast Interface Table\n"
+ " Mif Rate PhyIF "
+ "Pkts-In Pkts-Out\n");
+ banner_printed = 1;
+ }
+
+ printf(" %2u %4d",
+ mifi, mifp->m6_rate_limit);
+ printf(" %5s", (mifp->m6_flags & MIFF_REGISTER) ?
+ "reg0" : if_indextoname(ifnet.if_index, ifname));
+
+ printf(" %9qu %9qu\n", mifp->m6_pkt_in, mifp->m6_pkt_out);
+ }
+ if (!banner_printed)
+ printf("\nIPv6 Multicast Interface Table is empty\n");
+
+ kread(mfcaddr, (char *)&mf6ctable, sizeof(mf6ctable));
+ banner_printed = 0;
+ for (i = 0; i < MF6CTBLSIZ; ++i) {
+ mfcp = mf6ctable[i];
+ while(mfcp) {
+ kread((u_long)mfcp, (char *)&mfc, sizeof(mfc));
+ if (!banner_printed) {
+ printf ("\nIPv6 Multicast Forwarding Cache\n");
+ printf(" %-*.*s %-*.*s %s",
+ WID_ORG, WID_ORG, "Origin",
+ WID_GRP, WID_GRP, "Group",
+ " Packets Waits In-Mif Out-Mifs\n");
+ banner_printed = 1;
+ }
+
+ printf(" %-*.*s", WID_ORG, WID_ORG,
+ routename6(&mfc.mf6c_origin));
+ printf(" %-*.*s", WID_GRP, WID_GRP,
+ routename6(&mfc.mf6c_mcastgrp));
+ printf(" %9qu", mfc.mf6c_pkt_cnt);
+
+ for (waitings = 0, rtep = mfc.mf6c_stall; rtep; ) {
+ waitings++;
+ kread((u_long)rtep, (char *)&rte, sizeof(rte));
+ rtep = rte.next;
+ }
+ printf(" %3ld", waitings);
+
+ if (mfc.mf6c_parent == MF6C_INCOMPLETE_PARENT)
+ printf(" --- ");
+ else
+ printf(" %3d ", mfc.mf6c_parent);
+ for (mifi = 0; mifi <= maxmif; mifi++) {
+ if (IF_ISSET(mifi, &mfc.mf6c_ifset))
+ printf(" %u", mifi);
+ }
+ printf("\n");
+
+ mfcp = mfc.mf6c_next;
+ }
+ }
+ if (!banner_printed)
+ printf("\nIPv6 Multicast Routing Table is empty\n");
+
+ printf("\n");
+ nflag = saved_nflag;
+}
+
+void
+mrt6_stats(mstaddr)
+ u_long mstaddr;
+{
+ struct mrt6stat mrtstat;
+
+ if (mstaddr == 0) {
+ printf("No IPv6 multicast routing compiled into this"
+ "system.\n");
+ return;
+ }
+
+ kread(mstaddr, (char *)&mrtstat, sizeof(mrtstat));
+ printf("IPv6 multicast forwarding:\n");
+ printf(" %10qu multicast forwarding cache lookup%s\n",
+ mrtstat.mrt6s_mfc_lookups, plural(mrtstat.mrt6s_mfc_lookups));
+ printf(" %10qu multicast forwarding cache miss%s\n",
+ mrtstat.mrt6s_mfc_misses, plurales(mrtstat.mrt6s_mfc_misses));
+ printf(" %10qu upcall%s to mrouted\n",
+ mrtstat.mrt6s_upcalls, plural(mrtstat.mrt6s_upcalls));
+ printf(" %10qu upcall queue overflow%s\n",
+ mrtstat.mrt6s_upq_ovflw, plural(mrtstat.mrt6s_upq_ovflw));
+ printf(" %10qu upcall%s dropped due to full socket buffer\n",
+ mrtstat.mrt6s_upq_sockfull, plural(mrtstat.mrt6s_upq_sockfull));
+ printf(" %10qu cache cleanup%s\n",
+ mrtstat.mrt6s_cache_cleanups, plural(mrtstat.mrt6s_cache_cleanups));
+ printf(" %10qu datagram%s with no route for origin\n",
+ mrtstat.mrt6s_no_route, plural(mrtstat.mrt6s_no_route));
+ printf(" %10qu datagram%s arrived with bad tunneling\n",
+ mrtstat.mrt6s_bad_tunnel, plural(mrtstat.mrt6s_bad_tunnel));
+ printf(" %10qu datagram%s could not be tunneled\n",
+ mrtstat.mrt6s_cant_tunnel, plural(mrtstat.mrt6s_cant_tunnel));
+ printf(" %10qu datagram%s arrived on wrong interface\n",
+ mrtstat.mrt6s_wrong_if, plural(mrtstat.mrt6s_wrong_if));
+ printf(" %10qu datagram%s selectively dropped\n",
+ mrtstat.mrt6s_drop_sel, plural(mrtstat.mrt6s_drop_sel));
+ printf(" %10qu datagram%s dropped due to queue overflow\n",
+ mrtstat.mrt6s_q_overflow, plural(mrtstat.mrt6s_q_overflow));
+ printf(" %10qu datagram%s dropped for being too large\n",
+ mrtstat.mrt6s_pkt2large, plural(mrtstat.mrt6s_pkt2large));
+}
diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h
index 5ae8c7f..3b435c2 100644
--- a/usr.bin/netstat/netstat.h
+++ b/usr.bin/netstat/netstat.h
@@ -78,11 +78,9 @@ void ip6_stats __P((u_long, char *));
void ip6_ifstats __P((char *));
void icmp6_stats __P((u_long, char *));
void icmp6_ifstats __P((char *));
-#ifdef notyet
void pim6_stats __P((u_long, char *));
void mroute6pr __P((u_long, u_long));
void mrt6_stats __P((u_long));
-#endif
#endif /*INET6*/
void bdg_stats __P((u_long, char *));
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 5e7433d..f2a1e55 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -59,6 +59,8 @@ SUBDIR= IPXrouted \
pccard \
pciconf \
periodic \
+ pim6dd \
+ pim6sd \
pkg_install \
pnpinfo \
portmap \
diff --git a/usr.sbin/pim6dd/LICENSE.mrouted b/usr.sbin/pim6dd/LICENSE.mrouted
new file mode 100644
index 0000000..e03eb82
--- /dev/null
+++ b/usr.sbin/pim6dd/LICENSE.mrouted
@@ -0,0 +1,49 @@
+$FreeBSD$
+
+The mrouted program is covered by the following license. Use of the
+mrouted program represents acceptance of these terms and conditions.
+
+1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
+to use, copy and modify the computer software ``mrouted'' (hereinafter
+called the ``Program''), upon the terms and conditions hereinafter set
+out and until Licensee discontinues use of the Licensed Program.
+
+2. LICENSEE acknowledges that the Program is a research tool still in
+the development state, that it is being supplied ``as is,'' without any
+accompanying services from STANFORD, and that this license is entered
+into in order to encourage scientific collaboration aimed at further
+development and application of the Program.
+
+3. LICENSEE may copy the Program and may sublicense others to use object
+code copies of the Program or any derivative version of the Program.
+All copies must contain all copyright and other proprietary notices found
+in the Program as provided by STANFORD. Title to copyright to the
+Program remains with STANFORD.
+
+4. LICENSEE may create derivative versions of the Program. LICENSEE
+hereby grants STANFORD a royalty-free license to use, copy, modify,
+distribute and sublicense any such derivative works. At the time
+LICENSEE provides a copy of a derivative version of the Program to a
+third party, LICENSEE shall provide STANFORD with one copy of the source
+code of the derivative version at no charge to STANFORD.
+
+5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
+By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
+OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
+for any liability nor for any direct, indirect or consequential damages
+with respect to any claim by LICENSEE or any third party on account of or
+arising from this Agreement or use of the Program.
+
+6. This agreement shall be construed, interpreted and applied in
+accordance with the State of California and any legal action arising
+out of this Agreement or use of the Program shall be filed in a court
+in the State of California.
+
+7. Nothing in this Agreement shall be construed as conferring rights to
+use in advertising, publicity or otherwise any trademark or the name
+of ``Stanford''.
+
+The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
diff --git a/usr.sbin/pim6dd/LICENSE.pimd b/usr.sbin/pim6dd/LICENSE.pimd
new file mode 100644
index 0000000..384ad23
--- /dev/null
+++ b/usr.sbin/pim6dd/LICENSE.pimd
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: LICENSE.pimd,v 1.1.1.1 1999/08/08 23:30:50 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
diff --git a/usr.sbin/pim6dd/Makefile b/usr.sbin/pim6dd/Makefile
new file mode 100644
index 0000000..b0fdcd3
--- /dev/null
+++ b/usr.sbin/pim6dd/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= pim6dd
+MAN5= pim6dd.conf.5
+MAN8= pim6dd.8
+
+SRCS= mld6.c mld6_proto.c\
+ inet6.c kern.c main.c config.c debug.c routesock.c vers.c callout.c\
+ route.c vif.c timer.c mrt.c pim6.c pim6_proto.c trace.c
+
+CFLAGS+= -DINET6 -DPIM -DIOCTL_OK_ON_RAW_SOCKET
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pim6dd/VERSION b/usr.sbin/pim6dd/VERSION
new file mode 100644
index 0000000..69e5262
--- /dev/null
+++ b/usr.sbin/pim6dd/VERSION
@@ -0,0 +1,2 @@
+# $FreeBSD$
+0.2.1.0-alpha15
diff --git a/usr.sbin/pim6dd/callout.c b/usr.sbin/pim6dd/callout.c
new file mode 100644
index 0000000..6820dcd
--- /dev/null
+++ b/usr.sbin/pim6dd/callout.c
@@ -0,0 +1,256 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted". Use of the mrouted program represents acceptance
+ * of the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * callout.c,v 3.8.4.5 1997/05/16 20:18:25 fenner Exp
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+/* the code below implements a callout queue */
+static int id = 0;
+static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
+
+struct timeout_q {
+ struct timeout_q *next; /* next event */
+ int id;
+ cfunc_t func; /* function to call */
+ void *data; /* func's data */
+ int time; /* time offset to next event*/
+};
+
+#ifdef CALLOUT_DEBUG
+static void print_Q __P((void));
+#else
+#define print_Q()
+#endif
+
+void
+callout_init()
+{
+ if (Q) {
+ log(LOG_ERR, 0, "timer used before callout_init()");
+ exit(1);
+ }
+ Q = (struct timeout_q *) 0;
+}
+
+void
+free_all_callouts()
+{
+ struct timeout_q *p;
+
+ while (Q) {
+ p = Q;
+ Q = Q->next;
+ free(p);
+ }
+}
+
+
+/*
+ * elapsed_time seconds have passed; perform all the events that should
+ * happen.
+ */
+void
+age_callout_queue(elapsed_time)
+ int elapsed_time;
+{
+ struct timeout_q *ptr, *expQ;
+
+#ifdef CALLOUT_DEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "aging queue (elapsed time %d):", elapsed_time);
+ print_Q();
+#endif
+
+ expQ = Q;
+ ptr = NULL;
+
+ while (Q) {
+ if (Q->time > elapsed_time) {
+ Q->time -= elapsed_time;
+ if (ptr) {
+ ptr->next = NULL;
+ break;
+ }
+ return;
+ } else {
+ elapsed_time -= Q->time;
+ ptr = Q;
+ Q = Q->next;
+ }
+ }
+
+ /* handle queue of expired timers */
+ while (expQ) {
+ ptr = expQ;
+ if (ptr->func)
+ ptr->func(ptr->data);
+ expQ = expQ->next;
+ free(ptr);
+ }
+}
+
+/*
+ * Return in how many seconds age_callout_queue() would like to be called.
+ * Return -1 if there are no events pending.
+ */
+int
+timer_nextTimer()
+{
+ if (Q) {
+ if (Q->time < 0) {
+ log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
+ Q->time);
+ return 0;
+ }
+ return Q->time;
+ }
+ return -1;
+}
+
+/*
+ * sets the timer
+ */
+int
+timer_setTimer(delay, action, data)
+ int delay; /* number of units for timeout */
+ cfunc_t action; /* function to be called on timeout */
+ void *data; /* what to call the timeout function with */
+{
+ struct timeout_q *ptr, *node, *prev;
+
+#ifdef CALLOUT_DEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "setting timer:");
+ print_Q();
+#endif
+
+ /* create a node */
+ node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
+ if (node == 0) {
+ log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
+ return -1;
+ }
+ node->func = action;
+ node->data = data;
+ node->time = delay;
+ node->next = 0;
+ node->id = ++id;
+
+ prev = ptr = Q;
+
+ /* insert node in the queue */
+
+ /* if the queue is empty, insert the node and return */
+ if (!Q)
+ Q = node;
+ else {
+ /* chase the pointer looking for the right place */
+ while (ptr) {
+
+ if (delay < ptr->time) {
+ /* right place */
+
+ node->next = ptr;
+ if (ptr == Q)
+ Q = node;
+ else
+ prev->next = node;
+ ptr->time -= node->time;
+ return node->id;
+ } else {
+ /* keep moving */
+
+ delay -= ptr->time; node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+ prev->next = node;
+ }
+ return node->id;
+}
+
+/* returns the time until the timer is scheduled */
+int
+timer_leftTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left = 0;
+
+ if (!timer_id)
+ return -1;
+
+ for (ptr = Q; ptr; ptr = ptr->next) {
+ left += ptr->time;
+ if (ptr->id == timer_id)
+ return left;
+ }
+ return -1;
+}
+
+/* clears the associated timer */
+void
+timer_clearTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr, *prev;
+
+ if (!timer_id)
+ return;
+
+ prev = ptr = Q;
+
+ /*
+ * find the right node, delete it. the subsequent node's time
+ * gets bumped up
+ */
+
+ while (ptr) {
+ if (ptr->id == timer_id) {
+ /* got the right node */
+
+ /* unlink it from the queue */
+ if (ptr == Q)
+ Q = Q->next;
+ else
+ prev->next = ptr->next;
+
+ /* increment next node if any */
+ if (ptr->next != 0)
+ (ptr->next)->time += ptr->time;
+
+ if (ptr->data)
+ free(ptr->data);
+ free(ptr);
+ return;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+}
+
+#ifdef CALLOUT_DEBUG
+/*
+ * debugging utility
+ */
+static void
+print_Q()
+{
+ struct timeout_q *ptr;
+
+ IF_DEBUG(DEBUG_TIMEOUT)
+ for (ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time);
+}
+#endif /* CALLOUT_DEBUG */
diff --git a/usr.sbin/pim6dd/config.c b/usr.sbin/pim6dd/config.c
new file mode 100644
index 0000000..c52e06f
--- /dev/null
+++ b/usr.sbin/pim6dd/config.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: config.c,v 1.3 1999/12/30 09:23:08 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+
+/*
+ * Forward declarations.
+ */
+static char *next_word __P((char **));
+static int parse_phyint __P((char *s));
+static void add_phaddr __P((struct uvif *v, struct sockaddr_in6 *addr,
+ struct in6_addr *mask));
+static mifi_t ifname2mifi __P((char *ifname));
+static int wordToOption __P((char *));
+static int parse_filter __P((char *s));
+#if 0 /* not used */
+static int parse_default_source_metric __P((char *));
+static int parse_default_source_preference __P((char *));
+#endif
+
+/*
+ * Query the kernel to find network interfaces that are multicast-capable
+ * and install them in the uvifs array.
+ */
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp, *ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n;
+ struct sockaddr_in6 addr;
+ struct in6_addr mask, prefix;
+ short flags;
+ int num_ifreq = 64;
+ struct ifconf ifc;
+
+ total_interfaces = 0; /* The total number of physical interfaces */
+
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = calloc(ifc.ifc_len, sizeof(char));
+ while (ifc.ifc_buf) {
+ caddr_t newbuf;
+
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if ((num_ifreq * sizeof(struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
+
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ newbuf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ if (newbuf == NULL)
+ free(ifc.ifc_buf);
+ ifc.ifc_buf = newbuf;
+ }
+ if (ifc.ifc_buf == NULL)
+ log(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory");
+
+ ifrp = (struct ifreq *)ifc.ifc_buf;
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ /*
+ * Loop through all of the interfaces.
+ */
+ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+#ifdef HAVE_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+#else
+ n = sizeof(*ifrp);
+#endif /* HAVE_SA_LEN */
+
+ /*
+ * Ignore any interface for an address family other than IPv6.
+ */
+ if (ifrp->ifr_addr.sa_family != AF_INET6) {
+ total_interfaces++; /* Eventually may have IPv6 address later */
+ continue;
+ }
+
+ memcpy(&addr, &ifrp->ifr_addr, sizeof(struct sockaddr_in6));
+
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ memcpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));
+ memcpy(ifr6.ifr_name, ifrp->ifr_name, sizeof(ifr6.ifr_name));
+
+ /*
+ * Ignore loopback interfaces and interfaces that do not
+ * support multicast.
+ */
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+ flags = ifr.ifr_flags;
+ if ((flags & (IFF_LOOPBACK | IFF_MULTICAST)) != IFF_MULTICAST)
+ continue;
+
+ /*
+ * Get netmask of the address.
+ */
+ ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr;
+ if (ioctl(udp_socket, SIOCGIFNETMASK_IN6, (char *)&ifr6) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK_IN6 for %s",
+ ifr6.ifr_name);
+ memcpy(&mask, &ifr6.ifr_addr.sin6_addr, sizeof(mask));
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr)) {
+ addr.sin6_scope_id = if_nametoindex(ifrp->ifr_name);
+#ifdef __KAME__
+ /*
+ * Hack for KAME kernel. Set sin6_scope_id field of a
+ * link local address and clear the index embedded in
+ * the address.
+ */
+ /* clear interface index */
+ addr.sin6_addr.s6_addr[2] = 0;
+ addr.sin6_addr.s6_addr[3] = 0;
+#endif
+ }
+
+ /*
+ * If the address is connected to the same subnet as one already
+ * installed in the uvifs array, just add the address to the list
+ * of addresses of the uvif.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (strcmp(v->uv_name, ifr.ifr_name) == 0) {
+ add_phaddr(v, &addr, &mask);
+ break;
+ }
+ }
+ if (vifi != numvifs)
+ continue;
+
+ /*
+ * If there is room in the uvifs array, install this interface.
+ */
+ if (numvifs == MAXMIFS) {
+ log(LOG_WARNING, 0, "too many ifs, ignoring %s", ifr.ifr_name);
+ continue;
+ }
+
+ /*
+ * Everyone below is a potential vif interface.
+ * We don't care if it has wrong configuration or not configured
+ * at all.
+ */
+ total_interfaces++;
+
+ v = &uvifs[numvifs];
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+#if 0
+ v->uv_threshold = DEFAULT_THRESHOLD;
+#endif
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+#if 0
+ v->uv_rmt_addr = INADDR_ANY_N;
+#endif
+ v->uv_dst_addr = allpim6routers_group;
+ v->uv_prefix.sin6_addr = prefix;
+ v->uv_subnetmask = mask;
+ strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
+ v->uv_ifindex = if_nametoindex(v->uv_name);
+ v->uv_groups = (struct listaddr *)NULL;
+ v->uv_dvmrp_neighbors = (struct listaddr *)NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = (struct listaddr *)NULL;
+ v->uv_prune_lifetime = 0;
+ v->uv_acl = (struct vif_acl *)NULL;
+ v->uv_leaf_timer = 0;
+ v->uv_addrs = (struct phaddr *)NULL;
+ v->uv_filter = (struct vif_filter *)NULL;
+ v->uv_pim_hello_timer = 0;
+ v->uv_gq_timer = 0;
+ v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL;
+ v->uv_local_pref = default_source_preference;
+ v->uv_local_metric = default_source_metric;
+ add_phaddr(v, &addr, &mask);
+
+ if (flags & IFF_POINTOPOINT)
+ v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT);
+ log(LOG_INFO, 0,
+ "installing %s as if #%u - rate=%d",
+ v->uv_name, numvifs, v->uv_rate_limit);
+ ++numvifs;
+
+ /*
+ * If the interface is not yet up, set the vifs_down flag to
+ * remind us to check again later.
+ */
+ if (!(flags & IFF_UP)) {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+}
+
+static void
+add_phaddr(v, addr, mask)
+ struct uvif *v;
+ struct sockaddr_in6 *addr;
+ struct in6_addr *mask;
+{
+ struct phaddr *pa;
+ int i;
+
+ if ((pa = malloc(sizeof(*pa))) == NULL)
+ log(LOG_ERR, 0, "add_phaddr: memory exhausted");
+
+ memset(pa, 0, sizeof(*pa));
+ pa->pa_addr = *addr;
+ pa->pa_subnetmask = *mask;
+
+ /*
+ * install the prefix of the address derived from the address
+ * and the mask.
+ */
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ pa->pa_prefix.sin6_addr.s6_addr[i] =
+ addr->sin6_addr.s6_addr[i] & mask->s6_addr[i];
+ pa->pa_prefix.sin6_scope_id = addr->sin6_scope_id;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
+ if (v->uv_linklocal) {
+ log(LOG_WARNING, 0,
+ "add_phaddr: found more than one link-local "
+ "address on %s",
+ v->uv_name);
+ }
+ v->uv_linklocal = pa; /* relace anyway */
+ }
+
+ /* link into chain */
+ pa->pa_next = v->uv_addrs;
+ v->uv_addrs = pa;
+}
+
+static mifi_t
+ifname2mifi(ifname)
+ char *ifname;
+{
+ mifi_t mifi;
+ struct uvif *v;
+
+ for (mifi = 0, v = uvifs; mifi < numvifs; ++mifi, ++v) {
+ if (strcmp(v->uv_name, ifname) == 0)
+ break;
+ }
+ return(mifi);
+}
+
+#define UNKNOWN -1
+#define EMPTY 1
+#define PHYINT 2
+#define DEFAULT_SOURCE_METRIC 3
+#define DEFAULT_SOURCE_PREFERENCE 4
+#define FILTER 5
+
+/*
+ * function name: wordToOption
+ * input: char *word, a pointer to the word
+ * output: int; a number corresponding to the code of the word
+ * operation: converts the result of the string comparisons into numerics.
+ * comments: called by config_vifs_from_file()
+ */
+static int
+wordToOption(word)
+ char *word;
+{
+ if (EQUAL(word, ""))
+ return EMPTY;
+ if (EQUAL(word, "phyint"))
+ return PHYINT;
+ if (EQUAL(word, "default_source_metric"))
+ return DEFAULT_SOURCE_METRIC;
+ if (EQUAL(word, "default_source_preference"))
+ return DEFAULT_SOURCE_PREFERENCE;
+ if (EQUAL(word, "filter"))
+ return FILTER;
+ return UNKNOWN;
+}
+
+/*
+ * function name: parse_phyint
+ * input: char *s, pointing to the parsing point of the file
+ * output: int (TRUE if the parsing was successful, o.w. FALSE)
+ * operation: parses the physical interface file configurations, if any.
+ * The general form is:
+ * phyint <ifname> [disable]
+ */
+static int
+parse_phyint(s)
+ char *s;
+{
+ char *w, c, *ifname;
+ vifi_t vifi;
+ struct uvif *v;
+ u_int n;
+
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0, "Missing phyint in %s", configfilename);
+ return(FALSE);
+ } /* if empty */
+ ifname = w;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (vifi == numvifs) {
+ log(LOG_WARNING, 0,
+ "phyint %s in %s is not a configured interface",
+ ifname, configfilename);
+ return(FALSE);
+ } /* if vifi == numvifs */
+
+ if (strcmp(v->uv_name, ifname))
+ continue;
+
+ while (!EQUAL((w = next_word(&s)), "")) {
+ if (EQUAL(w, "disable"))
+ v->uv_flags |= VIFF_DISABLED;
+ else if (EQUAL(w, "nolistener"))
+ v->uv_flags |= VIFF_NOLISTENER;
+ else if(EQUAL(w, "preference")) {
+ if(EQUAL((w = next_word(&s)), ""))
+ log(LOG_WARNING, 0,
+ "Missing preference for phyint %s in %s",
+ ifname, configfilename);
+ else if (sscanf(w, "%u%c", &n, &c) != 1 ||
+ n < 1 || n > 255 )
+ log(LOG_WARNING, 0,
+ "Invalid preference '%s' for phyint %s in %s",
+ w, ifname,
+ configfilename);
+ else {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "Config setting default local preference on %s to %d.",
+ ifname, n);
+ v->uv_local_pref = n;
+ }
+
+ } else if(EQUAL(w, "metric")) {
+ if(EQUAL((w = next_word(&s)), ""))
+ log(LOG_WARNING, 0,
+ "Missing metric for phyint %s in %s",
+ ifname, configfilename);
+ else if (sscanf(w, "%u%c", &n, &c) != 1 ||
+ n < 1 || n > 1024 )
+ log(LOG_WARNING, 0,
+ "Invalid metric '%s' for phyint %s in %s",
+ w, ifname,
+ configfilename);
+ else {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "Config setting default local metric on %s to %d.",
+ ifname, n);
+ v->uv_local_metric = n;
+ }
+ }
+ } /* if not empty */
+ break;
+ }
+ return(TRUE);
+}
+
+static int
+parse_filter(s)
+ char *s;
+{
+ char *w, *groups, *p;
+ mifi_t mifi;
+ struct in6_addr grp1, grp2;
+ if_set filterset;
+ struct mrtfilter *filter;
+ int plen = 0, filtertype;
+
+ if (EQUAL((groups = next_word(&s)), "")) {
+ log(LOG_WARNING, 0, "Missing multicast group in %s",
+ configfilename);
+ return(FALSE);
+ }
+
+ /*
+ * Group address specification. Valid format are the followings.
+ * - Group1-Group2: specifies a numerical range of a scope.
+ * - GroupPrefix/Prefixlen: specifies a prefix of a scope. If the
+ * Prefixlen is omitted, it means the exact match.
+ */
+ if ((p = strchr(groups, '-')) != NULL) { /* Group1-Group2 */
+ char *maddr1, *maddr2;
+
+ maddr1 = groups;
+ maddr2 = p + 1;
+ *p = '\0';
+ if (inet_pton(AF_INET6, maddr1, (void *)&grp1) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp1)) {
+ log(LOG_WARNING, 0, "invalid group address %s", maddr1);
+ return(FALSE);
+ }
+ if (inet_pton(AF_INET6, maddr2, (void *)&grp2) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp2)) {
+ log(LOG_WARNING, 0, "invalid group address %s", maddr2);
+ return(FALSE);
+ }
+ filtertype = FILTER_RANGE;
+ }
+ else if ((p = strchr(groups, '/')) != NULL) { /* GroupPrefix/Plen */
+ char *mprefix = groups;
+ int plen = atoi(p + 1);
+ *p = '\0';
+ if (inet_pton(AF_INET6, mprefix, (void *)&grp1) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp1)) {
+ log(LOG_WARNING, 0, "invalid group prefix %s", mprefix);
+ return(FALSE);
+ }
+ if (plen < 0 || plen > 128) {
+ log(LOG_WARNING, 0, "invalid prefix length %s", p + 1);
+ return(FALSE);
+ }
+ filtertype = FILTER_PREFIX;
+ }
+ else {
+ if (inet_pton(AF_INET6, groups, (void *)&grp1) != 1) {
+ log(LOG_WARNING, 0, "invalid group address %s", groups);
+ return(FALSE);
+ }
+ plen = 128; /* exact match */
+ filtertype = FILTER_PREFIX;
+ }
+
+ IF_ZERO(&filterset);
+ while (!EQUAL((w = next_word(&s)), "")) {
+ if ((mifi = ifname2mifi(w)) == MAXMIFS) {
+ /* XXX: scope consideration?? */
+ log(LOG_WARNING, 0,
+ "phyint %s in %s is not a configured interface",
+ w, configfilename);
+ return(FALSE);
+ }
+
+ IF_SET(mifi, &filterset);
+ }
+ if (IF_ISEMPTY(&filterset)) {
+ log(LOG_WARNING, 0,
+ "filter set is empty. ignore it.");
+ return(FALSE);
+ }
+
+ filter = add_filter(filtertype, &grp1, &grp2, plen);
+ IF_COPY(&filterset, &filter->ifset);
+
+ return(TRUE);
+}
+
+#if 0 /* not used */
+/*
+ * function name: parse_default_source_metric
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source metric, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_metric <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+ */
+static int
+parse_default_source_metric(s)
+ char *s;
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_METRIC;
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0,
+ "Missing default source metric; set to default %u",
+ DEFAULT_LOCAL_METRIC);
+ } else if (sscanf(w, "%u", &value) != 1) {
+ log(LOG_WARNING, 0,
+ "Invalid default source metric; set to default %u",
+ DEFAULT_LOCAL_METRIC);
+ value = DEFAULT_LOCAL_METRIC;
+ }
+ default_source_metric = value;
+ log(LOG_INFO, 0, "default_source_metric is %u", value);
+
+ for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
+ v->uv_local_metric = default_source_metric;
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * function name: parse_default_source_preference
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source preference, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_preference <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+*/
+static int
+parse_default_source_preference(s)
+ char *s;
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_PREF;
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0,
+ "Missing default source preference; set to default %u",
+ DEFAULT_LOCAL_PREF);
+ } else if (sscanf(w, "%u", &value) != 1) {
+ log(LOG_WARNING, 0,
+ "Invalid default source preference; set to default %u",
+ DEFAULT_LOCAL_PREF);
+ value = DEFAULT_LOCAL_PREF;
+ }
+ default_source_preference = value;
+ log(LOG_INFO, 0, "default_source_preference is %u", value);
+
+ for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
+ v->uv_local_pref = default_source_preference;
+ }
+
+ return(TRUE);
+}
+#endif
+
+void
+config_vifs_from_file()
+{
+ FILE *f;
+ char linebuf[100];
+ char *w, *s;
+ struct ifconf ifc;
+ int option;
+ char ifbuf[BUFSIZ];
+
+ if ((f = fopen(configfilename, "r")) == NULL) {
+ if (errno != ENOENT) log(LOG_WARNING, errno, "can't open %s",
+ configfilename);
+ return;
+ }
+
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+ s = linebuf;
+ w = next_word(&s);
+ option = wordToOption(w);
+ switch(option) {
+ case EMPTY:
+ continue;
+ break;
+ case PHYINT:
+ parse_phyint(s);
+ break;
+ case FILTER:
+ parse_filter(s);
+ break;
+ default:
+ log(LOG_WARNING, 0, "unknown command '%s' in %s",
+ w, configfilename);
+ }
+ }
+ fclose(f);
+}
+
+static char *
+next_word(s)
+ char **s;
+{
+ char *w;
+
+ w = *s;
+ while (*w == ' ' || *w == '\t')
+ w++;
+
+ *s = w;
+ for(;;) {
+ switch (**s) {
+ case ' ' :
+ case '\t' :
+ **s = '\0';
+ (*s)++;
+ return(w);
+ case '\n' :
+ case '#' :
+ **s = '\0';
+ return(w);
+ case '\0' :
+ return(w);
+ default :
+ if (isascii(**s) && isupper(**s))
+ **s = tolower(**s);
+ (*s)++;
+ }
+ }
+}
+
diff --git a/usr.sbin/pim6dd/debug.c b/usr.sbin/pim6dd/debug.c
new file mode 100644
index 0000000..0894d5c
--- /dev/null
+++ b/usr.sbin/pim6dd/debug.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: debug.c,v 1.4 1999/09/20 14:28:08 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+extern int haveterminal;
+extern char *progname;
+
+int log_nmsgs = 0;
+unsigned long debug = 0x00000000; /* If (long) is smaller than
+ * 4 bytes, then we are in
+ * trouble.
+ */
+static char dumpfilename[] = _PATH_PIM6D_DUMP;
+static char cachefilename[] = _PATH_PIM6D_CACHE; /* TODO: notused */
+
+
+char *
+packet_kind(proto, type, code)
+ u_int proto, type, code;
+{
+ static char unknown[60];
+
+ switch (proto) {
+ case IPPROTO_ICMPV6:
+ switch (type) {
+ case MLD6_LISTENER_QUERY: return "Multicast Listener Query ";
+ case MLD6_LISTENER_REPORT: return "Multicast Listener Report ";
+ case MLD6_LISTENER_DONE: return "Multicast Listener Done ";
+ default:
+ sprintf(unknown,
+ "UNKNOWN ICMPv6 message: type = 0x%02x, code = 0x%02x ",
+ type, code);
+ return unknown;
+ }
+
+ case IPPROTO_PIM: /* PIM v2 */
+ switch (type) {
+ case PIM_V2_HELLO: return "PIM v2 Hello ";
+ case PIM_V2_REGISTER: return "PIM v2 Register ";
+ case PIM_V2_REGISTER_STOP: return "PIM v2 Register_Stop ";
+ case PIM_V2_JOIN_PRUNE: return "PIM v2 Join/Prune ";
+ case PIM_V2_BOOTSTRAP: return "PIM v2 Bootstrap ";
+ case PIM_V2_ASSERT: return "PIM v2 Assert ";
+ case PIM_V2_GRAFT: return "PIM-DM v2 Graft ";
+ case PIM_V2_GRAFT_ACK: return "PIM-DM v2 Graft_Ack ";
+ case PIM_V2_CAND_RP_ADV: return "PIM v2 Cand. RP Adv. ";
+ default:
+ sprintf(unknown, "UNKNOWN PIM v2 message type =%3d ", type);
+ return unknown;
+ }
+ default:
+ sprintf(unknown, "UNKNOWN proto =%3d ", proto);
+ return unknown;
+ }
+}
+
+
+/*
+ * Used for debugging particular type of messages.
+ */
+int
+debug_kind(proto, type, code)
+ u_int proto, type, code;
+{
+ switch (proto) {
+ case IPPROTO_ICMPV6:
+ switch (type) {
+ case MLD6_LISTENER_QUERY: return DEBUG_MLD;
+ case MLD6_LISTENER_REPORT: return DEBUG_MLD;
+ case MLD6_LISTENER_DONE: return DEBUG_MLD;
+ default: return DEBUG_MLD;
+ }
+ case IPPROTO_PIM: /* PIM v2 */
+ /* TODO: modify? */
+ switch (type) {
+ case PIM_V2_HELLO: return DEBUG_PIM;
+ case PIM_V2_REGISTER: return DEBUG_PIM_REGISTER;
+ case PIM_V2_REGISTER_STOP: return DEBUG_PIM_REGISTER;
+ case PIM_V2_JOIN_PRUNE: return DEBUG_PIM;
+ case PIM_V2_BOOTSTRAP: return DEBUG_PIM_BOOTSTRAP;
+ case PIM_V2_ASSERT: return DEBUG_PIM;
+ case PIM_V2_GRAFT: return DEBUG_PIM;
+ case PIM_V2_GRAFT_ACK: return DEBUG_PIM;
+ case PIM_V2_CAND_RP_ADV: return DEBUG_PIM_CAND_RP;
+ default: return DEBUG_PIM;
+ }
+ default: return 0;
+ }
+ return 0;
+}
+
+
+/*
+ * Some messages are more important than others. This routine
+ * determines the logging level at which to log a send error (often
+ * "No route to host"). This is important when there is asymmetric
+ * reachability and someone is trying to, i.e., mrinfo me periodically.
+ */
+int
+log_level(proto, type, code)
+ u_int proto, type, code;
+{
+ switch (proto) {
+ case IPPROTO_ICMPV6:
+ switch (type) {
+ default:
+ return LOG_WARNING;
+ }
+
+ case IPPROTO_PIM:
+ /* PIM v2 */
+ switch (type) {
+ default:
+ return LOG_INFO;
+ }
+ default:
+ return LOG_WARNING;
+ }
+ return LOG_WARNING;
+}
+
+
+/*
+ * Dump internal data structures to stderr.
+ */
+/* TODO: currently not used
+void
+dump(int i)
+{
+ dump_vifs(stderr);
+ dump_pim_mrt(stderr);
+}
+*/
+
+/*
+ * Dump internal data structures to a file.
+ */
+void
+fdump(i)
+ int i;
+{
+ FILE *fp;
+ fp = fopen(dumpfilename, "w");
+ if (fp != NULL) {
+ dump_vifs(fp);
+ dump_pim_mrt(fp);
+ dump_lcl_grp(fp);
+ (void) fclose(fp);
+ }
+}
+
+/* TODO: dummy, to be used in the future. */
+/*
+ * Dump local cache contents to a file.
+ */
+void
+cdump(i)
+ int i;
+{
+ FILE *fp;
+
+ fp = fopen(cachefilename, "w");
+ if (fp != NULL) {
+ /* TODO: implement it:
+ dump_cache(fp);
+ */
+ (void) fclose(fp);
+ }
+}
+
+void
+dump_vifs(fp)
+ FILE *fp;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ pim_nbr_entry_t *n;
+ struct phaddr *pa;
+ int width;
+ int i;
+
+ fprintf(fp, "\nMulticast Interface Table\n %-4s %-6s %-50s %-14s %s",
+ "Mif", " PhyIF", "Local-Address/Prefixlen", "Flags",
+ "Neighbors\n");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ int firstaddr = 1;
+ for (pa = v->uv_addrs; pa; pa = pa->pa_next) {
+ if (!firstaddr) {
+ fprintf(fp, " %3s %6s %-50s\n", "", "",
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ continue;
+ }
+
+ firstaddr = 0;
+ fprintf(fp, " %-3u %6s %-50s", vifi, v->uv_name,
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ firstaddr = 0;
+#if 0
+ if (v->uv_flags & MIFF_REGISTER)
+ fprintf(fp, "%-16s ", v->uv_name);
+ else
+ fprintf(fp,"%-16.16s ",
+ net6name(&v->uv_prefix.sin6_addr,
+ &v->uv_subnetmask));
+#endif
+ width = 0;
+ if (v->uv_flags & VIFF_DISABLED)
+ fprintf(fp, " DISABLED");
+ if (v->uv_flags & VIFF_NOLISTENER)
+ fprintf(fp, " NOLISTENER");
+ if (v->uv_flags & VIFF_DOWN)
+ fprintf(fp, " DOWN");
+ if (v->uv_flags & VIFF_DR) {
+ fprintf(fp, " DR");
+ width += 3;
+ }
+ if (v->uv_flags & VIFF_PIM_NBR) {
+ fprintf(fp, " PIM");
+ width += 4;
+ }
+#if 0 /* impossible */
+ if (v->uv_flags & VIFF_DVMRP_NBR) {
+ fprintf(fp, " DVMRP");
+ width += 6;
+ }
+#endif
+ if (v->uv_flags & VIFF_NONBRS) {
+ fprintf(fp, " %-12s", "NO-NBR");
+ width += 6;
+ }
+
+ if ((n = v->uv_pim_neighbors) != NULL) {
+ /* Print the first neighbor on the same line */
+ for (i = width; i <= 15; i++)
+ fprintf(fp, " ");
+ fprintf(fp, "%-12s\n",
+ inet6_fmt(&n->address.sin6_addr));
+ for (n = n->next; n != NULL; n = n->next)
+ fprintf(fp, "%64s %-15s\n", "",
+ inet6_fmt(&n->address.sin6_addr));
+
+ }
+ else
+ fprintf(fp, "\n");
+ }
+ }
+ fprintf(fp, "\n");
+}
+
+
+/*
+ * Log errors and other messages to the system log daemon and to stderr,
+ * according to the severity of the message and the current debug level.
+ * For errors of severity LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ static char fmt[211] = "warning - ";
+ char *msg;
+ struct timeval now;
+ struct tm *thyme;
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ static char fmt[311] = "warning - ";
+ char *msg;
+ char tbuf[20];
+ struct timeval now;
+ struct tm *thyme;
+
+ va_start(ap);
+#endif
+ vsprintf(&fmt[10], format, ap);
+ va_end(ap);
+ msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
+
+ /*
+ * Log to stderr if we haven't forked yet and it's a warning or worse,
+ * or if we're debugging.
+ */
+ if (haveterminal && (debug || severity <= LOG_WARNING)) {
+ time_t sectime;
+ gettimeofday(&now,NULL);
+ sectime = (time_t) now.tv_sec;
+ thyme = localtime(&sectime);
+ if (!debug)
+ fprintf(stderr, "%s: ", progname);
+ fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ /*
+ * Always log things that are worse than warnings, no matter what
+ * the log_nmsgs rate limiter says.
+ * Only count things worse than debugging in the rate limiter
+ * (since if you put daemon.debug in syslog.conf you probably
+ * actually want to log the debugging messages so they shouldn't
+ * be rate-limited)
+ */
+ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) {
+ if (severity < LOG_DEBUG)
+ log_nmsgs++;
+ if (syserr != 0) {
+ errno = syserr;
+ syslog(severity, "%s: %m", msg);
+ } else
+ syslog(severity, "%s", msg);
+ }
+
+ if (severity <= LOG_ERR) exit(-1);
+}
+
+/* TODO: format the output for better readability */
+void
+dump_pim_mrt(fp)
+ FILE *fp;
+{
+ grpentry_t *g;
+ register mrtentry_t *r;
+ register vifi_t vifi;
+ u_int number_of_groups = 0;
+ char oifs[(sizeof(if_set)<<3)+1];
+ char pruned_oifs[(sizeof(if_set)<<3)+1];
+ char asserted_oifs[(sizeof(if_set)<<3)+1];
+ char leaves_oifs[(sizeof(if_set)<<3)+1];
+ char filter_oifs[(sizeof(if_set)<<3)+1];
+ char incoming_iif[(sizeof(if_set)<<3)+1];
+
+ fprintf(fp, "Multicast Routing Table\n%s",
+ " Source Group Flags\n");
+
+ /* TODO: remove the dummy 0:: group (first in the chain) */
+ for (g = grplist->next; g != (grpentry_t *)NULL; g = g->next) {
+ number_of_groups++;
+ /* Print all (S,G) routing info */
+ for (r = g->mrtlink; r != (mrtentry_t *)NULL; r = r->grpnext) {
+ fprintf(fp, "---------------------------(S,G)----------------------------\n");
+ /* Print the routing info */
+ fprintf(fp, " %-15s", inet6_fmt(&r->source->address.sin6_addr));
+ fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr));
+
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'a' : '.';
+ filter_oifs[vifi] =
+ IF_ISSET(vifi, &r->filter_oifs) ? 'f' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ filter_oifs[vifi] = 0x0;
+ incoming_iif[vifi] = 0x0;
+ incoming_iif[r->incoming] = 'I';
+
+ /* TODO: don't need some of the flags */
+ if (r->flags & MRTF_SPT) fprintf(fp, " SPT");
+ if (r->flags & MRTF_WC) fprintf(fp, " WC");
+ if (r->flags & MRTF_RP) fprintf(fp, " RP");
+ if (r->flags & MRTF_REGISTER) fprintf(fp, " REG");
+ if (r->flags & MRTF_IIF_REGISTER) fprintf(fp, " IIF_REG");
+ if (r->flags & MRTF_NULL_OIF) fprintf(fp, " NULL_OIF");
+ if (r->flags & MRTF_KERNEL_CACHE) fprintf(fp, " CACHE");
+ if (r->flags & MRTF_ASSERTED) fprintf(fp, " ASSERTED");
+ if (r->flags & MRTF_REG_SUPP) fprintf(fp, " REG_SUPP");
+ if (r->flags & MRTF_SG) fprintf(fp, " SG");
+ if (r->flags & MRTF_PMBR) fprintf(fp, " PMBR");
+ fprintf(fp, "\n");
+
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Filtered oifs: %-20s\n", filter_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Outgoing oifs: %-20s\n", oifs);
+ fprintf(fp, "Incoming : %-20s\n", incoming_iif);
+
+ fprintf(fp, "Upstream nbr: %s\n",
+ r->upstream ? inet6_fmt(&r->upstream->address.sin6_addr) : "NONE");
+ fprintf(fp, "\nTIMERS: Entry Prune VIFS:");
+ for (vifi = 0; vifi < numvifs; vifi++)
+ fprintf(fp, " %2d", vifi);
+ fprintf(fp, "\n %3d ",
+ r->timer);
+ for (vifi = 0; vifi < numvifs; vifi++)
+ fprintf(fp, " %3d", r->prune_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }/* for all groups */
+
+ fprintf(fp, "Number of Groups: %u\n", number_of_groups);
+}
+
+void
+dump_lcl_grp(fp)
+ FILE *fp;
+{
+ vifi_t vifi;
+ struct uvif *v;
+ struct listaddr *g;
+
+ fprintf(fp, "\nList of local listener information per interface\n");
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ int first = 1;
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (first) {
+ fprintf(fp, " Mif %d(%s)\n", vifi, v->uv_name);
+ first = 0;
+ }
+ fprintf(fp, " %s", inet6_fmt(&g->al_addr.sin6_addr));
+ if (g->al_timerid)
+ fprintf(fp, " timeout: %d",
+ timer_leftTimer(g->al_timerid));
+ if (g->al_query)
+ fprintf(fp, " querytimer: %d",
+ timer_leftTimer(g->al_timerid));
+ fputc('\n', fp);
+ }
+ }
+}
diff --git a/usr.sbin/pim6dd/debug.h b/usr.sbin/pim6dd/debug.h
new file mode 100644
index 0000000..fbcc708
--- /dev/null
+++ b/usr.sbin/pim6dd/debug.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: debug.h,v 1.1.1.1 1999/08/08 23:30:52 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+extern unsigned long debug;
+extern int log_nmsgs;
+#define IF_DEBUG(l) if (debug && debug & (l))
+
+#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
+#define LOG_SHUT_UP 600 /* shut up for 10 minutes */
+
+
+/* Debug values definition */
+/* DVMRP reserved for future use */
+#define DEBUG_DVMRP_PRUNE 0x00000001
+#define DEBUG_DVMRP_ROUTE 0x00000002
+#define DEBUG_DVMRP_PEER 0x00000004
+#define DEBUG_DVMRP_TIMER 0x00000008
+#define DEBUG_DVMRP_DETAIL 0x01000000
+#define DEBUG_DVMRP ( DEBUG_DVMRP_PRUNE | DEBUG_DVMRP_ROUTE | \
+ DEBUG_DVMRP_PEER )
+
+/* MLD related */
+#define DEBUG_MLD_PROTO 0x00000010
+#define DEBUG_MLD_TIMER 0x00000020
+#define DEBUG_MLD_MEMBER 0x00000040
+#define DEBUG_MEMBER DEBUG_MLD_MEMBER
+#define DEBUG_MLD ( DEBUG_MLD_PROTO | DEBUG_MLD_TIMER | \
+ DEBUG_MLD_MEMBER )
+
+/* Misc */
+#define DEBUG_TRACE 0x00000080
+#define DEBUG_TIMEOUT 0x00000100
+#define DEBUG_PKT 0x00000200
+
+
+/* Kernel related */
+#define DEBUG_IF 0x00000400
+#define DEBUG_KERN 0x00000800
+#define DEBUG_MFC 0x00001000
+#define DEBUG_RSRR 0x00002000
+
+/* PIM related */
+#define DEBUG_PIM_GRAFT 0x02000000
+#define DEBUG_PIM_HELLO 0x00004000
+#define DEBUG_PIM_REGISTER 0x00008000
+#define DEBUG_PIM_JOIN_PRUNE 0x00010000
+#define DEBUG_PIM_BOOTSTRAP 0x00020000
+#define DEBUG_PIM_ASSERT 0x00040000
+#define DEBUG_PIM_CAND_RP 0x00080000
+#define DEBUG_PIM_MRT 0x00100000
+#define DEBUG_PIM_TIMER 0x00200000
+#define DEBUG_PIM_RPF 0x00400000
+#define DEBUG_RPF DEBUG_PIM_RPF
+#define DEBUG_PIM_DETAIL 0x00800000
+#define DEBUG_PIM ( DEBUG_PIM_HELLO | DEBUG_PIM_REGISTER | \
+ DEBUG_PIM_JOIN_PRUNE | DEBUG_PIM_BOOTSTRAP | \
+ DEBUG_PIM_ASSERT | DEBUG_PIM_CAND_RP | \
+ DEBUG_PIM_MRT | DEBUG_PIM_TIMER | \
+ DEBUG_PIM_RPF | DEBUG_PIM_GRAFT )
+
+#define DEBUG_MRT ( DEBUG_DVMRP_ROUTE | DEBUG_PIM_MRT )
+#define DEBUG_NEIGHBORS ( DEBUG_DVMRP_PEER | DEBUG_PIM_HELLO )
+#define DEBUG_TIMER ( DEBUG_MLD_TIMER | DEBUG_DVMRP_TIMER | \
+ DEBUG_PIM_TIMER )
+#define DEBUG_ASSERT ( DEBUG_PIM_ASSERT )
+#define DEBUG_ALL 0xffffffff
+
+
+#define DEBUG_DEFAULT 0xffffffff/* default if "-d" given without value */
+
+
+
+
+
+
diff --git a/usr.sbin/pim6dd/defs.h b/usr.sbin/pim6dd/defs.h
new file mode 100644
index 0000000..5970c74
--- /dev/null
+++ b/usr.sbin/pim6dd/defs.h
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: defs.h,v 1.6 1999/12/10 06:09:13 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#if ((defined(SYSV)) || (defined(__bsdi__)) || ((defined SunOS) && (SunOS < 50)))
+#include <sys/sockio.h>
+#endif /* SYSV || __bsdi__ || SunOS 4.x */
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+
+#include <arpa/inet.h>
+#include <osreldate.h>
+#define rtentry kernel_rtentry
+#include <net/route.h>
+#undef rtentry
+#include <netinet/ip_mroute.h>
+#include <netinet6/ip6_mroute.h>
+#include <strings.h>
+#ifdef RSRR
+#include <sys/un.h>
+#endif /* RSRR */
+
+typedef u_int u_int32;
+typedef u_short u_int16;
+typedef u_char u_int8;
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+typedef void (*cfunc_t) __P((void *));
+typedef void (*ihfunc_t) __P((int, fd_set *));
+
+#include "pimdd.h"
+#include "mrt.h"
+#include "mld6.h"
+#include "vif.h"
+#include "debug.h"
+#include "pathnames.h"
+#ifdef RSRR
+#include "rsrr.h"
+#include "rsrr_var.h"
+#endif /* RSRR */
+
+/*
+ * Miscellaneous constants and macros
+ */
+/* #if (!(defined(__bsdi__)) && !(defined(KERNEL))) */
+#ifndef KERNEL
+#define max(a, b) ((a) < (b) ? (b) : (a))
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+/*
+ * Various definitions to make it working for different platforms
+ */
+/* The old style sockaddr definition doesn't have sa_len */
+#if (defined(BSD) && (BSD >= 199006)) /* sa_len was added with 4.3-Reno */
+#define HAVE_SA_LEN
+#endif
+
+/* Versions of Solaris older than 2.6 don't have routing sockets. */
+/* XXX TODO: check FreeBSD version and add all other platforms */
+#if ((defined(SunOS) && SunOS >=56) || (defined __FreeBSD__) || (defined IRIX) || (defined __bsdi__) || defined(__NetBSD__))
+#define HAVE_ROUTING_SOCKETS
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define CREATE TRUE
+#define DONT_CREATE FALSE
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+/* obnoxious gcc gives an extraneous warning about this constant... */
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
+
+
+#define MINHLIM 1 /* min hoplim in the packets send locally */
+
+#define MAX_IP_PACKET_LEN 576
+#define MIN_IP_HEADER_LEN 20
+#define MAX_IP_HEADER_LEN 60
+
+
+/*
+ * The IGMPv2 <netinet/in.h> defines INADDR_ALLRTRS_GROUP, but earlier
+ * ones don't, so we define it conditionally here.
+ */
+#ifndef INADDR_ALLRTRS_GROUP
+ /* address for multicast mtrace msg */
+#define INADDR_ALLRTRS_GROUP (u_int32)0xe0000002 /* 224.0.0.2 */
+#endif
+
+#ifndef INADDR_MAX_LOCAL_GROUP
+define INADDR_MAX_LOCAL_GROUP (u_int32)0xe00000ff /* 224.0.0.255 */
+#endif
+
+#define INADDR_ANY_N (u_int32)0x00000000 /* INADDR_ANY in
+ * network order */
+#define CLASSD_PREFIX (u_int32)0xe0000000 /* 224.0.0.0 */
+#define ALL_MCAST_GROUPS_ADDR (u_int32)0xe0000000 /* 224.0.0.0 */
+#define ALL_MCAST_GROUPS_LENGTH 4
+
+/* Used by DVMRP */
+#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
+#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */
+
+#define TIMER_INTERVAL 5 /* 5 sec virtual timer granularity */
+
+#ifdef RSRR
+#define BIT_ZERO(X) ((X) = 0)
+#define BIT_SET(X,n) ((X) |= 1 << (n))
+#define BIT_CLR(X,n) ((X) &= ~(1 << (n)))
+#define BIT_TST(X,n) ((X) & 1 << (n))
+#endif /* RSRR */
+
+#ifdef SYSV
+#define bcopy(a, b, c) memcpy((b), (a), (c))
+#define bzero(s, n) memset((s), 0, (n))
+#define setlinebuf(s) setvbuf((s), (NULL), (_IOLBF), 0)
+#define RANDOM() lrand48()
+#else
+#define RANDOM() random()
+#endif /* SYSV */
+
+/*
+ * External declarations for global variables and functions.
+ */
+#define RECV_BUF_SIZE 64*1024 /* Maximum buff size to send
+ * or receive packet */
+#define SO_RECV_BUF_SIZE_MAX 256*1024
+#define SO_RECV_BUF_SIZE_MIN 48*1024
+
+/* TODO: describe the variables and clean up */
+extern char *mld6_recv_buf;
+extern char *mld6_send_buf;
+extern char *pim6_recv_buf;
+extern char *pim6_send_buf;
+extern int mld6_socket;
+extern int pim6_socket;
+extern struct sockaddr_in6 allnodes_group;
+extern struct sockaddr_in6 allrouters_group;
+extern struct sockaddr_in6 allpim6routers_group;
+extern if_set nbr_mifs;
+
+#ifdef RSRR
+extern int rsrr_socket;
+#endif /* RSRR */
+
+extern u_long virtual_time;
+extern char configfilename[];
+
+extern u_int32 default_source_metric;
+extern u_int32 default_source_preference;
+
+extern srcentry_t *srclist;
+extern grpentry_t *grplist;
+
+extern struct uvif uvifs[];
+extern vifi_t numvifs;
+extern int total_interfaces;
+extern int phys_vif;
+extern int udp_socket;
+
+extern int vifs_down;
+
+extern char s1[];
+extern char s2[];
+extern char s3[];
+extern char s4[];
+
+#if !(defined(BSD) && (BSD >= 199103))
+extern int errno;
+extern int sys_nerr;
+extern char * sys_errlist[];
+#endif
+
+
+#ifndef IGMP_MEMBERSHIP_QUERY
+#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY
+#if !(defined(__NetBSD__))
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT
+#else
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT
+#endif
+#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE
+#endif
+
+#if defined(__NetBSD__)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
+
+/* For timeout. The timers count down */
+#define SET_TIMER(timer, value) (timer) = (value)
+#define IF_TIMER_SET(timer) if ((timer) > 0)
+#define IF_TIMER_NOT_SET(timer) if ((timer) <= 0)
+#define FIRE_TIMER(timer) (timer) = 0
+
+#define IF_TIMEOUT(value) \
+ if (!(((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL)))
+
+#define IF_NOT_TIMEOUT(value) \
+ if (((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL))
+
+#define TIMEOUT(value) \
+ (!(((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL)))
+
+#define NOT_TIMEOUT(value) \
+ (((value) >= TIMER_INTERVAL) && ((value) -= TIMER_INTERVAL))
+
+#define ELSE else /* To make emacs cc-mode happy */
+
+
+/*
+ * External function definitions
+ */
+
+/* callout.c */
+extern void callout_init __P((void));
+extern void free_all_callouts __P((void));
+extern void age_callout_queue __P((int));
+extern int timer_nextTimer __P((void));
+extern int timer_setTimer __P((int, cfunc_t, void *));
+extern void timer_clearTimer __P((int));
+extern int timer_leftTimer __P((int));
+
+/* config.c */
+extern void config_vifs_from_kernel __P((void));
+extern void config_vifs_from_file __P((void));
+
+/* debug.c */
+extern char *packet_kind __P((u_int proto, u_int type, u_int code));
+extern int debug_kind __P((u_int proto, u_int type, u_int code));
+extern void log __P((int, int, char *, ...));
+extern int log_level __P((u_int proto, u_int type, u_int code));
+extern void dump __P((int i));
+extern void fdump __P((int i));
+extern void cdump __P((int i));
+extern void dump_vifs __P((FILE *fp));
+extern void dump_pim_mrt __P((FILE *fp));
+extern void dump_lcl_grp __P((FILE *fp));
+
+/* dvmrp_proto.c */
+extern void dvmrp_accept_probe __P((u_int32 src, u_int32 dst,
+ char *p, int datalen,
+ u_int32 level));
+extern void dvmrp_accept_report __P((u_int32 src, u_int32 dst,
+ char *p, int datalen,
+ u_int32 level));
+extern void dvmrp_accept_info_request __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen));
+extern void dvmrp_accept_info_reply __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen));
+extern void dvmrp_accept_neighbors __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen,
+ u_int32 level));
+extern void dvmrp_accept_neighbors2 __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen,
+ u_int32 level));
+extern void dvmrp_accept_prune __P((u_int32 src, u_int32 dst,
+ char *p, int datalen));
+extern void dvmrp_accept_graft __P((u_int32 src, u_int32 dst,
+ char *p, int datalen));
+extern void dvmrp_accept_g_ack __P((u_int32 src, u_int32 dst,
+ char *p, int datalen));
+/* mld6.c */
+void init_mld6 __P((void));
+void send_mld6 __P((int type, int code, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, struct in6_addr *group,
+ int index, int delay, int datalen, int alert));
+
+/* mld6_proto.c */
+extern void query_groups __P((struct uvif *v));
+extern int check_grp_membership __P((struct uvif *v,
+ struct sockaddr_in6 *group));
+extern void accept_listener_query __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group,
+ int tmo));
+extern void accept_listener_report __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group));
+extern void accept_listener_done __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group));
+extern int check_multicast_listener __P((struct uvif *v,
+ struct sockaddr_in6 *group));
+
+/* igmp.c */
+#if 0
+extern void init_igmp __P((void));
+extern void send_igmp __P((char *buf, u_int32 src, u_int32 dst,
+ int type, int code, u_int32 group,
+ int datalen));
+
+/* igmp_proto.c */
+extern void query_groups __P((struct uvif *v));
+extern void accept_membership_query __P((u_int32 src, u_int32 dst,
+ u_int32 group, int tmo));
+extern void accept_group_report __P((u_int32 src, u_int32 dst,
+ u_int32 group, int r_type));
+extern void accept_leave_message __P((u_int32 src, u_int32 dst,
+ u_int32 group));
+#endif
+
+#if 0
+/* inet.c */
+extern int inet_cksum __P((u_int16 *addr, u_int len));
+extern int inet_valid_host __P((u_int32 naddr));
+extern int inet_valid_mask __P((u_int32 mask));
+extern int inet_valid_subnet __P((u_int32 nsubnet, u_int32 nmask));
+extern char *inet_fmt __P((u_int32, char *s));
+#ifdef NOSUCHDEF
+extern char *inet_fmts __P((u_int32 addr, u_int32 mask, char *s));
+#endif /* NOSUCHDEF */
+extern char *netname __P((u_int32 addr, u_int32 mask));
+extern u_int32 inet_parse __P((char *s, int n));
+#endif
+
+/* inet6.c */
+extern int inet6_equal __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_lessoreq __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_lessthan __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_localif_address __P((struct sockaddr_in6 *sa,
+ struct uvif *v));
+extern int inet6_greaterthan __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_greateroreq __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_match_prefix __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2,
+ struct in6_addr *mask));
+extern int inet6_mask2plen __P((struct in6_addr *mask));
+extern int inet6_uvif2scopeid __P((struct sockaddr_in6 *sa, struct uvif *v));
+extern int inet6_valid_host __P((struct sockaddr_in6 *addr));
+extern char *inet6_fmt __P((struct in6_addr *addr));
+extern char *ifindex2str __P((int ifindex));
+extern char *net6name __P((struct in6_addr *prefix,
+ struct in6_addr *mask));
+
+/* kern.c */
+extern void k_set_rcvbuf __P((int socket, int bufsize, int minsize));
+extern void k_hdr_include __P((int socket, int bool));
+extern void k_set_hlim __P((int socket, int t));
+extern void k_set_loop __P((int socket, int l));
+extern void k_set_if __P((int socket, u_int ifindex));
+extern void k_join __P((int socket, struct in6_addr *grp,
+ u_int ifindex));
+extern void k_leave __P((int socket, struct in6_addr *grp,
+ u_int ifindex));
+extern void k_init_pim __P((int));
+extern void k_stop_pim __P((int));
+extern int k_del_mfc __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern int k_chg_mfc __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group, vifi_t iif,
+ if_set *oifs));
+extern void k_add_vif __P((int socket, vifi_t vifi, struct uvif *v));
+extern void k_del_vif __P((int socket, vifi_t vifi));
+extern int k_get_vif_count __P((vifi_t vifi, struct vif_count *retval));
+extern int k_get_sg_cnt __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group,
+ struct sg_count *retval));
+
+/* main.c */
+extern int register_input_handler __P((int fd, ihfunc_t func));
+
+/* mrt.c */
+extern void init_pim6_mrt __P((void));
+extern mrtentry_t *find_route __P((struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group,
+ u_int16 flags, char create));
+extern grpentry_t *find_group __P((struct sockaddr_in6 *group));
+extern srcentry_t *find_source __P((struct sockaddr_in6 *source));
+extern void delete_mrtentry __P((mrtentry_t *mrtentry_ptr));
+extern void delete_srcentry __P((srcentry_t *srcentry_ptr));
+extern void delete_grpentry __P((grpentry_t *grpentry_ptr));
+extern struct mrtfilter *search_filter __P((struct in6_addr *));
+extern struct mrtfilter *add_filter __P((int, struct in6_addr *,
+ struct in6_addr *, int));
+
+/* pim6.c */
+extern void init_pim6 __P((void));
+extern void send_pim6 __P((char *buf, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst,
+ int type, int datalen));
+
+/* pim6_poto.c */
+extern int receive_pim6_hello __P((struct sockaddr_in6 *src,
+ char *pim_message, int datalen));
+extern int send_pim6_hello __P((struct uvif *v, u_int16 holdtime));
+extern void delete_pim6_nbr __P((pim_nbr_entry_t *nbr_delete));
+extern int receive_pim6_join_prune __P((struct sockaddr_in6 *src,
+ char *pim_message, int datalen));
+extern int send_pim6_jp __P((mrtentry_t *mrtentry_ptr, int action,
+ mifi_t mifi,
+ struct sockaddr_in6 *target_addr,
+ u_int16 holdtime, int echo));
+
+extern int receive_pim6_assert __P((struct sockaddr_in6 *src,
+ char *pim_message, int datalen));
+extern int send_pim6_assert __P((struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group,
+ mifi_t mifi,
+ mrtentry_t *mrtentry_ptr));
+extern void delete_pim6_graft_entry __P((mrtentry_t *mrtentry_ptr));
+extern int receive_pim6_graft __P((struct sockaddr_in6 *src,
+ char *pim_message, int datalen,
+ int pimtype));
+extern int send_pim6_graft __P((mrtentry_t *mrtentry_ptr));
+
+#if 0
+/* pim.c */
+extern void init_pim __P((void));
+extern void send_pim __P((char *buf, u_int32 src, u_int32 dst,
+ int type, int datalen));
+extern void send_pim_unicast __P((char *buf, u_int32 src, u_int32 dst,
+ int type, int datalen));
+/* pim_proto.c */
+extern int receive_pim_hello __P((u_int32 src, u_int32 dst,
+ char *pim_message, int datalen));
+extern int send_pim_hello __P((struct uvif *v, u_int16 holdtime));
+extern void delete_pim_nbr __P((pim_nbr_entry_t *nbr_delete));
+extern int receive_pim_join_prune __P((u_int32 src, u_int32 dst,
+ char *pim_message, int datalen));
+extern int send_pim_jp __P((mrtentry_t *mrtentry_ptr, int action,
+ vifi_t vifi, u_int32 target_addr,
+ u_int16 holdtime));
+extern int receive_pim_assert __P((u_int32 src, u_int32 dst,
+ char *pim_message, int datalen));
+extern int send_pim_assert __P((u_int32 source, u_int32 group,
+ vifi_t vifi,
+ mrtentry_t *mrtentry_ptr));
+extern void delete_pim_graft_entry __P((mrtentry_t *mrtentry_ptr));
+extern int receive_pim_graft __P((u_int32 src, u_int32 dst,
+ char *pim_message, int datalen,
+ int pimtype));
+#endif
+
+/* route.c */
+extern int set_incoming __P((srcentry_t *srcentry_ptr,
+ int srctype));
+extern vifi_t get_iif __P((struct sockaddr_in6 *source));
+extern pim_nbr_entry_t *find_pim6_nbr __P((struct sockaddr_in6 *source));
+extern int add_sg_oif __P((mrtentry_t *mrtentry_ptr,
+ vifi_t vifi,
+ u_int16 holdtime,
+ int update_holdtime));
+extern void add_leaf __P((vifi_t vifi,
+ struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern void delete_leaf __P((vifi_t vifi,
+ struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern void set_leaves __P((mrtentry_t *mrtentry_ptr));
+extern int change_interfaces __P((mrtentry_t *mrtentry_ptr,
+ vifi_t new_iif,
+ if_set *new_pruned_oifs,
+ if_set *new_leaves_,
+ if_set *new_asserted_oifs));
+extern void calc_oifs __P((mrtentry_t *mrtentry_ptr,
+ if_set *oifs_ptr));
+extern void process_kernel_call __P((void));
+extern int delete_vif_from_mrt __P((vifi_t vifi));
+extern void trigger_join_alert __P((mrtentry_t *mrtentry_ptr));
+extern void trigger_prune_alert __P((mrtentry_t *mrtentry_ptr));
+
+
+/* routesock.c */
+extern int k_req_incoming __P((struct sockaddr_in6 *source,
+ struct rpfctl *rpfp));
+#ifdef HAVE_ROUTING_SOCKETS
+extern int init_routesock __P((void));
+#endif /* HAVE_ROUTING_SOCKETS */
+
+#ifdef RSRR
+#define gtable mrtentry
+#define RSRR_NOTIFICATION_OK TRUE
+#define RSRR_NOTIFICATION_FALSE FALSE
+
+/* rsrr.c */
+extern void rsrr_init __P((void));
+extern void rsrr_clean __P((void));
+extern void rsrr_cache_send __P((struct gtable *, int));
+extern void rsrr_cache_clean __P((struct gtable *));
+extern void rsrr_cache_bring_up __P((struct gtable *));
+#endif /* RSRR */
+
+/* timer.c */
+extern void init_timers __P((void));
+extern void age_vifs __P((void));
+extern void age_routes __P((void));
+extern int clean_srclist __P((void));
+
+/* trace.c */
+/* u_int is promoted u_char */
+extern void accept_mtrace __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst, struct in6_addr *group,
+ int ifindex, char *data, u_int no, int datalen));
+
+/* vif.c */
+extern void init_vifs __P((void));
+extern void stop_all_vifs __P((void));
+extern void check_vif_state __P((void));
+extern vifi_t local_address __P((struct sockaddr_in6 *src));
+extern vifi_t find_vif_direct __P((struct sockaddr_in6 *src));
+extern vifi_t find_vif_direct_local __P((struct sockaddr_in6 *src));
+extern struct sockaddr_in6 *max_global_address __P((void));
+extern struct sockaddr_in6 *uv_global __P((vifi_t vifi));
diff --git a/usr.sbin/pim6dd/inet6.c b/usr.sbin/pim6dd/inet6.c
new file mode 100644
index 0000000..7dc9ace
--- /dev/null
+++ b/usr.sbin/pim6dd/inet6.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+int
+inet6_uvif2scopeid(struct sockaddr_in6 *sa, struct uvif *v)
+{
+ if (IN6_IS_ADDR_MULTICAST(&sa->sin6_addr)) {
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&sa->sin6_addr))
+ return(v->uv_ifindex);
+ if (IN6_IS_ADDR_MC_SITELOCAL(&sa->sin6_addr))
+ return(v->uv_siteid);
+ }
+ else {
+ if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
+ return(v->uv_ifindex);
+
+ if (IN6_IS_ADDR_SITELOCAL(&sa->sin6_addr))
+ return(v->uv_siteid);
+ }
+
+ return(0);
+}
+
+int
+inet6_localif_address(struct sockaddr_in6 *sa, struct uvif *v)
+{
+ struct phaddr *pa;
+
+ for (pa = v->uv_addrs; pa; pa = pa->pa_next)
+ if (inet6_equal(sa, &pa->pa_addr))
+ return(TRUE);
+
+ return(FALSE);
+}
+
+int
+inet6_valid_host(struct sockaddr_in6 *addr)
+{
+ if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+int
+inet6_equal(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2)
+{
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id &&
+ IN6_ARE_ADDR_EQUAL(&sa1->sin6_addr, &sa2->sin6_addr))
+ return(1);
+
+ return(0);
+}
+
+int
+inet6_lessthan(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2)
+{
+ u_int32_t s32_1, s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id < sa2->sin6_scope_id)
+ return(1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id) {
+ for (i = 0; i < 4; i++) {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 > s32_2)
+ return(0);
+ if (s32_1 < s32_2)
+ return(1);
+
+ /* otherwide, continue to compare */
+ }
+ }
+
+ return(0);
+}
+
+int
+inet6_lessoreq(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2)
+{
+ u_int32_t s32_1, s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id < sa2->sin6_scope_id)
+ return(1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id) {
+ for (i = 0; i < 4; i++) {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 > s32_2)
+ return(0);
+ if (s32_1 < s32_2)
+ return(1);
+
+ /* otherwide, continue to compare */
+ }
+ /* sa1 == sa2 */
+ return(1);
+ }
+
+ return(0);
+}
+
+int
+inet6_greaterthan(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2)
+{
+ u_int32_t s32_1, s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id > sa2->sin6_scope_id)
+ return(1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id) {
+ for (i = 0; i < 4; i++) {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 < s32_2)
+ return(0);
+ if (s32_1 > s32_2)
+ return(1);
+
+ /* otherwide, continue to compare */
+ }
+ }
+
+ return(0);
+}
+
+int
+inet6_greateroreq(struct sockaddr_in6 *sa1, struct sockaddr_in6 *sa2)
+{
+ u_int32_t s32_1, s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id > sa2->sin6_scope_id)
+ return(1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id) {
+ for (i = 0; i < 4; i++) {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 < s32_2)
+ return(0);
+ if (s32_1 > s32_2)
+ return(1);
+
+ /* otherwide, continue to compare */
+ }
+ /* sa1 == sa2 */
+ return(1);
+ }
+
+ return(0);
+}
+
+int
+inet6_match_prefix(sa1, sa2, mask)
+ struct sockaddr_in6 *sa1, *sa2;
+ struct in6_addr *mask;
+{
+ int i;
+
+ if (sa1->sin6_scope_id != sa2->sin6_scope_id)
+ return(0);
+
+ for (i = 0; i < 16; i++) {
+ if ((sa1->sin6_addr.s6_addr[i] ^ sa2->sin6_addr.s6_addr[i]) &
+ mask->s6_addr[i])
+ return(0);
+ }
+
+ return(1);
+}
+
+char *
+inet6_fmt(struct in6_addr *addr)
+{
+ static char ip6buf[8][INET6_ADDRSTRLEN];
+ static int ip6round = 0;
+ char *cp;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ inet_ntop(AF_INET6, addr, cp, INET6_ADDRSTRLEN);
+ return(cp);
+}
+
+char *
+ifindex2str(int ifindex)
+{
+ static char ifname[IFNAMSIZ];
+
+ return(if_indextoname(ifindex, ifname));
+}
+
+int
+inet6_mask2plen(struct in6_addr *mask)
+{
+ int masklen;
+ u_char *p = (u_char *)mask;
+ u_char *lim = p + 16;
+
+ for (masklen = 0; p < lim; p++) {
+ switch (*p) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ }
+ }
+
+ return(masklen);
+}
+
+char *
+net6name(struct in6_addr *prefix, struct in6_addr *mask)
+{
+ static char ip6buf[8][INET6_ADDRSTRLEN + 4]; /* length of addr/plen */
+ static int ip6round = 0;
+ char *cp;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ inet_ntop(AF_INET6, prefix, cp, INET6_ADDRSTRLEN);
+ cp += strlen(cp);
+ *cp = '/';
+ cp++;
+ sprintf(cp, "%d", inet6_mask2plen(mask));
+
+ return(ip6buf[ip6round]);
+}
diff --git a/usr.sbin/pim6dd/kern.c b/usr.sbin/pim6dd/kern.c
new file mode 100644
index 0000000..170e85e
--- /dev/null
+++ b/usr.sbin/pim6dd/kern.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: kern.c,v 1.1.1.1 1999/08/08 23:30:52 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+#ifdef RAW_OUTPUT_IS_RAW
+int curttl = 0;
+#endif
+
+
+/*
+ * Open/init the multicast routing in the kernel and sets the MRT_ASSERT
+ * flag in the kernel.
+ *
+ */
+void
+k_init_pim(socket)
+ int socket;
+{
+ int v = 1;
+
+ if (setsockopt(socket, IPPROTO_IPV6,
+ MRT6_INIT, (char *)&v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "cannot enable multicast routing in kernel");
+
+ if(setsockopt(socket, IPPROTO_IPV6,
+ MRT6_PIM, (char *)&v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "cannot set ASSERT flag in kernel");
+}
+
+
+/*
+ * Stops the multicast routing in the kernel and resets the MRT_ASSERT
+ * flag in the kernel.
+ */
+void
+k_stop_pim(socket)
+ int socket;
+{
+ int v = 0;
+
+ if(setsockopt(socket, IPPROTO_IPV6, MRT6_PIM,
+ (char *)&v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "cannot reset ASSERT flag in kernel");
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DONE, (char *)NULL, 0) < 0)
+ log(LOG_ERR, errno, "cannot disable multicast routing in kernel");
+
+}
+
+
+/*
+ * Set the socket receiving buffer. `bufsize` is the preferred size,
+ * `minsize` is the smallest acceptable size.
+ */
+void k_set_rcvbuf(socket, bufsize, minsize)
+ int socket;
+ int bufsize;
+ int minsize;
+{
+ int delta = bufsize / 2;
+ int iter = 0;
+
+ /*
+ * Set the socket buffer. If we can't set it as large as we
+ * want, search around to try to find the highest acceptable
+ * value. The highest acceptable value being smaller than
+ * minsize is a fatal error.
+ */
+ if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ while (1) {
+ iter++;
+ if (delta > 1)
+ delta /= 2;
+
+ if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ } else {
+ if (delta < 1024)
+ break;
+ bufsize += delta;
+ }
+ }
+ if (bufsize < minsize) {
+ log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
+ bufsize, minsize);
+ /*NOTREACHED*/
+ }
+ }
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations",
+ bufsize, iter);
+}
+
+#if 0 /* there is no HDRINCL option in IPv6 */
+/*
+ * Set/reset the IP_HDRINCL option. My guess is we don't need it for raw
+ * sockets, but having it here won't hurt. Well, unless you are running
+ * an older version of FreeBSD (older than 2.2.2). If the multicast
+ * raw packet is bigger than 208 bytes, then IP_HDRINCL triggers a bug
+ * in the kernel and "panic". The kernel patch for netinet/ip_raw.c
+ * coming with this distribution fixes it.
+ */
+void k_hdr_include(socket, bool)
+ int socket;
+ int bool;
+{
+#ifdef IP_HDRINCL
+ if (setsockopt(socket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&bool, sizeof(bool)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
+#endif
+}
+#endif /* 0 */
+
+/*
+ * Set the default Hop Limit for the multicast packets outgoing from this
+ * socket.
+ */
+void k_set_hlim(socket, h)
+ int socket;
+ int h;
+{
+#ifdef RAW_OUTPUT_IS_RAW
+ curttl = h;
+#else
+ int hlim = h;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (char *)&hlim, sizeof(hlim)) < 0)
+ log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_HOPS %u", hlim);
+#endif
+}
+
+
+/*
+ * Set/reset the IPV6_MULTICAST_LOOP. Set/reset is specified by "flag".
+ */
+void k_set_loop(socket, flag)
+ int socket;
+ int flag;
+{
+ u_int loop;
+
+ loop = flag;
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0)
+ log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_LOOP %u", loop);
+}
+
+
+/*
+ * Set the IPV6_MULTICAST_IF option on local interface which has the
+ * specified index.
+ */
+void k_set_if(socket, ifindex)
+ int socket;
+ u_int ifindex;
+{
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ (char *)&ifindex, sizeof(ifindex)) < 0)
+ log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_IF for %s",
+ ifindex2str(ifindex));
+}
+
+
+/*
+ * Join a multicast grp group on local interface ifa.
+ */
+void k_join(socket, grp, ifindex)
+ int socket;
+ struct in6_addr *grp;
+ u_int ifindex;
+{
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = *grp;
+ mreq.ipv6mr_interface = ifindex;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "cannot join group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+
+/*
+ * Leave a multicats grp group on local interface ifa.
+ */
+void k_leave(socket, grp, ifindex)
+ int socket;
+ struct in6_addr *grp;
+ u_int ifindex;
+{
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = *grp;
+ mreq.ipv6mr_interface = ifindex;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "cannot leave group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+
+/*
+ * Add a virtual interface in the kernel.
+ */
+void k_add_vif(socket, vifi, v)
+ int socket;
+ vifi_t vifi;
+ struct uvif *v;
+{
+ struct mif6ctl mc;
+
+ mc.mif6c_mifi = vifi;
+ /* TODO: only for DVMRP tunnels?
+ mc.mif6c_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
+ */
+ mc.mif6c_flags = v->uv_flags;
+#ifdef notyet
+ mc.mif6c_rate_limit = v->uv_rate_limit;
+#endif
+ mc.mif6c_pifi = v->uv_ifindex;
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MIF,
+ (char *)&mc, sizeof(mc)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT6_ADD_MIF on mif %d", vifi);
+}
+
+/*
+ * Delete a virtual interface in the kernel.
+ */
+void k_del_vif(socket, vifi)
+ int socket;
+ vifi_t vifi;
+{
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MIF,
+ (char *)&vifi, sizeof(vifi)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT6_DEL_MIF on mif %d", vifi);
+}
+
+
+/*
+ * Delete all MFC entries for particular routing entry from the kernel.
+ */
+int
+k_del_mfc(socket, source, group)
+ int socket;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ struct mf6cctl mc;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MFC, (char *)&mc,
+ sizeof(mc)) < 0) {
+ log(LOG_WARNING, errno, "setsockopt MRT6_DEL_MFC");
+ return FALSE;
+ }
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Deleted MFC entry: src %s, grp %s",
+ inet6_fmt(&source->sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ return(TRUE);
+}
+
+
+/*
+ * Install/modify a MFC entry in the kernel
+ */
+int
+k_chg_mfc(socket, source, group, iif, oifs)
+ int socket;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ vifi_t iif;
+ if_set *oifs;
+{
+ struct mf6cctl mc;
+ vifi_t vifi;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+ mc.mf6cc_parent = iif;
+
+ IF_ZERO(&mc.mf6cc_ifset);
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (IF_ISSET(vifi, oifs))
+ IF_SET(vifi, &mc.mf6cc_ifset);
+ else
+ IF_CLR(vifi, &mc.mf6cc_ifset);
+ }
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MFC, (char *)&mc,
+ sizeof(mc)) < 0) {
+ log(LOG_WARNING, errno,
+ "setsockopt MRT6_ADD_MFC for source %s and group %s",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ return(FALSE);
+ }
+ return(TRUE);
+}
+
+
+/*
+ * Get packet counters for particular interface
+ */
+/*
+ * XXX: TODO: currently not used, but keep just in case we need it later.
+ */
+int k_get_vif_count(vifi, retval)
+ vifi_t vifi;
+ struct vif_count *retval;
+{
+ struct sioc_mif_req6 mreq;
+
+ mreq.mifi = vifi;
+ if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETMIFCNT_IN6 on vif %d", vifi);
+ retval->icount = retval->ocount = retval->ibytes =
+ retval->obytes = 0xffffffff;
+ return (1);
+ }
+ retval->icount = mreq.icount;
+ retval->ocount = mreq.ocount;
+ retval->ibytes = mreq.ibytes;
+ retval->obytes = mreq.obytes;
+ return (0);
+}
+
+
+/*
+ * Gets the number of packets, bytes, and number of packets arrived
+ * on wrong if in the kernel for particular (S,G) entry.
+ */
+int
+k_get_sg_cnt(socket, source, group, retval)
+ int socket; /* udp_socket */
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ struct sg_count *retval;
+{
+ struct sioc_sg_req6 sgreq;
+
+ sgreq.src = *source;
+ sgreq.grp = *group;
+ if (ioctl(socket, SIOCGETSGCNT_IN6, (char *)&sgreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT_IN6 on (%s %s)",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0; /* XXX */
+ return(1);
+ }
+ retval->pktcnt = sgreq.pktcnt;
+ retval->bytecnt = sgreq.bytecnt;
+ retval->wrong_if = sgreq.wrong_if;
+ return(0);
+}
+
+
+
diff --git a/usr.sbin/pim6dd/main.c b/usr.sbin/pim6dd/main.c
new file mode 100644
index 0000000..7efc30e
--- /dev/null
+++ b/usr.sbin/pim6dd/main.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: main.c,v 1.2 1999/08/13 09:20:13 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+#ifdef SNMP
+#include "snmp.h"
+#endif
+
+char configfilename[256] = _PATH_PIM6D_CONF;
+char versionstring[100];
+
+static char pidfilename[] = _PATH_PIM6D_PID;
+/* TODO: not used
+static char genidfilename[] = _PATH_PIM6D_GENID;
+*/
+
+int haveterminal = 1;
+char *progname;
+
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+#define GOT_SIGALRM 0x10
+
+
+#ifdef SNMP
+#define NHANDLERS 34
+#else
+#define NHANDLERS 3
+#endif
+
+static struct ihandler {
+ int fd; /* File descriptor */
+ ihfunc_t func; /* Function to call with &fd_set */
+} ihandlers[NHANDLERS];
+static int nhandlers = 0;
+
+static struct debugname {
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+#if 0
+ { "dvmrp_detail", DEBUG_DVMRP_DETAIL, 5 },
+ { "dvmrp_prunes", DEBUG_DVMRP_PRUNE, 8 },
+ { "dvmrp_pruning", DEBUG_DVMRP_PRUNE, 8 },
+ { "dvmrp_mrt", DEBUG_DVMRP_ROUTE, 7 },
+ { "dvmrp_routes", DEBUG_DVMRP_ROUTE, 7 },
+ { "dvmrp_routing", DEBUG_DVMRP_ROUTE, 7 },
+ { "dvmrp_neighbors", DEBUG_DVMRP_PEER, 7 },
+ { "dvmrp_peers", DEBUG_DVMRP_PEER, 8 },
+ { "dvmrp_hello", DEBUG_DVMRP_PEER, 7 },
+ { "dvmrp_timers", DEBUG_DVMRP_TIMER, 7 },
+ { "dvmrp", DEBUG_DVMRP, 1 },
+ { "igmp_proto", DEBUG_IGMP_PROTO, 6 },
+ { "igmp_timers", DEBUG_IGMP_TIMER, 6 },
+ { "igmp_members", DEBUG_IGMP_MEMBER, 6 },
+ { "groups", DEBUG_MEMBER, 1 },
+ { "membership", DEBUG_MEMBER, 2 },
+ { "igmp", DEBUG_IGMP, 1 },
+#endif
+ { "trace", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "timeout", DEBUG_TIMEOUT, 2 },
+ { "callout", DEBUG_TIMEOUT, 3 },
+ { "pkt", DEBUG_PKT, 2 },
+ { "packets", DEBUG_PKT, 2 },
+ { "interfaces", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "kernel", DEBUG_KERN, 2 },
+ { "cache", DEBUG_MFC, 1 },
+ { "mfc", DEBUG_MFC, 2 },
+ { "k_cache", DEBUG_MFC, 2 },
+ { "k_mfc", DEBUG_MFC, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "pim_detail", DEBUG_PIM_DETAIL, 5 },
+ { "pim_hello", DEBUG_PIM_HELLO, 5 },
+ { "pim_neighbors", DEBUG_PIM_HELLO, 5 },
+ { "pim_register", DEBUG_PIM_REGISTER, 5 },
+ { "registers", DEBUG_PIM_REGISTER, 2 },
+ { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_graft", DEBUG_PIM_GRAFT, 5 },
+ { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "bsr", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "pim_asserts", DEBUG_PIM_ASSERT, 5 },
+ { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_rp", DEBUG_PIM_CAND_RP, 6 },
+ { "rp", DEBUG_PIM_CAND_RP, 2 },
+ { "pim_routes", DEBUG_PIM_MRT, 6 },
+ { "pim_routing", DEBUG_PIM_MRT, 6 },
+ { "pim_mrt", DEBUG_PIM_MRT, 5 },
+ { "pim_timers", DEBUG_PIM_TIMER, 5 },
+ { "pim_rpf", DEBUG_PIM_RPF, 6 },
+ { "rpf", DEBUG_RPF, 3 },
+ { "pim", DEBUG_PIM, 1 },
+ { "routes", DEBUG_MRT, 1 },
+ { "routing", DEBUG_MRT, 1 },
+ { "mrt", DEBUG_MRT, 1 },
+ { "routers", DEBUG_NEIGHBORS, 6 },
+ { "mrouters", DEBUG_NEIGHBORS, 7 },
+ { "neighbors", DEBUG_NEIGHBORS, 1 },
+ { "timers", DEBUG_TIMER, 1 },
+ { "asserts", DEBUG_ASSERT, 1 },
+ { "all", DEBUG_ALL, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
+/*
+ * Forward declarations.
+ */
+static void handler __P((int));
+static void timer __P((void *));
+static void cleanup __P((void));
+static void restart __P((int));
+static void cleanup __P((void));
+static void resetlogging __P((void *));
+
+
+/* To shut up gcc -Wstrict-prototypes */
+int main __P((int argc, char **argv));
+
+int
+register_input_handler(fd, func)
+ int fd;
+ ihfunc_t func;
+{
+ if (nhandlers >= NHANDLERS)
+ return -1;
+
+ ihandlers[nhandlers].fd = fd;
+ ihandlers[nhandlers++].func = func;
+
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int dummy, dummysigalrm;
+ FILE *fp;
+ struct timeval tv, difftime, curtime, lasttime, *timeout;
+ fd_set rfds, readers;
+ int nfds, n, i, secs;
+ extern char todaysversion[];
+ struct sigaction sa;
+ struct debugname *d;
+ char c;
+ int tmpd;
+
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "pim6dd: must be root\n");
+ exit(1);
+ }
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ argv++;
+ argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-d") == 0) {
+ if (argc > 1 && *(argv + 1)[0] != '-') {
+ char *p,*q;
+ int i, len;
+ struct debugname *d;
+
+ argv++;
+ argc--;
+ debug = 0;
+ p = *argv; q = NULL;
+ while (p) {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0])) {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++) {
+ if ((j & d->level) == d->level) {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ goto usage;
+ }
+ debug |= d->level;
+ p = q;
+ }
+ }
+ else
+ debug = DEBUG_DEFAULT;
+ }
+ else if (strcmp(*argv, "-c") == 0) {
+ if (argc > 1) {
+ argv++; argc--;
+ strcpy(configfilename, *argv);
+ }
+ else
+ goto usage;
+/* TODO: not implemented */
+#ifdef SNMP
+ }
+ else if (strcmp(*argv, "-P") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++, argc--;
+ dest_port = atoi(*argv);
+ }
+ else
+ dest_port = DEFAULT_PORT;
+#endif
+ }
+ else
+ goto usage;
+ argv++; argc--;
+ }
+
+ if (argc > 0) {
+ usage:
+ tmpd = 0xffffffff;
+ fprintf(stderr, "usage: pim6dd [-c configfile] [-d [debug_level][,debug_level]]\n");
+
+ fprintf(stderr, "debug levels: ");
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++) {
+ if ((tmpd & d->level) == d->level) {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ exit(1);
+ }
+
+ if (debug != 0) {
+ tmpd = debug;
+ fprintf(stderr, "debug level 0x%lx ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++) {
+ if ((tmpd & d->level) == d->level) {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef LOG_DAEMON
+ (void)openlog("pim6dd", LOG_PID, LOG_DAEMON);
+ (void)setlogmask(LOG_UPTO(LOG_NOTICE));
+#else
+ (void)openlog("pim6dd", LOG_PID);
+#endif /* LOG_DAEMON */
+ sprintf(versionstring, "pim6dd version %s", todaysversion);
+
+ log(LOG_DEBUG, 0, "%s starting", versionstring);
+
+/* TODO: XXX: use a combination of time and hostid to initialize the random
+ * generator.
+ */
+#ifdef SYSV
+ srand48(time(NULL));
+#else
+ {
+ struct timeval tm;
+ gettimeofday(&tm, NULL);
+ srandom(tm.tv_usec + gethostid());
+ }
+#endif
+
+ callout_init();
+
+ /* Start up the log rate-limiter */
+ resetlogging(NULL);
+
+ init_mld6();
+#if 0
+ k_stop_pim(mld6_socket);
+ exit(0); /* XXX */
+#endif
+ init_pim6();
+
+ init_pim6_mrt();
+ init_timers();
+
+ /* TODO: check the kernel DVMRP/MROUTED/PIM support version */
+
+#ifdef SNMP
+ if (i = snmp_init())
+ return i;
+#endif /* SNMP */
+ init_vifs();
+
+#ifdef RSRR
+ rsrr_init();
+#endif /* RSRR */
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
+
+ FD_ZERO(&readers);
+ FD_SET(mld6_socket, &readers);
+ nfds = mld6_socket + 1;
+ for (i = 0; i < nhandlers; i++) {
+ FD_SET(ihandlers[i].fd, &readers);
+ if (ihandlers[i].fd >= nfds)
+ nfds = ihandlers[i].fd + 1;
+ }
+
+ IF_DEBUG(DEBUG_IF)
+ dump_vifs(stderr);
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(stderr);
+
+ /* schedule first timer interrupt */
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+
+ if (debug == 0) {
+ /* Detach from the terminal */
+#ifdef TIOCNOTTY
+ int t;
+#endif /* TIOCNOTTY */
+
+ haveterminal = 0;
+ if (fork())
+ exit(0);
+ (void)close(0);
+ (void)close(1);
+ (void)close(2);
+ (void)open("/", 0);
+ (void)dup2(0, 1);
+ (void)dup2(0, 2);
+#if defined(SYSV) || defined(linux)
+ (void)setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void)ioctl(t, TIOCNOTTY, (char *)0);
+ (void)close(t);
+ }
+#else
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* TIOCNOTTY */
+#endif /* SYSV */
+ } /* End of child process code */
+
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ (void) fclose(fp);
+ }
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ dummysigalrm = SIGALRM;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
+ for(;;) {
+ bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
+
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP) {
+ sighandled &= ~GOT_SIGHUP;
+ restart(SIGHUP);
+ }
+ if (sighandled & GOT_SIGUSR1) {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump(SIGUSR1);
+ }
+ if (sighandled & GOT_SIGUSR2) {
+ sighandled &= ~GOT_SIGUSR2;
+ cdump(SIGUSR2);
+ }
+ if (sighandled & GOT_SIGALRM) {
+ sighandled &= ~GOT_SIGALRM;
+ timer(&dummysigalrm);
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) {
+ if (errno != EINTR) /* SIGALRM is expected */
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second,
+ * or if there is a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the
+ * time doesn't drift too badly.
+ *
+ * If the timeout handlers took more than 1 second,
+ * age the timeout queue again. XXX This introduces the
+ * potential for infinite loops!
+ */
+ do {
+ /*
+ * If the select timed out, then there's no other
+ * activity to account for and we don't need to
+ * call gettimeofday.
+ */
+ if (n == 0) {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ } else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "TIMEOUT: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec );
+#endif
+ while (difftime.tv_usec > 1000000) {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0) {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0) {
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "\taging callouts: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec );
+#endif
+ age_callout_queue(difftime.tv_sec);
+ }
+ secs = -1;
+ } while (difftime.tv_sec > 0);
+
+ /* Handle sockets */
+ if (n > 0) {
+ /* TODO: shall check first mld6_socket for better performance? */
+ for (i = 0; i < nhandlers; i++) {
+ if (FD_ISSET(ihandlers[i].fd, &rfds)) {
+ (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ }
+ }
+ }
+
+ } /* Main loop */
+
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
+}
+
+/*
+ * The 'virtual_time' variable is initialized to a value that will cause the
+ * first invocation of timer() to send a probe or route report to all vifs
+ * and send group membership queries to all subnets for which this router is
+ * querier. This first invocation occurs approximately TIMER_INTERVAL seconds
+ * after the router starts up. Note that probes for neighbors and queries
+ * for group memberships are also sent at start-up time, as part of initial-
+ * ization. This repetition after a short interval is desirable for quickly
+ * building up topology and membership information in the presence of possible
+ * packet loss.
+ *
+ * 'virtual_time' advances at a rate that is only a crude approximation of
+ * real time, because it does not take into account any time spent processing,
+ * and because the timer intervals are sometimes shrunk by a random amount to
+ * avoid unwanted synchronization with other routers.
+ */
+
+u_long virtual_time = 0;
+
+/*
+ * Timer routine. Performs all perodic functions:
+ * aging interfaces, quering neighbors and members, etc... The granularity
+ * is equal to TIMER_INTERVAL.
+ */
+static void
+timer(i)
+ void *i;
+{
+ age_vifs(); /* Timeout neighbors and groups */
+ age_routes(); /* Timeout routing entries */
+
+ virtual_time += TIMER_INTERVAL;
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+/*
+ * Performs all necessary functions to quit gracefully
+ */
+/* TODO: implement all necessary stuff */
+static void
+cleanup()
+{
+
+#ifdef RSRR
+ rsrr_clean();
+#endif /* RSRR */
+
+ k_stop_pim(mld6_socket);
+
+ /* TODO: XXX (not in the spec)
+ */
+}
+
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig) {
+ case SIGALRM:
+ sighandled |= GOT_SIGALRM;
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
+ }
+}
+
+
+/* TODO: not verified */
+/* PIMDM TODO */
+/*
+ * Restart the daemon
+ */
+static void
+restart(i)
+ int i;
+{
+#ifdef SNMP
+ int s;
+#endif /* SNMP */
+
+ log(LOG_NOTICE, 0, "% restart", versionstring);
+
+ /*
+ * reset all the entries
+ */
+ /*
+ * TODO: delete?
+ * free_all_routes();
+ */
+ free_all_callouts();
+ stop_all_vifs();
+ nhandlers=0;
+ k_stop_pim(mld6_socket);
+ close(mld6_socket);
+ close(pim6_socket);
+ close(udp_socket);
+
+ /*
+ * start processing again
+ */
+ init_mld6();
+ init_pim6();
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+ init_pim6_mrt();
+#ifdef SNMP
+ if ( s = snmp_init())
+ exit(s);
+#endif /* SNMP */
+ init_vifs();
+
+#ifdef RSRR
+ rsrr_init();
+#endif /* RSRR */
+
+ /* schedule timer interrupts */
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+
+static void
+resetlogging(arg)
+ void *arg;
+{
+ int nxttime = 60;
+ void *narg = NULL;
+
+ if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) {
+ nxttime = LOG_SHUT_UP;
+ narg = (void *)&log_nmsgs; /* just need some valid void * */
+ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes",
+ LOG_SHUT_UP / 60);
+ } else {
+ log_nmsgs = 0;
+ }
+
+ timer_setTimer(nxttime, resetlogging, narg);
+}
diff --git a/usr.sbin/pim6dd/mld6.c b/usr.sbin/pim6dd/mld6.c
new file mode 100644
index 0000000..9185935
--- /dev/null
+++ b/usr.sbin/pim6dd/mld6.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: mld6.c,v 1.7 2000/01/04 17:17:21 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include <sys/uio.h>
+
+/*
+ * Exported variables.
+ */
+
+char *mld6_recv_buf; /* input packet buffer */
+char *mld6_send_buf; /* output packet buffer */
+int mld6_socket; /* socket for all network I/O */
+struct sockaddr_in6 allrouters_group = {sizeof(struct sockaddr_in6), AF_INET6};
+struct sockaddr_in6 allnodes_group = {sizeof(struct sockaddr_in6), AF_INET6};
+
+/* Extenals */
+
+extern struct in6_addr in6addr_linklocal_allnodes;
+
+/* local variables. */
+static struct sockaddr_in6 dst = {sizeof(dst), AF_INET6};
+static struct msghdr sndmh,
+ rcvmh;
+static struct iovec sndiov[2];
+static struct iovec rcviov[2];
+static struct sockaddr_in6 from;
+static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+#ifndef USE_RFC2292BIS
+u_int8_t raopt[IP6OPT_RTALERT_LEN];
+#endif
+static char *sndcmsgbuf;
+static int ctlbuflen = 0;
+static u_short rtalert_code;
+
+/* local functions */
+
+static void mld6_read __P((int i, fd_set * fds));
+static void accept_mld6 __P((int len));
+static void make_mld6_msg __P((int, int, struct sockaddr_in6 *,
+ struct sockaddr_in6 *, struct in6_addr *, int, int, int, int));
+
+#ifndef IP6OPT_ROUTER_ALERT
+#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
+#endif
+
+/*
+ * Open and initialize the MLD socket.
+ */
+void
+init_mld6()
+{
+ struct icmp6_filter filt;
+ int on;
+
+ rtalert_code = htons(IP6OPT_RTALERT_MLD);
+ if (!mld6_recv_buf && (mld6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR, 0, "malloca failed");
+ if (!mld6_send_buf && (mld6_send_buf = malloc(RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR, 0, "malloca failed");
+
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE);
+
+ if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+ log(LOG_ERR, errno, "MLD6 socket");
+
+ k_set_rcvbuf(mld6_socket, SO_RECV_BUF_SIZE_MAX,
+ SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
+ k_set_hlim(mld6_socket, MINHLIM); /* restrict multicasts to one hop */
+ k_set_loop(mld6_socket, FALSE); /* disable multicast loopback */
+
+ /* address initialization */
+ allnodes_group.sin6_addr = in6addr_linklocal_allnodes;
+ if (inet_pton(AF_INET6, "ff02::2",
+ (void *) &allrouters_group.sin6_addr) != 1)
+ log(LOG_ERR, 0, "inet_pton failed for ff02::2");
+
+ /* filter all non-MLD ICMP messages */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filt);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE, &filt);
+ if (setsockopt(mld6_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)");
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
+#else /* old adv. API */
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
+#endif
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)");
+#else /* old adv. API */
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)");
+#endif
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t) mld6_recv_buf;
+ rcviov[0].iov_len = RECV_BUF_SIZE;
+ rcvmh.msg_name = (caddr_t) & from;
+ rcvmh.msg_namelen = sizeof(from);
+ rcvmh.msg_iov = rcviov;
+ rcvmh.msg_iovlen = 1;
+ rcvmh.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
+
+ /* initialize msghdr for sending packets */
+ sndiov[0].iov_base = (caddr_t)mld6_send_buf;
+ sndmh.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmh.msg_iov = sndiov;
+ sndmh.msg_iovlen = 1;
+ /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
+#ifndef USE_RFC2292BIS
+ raopt[0] = IP6OPT_ROUTER_ALERT;
+ raopt[1] = IP6OPT_RTALERT_LEN - 2;
+ memcpy(&raopt[2], (caddr_t) & rtalert_code, sizeof(u_short));
+#endif
+
+ /* register MLD message handler */
+ if (register_input_handler(mld6_socket, mld6_read) < 0)
+ log(LOG_ERR, 0,
+ "Couldn't register mld6_read as an input handler");
+}
+
+/* Read an MLD message */
+static void
+mld6_read(i, rfd)
+ int i;
+ fd_set *rfd;
+{
+ register int mld6_recvlen;
+
+ mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0);
+
+ if (mld6_recvlen < 0)
+ {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "MLD6 recvmsg");
+ return;
+ }
+
+ /* TODO: make it as a thread in the future releases */
+ accept_mld6(mld6_recvlen);
+}
+
+/*
+ * Process a newly received MLD6 packet that is sitting in the input packet
+ * buffer.
+ */
+static void
+accept_mld6(recvlen)
+int recvlen;
+{
+ struct in6_addr *group, *dst = NULL;
+ struct mld6_hdr *mldh;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ int *hlimp = NULL;
+ int ifindex = 0;
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name;
+
+ /*
+ * If control length is zero, it must be an upcall from the kernel
+ * multicast forwarding engine.
+ * XXX: can we trust it?
+ */
+ if (rcvmh.msg_controllen == 0) {
+ /* XXX: msg_controllen must be reset in this case. */
+ rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
+
+ process_kernel_call();
+ return;
+ }
+
+ if (recvlen < sizeof(struct mld6_hdr))
+ {
+ log(LOG_WARNING, 0,
+ "received packet too short (%u bytes) for MLD header",
+ recvlen);
+ return;
+ }
+ mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base;
+ group = &mldh->mld6_addr;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh);
+ cm;
+ cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm))
+ {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
+ {
+ pi = (struct in6_pktinfo *) (CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = &pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *) CMSG_DATA(cm);
+ }
+ if (hlimp == NULL)
+ {
+ log(LOG_WARNING, 0,
+ "failed to get receiving hop limit");
+ return;
+ }
+
+ /* TODO: too noisy. Remove it? */
+//#define NOSUCHDEF
+#ifdef NOSUCHDEF
+ IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type,
+ mldh->mld6_code))
+ log(LOG_DEBUG, 0, "RECV %s from %s to %s",
+ packet_kind(IPPROTO_ICMPV6,
+ mldh->mld6_type, mldh->mld6_code),
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
+#endif /* NOSUCHDEF */
+
+ /* for an mtrace message, we don't need strict checks */
+ if (mldh->mld6_type == MLD6_MTRACE) {
+ accept_mtrace(src, dst, group, ifindex, (char *)(mldh + 1),
+ mldh->mld6_code, recvlen - sizeof(struct mld6_hdr));
+ return;
+ }
+
+ /* hop limit check */
+ if (*hlimp != 1)
+ {
+ log(LOG_WARNING, 0,
+ "received an MLD6 message with illegal hop limit(%d) from %s",
+ *hlimp, inet6_fmt(&src->sin6_addr));
+ /* but accept the packet */
+ }
+ if (ifindex == 0)
+ {
+ log(LOG_WARNING, 0, "failed to get receiving interface");
+ return;
+ }
+
+ /* scope check */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr))
+ {
+ log(LOG_INFO, 0,
+ "RECV %s with an invalid scope: %s from %s",
+ inet6_fmt(&mldh->mld6_addr),
+ inet6_fmt(&src->sin6_addr));
+ return; /* discard */
+ }
+
+ /* source address check */
+ if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ {
+ log(LOG_INFO, 0,
+ "RECV %s from a non link local address: %s",
+ packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
+ mldh->mld6_code),
+ inet6_fmt(&src->sin6_addr));
+ return;
+ }
+
+ switch (mldh->mld6_type)
+ {
+ case MLD6_LISTENER_QUERY:
+ accept_listener_query(src, dst, group,
+ ntohs(mldh->mld6_maxdelay));
+ return;
+
+ case MLD6_LISTENER_REPORT:
+ accept_listener_report(src, dst, group);
+ return;
+
+ case MLD6_LISTENER_DONE:
+ accept_listener_done(src, dst, group);
+ return;
+
+ default:
+ /* This must be impossible since we set a type filter */
+ log(LOG_INFO, 0,
+ "ignoring unknown ICMPV6 message type %x from %s to %s",
+ mldh->mld6_type, inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ return;
+ }
+}
+
+static void
+make_mld6_msg(type, code, src, dst, group, ifindex, delay, datalen, alert)
+ int type, code, ifindex, delay, datalen, alert;
+ struct sockaddr_in6 *src, *dst;
+ struct in6_addr *group;
+{
+ static struct sockaddr_in6 dst_sa = {sizeof(dst_sa), AF_INET6};
+ struct mld6_hdr *mhp = (struct mld6_hdr *)mld6_send_buf;
+ int ctllen, hbhlen = 0;
+
+ switch(type) {
+ case MLD6_MTRACE:
+ case MLD6_MTRACE_RESP:
+ sndmh.msg_name = (caddr_t)dst;
+ break;
+ default:
+ if (IN6_IS_ADDR_UNSPECIFIED(group))
+ dst_sa.sin6_addr = allnodes_group.sin6_addr;
+ else
+ dst_sa.sin6_addr = *group;
+ sndmh.msg_name = (caddr_t)&dst_sa;
+ datalen = sizeof(struct mld6_hdr);
+ break;
+ }
+
+ bzero(mhp, sizeof(*mhp));
+ mhp->mld6_type = type;
+ mhp->mld6_code = code;
+ mhp->mld6_maxdelay = htons(delay);
+ mhp->mld6_addr = *group;
+
+ sndiov[0].iov_len = datalen;
+
+ /* estimate total ancillary data length */
+ ctllen = 0;
+ if (ifindex != -1 || src)
+ ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
+ if (alert) {
+#ifdef USE_RFC2292BIS
+ if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_init(0) failed");
+ if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
+ 2, NULL)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_append(0) failed");
+ if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_finish(0) failed");
+#else /* old advanced API */
+ hbhlen = inet6_option_space(sizeof(raopt));
+#endif
+ ctllen += CMSG_SPACE(hbhlen);
+ }
+ /* extend ancillary data space (if necessary) */
+ if (ctlbuflen < ctllen) {
+ if (sndcmsgbuf)
+ free(sndcmsgbuf);
+ if ((sndcmsgbuf = malloc(ctllen)) == NULL)
+ log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */
+ ctlbuflen = ctllen;
+ }
+ /* store ancillary data */
+ if ((sndmh.msg_controllen = ctllen) > 0) {
+ struct cmsghdr *cmsgp;
+
+ sndmh.msg_control = sndcmsgbuf;
+ cmsgp = CMSG_FIRSTHDR(&sndmh);
+
+ if (ifindex != -1 || src) {
+ struct in6_pktinfo *pktinfo;
+
+ cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ memset((caddr_t)pktinfo, 0, sizeof(*pktinfo));
+ if (ifindex != -1)
+ pktinfo->ipi6_ifindex = ifindex;
+ if (src)
+ pktinfo->ipi6_addr = src->sin6_addr;
+ cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
+ }
+ if (alert) {
+#ifdef USE_RFC2292BIS
+ int currentlen;
+ void *hbhbuf, *optp = NULL;
+
+ cmsgp->cmsg_len = CMSG_SPACE(hbhlen);
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPOPTS;
+ hbhbuf = CMSG_DATA(cmsgp);
+
+ if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed",
+ hbhlen);
+ if ((currentlen = inet6_opt_append(hbhbuf, hbhlen,
+ currentlen,
+ IP6OPT_ROUTER_ALERT, 2,
+ 2, &optp)) == -1)
+ log(LOG_ERR, 0,
+ "inet6_opt_append(len = %d) failed",
+ currentlen, hbhlen);
+ (void)inet6_opt_set_val(optp, 0, &rtalert_code,
+ sizeof(rtalert_code));
+ if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1)
+ log(LOG_ERR, 0, "inet6_opt_finish(buf) failed");
+#else /* old advanced API */
+ if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
+ log(LOG_ERR, 0, /* assert */
+ "make_mld6_msg: inet6_option_init failed");
+ if (inet6_option_append(cmsgp, raopt, 4, 0))
+ log(LOG_ERR, 0, /* assert */
+ "make_mld6_msg: inet6_option_append failed");
+#endif
+ cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
+ }
+ }
+ else
+ sndmh.msg_control = NULL; /* clear for safety */
+}
+
+void
+send_mld6(type, code, src, dst, group, index, delay, datalen, alert)
+ int type;
+ int code; /* for trace packets only */
+ struct sockaddr_in6 *src;
+ struct sockaddr_in6 *dst; /* may be NULL */
+ struct in6_addr *group;
+ int index, delay, alert;
+ int datalen; /* for trace packets only */
+{
+ int setloop = 0;
+ struct sockaddr_in6 *dstp;
+
+ make_mld6_msg(type, code, src, dst, group, index, delay, datalen, alert);
+ dstp = (struct sockaddr_in6 *)sndmh.msg_name;
+ if (IN6_ARE_ADDR_EQUAL(&dstp->sin6_addr, &allnodes_group.sin6_addr)) {
+ setloop = 1;
+ k_set_loop(mld6_socket, TRUE);
+ }
+ if (sendmsg(mld6_socket, &sndmh, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(log_level(IPPROTO_ICMPV6, type, 0), errno,
+ "sendmsg to %s with src %s on %s",
+ inet6_fmt(&dstp->sin6_addr),
+ src ? inet6_fmt(&src->sin6_addr) : "(unspec)",
+ ifindex2str(index));
+
+ if (setloop)
+ k_set_loop(mld6_socket, FALSE);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, 0))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ packet_kind(IPPROTO_ICMPV6, type, 0),
+ src ? inet6_fmt(&src->sin6_addr) : "unspec",
+ inet6_fmt(&dstp->sin6_addr));
+}
diff --git a/usr.sbin/pim6dd/mld6.h b/usr.sbin/pim6dd/mld6.h
new file mode 100644
index 0000000..21453a3
--- /dev/null
+++ b/usr.sbin/pim6dd/mld6.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Constans for Multicast Listener Discovery protocol for IPv6.
+ */
+#define MLD6_ROBUSTNESS_VARIABLE 2
+#define MLD6_QUERY_INTERVAL 125 /* in seconds */
+#define MLD6_QUERY_RESPONSE_INTERVAL 10000 /* in milliseconds */
+#ifndef MLD6_TIMER_SCALE
+#define MLD6_TIMER_SCALE 1000
+#endif
+#define MLD6_LISTENER_INTERVAL (MLD6_ROBUSTNESS_VARIABLE * \
+ MLD6_QUERY_INTERVAL + \
+ MLD6_QUERY_RESPONSE_INTERVAL / MLD6_TIMER_SCALE)
+#define MLD6_LAST_LISTENER_QUERY_INTERVAL 1000 /* in milliseconds */
+#define MLD6_LAST_LISTENER_QUERY_COUNT MLD6_ROBUSTNESS_VARIABLE
diff --git a/usr.sbin/pim6dd/mld6_proto.c b/usr.sbin/pim6dd/mld6_proto.c
new file mode 100644
index 0000000..97cbea1
--- /dev/null
+++ b/usr.sbin/pim6dd/mld6_proto.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: mld6_proto.c,v 1.2 1999/09/12 17:00:09 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+extern struct in6_addr in6addr_any;
+
+typedef struct {
+ mifi_t mifi;
+ struct listaddr *g;
+ int q_time;
+} cbk_t;
+
+
+/*
+ * Forward declarations.
+ */
+static void DelVif __P((void *arg));
+static int SetTimer __P((int mifi, struct listaddr *g));
+static int DeleteTimer __P((int id));
+static void SendQuery __P((void *arg));
+static int SetQueryTimer __P((struct listaddr *g, int mifi, int to_expire,
+ int q_time));
+
+/*
+ * Send group membership queries on that interface if I am querier.
+ */
+void
+query_groups(v)
+ register struct uvif *v;
+{
+ register struct listaddr *g;
+
+ v->uv_gq_timer = MLD6_QUERY_INTERVAL;
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0)
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, (struct in6_addr *)&in6addr_any,
+ v->uv_ifindex, MLD6_QUERY_RESPONSE_INTERVAL, 0, 1);
+
+ /*
+ * Decrement the old-hosts-present timer for each
+ * active group on that vif.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (g->al_old > TIMER_INTERVAL)
+ g->al_old -= TIMER_INTERVAL;
+ else
+ g->al_old = 0;
+}
+
+
+/*
+ * Process an incoming host membership query
+ */
+void
+accept_listener_query(src, dst, group, tmo)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst, *group;
+ int tmo;
+{
+ register int mifi;
+ register struct uvif *v;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ /* Ignore my own membership query */
+ if (local_address(src) != NO_VIF)
+ return;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_query: can't find a mif");
+ return;
+ }
+
+ v = &uvifs[mifi];
+
+ if (v->uv_querier == NULL || inet6_equal(&v->uv_querier->al_addr, src))
+ {
+ /*
+ * This might be:
+ * - A query from a new querier, with a lower source address
+ * than the current querier (who might be me)
+ * - A query from a new router that just started up and doesn't
+ * know who the querier is.
+ * - A query from the current querier
+ */
+ if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
+ : &v->uv_linklocal->pa_addr))) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0, "new querier %s (was %s) "
+ "on mif %d",
+ inet6_fmt(&src->sin6_addr),
+ v->uv_querier ?
+ inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
+ "me", mifi);
+ if (!v->uv_querier) {
+ v->uv_querier = (struct listaddr *)
+ malloc(sizeof(struct listaddr));
+ v->uv_querier->al_next = (struct listaddr *)NULL;
+ v->uv_querier->al_timer = 0;
+ v->uv_querier->al_genid = 0;
+ v->uv_querier->al_pv = 0;
+ v->uv_querier->al_mv = 0;
+ v->uv_querier->al_old = 0;
+ v->uv_querier->al_index = 0;
+ v->uv_querier->al_timerid = 0;
+ v->uv_querier->al_query = 0;
+ v->uv_querier->al_flags = 0;
+
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ v->uv_querier->al_addr = *src;
+ time(&v->uv_querier->al_ctime);
+ }
+ }
+
+ /*
+ * Reset the timer since we've received a query.
+ */
+ if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
+ v->uv_querier->al_timer = 0;
+
+ /*
+ * If this is a Group-Specific query which we did not source,
+ * we must set our membership timer to [Last Member Query Count] *
+ * the [Max Response Time] in the packet.
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
+ inet6_equal(src, &v->uv_linklocal->pa_addr)) {
+ register struct listaddr *g;
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "%s for %s from %s on mif %d, timer %d",
+ "Group-specific membership query",
+ inet6_fmt(group),
+ inet6_fmt(&src->sin6_addr), mifi, tmo);
+
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (inet6_equal(&group_sa, &g->al_addr)
+ && g->al_query == 0) {
+ /* setup a timeout to remove the group membership */
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
+ tmo / MLD6_TIMER_SCALE;
+ /*
+ * use al_query to record our presence
+ * in last-member state
+ */
+ g->al_query = -1;
+ g->al_timerid = SetTimer(mifi, g);
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "timer for grp %s on mif %d "
+ "set to %d",
+ inet6_fmt(group),
+ mifi, g->al_timer);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void
+accept_listener_report(src, dst, group)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst, *group;
+{
+ register mifi_t mifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_report: group(%s) has the "
+ "link-local scope. discard", inet6_fmt(group));
+ return;
+ }
+
+ if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_report: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accepting multicast listener report: "
+ "src %s, dst% s, grp %s",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
+ inet6_fmt(group));
+
+ v = &uvifs[mifi];
+
+ /*
+ * Look for the group in our group list; if found, reset its timer.
+ */
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (inet6_equal(&group_sa, &g->al_addr)) {
+ g->al_reporter = *src;
+
+ /* delete old timers, set a timer for expiration */
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ if (g->al_query)
+ g->al_query = DeleteTimer(g->al_query);
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timerid = SetTimer(mifi, g);
+ add_leaf(mifi, NULL, &group_sa);
+ break;
+ }
+ }
+
+ /*
+ * If not found, add it to the list and update kernel cache.
+ */
+ if (g == NULL) {
+ g = (struct listaddr *)malloc(sizeof(struct listaddr));
+ if (g == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ g->al_addr = group_sa;
+ g->al_old = 0;
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ g->al_reporter = *src;
+ g->al_timerid = SetTimer(mifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+ time(&g->al_ctime);
+
+ add_leaf(mifi, NULL, &group_sa);
+ }
+}
+
+
+/* TODO: send PIM prune message if the last member? */
+void
+accept_listener_done(src, dst, group)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst, *group;
+{
+ register mifi_t mifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_done: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accepting listener done message: src %s, dst% s, grp %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst), inet6_fmt(group));
+
+ v = &uvifs[mifi];
+
+ if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
+ return;
+
+ /*
+ * Look for the group in our group list in order to set up a
+ * short-timeout query.
+ */
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (inet6_equal(&group_sa, &g->al_addr)) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "[accept_done_message] %d %d \n",
+ g->al_old, g->al_query);
+
+ /*
+ * Ignore the done message if there are old
+ * hosts present
+ */
+ if (g->al_old)
+ return;
+
+ /*
+ * still waiting for a reply to a query,
+ * ignore the done
+ */
+ if (g->al_query)
+ return;
+
+ /** delete old timer set a timer for expiration **/
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+
+ /** send a group specific querry **/
+ g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE) *
+ (MLD6_LAST_LISTENER_QUERY_COUNT + 1);
+ if (v->uv_flags & VIFF_QUERIER &&
+ (v->uv_flags & VIFF_NOLISTENER) == 0)
+ send_mld6(MLD6_LISTENER_QUERY, 0,
+ &v->uv_linklocal->pa_addr, NULL,
+ &g->al_addr.sin6_addr,
+ v->uv_ifindex,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
+ g->al_query = SetQueryTimer(g, mifi,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL);
+ g->al_timerid = SetTimer(mifi, g);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Time out record of a group membership on a vif
+ */
+static void
+DelVif(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ mifi_t mifi = cbk->mifi;
+ struct uvif *v = &uvifs[mifi];
+ struct listaddr *a, **anp, *g = cbk->g;
+
+ /*
+ * Group has expired
+ * delete all kernel cache entries with this group
+ */
+ if (g->al_query)
+ DeleteTimer(g->al_query);
+
+ delete_leaf(mifi, NULL, &g->al_addr);
+
+ anp = &(v->uv_groups);
+ while ((a = *anp) != NULL) {
+ if (a == g) {
+ *anp = a->al_next;
+ free((char *)a);
+ } else {
+ anp = &a->al_next;
+ }
+ }
+
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to delete the record of a group membership on a vif.
+ */
+static int
+SetTimer(mifi, g)
+ mifi_t mifi;
+ struct listaddr *g;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->mifi = mifi;
+ cbk->g = g;
+ return timer_setTimer(g->al_timer, DelVif, cbk);
+}
+
+
+/*
+ * Delete a timer that was set above.
+ */
+static int
+DeleteTimer(id)
+ int id;
+{
+ timer_clearTimer(id);
+ return 0;
+}
+
+
+/*
+ * Send a group-specific query.
+ */
+static void
+SendQuery(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ register struct uvif *v = &uvifs[cbk->mifi];
+
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0)
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, &cbk->g->al_addr.sin6_addr,
+ v->uv_ifindex, cbk->q_time, 0, 1);
+ cbk->g->al_query = 0;
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to send a group-specific query.
+ */
+static int
+SetQueryTimer(g, mifi, to_expire, q_time)
+ struct listaddr *g;
+ mifi_t mifi;
+ int to_expire;
+ int q_time;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->q_time = q_time;
+ cbk->mifi = mifi;
+ return timer_setTimer(to_expire, SendQuery, cbk);
+}
+
+/* Checks for MLD listener: returns TRUE if there is a receiver for the
+ * group on the given uvif, or returns FALSE otherwise.
+ */
+int
+check_multicast_listener(v, group)
+ struct uvif *v;
+ struct sockaddr_in6 *group;
+{
+ register struct listaddr *g;
+
+ /*
+ * Look for the group in our listener list;
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (inet6_equal(group, &g->al_addr))
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/usr.sbin/pim6dd/mrt.c b/usr.sbin/pim6dd/mrt.c
new file mode 100644
index 0000000..13d5149
--- /dev/null
+++ b/usr.sbin/pim6dd/mrt.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: mrt.c,v 1.2 1999/08/24 10:04:56 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+srcentry_t *srclist;
+grpentry_t *grplist;
+
+/*
+ * Local functions definition
+ */
+static srcentry_t *create_srcentry __P((struct sockaddr_in6 *source));
+static int search_srclist __P((struct sockaddr_in6 *source,
+ srcentry_t **sourceEntry));
+static int search_srcmrtlink __P((srcentry_t *srcentry_ptr,
+ struct sockaddr_in6 *group,
+ mrtentry_t **mrtPtr));
+static void insert_srcmrtlink __P((mrtentry_t *elementPtr,
+ mrtentry_t *insertPtr,
+ srcentry_t *srcListPtr));
+static grpentry_t *create_grpentry __P((struct sockaddr_in6 *group));
+static int search_grplist __P((struct sockaddr_in6 *group,
+ grpentry_t **groupEntry));
+static int search_grpmrtlink __P((grpentry_t *grpentry_ptr,
+ struct sockaddr_in6 *source,
+ mrtentry_t **mrtPtr));
+static void insert_grpmrtlink __P((mrtentry_t *elementPtr,
+ mrtentry_t *insertPtr,
+ grpentry_t *grpListPtr));
+static mrtentry_t *alloc_mrtentry __P((srcentry_t *srcentry_ptr,
+ grpentry_t *grpentry_ptr));
+static mrtentry_t *create_mrtentry __P((srcentry_t *srcentry_ptr,
+ grpentry_t *grpentry_ptr,
+ u_int16 flags));
+
+
+void
+init_pim6_mrt()
+{
+
+ /* TODO: delete any existing routing table */
+
+ /* Initialize the source list */
+ /* The first entry has the unspecified address and is not used */
+ /* The order is the smallest address first. */
+ srclist = (srcentry_t *)malloc(sizeof(srcentry_t));
+ srclist->next = (srcentry_t *)NULL;
+ srclist->prev = (srcentry_t *)NULL;
+ memset(&srclist->address, 0, sizeof(struct sockaddr_in6));
+ srclist->address.sin6_len = sizeof(struct sockaddr_in6);
+ srclist->address.sin6_family = AF_INET6;
+ srclist->mrtlink = (mrtentry_t *)NULL;
+ srclist->incoming = NO_VIF;
+ srclist->upstream = (pim_nbr_entry_t *)NULL;
+ srclist->metric = 0;
+ srclist->preference = 0;
+ srclist->timer = 0;
+
+ /* Initialize the group list */
+ /* The first entry has the unspecified address and is not used */
+ /* The order is the smallest address first. */
+ grplist = (grpentry_t *)malloc(sizeof(grpentry_t));
+ grplist->next = (grpentry_t *)NULL;
+ grplist->prev = (grpentry_t *)NULL;
+ memset(&grplist->group, 0, sizeof(struct sockaddr_in6));
+ grplist->group.sin6_len = sizeof(struct sockaddr_in6);
+ grplist->group.sin6_family = AF_INET6;
+ grplist->mrtlink = (mrtentry_t *)NULL;
+}
+
+
+grpentry_t*
+find_group(group)
+ struct sockaddr_in6 *group;
+{
+ grpentry_t *grpentry_ptr;
+
+ if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
+ return (grpentry_t *)NULL;
+
+ if (search_grplist(group, &grpentry_ptr) == TRUE) {
+ /* Group found! */
+ return (grpentry_ptr);
+ }
+ return (grpentry_t *)NULL;
+}
+
+
+srcentry_t *
+find_source(source)
+ struct sockaddr_in6 *source;
+{
+ srcentry_t *srcentry_ptr;
+
+ if (!inet6_valid_host(source))
+ return (srcentry_t *)NULL;
+
+ if (search_srclist(source, &srcentry_ptr) == TRUE) {
+ /* Source found! */
+ return (srcentry_ptr);
+ }
+ return (srcentry_t *)NULL;
+}
+
+
+mrtentry_t *
+find_route(source, group, flags, create)
+ struct sockaddr_in6 *source, *group;
+ u_int16 flags;
+ char create;
+{
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+
+ if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
+ return (mrtentry_t *)NULL;
+
+ if (!inet6_valid_host(source))
+ return (mrtentry_t *)NULL;
+
+ if (create == DONT_CREATE) {
+ if (search_grplist(group, &grpentry_ptr) == FALSE)
+ return (mrtentry_t *)NULL;
+ /* Search for the source */
+ if (search_grpmrtlink(grpentry_ptr, source,
+ &mrtentry_ptr) == TRUE) {
+ /* Exact (S,G) entry found */
+ return (mrtentry_ptr);
+ }
+ return (mrtentry_t *)NULL;
+ }
+
+
+ /* Creation allowed */
+
+ grpentry_ptr = create_grpentry(group);
+ if (grpentry_ptr == (grpentry_t *)NULL) {
+ return (mrtentry_t *)NULL;
+ }
+
+ /* Setup the (S,G) routing entry */
+ srcentry_ptr = create_srcentry(source);
+ if (srcentry_ptr == (srcentry_t *)NULL) {
+ if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) {
+ /* New created grpentry. Delete it. */
+ delete_grpentry(grpentry_ptr);
+ }
+ return (mrtentry_t *)NULL;
+ }
+
+ mrtentry_ptr = create_mrtentry(srcentry_ptr, grpentry_ptr, MRTF_SG);
+ if (mrtentry_ptr == (mrtentry_t *)NULL) {
+ if (grpentry_ptr->mrtlink == (mrtentry_t *)NULL) {
+ /* New created grpentry. Delete it. */
+ delete_grpentry(grpentry_ptr);
+ }
+ if (srcentry_ptr->mrtlink == (mrtentry_t *)NULL) {
+ /* New created srcentry. Delete it. */
+ delete_srcentry(srcentry_ptr);
+ }
+ return (mrtentry_t *)NULL;
+ }
+
+ if (mrtentry_ptr->flags & MRTF_NEW) {
+ struct mrtfilter *f;
+ /* The mrtentry pref/metric should be the pref/metric of the
+ * _upstream_ assert winner. Since this isn't known now,
+ * set it to the config'ed default
+ */
+ mrtentry_ptr->incoming = srcentry_ptr->incoming;
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+
+ if ((f = search_filter(&group->sin6_addr)))
+ IF_COPY(&f->ifset, &mrtentry_ptr->filter_oifs);
+ }
+
+ return (mrtentry_ptr);
+}
+
+
+void
+delete_srcentry(srcentry_ptr)
+ srcentry_t *srcentry_ptr;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_next;
+
+ if (srcentry_ptr == (srcentry_t *)NULL)
+ return;
+ /* TODO: XXX: the first entry is unused and always there */
+ srcentry_ptr->prev->next = srcentry_ptr->next;
+ if (srcentry_ptr->next != (srcentry_t *)NULL)
+ srcentry_ptr->next->prev = srcentry_ptr->prev;
+
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ mrtentry_ptr = mrtentry_next) {
+ mrtentry_next = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL)
+ mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
+ else {
+ mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) {
+ /* Delete the group entry */
+ delete_grpentry(mrtentry_ptr->group);
+ }
+ }
+ if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL)
+ mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
+ FREE_MRTENTRY(mrtentry_ptr);
+ }
+ free((char *)srcentry_ptr);
+}
+
+
+void
+delete_grpentry(grpentry_ptr)
+ grpentry_t *grpentry_ptr;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_next;
+
+ if (grpentry_ptr == (grpentry_t *)NULL)
+ return;
+ /* TODO: XXX: the first entry is unused and always there */
+ grpentry_ptr->prev->next = grpentry_ptr->next;
+ if (grpentry_ptr->next != (grpentry_t *)NULL)
+ grpentry_ptr->next->prev = grpentry_ptr->prev;
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ mrtentry_ptr = mrtentry_next) {
+ mrtentry_next = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL)
+ mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
+ else {
+ mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) {
+ /* Delete the srcentry if this was the last routing entry */
+ delete_srcentry(mrtentry_ptr->source);
+ }
+ }
+ if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL)
+ mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
+ FREE_MRTENTRY(mrtentry_ptr);
+ }
+ free((char *)grpentry_ptr);
+}
+
+
+void
+delete_mrtentry(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ if (mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ /* Delete the kernel cache first */
+ k_del_mfc(mld6_socket, &mrtentry_ptr->source->address,
+ &mrtentry_ptr->group->group);
+
+#ifdef RSRR
+ /* Tell the reservation daemon */
+ rsrr_cache_clean(mrtentry_ptr);
+#endif /* RSRR */
+
+ /* (S,G) mrtentry */
+ /* Delete from the grpentry MRT chain */
+ if (mrtentry_ptr->grpprev != (mrtentry_t *)NULL)
+ mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
+ else {
+ mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->grpnext == (mrtentry_t *)NULL) {
+ /* Delete the group entry */
+ delete_grpentry(mrtentry_ptr->group);
+ }
+ }
+
+ if (mrtentry_ptr->grpnext != (mrtentry_t *)NULL)
+ mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
+
+ /* Delete from the srcentry MRT chain */
+ if (mrtentry_ptr->srcprev != (mrtentry_t *)NULL)
+ mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
+ else {
+ mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->srcnext == (mrtentry_t *)NULL) {
+ /* Delete the srcentry if this was the last routing entry */
+ delete_srcentry(mrtentry_ptr->source);
+ }
+ }
+ if (mrtentry_ptr->srcnext != (mrtentry_t *)NULL)
+ mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
+
+ FREE_MRTENTRY(mrtentry_ptr);
+}
+
+
+static int
+search_srclist(source, sourceEntry)
+ struct sockaddr_in6 *source;
+ register srcentry_t **sourceEntry;
+{
+ register srcentry_t *s_prev,*s;
+
+ for (s_prev = srclist, s = s_prev->next; s != (srcentry_t *)NULL;
+ s_prev = s, s = s->next) {
+ /* The srclist is ordered with the smallest addresses first.
+ * The first entry is not used.
+ */
+ if (inet6_lessthan(&s->address, source))
+ continue;
+ if (inet6_equal(&s->address, source)) {
+ *sourceEntry = s;
+ return(TRUE);
+ }
+ break;
+ }
+ *sourceEntry = s_prev; /* The insertion point is between s_prev and s */
+ return(FALSE);
+}
+
+
+static int
+search_grplist(group, groupEntry)
+ struct sockaddr_in6 *group;
+ register grpentry_t **groupEntry;
+{
+ register grpentry_t *g_prev, *g;
+
+ for (g_prev = grplist, g = g_prev->next; g != (grpentry_t *)NULL;
+ g_prev = g, g = g->next) {
+ /* The grplist is ordered with the smallest address first.
+ * The first entry is not used.
+ */
+ if (inet6_lessthan(&g->group, group))
+ continue;
+ if (inet6_equal(&g->group, group)) {
+ *groupEntry = g;
+ return(TRUE);
+ }
+ break;
+ }
+ *groupEntry = g_prev; /* The insertion point is between g_prev and g */
+ return(FALSE);
+}
+
+static srcentry_t *
+create_srcentry(source)
+ struct sockaddr_in6 *source;
+{
+ register srcentry_t *srcentry_ptr;
+ srcentry_t *srcentry_prev;
+
+ if (search_srclist(source, &srcentry_prev) == TRUE)
+ return (srcentry_prev);
+
+ srcentry_ptr = (srcentry_t *)malloc(sizeof(srcentry_t));
+ if (srcentry_ptr == (srcentry_t *)NULL) {
+ log(LOG_WARNING, 0, "Memory allocation error for srcentry %s",
+ inet6_fmt(&source->sin6_addr));
+ return (srcentry_t *)NULL;
+ }
+
+ srcentry_ptr->address = *source;
+ /*
+ * Free the memory if there is error getting the iif and
+ * the next hop (upstream) router.
+ */
+ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) {
+ free((char *)srcentry_ptr);
+ return (srcentry_t *)NULL;
+ }
+ srcentry_ptr->mrtlink = (mrtentry_t *)NULL;
+ srcentry_ptr->timer = 0;
+ srcentry_ptr->next = srcentry_prev->next;
+ srcentry_prev->next = srcentry_ptr;
+ srcentry_ptr->prev = srcentry_prev;
+ if (srcentry_ptr->next != (srcentry_t *)NULL)
+ srcentry_ptr->next->prev = srcentry_ptr;
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "create source entry, source %s",
+ inet6_fmt(&source->sin6_addr));
+ return (srcentry_ptr);
+}
+
+
+static grpentry_t *
+create_grpentry(group)
+ struct sockaddr_in6 *group;
+{
+ register grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_prev;
+
+ if (search_grplist(group, &grpentry_prev) == TRUE)
+ return (grpentry_prev);
+
+ grpentry_ptr = (grpentry_t *)malloc(sizeof(grpentry_t));
+ if (grpentry_ptr == (grpentry_t *)NULL) {
+ log(LOG_WARNING, 0, "Memory allocation error for grpentry %s",
+ inet6_fmt(&group->sin6_addr));
+ return (grpentry_t *)NULL;
+ }
+
+ grpentry_ptr->group = *group;
+ grpentry_ptr->mrtlink = (mrtentry_t *)NULL;
+
+ /* Now it is safe to include the new group entry */
+ grpentry_ptr->next = grpentry_prev->next;
+ grpentry_prev->next = grpentry_ptr;
+ grpentry_ptr->prev = grpentry_prev;
+ if (grpentry_ptr->next != (grpentry_t *)NULL)
+ grpentry_ptr->next->prev = grpentry_ptr;
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "create group entry, group %s",
+ inet6_fmt(&group->sin6_addr));
+ return(grpentry_ptr);
+}
+
+
+/*
+ * Return TRUE if the entry is found and then *mrtPtr is set to point to that
+ * entry. Otherwise return FALSE and *mrtPtr points the the previous entry
+ * (or NULL if first in the chain.
+ */
+static int
+search_srcmrtlink(srcentry_ptr, group, mrtPtr)
+ srcentry_t *srcentry_ptr;
+ struct sockaddr_in6 *group;
+ mrtentry_t **mrtPtr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ register mrtentry_t *m_prev = (mrtentry_t *)NULL;
+
+ for(mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->srcnext) {
+ /* The entries are ordered with the smaller group address first.
+ * The addresses are in network order.
+ */
+ if (inet6_lessthan(&mrtentry_ptr->group->group, group))
+ continue;
+ if (inet6_equal(&mrtentry_ptr->group->group, group)) {
+ *mrtPtr = mrtentry_ptr;
+ return(TRUE);
+ }
+ break;
+ }
+ *mrtPtr = m_prev;
+ return(FALSE);
+}
+
+
+/*
+ * Return TRUE if the entry is found and then *mrtPtr is set to point to that
+ * entry. Otherwise return FALSE and *mrtPtr points the the previous entry
+ * (or NULL if first in the chain.
+ */
+static int
+search_grpmrtlink(grpentry_ptr, source, mrtPtr)
+ grpentry_t *grpentry_ptr;
+ struct sockaddr_in6 *source;
+ mrtentry_t **mrtPtr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ register mrtentry_t *m_prev = (mrtentry_t *)NULL;
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->grpnext) {
+ /* The entries are ordered with the smaller source address first.
+ * The addresses are in network order.
+ */
+ if (inet6_lessthan(&mrtentry_ptr->source->address, source))
+ continue;
+ if (inet6_equal(source, &mrtentry_ptr->source->address)) {
+ *mrtPtr = mrtentry_ptr;
+ return(TRUE);
+ }
+ break;
+ }
+ *mrtPtr = m_prev;
+ return(FALSE);
+}
+
+
+static void
+insert_srcmrtlink(mrtentry_new, mrtentry_prev, srcentry_ptr)
+ mrtentry_t *mrtentry_new;
+ mrtentry_t *mrtentry_prev;
+ srcentry_t *srcentry_ptr;
+{
+ if (mrtentry_prev == (mrtentry_t *)NULL) {
+ /* Has to be insert as the head entry for this source */
+ mrtentry_new->srcnext = srcentry_ptr->mrtlink;
+ mrtentry_new->srcprev = (mrtentry_t *)NULL;
+ srcentry_ptr->mrtlink = mrtentry_new;
+ }
+ else {
+ /* Insert right after the mrtentry_prev */
+ mrtentry_new->srcnext = mrtentry_prev->srcnext;
+ mrtentry_new->srcprev = mrtentry_prev;
+ mrtentry_prev->srcnext = mrtentry_new;
+ }
+ if (mrtentry_new->srcnext != (mrtentry_t *)NULL)
+ mrtentry_new->srcnext->srcprev = mrtentry_new;
+}
+
+
+static void
+insert_grpmrtlink(mrtentry_new, mrtentry_prev, grpentry_ptr)
+ mrtentry_t *mrtentry_new;
+ mrtentry_t *mrtentry_prev;
+ grpentry_t *grpentry_ptr;
+{
+ if (mrtentry_prev == (mrtentry_t *)NULL) {
+ /* Has to be insert as the head entry for this group */
+ mrtentry_new->grpnext = grpentry_ptr->mrtlink;
+ mrtentry_new->grpprev = (mrtentry_t *)NULL;
+ grpentry_ptr->mrtlink = mrtentry_new;
+ }
+ else {
+ /* Insert right after the mrtentry_prev */
+ mrtentry_new->grpnext = mrtentry_prev->grpnext;
+ mrtentry_new->grpprev = mrtentry_prev;
+ mrtentry_prev->grpnext = mrtentry_new;
+ }
+ if (mrtentry_new->grpnext != (mrtentry_t *)NULL)
+ mrtentry_new->grpnext->grpprev = mrtentry_new;
+}
+
+
+static mrtentry_t *
+alloc_mrtentry(srcentry_ptr, grpentry_ptr)
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ u_int16 i, *i_ptr;
+ u_long *il_ptr;
+ u_int8 vif_numbers;
+
+ mrtentry_ptr = (mrtentry_t *)malloc(sizeof(mrtentry_t));
+ if (mrtentry_ptr == (mrtentry_t *)NULL) {
+ log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
+ return (mrtentry_t *)NULL;
+ }
+
+ /*
+ * grpnext, grpprev, srcnext, srcprev will be setup when we link the
+ * mrtentry to the source and group chains
+ */
+ mrtentry_ptr->source = srcentry_ptr;
+ mrtentry_ptr->group = grpentry_ptr;
+ mrtentry_ptr->incoming = NO_VIF;
+ IF_ZERO(&mrtentry_ptr->leaves);
+ IF_ZERO(&mrtentry_ptr->pruned_oifs);
+ IF_ZERO(&mrtentry_ptr->oifs);
+ IF_ZERO(&mrtentry_ptr->filter_oifs);
+ IF_ZERO(&mrtentry_ptr->asserted_oifs);
+ mrtentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
+ mrtentry_ptr->metric = 0;
+ mrtentry_ptr->preference = 0;
+#ifdef RSRR
+ mrtentry_ptr->rsrr_cache = (struct rsrr_cache *)NULL;
+#endif /* RSRR */
+
+/*
+ * XXX: TODO: if we are short in memory, we can reserve as few as possible
+ * space for vif timers (per group and/or routing entry), but then everytime
+ * when a new interfaces is configured, the router will be restarted and
+ * will delete the whole routing table. The "memory is cheap" solution is
+ * to reserve timer space for all potential vifs in advance and then no
+ * need to delete the routing table and disturb the forwarding.
+ */
+#ifdef SAVE_MEMORY
+ mrtentry_ptr->prune_timers = (u_int16 *)malloc(sizeof(u_int16) * numvifs);
+ mrtentry_ptr->prune_delay_timerids =
+ (u_long *)malloc(sizeof(u_long) * numvifs);
+ mrtentry_ptr->last_assert = (u_long *)malloc(sizeof(u_long) * numvifs);
+ mrtentry_ptr->last_prune = (u_long *)malloc(sizeof(u_long) * numvifs);
+ vif_numbers = numvifs;
+#else
+ mrtentry_ptr->prune_timers =
+ (u_int16 *)malloc(sizeof(u_int16) * total_interfaces);
+ mrtentry_ptr->prune_delay_timerids =
+ (u_long *)malloc(sizeof(u_long) * total_interfaces);
+ mrtentry_ptr->last_assert =
+ (u_long *)malloc(sizeof(u_long) * total_interfaces);
+ mrtentry_ptr->last_prune =
+ (u_long *)malloc(sizeof(u_long) * total_interfaces);
+ vif_numbers = total_interfaces;
+#endif /* SAVE_MEMORY */
+ if ((mrtentry_ptr->prune_timers == (u_int16 *)NULL) ||
+ (mrtentry_ptr->prune_delay_timerids == (u_long *)NULL) ||
+ (mrtentry_ptr->last_assert == (u_long *)NULL) ||
+ (mrtentry_ptr->last_prune == (u_long *)NULL)) {
+ log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
+ FREE_MRTENTRY(mrtentry_ptr);
+ return (mrtentry_t *)NULL;
+ }
+ /* Reset the timers */
+ for (i = 0, i_ptr = mrtentry_ptr->prune_timers; i < vif_numbers;
+ i++, i_ptr++)
+ *i_ptr = 0;
+ for (i = 0, il_ptr = mrtentry_ptr->prune_delay_timerids; i < vif_numbers;
+ i++, il_ptr++)
+ *il_ptr = 0;
+ for (i = 0, il_ptr = mrtentry_ptr->last_assert; i < vif_numbers;
+ i++, il_ptr++)
+ *il_ptr = 0;
+ for (i = 0, il_ptr = mrtentry_ptr->last_prune; i < vif_numbers;
+ i++, il_ptr++)
+ *il_ptr = 0;
+
+ mrtentry_ptr->flags = MRTF_NEW;
+ mrtentry_ptr->timer = 0;
+ mrtentry_ptr->join_delay_timerid = 0;
+ mrtentry_ptr->assert_timer = 0;
+ mrtentry_ptr->graft = (pim_graft_entry_t *)NULL;
+
+ return(mrtentry_ptr);
+}
+
+
+static mrtentry_t *
+create_mrtentry(srcentry_ptr, grpentry_ptr, flags)
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+ u_int16 flags;
+{
+ mrtentry_t *r_new;
+ mrtentry_t *r_grp_insert, *r_src_insert; /* pointers to insert */
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+
+ /* (S,G) entry */
+ source = &srcentry_ptr->address;
+ group = &grpentry_ptr->group;
+
+ if (search_grpmrtlink(grpentry_ptr, source, &r_grp_insert) == TRUE) {
+ return(r_grp_insert);
+ }
+ if (search_srcmrtlink(srcentry_ptr, group, &r_src_insert) == TRUE) {
+ /*
+ * Hmmm, search_grpmrtlink() didn't find the entry, but
+ * search_srcmrtlink() did find it! Shoudn't happen. Panic!
+ */
+ log(LOG_ERR, 0, "MRT inconsistency for src %s and grp %s\n",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ /* not reached but to make lint happy */
+ return (mrtentry_t *)NULL;
+ }
+ /*
+ * Create and insert in group mrtlink and source mrtlink chains.
+ */
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *)NULL)
+ return (mrtentry_t *)NULL;
+ /*
+ * r_new has to be insert right after r_grp_insert in the
+ * grp mrtlink chain and right after r_src_insert in the
+ * src mrtlink chain
+ */
+ insert_grpmrtlink(r_new, r_grp_insert, grpentry_ptr);
+ insert_srcmrtlink(r_new, r_src_insert, srcentry_ptr);
+ r_new->flags |= MRTF_SG;
+ return (r_new);
+}
+
+/* ======================== */
+/* filter related functions */
+struct mrtfilter *filterlist;
+
+/*
+ * Search for a filter entry in the filter list.
+ */
+struct mrtfilter *
+search_filter(maddr)
+ struct in6_addr *maddr;
+{
+ struct mrtfilter *f;
+ struct sockaddr_in6 msa6;
+
+ for (f = filterlist; f; f = f->next) {
+ switch(f->type) {
+ case FILTER_RANGE:
+ msa6.sin6_scope_id = 0; /* XXX: scope consideration */
+ msa6.sin6_addr = *maddr;
+ if (inet6_greateroreq(&msa6, &f->mrtf_from) &&
+ inet6_lessoreq(&msa6, &f->mrtf_to))
+ return(f);
+ break;
+ case FILTER_PREFIX:
+ msa6.sin6_scope_id = 0; /* XXX: scope consideration */
+ if (inet6_match_prefix(&msa6, &f->mrtf_prefix,
+ &f->mrtf_mask))
+ return(f);
+ break;
+ }
+ }
+
+ return(NULL);
+}
+
+/*
+ * Make a new filter entry.
+ * This function assumes
+ */
+struct mrtfilter *
+add_filter(type, maddr1, maddr2, plen)
+ struct in6_addr *maddr1, *maddr2;
+ int type, plen;
+{
+ struct mrtfilter *f;
+ struct sockaddr_in6 from, to;
+
+ if ((f = malloc(sizeof(*f))) == NULL)
+ log(LOG_ERR, 0, "add_filter: malloc failed"); /* assert */
+ memset((void *)f, 0, sizeof(*f));
+
+ f->type = type;
+ switch(type) {
+ case FILTER_RANGE:
+ memset((void *)&from, 0, sizeof(from));
+ memset((void *)&to, 0, sizeof(to));
+ from.sin6_addr = *maddr1;
+ to.sin6_addr = *maddr2;
+ if (inet6_lessthan(&from, &to)) {
+ f->mrtf_from = from;
+ f->mrtf_to = to;
+ }
+ else {
+ f->mrtf_from = to;
+ f->mrtf_to = from;
+ }
+ break;
+ case FILTER_PREFIX:
+ f->mrtf_prefix.sin6_addr = *maddr1;
+ MASKLEN_TO_MASK6(plen, f->mrtf_mask);
+ break;
+ }
+ f->next = filterlist;
+ filterlist = f;
+
+ return(f);
+}
diff --git a/usr.sbin/pim6dd/mrt.h b/usr.sbin/pim6dd/mrt.h
new file mode 100644
index 0000000..156476d
--- /dev/null
+++ b/usr.sbin/pim6dd/mrt.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: mrt.h,v 1.2 1999/08/24 10:04:56 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#define MRTF_SPT 0x0001 /* iif toward source */
+#define MRTF_WC 0x0002 /* (*,G) entry */
+#define MRTF_RP 0x0004 /* iif toward RP */
+#define MRTF_NEW 0x0008 /* new created routing entry */
+#define MRTF_IIF_REGISTER 0x0020 /* ??? */
+#define MRTF_REGISTER 0x0080 /* ??? */
+#define MRTF_KERNEL_CACHE 0x0200 /* a mirror for the kernel cache */
+#define MRTF_NULL_OIF 0x0400 /* null oif cache.. ??? */
+#define MRTF_REG_SUPP 0x0800 /* register suppress ??? */
+#define MRTF_ASSERTED 0x1000 /* upstream is not that of src ??? */
+#define MRTF_SG 0x2000 /* (S,G) pure, not hanging off of (*,G)*/
+#define MRTF_PMBR 0x4000 /* (*,*,RP) entry (for interop) */
+
+/* Macro to duplicate oif info (oif bits, timers): XXX: unused */
+#define VOIF_COPY(from, to) \
+ do { \
+ VIFM_COPY((from)->joined_oifs, (to)->joined_oifs); \
+ VIFM_COPY((from)->oifs, (to)->oifs); \
+ VIFM_COPY((from)->leaves, (to)->leaves); \
+ VIFM_COPY((from)->pruned_oifs, (to)->pruned_oifs); \
+ bcopy((from)->prune_timers, (to)->prune_timers, \
+ numvifs*sizeof((from)->prune_timers[0])); \
+ bcopy((from)->prune_delay_timerids, \
+ (to)->prune_delay_timerids, \
+ numvifs*sizeof((from)->prune_delay_timerids[0])); \
+ (to)->join_delay_timerid = (from)->join_delay_timerid; \
+ } while (0)
+
+#ifdef SAVE_MEMORY
+#define FREE_MRTENTRY(mrtentry_ptr) \
+ do { \
+ u_int16 i; \
+ u_long *il_ptr; \
+ free((char *)((mrtentry_ptr)->prune_timers)); \
+ for(i=0, il_ptr=(mrtentry_ptr)->prune_delay_timerids; \
+ i<numvifs; i++, il_ptr++) \
+ timer_clearTimer(*il_ptr); \
+ free((char *)((mrtentry_ptr)->prune_delay_timerids)); \
+ timer_clearTimer((mrtentry_ptr)->join_delay_timerid); \
+ delete_pim6_graft_entry((mrtentry_ptr)); \
+ free((char *)(mrtentry_ptr)); \
+ } while (0)
+#else
+#define FREE_MRTENTRY(mrtentry_ptr) \
+ do { \
+ u_int16 i; \
+ u_long *il_ptr; \
+ free((char *)((mrtentry_ptr)->prune_timers)); \
+ for(i=0, il_ptr=(mrtentry_ptr)->prune_delay_timerids; \
+ i<total_interfaces; i++, il_ptr++) \
+ timer_clearTimer(*il_ptr); \
+ free((char *)((mrtentry_ptr)->prune_delay_timerids)); \
+ free((char *)((mrtentry_ptr)->last_assert)); \
+ free((char *)((mrtentry_ptr)->last_prune)); \
+ timer_clearTimer((mrtentry_ptr)->join_delay_timerid); \
+ delete_pim6_graft_entry((mrtentry_ptr)); \
+ free((char *)(mrtentry_ptr)); \
+ } while (0)
+#endif /* SAVE_MEMORY */
+
+typedef struct pim_nbr_entry {
+ struct pim_nbr_entry *next; /* link to next neighbor */
+ struct pim_nbr_entry *prev; /* link to prev neighbor */
+ struct sockaddr_in6 address; /* neighbor address */
+ vifi_t vifi; /* which interface */
+ u_int16 timer; /* for timing out neighbor */
+} pim_nbr_entry_t;
+
+
+/*
+ * Used to get forwarded data related counts (number of packet, number of
+ * bits, etc)
+ */
+struct sg_count {
+ u_long pktcnt; /* Number of packets for (s,g) */
+ u_long bytecnt; /* Number of bytes for (s,g) */
+ u_long wrong_if; /* Number of packets received on wrong iif for (s,g) */
+};
+
+typedef struct mrtentry mrtentry_t;
+typedef struct pim_graft_entry {
+ struct pim_graft_entry *next;
+ struct pim_graft_entry *prev;
+ mrtentry_t *mrtlink;
+} pim_graft_entry_t;
+
+
+typedef struct srcentry {
+ struct srcentry *next; /* link to next entry */
+ struct srcentry *prev; /* link to prev entry */
+ struct sockaddr_in6 address; /* source or RP address */
+ struct mrtentry *mrtlink; /* link to routing entries */
+ vifi_t incoming; /* incoming vif */
+ struct pim_nbr_entry *upstream; /* upstream router */
+ u_int32 metric; /* Unicast Routing Metric to the source */
+ u_int32 preference; /* The metric preference (for assers)*/
+ u_int16 timer; /* Entry timer??? Delete? */
+} srcentry_t;
+
+
+typedef struct grpentry {
+ struct grpentry *next; /* link to next entry */
+ struct grpentry *prev; /* link to prev entry */
+ struct sockaddr_in6 group; /* subnet group of multicasts */
+ struct mrtentry *mrtlink; /* link to (S,G) routing entries */
+} grpentry_t;
+
+struct mrtentry {
+ struct mrtentry *grpnext; /* next entry of same group */
+ struct mrtentry *grpprev; /* prev entry of same group */
+ struct mrtentry *srcnext; /* next entry of same source */
+ struct mrtentry *srcprev; /* prev entry of same source */
+ struct grpentry *group; /* pointer to group entry */
+ struct srcentry *source; /* pointer to source entry (or RP) */
+ vifi_t incoming; /* the iif (either toward S or RP) */
+
+ if_set oifs; /* The current result oifs */
+ if_set pruned_oifs; /* The pruned oifs (Prune received) */
+ if_set asserted_oifs; /* The asserted oifs (Lost Assert) */
+ if_set filter_oifs; /* The filtered oifs */
+ if_set leaves; /* Has directly connected members */
+ struct pim_nbr_entry *upstream; /* upstream router, needed because
+ * of the asserts it may be different
+ * than the source (or RP) upstream
+ * router.
+ */
+ u_int32 metric; /* Metric for the upstream */
+ u_int32 preference; /* preference for the upstream */
+ u_int16 *prune_timers; /* prune timer list */
+ u_long *prune_delay_timerids; /* timers for LAN prunes */
+ u_long join_delay_timerid; /* timer for delay joins */
+ u_int16 flags; /* The MRTF_* flags */
+ u_int16 timer; /* entry timer */
+ u_int assert_timer;
+ struct sg_count sg_count;
+ u_long *last_assert; /* time for last data-driven assert */
+ u_long *last_prune; /* time for last data-driven prune */
+ pim_graft_entry_t *graft; /* Pointer into graft entry list */
+#ifdef RSRR
+ struct rsrr_cache *rsrr_cache; /* Used to save RSRR requests for
+ * routes change notification.
+ */
+#endif /* RSRR */
+};
+
+
+struct vif_count {
+ u_long icount; /* Input packet count on vif */
+ u_long ocount; /* Output packet count on vif */
+ u_long ibytes; /* Input byte count on vif */
+ u_long obytes; /* Output byte count on vif */
+};
+
+#define FILTER_RANGE 0
+#define FILTER_PREFIX 1
+struct mrtfilter {
+ struct mrtfilter *next; /* link to the next entry */
+ int type; /* filter type: RANGE or PREFIX */
+ union { /* type specific data structure */
+ struct {
+ struct sockaddr_in6 from;
+ struct sockaddr_in6 to;
+ } mrtfu_range;
+ struct {
+ struct sockaddr_in6 prefix;
+ struct in6_addr mask;
+ } mrtfu_prefix;
+ } mrtu;
+ if_set ifset; /* interface list */
+};
+#define mrtf_from mrtu.mrtfu_range.from
+#define mrtf_to mrtu.mrtfu_range.to
+#define mrtf_prefix mrtu.mrtfu_prefix.prefix
+#define mrtf_mask mrtu.mrtfu_prefix.mask
diff --git a/usr.sbin/pim6dd/pathnames.h b/usr.sbin/pim6dd/pathnames.h
new file mode 100644
index 0000000..487d6a37
--- /dev/null
+++ b/usr.sbin/pim6dd/pathnames.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: pathnames.h,v 1.2 1999/12/16 05:36:37 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+#define _PATH_PIM6D_CONF "/usr/local/v6/etc/pim6dd.conf"
+
+#if (defined(BSD) && (BSD >= 199103))
+#define _PATH_PIM6D_PID "/var/run/pim6dd.pid"
+#define _PATH_PIM6D_GENID "/var/run/pim6dd.genid"
+#define _PATH_PIM6D_DUMP "/var/run/pim6dd.dump"
+#define _PATH_PIM6D_CACHE "/var/run/pim6dd.cache"
+#else
+#define _PATH_PIM6D_PID "/etc/pim6dd.pid"
+#define _PATH_PIM6D_GENID "/etc/pim6dd.genid"
+#define _PATH_PIM6D_DUMP "/etc/pim6dd.dump"
+#define _PATH_PIM6D_CACHE "/etc/pim6dd.cache"
+#endif
diff --git a/usr.sbin/pim6dd/pim6.c b/usr.sbin/pim6dd/pim6.c
new file mode 100644
index 0000000..f15d6b1
--- /dev/null
+++ b/usr.sbin/pim6dd/pim6.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: pim6.c,v 1.3 1999/10/26 08:39:19 itojun Exp $
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+#include <sys/uio.h>
+
+/*
+ * Exported variables.
+ */
+char *pim6_recv_buf; /* input packet buffer */
+char *pim6_send_buf; /* output packet buffer */
+
+struct sockaddr_in6 allpim6routers_group; /* ALL_PIM_ROUTERS group */
+int pim6_socket; /* socket for PIM control msgs */
+
+/*
+ * Local variables.
+ */
+static struct sockaddr_in6 from;
+static struct msghdr sndmh;
+static struct iovec sndiov[2];
+static struct in6_pktinfo *sndpktinfo;
+
+/*
+ * Local function definitions.
+ */
+static void pim6_read __P((int f, fd_set *rfd));
+static void accept_pim6 __P((int recvlen));
+static int pim6_cksum __P((u_short *, struct in6_addr *,
+ struct in6_addr *, int));
+
+void
+init_pim6()
+{
+ static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ struct cmsghdr *cmsgp = (struct cmsghdr *)sndcmsgbuf;
+
+ if ((pim6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_PIM)) < 0)
+ log(LOG_ERR, errno, "PIM6 socket");
+
+ k_set_rcvbuf(pim6_socket, SO_RECV_BUF_SIZE_MAX,
+ SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
+ k_set_hlim(pim6_socket, MINHLIM); /* restrict multicasts to one hop */
+ k_set_loop(pim6_socket, FALSE); /* disable multicast loopback */
+
+ allpim6routers_group.sin6_len = sizeof(allpim6routers_group);
+ allpim6routers_group.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, "ff02::d",
+ (void *)&allpim6routers_group.sin6_addr) != 1)
+ log(LOG_ERR, 0, "inet_pton failed for ff02::d");
+
+ if ((pim6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL ||
+ (pim6_send_buf = malloc(RECV_BUF_SIZE)) == NULL) {
+ log(LOG_ERR, 0, "init_pim6: malloc failed\n");
+ }
+
+ /* initialize msghdr for sending packets */
+ sndmh.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmh.msg_iov = sndiov;
+ sndmh.msg_iovlen = 1;
+ sndmh.msg_control = (caddr_t)sndcmsgbuf;
+ sndmh.msg_controllen = sizeof(sndcmsgbuf);
+ /* initilization cmsg for specifing outgoing interfaces and source */
+ sndpktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+
+ if (register_input_handler(pim6_socket, pim6_read) < 0)
+ log(LOG_ERR, 0,
+ "cannot register pim6_read() as an input handler");
+
+ IF_ZERO(&nbr_mifs);
+}
+
+/* Read a PIM message */
+static void
+pim6_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int pim6_recvlen;
+ int fromlen = sizeof(from);
+#ifdef SYSV
+ sigset_t block, oblock;
+#else
+ register int omask;
+#endif
+
+ pim6_recvlen = recvfrom(pim6_socket, pim6_recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)&from, &fromlen);
+
+ if (pim6_recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "PIM6 recvmsg");
+ return;
+ }
+
+#ifdef SYSV
+ (void)sigemptyset(&block);
+ (void)sigaddset(&block, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
+ log(LOG_ERR, errno, "sigprocmask");
+#else
+ /* Use of omask taken from main() */
+ omask = sigblock(sigmask(SIGALRM));
+#endif /* SYSV */
+
+ accept_pim6(pim6_recvlen);
+
+#ifdef SYSV
+ (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
+#else
+ (void)sigsetmask(omask);
+#endif /* SYSV */
+}
+
+
+static void
+accept_pim6(pimlen)
+ int pimlen;
+{
+ register struct pim *pim;
+ struct sockaddr_in6 *src = &from;
+
+ /* sanity check */
+ if (pimlen < sizeof(pim)) {
+ log(LOG_WARNING, 0,
+ "data field too short (%u bytes) for PIM header, from %s",
+ pimlen, inet6_fmt(&src->sin6_addr));
+ return;
+ }
+ pim = (struct pim *)pim6_recv_buf;
+
+#ifdef NOSUCHDEF /* TODO: delete. Too noisy */
+ IF_DEBUG(DEBUG_PIM_DETAIL) {
+ IF_DEBUG(DEBUG_PIM) {
+ log(LOG_DEBUG, 0, "Receiving %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ log(LOG_DEBUG, 0, "PIM type is %u", pim->pim_type);
+ }
+ }
+#endif /* NOSUCHDEF */
+
+
+ /* Check of PIM version is already done in the kernel */
+ /*
+ * TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address)
+ * is it necessary?
+ */
+ /* Checksum verification is done in the kernel. */
+
+ switch (pim->pim_type) {
+ case PIM_HELLO:
+ receive_pim6_hello(src, (char *)(pim), pimlen);
+ break;
+ case PIM_REGISTER:
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_REGISTER_STOP:
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_JOIN_PRUNE:
+ receive_pim6_join_prune(src, (char *)(pim), pimlen);
+ break;
+ case PIM_BOOTSTRAP:
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_ASSERT:
+ receive_pim6_assert(src, (char *)(pim), pimlen);
+ break;
+ case PIM_GRAFT:
+ case PIM_GRAFT_ACK:
+ receive_pim6_graft(src, (char *)(pim), pimlen, pim->pim_type);
+ break;
+ case PIM_CAND_RP_ADV:
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ default:
+ log(LOG_INFO, 0,
+ "ignore unknown PIM message code %u from %s",
+ pim->pim_type,
+ inet6_fmt(&src->sin6_addr));
+ break;
+ }
+}
+
+
+/*
+ * Send a multicast PIM packet from src to dst, PIM message type = "type"
+ * and data length (after the PIM header) = "datalen"
+ */
+void
+send_pim6(buf, src, dst, type, datalen)
+ char *buf;
+ struct sockaddr_in6 *src, *dst;
+ int type, datalen;
+{
+ struct pim *pim;
+ int setloop = 0;
+ int ifindex = 0, sendlen = sizeof(struct pim) + datalen;
+
+ /* Prepare the PIM packet */
+ pim = (struct pim *)buf;
+ pim->pim_type = type;
+ pim->pim_ver = PIM_PROTOCOL_VERSION;
+ pim->pim_rsv = 0;
+ pim->pim_cksum = 0;
+ /*
+ * TODO: XXX: if start using this code for PIM_REGISTERS, exclude the
+ * encapsulated packet from the checksum.
+ */
+ pim->pim_cksum = pim6_cksum((u_int16 *)pim,
+ &src->sin6_addr, &dst->sin6_addr,
+ sendlen);
+
+ /*
+ * Specify the source address of the packet. Also, specify the
+ * outgoing interface and the source address if possible.
+ */
+ memcpy(&sndpktinfo->ipi6_addr, &src->sin6_addr,
+ sizeof(src->sin6_addr));
+ if ((ifindex = src->sin6_scope_id) != 0) {
+ sndpktinfo->ipi6_ifindex = ifindex;
+ }
+ else {
+ sndpktinfo->ipi6_ifindex = 0; /* make sure to be cleared */
+ log(LOG_WARNING, 0,
+ "send_pim6: could not determine the outgoint IF; send anyway");
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) {
+ k_set_if(pim6_socket, ifindex);
+ if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allnodes_group.sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allrouters_group.sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allpim6routers_group.sin6_addr)) {
+ setloop = 1;
+ k_set_loop(pim6_socket, TRUE);
+ }
+ }
+
+ sndmh.msg_name = (caddr_t)dst;
+ sndiov[0].iov_base = (caddr_t)buf;
+ sndiov[0].iov_len = sendlen;
+ if (sendmsg(pim6_socket, &sndmh, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno, "sendto from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&dst->sin6_addr));
+ if (setloop)
+ k_set_loop(pim6_socket, FALSE);
+ return;
+ }
+
+ if (setloop)
+ k_set_loop(pim6_socket, FALSE);
+
+ IF_DEBUG(DEBUG_PIM_DETAIL) {
+ IF_DEBUG(DEBUG_PIM) {
+ char ifname[IFNAMSIZ];
+
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s on %s",
+ packet_kind(IPPROTO_PIM, type, 0),
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&dst->sin6_addr),
+ ifindex ? if_indextoname(ifindex, ifname) : "?");
+ }
+ }
+}
+
+u_int pim_send_cnt = 0;
+#define SEND_DEBUG_NUMBER 50
+
+/* ============================== */
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+static union {
+ u_short phs[4];
+ struct {
+ u_long ph_len;
+ u_char ph_zero[3];
+ u_char ph_nxt;
+ } ph;
+} uph;
+
+/*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+static int
+pim6_cksum(addr, src, dst, len)
+ u_short *addr;
+ struct in6_addr *src, *dst;
+ int len;
+{
+ register int nleft = len;
+ register u_short *w;
+ register int sum = 0;
+ u_short answer = 0;
+
+ /*
+ * First create IP6 pseudo header and calculate a summary.
+ */
+ w = (u_short *)src;
+ uph.ph.ph_len = htonl(len);
+ uph.ph.ph_nxt = IPPROTO_PIM;
+
+ /* IPv6 source address */
+ sum += w[0];
+ /* XXX: necessary? */
+ if (!(IN6_IS_ADDR_LINKLOCAL(src) || IN6_IS_ADDR_MC_LINKLOCAL(src)))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* IPv6 destination address */
+ w = (u_short *)dst;
+ sum += w[0];
+ /* XXX: necessary? */
+ if (!(IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst)))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* Payload length and upper layer identifier */
+ sum += uph.phs[0]; sum += uph.phs[1];
+ sum += uph.phs[2]; sum += uph.phs[3];
+
+ /*
+ * Secondly calculate a summary of the first mbuf excluding offset.
+ */
+ w = addr;
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *)(&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
diff --git a/usr.sbin/pim6dd/pim6_proto.c b/usr.sbin/pim6dd/pim6_proto.c
new file mode 100644
index 0000000..2be8f5c
--- /dev/null
+++ b/usr.sbin/pim6dd/pim6_proto.c
@@ -0,0 +1,1598 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: pim6_proto.c,v 1.4 1999/10/27 11:40:30 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+/*
+ * Local functions definitions.
+ */
+static int parse_pim6_hello __P((char *pktPtr, int datalen,
+ struct sockaddr_in6 *src,
+ u_int16 *holdtime));
+static void delayed_join_job __P((void *));
+static void schedule_delayed_join __P((mrtentry_t *, struct sockaddr_in6 *));
+static void delayed_prune_job __P((void *));
+static void schedule_delayed_prune __P((mrtentry_t *, mifi_t, u_int16));
+static int compare_metrics __P((u_int32 local_preference,
+ u_int32 local_metric,
+ struct sockaddr_in6 *local_address,
+ u_int32 remote_preference,
+ u_int32 remote_metric,
+ struct sockaddr_in6 *remote_address));
+static int retransmit_pim6_graft __P((mrtentry_t *));
+static void retransmit_all_pim6_grafts __P((void *));
+
+if_set nbr_mifs; /* Mifs that have one or more neighbors attached */
+
+/************************************************************************
+ * PIM_HELLO
+ ************************************************************************/
+int
+receive_pim6_hello(src, pim_message, datalen)
+ struct sockaddr_in6 *src;
+ register char *pim_message;
+ int datalen;
+{
+ mifi_t mifi;
+ struct uvif *v;
+ register pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr;
+ u_int16 holdtime;
+ u_int8 *data_ptr;
+ int state_change;
+ srcentry_t *srcentry_ptr;
+ srcentry_t *srcentry_ptr_next;
+ mrtentry_t *mrtentry_ptr;
+ u_long random_delay;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF) {
+ /* Either a local vif or somehow received PIM_HELLO from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_HELLO from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED))
+ return(FALSE); /* Shoudn't come on this interface */
+ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim));
+
+ /* Get the Holdtime (in seconds) from the message. Return if error. */
+ if (parse_pim6_hello(pim_message, datalen, src, &holdtime) == FALSE)
+ return(FALSE);
+ IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
+ log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
+ inet6_fmt(&src->sin6_addr), holdtime);
+
+ for (prev_nbr = (pim_nbr_entry_t *)NULL, nbr = v->uv_pim_neighbors;
+ nbr != (pim_nbr_entry_t *)NULL;
+ prev_nbr = nbr, nbr = nbr->next) {
+ /* The PIM neighbors are sorted in decreasing order of the
+ * network addresses (note that to be able to compare them
+ * correctly we must translate the addresses in host order.
+ */
+ if (inet6_lessthan(src, &nbr->address))
+ continue;
+ if (inet6_equal(src, &nbr->address)) {
+ /* We already have an entry for this host */
+ if (0 == holdtime) {
+ /*
+ * Looks like we have a nice neighbor who is
+ * going down and wants to inform us by sending
+ * "holdtime=0". Thanks buddy and see you again!
+ */
+ log(LOG_INFO, 0,
+ "PIM HELLO received: neighbor %s going down",
+ inet6_fmt(&src->sin6_addr));
+ delete_pim6_nbr(nbr);
+ return(TRUE);
+ }
+ /* Set the timer */
+ nbr->timer = holdtime;
+ return(TRUE);
+ }
+ else
+ /*
+ * No entry for this neighbor. Exit the loop and create an
+ * entry for it.
+ */
+ break;
+ }
+
+ /*
+ * This is a new neighbor. Create a new entry for it.
+ * It must be added right after `prev_nbr`
+ */
+ new_nbr = (pim_nbr_entry_t *)malloc(sizeof(pim_nbr_entry_t));
+ new_nbr->address = *src;
+ new_nbr->vifi = mifi;
+ new_nbr->timer = holdtime;
+ new_nbr->next = nbr;
+ new_nbr->prev = prev_nbr;
+
+ if (prev_nbr != (pim_nbr_entry_t *)NULL)
+ prev_nbr->next = new_nbr;
+ else
+ v->uv_pim_neighbors = new_nbr;
+ if (new_nbr->next != (pim_nbr_entry_t *)NULL)
+ new_nbr->next->prev = new_nbr;
+
+ v->uv_flags &= ~VIFF_NONBRS;
+ v->uv_flags |= VIFF_PIM_NBR;
+ IF_SET(mifi, &nbr_mifs);
+
+ /* Elect a new DR */
+ if (inet6_lessthan(&v->uv_linklocal->pa_addr,
+ &v->uv_pim_neighbors->address)) {
+ /* The first address is the new potential remote
+ * DR address and it wins (is >) over the local address.
+ */
+ v->uv_flags &= ~VIFF_DR;
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+
+ /*
+ * Since a new neighbour has come up, let it know your existence ASAP;
+ * compute a random value, and reset the value to the hello timer
+ * if it's smaller than the rest of the timer.
+ * XXX: not in the spec...
+ */
+ random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1));
+ if (random_delay < v->uv_pim_hello_timer)
+ v->uv_pim_hello_timer = random_delay;
+
+ /* Update the source entries */
+ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
+ srcentry_ptr = srcentry_ptr_next) {
+ srcentry_ptr_next = srcentry_ptr->next;
+
+ if (srcentry_ptr->incoming == mifi)
+ continue;
+
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext) {
+
+ if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs))) {
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ srcentry_ptr->incoming,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+ if(state_change == 1)
+ trigger_join_alert(mrtentry_ptr);
+ }
+ }
+ }
+
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ dump_vifs(stderr); /* Show we got a new neighbor */
+ return(TRUE);
+}
+
+void
+delete_pim6_nbr(nbr_delete)
+ pim_nbr_entry_t *nbr_delete;
+{
+ srcentry_t *srcentry_ptr;
+ srcentry_t *srcentry_ptr_next;
+ mrtentry_t *mrtentry_ptr;
+ struct uvif *v;
+ int state_change;
+
+ v = &uvifs[nbr_delete->vifi];
+
+ /* Delete the entry from the pim_nbrs chain */
+ if (nbr_delete->prev != (pim_nbr_entry_t *)NULL)
+ nbr_delete->prev->next = nbr_delete->next;
+ else
+ v->uv_pim_neighbors = nbr_delete->next;
+ if (nbr_delete->next != (pim_nbr_entry_t *)NULL)
+ nbr_delete->next->prev = nbr_delete->prev;
+
+ if (v->uv_pim_neighbors == (pim_nbr_entry_t *)NULL) {
+ /* This was our last neighbor. */
+ v->uv_flags &= ~VIFF_PIM_NBR;
+ v->uv_flags |= (VIFF_NONBRS | VIFF_DR | VIFF_QUERIER);
+ IF_CLR(nbr_delete->vifi, &nbr_mifs);
+ }
+ else {
+ if (inet6_greaterthan(&v->uv_linklocal->pa_addr,
+ &v->uv_pim_neighbors->address)) {
+ /* The first address is the new potential remote
+ * DR address, but the local address is the winner.
+ */
+ v->uv_flags |= VIFF_DR;
+ v->uv_flags |= VIFF_QUERIER;
+ }
+ }
+
+ /* Update the source entries:
+ * If the deleted nbr was my upstream, then reset incoming and
+ * update all (S,G) entries for sources reachable through it.
+ * If the deleted nbr was the last on a non-iif vif, then recalcuate
+ * outgoing interfaces.
+ */
+ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
+ srcentry_ptr = srcentry_ptr_next) {
+ srcentry_ptr_next = srcentry_ptr->next;
+
+ /* The only time we don't need to scan all mrtentries is
+ * when the nbr was on the iif, but not the upstream nbr!
+ */
+ if (nbr_delete->vifi == srcentry_ptr->incoming &&
+ srcentry_ptr->upstream != nbr_delete)
+ continue;
+
+ /* Reset the next hop (PIM) router */
+ if(srcentry_ptr->upstream == nbr_delete)
+ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) {
+ /*
+ * Couldn't reset it. Sorry, the next hop router
+ * toward that source is probably not
+ * a PIM router, or cannot find route at all,
+ * hence I cannot handle this source and have to
+ * delete it.
+ */
+ delete_srcentry(srcentry_ptr);
+ free((char *)nbr_delete);
+ return;
+ }
+
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext) {
+ mrtentry_ptr->incoming = srcentry_ptr->incoming;
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ srcentry_ptr->incoming,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+ if(state_change == -1) {
+ trigger_prune_alert(mrtentry_ptr);
+ } else if(state_change == 1) {
+ trigger_join_alert(mrtentry_ptr);
+ }
+ }
+ }
+
+ free((char *)nbr_delete);
+}
+
+
+/* TODO: simplify it! */
+static int
+parse_pim6_hello(pim_message, datalen, src, holdtime)
+ char *pim_message;
+ int datalen;
+ struct sockaddr_in6 *src;
+ u_int16 *holdtime;
+{
+ u_int8 *pim_hello_message;
+ u_int8 *data_ptr;
+ u_int16 option_type;
+ u_int16 option_length;
+ int holdtime_received_ok = FALSE;
+ int option_total_length;
+
+ pim_hello_message = (u_int8 *)(pim_message + sizeof(struct pim));
+ datalen -= sizeof(struct pim);
+ for ( ; datalen >= sizeof(pim_hello_t); ) {
+ /* Ignore any data if shorter than (pim_hello header) */
+ data_ptr = pim_hello_message;
+ GET_HOSTSHORT(option_type, data_ptr);
+ GET_HOSTSHORT(option_length, data_ptr);
+ switch (option_type) {
+ case PIM_MESSAGE_HELLO_HOLDTIME:
+ if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) {
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG, 0,
+ "PIM HELLO Holdtime from %s: "
+ "invalid OptionLength = %u",
+ inet6_fmt(&src->sin6_addr),
+ option_length);
+ return (FALSE);
+ }
+ GET_HOSTSHORT(*holdtime, data_ptr);
+ holdtime_received_ok = TRUE;
+ break;
+ default:
+ /* Ignore any unknown options */
+ break;
+ }
+
+ /* Move to the next option */
+ /* XXX: TODO: If we are padding to the end of the 32 bit boundary,
+ * use the first method to move to the next option, otherwise
+ * simply (sizeof(pim_hello_t) + option_length).
+ */
+#ifdef BOUNDARY_32_BIT
+ option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) +
+ ((option_length & 0x3) ? 4 : 0));
+#else
+ option_total_length = (sizeof(pim_hello_t) + option_length);
+#endif /* BOUNDARY_32_BIT */
+ datalen -= option_total_length;
+ pim_hello_message += option_total_length;
+ }
+ return (holdtime_received_ok);
+}
+
+
+int
+send_pim6_hello(v, holdtime)
+ struct uvif *v;
+ u_int16 holdtime;
+{
+ char *buf;
+ u_int8 *data_ptr;
+
+ int datalen;
+
+ buf = pim6_send_buf + sizeof(struct pim);
+ data_ptr = (u_int8 *)buf;
+ PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr);
+ PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr);
+ PUT_HOSTSHORT(holdtime, data_ptr);
+
+ datalen = data_ptr - (u_int8 *)buf;
+
+ send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_HELLO, datalen);
+ v->uv_pim_hello_timer = PIM_TIMER_HELLO_PERIOD;
+ return(TRUE);
+}
+
+
+/************************************************************************
+ * PIM_JOIN_PRUNE
+ ************************************************************************/
+
+typedef struct {
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ struct sockaddr_in6 target;
+} join_delay_cbk_t;
+
+typedef struct {
+ mifi_t mifi;
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ u_int16 holdtime;
+} prune_delay_cbk_t;
+
+static void
+delayed_join_job(arg)
+ void *arg;
+{
+ mrtentry_t *mrtentry_ptr;
+
+ join_delay_cbk_t *cbk = (join_delay_cbk_t *)arg;
+
+ mrtentry_ptr = find_route(&cbk->source, &cbk->group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ if(mrtentry_ptr->join_delay_timerid)
+ timer_clearTimer(mrtentry_ptr->join_delay_timerid);
+
+ if(mrtentry_ptr->upstream)
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->upstream->address, 0, 0);
+
+ free(cbk);
+}
+
+static void
+schedule_delayed_join(mrtentry_ptr, target)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *target;
+{
+ u_long random_delay;
+ join_delay_cbk_t *cbk;
+
+ /* Delete existing timer */
+ if(mrtentry_ptr->join_delay_timerid)
+ timer_clearTimer(mrtentry_ptr->join_delay_timerid);
+
+#ifdef SYSV
+ random_delay = lrand48() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
+#else
+ random_delay = random() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
+#endif
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0, "Scheduling join for src %s, grp %s, delay %d",
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr),
+ random_delay);
+
+ if(random_delay == 0 && mrtentry_ptr->upstream) {
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->upstream->address, 0, 0);
+ return;
+ }
+
+ cbk = (join_delay_cbk_t *)malloc(sizeof(join_delay_cbk_t));
+ cbk->source = mrtentry_ptr->source->address;
+ cbk->group = mrtentry_ptr->group->group;
+ cbk->target = *target;
+
+ mrtentry_ptr->join_delay_timerid =
+ timer_setTimer(random_delay, delayed_join_job, cbk);
+}
+
+
+static void
+delayed_prune_job(arg)
+ void *arg;
+{
+ mrtentry_t *mrtentry_ptr;
+ if_set new_pruned_oifs;
+ int state_change;
+
+ prune_delay_cbk_t *cbk = (prune_delay_cbk_t *)arg;
+
+ mrtentry_ptr = find_route(&cbk->source, &cbk->group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ if(mrtentry_ptr->prune_delay_timerids[cbk->mifi])
+ timer_clearTimer(mrtentry_ptr->prune_delay_timerids[cbk->mifi]);
+
+ if(IF_ISSET(cbk->mifi, &mrtentry_ptr->oifs)) {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "Deleting pruned mif %d for src %s, grp %s",
+ cbk->mifi,
+ inet6_fmt(&cbk->source.sin6_addr),
+ inet6_fmt(&cbk->group.sin6_addr));
+
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs);
+ IF_SET(cbk->mifi, &new_pruned_oifs);
+ SET_TIMER(mrtentry_ptr->prune_timers[cbk->mifi], cbk->holdtime);
+
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &new_pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+
+ /* Handle transition to negative cache */
+ if(state_change == -1)
+ trigger_prune_alert(mrtentry_ptr);
+ }
+
+ free(cbk);
+}
+
+static void
+schedule_delayed_prune(mrtentry_ptr, mifi, holdtime)
+ mrtentry_t *mrtentry_ptr;
+ mifi_t mifi;
+ u_int16 holdtime;
+{
+ prune_delay_cbk_t *cbk;
+
+ /* Delete existing timer */
+ if(mrtentry_ptr->prune_delay_timerids[mifi])
+ timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]);
+
+ cbk = (prune_delay_cbk_t *)malloc(sizeof(prune_delay_cbk_t));
+ cbk->mifi = mifi;
+ cbk->source = mrtentry_ptr->source->address;
+ cbk->group = mrtentry_ptr->group->group;
+ cbk->holdtime = holdtime;
+
+ mrtentry_ptr->prune_delay_timerids[mifi] =
+ timer_setTimer((u_int16)PIM_RANDOM_DELAY_JOIN_TIMEOUT,
+ delayed_prune_job, cbk);
+}
+
+
+/* TODO: when parsing, check if we go beyong message size */
+int
+receive_pim6_join_prune(src, pim_message, datalen)
+ struct sockaddr_in6 *src;
+ char *pim_message;
+ register int datalen;
+{
+ mifi_t mifi;
+ struct uvif *v;
+ pim6_encod_uni_addr_t uni_target_addr;
+ pim6_encod_grp_addr_t encod_group;
+ pim6_encod_src_addr_t encod_src;
+ u_int8 *data_ptr;
+ u_int8 num_groups;
+ u_int16 holdtime;
+ u_int16 num_j_srcs, num_p_srcs;
+ struct sockaddr_in6 source, group, target;
+ struct in6_addr s_mask, g_mask;
+ u_int8 s_flags;
+ u_int8 reserved;
+ mrtentry_t *mrtentry_ptr;
+ pim_nbr_entry_t *upstream_router;
+ if_set new_pruned_oifs;
+ int state_change;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF) {
+ /*
+ * Either a local vif or somehow received PIM_JOIN_PRUNE from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
+ return(FALSE); /* Shoudn't come on this interface */
+ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim));
+
+ /* Get the target address */
+ GET_EUADDR6(&uni_target_addr, data_ptr);
+ GET_BYTE(reserved, data_ptr);
+ GET_BYTE(num_groups, data_ptr);
+ if (num_groups == 0)
+ return (FALSE); /* No indication for groups in the message */
+ GET_HOSTSHORT(holdtime, data_ptr);
+ target.sin6_len = sizeof(target);
+ target.sin6_family = AF_INET6;
+ target.sin6_addr = uni_target_addr.unicast_addr;
+ target.sin6_scope_id = inet6_uvif2scopeid(&target, v);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "PIM Join/Prune received from %s : target %s, holdtime %d",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&target.sin6_addr),
+ holdtime);
+
+ if (!inet6_localif_address(&target, v) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr)) {
+ /* if I am not the target of the join or prune message */
+ /*
+ * Join Suppression: when receiving a join not addressed to me,
+ * if I am delaying a join for this (S,G) then cancel the delayed
+ * join.
+ * Prune Soliticiting Joins: when receiving a prune not
+ * addressed to me on a LAN, schedule delayed join if I have
+ * downstream receivers.
+ */
+ upstream_router = find_pim6_nbr(&target);
+ if (upstream_router == (pim_nbr_entry_t *)NULL)
+ return (FALSE); /* I have no such neighbor */
+ group.sin6_len = sizeof(group);
+ group.sin6_family = AF_INET6;
+ source.sin6_len = sizeof(source);
+ source.sin6_family = AF_INET6;
+ while (num_groups--) {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ if (encod_group.masklen > (sizeof(struct in6_addr) << 3))
+ continue;
+ MASKLEN_TO_MASK6(encod_group.masklen, g_mask);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+ if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) {
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue; /* Ignore this group and jump to the next */
+ }
+
+ while (num_j_srcs--) {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+ /* sanity checks */
+ if (!inet6_valid_host(&source))
+ continue;
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+
+ /* (S,G) Join suppresion */
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ continue;
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "\tJOIN src %s, group %s - canceling "
+ "delayed join",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ /* Cancel the delayed join */
+ if(mrtentry_ptr->join_delay_timerid) {
+ timer_clearTimer(mrtentry_ptr->join_delay_timerid);
+ mrtentry_ptr->join_delay_timerid = 0;
+ }
+ }
+
+ while (num_p_srcs--) {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+ /* sanity checks */
+ if (!inet6_valid_host(&source))
+ continue;
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+ s_flags = encod_src.flags;
+
+ /* if P2P link (not addressed to me) ignore
+ */
+ if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT)
+ continue;
+
+ /*
+ * if non-null oiflist then schedule delayed join.
+ */
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ continue;
+
+ if(!(IF_ISEMPTY(&mrtentry_ptr->oifs))) {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "\tPRUNE src %s, group %s "
+ "- scheduling delayed join",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ schedule_delayed_join(mrtentry_ptr,
+ &target);
+ }
+ }
+
+ } /* while groups */
+
+ return(TRUE);
+ } /* if not unicast target */
+
+ /* I am the target of this join/prune:
+ * For joins, cancel delayed prunes that I have scheduled.
+ * For prunes, echo the prune and schedule delayed prunes on LAN or
+ * prune immediately on point-to-point links.
+ */
+ else {
+ while (num_groups--) {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "PIM Join/Prune received: grp: %s plen: %d, "
+ "%d jsrc, %d psrc",
+ inet6_fmt(&encod_group.mcast_addr),
+ encod_group.masklen, num_j_srcs, num_p_srcs);
+ if (encod_group.masklen > (sizeof(struct in6_addr) << 3))
+ continue; /* Ignore this group */
+ MASKLEN_TO_MASK6(encod_group.masklen, g_mask);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+ if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) {
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue; /* Ignore this group and jump to the next */
+ }
+
+ while (num_j_srcs--) {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+ if (!inet6_valid_host(&source))
+ continue;
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ continue;
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "\tJOIN src %s, group %s - canceling "
+ "delayed prune",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ /* Cancel the delayed prune */
+ if(mrtentry_ptr->prune_delay_timerids[mifi]) {
+ timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]);
+ mrtentry_ptr->prune_delay_timerids[mifi] = 0;
+ }
+ }
+
+ while (num_p_srcs--) {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+
+
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ continue;
+
+ /* if P2P link (addressed to me) prune immediately
+ */
+ if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) {
+ if(IF_ISSET(mifi, &mrtentry_ptr->oifs)) {
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "\tPRUNE(P2P) src %s,"
+ "group %s - pruning "
+ "mif",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Deleting pruned mif %d for src %s, grp %s",
+ mifi,
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs);
+ IF_SET(mifi, &new_pruned_oifs);
+ SET_TIMER(mrtentry_ptr->prune_timers[mifi], holdtime);
+
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &new_pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+
+ /* Handle transition to negative cache */
+ if(state_change == -1)
+ trigger_prune_alert(mrtentry_ptr);
+ } /* if is pruned */
+ } /* if p2p */
+
+ /* if LAN link, echo the prune and schedule delayed
+ * oif deletion
+ */
+ else {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "\tPRUNE(LAN) src %s, group "
+ "%s - scheduling delayed prune",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+
+ send_pim6_jp(mrtentry_ptr,
+ PIM_ACTION_PRUNE, mifi,
+ &target, holdtime, 1);
+ schedule_delayed_prune(mrtentry_ptr,
+ mifi, holdtime);
+ }
+ }
+ } /* while groups */
+ } /* else I am unicast target */
+
+ return(TRUE);
+}
+
+
+int
+send_pim6_jp(mrtentry_ptr, action, mifi, target_addr, holdtime, echo)
+ mrtentry_t *mrtentry_ptr;
+ int action; /* PIM_ACTION_JOIN or PIM_ACTION_PRUNE */
+ mifi_t mifi; /* vif to send join/prune on */
+ struct sockaddr_in6 *target_addr; /* encoded unicast target neighbor */
+ u_int16 holdtime; /* holdtime */
+ int echo;
+{
+ u_int8 *data_ptr, *data_start_ptr;
+
+ data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim));
+ data_start_ptr = data_ptr;
+
+ if(echo == 0 && mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
+ /* No upstream neighbor - don't send */
+ return(FALSE);
+ }
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG, 0,
+ "Sending %s: vif %s, src %s, group %s, "
+ "target %s, holdtime %d",
+ action==PIM_ACTION_JOIN ? "JOIN" : "PRUNE",
+ inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr),
+ inet6_fmt(&target_addr->sin6_addr), holdtime);
+
+ PUT_EUADDR6(target_addr->sin6_addr, data_ptr); /* encoded unicast target addr */
+ PUT_BYTE(0, data_ptr); /* Reserved */
+ *data_ptr++ = (u_int8)1; /* number of groups */
+ PUT_HOSTSHORT(holdtime, data_ptr); /* holdtime */
+
+ /* data_ptr points at the first, and only encoded mcast group */
+ PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr,
+ SINGLE_GRP_MSK6LEN, 0, data_ptr);
+
+ /* set the number of join and prune sources */
+ if(action == PIM_ACTION_JOIN) {
+ PUT_HOSTSHORT(1, data_ptr);
+ PUT_HOSTSHORT(0, data_ptr);
+ } else if(action == PIM_ACTION_PRUNE) {
+ PUT_HOSTSHORT(0, data_ptr);
+ PUT_HOSTSHORT(1, data_ptr);
+ }
+
+ PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN,
+ 0, data_ptr);
+
+ /* Cancel active graft */
+ if (echo == 0)
+ delete_pim6_graft_entry(mrtentry_ptr);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_JOIN_PRUNE,
+ data_ptr - data_start_ptr);
+
+ return(TRUE);
+}
+
+
+/************************************************************************
+ * PIM_ASSERT
+ ************************************************************************/
+
+/* Notes on assert prefs/metrics
+ * - For downstream routers, compare pref/metric previously received from
+ * winner against those in message.
+ * ==> store assert winner's pref/metric in mrtentry
+ * - For upstream router compare my actualy pref/metric for the source
+ * against those received in message.
+ * ==> store my actual pref/metric in srcentry
+ */
+
+int
+receive_pim6_assert(src, pim_message, datalen)
+ struct sockaddr_in6 *src;
+ register char *pim_message;
+ int datalen;
+{
+ mifi_t mifi;
+ pim6_encod_uni_addr_t eusaddr;
+ pim6_encod_grp_addr_t egaddr;
+ struct sockaddr_in6 source, group;
+ mrtentry_t *mrtentry_ptr;
+ u_int8 *data_ptr;
+ struct uvif *v;
+ u_int32 assert_preference;
+ u_int32 assert_metric;
+ u_int32 local_metric;
+ u_int32 local_preference;
+ u_int8 local_wins;
+ if_set new_pruned_oifs, new_leaves;
+ int state_change;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF) {
+ /* Either a local vif or somehow received PIM_ASSERT from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_ASSERT from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
+ return(FALSE); /* Shoudn't come on this interface */
+ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim));
+
+ /* Get the group and source addresses */
+ GET_EGADDR6(&egaddr, data_ptr);
+ GET_EUADDR6(&eusaddr, data_ptr);
+
+ /* Get the metric related info */
+ GET_HOSTLONG(assert_preference, data_ptr);
+ GET_HOSTLONG(assert_metric, data_ptr);
+
+ source.sin6_addr = eusaddr.unicast_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
+ group.sin6_addr = egaddr.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "PIM Assert received from %s: src %s, grp %s, "
+ "pref %d, metric %d",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr),
+ assert_preference, assert_metric);
+
+ if ((mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE))
+ == NULL) {
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_INFO, 0,
+ "\tFailed to create a mrtentry src:%s grp:%s",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr));
+ return(FALSE);
+ }
+ if(mrtentry_ptr->flags & MRTF_NEW) {
+ /* For some reason, it's possible for asserts to be processed
+ * before the data alerts a cache miss. Therefore, when an
+ * assert is received, create (S,G) state and continue, since
+ * we know by the assert that there are upstream forwarders.
+ */
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0, "\tNo MRT entry - creating...");
+
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+
+ /* Set oifs */
+ set_leaves(mrtentry_ptr);
+ calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
+
+ /* Add it to the kernel */
+ k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming,
+ &mrtentry_ptr->oifs);
+
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+
+ /* No need to call change_interfaces, but check for NULL oiflist */
+ if(IF_ISEMPTY(&mrtentry_ptr->oifs))
+ trigger_prune_alert(mrtentry_ptr);
+ }
+
+ /* If arrived on iif, I'm downstream of the asserted LAN.
+ * If arrived on oif, I'm upstream of the asserted LAN.
+ */
+ if (mifi == mrtentry_ptr->incoming) {
+ /* assert arrived on iif ==> I'm a downstream router */
+
+ /* Determine local (really that of upstream nbr!) pref/metric */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+
+ if(mrtentry_ptr->upstream &&
+ inet6_equal(&mrtentry_ptr->upstream->address, src) &&
+ assert_preference == local_preference &&
+ assert_metric == local_metric)
+
+ /* if assert from previous winner w/ same pref/metric,
+ * then assert sender wins again */
+ local_wins = FALSE;
+
+ else
+ /* assert from someone else or something changed */
+ local_wins = compare_metrics(local_preference,
+ local_metric,
+ &mrtentry_ptr->upstream->address,
+ assert_preference,
+ assert_metric, src);
+
+ /*
+ * This is between the assert sender and previous winner or rpf
+ * (who is the "local" in this case).
+ */
+ if(local_wins == TRUE) {
+ /* the assert-sender loses, so discard the assert */
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "\tAssert sender %s loses",
+ inet6_fmt(&src->sin6_addr));
+ return(TRUE);
+ }
+
+ /* The assert sender wins: upstream must be changed to the winner */
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0, "\tAssert sender %s wins",
+ inet6_fmt(&src->sin6_addr));
+ if(inet6_equal(&mrtentry_ptr->upstream->address, src)) {
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "\tChanging upstream nbr to %s",
+ inet6_fmt(&src->sin6_addr));
+ mrtentry_ptr->preference = assert_preference;
+ mrtentry_ptr->metric = assert_metric;
+ mrtentry_ptr->upstream = find_pim6_nbr(src);
+ }
+ SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT);
+ mrtentry_ptr->flags |= MRTF_ASSERTED;
+
+ /* Send a join for the S,G if oiflist is non-empty */
+ if(!(IF_ISEMPTY(&mrtentry_ptr->oifs)))
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN,
+ mrtentry_ptr->incoming, src, 0, 0);
+
+ } /* if assert on iif */
+
+ /* If the assert arrived on an oif: */
+ else {
+ if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs)))
+ return(FALSE);
+ /* assert arrived on oif ==> I'm a upstream router */
+
+ /* Determine local pref/metric */
+ local_metric = mrtentry_ptr->source->metric;
+ local_preference = mrtentry_ptr->source->preference;
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &v->uv_linklocal->pa_addr,
+ assert_preference,
+ assert_metric, src);
+
+ if(local_wins == FALSE) {
+
+ /* Assert sender wins - prune the interface */
+
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "\tAssert sender %s wins - pruning...",
+ inet6_fmt(&src->sin6_addr));
+
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs);
+ IF_SET(mifi, &new_pruned_oifs);
+ IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
+ SET_TIMER(mrtentry_ptr->prune_timers[mifi],
+ PIM_JOIN_PRUNE_HOLDTIME);
+
+ if (IF_ISSET(mifi, &mrtentry_ptr->leaves)) {
+ IF_COPY(&mrtentry_ptr->leaves, &new_leaves);
+ IF_CLR(mifi, &new_leaves);
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &new_pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &new_leaves);
+ }
+ else {
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &new_pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+ }
+
+ /* Handle transition to negative cache */
+ if(state_change == -1)
+ trigger_prune_alert(mrtentry_ptr);
+
+ } /* assert sender wins */
+
+ else {
+
+ /* Local wins (assert sender loses):
+ * send assert and schedule prune
+ */
+
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "\tAssert sender %s loses - "
+ "sending assert and scheuling prune",
+ inet6_fmt(&src->sin6_addr));
+
+ if(!(IF_ISSET(mifi, &mrtentry_ptr->leaves))) {
+ /*
+ * No directly connected receivers - delay prune
+ */
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE,
+ mifi, &v->uv_linklocal->pa_addr,
+ PIM_JOIN_PRUNE_HOLDTIME, 0);
+ schedule_delayed_prune(mrtentry_ptr, mifi,
+ PIM_JOIN_PRUNE_HOLDTIME);
+ }
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ }
+
+ } /* if assert on oif */
+
+ return(TRUE);
+}
+
+
+int
+send_pim6_assert(source, group, mifi, mrtentry_ptr)
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ mifi_t mifi;
+ mrtentry_t *mrtentry_ptr;
+{
+ u_int8 *data_ptr;
+ u_int8 *data_start_ptr;
+ u_int32 local_preference;
+ u_int32 local_metric;
+
+ data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim));
+ data_start_ptr = data_ptr;
+ PUT_EGADDR6(group->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
+ PUT_EUADDR6(source->sin6_addr, data_ptr);
+
+ local_metric = mrtentry_ptr->source->metric;
+ local_preference = mrtentry_ptr->source->preference;
+
+ PUT_HOSTLONG(local_preference, data_ptr);
+ PUT_HOSTLONG(local_metric, data_ptr);
+
+ IF_DEBUG(DEBUG_PIM_ASSERT)
+ log(LOG_DEBUG, 0,
+ "PIM Assert sending: src %s, grp %s, "
+ "pref %d, metric %d",
+ inet6_fmt(&source->sin6_addr),
+ inet6_fmt(&group->sin6_addr),
+ local_metric, local_preference);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_ASSERT,
+ data_ptr - data_start_ptr);
+
+ return(TRUE);
+}
+
+
+/* Return TRUE if the local win, otherwise FALSE */
+static int
+compare_metrics(local_preference, local_metric, local_address,
+ remote_preference, remote_metric, remote_address)
+ u_int32 local_preference;
+ u_int32 local_metric;
+ struct sockaddr_in6 *local_address;
+ u_int32 remote_preference;
+ u_int32 remote_metric;
+ struct sockaddr_in6 *remote_address;
+{
+ /* Now lets see who has a smaller gun (aka "asserts war") */
+ /* FYI, the smaller gun...err metric wins, but if the same
+ * caliber, then the bigger network address wins. The order of
+ * treatment is: preference, metric, address.
+ */
+ /* The RPT bits are already included as the most significant bits
+ * of the preferences.
+ */
+ if (remote_preference > local_preference)
+ return TRUE;
+ if (remote_preference < local_preference)
+ return FALSE;
+ if (remote_metric > local_metric)
+ return TRUE;
+ if (remote_metric < local_metric)
+ return FALSE;
+ if (inet6_greaterthan(local_address, remote_address))
+ return TRUE;
+ return FALSE;
+}
+
+
+
+
+/************************************************************************
+ * PIM_GRAFT
+ ************************************************************************/
+
+
+u_long graft_retrans_timer; /* Graft retransmission timer */
+pim_graft_entry_t *graft_list; /* Active grafting entries */
+
+
+void
+delete_pim6_graft_entry(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ pim_graft_entry_t *graft_entry;
+
+ if(mrtentry_ptr->graft == (pim_graft_entry_t *)NULL)
+ return;
+ graft_entry = mrtentry_ptr->graft;
+
+ if(graft_entry->prev)
+ graft_entry->prev->next = graft_entry->next;
+ else
+ graft_list = graft_entry->next;
+ if(graft_entry->next)
+ graft_entry->next->prev = graft_entry->prev;
+ mrtentry_ptr->graft = (pim_graft_entry_t *)NULL;
+ free(graft_entry);
+
+ /* Stop the timer if there are no more entries */
+ if(!graft_list) {
+ timer_clearTimer(graft_retrans_timer);
+ graft_retrans_timer = 0;
+ }
+}
+
+
+static int
+retransmit_pim6_graft(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ u_int8 *data_ptr, *data_start_ptr;
+
+ data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim));
+ data_start_ptr = data_ptr;
+
+ if (mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
+ /* No upstream neighbor - don't send */
+ return(FALSE);
+ }
+
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0,
+ "Sending GRAFT: vif %s, src %s, grp %s, dst %s",
+ inet6_fmt(&uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->upstream->address.sin6_addr));
+
+
+ /* unicast target */
+ PUT_EUADDR6(mrtentry_ptr->upstream->address.sin6_addr, data_ptr);
+ PUT_BYTE(0, data_ptr); /* Reserved */
+ *data_ptr++ = (u_int8)1; /* number of groups */
+ PUT_HOSTSHORT(0, data_ptr); /* no holdtime */
+
+ /* data_ptr points at the first, and only encoded mcast group */
+ PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr,
+ SINGLE_GRP_MSK6LEN, 0, data_ptr);
+
+ /* set the number of join(graft) and prune sources */
+ PUT_HOSTSHORT(1, data_ptr);
+ PUT_HOSTSHORT(0, data_ptr);
+
+ PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN,
+ 0, data_ptr);
+
+ send_pim6(pim6_send_buf,
+ &uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr,
+ &mrtentry_ptr->upstream->address,
+ PIM_GRAFT, data_ptr - data_start_ptr);
+
+ return(TRUE);
+}
+
+
+static void
+retransmit_all_pim6_grafts(arg)
+ void *arg; /* UNUSED */
+{
+ pim_graft_entry_t *graft_ptr;
+
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0, "Retransmitting all pending PIM-Grafts");
+
+
+ for(graft_ptr = graft_list;
+ graft_ptr != NULL;
+ graft_ptr = graft_ptr->next) {
+
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0, "\tGRAFT src %s, grp %s",
+ inet6_fmt(&graft_ptr->mrtlink->source->address.sin6_addr),
+ inet6_fmt(&graft_ptr->mrtlink->group->group.sin6_addr));
+
+ retransmit_pim6_graft(graft_ptr->mrtlink);
+ }
+
+ if (graft_list)
+ timer_setTimer(PIM_GRAFT_RETRANS_PERIOD,
+ retransmit_all_pim6_grafts, (void *)NULL);
+}
+
+
+int
+receive_pim6_graft(src, pim_message, datalen, pimtype)
+ struct sockaddr_in6 *src;
+ register char *pim_message;
+ int datalen;
+ int pimtype;
+{
+ mifi_t mifi;
+ struct uvif *v;
+ pim6_encod_uni_addr_t uni_target_addr;
+ pim6_encod_grp_addr_t encod_group;
+ pim6_encod_src_addr_t encod_src;
+ u_int8 *data_ptr;
+ u_int8 num_groups;
+ u_int16 holdtime;
+ u_int16 num_j_srcs;
+ u_int16 num_p_srcs;
+ struct sockaddr_in6 source, group;
+ struct in6_addr s_mask, g_mask;
+ u_int8 s_flags;
+ u_int8 reserved;
+ mrtentry_t *mrtentry_ptr;
+ int state_change;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF) {
+ /* Either a local vif or somehow received PIM_GRAFT from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_GRAFT from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
+ return(FALSE); /* Shoudn't come on this interface */
+ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim));
+
+ /* Get the target address */
+ GET_EUADDR6(&uni_target_addr, data_ptr);
+ GET_BYTE(reserved, data_ptr);
+ GET_BYTE(num_groups, data_ptr);
+ if (num_groups == 0)
+ return (FALSE); /* No indication for groups in the message */
+ GET_HOSTSHORT(holdtime, data_ptr);
+
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0,
+ "PIM %s received from %s on mif %d, grps: %d",
+ pimtype == PIM_GRAFT ? "GRAFT" : "GRAFT-ACK",
+ inet6_fmt(&src->sin6_addr), mifi, num_groups);
+
+ group.sin6_len = sizeof(group);
+ group.sin6_family = AF_INET6;
+ source.sin6_len = sizeof(source);
+ source.sin6_family = AF_INET6;
+ while (num_groups--) {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0,
+ " PIM graft: grp: %s, plen: %d, %d jsrcs, %d psrcs",
+ inet6_fmt(&encod_group.mcast_addr),
+ encod_group.masklen, num_j_srcs, num_p_srcs);
+ if (encod_group.masklen > (sizeof(struct in6_addr) << 3))
+ continue;
+ MASKLEN_TO_MASK6(encod_group.masklen, g_mask);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+ if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) {
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue; /* Ignore this group and jump to the next */
+ }
+
+ while (num_j_srcs--) {
+ GET_ESADDR6(&encod_src, data_ptr);
+ if (encod_src.masklen > (sizeof(struct in6_addr) << 3))
+ continue;
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ continue;
+
+ if(pimtype == PIM_GRAFT) {
+ /* Graft */
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0,
+ "\tGRAFT src %s, group %s - "
+ "forward data on mif %d",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr),
+ mifi);
+
+ /* Cancel any delayed prune */
+ if(mrtentry_ptr->prune_delay_timerids[mifi]) {
+ timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]);
+ mrtentry_ptr->prune_delay_timerids[mifi] = 0;
+ }
+
+ /* Add to oiflist (unprune) */
+ if (IF_ISSET(mifi, &mrtentry_ptr->pruned_oifs)) {
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ SET_TIMER(mrtentry_ptr->prune_timers[mifi], 0);
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+ if(state_change == 1)
+ trigger_join_alert(mrtentry_ptr);
+ }
+ } /* Graft */
+ else {
+ /* Graft-Ack */
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0,
+ "\tGRAFT-ACK src %s, group %s - "
+ "forward data on mif %d",
+ inet6_fmt(&source.sin6_addr),
+ inet6_fmt(&group.sin6_addr),
+ mifi);
+ if(mrtentry_ptr->graft)
+ delete_pim6_graft_entry(mrtentry_ptr);
+ }
+ }
+ /* Ignore anything in the prune portion of the message! */
+ }
+
+ /* Respond to graft with a graft-ack */
+ if(pimtype == PIM_GRAFT) {
+ IF_DEBUG(DEBUG_PIM_GRAFT)
+ log(LOG_DEBUG, 0, "Sending GRAFT-ACK: mif %s, dst %s",
+ inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr),
+ inet6_fmt(&src->sin6_addr));
+ bcopy(pim_message, pim6_send_buf, datalen);
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ src, PIM_GRAFT_ACK, datalen - sizeof(struct pim));
+ }
+
+ return(TRUE);
+}
+
+int
+send_pim6_graft(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ pim_graft_entry_t *new_graft;
+ int was_sent = 0;
+
+ if(mrtentry_ptr->graft != (pim_graft_entry_t *)NULL)
+ /* Already sending grafts */
+ return(FALSE);
+
+ /* Send the first graft */
+ was_sent = retransmit_pim6_graft(mrtentry_ptr);
+ if(!was_sent)
+ return(FALSE);
+
+ /* Set up retransmission */
+ new_graft = (pim_graft_entry_t *)malloc(sizeof(pim_graft_entry_t));
+ if (new_graft == (pim_graft_entry_t *)NULL) {
+ log(LOG_WARNING, 0,
+ "Memory allocation error for graft entry src %s, grp %s",
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
+ return(FALSE);
+ }
+ new_graft->next = graft_list;
+ new_graft->prev = (pim_graft_entry_t *)NULL;
+ new_graft->mrtlink = mrtentry_ptr;
+ if(graft_list)
+ graft_list->prev = new_graft;
+ graft_list = new_graft;
+ mrtentry_ptr->graft = new_graft;
+
+ /* Set up timer if not running */
+ if(!graft_retrans_timer)
+ graft_retrans_timer = timer_setTimer(PIM_GRAFT_RETRANS_PERIOD,
+ retransmit_all_pim6_grafts,
+ (void *)NULL);
+
+ return(TRUE);
+}
diff --git a/usr.sbin/pim6dd/pim6dd.8 b/usr.sbin/pim6dd/pim6dd.8
new file mode 100644
index 0000000..6109b91
--- /dev/null
+++ b/usr.sbin/pim6dd/pim6dd.8
@@ -0,0 +1,112 @@
+.\" Copyright (C) 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pim6dd.8,v 1.3 1999/08/13 09:20:43 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd Nov 17, 1998
+.Dt PIM6DD 8
+.Os KAME
+.Sh NAME
+.Nm pim6dd
+.Nd PIM for IPv6 dense mode daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d Op debug_level Op ,debug_level
+.Sh DESCRIPTION
+.Nm Pim6dd
+is an IPv6 multicast routing daemon, which supports
+PIMv2(Protocol Independent Multicast Version 2) dense mode
+for IPv6.
+.Pp
+Options supported by
+.Nm pim6dd :
+.Bl -tag -width Ds
+.It Fl c Ar configfile
+Specify alternate location,
+.Ar configfile ,
+for configuration file.
+By default,
+.Pa /usr/local/v6/etc/pim6dd.conf
+is used.
+.It Fl d
+Specify debug levels. If this option is specified without any arguments,
+all debug messages will be printed out.
+A subset of the messages to be printed out can be specified
+as arguments of the option.
+Valid debug levels are
+.Ic timeout, packets, interfaces, kernel, mfc, pim_detail, pim_hello,
+.Ic kernel, mfc, pim_detail, pim_hello, pim_jp, pim_graft, pim_asserts,
+.Ic pim_routes, pim_timers, rpf, pim, routes, routers, timers,
+and
+.Ic asserts.
+.El
+.Pp
+.Nm Pim6dd
+automatically configures itself to forward on all multicast-capable
+interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
+the "loopback interface").
+To override the default configuration,
+configuration commands may be placed in
+.Pa /usr/local/v6/etc/pim6dd.conf
+(or an alternative file, specified by the "\-c" option).
+.\"
+.Sh FILES
+.Bl -tag -width /usr/local/v6/etc/pim6dd.conf -compact
+.It Pa /usr/local/v6/etc/pim6dd.conf
+The default configuration file.
+.El
+.Sh SEE ALSO
+.Xr daemon 3 ,
+.Xr pim6dd.conf 5
+.Sh HISTORY
+The
+.Nm
+command is based on
+.Nm pimdd,
+which is an IPv4 multicast routing daemon
+developed at the University of Oregon.
+.Nm Pimdd
+has been derived from PIM sparse-mode
+.Nm pimd
+developed at University of Southern California.
+Part of these two programs above has also been derived from
+.Nm mrouted.
+.Nm Mrouted
+is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
+.\"
+.Sh BUGS
+.Nm Pim6dd
+does not contain any unicast routing engine, so a unicast routing
+daemon needs to run on the system.
+.Pp
+The kernel unicast routing table is periodically polled by
+.Nm
+in order to follow changes of existing unicast routes.
+.\"
diff --git a/usr.sbin/pim6dd/pim6dd.conf.5 b/usr.sbin/pim6dd/pim6dd.conf.5
new file mode 100644
index 0000000..a526c59
--- /dev/null
+++ b/usr.sbin/pim6dd/pim6dd.conf.5
@@ -0,0 +1,157 @@
+.\" Copyright (C) 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pim6dd.conf.5,v 1.2 1999/12/10 06:08:49 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd Nov 17, 1998
+.Dt PIM6DD.CONF 5
+.Os KAME
+.Sh NAME
+.Nm pim6dd.conf
+.Nd "config file for pim6dd, PIM for IPv6 dense mode daemon"
+.\"
+.Sh DESCRIPTION
+The file describes how the
+.Nm pim6dd
+daemon treats each interface on the system.
+By default(including the case where there is no configuration file),
+PIM will be activated on all interfaces, which can be overridden
+by the file.
+Lines beginning with
+.Ql #
+are comments.
+.Pp
+There are four types of configuration commands:
+.Bl -tag -width Ds -compact
+.It Xo
+.Ic default_source_preference Ar preference
+.Xc
+Specifies a default preference value when sending a PIM assert message.
+Preferences are used by assert elections to determine upstream routers.
+Currently
+.Nm
+cannot reliably obtain preferences and metrics from the
+unicast routing protocols, so a default value may be configured.
+.\"
+.It Ic default_source_metric Ar metric
+Specifies a default metric value when sending a PIM assert message.
+It is recommended that preferences be set such that metrics are never
+consulted. However, default metrics may also be set and will default to
+1024.
+.\"
+.It Xo
+.Ic phyint Ar interface
+.Op disable
+.Xc
+Specifies
+.Nm
+to ignore the interface even if the interface is multicast-capable.
+Interfaces are specified in the form of "name unit", such as
+.Ar gif0
+and
+.Ar ep1.
+.\"
+.It Xo
+.Ic phyint Ar interface
+.Op preference Ar preference
+.Op metric Ar metric
+.Xc
+Specifies the preference and/or metric values when sending a PIM
+assert message on the interface.
+.\"
+.It Xo
+.Ic filter Ar groupaddrs Ar interfaces...
+.Xc
+Specifies an output filter. If an incoming multicast packet's destination
+matches the specified
+.Ar groupaddrs,
+the packet is not sent on the
+.Ar interfaces.
+Moreover, if there is no other interface than the specified
+interfaces,
+.Nm pim6dd
+sends a prune message to the upstream neighbor.
+Valid formats of
+.Ar groupaddrs
+are as follows.
+.Bl -tag -width Ds -compact
+.It Ar multicastaddr1-multicastaddr2
+specifies a numerical range of a scope.
+Multicast addresses
+from
+.Ar multicastaddr1
+to
+.Ar multicastaddr2
+will be filtered out.
+Note that neither a white space nor a tab character must not be
+inserted before nor after
+.Ql - .
+.It Ar multicastaddr/prefixlen
+specifies a group prefix of a scope.
+Multicast addresses which match the specified prefix will be filtered
+out.
+If
+.Ar prefixlen
+is omitted, it means the exact match for
+.Ar multicastaddr.
+.El
+.Ar interfaces
+are specified as a blank separated list of interfaces. Each interface is
+specified in the form of "name unit".
+.El
+.\"
+.Sh EXAMPLE
+.Bd -literal -offset
+#phyint gif0 disable
+#phyint ep0 preference 101
+phyint de0 disable
+filter ff15::4000/120 gif1 gif2
+filter ff18::1000-ff18::1050 gif3
+.Ed
+.Sh SEE ALSO
+.Xr pim6dd 8
+.Sh HISTORY
+The
+.Nm pim6dd
+command and the configuration file
+.Nm
+are based on
+.Nm pimdd,
+which is an IPv4 multicast routing daemon
+developed at the University of Oregon.
+.Nm Pimdd
+has been derived from PIM sparse-mode
+.Nm pimd
+developed at University of Southern California.
+Part of these two programs above has also been derived from
+.Nm mrouted.
+.Nm Mrouted
+is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/pim6dd/pimdd.h b/usr.sbin/pim6dd/pimdd.h
new file mode 100644
index 0000000..31b46d5
--- /dev/null
+++ b/usr.sbin/pim6dd/pimdd.h
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: pimdd.h,v 1.1.1.1 1999/08/08 23:30:53 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include <netinet6/pim6.h>
+
+#define PIM_PROTOCOL_VERSION 2
+#define PIMD_VERSION PIM_PROTOCOL_VERSION
+#define PIMD_SUBVERSION 1
+#if 0
+#define PIM_CONSTANT 0x000eff00 /* constant portion of 'group' field */
+#endif
+#define PIM_CONSTANT 0
+#define PIMD_LEVEL (PIM_CONSTANT | PIMD_VERSION | (PIMD_SUBVERSION << 8))
+
+#define INADDR_ALL_PIM_ROUTERS (u_int32)0xe000000D /* 224.0.0.13 */
+
+
+/* PIM protocol timers (in seconds) */
+#ifndef TIMER_INTERVAL
+#define TIMER_INTERVAL 5 /* virtual timer granularity */
+#endif /* TIMER_INTERVAL */
+
+#define PIM_DATA_TIMEOUT 210
+#define PIM_TIMER_HELLO_PERIOD 30
+#define PIM_JOIN_PRUNE_HOLDTIME 210
+#define PIM_RANDOM_DELAY_JOIN_TIMEOUT 3
+#define PIM_GRAFT_RETRANS_PERIOD 3
+#define PIM_TIMER_HELLO_HOLDTIME (3.5 * PIM_TIMER_HELLO_PERIOD)
+#define PIM_ASSERT_TIMEOUT 210
+
+
+/* Misc definitions */
+#define SINGLE_SRC_MSKLEN 32 /* the single source mask length */
+#define SINGLE_GRP_MSKLEN 32 /* the single group mask length */
+
+#define SINGLE_SRC_MSK6LEN 128 /* the single source mask length for IPv6*/
+#define SINGLE_GRP_MSK6LEN 128 /* the single group mask length for IPv6*/
+
+/* TODO: change? */
+#define PIM_GROUP_PREFIX_DEFAULT_MASKLEN 16 /* The default group masklen if
+ * omitted in the config file.
+ */
+
+#define UCAST_ROUTING_CHECK_INTERVAL 20 /* Unfortunately, if the unicast
+ * routing changes, the kernel
+ * or any of the existing
+ * unicast routing daemons
+ * don't send us a signal.
+ * Have to ask periodically the
+ * kernel for any route changes.
+ * Default: every 20 seconds.
+ * Sigh.
+ */
+
+
+#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */
+
+#define DEFAULT_LOCAL_PREF 101 /* Default assert preference */
+#define DEFAULT_LOCAL_METRIC 1024 /* Default assert metric */
+
+/**************************************************************************
+ * PIM Encoded-Unicast, Encoded-Group and Encoded-Source Address formats *
+ *************************************************************************/
+/* Address families definition */
+#define ADDRF_RESERVED 0
+#define ADDRF_IPv4 1
+#define ADDRF_IPv6 2
+#define ADDRF_NSAP 3
+#define ADDRF_HDLC 4
+#define ADDRF_BBN1822 5
+#define ADDRF_802 6
+#define ADDRF_ETHERNET ADDRF_802
+#define ADDRF_E163 7
+#define ADDRF_E164 8
+#define ADDRF_SMDS ADDRF_E164
+#define ADDRF_ATM ADDRF_E164
+#define ADDRF_F69 9
+#define ADDRF_TELEX ADDRF_F69
+#define ADDRF_X121 10
+#define ADDRF_X25 ADDRF_X121
+#define ADDRF_IPX 11
+#define ADDRF_APPLETALK 12
+#define ADDRF_DECNET_IV 13
+#define ADDRF_BANYAN 14
+#define ADDRF_E164_NSAP 15
+
+/* Addresses Encoding Type (specific for each Address Family */
+#define ADDRT_IPv4 0
+#define ADDRT_IPv6 0
+
+
+#if 0 /* XXX: the definition is for IPv4 only */
+/* Encoded-Unicast: 6 bytes long */
+typedef struct pim_encod_uni_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int32 unicast_addr; /* XXX: Note the 32-bit boundary
+ * misalignment for the unicast
+ * address when placed in the
+ * memory. Must read it byte-by-byte!
+ */
+} pim_encod_uni_addr_t;
+#endif
+/* Encoded-Unicast: 18 bytes long */
+typedef struct pim6_encod_uni_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ struct in6_addr unicast_addr; /* XXX: Note the 32-bit boundary
+ * misalignment for the unicast
+ * address when placed in the
+ * memory. Must read it byte-by-byte!
+ */
+} pim6_encod_uni_addr_t;
+
+#if 0 /* XXX: the definition is for IPv4 only */
+/* Encoded-Group */
+typedef struct pim_encod_grp_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 reserved;
+ u_int8 masklen;
+ u_int32 mcast_addr;
+} pim_encod_grp_addr_t;
+#endif
+/* Encoded-Group */
+typedef struct pim6_encod_grp_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 reserved;
+ u_int8 masklen;
+ struct in6_addr mcast_addr;
+} pim6_encod_grp_addr_t;
+
+#if 0 /* XXX: the definition is for IPv4 only */
+/* Encoded-Source */
+typedef struct pim_encod_src_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 flags;
+ u_int8 masklen;
+ u_int32 src_addr;
+} pim_encod_src_addr_t;
+#endif
+/* Encoded-Source */
+typedef struct pim6_encod_src_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 flags;
+ u_int8 masklen;
+ struct in6_addr src_addr;
+} pim6_encod_src_addr_t;
+#define USADDR_RP_BIT 0x1
+#define USADDR_WC_BIT 0x2
+#define USADDR_S_BIT 0x4
+
+/**************************************************************************
+ * PIM Messages formats *
+ *************************************************************************/
+/* TODO: XXX: some structures are probably not used at all */
+
+typedef struct pim pim_header_t;
+
+/* PIM Hello */
+typedef struct pim_hello_ {
+ u_int16 option_type; /* Option type */
+ u_int16 option_length; /* Length of the Option Value field in bytes */
+} pim_hello_t;
+
+#if 0
+/* PIM Join/Prune: XXX: all 32-bit addresses misaligned! */
+typedef struct pim_jp_header_ {
+ pim_encod_uni_addr_t encod_upstream_nbr;
+ u_int8 reserved;
+ u_int8 num_groups;
+ u_int16 holdtime;
+} pim_jp_header_t;
+
+typedef struct pim_jp_encod_grp_ {
+ pim_encod_grp_addr_t encod_grp;
+ u_int16 number_join_src;
+ u_int16 number_prune_src;
+} pim_jp_encod_grp_t;
+#endif
+
+#define PIM_ACTION_NOTHING 0
+#define PIM_ACTION_JOIN 1
+#define PIM_ACTION_PRUNE 2
+
+#define PIM_IIF_SOURCE 1
+#define PIM_IIF_RP 2
+
+#define PIM_ASSERT_RPT_BIT 0x80000000
+
+
+/* PIM messages type */
+#define PIM_HELLO 0
+#ifndef PIM_REGISTER
+#define PIM_REGISTER 1
+#endif
+#define PIM_REGISTER_STOP 2
+#define PIM_JOIN_PRUNE 3
+#define PIM_BOOTSTRAP 4
+#define PIM_ASSERT 5
+#define PIM_GRAFT 6
+#define PIM_GRAFT_ACK 7
+#define PIM_CAND_RP_ADV 8
+
+#define PIM_V2_HELLO PIM_HELLO
+#define PIM_V2_REGISTER PIM_REGISTER
+#define PIM_V2_REGISTER_STOP PIM_REGISTER_STOP
+#define PIM_V2_JOIN_PRUNE PIM_JOIN_PRUNE
+#define PIM_V2_BOOTSTRAP PIM_BOOTSTRAP
+#define PIM_V2_ASSERT PIM_ASSERT
+#define PIM_V2_GRAFT PIM_GRAFT
+#define PIM_V2_GRAFT_ACK PIM_GRAFT_ACK
+#define PIM_V2_CAND_RP_ADV PIM_CAND_RP_ADV
+
+#define PIM_V1_QUERY 0
+#define PIM_V1_REGISTER 1
+#define PIM_V1_REGISTER_STOP 2
+#define PIM_V1_JOIN_PRUNE 3
+#define PIM_V1_RP_REACHABILITY 4
+#define PIM_V1_ASSERT 5
+#define PIM_V1_GRAFT 6
+#define PIM_V1_GRAFT_ACK 7
+
+/* Vartious options from PIM messages definitions */
+/* PIM_HELLO definitions */
+#define PIM_MESSAGE_HELLO_HOLDTIME 1
+#define PIM_MESSAGE_HELLO_HOLDTIME_LENGTH 2
+#define PIM_MESSAGE_HELLO_HOLDTIME_FOREVER 0xffff
+
+
+#define MASK_TO_MASKLEN(mask, masklen) \
+ do { \
+ register u_int32 tmp_mask = ntohl((mask)); \
+ register u_int8 tmp_masklen = sizeof((mask)) << 3; \
+ for ( ; tmp_masklen > 0; tmp_masklen--, tmp_mask >>= 1) \
+ if (tmp_mask & 0x1) \
+ break; \
+ (masklen) = tmp_masklen; \
+ } while (0)
+
+#define MASKLEN_TO_MASK(masklen, mask) \
+do { \
+ (mask) = (masklen)? htonl(~0 << ((sizeof((mask)) << 3) - (masklen))) : 0;\
+} while (0)
+
+#define MASKLEN_TO_MASK6(masklen, mask6) \
+ do {\
+ u_char maskarray[8] = \
+ {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \
+ int bytelen, bitlen, i; \
+ memset(&(mask6), 0, sizeof(mask6));\
+ bytelen = (masklen) / 8;\
+ bitlen = (masklen) % 8;\
+ for (i = 0; i < bytelen; i++) \
+ (mask6).s6_addr[i] = 0xff;\
+ if (bitlen) \
+ (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \
+ }while(0);
+
+/*
+ * A bunch of macros because of the lack of 32-bit boundary alignment.
+ * All because of one misalligned address format. Hopefully this will be
+ * fixed in PIMv3. (cp) must be (u_int8 *) .
+ */
+/* Originates from Eddy Rusty's (eddy@isi.edu) PIM-SM implementation for
+ * gated.
+ */
+
+/* PUT_NETLONG puts "network ordered" data to the datastream.
+ * PUT_HOSTLONG puts "host ordered" data to the datastream.
+ * GET_NETLONG gets the data and keeps it in "network order" in the memory
+ * GET_HOSTLONG gets the data, but in the memory it is in "host order"
+ * The same for all {PUT,GET}_{NET,HOST}{SHORT,LONG}
+ */
+#define GET_BYTE(val, cp) ((val) = *(cp)++)
+#define PUT_BYTE(val, cp) (*(cp)++ = (u_int8)(val))
+
+#define GET_HOSTSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (*(cp)++) << 8; \
+ Xv |= *(cp)++; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_HOSTSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (u_int16)(val); \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)Xv; \
+ } while (0)
+
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+#define GET_NETSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = *(cp)++; \
+ Xv |= (*(cp)++) << 8; \
+ (val) = Xv; \
+ } while (0)
+#define PUT_NETSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (u_int16)(val); \
+ *(cp)++ = (u_int8)Xv; \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ } while (0)
+#else
+#define GET_NETSHORT(val, cp) GET_HOSTSHORT(val, cp)
+#define PUT_NETSHORT(val, cp) PUT_HOSTSHORT(val, cp)
+#endif /* {GET,PUT}_NETSHORT */
+
+#define GET_HOSTLONG(val, cp) \
+ do { \
+ register u_long Xv; \
+ Xv = (*(cp)++) << 24; \
+ Xv |= (*(cp)++) << 16; \
+ Xv |= (*(cp)++) << 8; \
+ Xv |= *(cp)++; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_HOSTLONG(val, cp) \
+ do { \
+ register u_int32 Xv; \
+ Xv = (u_int32)(val); \
+ *(cp)++ = (u_int8)(Xv >> 24); \
+ *(cp)++ = (u_int8)(Xv >> 16); \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)Xv; \
+ } while (0)
+
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+#define GET_NETLONG(val, cp) \
+ do { \
+ register u_long Xv; \
+ Xv = *(cp)++; \
+ Xv |= (*(cp)++) << 8; \
+ Xv |= (*(cp)++) << 16; \
+ Xv |= (*(cp)++) << 24; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_NETLONG(val, cp) \
+ do { \
+ register u_int32 Xv; \
+ Xv = (u_int32)(val); \
+ *(cp)++ = (u_int8)Xv; \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)(Xv >> 16); \
+ *(cp)++ = (u_int8)(Xv >> 24); \
+ } while (0)
+#else
+#define GET_NETLONG(val, cp) GET_HOSTLONG(val, cp)
+#define PUT_NETLONG(val, cp) PUT_HOSTLONG(val, cp)
+#endif /* {GET,PUT}_HOSTLONG */
+
+
+#define GET_ESADDR(esa, cp) \
+ do { \
+ (esa)->addr_family = *(cp)++; \
+ (esa)->encod_type = *(cp)++; \
+ (esa)->flags = *(cp)++; \
+ (esa)->masklen = *(cp)++; \
+ GET_NETLONG((esa)->src_addr, (cp)); \
+ } while(0)
+
+#define GET_ESADDR6(esa, cp) /* XXX: hard coding */ \
+ do { \
+ (esa)->addr_family = *(cp)++; \
+ (esa)->encod_type = *(cp)++; \
+ (esa)->flags = *(cp)++; \
+ (esa)->masklen = *(cp)++; \
+ memcpy(&(esa)->src_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_ESADDR(addr, masklen, flags, cp) \
+ do { \
+ u_int32 mask; \
+ MASKLEN_TO_MASK((masklen), mask); \
+ *(cp)++ = ADDRF_IPv4; /* family */ \
+ *(cp)++ = ADDRT_IPv4; /* type */ \
+ *(cp)++ = (flags); /* flags */ \
+ *(cp)++ = (masklen); \
+ PUT_NETLONG((addr) & mask, (cp)); \
+ } while(0)
+
+#define PUT_ESADDR6(addr, masklen, flags, cp) \
+ do { \
+ int i; \
+ struct in6_addr maskaddr; \
+ MASKLEN_TO_MASK6(masklen, maskaddr); \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ *(cp)++ = (flags); /* flags */ \
+ *(cp)++ = (masklen); \
+ for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \
+ *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \
+ } while(0)
+
+#define GET_EGADDR(ega, cp) \
+ do { \
+ (ega)->addr_family = *(cp)++; \
+ (ega)->encod_type = *(cp)++; \
+ (ega)->reserved = *(cp)++; \
+ (ega)->masklen = *(cp)++; \
+ GET_NETLONG((ega)->mcast_addr, (cp)); \
+ } while(0)
+
+#define GET_EGADDR6(ega, cp) /* XXX: hard coding */ \
+ do { \
+ (ega)->addr_family = *(cp)++; \
+ (ega)->encod_type = *(cp)++; \
+ (ega)->reserved = *(cp)++; \
+ (ega)->masklen = *(cp)++; \
+ memcpy(&(ega)->mcast_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_EGADDR(addr, masklen, reserved, cp) \
+ do { \
+ u_int32 mask; \
+ MASKLEN_TO_MASK((masklen), mask); \
+ *(cp)++ = ADDRF_IPv4; /* family */ \
+ *(cp)++ = ADDRT_IPv4; /* type */ \
+ *(cp)++ = (reserved); /* reserved; should be 0 */ \
+ *(cp)++ = (masklen); \
+ PUT_NETLONG((addr) & mask, (cp)); \
+ } while(0)
+
+#define PUT_EGADDR6(addr, masklen, reserved, cp) \
+ do { \
+ int i; \
+ struct in6_addr maskaddr; \
+ MASKLEN_TO_MASK6(masklen, maskaddr); \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ *(cp)++ = (reserved); /* reserved; should be 0 */ \
+ *(cp)++ = (masklen); \
+ for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \
+ *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \
+ } while(0)
+
+#define GET_EUADDR(eua, cp) \
+ do { \
+ (eua)->addr_family = *(cp)++; \
+ (eua)->encod_type = *(cp)++; \
+ GET_NETLONG((eua)->unicast_addr, (cp)); \
+ } while(0)
+
+#define GET_EUADDR6(eua, cp) /* XXX hard conding */ \
+ do { \
+ (eua)->addr_family = *(cp)++; \
+ (eua)->encod_type = *(cp)++; \
+ memcpy(&(eua)->unicast_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_EUADDR(addr, cp) \
+ do { \
+ *(cp)++ = ADDRF_IPv4; /* family */ \
+ *(cp)++ = ADDRT_IPv4; /* type */ \
+ PUT_NETLONG((addr), (cp)); \
+ } while(0)
+
+#define PUT_EUADDR6(addr, cp) \
+ do { \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ memcpy((cp), &(addr), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+/* TODO: Currently not used. Probably not need at all. Delete! */
+#ifdef NOSUCHDEF
+/* This is completely IGMP related stuff? */
+#define PIM_LEAF_TIMEOUT (3.5 * IGMP_QUERY_INTERVAL)
+#endif /* NOSUCHDEF */
+
+#if defined(__bsdi__) || defined(__NetBSD__)
+/*
+ * Struct used to communicate from kernel to multicast router
+ * note the convenient similarity to an IP packet
+ */
+struct igmpmsg {
+ u_long unused1;
+ u_long unused2;
+ u_char im_msgtype; /* what type of message */
+#define IGMPMSG_NOCACHE 1
+#define IGMPMSG_WRONGVIF 2
+#define IGMPMSG_WHOLEPKT 3 /* used for user level encap*/
+ u_char im_mbz; /* must be zero */
+ u_char im_vif; /* vif rec'd on */
+ u_char unused3;
+ struct in_addr im_src, im_dst;
+};
+#endif
diff --git a/usr.sbin/pim6dd/route.c b/usr.sbin/pim6dd/route.c
new file mode 100644
index 0000000..6684f2c
--- /dev/null
+++ b/usr.sbin/pim6dd/route.c
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: route.c,v 1.3 1999/10/27 11:40:30 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+
+static u_int16 max_prune_timeout __P((mrtentry_t *));
+static void process_cache_miss __P((struct mrt6msg *im));
+static void process_wrong_iif __P((struct mrt6msg *im));
+
+u_int32 default_source_preference = DEFAULT_LOCAL_PREF;
+u_int32 default_source_metric = DEFAULT_LOCAL_METRIC;
+
+
+/* Return the iif for given address */
+vifi_t
+get_iif(address)
+ struct sockaddr_in6 *address;
+{
+ struct rpfctl rpfc;
+
+ k_req_incoming(address, &rpfc);
+ if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ return (NO_VIF);
+ return (rpfc.iif);
+}
+
+/* Return the PIM neighbor toward a source */
+/* If route not found or if a local source or if a directly connected source,
+ * but is not PIM router, or if the first hop router is not a PIM router,
+ * then return NULL.
+ */
+pim_nbr_entry_t *
+find_pim6_nbr(source)
+ struct sockaddr_in6 *source;
+{
+ struct rpfctl rpfc;
+ pim_nbr_entry_t *pim_nbr;
+ struct sockaddr_in6 *next_hop_router_addr;
+
+ if (local_address(source) != NO_VIF)
+ return (pim_nbr_entry_t *)NULL;
+ k_req_incoming(source, &rpfc);
+ if ((IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ || (rpfc.iif == NO_VIF))
+ return (pim_nbr_entry_t *)NULL;
+ next_hop_router_addr = &rpfc.rpfneighbor;
+ for (pim_nbr = uvifs[rpfc.iif].uv_pim_neighbors;
+ pim_nbr != (pim_nbr_entry_t *)NULL;
+ pim_nbr = pim_nbr->next)
+ if (inet6_equal(&pim_nbr->address, next_hop_router_addr))
+ return(pim_nbr);
+ return (pim_nbr_entry_t *)NULL;
+}
+
+/* TODO: check again the exact setup if the source is local or directly
+ * connected!!!
+ */
+/* TODO: XXX: change the metric and preference for all (S,G) entries per
+ * source?
+ */
+/* PIMDM TODO - If possible, this would be the place to correct set the
+ * source's preference and metric to that obtained from the kernel
+ * and/or unicast routing protocol. For now, set it to the configured
+ * default for local pref/metric.
+ */
+/*
+ * Set the iif, upstream router, preference and metric for the route
+ * toward the source. Return TRUE is the route was found, othewise FALSE.
+ * If srctype==PIM_IIF_SOURCE and if the source is directly connected
+ * then the "upstream" is set to NULL.
+ * Note that srctype is a hold-over from the PIM-SM daemon and is unused.
+ */
+int
+set_incoming(srcentry_ptr, srctype)
+ srcentry_t *srcentry_ptr;
+ int srctype;
+{
+ struct rpfctl rpfc;
+ struct sockaddr_in6 *source = &srcentry_ptr->address;
+ struct sockaddr_in6 *neighbor_addr;
+ register struct uvif *v;
+ register pim_nbr_entry_t *n;
+
+ /* Preference will be 0 if directly connected */
+ srcentry_ptr->preference = 0;
+ srcentry_ptr->metric = 0;
+
+ if ((srcentry_ptr->incoming = local_address(source)) != NO_VIF) {
+ /* The source is a local address */
+ /* TODO: set the upstream to myself? */
+ srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
+ return (TRUE);
+ }
+
+ if ((srcentry_ptr->incoming = find_vif_direct(source)) == NO_VIF) {
+ /* TODO: probably need to check the case if the iif is disabled */
+ /* Use the lastest resource: the kernel unicast routing table */
+ k_req_incoming(source, &rpfc);
+ if ((rpfc.iif == NO_VIF) ||
+ IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) {
+ /* couldn't find a route */
+ IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF)
+ log(LOG_DEBUG, 0, "NO ROUTE found for %s",
+ inet6_fmt(&source->sin6_addr));
+ return(FALSE);
+ }
+ srcentry_ptr->incoming = rpfc.iif;
+ neighbor_addr = &rpfc.rpfneighbor;
+ }
+ else {
+ /* The source is directly connected.
+ */
+ srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
+ return (TRUE);
+ }
+
+ /* set the preference for sources that aren't directly connected. */
+ v = &uvifs[srcentry_ptr->incoming];
+ srcentry_ptr->preference = v->uv_local_pref;
+ srcentry_ptr->metric = v->uv_local_metric;
+
+ /*
+ * The upstream router must be a (PIM router) neighbor, otherwise we
+ * are in big trouble ;-)
+ */
+ for (n = v->uv_pim_neighbors; n != NULL; n = n->next) {
+ if (inet6_lessthan(neighbor_addr, &n->address))
+ continue;
+ if (inet6_equal(neighbor_addr, &n->address)) {
+ /*
+ *The upstream router is found in the list of neighbors.
+ * We are safe!
+ */
+ srcentry_ptr->upstream = n;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "For src %s, iif is %d, next hop router is %s",
+ inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
+ inet6_fmt(&neighbor_addr->sin6_addr));
+ return(TRUE);
+ }
+ else break;
+ }
+
+ /* TODO: control the number of messages! */
+ log(LOG_INFO, 0,
+ "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER",
+ inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
+ inet6_fmt(&neighbor_addr->sin6_addr));
+ srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
+
+ return(FALSE);
+}
+
+
+/* Set the leaves in a new mrtentry */
+void set_leaves(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ /* Check for a group report on each vif */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ if(check_multicast_listener(v, &mrtentry_ptr->group->group))
+ IF_SET(vifi, &mrtentry_ptr->leaves);
+}
+
+
+/* Handle new receiver
+ *
+ * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
+ * we have source-specific Join/Prune.
+ */
+void
+add_leaf(vifi, source, group)
+ vifi_t vifi;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ if_set new_leaves;
+ int state_change;
+
+ grpentry_ptr = find_group(group);
+ if (grpentry_ptr == (grpentry_t *)NULL)
+ return;
+
+ /* walk the source list for the group and add vif to oiflist */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *)NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext) {
+
+ /* if applicable, add the vif to the leaves */
+ if (mrtentry_srcs->incoming == vifi)
+ continue;
+
+ if(!(IF_ISSET(vifi, &mrtentry_srcs->leaves))) {
+
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Adding leaf vif %d for src %s group %s",
+ vifi,
+ inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
+ IF_SET(vifi, &new_leaves); /* Add the leaf */
+
+ state_change =
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->pruned_oifs,
+ &new_leaves,
+ &mrtentry_srcs->asserted_oifs);
+
+ /* Handle transition from negative cache */
+ if(state_change == 1)
+ trigger_join_alert(mrtentry_srcs);
+ }
+ }
+}
+
+
+/*
+ * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
+ * we have source-specific joins/prunes.
+ */
+void
+delete_leaf(vifi, source, group)
+ vifi_t vifi;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ if_set new_leaves;
+ int state_change;
+
+ /* mrtentry_t *mrtentry_ptr;
+ * mrtentry_t *mrtentry_srcs;
+ * vifbitmap_t new_oifs;
+ * vifbitmap_t old_oifs;
+ * vifbitmap_t new_leaves;
+ */
+
+ grpentry_ptr = find_group(group);
+ if (grpentry_ptr == (grpentry_t *)NULL)
+ return;
+
+ /* walk the source list for the group and delete vif to leaves */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *)NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext) {
+
+ /* if applicable, delete the vif from the leaves */
+ if (mrtentry_srcs->incoming == vifi)
+ continue;
+
+ if(IF_ISSET(vifi, &mrtentry_srcs->leaves)) {
+
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Deleting leaf vif %d for src %s, group %s",
+ vifi,
+ inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
+ IF_CLR(vifi, &new_leaves); /* Remove the leaf */
+
+ state_change =
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->pruned_oifs,
+ &new_leaves,
+ &mrtentry_srcs->asserted_oifs);
+
+ /* Handle transition to negative cache */
+ if(state_change == -1)
+ trigger_prune_alert(mrtentry_srcs);
+ }
+ }
+}
+
+void
+calc_oifs(mrtentry_ptr, oifs_ptr)
+ mrtentry_t *mrtentry_ptr;
+ if_set *oifs_ptr;
+{
+ if_set oifs;
+
+ /*
+ * oifs =
+ * ((nbr_ifs - my_prune) + my_leaves) - my_filters - incoming_interface,
+ * i.e.`leaves` have higher priority than `prunes`, but lower than `filters'.
+ * Asserted oifs (those that lost assert) are handled as pruned oifs.
+ * The incoming interface is always deleted from the oifs
+ */
+
+ if (mrtentry_ptr == (mrtentry_t *)NULL) {
+ IF_ZERO(oifs_ptr);
+ return;
+ }
+
+ IF_COPY(&nbr_mifs, &oifs);
+ IF_CLR_MASK(&oifs, &mrtentry_ptr->pruned_oifs);
+ IF_MERGE(&oifs, &mrtentry_ptr->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &mrtentry_ptr->asserted_oifs);
+ IF_CLR_MASK(&oifs, &mrtentry_ptr->filter_oifs);
+ IF_CLR(mrtentry_ptr->incoming, &oifs);
+ IF_COPY(&oifs, oifs_ptr);
+}
+
+
+/*
+ * Set the iif, join/prune/leaves/asserted interfaces. Calculate and
+ * set the oifs.
+ * Return 1 if oifs change from NULL to not-NULL.
+ * Return -1 if oifs change from non-NULL to NULL
+ * else return 0
+ * If the iif change or if the oifs change from NULL to non-NULL
+ * or vice-versa, then schedule that mrtentry join/prune timer to
+ * timeout immediately.
+ */
+int
+change_interfaces(mrtentry_ptr, new_iif, new_pruned_oifs,
+ new_leaves_, new_asserted_oifs)
+ mrtentry_t *mrtentry_ptr;
+ vifi_t new_iif;
+ if_set *new_pruned_oifs;
+ if_set *new_leaves_;
+ if_set *new_asserted_oifs;
+{
+ if_set old_pruned_oifs; /* unnecessary? */
+ if_set old_leaves; /* unnecessary? */
+ if_set new_leaves;
+ if_set new_real_oifs; /* The result oifs */
+ if_set old_real_oifs;
+ if_set old_asserted_oifs; /* unnecessary? */
+ vifi_t old_iif;
+ int return_value;
+
+ if (mrtentry_ptr == (mrtentry_t *)NULL)
+ return (0);
+
+ IF_COPY(new_leaves_, &new_leaves);
+
+ old_iif = mrtentry_ptr->incoming;
+ IF_COPY(&mrtentry_ptr->leaves, &old_leaves);
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &old_pruned_oifs);
+ IF_COPY(&mrtentry_ptr->asserted_oifs, &old_asserted_oifs);
+
+ IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs);
+
+ mrtentry_ptr->incoming = new_iif;
+ IF_COPY(new_pruned_oifs, &mrtentry_ptr->pruned_oifs);
+ IF_COPY(&new_leaves, &mrtentry_ptr->leaves);
+ IF_COPY(new_asserted_oifs, &mrtentry_ptr->asserted_oifs);
+ calc_oifs(mrtentry_ptr, &new_real_oifs);
+
+ if (IF_ISEMPTY(&old_real_oifs)) {
+ if (IF_ISEMPTY(&new_real_oifs))
+ return_value = 0;
+ else
+ return_value = 1;
+ } else {
+ if (IF_ISEMPTY(&new_real_oifs))
+ return_value = -1;
+ else
+ return_value = 0;
+ }
+
+ if ((IF_SAME(&new_real_oifs, &old_real_oifs))
+ && (new_iif == old_iif))
+ return 0; /* Nothing to change */
+
+ IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
+
+ k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
+ &mrtentry_ptr->group->group, new_iif, &new_real_oifs);
+
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+
+ return (return_value);
+}
+
+
+/* TODO: implement it. Required to allow changing of the physical interfaces
+ * configuration without need to restart pimd.
+ */
+int
+delete_vif_from_mrt(vifi)
+vifi_t vifi;
+{
+ return TRUE;
+}
+
+
+static u_int16
+max_prune_timeout(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ vifi_t vifi;
+#if 0
+ /* XXX: I don't understand how the variable works...(jinmei@kame.net) */
+ u_int16 time_left = 0;
+#endif
+ u_int16 max_holdtime = 0;
+
+ for(vifi=0; vifi < numvifs; ++vifi)
+ if(IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs) &&
+ mrtentry_ptr->prune_timers[vifi])
+ /* XXX - too expensive ? */
+ if(mrtentry_ptr->prune_timers[vifi] > max_holdtime)
+ max_holdtime = mrtentry_ptr->prune_timers[vifi];
+#if 0
+ /* XXX: This is original. But does it have any meaning? */
+ max_holdtime = time_left;
+#endif
+
+ if(max_holdtime == 0)
+ max_holdtime = (u_int16)PIM_JOIN_PRUNE_HOLDTIME;
+
+ return(max_holdtime);
+}
+
+
+void
+process_kernel_call()
+{
+ register struct mrt6msg *im; /* igmpmsg control struct */
+
+ im = (struct mrt6msg *) mld6_recv_buf;
+
+ switch (im->im6_msgtype) {
+ case MRT6MSG_NOCACHE:
+ process_cache_miss(im);
+ break;
+ case MRT6MSG_WRONGMIF:
+ process_wrong_iif(im);
+ break;
+ default:
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Unknown kernel_call code, %d", im->im6_msgtype);
+ break;
+ }
+}
+
+
+/*
+ * Protocol actions:
+ * 1. Create (S,G) entry (find_route(CREATE))
+ * a. set iif and oifs
+ */
+static void
+process_cache_miss(im)
+ struct mrt6msg *im;
+{
+ static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
+ static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
+ mrtentry_t *mrtentry_ptr;
+
+ /*
+ * When there is a cache miss, we check only the header of the packet
+ * (and only it should be sent up by the kernel.
+ */
+
+ group.sin6_addr = im->im6_dst;
+ source.sin6_addr = im->im6_src;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]);
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]);
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s",
+ inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr));
+
+ /* Don't create routing entries for the LAN scoped addresses */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
+ IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
+ return;
+
+ /* Create the (S,G) entry */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+
+ /* Set oifs */
+ set_leaves(mrtentry_ptr);
+ calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
+
+ /* Add it to the kernel */
+ k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming,
+ &mrtentry_ptr->oifs);
+
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+
+ /* No need to call change_interfaces, but check for NULL oiflist */
+ if(IF_ISEMPTY(&mrtentry_ptr->oifs))
+ trigger_prune_alert(mrtentry_ptr);
+}
+
+
+/*
+ * A multicast packet has been received on wrong iif by the kernel.
+ * If the packet was received on a point-to-point interface, rate-limit
+ * prunes. if the packet was received on a LAN interface, rate-limit
+ * asserts.
+ */
+static void
+process_wrong_iif(im)
+ struct mrt6msg *im;
+{
+ static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
+ static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
+ mifi_t mifi;
+ mrtentry_t *mrtentry_ptr;
+
+ group.sin6_addr = im->im6_dst;
+ source.sin6_addr = im->im6_src;
+ mifi = (mifi_t)im->im6_mif;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[mifi]);
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[mifi]);
+
+ /* PIMDM TODO Don't create routing entries for the LAN scoped addresses */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
+ IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
+ return;
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE);
+ if(mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ /* Ratelimit prunes or asserts */
+#ifdef notyet
+ if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) {
+
+ /* Wrong vif on P2P interface - rate-limit prunes */
+
+ if(mrtentry_ptr->last_prune[mifi] == virtual_time)
+ /* Skip due to rate-limiting */
+ return;
+ mrtentry_ptr->last_prune[mifi] = virtual_time;
+
+ if(uvifs[mifi].uv_rmt_addr)
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi,
+ uvifs[mifi].uv_rmt_addr,
+ max_prune_timeout(mrtentry_ptr), 0);
+ else
+ log(LOG_WARNING, 0,
+ "Can't send wrongvif prune on p2p %s: no remote address",
+ uvifs[mifi].uv_lcl_addr);
+ } else
+#endif
+ {
+
+ /* Wrong vif on LAN interface - rate-limit asserts */
+
+ if(mrtentry_ptr->last_assert[mifi] == virtual_time)
+ /* Skip due to rate-limiting */
+ return;
+ mrtentry_ptr->last_assert[mifi] = virtual_time;
+
+ /* Send the assert */
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ }
+}
+
+
+void
+trigger_prune_alert(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Now negative cache for src %s, grp %s - pruning",
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
+
+ /* Set the entry timer to the max of the prune timers */
+ SET_TIMER(mrtentry_ptr->timer, max_prune_timeout(mrtentry_ptr));
+
+ /* Send a prune */
+ if(mrtentry_ptr->upstream)
+ send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mrtentry_ptr->incoming,
+ &mrtentry_ptr->upstream->address,
+ max_prune_timeout(mrtentry_ptr), 0);
+}
+
+void
+trigger_join_alert(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Now forwarding state for src %s, grp %s - grafting",
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
+
+ /* Refresh the entry timer */
+ SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
+
+ /* Send graft */
+ send_pim6_graft(mrtentry_ptr);
+}
diff --git a/usr.sbin/pim6dd/routesock.c b/usr.sbin/pim6dd/routesock.c
new file mode 100644
index 0000000..bb2d590
--- /dev/null
+++ b/usr.sbin/pim6dd/routesock.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: routesock.c,v 1.4 1999/11/19 04:05:48 sumikawa Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include "defs.h"
+#include <sys/socket.h>
+#include <net/route.h>
+#ifdef HAVE_ROUTING_SOCKETS
+#include <net/if_dl.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ROUTING_SOCKETS
+union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_dl sdl;
+} so_dst, so_ifp;
+typedef union sockunion *sup;
+int routing_socket;
+int rtm_addrs, pid;
+struct rt_metrics rt_metrics;
+u_long rtm_inits;
+
+/*
+ * Local functions definitions.
+ */
+static int getmsg __P((register struct rt_msghdr *, int,
+ struct rpfctl *rpfinfo));
+
+/*
+ * TODO: check again!
+ */
+#ifdef IRIX
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \
+ : sizeof(__uint64_t))
+#else
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \
+ : sizeof(long))
+#endif /* IRIX */
+
+#ifdef HAVE_SA_LEN
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+#else
+#define ADVANCE(x, n) (x += ROUNDUP(4)) /* TODO: a hack!! */
+#endif
+
+/* Open and initialize the routing socket */
+int
+init_routesock()
+{
+ pid = getpid();
+ routing_socket = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
+ if (routing_socket < 0) {
+ log(LOG_ERR, 0, "\nRouting socket error");
+ return -1;
+ }
+ if (fcntl(routing_socket, F_SETFL, O_NONBLOCK) == -1){
+ log(LOG_ERR, 0, "\n Routing socket error");
+ return -1;
+ }
+#if 0
+ {
+ int off;
+
+ off = 0;
+ if (setsockopt(routing_socket, SOL_SOCKET,
+ SO_USELOOPBACK, (char *)&off,
+ sizeof(off)) < 0){
+ log(LOG_ERR, 0 , "\n setsockopt(SO_USELOOPBACK,0)");
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+
+/* get the rpf neighbor info */
+int
+k_req_incoming(source, rpfp)
+ struct sockaddr_in6 *source;
+ struct rpfctl *rpfp;
+{
+ int flags = RTF_STATIC;
+ register sup su;
+ static int seq;
+ int rlen;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+ struct rpfctl rpfinfo;
+
+/* TODO: a hack!!!! */
+#ifdef HAVE_SA_LEN
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\
+ }
+#else
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l = ROUNDUP(4); bcopy((char *)&(u), cp, l); cp += l;\
+ }
+#endif /* HAVE_SA_LEN */
+
+ /* initialize */
+ memset(&rpfp->rpfneighbor, 0, sizeof(rpfp->rpfneighbor));
+ rpfp->source = *source;
+
+ /* check if local address or directly connected before calling the
+ * routing socket
+ */
+
+ if ((rpfp->iif = find_vif_direct_local(source)) != NO_VIF) {
+ rpfp->rpfneighbor = *source;
+ return(TRUE);
+ }
+
+ /* prepare the routing socket params */
+ rtm_addrs |= RTA_DST;
+ rtm_addrs |= RTA_IFP;
+ su = &so_dst;
+ su->sin6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ su->sin6.sin6_addr = source->sin6_addr;
+ su->sin6.sin6_scope_id = source->sin6_scope_id;
+ so_ifp.sa.sa_family = AF_LINK;
+#ifdef HAVE_SA_LEN
+ so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
+#endif
+ flags |= RTF_UP;
+ flags |= RTF_HOST;
+ flags |= RTF_GATEWAY;
+ errno = 0;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+
+#define rtm m_rtmsg.m_rtm
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = flags;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+ rtm.rtm_rmx = rt_metrics;
+ rtm.rtm_inits = rtm_inits;
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_IFP, so_ifp);
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+ if ((rlen = write(routing_socket, (char *)&m_rtmsg, l)) < 0) {
+ IF_DEBUG(DEBUG_RPF | DEBUG_KERN) {
+ if (errno == ESRCH)
+ log(LOG_DEBUG, 0,
+ "Writing to routing socket: no such route\n");
+ else
+ log(LOG_DEBUG, 0, "Error writing to routing socket");
+ }
+ return(FALSE);
+ }
+
+ do {
+ l = read(routing_socket, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ if (l < 0) {
+ IF_DEBUG(DEBUG_RPF | DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Read from routing socket failed: %s", strerror(errno));
+ return(FALSE);
+ }
+
+ if (getmsg(&rtm, l, &rpfinfo)){
+ rpfp->rpfneighbor = rpfinfo.rpfneighbor;
+ rpfp->iif = rpfinfo.iif;
+ }
+#undef rtm
+ return (TRUE);
+}
+
+
+/*
+ * Returns TRUE on success, FALSE otherwise. rpfinfo contains the result.
+ */
+int
+getmsg(rtm, msglen, rpfinfop)
+ register struct rt_msghdr *rtm;
+ int msglen;
+ struct rpfctl *rpfinfop;
+{
+ struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ register struct sockaddr *sa;
+ register char *cp;
+ register int i;
+ struct sockaddr_in6 *sin6;
+ vifi_t vifi;
+ struct uvif *v;
+ char in6txt[INET6_ADDRSTRLEN];
+
+ if (rpfinfop == (struct rpfctl *)NULL)
+ return(FALSE);
+
+ sin6 = (struct sockaddr_in6 *)&so_dst;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, "route to: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ cp = ((char *)(rtm + 1));
+ if (rtm->rtm_addrs)
+ for (i = 1; i; i <<= 1)
+ if (i & rtm->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_DST:
+ dst = sa;
+ break;
+ case RTA_GATEWAY:
+ gate = sa;
+ break;
+ case RTA_NETMASK:
+ mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->sdl_nlen)
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ }
+ ADVANCE(cp, sa);
+ }
+
+ if (!ifp){ /* No incoming interface */
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "No incoming interface for destination %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ return(FALSE);
+ }
+ if (dst && mask)
+ mask->sa_family = dst->sa_family;
+ if (dst) {
+ sin6 = (struct sockaddr_in6 *)dst;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " destination is: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ }
+ if (gate && rtm->rtm_flags & RTF_GATEWAY) {
+ sin6 = (struct sockaddr_in6 *)gate;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " gateway is: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ rpfinfop->rpfneighbor = *sin6;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+#if 0
+ rpfinfop->rpfneighbor.sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+#endif
+ rpfinfop->rpfneighbor.sin6_scope_id = ifp->sdl_index;
+ /*
+ * XXX: KAME kernel embeds the interface index to the address.
+ * Clear the index for safety.
+ */
+ rpfinfop->rpfneighbor.sin6_addr.s6_addr[2] = 0;
+ rpfinfop->rpfneighbor.sin6_addr.s6_addr[3] = 0;
+ }
+ }
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ /* get the number of the interface by matching the name */
+ if ((strlen(v->uv_name) == ifp->sdl_nlen) &&
+ !(strncmp(v->uv_name,ifp->sdl_data,ifp->sdl_nlen)))
+ break;
+
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " iif is %d", vifi);
+
+ rpfinfop->iif = vifi;
+
+ if (vifi >= numvifs){
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "Invalid incoming interface for destination %s, because of invalid virtual interface",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ return(FALSE);/* invalid iif */
+ }
+
+ return(TRUE);
+}
+
+
+#else /* HAVE_ROUTING_SOCKETS */
+
+
+/*
+ * Return in rpfcinfo the incoming interface and the next hop router
+ * toward source.
+ */
+/* TODO: check whether next hop router address is in network or host order */
+int
+k_req_incoming(source, rpfcinfo)
+ struct sockaddr_in6 *source;
+ struct rpfctl *rpfcinfo;
+{
+ rpfcinfo->source = *source;
+ rpfcinfo->iif = NO_VIF; /* just initialized, will be */
+ /* changed in kernel */
+ memset(&rpfcinfo->rpfneighbor, 0, sizeof(rpfcinfo->rpfneighbor)); /* initialized */
+
+ if (ioctl(udp_socket, SIOCGETRPF, (char *) rpfcinfo) < 0){
+ log(LOG_ERR, errno, "ioctl SIOCGETRPF k_req_incoming");
+ return(FALSE);
+ }
+ return (TRUE);
+}
+
+#endif /* HAVE_ROUTING_SOCKETS */
+
diff --git a/usr.sbin/pim6dd/timer.c b/usr.sbin/pim6dd/timer.c
new file mode 100644
index 0000000..410183f
--- /dev/null
+++ b/usr.sbin/pim6dd/timer.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1998 by the University of Oregon.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Oregon.
+ * The name of the University of Oregon may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Kurt Windisch (kurtw@antc.uoregon.edu)
+ *
+ * $Id: timer.c,v 1.3 1999/09/15 07:45:12 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from PIM sparse-mode pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * The pimd program is COPYRIGHT 1998 by University of Southern California.
+ *
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+
+/*
+ * Global variables
+ */
+
+/*
+ * Local functions definitions.
+ */
+
+
+/*
+ * Local variables
+ */
+u_int16 unicast_routing_timer; /* Used to check periodically for any
+ * change in the unicast routing.
+ */
+u_int16 unicast_routing_check_interval;
+u_int8 ucast_flag; /* Used to indicate there was a timeout */
+
+
+/* to request and compare any route changes */
+srcentry_t srcentry_save;
+
+/*
+ * Init some timers
+ */
+void
+init_timers()
+{
+ unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL;
+ unicast_routing_timer = unicast_routing_check_interval;
+
+ /* Initialize the srcentry used to save the old routes
+ * during unicast routing change discovery process.
+ */
+ srcentry_save.prev = (srcentry_t *)NULL;
+ srcentry_save.next = (srcentry_t *)NULL;
+ memset(&srcentry_save.address, 0, sizeof(struct sockaddr_in6));
+ srcentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
+ srcentry_save.address.sin6_family= AF_INET6;
+ srcentry_save.mrtlink = (mrtentry_t *)NULL;
+ srcentry_save.incoming = NO_VIF;
+ srcentry_save.upstream = (pim_nbr_entry_t *)NULL;
+ srcentry_save.metric = ~0;
+ srcentry_save.preference = ~0;
+ srcentry_save.timer = 0;
+
+}
+
+/*
+ * On every timer interrupt, advance (i.e. decrease) the timer for each
+ * neighbor and group entry for each vif.
+ */
+void
+age_vifs()
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register pim_nbr_entry_t *next_nbr, *curr_nbr;
+
+/* XXX: TODO: currently, sending to qe* interface which is DOWN
+ * doesn't return error (ENETDOWN) on my Solaris machine,
+ * so have to check periodically the
+ * interfaces status. If this is fixed, just remove the defs around
+ * the "if (vifs_down)" line.
+ */
+
+#if (!((defined SunOS) && (SunOS >= 50)))
+ if (vifs_down)
+#endif /* Solaris */
+ check_vif_state();
+
+ /* Age many things */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN))
+ continue;
+ /* Timeout neighbors */
+ for (curr_nbr = v->uv_pim_neighbors; curr_nbr != NULL;
+ curr_nbr = next_nbr) {
+ next_nbr = curr_nbr->next;
+ /*
+ * Never timeout neighbors with holdtime = 0xffff.
+ * This may be used with ISDN lines to avoid keeping the
+ * link up with periodic Hello messages.
+ */
+ if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer)
+ continue;
+ IF_NOT_TIMEOUT(curr_nbr->timer)
+ continue;
+
+ delete_pim6_nbr(curr_nbr);
+ }
+
+ /* PIM_HELLO periodic */
+ IF_TIMEOUT(v->uv_pim_hello_timer)
+ send_pim6_hello(v, PIM_TIMER_HELLO_HOLDTIME);
+
+ /* MLD query periodic */
+ IF_TIMEOUT(v->uv_gq_timer)
+ query_groups(v);
+ }
+
+ IF_DEBUG(DEBUG_IF) {
+ dump_vifs(stderr);
+ dump_lcl_grp(stderr);
+ }
+}
+
+
+/*
+ * Scan the whole routing table and timeout a bunch of timers:
+ * - prune timers
+ * - Join/Prune delay timer
+ * - routing entry
+ * - Assert timer
+ */
+void
+age_routes()
+{
+ mrtentry_t *mrtentry_ptr, *mrtentry_next;
+ grpentry_t *grpentry_ptr, *grpentry_next;
+ vifi_t vifi;
+ int change_flag, state_change;
+ int update_src_iif;
+ u_long curr_bytecnt;
+
+ /*
+ * Timing out of the global `unicast_routing_timer` and data rate timer
+ */
+ IF_TIMEOUT(unicast_routing_timer) {
+ ucast_flag = TRUE;
+ unicast_routing_timer = unicast_routing_check_interval;
+ }
+ ELSE {
+ ucast_flag = FALSE;
+ }
+
+ /* Walk the the (S,G) entries */
+ if(grplist == (grpentry_t *)NULL)
+ return;
+ for(grpentry_ptr = grplist;
+ grpentry_ptr != (grpentry_t *)NULL;
+ grpentry_ptr = grpentry_next) {
+ grpentry_next = grpentry_ptr->next;
+
+ for(mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *)NULL;
+ mrtentry_ptr = mrtentry_next) {
+ mrtentry_next = mrtentry_ptr->grpnext;
+
+ /* Refresh entry timer if data forwarded */
+ curr_bytecnt = mrtentry_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket,
+ &mrtentry_ptr->source->address,
+ &mrtentry_ptr->group->group,
+ &mrtentry_ptr->sg_count)) {
+ /* No such routing entry in kernel */
+ delete_mrtentry(mrtentry_ptr);
+ continue;
+ }
+ if(!(IF_ISEMPTY(&mrtentry_ptr->oifs)) &&
+ curr_bytecnt != mrtentry_ptr->sg_count.bytecnt) {
+ /* Packets have been forwarded - refresh timer
+ * Note that these counters count packets received,
+ * not packets forwarded. So only refresh if packets
+ * received and non-null oiflist.
+ */
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0,
+ "Refreshing src %s, dst %s after %d bytes forwarded",
+ inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
+ inet6_fmt(&mrtentry_ptr->group->group.sin6_addr),
+ mrtentry_ptr->sg_count.bytecnt);
+ SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
+ }
+
+ /* Time out the entry */
+ IF_TIMEOUT(mrtentry_ptr->timer) {
+ delete_mrtentry(mrtentry_ptr);
+ continue;
+ }
+
+ /* Time out asserts */
+ if(mrtentry_ptr->flags & MRTF_ASSERTED)
+ IF_TIMEOUT(mrtentry_ptr->assert_timer) {
+ mrtentry_ptr->flags &= ~MRTF_ASSERTED;
+ mrtentry_ptr->upstream = mrtentry_ptr->source->upstream;
+ mrtentry_ptr->metric = mrtentry_ptr->source->metric;
+ mrtentry_ptr->preference = mrtentry_ptr->source->preference;
+ }
+
+ /* Time out Pruned interfaces */
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ if (IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs))
+ IF_TIMEOUT(mrtentry_ptr->prune_timers[vifi]) {
+ IF_CLR(vifi, &mrtentry_ptr->pruned_oifs);
+ SET_TIMER(mrtentry_ptr->prune_timers[vifi], 0);
+ change_flag = TRUE;
+ }
+ }
+
+ /* Unicast Route changes */
+ update_src_iif = FALSE;
+ if (ucast_flag == TRUE) {
+ /* iif toward the source */
+ srcentry_save.incoming = mrtentry_ptr->source->incoming;
+ srcentry_save.upstream = mrtentry_ptr->source->upstream;
+ srcentry_save.preference = mrtentry_ptr->source->preference;
+ srcentry_save.metric = mrtentry_ptr->source->metric;
+
+ if (set_incoming(mrtentry_ptr->source,
+ PIM_IIF_SOURCE) != TRUE) {
+ /*
+ * XXX: not in the spec!
+ * Cannot find route toward that source.
+ * This is bad. Delete the entry.
+ */
+ delete_mrtentry(mrtentry_ptr);
+ continue;
+ }
+ else {
+ /* iif info found */
+ if (!(mrtentry_ptr->flags & MRTF_ASSERTED) &&
+ ((srcentry_save.incoming !=
+ mrtentry_ptr->incoming)
+ || (srcentry_save.upstream !=
+ mrtentry_ptr->upstream))) {
+ /* Route change has occur */
+ update_src_iif = TRUE;
+ mrtentry_ptr->incoming =
+ mrtentry_ptr->source->incoming;
+ mrtentry_ptr->upstream =
+ mrtentry_ptr->source->upstream;
+ /* mrtentry should have pref/metric of upstream
+ * assert winner, but we dont have that info,
+ * so use the source pref/metric, which will be
+ * larger and thus the correct assert winner
+ * from upstream will be chosen.
+ */
+ mrtentry_ptr->preference =
+ mrtentry_ptr->source->preference;
+ mrtentry_ptr->metric =
+ mrtentry_ptr->source->metric;
+ }
+ }
+ }
+
+ if ((change_flag == TRUE) || (update_src_iif == TRUE)) {
+ /* Flush the changes */
+ state_change =
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs);
+ if(state_change == -1)
+ trigger_prune_alert(mrtentry_ptr);
+ }
+ }
+ }
+
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(stderr);
+ return;
+}
diff --git a/usr.sbin/pim6dd/trace.c b/usr.sbin/pim6dd/trace.c
new file mode 100644
index 0000000..1556097
--- /dev/null
+++ b/usr.sbin/pim6dd/trace.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * non-commercial purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.c,v 1.5 1999/09/16 08:46:00 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+#include "defs.h"
+#include "trace.h"
+
+/* TODO */
+/*
+ * Traceroute function which returns traceroute replies to the requesting
+ * router. Also forwards the request to downstream routers.
+ */
+void
+accept_mtrace(src, dst, group, ifindex, data, no, datalen)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst;
+ struct in6_addr *group;
+ int ifindex;
+ char *data;
+ u_int no; /* promoted u_char */
+ int datalen;
+{
+ u_char type;
+ mrtentry_t *mrt;
+ struct tr6_query *qry;
+ struct tr6_resp *resp;
+ int vifi, ovifi;
+ char *p;
+ int rcount;
+ int errcode = TR_NO_ERR;
+ int resptype;
+ struct timeval tp;
+ struct sioc_mif_req6 mreq;
+ struct in6_addr parent_address;
+ struct sockaddr_in6 src_sa6 = {sizeof(src_sa6), AF_INET6};
+ struct sockaddr_in6 dst_sa6 = {sizeof(dst_sa6), AF_INET6};
+ struct sockaddr_in6 resp_sa6 = {sizeof(resp_sa6), AF_INET6};
+ struct sockaddr_in6 grp_sa6 = {sizeof(grp_sa6), AF_INET6};
+ struct sockaddr_in6 *sa_global;
+#ifdef SM_ONLY
+ rpentry_t *rpentry_ptr;
+#endif
+
+ /* Remember qid across invocations */
+ static u_int32 oqid = 0;
+
+ /* timestamp the request/response */
+ gettimeofday(&tp, 0);
+
+ /*
+ * Check if it is a query or a response
+ */
+ if (datalen == QLEN) {
+ type = QUERY;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Initial traceroute query rcvd "
+ "from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ }
+ else if ((datalen - QLEN) % RLEN == 0) {
+ type = RESP;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "In-transit traceroute query rcvd "
+ "from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Dropping multicast response");
+ return;
+ }
+ }
+ else {
+ log(LOG_WARNING, 0, "%s from %s to %s",
+ "Non decipherable traceroute request recieved",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
+ return;
+ }
+
+ qry = (struct tr6_query *)data;
+ src_sa6.sin6_addr = qry->tr_src;
+ src_sa6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&qry->tr_src)
+ || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_src)) ? ifindex : 0;
+ dst_sa6.sin6_addr = qry->tr_dst;
+ dst_sa6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&qry->tr_dst)
+ || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_dst)) ? ifindex : 0;
+ grp_sa6.sin6_addr = *group;
+ grp_sa6.sin6_scope_id = 0;
+
+ /*
+ * if it is a packet with all reports filled, drop it
+ */
+ if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_TRACE) {
+ log(LOG_DEBUG, 0, "s: %s g: %s d: %s ",
+ inet6_fmt(&qry->tr_src),
+ inet6_fmt(group), inet6_fmt(&qry->tr_dst));
+ log(LOG_DEBUG, 0, "rhlim: %d rd: %s", qry->tr_rhlim,
+ inet6_fmt(&qry->tr_raddr));
+ log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
+ }
+
+ /* determine the routing table entry for this traceroute */
+ mrt = find_route(&src_sa6, &grp_sa6, MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ IF_DEBUG(DEBUG_TRACE) {
+ if (mrt != (mrtentry_t *)NULL) {
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address = mrt->upstream->address.sin6_addr;
+ else
+ parent_address = in6addr_any;
+ log(LOG_DEBUG, 0,
+ "mrt parent mif: %d rtr: %s metric: %d",
+ mrt->incoming,
+ inet6_fmt(&parent_address), mrt->metric);
+ /* TODO
+ log(LOG_DEBUG, 0, "mrt origin %s",
+ RT_FMT(rt, s1));
+ */
+ } else
+ log(LOG_DEBUG, 0, "...no route");
+ }
+
+ /*
+ * Query type packet - check if rte exists
+ * Check if the query destination is a vif connected to me.
+ * and if so, whether I should start response back
+ */
+ if (type == QUERY) {
+ if (oqid == qry->tr_qid) {
+ /*
+ * If the multicast router is a member of the group
+ * being queried, and the query is multicasted,
+ * then the router can recieve multiple copies of
+ * the same query. If we have already replied to
+ * this traceroute, just ignore it this time.
+ *
+ * This is not a total solution, but since if this
+ * fails you only get N copies, N <= the number of
+ * interfaces on the router, it is not fatal.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "ignoring duplicate traceroute packet");
+ return;
+ }
+
+ if (mrt == (mrtentry_t *)NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Mcast traceroute: no route entry %s",
+ inet6_fmt(&qry->tr_src));
+#if 0
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+#endif
+ }
+ vifi = find_vif_direct(&dst_sa6);
+
+ if (vifi == NO_VIF) {
+ /*
+ * The traceroute destination is not on one of
+ * my subnet vifs.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not an interface",
+ inet6_fmt(&qry->tr_dst));
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+ errcode = TR_WRONG_IF;
+ } else if (mrt != (mrtentry_t *)NULL &&
+ !IF_ISSET(vifi, &mrt->oifs)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not on forwarding tree "
+ "for src %s",
+ inet6_fmt(&qry->tr_dst),
+ inet6_fmt(&qry->tr_src));
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+ errcode = TR_WRONG_IF;
+ }
+ }
+ else {
+ /*
+ * determine which interface the packet came in on
+ * RESP packets travel hop-by-hop so this either traversed
+ * a tunnel or came from a directly attached mrouter.
+ */
+ if ((vifi = find_vif_direct(src)) == NO_VIF) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Wrong interface for packet");
+ errcode = TR_WRONG_IF;
+ }
+ }
+
+ /* Now that we've decided to send a response, save the qid */
+ oqid = qry->tr_qid;
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending traceroute response");
+
+ /* copy the packet to the sending buffer */
+ p = mld6_send_buf + sizeof(struct mld6_hdr);
+
+ bcopy(data, p, datalen);
+
+ p += datalen;
+
+ /*
+ * If there is no room to insert our reply, coopt the previous hop
+ * error indication to relay this fact.
+ */
+ if (p + sizeof(struct tr6_resp) > mld6_send_buf + RECV_BUF_SIZE) {
+ resp = (struct tr6_resp *)p - 1;
+ resp->tr_rflags = TR_NO_SPACE;
+ mrt = NULL;
+ goto sendit;
+ }
+
+ /*
+ * fill in initial response fields
+ */
+ resp = (struct tr6_resp *)p;
+ bzero(resp, sizeof(struct tr6_resp));
+ datalen += (RLEN + sizeof(struct mld6_hdr));
+
+ resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec << 10) / 15625));
+
+ resp->tr_rproto = PROTO_PIM;
+ resp->tr_outifid = (vifi == NO_VIF) ? TR_NO_VIF : htonl(vifi);
+ resp->tr_rflags = errcode;
+ if ((sa_global = max_global_address()) == NULL) /* impossible */
+ log(LOG_ERR, 0, "acept_mtrace: max_global_address returns NULL");
+ resp->tr_lcladdr = sa_global->sin6_addr;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ mreq.mifi = vifi;
+ if (vifi != NO_VIF &&
+ ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
+ resp->tr_vifout = htonl(mreq.ocount);
+ else
+ resp->tr_vifout = 0xffffffff;
+
+ /*
+ * fill in scoping & pruning information
+ */
+ /* TODO */
+#if 0
+ if (mrt != (mrtentry_t *)NULL)
+ for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
+ if (gt->gt_mcastgrp >= group)
+ break;
+ }
+ else
+ gt = NULL;
+
+ if (gt && gt->gt_mcastgrp == group) {
+ struct stable *st;
+
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (qry->tr_src == st->st_origin)
+ break;
+
+ sg_req.src.s_addr = qry->tr_src;
+ sg_req.grp.s_addr = group;
+ if (st && st->st_ctime != 0 &&
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
+ else
+ resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
+
+ if (VIFM_ISSET(vifi, gt->gt_scope))
+ resp->tr_rflags = TR_SCOPED;
+ else if (gt->gt_prsent_timer)
+ resp->tr_rflags = TR_PRUNED;
+ else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
+ if (VIFM_ISSET(vifi, rt->rt_children) &&
+ NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap,
+ rt->rt_subordinates)) /*XXX*/
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if (scoped_addr(vifi, group))
+ resp->tr_rflags = TR_SCOPED;
+ else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
+ resp->tr_rflags = TR_NO_FWD;
+ }
+#endif /* 0 */
+
+ /*
+ * if no rte exists, set NO_RTE error
+ */
+ if (mrt == (mrtentry_t *)NULL) {
+ src->sin6_addr = *dst; /* the dst address of resp. pkt */
+ resp->tr_inifid = TR_NO_VIF;
+ resp->tr_rflags = TR_NO_RTE;
+ memset(&resp->tr_rmtaddr, 0, sizeof(struct in6_addr));
+ } else {
+ /* get # of packets in on interface */
+ mreq.mifi = mrt->incoming;
+ if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
+ resp->tr_vifin = htonl(mreq.icount);
+ else
+ resp->tr_vifin = 0xffffffff;
+
+ /*
+ * TODO
+ * MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
+ */
+ resp->tr_inifid = htonl(mrt->incoming);
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address = mrt->upstream->address.sin6_addr;
+ else
+ parent_address = in6addr_any;
+
+ resp->tr_rmtaddr = parent_address;
+ if (!IF_ISSET(vifi, &mrt->oifs)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not on forwarding tree "
+ "for src %s",
+ inet6_fmt(&qry->tr_dst),
+ inet6_fmt(&qry->tr_src));
+ resp->tr_rflags = TR_WRONG_IF;
+ }
+#if 0
+ if (rt->rt_metric >= UNREACHABLE) {
+ resp->tr_rflags = TR_NO_RTE;
+ /* Hack to send reply directly */
+ rt = NULL;
+ }
+#endif /* 0 */
+ }
+
+#ifdef SM_ONLY
+ /*
+ * If we're the RP for the trace group, note it.
+ */
+ rpentry_ptr = rp_match(&grp_sa6);
+ if (rpentry_ptr && local_address(&rpentry_ptr->address) != NO_VIF)
+ resp->tr_rflags = TR_RP;
+#endif /* SM_ONLY */
+
+ sendit:
+ /*
+ * if metric is 1 or no. of reports is 1, send response to requestor
+ * else send to upstream router. If the upstream router can't handle
+ * mtrace, set an error code and send to requestor anyway.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
+
+ ovifi = NO_VIF; /* unspecified */
+ if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) {
+ resptype = MLD6_MTRACE_RESP;
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ if ((ovifi = find_vif_direct(&dst_sa6)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "can't determine outgoing i/f for mtrace "
+ "response.");
+ return;
+ }
+ }
+ } else
+ /* TODO */
+ {
+#if 0
+ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ resp->tr_rflags = TR_OLD_ROUTER;
+ resptype = MLD6_MTRACE_RESP;
+ } else
+#endif /* 0 */
+#ifdef SM_ONLY
+ if (mrt->incoming &&
+ (uvifs[mrt->incoming].uv_flags & MIFF_REGISTER)) {
+ log(LOG_DEBUG, 0,
+ "incoming i/f is for register. "
+ "Can't be forwarded anymore.");
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ resptype = MLD6_MTRACE_RESP;
+ } else
+#endif /* SM_ONLY */
+ {
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address =
+ mrt->upstream->address.sin6_addr;
+ else
+ parent_address = allrouters_group.sin6_addr;
+ resp_sa6.sin6_addr = parent_address;
+ ovifi = mrt->incoming;
+ resptype = MLD6_MTRACE;
+ }
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&resp_sa6.sin6_addr)) {
+ struct sockaddr_in6 *sa6;
+
+ /*
+ * Send the reply on a known multicast capable vif.
+ * If we don't have one, we can't source any
+ * multicasts anyway.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
+ ifindex = uvifs[ovifi].uv_ifindex;
+ }
+ else {
+ if (phys_vif != -1 &&
+ (sa6 = uv_global(phys_vif)) != NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Sending reply to %s from %s",
+ inet6_fmt(dst),
+ inet6_fmt(&sa6->sin6_addr));
+ ifindex = uvifs[phys_vif].uv_ifindex;
+ }
+ else {
+ log(LOG_INFO, 0, "No enabled phyints -- %s",
+ "dropping traceroute reply");
+ return;
+ }
+ }
+ k_set_hlim(mld6_socket, qry->tr_rhlim);
+ send_mld6(resptype, no, sa6, &resp_sa6, group,
+ ifindex, 0, datalen, 0);
+ k_set_hlim(mld6_socket, 1);
+ } else {
+ struct sockaddr_in6 *sa6 = NULL;
+ ifindex = -1; /* unspecified by default */
+
+ if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ /* ovifi must be valid in this case */
+ ifindex = uvifs[ovifi].uv_ifindex;
+ sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
+ }
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending %s to %s from %s",
+ resptype == MLD6_MTRACE_RESP ?
+ "reply" : "request on",
+ inet6_fmt(dst),
+ sa6 ? inet6_fmt(&sa6->sin6_addr) : "unspecified");
+
+ send_mld6(resptype, no, sa6, &resp_sa6, group, ifindex,
+ 0, datalen, 0);
+ }
+ return;
+}
diff --git a/usr.sbin/pim6dd/trace.h b/usr.sbin/pim6dd/trace.h
new file mode 100644
index 0000000..31e79c7
--- /dev/null
+++ b/usr.sbin/pim6dd/trace.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.h,v 1.2 1999/09/12 17:00:10 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr6_query {
+ struct in6_addr tr_src; /* traceroute source */
+ struct in6_addr tr_dst; /* traceroute destination */
+ struct in6_addr tr_raddr; /* traceroute response address */
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ struct {
+ u_int32_t qid : 24; /* traceroute query id */
+ u_int32_t rhlim : 8; /* traceroute response ttl */
+ } q;
+#else
+ struct {
+ u_int32_t rhlim : 8; /* traceroute response ttl */
+ u_int32_t qid : 24; /* traceroute query id */
+ } q;
+#endif /* BYTE_ORDER */
+};
+
+#define tr_rhlim q.rhlim
+#define tr_qid q.qid
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr6_resp {
+ u_int32_t tr_qarr; /* query arrival time */
+#if 0
+ struct in6_addr tr_inaddr; /* incoming interface address */
+ struct in6_addr tr_outaddr; /* outgoing interface address */
+#endif
+ u_int32_t tr_inifid; /* incoming interface identifier */
+ u_int32_t tr_outifid; /* outgoing interface identifier */
+ struct in6_addr tr_lcladdr; /* router's address(must have largest scope) */
+ struct in6_addr tr_rmtaddr; /* parent address in source tree */
+ u_int32_t tr_vifin; /* input packet count on interface */
+ u_int32_t tr_vifout; /* output packet count on interface */
+ u_int32_t tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+#if 0
+ u_char tr_fhlim; /* hop limit required to forward on outvif */
+#endif
+ u_char tr_flags; /* flags */
+ u_char tr_plen; /* prefix length for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QUERY 1
+#define RESP 2
+#define QLEN sizeof(struct tr6_query)
+#define RLEN sizeof(struct tr6_resp)
+
+/* fields for tr_inifid and tr_outifid */
+#define TR_NO_VIF 0xffffffff/* interface can't be determined */
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0 /* No error */
+#define TR_WRONG_IF 1 /* traceroute arrived on non-oif */
+#define TR_PRUNED 2 /* router has sent a prune upstream */
+#define TR_OPRUNED 3 /* stop forw. after request from next hop rtr*/
+#define TR_SCOPED 4 /* group adm. scoped at this hop */
+#define TR_NO_RTE 5 /* no route for the source */
+#define TR_NO_LHR 6 /* not the last-hop router */
+#define TR_NO_FWD 7 /* not forwarding for this (S,G). Reason = ? */
+#define TR_RP 8 /* I am the RP/Core */
+#define TR_IIF 9 /* request arrived on the iif */
+#define TR_NO_MULTI 0x0a /* multicast disabled on that interface */
+#define TR_NO_SPACE 0x81 /* no space to insert responce data block */
+#define TR_OLD_ROUTER 0x82 /* previous hop does not support traceroute */
+#define TR_ADMIN_PROHIB 0x83 /* traceroute adm. prohibited */
+
+/* fields for tr_flags */
+#define TR_SUBNET_COUNT 0x80 /* pkt count for (S,G) is for source network */
+
+/* fields for r_plen */
+#define TR_GROUP_ONLY 0xff /* forwarding solely on group state */
+
+/* fields for packets count */
+#define TR_CANT_COUNT 0xffffffff /* no count can be reported */
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+#define PROTO_PIM_SPECIAL 5
+#define PROTO_PIM_STATIC 6
+#define PROTO_DVMRP_STATIC 7
+
+#define MASK_TO_VAL(x, i) { \
+ u_int32_t _x = ntohl(x); \
+ (i) = 1; \
+ while ((_x) <<= 1) \
+ (i)++; \
+ };
+
+#define VAL_TO_MASK(x, i) { \
+ x = htonl(~((1 << (32 - (i))) - 1)); \
+ };
+
+#define MASKLEN_TO_MASK6(masklen, mask6) \
+ do {\
+ u_char maskarray[8] = \
+ {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \
+ int bytelen, bitlen, i; \
+ memset(&(mask6), 0, sizeof(mask6));\
+ bytelen = (masklen) / 8;\
+ bitlen = (masklen) % 8;\
+ for (i = 0; i < bytelen; i++) \
+ (mask6).s6_addr[i] = 0xff;\
+ if (bitlen) \
+ (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \
+ }while(0);
+
+/* obnoxious gcc gives an extraneous warning about this constant... */
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
+
+#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv)
diff --git a/usr.sbin/pim6dd/vers.c b/usr.sbin/pim6dd/vers.c
new file mode 100644
index 0000000..72668af
--- /dev/null
+++ b/usr.sbin/pim6dd/vers.c
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+char todaysversion[]="0.2.1.0-alpha15";
diff --git a/usr.sbin/pim6dd/vif.c b/usr.sbin/pim6dd/vif.c
new file mode 100644
index 0000000..55bdb1f
--- /dev/null
+++ b/usr.sbin/pim6dd/vif.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: vif.c,v 1.3 1999/09/12 17:00:11 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+/*
+ * Exported variables.
+ */
+struct uvif uvifs[MAXMIFS]; /* array of all virtual interfaces */
+vifi_t numvifs; /* Number of vifs in use */
+int vifs_down; /* 1=>some interfaces are down */
+int phys_vif; /* An enabled vif */
+int udp_socket; /* Since the honkin' kernel doesn't support */
+ /* ioctls on raw IP sockets, we need a UDP */
+ /* socket as well as our IGMP (raw) socket. */
+ /* How dumb. */
+int total_interfaces; /* Number of all interfaces: including the
+ * non-configured, but excluding the
+ * loopback interface and the non-multicast
+ * capable interfaces.
+ */
+if_set if_nullset; /* an interface face that has all-0 bit
+ * (for comparison)
+ */
+
+/*
+ * Forward declarations.
+ */
+static void start_vif __P((vifi_t vifi));
+static void stop_vif __P((vifi_t vifi));
+static void start_all_vifs __P((void));
+
+
+void
+init_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ int enabled_vifs;
+
+ numvifs = 0;
+ vifs_down = FALSE;
+
+ /*
+ * Configure the vifs based on the interface configuration of
+ * the kernel and the contents of the configuration file.
+ * (Open a UDP socket for ioctl use in the config procedures if
+ * the kernel can't handle IOCTL's on the MLD socket.)
+ */
+#ifdef IOCTL_OK_ON_RAW_SOCKET
+ udp_socket = mld6_socket;
+#else
+ if ((udp_socket = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "UDP6 socket");
+#endif
+
+ /*
+ * Clean up all vifs
+ */
+ for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
+ memset(v, 0, sizeof(*v));
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+ strncpy(v->uv_name, "", IFNAMSIZ);
+ v->uv_groups = (struct listaddr *)NULL;
+ v->uv_dvmrp_neighbors = (struct listaddr *)NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = (struct listaddr *)NULL;
+ v->uv_prune_lifetime = 0;
+ v->uv_acl = (struct vif_acl *)NULL;
+ v->uv_leaf_timer = 0;
+ v->uv_addrs = (struct phaddr *)NULL;
+ v->uv_filter = (struct vif_filter *)NULL;
+ v->uv_pim_hello_timer = 0;
+ v->uv_gq_timer = 0;
+ v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL;
+ v->uv_local_pref = default_source_preference;
+ v->uv_local_metric = default_source_metric;
+ }
+
+ log(LOG_INFO, 0, "Getting ifs from kernel");
+ config_vifs_from_kernel();
+ log(LOG_INFO, 0, "Getting ifs from %s", configfilename);
+ config_vifs_from_file();
+
+ /*
+ * Quit if there are fewer than two enabled vifs or there is a vif
+ * which has no link local address.
+ */
+ enabled_vifs = 0;
+ phys_vif = -1;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN))
+ continue;
+ if (v->uv_linklocal == NULL)
+ log(LOG_ERR, 0,
+ "there is no link-local address on vif#%d", vifi);
+ if (phys_vif == -1) {
+ struct phaddr *p;
+
+ /*
+ * If this vif has a global address, set its id
+ * to phys_vif.
+ */
+ for(p = v->uv_addrs; p; p = p->pa_next) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
+ phys_vif = vifi;
+ break;
+ }
+ }
+ }
+ enabled_vifs++;
+ }
+
+ if (enabled_vifs < 2)
+ log(LOG_ERR, 0, "can't forward: %s",
+ enabled_vifs == 0 ? "no enabled ifs" : "only one enabled if");
+
+ memset(&if_nullset, 0, sizeof(if_nullset));
+ k_init_pim(mld6_socket); /* Call to kernel to initiliaze structures */
+
+ start_all_vifs();
+}
+
+
+static void
+start_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ /* Start vif if not DISABLED or DOWN */
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) {
+ if (v->uv_flags & VIFF_DISABLED)
+ log(LOG_INFO, 0,
+ "%s is DISABLED; if #%u out of service",
+ v->uv_name, vifi);
+ else
+ log(LOG_INFO, 0,
+ "%s is DOWN; if #%u out of service",
+ v->uv_name, vifi);
+ }
+ else
+ start_vif(vifi);
+ }
+}
+
+
+
+/*
+ * stop all vifs
+ */
+void
+stop_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ for (vifi = 0, v=uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ stop_vif(vifi);
+ }
+ }
+}
+
+
+/*
+ * Initialize the vif and add to the kernel. The vif can be either
+ * physical, tunnel (tunnels will be used in the future
+ * when this code becomes PIM multicast boarder router.)
+ */
+static void
+start_vif(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ u_long random_delay;
+
+ v = &uvifs[vifi];
+ /* Initialy no router on any vif */
+ v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~VIFF_DOWN;
+ v->uv_pim_hello_timer = 1 + RANDOM() % PIM_TIMER_HELLO_PERIOD;
+ /* TODO: CHECK THE TIMERS!!!!! Set or reset? */
+ v->uv_gq_timer = 0;
+ v->uv_pim_neighbors = (pim_nbr_entry_t *)NULL;
+
+ /* Tell kernel to add, i.e. start this vif */
+ k_add_vif(mld6_socket, vifi, &uvifs[vifi]);
+ log(LOG_INFO, 0, "%s comes up; if #%u now in service", v->uv_name, vifi);
+
+ /*
+ * Join the PIM multicast group on the interface.
+ */
+ k_join(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex);
+
+ /*
+ * Join the ALL-ROUTERS multicast group on the interface.
+ * This allows mtrace requests to loop back if they are run
+ * on the multicast router.
+ */
+ k_join(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex);
+
+ /*
+ * Until neighbors are discovered, assume responsibility for sending
+ * periodic group membership queries to the subnet. Send the first
+ * query.
+ */
+ v->uv_flags |= VIFF_QUERIER;
+ query_groups(v);
+
+ /*
+ * To avoid synchronization among routers booting simultaneously, set
+ * the hello timer to a random value between 1 to PIM_TIMER_HELLO_PERIOD.
+ */
+ random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1));
+ v->uv_pim_hello_timer = random_delay;
+}
+
+
+/*
+ * Stop a vif (either physical interface or tunnel).
+ * If we are running only PIM we don't have tunnels.
+ */
+static void
+stop_vif(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ struct listaddr *a;
+ register pim_nbr_entry_t *n, *next;
+ struct vif_acl *acl;
+
+ /*
+ * TODO: make sure that the kernel viftable is
+ * consistent with the daemon table
+ */
+ v = &uvifs[vifi];
+ k_leave(mld6_socket, &allpim6routers_group.sin6_addr, v->uv_ifindex);
+ k_leave(mld6_socket, &allrouters_group.sin6_addr, v->uv_ifindex);
+ /*
+ * Discard all group addresses. (No need to tell kernel;
+ * the k_del_vif() call will clean up kernel state.)
+ */
+ while (v->uv_groups != NULL) {
+ a = v->uv_groups;
+ v->uv_groups = a->al_next;
+ free((char *)a);
+ }
+
+ /*
+ * TODO: inform (eventually) the neighbors I am going down by sending
+ * PIM_HELLO with holdtime=0 so someone else should become a DR.
+ */
+
+ /* TODO: dummy! Implement it!! Any problems if don't use it? */
+ delete_vif_from_mrt(vifi);
+
+ /*
+ * Delete the interface from the kernel's vif structure.
+ */
+ k_del_vif(mld6_socket, vifi);
+ v->uv_flags = (v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS )
+ | VIFF_DOWN;
+ v->uv_pim_hello_timer = 0;
+ v->uv_gq_timer = 0;
+ for (n = v->uv_pim_neighbors; n != NULL; n = next) {
+ next = n->next; /* Free the space for each neighbour */
+ free((char *)n);
+ }
+ v->uv_pim_neighbors = NULL;
+
+ /* TODO: currently not used */
+ /* The Access Control List (list with the scoped addresses) */
+ while (v->uv_acl != NULL) {
+ acl = v->uv_acl;
+ v->uv_acl = acl->acl_next;
+ free((char *)acl);
+ }
+
+ vifs_down = TRUE;
+ log(LOG_INFO, 0,
+ "%s goes down; if #%u out of service", v->uv_name, vifi);
+}
+
+/*
+ * return the max global Ipv6 address of an UP and ENABLED interface
+ * other than the MIFF_REGISTER interface.
+*/
+struct sockaddr_in6 *
+max_global_address()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ struct phaddr *p;
+ struct phaddr *pmax = NULL;
+
+ for(vifi=0,v=uvifs;vifi< numvifs;++vifi,++v)
+ {
+ if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ /*
+ * take first the max global address of the interface
+ * (without link local) => aliasing
+ */
+ for(p=v->uv_addrs;p!=NULL;p=p->pa_next)
+ {
+ /*
+ * If this is the first global address, take it anyway.
+ */
+ if (pmax == NULL) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ pmax = p;
+ }
+ else {
+ if (inet6_lessthan(&pmax->pa_addr,
+ &p->pa_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ pmax=p;
+ }
+ }
+ }
+
+ return(pmax ? &pmax->pa_addr : NULL);
+}
+
+struct sockaddr_in6 *
+uv_global(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v = &uvifs[vifi];
+ struct phaddr *p;
+
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ return(&p->pa_addr);
+ }
+
+ return(NULL);
+}
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points. Ignore interfaces that have been administratively
+ * disabled.
+ */
+void
+check_vif_state()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ struct ifreq ifr;
+ static int checking_vifs = 0;
+
+ /*
+ * XXX: TODO: True only for DVMRP?? Check.
+ * If we get an error while checking, (e.g. two interfaces go down
+ * at once, and we decide to send a prune out one of the failed ones)
+ * then don't go into an infinite loop!
+ */
+ if (checking_vifs)
+ return;
+
+ vifs_down = FALSE;
+ checking_vifs = 1;
+ /* TODO: Check all potential interfaces!!! */
+ /* Check the physical interfaces only */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & VIFF_DISABLED)
+ continue;
+
+ strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
+ /* get the interface flags */
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno,
+ "check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+ if (v->uv_flags & VIFF_DOWN) {
+ if (ifr.ifr_flags & IFF_UP) {
+ start_vif(vifi);
+ }
+ else vifs_down = TRUE;
+ }
+ else {
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ log(LOG_NOTICE, 0,
+ "%s has gone down; if #%u taken out of service",
+ v->uv_name, vifi);
+ stop_vif(vifi);
+ vifs_down = TRUE;
+ }
+ }
+ }
+ checking_vifs = 0;
+}
+
+
+/*
+ * If the source is directly connected to us, find the vif number for
+ * the corresponding physical interface (tunnels excluded).
+ * Local addresses are excluded.
+ * Return the vif number or NO_VIF if not found.
+ */
+vifi_t
+find_vif_direct(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (inet6_equal(src, &p->pa_addr))
+ return(NO_VIF);
+ if (inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
+ return(vifi);
+ }
+ }
+ return (NO_VIF);
+}
+
+
+/*
+ * Checks if src is local address. If "yes" return the vif index,
+ * otherwise return value is NO_VIF.
+ */
+vifi_t
+local_address(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (inet6_equal(src, &p->pa_addr))
+ return(vifi);
+ }
+ }
+ /* Returning NO_VIF means not a local address */
+ return (NO_VIF);
+}
+
+/*
+ * If the source is directly connected, or is local address,
+ * find the vif number for the corresponding physical interface
+ * (tunnels excluded).
+ * Return the vif number or NO_VIF if not found.
+ */
+vifi_t
+find_vif_direct_local(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (inet6_equal(src, &p->pa_addr) ||
+ inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
+ return(vifi);
+ }
+ }
+ return (NO_VIF);
+}
diff --git a/usr.sbin/pim6dd/vif.h b/usr.sbin/pim6dd/vif.h
new file mode 100644
index 0000000..8dd44fc
--- /dev/null
+++ b/usr.sbin/pim6dd/vif.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: vif.h,v 1.2 1999/08/24 16:45:23 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * Bitmap handling functions.
+ * These should be fast but generic. bytes can be slow to zero and compare,
+ * words are hard to make generic. Thus two sets of macros (yuk).
+ */
+
+/*
+ * The VIFM_ functions should migrate out of <netinet/ip_mroute.h>, since
+ * the kernel no longer uses vifbitmaps.
+ */
+#ifndef VIFM_SET
+
+typedef u_int32 vifbitmap_t;
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+#endif
+/*
+ * And <netinet/ip_mroute.h> was missing some required functions anyway
+ */
+#if !defined(__NetBSD__)
+#define VIFM_SETALL(m) ((m) = ~0)
+#endif
+#define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n)))
+#define VIFM_ISEMPTY(m) ((m) == 0)
+#define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask))
+#define VIFM_SET_MASK(m, mask) ((m) |= (mask))
+#define VIFM_MERGE(m1, m2, result) ((result) = (m1) | (m2))
+
+/*
+ * And <netinet6/ip6_mroute.h> was missing some required functions anyway
+ */
+extern if_set if_nullset;
+#define IF_ISEMPTY(p) (memcmp((p), &if_nullset, sizeof(if_nullset)) == 0)
+#define IF_CLR_MASK(p, mask) \
+ {\
+ int idx;\
+ for (idx = 0; idx < sizeof(*(p))/sizeof(fd_mask); idx++) {\
+ (p)->ifs_bits[idx] &= ~((mask)->ifs_bits[idx]);\
+ }\
+ }
+#define IF_MERGE(p1, p2, result) \
+ {\
+ int idx;\
+ for (idx = 0; idx < sizeof(*(p1))/sizeof(fd_mask); idx++) {\
+ (result)->ifs_bits[idx] = (p1)->ifs_bits[idx]|(p2)->ifs_bits[idx]; \
+ }\
+ }
+#define IF_SAME(p1, p2) (memcmp((p1),(p2),sizeof(*(p1))) == 0)
+
+/* Check whether I am the forwarder on some LAN */
+#define VIFM_FORWARDER(leaves, oifs) ((leaves) & (oifs))
+
+/*
+ * Neighbor bitmaps are, for efficiency, implemented as a struct
+ * containing two variables of a native machine type. If you
+ * have a native type that's bigger than a long, define it below.
+ */
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) * 8
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+#define MAXNBRS 2 * NBRBITS
+
+#define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \
+ ((m).hi |= (1 << (n - NBRBITS))))
+#define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \
+ ((m).hi &= ~(1 << (n - NBRBITS))))
+#define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \
+ ((m).hi & (1 << ((n) - NBRBITS))))
+#define NBRM_CLRALL(m) ((m).lo = (m).hi = 0)
+#define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi)
+#define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi))
+#define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0))
+#define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi))
+#define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi))
+#define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi))
+#define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi))
+#define NBRM_ISSETALLMASK(m, mask)\
+ ((((m).lo & (mask).lo) == (mask).lo) && \
+ (((m).hi & (mask).hi) == (mask).hi))
+/*
+ * This macro is TRUE if all the subordinates have been pruned, or if
+ * there are no subordinates on this vif.
+ * The arguments is the map of subordinates, the map of neighbors on the
+ * vif, and the map of received prunes.
+ */
+#define SUBS_ARE_PRUNED(sub, vifmask, prunes) \
+ (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \
+ ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi))
+
+/*
+ * User level Virtual Interface structure
+ *
+ * A "virtual interface" is either a physical, multicast-capable interface
+ * (called a "phyint"), a virtual point-to-point link (called a "tunnel")
+ * or a "register vif" used by PIM. The register vif is used by the
+ * Designated Router (DR) to send encapsulated data packets to the
+ * Rendevous Point (RP) for a particular group. The data packets are
+ * encapsulated in PIM messages (IPPROTO_PIM = 103) and then unicast to
+ * the RP.
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ */
+struct uvif {
+ u_int uv_flags; /* VIFF_ flags defined below */
+ u_char uv_metric; /* cost of this vif */
+ u_char uv_admetric; /* advertised cost of this vif */
+#if 0 /* unused for IPv6? */
+ u_char uv_threshold; /* min ttl required to forward on vif */
+#endif
+ u_int uv_rate_limit; /* rate limit on this vif */
+ struct sockaddr_in6 uv_lcl_addr;/* local address of this vif */
+ struct phaddr *uv_linklocal;/* link-local address of this vif */
+#if 0
+ u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */
+#endif
+ struct sockaddr_in6 uv_dst_addr; /* destination for PIM messages */
+ struct sockaddr_in6 uv_prefix; /* prefix (phyints only) */
+ struct in6_addr uv_subnetmask; /* subnet mask (phyints only) */
+ char uv_name[IFNAMSIZ]; /* interface name */
+ u_int uv_ifindex; /* index of the interface */
+ u_int uv_siteid; /* index of the site on the interface */
+ struct listaddr *uv_groups; /* list of local groups (phyints only) */
+ struct listaddr *uv_dvmrp_neighbors; /* list of neighboring routers */
+ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */
+ struct listaddr *uv_querier; /* MLD querier on vif */
+ int uv_prune_lifetime; /* Prune lifetime or 0 for default */
+ struct vif_acl *uv_acl; /* access control list of groups */
+ int uv_leaf_timer; /* time until this vif is considrd leaf */
+ struct phaddr *uv_addrs; /* Additional addresses on this vif */
+ struct vif_filter *uv_filter; /* Route filters on this vif */
+ u_int16 uv_pim_hello_timer;/* timer for sending PIM hello msgs */
+ u_int16 uv_gq_timer; /* Group Query timer */
+ int uv_local_pref; /* default local preference for assert */
+ int uv_local_metric; /* default local metric for assert */
+ struct pim_nbr_entry *uv_pim_neighbors; /* list of PIM neighbor routers */
+};
+
+/* TODO: define VIFF_KERNEL_FLAGS */
+#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL | VIFF_SRCRT)
+#define VIFF_DOWN 0x000100 /* kernel state of interface */
+#define VIFF_DISABLED 0x000200 /* administratively disabled */
+#define VIFF_QUERIER 0x000400 /* I am the subnet's querier */
+#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */
+#define VIFF_LEAF 0x001000 /* all neighbors are leaves */
+#define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */
+#define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */
+#define VIFF_PASSIVE 0x008000 /* passive tunnel */
+#define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */
+#define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */
+#define VIFF_DR 0x040000 /* designated router */
+/* TODO: VIFF_NONBRS == VIFF_ONEWAY? */
+#define VIFF_NONBRS 0x080000 /* no neighbor on vif */
+#define VIFF_POINT_TO_POINT 0x100000 /* point-to-point link */
+#define VIFF_PIM_NBR 0x200000 /* PIM neighbor */
+#define VIFF_DVMRP_NBR 0x400000 /* DVMRP neighbor */
+#define VIFF_NOLISTENER 0x800000 /* no listener on the link */
+
+struct phaddr {
+ struct phaddr *pa_next;
+ struct sockaddr_in6 pa_addr; /* extra address */
+ struct sockaddr_in6 pa_prefix; /* prefix of the extra address */
+ struct in6_addr pa_subnetmask; /* netmask */
+};
+
+/* The Access Control List (list with scoped addresses) member */
+struct vif_acl {
+ struct vif_acl *acl_next; /* next acl member */
+ u_int32 acl_addr; /* Group address */
+ u_int32 acl_mask; /* Group addr. mask */
+};
+
+struct vif_filter {
+ int vf_type;
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+ int vf_flags;
+#define VFF_BIDIR 1
+ struct vf_element *vf_filter;
+};
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ struct sockaddr_in6 *vfe_addr;
+ struct in6_addr vfe_mask;
+ int vfe_flags;
+#define VFEF_EXACT 0x0001
+};
+
+struct listaddr {
+ struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
+ struct sockaddr_in6 al_addr; /* local group or neighbor address */
+ u_long al_timer; /* for timing out group or neighbor */
+ time_t al_ctime; /* entry creation time */
+ union {
+ u_int32 alu_genid; /* generation id for neighbor */
+ struct sockaddr_in6 alu_reporter;/* a host which reported membership */
+ } al_alu;
+ u_char al_pv; /* router protocol version */
+ u_char al_mv; /* router mrouted version */
+ u_char al_old; /* time since heard old report: unnecessary for mld */
+ u_char al_index; /* neighbor index */
+ u_long al_timerid; /* timer for group membership */
+ u_long al_query; /* timer for repeated leave query */
+ u_int16 al_flags; /* flags related to this neighbor */
+};
+#define al_genid al_alu.alu_genid
+#define al_reporter al_alu.alu_reporter
+
+#define NBRF_LEAF 0x0001 /* This neighbor is a leaf */
+#define NBRF_GENID 0x0100 /* I know this neighbor's genid */
+#define NBRF_WAITING 0x0200 /* Waiting for peering to come up */
+#define NBRF_ONEWAY 0x0400 /* One-way peering */
+#define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */
+#define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */
+#define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */
+
+/*
+ * Don't peer with neighbors with any of these flags set
+ */
+#define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \
+ NBRF_TOOMANYROUTES|NBRF_NOTPRUNING)
+
+#define NO_VIF ((vifi_t)MAXMIFS) /* An invalid vif index */
+
+
+/*
+ * Used to get the RPF neighbor and IIF info
+ * for a given source from the unicast routing table.
+ */
+struct rpfctl {
+ struct sockaddr_in6 source; /* the source for which we want iif and rpfnbr */
+ struct sockaddr_in6 rpfneighbor;/* next hop towards the source */
+ vifi_t iif; /* the incoming interface to reach the next hop */
+};
diff --git a/usr.sbin/pim6sd/BUGS.TODO b/usr.sbin/pim6sd/BUGS.TODO
new file mode 100644
index 0000000..4a9e684
--- /dev/null
+++ b/usr.sbin/pim6sd/BUGS.TODO
@@ -0,0 +1,126 @@
+ $Id: BUGS.TODO,v 1.1.1.1 1999/08/08 23:30:57 itojun Exp $
+ $FreeBSD$
+
+THIS LIST IS FAR AWAY FROM BEING COMPLETE, so these are the few things
+that came up at the right moment to be written down.
+
+ * Experimental kernel MFC (*,G) related:
+ If the (S,G) iif or oifs are different from the (*,G) or (*,*,RP)
+ iifs/oifs, the resp. (*,G) or (*,*,RP) will delete and disallow
+ creating (*,G) MFC. Only after all MRT (S,G) are deleted, the
+ corresponding (*,G) or (*,*,RP) will create (*,G) MFC.
+
+ * Experimental kernel MFC (*,G) related:
+ Right now when the MFC (*,G) total datarate is above the SPT switch
+ threshold, the (*,G) MFC will be deleted, and any further cache miss
+ will result in (S,G) MFC (the problem is that we must do (S,G)
+ monitoring for eventually high datagate sources). Only after all
+ (S,G) MFCs expire, the daemon's MRT will stop creating (S,G) MFCs
+ (i.e. the next cache miss will result in (*,G) kernel MFC).
+ A better selection should be applied to sort out the higher
+ datarate sources, and at the same time to have (*,G)MFC as well.
+ For example, create few (S,G), and after that create the (*,G). If some
+ of the created (S,G) MFC entries have very low datarate, delete them.
+
+ * Use NetBSD's definition for IPADDR (netinet/in.h):
+#ifdef _KERNEL
+#define __IPADDR(x) ((u_int32_t) htonl((u_int32_t)(x)))
+#else
+#define __IPADDR(x) ((u_int32_t)(x))
+#endif
+
+
+ * The (S,G)RPbit in the DR for the sender and the (S,G)SPT in the
+ downstream router won't timeout and will refresh each other even
+ if the sender is not active:
+
+ S--DR-----------------R1------------RP
+ (S,G)RPbit (S,G)
+ iif toward S
+
+ * Check whether the kernel code sends CACHE_MISS and WRONG_IIF for
+ the LAN-scoped addresses
+
+ * If the RP for a group changes, the DR should cancel any PIM-register-stop
+ timers (XXX: not in the spec, but should be there)
+
+ * If a new interface is configured, include it automatically
+
+ * Don't create routing entries for local link scoped groups
+
+ * Implement adm. scoped filters
+
+ * Do precise check of the timer events to speed up the propagation of the
+ Cand-RP messages + Cand-BSR messages and the election of the BSR.
+
+ * Fix the bug for messing up the things when the receiver is on the
+ same host as the RP for the multicast group (probably was fixed with alpha6,
+ because I cannot reproduce it anymore)
+
+ * Do more precise error check for the received PIM messages. In most cases,
+ the whole message must be parsed completely before starting processing it.
+
+ * Clean up the debugging messages.
+
+ * Use Patricia tree to search the routing table
+ (There is a nice paper in Sigcomm '97 about fast routing tables
+ implementation, so need to check it as well)
+
+ * Do switch back to the Shared Tree by timing out the SPT if the rate
+ is too low (not in the spec, but Ahmed pointed out some complications if
+ this happens)
+
+ * Change all countdown timers to events timeout (callout.c)
+ (The current implementation is very unefficient if the routing table becomes
+ very large)
+
+ * Send immediately Join/Prune, instead of relying of Join/Prune timer = 0
+
+ * Fix the code allowing interface UP/DOWN without restarting pimd.
+
+ * Do more testings for SPT switch, Join/Prune, asserts, etc...
+
+ * Test the (*,*,RP) code (need PIM/DVMRP border router to do so)
+
+ * Test the RSRR (RSVP support) code
+
+ * Send Initial_Reply RSRR message if the interfaces detected by pimd change
+
+ * SNMP support
+
+===TODO by function name===
+igmp_proto.c:
+ * accept_group_report():
+ - add a leaf if DR or forwarder (currently only if DR)???
+ * accept_leave_message():
+ - send immediately PIM prune message if the last member has left
+
+main.c
+ * main():
+ - use a combination of time and hostid to initialize the random generator.
+ * restart():
+ - check the implementation
+
+pim_proto.c
+ * pim_register():
+ - IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM USER SPACE
+ AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE KERNEL.
+ * register_stop():
+ - REGISTER_STOP rate limiting
+
+route.c
+ * process_cache_miss()
+ - use negative cache.
+
+rp.c
+ * add_rp_grp_entry():
+ - FIX THE BUG when adding an RP for different prefix requires remapping
+ for some groups!!!
+ (Intentionally left, waiting to come up with an idea how to implement
+ it simple and efficient. If you configure all RPs to advertise the
+ same prefix, the bug won't "show up")
+
+================DONE=====================
+
+ * When receive PIM_REGISTER, check whether I am the chosen RP
+
diff --git a/usr.sbin/pim6sd/BUGS.V6 b/usr.sbin/pim6sd/BUGS.V6
new file mode 100644
index 0000000..cdc1903
--- /dev/null
+++ b/usr.sbin/pim6sd/BUGS.V6
@@ -0,0 +1,10 @@
+ $FreeBSD$
+
+Write your bugs reports here
+
+BSR and Cand_rp discovery (base mecanism ): fixed
+Encap/decap and unicast register send : fixed.
+Join/Prune messages: They are sent but the RP don't create the (*,G) entry: fixed.
+Check with the sockaddr6_any and sockaddr_d (not sure it works because of the scope..)
+Pb with filling the upstream neighbor:fixed
+to query groups at boot time not efficient because there is no RP
diff --git a/usr.sbin/pim6sd/LICENSE.mrouted b/usr.sbin/pim6sd/LICENSE.mrouted
new file mode 100644
index 0000000..9cf01b6
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.mrouted
@@ -0,0 +1,49 @@
+ $FreeBSD$
+
+The mrouted program is covered by the following license. Use of the
+mrouted program represents acceptance of these terms and conditions.
+
+1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
+to use, copy and modify the computer software ``mrouted'' (hereinafter
+called the ``Program''), upon the terms and conditions hereinafter set
+out and until Licensee discontinues use of the Licensed Program.
+
+2. LICENSEE acknowledges that the Program is a research tool still in
+the development state, that it is being supplied ``as is,'' without any
+accompanying services from STANFORD, and that this license is entered
+into in order to encourage scientific collaboration aimed at further
+development and application of the Program.
+
+3. LICENSEE may copy the Program and may sublicense others to use object
+code copies of the Program or any derivative version of the Program.
+All copies must contain all copyright and other proprietary notices found
+in the Program as provided by STANFORD. Title to copyright to the
+Program remains with STANFORD.
+
+4. LICENSEE may create derivative versions of the Program. LICENSEE
+hereby grants STANFORD a royalty-free license to use, copy, modify,
+distribute and sublicense any such derivative works. At the time
+LICENSEE provides a copy of a derivative version of the Program to a
+third party, LICENSEE shall provide STANFORD with one copy of the source
+code of the derivative version at no charge to STANFORD.
+
+5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
+By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
+OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
+for any liability nor for any direct, indirect or consequential damages
+with respect to any claim by LICENSEE or any third party on account of or
+arising from this Agreement or use of the Program.
+
+6. This agreement shall be construed, interpreted and applied in
+accordance with the State of California and any legal action arising
+out of this Agreement or use of the Program shall be filed in a court
+in the State of California.
+
+7. Nothing in this Agreement shall be construed as conferring rights to
+use in advertising, publicity or otherwise any trademark or the name
+of ``Stanford''.
+
+The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
diff --git a/usr.sbin/pim6sd/LICENSE.pim6dd b/usr.sbin/pim6sd/LICENSE.pim6dd
new file mode 100644
index 0000000..0055e6e
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.pim6dd
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
diff --git a/usr.sbin/pim6sd/LICENSE.pim6sd b/usr.sbin/pim6sd/LICENSE.pim6sd
new file mode 100644
index 0000000..853f9e0
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.pim6sd
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ * $FreeBSD$
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
diff --git a/usr.sbin/pim6sd/LICENSE.pimd b/usr.sbin/pim6sd/LICENSE.pimd
new file mode 100644
index 0000000..96791e5
--- /dev/null
+++ b/usr.sbin/pim6sd/LICENSE.pimd
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: LICENSE.pimd,v 1.1.1.1 1999/08/08 23:30:57 itojun Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
diff --git a/usr.sbin/pim6sd/Makefile b/usr.sbin/pim6sd/Makefile
new file mode 100644
index 0000000..9cda3ce
--- /dev/null
+++ b/usr.sbin/pim6sd/Makefile
@@ -0,0 +1,49 @@
+# Copyright (c) 1999 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# $FreeBSD$
+
+PROG= pim6sd
+SRCS= mld6.c mld6_proto.c\
+ inet6.c kern.c main.c config.c debug.c routesock.c vers.c callout.c\
+ route.c vif.c timer.c mrt.c pim6.c pim6_proto.c rp.c crc.c trace.c\
+ cfparse.y cftoken.l
+SRCS+= y.tab.h
+y.tab.h: cfparse.y
+CLEANFILES+= lex.yy.c y.tab.h y.tab.c
+CFLAGS+= -g
+CFLAGS+=-DINET6 -DPIM -DIOCTL_OK_ON_RAW_SOCKET -I${.OBJDIR}
+CFLAGS+=-DHAVE_STDARG_H
+LDADD+= -ly -ll
+
+MAN1= pim6stat.1
+MAN5= pim6sd.conf.5
+MAN8= pim6sd.8
+
+afterinstall:
+ install ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${INSTALLFLAGS} ${SRCDIR}/pim6stat ${DESTDIR}${BINDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pim6sd/README b/usr.sbin/pim6sd/README
new file mode 100644
index 0000000..49dfc17
--- /dev/null
+++ b/usr.sbin/pim6sd/README
@@ -0,0 +1,85 @@
+ $Id: README,v 1.1.1.1 1999/08/08 23:30:57 itojun Exp $
+ $FreeBSD$
+
+WARNING! WARNING! WARNING!
+THIS RELEASE IS VERY ALPHA, SO PLEASE DO NOT REDISTRIBUTE AND
+DO NOT TRY IT OUTSIDE OF YOUR TESTBED.
+
+This is README for pimd, the PIM multicast daemon.
+PIM-SM version: 2
+Check ftp://catarina.usc.edu/pub/pim/pimd/ for lastest version.
+
+SUPPORTED PLATFORMS: FreeBSD-2.2.*, SunOS-4.1.3, Solaris-2.5.1 and 2.6,
+SGI, BSDI 3.0/3.1, NetBSD-1.3
+
+AVAILABLE PIM kernel patches: FreeBSD-2.2.1, FreeBSD-2.2.2, FreeBSD-2.2,5,
+ SunOS-4.1.3, SGI, BSDI-3.0, BSDI-3.1, NetBSD-1.3
+
+Linux: pimd compiles under Linux, and linux-2.1.103 seems to have PIM-SMv2
+kernel support, but I haven't tested whether the kernel patches really work.
+
+FAST START (read "fast explanation" :))
+
+1. Apply the PIM kernel patches, recompile, reboot
+
+2. Copy pimd.conf to /etc and edit as appropriate. Disable the interfaces
+you don't need. Note that you need at least 2 physical interfaces enabled.
+
+3. Edit Makefile by uncommenting the line(s) corresponding to your platform.
+
+4. Recompile pimd
+
+5. Run pimd as a root. It is highly recommended to run it in debug mode.
+Because there are many debug messages, you can specify only a subset of
+the messages to be printed out:
+
+usage: pimd [-c configfile] [-d [debug_level][,debug_level]]
+
+Valid debug levels: dvmrp_prunes,dvmrp_mrt,dvmrp_neighbors,dvmrp_timers,igmp_proto,igmp_timers,igmp_members,trace,timeout,pkt,interfaces,kernel,cache,rsrr,pim_hello,pim_register,pim_join_prune,pim_bootstrap,pim_asserts,pim_cand_rp,pim_routes,pim_timers,pim_rpf
+
+If you want to see all messages, use "pimd -d" only.
+
+6. Note that it takes of the order of 30 seconds to 1 minute until the
+Bootstrap router is elected and the RP-set distributed to the PIM routers,
+and without the RP-set in the routers the multicast packets cannot be
+forwarded.
+
+7. There are plenty of bugs, some of them known (check BUGS.TODO),
+some of them unknown, so your bug reports are more than welcome.
+
+
+Pavlin Ivanov Radoslavov
+pavlin@catarina.usc.edu
+
+ACKNOWLEDGEMENTS:
+
+ * The PIM kernel modifications and pimd itself were originally
+ written by Ahmed Helmy (ahelmy@catarina.usc.edu) as a summer intern in SGI.
+
+ * The "up to the March '97 I-D spec" + RSVP support pimd version was done
+ during my summer'97 intern in Sun Microsystems under Michael Speer's
+ supervision.
+
+ * BSDI 3.0/3.1 support + various improvements and bug reports
+ by Hitoshi Asaeda (asaeda@yamato.ibm.co.jp).
+
+ * Bug reports and SGI tests by Nidhi Bhaskar (nidhi@cho-oyu.engr.sgi.com).
+
+ * Bug reports and SunOS tests by Isabelle Girard (girardi@rc.bel.alcatel.be)
+ and Dirk Ooms (oomsd@rc.bel.alcatel.be)
+
+ * NetBSD-1.3 compilation support (both for pimd and the kernel mods) and
+ bug reports by Heiko W.Rupp <hwr@pilhuhn.de>
+
+ * Bug reports by Chirayu Shah (shahzad@torrentnet.com)
+
+ * A number of changes copied back from pimdd (PIM-DM) stand-alone
+ implementation by Kurt Windisch (kurtw@antc.uoregon.edu)
+
+ * Linux patches by "Jonathan Day" <jd9812@my-dejanews.com> and
+ Fred Griffoul <griffoul@ccrle.nec.de>
+
+ * Thanks to the FreeBSD team and particularly to the
+ freebsd-hackers mailing list participants for the help
+ with the real-time debugging of the FreeBSD kernel.
+
diff --git a/usr.sbin/pim6sd/README.first b/usr.sbin/pim6sd/README.first
new file mode 100644
index 0000000..b58efbc
--- /dev/null
+++ b/usr.sbin/pim6sd/README.first
@@ -0,0 +1,15 @@
+ $FreeBSD$
+
+WARNING WARNING WARNING:
+
+This is Pim Sparse Mode for IPv6. BUT IT HAS STILL SOME PROBLEMS !
+So , before trying to run it , take a look to the code.
+
+
+THIS VERSION NEEDS :
+
+lots of debugging work...
+AND : more debugging work :)
+
+
+Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
diff --git a/usr.sbin/pim6sd/callout.c b/usr.sbin/pim6sd/callout.c
new file mode 100644
index 0000000..52eda4f
--- /dev/null
+++ b/usr.sbin/pim6sd/callout.c
@@ -0,0 +1,316 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted". Use of the mrouted program represents acceptance
+ * of the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * callout.c,v 3.8.4.5 1997/05/16 20:18:25 fenner Exp
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "debug.h"
+#include "defs.h"
+
+/* the code below implements a callout queue */
+
+static int id = 0;
+static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
+
+struct timeout_q
+{
+ struct timeout_q *next; /* next event */
+ int id;
+ cfunc_t func; /* function to call */
+ void *data; /* func's data */
+ int time; /* time offset to next event */
+};
+
+#if 0
+#define CALLOUT_DEBUG 1
+#define CALLOUT_DEBUG2 1
+#endif /* 0 */
+
+#ifdef CALLOUT_DEBUG2
+static void print_Q __P((void));
+#else
+#define print_Q()
+#endif
+
+void
+callout_init()
+{
+ Q = (struct timeout_q *) 0;
+}
+
+void
+free_all_callouts()
+{
+ struct timeout_q *p;
+
+ while (Q)
+ {
+ p = Q;
+ Q = Q->next;
+ free(p);
+ }
+}
+
+
+/*
+ * elapsed_time seconds have passed; perform all the events that should
+ * happen.
+ */
+void
+age_callout_queue(elapsed_time)
+ int elapsed_time;
+{
+ struct timeout_q *ptr,
+ *expQ;
+
+#ifdef CALLOUT_DEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "aging queue (elapsed time %d):", elapsed_time);
+ print_Q();
+#endif
+
+ expQ = Q;
+ ptr = NULL;
+
+ while (Q)
+ {
+ if (Q->time > elapsed_time)
+ {
+ Q->time -= elapsed_time;
+ if (ptr)
+ {
+ ptr->next = NULL;
+ break;
+ }
+ return;
+ }
+ else
+ {
+ elapsed_time -= Q->time;
+ ptr = Q;
+ Q = Q->next;
+ }
+ }
+
+ /* handle queue of expired timers */
+ while (expQ)
+ {
+ ptr = expQ;
+ if (ptr->func)
+ ptr->func(ptr->data);
+ expQ = expQ->next;
+ free(ptr);
+ }
+}
+
+/*
+ * Return in how many seconds age_callout_queue() would like to be called.
+ * Return -1 if there are no events pending.
+ */
+
+int
+timer_nextTimer()
+{
+ if (Q)
+ {
+ if (Q->time < 0)
+ {
+ log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
+ Q->time);
+ return 0;
+ }
+ return Q->time;
+ }
+ return -1;
+}
+
+/*
+ * sets the timer
+ */
+
+int
+timer_setTimer(delay, action, data)
+ int delay; /* number of units for timeout */
+ cfunc_t action; /* function to be called on timeout */
+ void *data; /* what to call the timeout function with */
+{
+ struct timeout_q *ptr,
+ *node,
+ *prev;
+
+#ifdef CALLOUT_DEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "setting timer:");
+ print_Q();
+#endif
+
+ /* create a node */
+
+ node = (struct timeout_q *) malloc(sizeof(struct timeout_q));
+ if (node == 0)
+ {
+ log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
+ return -1;
+ }
+ node->func = action;
+ node->data = data;
+ node->time = delay;
+ node->next = 0;
+ node->id = ++id;
+
+ prev = ptr = Q;
+
+ /* insert node in the queue */
+
+ /* if the queue is empty, insert the node and return */
+
+ if (!Q)
+ Q = node;
+ else
+ {
+ /* chase the pointer looking for the right place */
+
+ while (ptr)
+ {
+
+ if (delay < ptr->time)
+ {
+ /* right place */
+
+ node->next = ptr;
+ if (ptr == Q)
+ Q = node;
+ else
+ prev->next = node;
+ ptr->time -= node->time;
+ print_Q();
+ return node->id;
+ }
+ else
+ {
+ /* keep moving */
+
+ delay -= ptr->time;
+ node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+ prev->next = node;
+ }
+ print_Q();
+ return node->id;
+}
+
+/* returns the time until the timer is scheduled */
+
+int
+timer_leftTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left = 0;
+
+ if (!timer_id)
+ return -1;
+
+ for (ptr = Q; ptr; ptr = ptr->next)
+ {
+ left += ptr->time;
+ if (ptr->id == timer_id)
+ return left;
+ }
+ return -1;
+}
+
+/* clears the associated timer */
+
+void
+timer_clearTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr,
+ *prev;
+
+ if (!timer_id)
+ return;
+
+ prev = ptr = Q;
+
+ /*
+ * find the right node, delete it. the subsequent node's time gets bumped
+ * up
+ */
+
+ print_Q();
+ while (ptr)
+ {
+ if (ptr->id == timer_id)
+ {
+ /* got the right node */
+
+ /* unlink it from the queue */
+
+ if (ptr == Q)
+ Q = Q->next;
+ else
+ prev->next = ptr->next;
+
+ /* increment next node if any */
+ if (ptr->next != 0)
+ (ptr->next)->time += ptr->time;
+
+ if (ptr->data)
+ free(ptr->data);
+ free(ptr);
+ print_Q();
+ return;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ print_Q();
+}
+
+#ifdef CALLOUT_DEBUG2
+/*
+ * debugging utility
+ */
+
+static void
+print_Q()
+{
+ struct timeout_q *ptr;
+
+ IF_DEBUG(DEBUG_TIMEOUT)
+ for (ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time);
+}
+#endif /* CALLOUT_DEBUG2 */
diff --git a/usr.sbin/pim6sd/callout.h b/usr.sbin/pim6sd/callout.h
new file mode 100644
index 0000000..61c4de3
--- /dev/null
+++ b/usr.sbin/pim6sd/callout.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+
+#ifndef CALLOUT_H
+#define CALLOUT_H
+
+#include "defs.h"
+
+extern void callout_init __P((void));
+extern void free_all_callouts __P((void));
+extern void age_callout_queue __P((int));
+extern int timer_nextTimer __P((void));
+extern int timer_setTimer __P((int, cfunc_t, void *));
+extern void timer_clearTimer __P((int));
+extern int timer_leftTimer __P((int));
+
+#endif
diff --git a/usr.sbin/pim6sd/cfparse.h b/usr.sbin/pim6sd/cfparse.h
new file mode 100644
index 0000000..256b3f1
--- /dev/null
+++ b/usr.sbin/pim6sd/cfparse.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#if defined(YIPS_DEBUG)
+# define DP(str) YIPSDEBUG(DEBUG_CONF, cfdebug_print(str, yytext, yyleng))
+# define YYD_ECHO \
+ { YIPSDEBUG(DEBUG_CONF, printf("<%d>", yy_start); ECHO ; printf("\n");); }
+# define YIPSDP(cmd) YIPSDEBUG(DEBUG_CONF, cmd)
+# define PLOG printf
+#else
+# define DP(str)
+# define YYD_ECHO
+# define YIPSDP(cmd)
+# define PLOG(cmd)
+#endif /* defined(YIPS_DEBUG) */
+
+/* cfparse.y */
+extern void cf_init __P((int, int));
+#ifdef notyet
+extern int re_cfparse __P((void));
+#endif
+extern int cf_post_config __P((void));
+extern int yyparse __P((void));
+
+/* cftoken.l */
+extern void yyerror __P((char *, ...));
+extern void yywarn __P((char *, ...));
+extern int cfparse __P((int, int));
diff --git a/usr.sbin/pim6sd/cfparse.y b/usr.sbin/pim6sd/cfparse.y
new file mode 100644
index 0000000..3bc4341
--- /dev/null
+++ b/usr.sbin/pim6sd/cfparse.y
@@ -0,0 +1,960 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+%{
+#include <sys/types.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <syslog.h>
+
+#include "defs.h"
+#include "rp.h"
+#include "vif.h"
+
+#include "var.h"
+#include "vmbuf.h"
+#include "cfparse.h"
+#include "debug.h"
+#include "pimd.h"
+#include "timer.h"
+#include "inet6.h"
+
+#define set_param(var,val,p) \
+ do {\
+ if ((var) != -1) {\
+ yywarn("%s doubly defined(ignore %d)", (p), (val));\
+ }\
+ else {\
+ (var) = val;\
+ }\
+ } while(0)
+
+struct in6_prefix {
+ struct in6_addr paddr;
+ int plen;
+};
+
+struct attr_list {
+ struct attr_list *next;
+ int type;
+ union {
+ unsigned int flags;
+ double number;
+ struct in6_prefix prefix;
+ }attru;
+};
+
+enum {IFA_FLAG, IFA_PREFERENCE, IFA_METRIC, RPA_PRIORITY, RPA_TIME,
+ BSRA_PRIORITY, BSRA_TIME, IN6_PREFIX, THRESA_RATE, THRESA_INTERVAL};
+
+static int strict; /* flag if the grammer check is strict */
+static struct attr_list *rp_attr, *bsr_attr, *grp_prefix, *regthres_attr,
+ *datathres_attr;
+static char *cand_rp_ifname, *cand_bsr_ifname;
+static int srcmetric, srcpref, helloperiod, jpperiod, granularity,
+ datatimo, regsuptimo, probetime, asserttimo;
+static double helloperiod_coef, jpperiod_coef;
+
+static int debugonly;
+%}
+
+%union {
+ unsigned long num;
+ double fl;
+ vchar_t val;
+ struct attr_list *attr;
+}
+
+%token EOS
+%token LOGGING LOGLEV NOLOGLEV
+%token YES NO
+%token REVERSELOOKUP
+%token PHYINT IFNAME DISABLE PREFERENCE METRIC NOLISTENER
+%token GRPPFX
+%token CANDRP CANDBSR TIME PRIORITY
+%token NUMBER STRING SLASH
+%token REGTHRES DATATHRES RATE INTERVAL
+%token SRCMETRIC SRCPREF HELLOPERIOD GRANULARITY JPPERIOD
+%token DATATIME REGSUPTIME PROBETIME ASSERTTIME
+
+%type <num> LOGLEV NOLOGLEV
+%type <fl> NUMBER
+%type <val> STRING IFNAME
+%type <attr> if_attributes rp_substatement rp_attributes
+%type <attr> bsr_substatement bsr_attributes thres_attributes
+
+%%
+statements:
+ /* empty */
+ | statements statement
+ ;
+
+statement:
+ logging_statement
+ | reverselookup_statement
+ | phyint_statement
+ | candrp_statement
+ | candbsr_statement
+ | grppfx_statement
+ | regthres_statement
+ | datathres_statement
+ | param_statement
+ ;
+
+/* logging */
+logging_statement:
+ LOGGING log_specs EOS
+ ;
+
+log_specs:
+ /* empty */
+ | log_specs LOGLEV {debug |= $2;}
+ | log_specs NOLOGLEV {debug &= ~($2);}
+ ;
+
+/* reverselookup */
+reverselookup_statement:
+ REVERSELOOKUP YES EOS { numerichost = FALSE; }
+ | REVERSELOOKUP NO EOS { numerichost = TRUE; }
+ ;
+
+/* phyint */
+phyint_statement:
+ PHYINT IFNAME if_attributes EOS {
+ struct uvif *v;
+
+ v = find_vif($2.v);
+ free($2.v); /* XXX */
+ if (v == NULL) {
+ yywarn("unknown interface: %s", $2.v);
+ free_attr_list($3);
+ if (strict)
+ return(-1);
+ }
+ else {
+ struct attr_list *p;
+
+ for (p = (struct attr_list *)v->config_attr;
+ p && p->next; p = p->next)
+ ;
+ if (p)
+ p->next = (void *)$3;
+ else
+ v->config_attr = (void *)$3;
+ }
+ }
+ ;
+
+if_attributes:
+ { $$ = NULL; }
+ | if_attributes DISABLE
+ {
+ if (($$ = add_attribute_flag($1, IFA_FLAG,
+ VIFF_DISABLED)) == NULL)
+ return(-1);
+ }
+ | if_attributes NOLISTENER
+ {
+ if (($$ = add_attribute_flag($1, IFA_FLAG,
+ VIFF_NOLISTENER)) == NULL)
+ return(-1);
+ }
+ | if_attributes PREFERENCE NUMBER
+ {
+ if (($$ = add_attribute_num($1, IFA_PREFERENCE, $3))
+ == NULL)
+ return(-1);
+ }
+ | if_attributes METRIC NUMBER
+ {
+ if (($$ = add_attribute_num($1, IFA_METRIC, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* cand_rp */
+candrp_statement:
+ CANDRP rp_substatement EOS {
+ if (cand_rp_flag == TRUE) {
+ yywarn("cand_rp doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else {
+ cand_rp_flag = TRUE;
+ rp_attr = $2;
+ }
+ }
+ ;
+/* XXX: intermediate rule to avoid shift-reduce conflict */
+rp_substatement:
+ IFNAME rp_attributes
+ {
+ if (cand_rp_ifname) {
+ yywarn("ifname for cand_rp doubly defined");
+ if (strict)
+ return(-1);
+ }
+ else
+ cand_rp_ifname = $1.v;
+ $$ = $2;
+ }
+ | rp_attributes
+ ;
+rp_attributes:
+ { $$ = NULL; }
+ | rp_attributes PRIORITY NUMBER
+ {
+ if (($$ = add_attribute_num($1, RPA_PRIORITY, $3))
+ == NULL)
+ return(-1);
+ }
+ | rp_attributes TIME NUMBER
+ {
+ if (($$ = add_attribute_num($1, RPA_TIME, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* cand_bootstrap_router */
+candbsr_statement:
+ CANDBSR bsr_substatement EOS {
+ if (cand_bsr_flag == TRUE) {
+ yywarn("cand_bsr doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else {
+ cand_bsr_flag = TRUE;
+ bsr_attr = $2;
+ }
+ }
+ ;
+/* XXX: intermediate rule to avoid shift-reduce conflict */
+bsr_substatement:
+ IFNAME bsr_attributes
+ {
+ if (cand_bsr_ifname) {
+ yywarn("ifname for cand_bsr doubly defined");
+ if (strict)
+ return(-1);
+ }
+ else
+ cand_bsr_ifname = $1.v;
+ $$ = $2;
+ }
+ | bsr_attributes
+ ;
+
+bsr_attributes:
+ { $$ = NULL; }
+ | bsr_attributes PRIORITY NUMBER
+ {
+ if (($$ = add_attribute_num($1, BSRA_PRIORITY, $3))
+ == NULL)
+ return(-1);
+ }
+ | bsr_attributes TIME NUMBER
+ {
+ if (($$ = add_attribute_num($1, BSRA_TIME, $3))
+ == NULL)
+ return(-1);
+ }
+ ;
+
+/* group_prefix <group-addr>/<prefix_len> */
+grppfx_statement:
+ GRPPFX STRING SLASH NUMBER EOS {
+ struct in6_prefix prefix;
+ int prefixok = 1;
+
+ if (inet_pton(AF_INET6, $2.v, &prefix.paddr) != 1) {
+ yywarn("invalid IPv6 address: %s (ignored)", $2);
+ prefixok = 0;
+ }
+ free($2.v); /* XXX: which was allocated dynamically */
+
+ prefix.plen = $4;
+ if (prefix.plen < 0 || prefix.plen > 128) {
+ yywarn("invalid prefix length: %d (ignored)",
+ prefix.plen);
+ prefixok = 0;
+ }
+
+ if (prefixok) {
+ struct attr_list *new;
+
+ if ((new = malloc(sizeof(*new))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset(new, 0, sizeof(*new));
+
+ new->type = IN6_PREFIX;
+ new->attru.prefix = prefix;
+ new->next = grp_prefix;
+
+ grp_prefix = new;
+ }
+ }
+ ;
+
+/*
+ * switch_register_threshold [rate <number> interval <number>]
+ * Operation: reads and assigns the switch to the spt threshold
+ * due to registers for the router, if used as RP.
+ * Maybe extended to support different thresholds for different
+ * groups(prefixes).
+ */
+regthres_statement:
+ REGTHRES thres_attributes EOS {
+ if (regthres_attr) {
+ yywarn("switch_register_threshold doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else
+ regthres_attr = $2;
+ }
+ ;
+
+thres_attributes:
+ { $$ = NULL; }
+ | thres_attributes RATE NUMBER
+ {
+ if (($$ = add_attribute_num($1, THRESA_RATE, $3))
+ == NULL)
+ return(-1);
+ }
+ | thres_attributes INTERVAL NUMBER
+ {
+ if (($$ = add_attribute_num($1, THRESA_INTERVAL, $3))
+ == NULL)
+ return(-1);
+ }
+
+/*
+ * switch_data_threshold [rate <number> interval <number>]
+ * Operation: reads and assigns the switch to the spt threshold due to
+ * data packets, if used as DR.
+ */
+datathres_statement:
+ DATATHRES thres_attributes EOS {
+ if (datathres_attr) {
+ yywarn("switch_data_threshold doubly defined");
+ free_attr_list($2);
+ if (strict)
+ return(-1);
+ }
+ else
+ datathres_attr = $2;
+ }
+ ;
+
+param_statement:
+ SRCMETRIC NUMBER EOS
+ {
+ set_param(srcmetric, $2, "default_source_metric");
+ }
+ | SRCPREF NUMBER EOS
+ {
+ set_param(srcpref, $2, "default_source_preference");
+ }
+ | HELLOPERIOD NUMBER EOS
+ {
+ set_param(helloperiod, $2, "hello_period");
+ }
+ | HELLOPERIOD NUMBER NUMBER EOS
+ {
+ set_param(helloperiod, $2, "hello_period");
+ set_param(helloperiod_coef, $3, "hello_period(coef)");
+ }
+ | JPPERIOD NUMBER EOS
+ {
+ set_param(jpperiod, $2, "join_prune_period");
+ }
+ | JPPERIOD NUMBER NUMBER EOS
+ {
+ set_param(jpperiod, $2, "join_prune_period");
+ set_param(jpperiod_coef, $3, "join_prune_period(coef)");
+ }
+ | GRANULARITY NUMBER EOS
+ {
+ set_param(granularity, $2, "granularity");
+ }
+ | REGSUPTIME NUMBER EOS
+ {
+ set_param(regsuptimo, $2, "register_suppression_timeout");
+ }
+ | PROBETIME NUMBER EOS
+ {
+ set_param(probetime, $2, "probe_time");
+ }
+ | ASSERTTIME NUMBER EOS
+ {
+ set_param(asserttimo, $2, "assert_timeout");
+ }
+ ;
+%%
+
+static struct attr_list *
+add_attribute_flag(list, type, flag)
+ struct attr_list *list;
+ int type;
+ unsigned int flag;
+{
+ struct attr_list *p;
+
+ if ((p = malloc(sizeof(*p))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset((void *)p, 0, sizeof(*p));
+ p->type = type;
+ p->attru.flags = flag;
+ p->next = list;
+
+ return(p);
+}
+
+/* XXX: too many dup code... */
+static struct attr_list *
+add_attribute_num(list, type, num)
+ struct attr_list *list;
+ int type;
+ double num;
+{
+ struct attr_list *p;
+
+ if ((p = malloc(sizeof(*p))) == NULL) {
+ yyerror("malloc failed");
+ return(NULL);
+ }
+ memset((void *)p, 0, sizeof(*p));
+ p->type = type;
+ p->attru.number = num;
+ p->next = list;
+
+ return(p);
+}
+
+static void
+free_attr_list(list)
+ struct attr_list *list;
+{
+ struct attr_list *p, *next;
+
+ for(p = list; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+}
+
+int
+param_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ /* at first, set the default values to all the undefined variables */
+ if (srcmetric == -1) srcmetric = DEFAULT_LOCAL_METRIC;
+ if (srcpref == -1) srcpref = DEFAULT_LOCAL_PREF;
+ if (helloperiod == -1) helloperiod = PIM_TIMER_HELLO_PERIOD;
+ if (helloperiod_coef == -1) helloperiod_coef = 3.5;
+ if (jpperiod == -1) jpperiod = PIM_JOIN_PRUNE_PERIOD;
+ if (jpperiod_coef == -1) jpperiod_coef = 3.5;
+ if (granularity == -1) granularity = DEFAULT_TIMER_INTERVAL;
+ if (datatimo == -1) datatimo = PIM_DATA_TIMEOUT;
+ if (regsuptimo == -1) regsuptimo = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ if (probetime == -1) probetime = PIM_REGISTER_PROBE_TIME;
+ if (asserttimo == -1) asserttimo = PIM_ASSERT_TIMEOUT;
+
+ /* set protocol parameters using the configuration variables */
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) {
+ v->uv_local_metric = srcmetric;
+ v->uv_local_pref = srcpref;
+ }
+ pim_hello_period = helloperiod;
+ pim_hello_holdtime = helloperiod * helloperiod_coef;
+ pim_join_prune_period = jpperiod;
+ pim_join_prune_holdtime = jpperiod * jpperiod_coef;
+ timer_interval = granularity;
+ pim_data_timeout = datatimo;
+ pim_register_suppression_timeout = regsuptimo;
+ pim_register_probe_time = probetime;
+ pim_assert_timeout = asserttimo;
+
+ IF_DEBUG(DEBUG_PIM_HELLO) {
+ log(LOG_DEBUG, 0, "pim_hello_period set to: %u",
+ pim_hello_period);
+ log(LOG_DEBUG, 0, "pim_hello_holdtime set to: %u",
+ pim_hello_holdtime);
+ }
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) {
+ log(LOG_DEBUG,0 , "pim_join_prune_period set to: %u",
+ pim_join_prune_period);
+ log(LOG_DEBUG, 0, "pim_join_prune_holdtime set to: %u",
+ pim_join_prune_holdtime);
+ }
+
+ return(0);
+}
+
+int
+phyint_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+ struct attr_list *al;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) {
+ for (al = (struct attr_list *)v->config_attr; al; al = al->next) {
+ switch(al->type) {
+ case IFA_FLAG:
+ v->uv_flags |= al->attru.flags;
+ break;
+ case IFA_PREFERENCE:
+ if (al->attru.number < 1 ||
+ al->attru.number > 255)
+ yywarn("invalid phyint preference(%d)",
+ (int)al->attru.number);
+ else {
+ v->uv_local_pref = al->attru.number;
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "default localpref for %s "
+ "is %d",
+ v->uv_name,
+ v->uv_local_pref);
+ }
+ break;
+ case IFA_METRIC:
+ if (al->attru.number < 1 ||
+ al->attru.number > 1024)
+ yywarn("invalid metric(%d)",
+ al->attru.number);
+ else {
+ v->uv_metric = al->attru.number;
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "default local metric for %s "
+ "is %d",
+ v->uv_name,
+ v->uv_metric);
+ }
+ break;
+ }
+ }
+ }
+
+ return(0);
+}
+
+int
+rp_config()
+{
+ struct sockaddr_in6 *sa6_rp = NULL;
+ struct attr_list *al;
+ u_int8 *data_ptr;
+
+ /* initialization by default values */
+ my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ my_cand_rp_priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+
+ if (cand_rp_ifname) {
+ sa6_rp = local_iface(cand_rp_ifname);
+ if (!sa6_rp)
+ log(LOG_WARNING, 0,
+ "cand_rp '%s' is not configured. "
+ "take the max local address the router..",
+ cand_rp_ifname);
+ }
+
+ for (al = rp_attr; al; al = al->next) {
+ switch(al->type) {
+ case RPA_PRIORITY:
+ if (al->attru.number < 0)
+ my_cand_rp_priority =
+ PIM_DEFAULT_CAND_RP_PRIORITY;
+ else
+ my_cand_rp_priority = al->attru.number;
+ break;
+ case RPA_TIME:
+ if (al->attru.number < 10)
+ my_cand_rp_adv_period = 10;
+ else if (al->attru.number > PIM_DEFAULT_CAND_RP_ADV_PERIOD)
+ my_cand_rp_adv_period =
+ PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ else
+ my_cand_rp_adv_period = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for RP", al->type);
+ break;
+ }
+ }
+
+ if (!sa6_rp)
+ sa6_rp = max_global_address(); /* this MUST suceed */
+ my_cand_rp_address = *sa6_rp;
+
+ /*
+ * initialize related parameters
+ */
+
+ /*
+ * Note that sizeof(pim6_enocd_uni_addr_t) might be larger than
+ * the length of the Encoded-Unicast-address field(18 byte) due to
+ * some padding put in the compiler. However, it doesn't matter
+ * since we use the space just as a buffer(i.e not as the message).
+ */
+ cand_rp_adv_message.buffer = (u_int8 *)malloc(4 +
+ sizeof(pim6_encod_uni_addr_t) +
+ 255*sizeof(pim6_encod_grp_addr_t));
+ if(cand_rp_adv_message.buffer == NULL)
+ log(LOG_ERR, 0, "Candrpadv Buffer allocation");
+
+ cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer;
+
+ /*
+ * By default, if no group_prefix configured, then prefix_cnt == 0
+ * implies group_prefix = ff00::/8 and masklen = 8.
+ */
+ *cand_rp_adv_message.prefix_cnt_ptr = 0;
+ cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer;
+
+ /* TODO: XXX: HARDCODING!!! */
+ cand_rp_adv_message.insert_data_ptr += (4 + 18);
+ cand_rp_adv_message.message_size =
+ cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+
+ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period;
+
+ /* TODO: HARDCODING! */
+ data_ptr = cand_rp_adv_message.buffer + 1; /* WARNING */
+ PUT_BYTE(my_cand_rp_priority,data_ptr);
+ PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr);
+ PUT_EUADDR6(my_cand_rp_address.sin6_addr,data_ptr);
+ IF_DEBUG(DEBUG_PIM_CAND_RP) {
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP address is : %s",
+ inet6_fmt(&my_cand_rp_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP priority is : %u",my_cand_rp_priority);
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP advertisement period is : %u sec.",
+ my_cand_rp_adv_period);
+ }
+
+ return(0);
+}
+
+int
+bsr_config()
+{
+ struct sockaddr_in6 *sa6_bsr = NULL;
+ struct attr_list *al;
+
+ /* initialization by default values */
+ my_bsr_period = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ my_bsr_priority = PIM_DEFAULT_BSR_PRIORITY;
+
+ if (cand_bsr_ifname) {
+ sa6_bsr = local_iface(cand_bsr_ifname);
+ if (!sa6_bsr)
+ log(LOG_WARNING, 0,
+ "bsr '%s' is not configured. "
+ "take the max local address the router..",
+ cand_bsr_ifname);
+ }
+
+ for (al = bsr_attr; al; al = al->next) {
+ switch(al->type) {
+ case BSRA_PRIORITY:
+ if (al->attru.number >= 0)
+ my_bsr_priority = al->attru.number;
+ break;
+ case BSRA_TIME:
+ if (al->attru.number < 10)
+ my_bsr_period = 10;
+ else if (al->attru.number > PIM_DEFAULT_BOOTSTRAP_PERIOD)
+ my_bsr_period =
+ PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ else
+ my_bsr_period = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for BSR", al->type);
+ break;
+ }
+ }
+
+ if (!sa6_bsr)
+ sa6_bsr = max_global_address(); /* this MUST suceed */
+ my_bsr_address = *sa6_bsr;
+
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP) {
+ log(LOG_DEBUG, 0,
+ "Local BSR address: %s",
+ inet6_fmt(&my_bsr_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local BSR priority : %u",my_bsr_priority);
+ log(LOG_DEBUG,0,
+ "Local BSR period is : %u sec.",
+ my_bsr_period);
+ }
+
+ return(0);
+}
+
+int
+grp_prefix_config()
+{
+ struct attr_list *pl;
+
+ if (cand_rp_flag != TRUE) {
+ log(LOG_WARNING, 0,
+ "group_prefix was specified without cand_rp(ignored)");
+ return(0);
+ }
+
+ for (pl = grp_prefix; pl; pl = pl->next) {
+ if (!IN6_IS_ADDR_MULTICAST(&pl->attru.prefix.paddr)) {
+ log(LOG_WARNING, 0,
+ "Config error: %s is not a mulicast address(ignored)",
+ inet6_fmt(&pl->attru.prefix.paddr));
+ continue;
+ }
+
+ if (!(~(*cand_rp_adv_message.prefix_cnt_ptr))) {
+ log(LOG_WARNING, 0,
+ "Too many group_prefix configured. Truncating...");
+ break;
+ }
+
+ /* validation for plen has almost done */
+ if (pl->attru.prefix.plen < PIM_GROUP_PREFIX_DEFAULT_MASKLEN)
+ pl->attru.prefix.plen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+
+ PUT_EGADDR6(pl->attru.prefix.paddr,
+ (u_int8)pl->attru.prefix.plen, 0,
+ cand_rp_adv_message.insert_data_ptr);
+ (*cand_rp_adv_message.prefix_cnt_ptr)++;
+ }
+
+ /* finally, adjust the data size */
+ cand_rp_adv_message.message_size =
+ cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+
+ return(0);
+}
+
+int
+regthres_config()
+{
+ struct attr_list *al;
+ int rate = -1;
+ int interval = -1;
+
+ if (cand_rp_flag != TRUE) {
+ log(LOG_WARNING, 0,
+ "register_threshold was specified without cand_rp");
+ }
+
+ for (al = regthres_attr; al; al = al->next) {
+ switch(al->type) {
+ case THRESA_RATE:
+ if (al->attru.number < 0)
+ yywarn("invalid regthres rate: %d(ignored)",
+ al->attru.number);
+ else if (rate != -1)
+ yywarn("regthres rate is doubly defined(ignored)");
+ else
+ rate = al->attru.number;
+ break;
+ case THRESA_INTERVAL:
+ if (al->attru.number < 0)
+ yywarn("invalid regthres interval: %d(ignored)",
+ al->attru.number);
+ else if (interval != -1)
+ yywarn("regthres interval is doubly defined(ignored)");
+ else
+ interval = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for regthres", al->type);
+ break;
+ }
+ }
+
+ /* set default values if not specified */
+ if (rate == -1)
+ rate = PIM_DEFAULT_REG_RATE;
+ if (interval == -1)
+ interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+
+ pim_reg_rate_bytes = (rate * interval ) /10;
+ pim_reg_rate_check_interval = interval;
+
+ return(0);
+}
+
+int
+datathres_config()
+{
+ struct attr_list *al;
+ int rate = -1;
+ int interval = -1;
+
+ for (al = datathres_attr; al; al = al->next) {
+ switch(al->type) {
+ case THRESA_RATE:
+ if (al->attru.number < 0)
+ yywarn("invalid datathres rate: %d(ignored)",
+ al->attru.number);
+ else if (rate != -1)
+ yywarn("datathres rate is doubly defined(ignored)");
+ else
+ rate = al->attru.number;
+ break;
+ case THRESA_INTERVAL:
+ if (al->attru.number < 0)
+ yywarn("invalid datathres interval: %d(ignored)",
+ al->attru.number);
+ else if (interval != -1)
+ yywarn("datathres interval is doubly defined(ignored)");
+ else
+ interval = al->attru.number;
+ break;
+ default:
+ yywarn("unknown attribute(%d) for datathres", al->type);
+ break;
+ }
+ }
+
+ /* set default values if not specified */
+ if (rate == -1)
+ rate = PIM_DEFAULT_DATA_RATE;
+ if (interval == -1)
+ interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+
+ pim_data_rate_bytes = (rate * interval ) /10;
+ pim_data_rate_check_interval = interval;
+
+ return(0);
+}
+
+int
+cf_post_config()
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ if (debugonly)
+ goto cleanup;
+
+ param_config(); /* must be called before phyint_conifg() */
+
+ phyint_config();
+
+ if (cand_bsr_flag == TRUE)
+ bsr_config();
+
+ if (cand_rp_flag == TRUE)
+ rp_config();
+
+ if (grp_prefix) /* this must be called after rp_config() */
+ grp_prefix_config();
+
+ if (cand_rp_flag == TRUE)
+ regthres_config();
+
+ datathres_config();
+
+ IF_DEBUG(DEBUG_SWITCH) {
+ log(LOG_DEBUG, 0, "reg_rate_limit set to %u (bits/s)",
+ pim_reg_rate_bytes);
+ log(LOG_DEBUG, 0, "reg_rate_interval set to %u s.",
+ pim_reg_rate_check_interval);
+ log(LOG_DEBUG, 0, "data_rate_limit set to %u (bits/s)",
+ pim_data_rate_bytes);
+ log(LOG_DEBUG, 0, "data_rate_interval set to %u s.",
+ pim_data_rate_check_interval);
+ }
+
+ cleanup:
+ /* cleanup temporary variables */
+ if (cand_rp_ifname) free(cand_rp_ifname);
+ if (cand_bsr_ifname) free(cand_bsr_ifname);
+ if (rp_attr) free_attr_list(rp_attr);
+ if (bsr_attr) free_attr_list(bsr_attr);
+ if (grp_prefix) free_attr_list(grp_prefix);
+ if (regthres_attr) free_attr_list(regthres_attr);
+ if (datathres_attr) free_attr_list(datathres_attr);
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v)
+ free_attr_list((struct attr_list *)v->config_attr);
+
+ return(0);
+}
+
+/* initialize all the temporary variables */
+void
+cf_init(s, d)
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ strict = s;
+ debugonly = d;
+
+ debug = 0;
+
+ rp_attr = bsr_attr = grp_prefix = regthres_attr = datathres_attr = NULL;
+
+ cand_rp_ifname = cand_bsr_ifname = NULL;
+
+ srcmetric = srcpref = helloperiod = jpperiod = jpperiod_coef
+ = granularity = datatimo = regsuptimo = probetime
+ = asserttimo = -1;
+ helloperiod_coef = jpperiod_coef = -1;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v)
+ v->config_attr = NULL;
+}
diff --git a/usr.sbin/pim6sd/cftoken.l b/usr.sbin/pim6sd/cftoken.l
new file mode 100644
index 0000000..09725d3
--- /dev/null
+++ b/usr.sbin/pim6sd/cftoken.l
@@ -0,0 +1,657 @@
+%{
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "var.h"
+#include "vmbuf.h"
+#include "debug.h"
+#include "cfparse.h"
+#include "y.tab.h"
+
+static int yyerrorcount = 0;
+int lineno = 1;
+int yy_first_time = 1;
+
+extern char configfilename[];
+
+static void cfdebug_print __P((char *, char *, int));
+%}
+
+/* common seciton */
+nl \n
+ws [ \t]+
+comment \#.*
+semi \;
+string [a-zA-Z0-9:\._][a-zA-Z0-9:\._]*
+digit [0-9]
+integer {digit}+
+number {integer}|({digit}*\.{integer})
+hexdigit [0-9A-Fa-f]
+hexpair {hexdigit}{hexdigit}
+hexstring 0[xX]{hexpair}+
+ifname [a-zA-Z]+[0-9]+
+slash \/
+
+%s S_CNF
+%s S_LOG
+%s S_PHYINT S_IFCONF S_CANDRP S_CANDBSR S_PREFIX
+%s S_THRES
+
+%%
+%{
+ if (yy_first_time) {
+ BEGIN S_CNF;
+ yy_first_time = 0;
+ }
+%}
+
+ /* logging */
+<S_CNF>log { DP("begin logging"); BEGIN S_LOG; return(LOGGING); }
+<S_LOG>(no)?mld_proto {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_PROTO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld_timer {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld_member {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD_MEMBER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mld {
+ YYD_ECHO;
+ yylval.num = DEBUG_MLD;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?switch {
+ YYD_ECHO;
+ yylval.num = DEBUG_SWITCH;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?m?trace {
+ YYD_ECHO;
+ yylval.num = DEBUG_TRACE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?traceroute {
+ YYD_ECHO;
+ yylval.num = DEBUG_TRACE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?timeout {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMEOUT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?callout {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMEOUT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pkt {
+ YYD_ECHO;
+ yylval.num = DEBUG_PKT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?packets {
+ YYD_ECHO;
+ yylval.num = DEBUG_PKT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?interfaces {
+ YYD_ECHO;
+ yylval.num = DEBUG_IF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?vif {
+ YYD_ECHO;
+ yylval.num = DEBUG_IF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?kernel {
+ YYD_ECHO;
+ yylval.num = DEBUG_KERN;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?cache {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mfc {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?k_cache {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?k_mfc {
+ YYD_ECHO;
+ yylval.num = DEBUG_MFC;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rsrr {
+ YYD_ECHO;
+ yylval.num = DEBUG_RSRR;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_detail {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_DETAIL;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_hello {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_HELLO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_neighbors {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_HELLO;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_register {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_REGISTER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?registers {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_REGISTER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_join_prune {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_j_p {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_jp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_JOIN_PRUNE;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_bootstrap {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_bsr {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?bsr {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?bootstrap {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_BOOTSTRAP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_asserts {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_ASSERT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_cand_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_c_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rp {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_CAND_RP;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_routes {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_routing {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_mrt {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_timers {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim_rpf {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM_RPF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?rpf {
+ YYD_ECHO;
+ yylval.num = DEBUG_RPF;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?pim {
+ YYD_ECHO;
+ yylval.num = DEBUG_PIM;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routes {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routing {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mrt {
+ YYD_ECHO;
+ yylval.num = DEBUG_MRT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?routers {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?mrouters {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?neighbors {
+ YYD_ECHO;
+ yylval.num = DEBUG_NEIGHBORS;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?timers {
+ YYD_ECHO;
+ yylval.num = DEBUG_TIMER;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>(no)?asserts {
+ YYD_ECHO;
+ yylval.num = DEBUG_ASSERT;
+ if (strncmp("no", yytext, 2))
+ return(LOGLEV);
+ else
+ return(NOLOGLEV);
+}
+<S_LOG>all { YYD_ECHO; yylval.num = DEBUG_ALL; return(LOGLEV); }
+<S_LOG>3 { YYD_ECHO; yylval.num = DEBUG_ALL; return(LOGLEV); }
+<S_LOG>{semi} { DP("end logging"); BEGIN S_CNF; return(EOS); }
+<S_LOG>{string} { yywarn("unknown log type: %s (ignored)", yytext); }
+
+ /* yes-or-no */
+[yY][eE][sS] { YYD_ECHO; return(YES); }
+[nN][oO] { YYD_ECHO; return(NO); }
+
+ /* reverselookup */
+<S_CNF>reverselookup { YYD_ECHO; return(REVERSELOOKUP); }
+
+ /* phyint */
+<S_CNF>phyint { DP("begin phyint"); BEGIN S_PHYINT; return(PHYINT); }
+<S_PHYINT>{string} {
+ YYD_ECHO;
+ BEGIN S_IFCONF;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_PHYINT>{semi} { yyerror("phyint was specified without interface."); return(EOS); }
+<S_IFCONF>disable { YYD_ECHO; return(DISABLE); }
+<S_IFCONF>preference { YYD_ECHO; return(PREFERENCE); }
+<S_IFCONF>metric { YYD_ECHO; return(METRIC); }
+<S_IFCONF>nolistener { YYD_ECHO; return(NOLISTENER); }
+<S_IFCONF>{semi} { DP("end phyint"); BEGIN S_CNF; return(EOS); }
+
+ /* cand_rp */
+<S_CNF>cand_rp { DP("begin cand_rp"); BEGIN S_CANDRP; return(CANDRP); }
+<S_CANDRP>priority { YYD_ECHO; return(PRIORITY); }
+<S_CANDRP>time { YYD_ECHO; return(TIME); }
+<S_CANDRP>{ifname} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_CANDRP>{semi} { DP("end cand_rp"); BEGIN S_CNF; return(EOS); }
+
+ /* cand_bootstrap_router */
+<S_CNF>cand_bootstrap_router { DP("begin cand_bsr"); BEGIN S_CANDBSR; return(CANDBSR); }
+<S_CANDBSR>priority { YYD_ECHO; return(PRIORITY); }
+<S_CANDBSR>time { YYD_ECHO; return(TIME); }
+<S_CANDBSR>{ifname} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(IFNAME);
+}
+<S_CANDBSR>{semi} { DP("end cand_bsr"); BEGIN S_CNF; return(EOS); }
+
+ /* group_prefix */
+<S_CNF>group_prefix { YYD_ECHO; return(GRPPFX); }
+
+ /* switch_register_threshold */
+<S_CNF>switch_register_threshold {
+ YYD_ECHO; BEGIN S_THRES; return(REGTHRES);
+}
+ /* switch_data_threshold */
+<S_CNF>switch_data_threshold {
+ YYD_ECHO; BEGIN S_THRES; return(DATATHRES);
+}
+<S_THRES>rate { YYD_ECHO; return(RATE); }
+<S_THRES>interval { YYD_ECHO; return(INTERVAL); }
+<S_THRES>{semi} { DP("end thres"); BEGIN S_CNF; return(EOS); }
+
+ /* various parameters */
+<S_CNF>default_source_metric { YYD_ECHO; return(SRCMETRIC); }
+<S_CNF>default_source_preference { YYD_ECHO; return(SRCPREF); }
+<S_CNF>hello_period { YYD_ECHO; return(HELLOPERIOD); }
+<S_CNF>granularity { YYD_ECHO; return(GRANULARITY); }
+<S_CNF>join_prune_period { YYD_ECHO; return(JPPERIOD); }
+<S_CNF>data_timeout { YYD_ECHO; return(DATATIME); }
+<S_CNF>register_suppression_timeout { YYD_ECHO; return(REGSUPTIME); }
+<S_CNF>probe_time { YYD_ECHO; return(PROBETIME); }
+<S_CNF>assert_timeout { YYD_ECHO; return(ASSERTTIME); }
+
+ /* misc */
+{ws} { ; }
+{nl} { lineno++; }
+{comment} { DP("comment"); }
+{number} { YYD_ECHO; yylval.fl = atof(yytext); return(NUMBER); }
+{slash} { YYD_ECHO; return(SLASH); }
+{semi} { DP("end cnf"); return(EOS); }
+
+ /* last resort */
+{string} {
+ YYD_ECHO;
+ yylval.val.l = strlen(yytext);
+ yylval.val.v = strdup(yytext);
+ return(STRING);
+ }
+%%
+
+static void
+cfdebug_print(w, t, l)
+ char *w, *t;
+ int l;
+{
+ printf("<%d>%s [%s] (%d)\n", yy_start, w, t, l);
+}
+
+static void
+yyerror0(char *s, va_list ap)
+{
+ fprintf(stderr, "%s %d: ", configfilename, lineno);
+ vfprintf(stderr, s, ap);
+ fprintf(stderr, "\n");
+}
+
+void
+yyerror(char *s, ...)
+{
+ va_list ap;
+#ifdef HAVE_STDARG_H
+ va_start(ap, s);
+#else
+ va_start(ap);
+#endif
+ yyerror0(s, ap);
+ va_end(ap);
+ yyerrorcount++;
+}
+
+void
+yywarn(char *s, ...)
+{
+ va_list ap;
+#ifdef HAVE_STDARG_H
+ va_start(ap, s);
+#else
+ va_start(ap);
+#endif
+ yyerror0(s, ap);
+ va_end(ap);
+}
+
+int
+cfparse(strict, debugonly)
+ int strict, debugonly;
+{
+ if ((yyin = fopen(configfilename, "r")) == NULL) {
+ fprintf(stderr, "cfparse: fopen(%s)", configfilename);
+ return(-1);
+ }
+
+ cf_init(strict, debugonly);
+
+ if ((yyparse() || yyerrorcount) && strict) {
+ if (yyerrorcount) {
+ yyerror("fatal parse failure: exiting (%d errors)",
+ yyerrorcount);
+ } else
+ yyerror("fatal parse failure: exiting");
+ return(-1);
+ }
+
+ YIPSDP(PLOG("parse successed.\n"));
+
+ return cf_post_config();
+}
diff --git a/usr.sbin/pim6sd/config.c b/usr.sbin/pim6sd/config.c
new file mode 100644
index 0000000..04689ca
--- /dev/null
+++ b/usr.sbin/pim6sd/config.c
@@ -0,0 +1,1609 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "vif.h"
+#include "pim6.h"
+#include "inet6.h"
+#include "rp.h"
+#include "pimd.h"
+#include "timer.h"
+#include "route.h"
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "config.h"
+#include <arpa/inet.h>
+#include <stdio.h>
+#include "debug.h"
+
+void add_phaddr(struct uvif *v , struct sockaddr_in6 *addr,struct in6_addr *mask);
+char *next_word(char **s);
+int wordToOption(char *word);
+int parse_phyint(char *s);
+int parse_candidateRP(char *s);
+int parse_group_prefix(char *s);
+int parseBSR(char *s);
+int parse_reg_threshold(char *s);
+int parse_data_threshold(char *s);
+int parse_default_source_metric(char *s);
+int parse_default_source_preference(char *s);
+int parse_hello_period(char *s);
+int parse_granularity(char *s);
+int parse_jp_period(char *s);
+int parse_data_timeout(char *s);
+int parse_register_suppression_timeout(char *s);
+int parse_probe_time(char *s);
+int parse_assert_timeout(char *s);
+
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp,*ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n,i;
+ struct sockaddr_in6 addr;
+ struct in6_addr mask;
+ short flags;
+ int num_ifreq = 64;
+ struct ifconf ifc;
+
+ total_interfaces= 0; /* The total number of physical interfaces */
+
+ ifc.ifc_len = num_ifreq * sizeof (struct ifreq);
+ ifc.ifc_buf = calloc(ifc.ifc_len,sizeof(char));
+ while (ifc.ifc_buf) {
+ caddr_t newbuf;
+
+ if (ioctl(udp_socket,SIOCGIFCONF,(char *)&ifc) <0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if( (num_ifreq * sizeof (struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
+
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ newbuf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ if (newbuf == NULL)
+ free(ifc.ifc_buf);
+ ifc.ifc_buf = newbuf;
+ }
+ if (ifc.ifc_buf == NULL)
+ log(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory");
+
+
+ ifrp = (struct ifreq *) ifc.ifc_buf;
+ ifend = (struct ifreq * ) (ifc.ifc_buf + ifc.ifc_len);
+
+ /*
+ * Loop through all of the interfaces.
+ */
+ for(;ifrp < ifend;ifrp = (struct ifreq *)((char *)ifrp+n))
+ {
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+
+#ifdef HAVE_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if(n < sizeof(*ifrp))
+ n=sizeof(*ifrp);
+#else
+ n=sizeof(*ifrp);
+#endif
+
+ /*
+ * Ignore any interface for an address family other than IPv6.
+ */
+ if ( ifrp->ifr_addr.sa_family != AF_INET6)
+ {
+ /* Eventually may have IP address later */
+ total_interfaces++;
+ continue;
+ }
+
+ memcpy(&addr,&ifrp->ifr_addr,sizeof(struct sockaddr_in6));
+
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ memcpy(ifr.ifr_name,ifrp->ifr_name,sizeof(ifr.ifr_name));
+ memcpy(ifr6.ifr_name,ifrp->ifr_name,sizeof(ifr6.ifr_name));
+
+ if(ioctl(udp_socket,SIOCGIFFLAGS,(char *)&ifr) <0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+ flags = ifr.ifr_flags;
+
+#if 0
+ /*
+ * Ignore loopback interfaces and interfaces that do not
+ * support multicast.
+ */
+ if((flags & (IFF_LOOPBACK | IFF_MULTICAST ))!= IFF_MULTICAST)
+ continue;
+#endif
+
+ /*
+ * Get netmask of the address.
+ */
+ ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr;
+ if(ioctl(udp_socket, SIOCGIFNETMASK_IN6, (char *)&ifr6) <0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK_IN6 for %s",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr));
+ memcpy(&mask,&ifr6.ifr_addr.sin6_addr,sizeof(mask));
+
+ /*
+ * Get IPv6 specific flags, and ignore an anycast address.
+ * XXX: how about a deprecated, tentative, duplicated or
+ * detached address?
+ */
+ ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr;
+ if (ioctl(udp_socket, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ log(LOG_ERR, errno, "ioctl SIOCGIFAFLAG_IN6 for %s",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr));
+ }
+ else {
+ if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) {
+ log(LOG_DEBUG, 0, "config_vifs_from_kernel: "
+ "%s on %s is an anycast address, ignored",
+ inet6_fmt(&ifr6.ifr_addr.sin6_addr),
+ ifr.ifr_name);
+ continue;
+ }
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr))
+ {
+ addr.sin6_scope_id = if_nametoindex(ifrp->ifr_name);
+#ifdef __KAME__
+ /*
+ * Hack for KAME kernel.
+ * Set sin6_scope_id field of a link local address and clear
+ * the index embedded in the address.
+ */
+ /* clear interface index */
+ addr.sin6_addr.s6_addr[2] = 0;
+ addr.sin6_addr.s6_addr[3] = 0;
+#endif
+ }
+
+ /*
+ * If the address is connected to the same subnet as one
+ * already installed in the uvifs array, just add the address
+ * to the list of addresses of the uvif.
+ */
+ for(vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ {
+ if( strcmp(v->uv_name , ifr.ifr_name) == 0 )
+ {
+ add_phaddr(v, &addr,&mask);
+ break;
+ }
+ }
+
+ if( vifi != numvifs )
+ continue;
+
+ /*
+ * If there is room in the uvifs array, install this interface.
+ */
+ if( numvifs == MAXMIFS )
+ {
+ log(LOG_WARNING, 0,
+ "too many vifs, ignoring %s", ifr.ifr_name);
+ continue;
+ }
+
+ /*
+ * Everyone below is a potential vif interface.
+ * We don't care if it has wrong configuration or not
+ * configured at all.
+ */
+ total_interfaces++;
+
+ v = &uvifs[numvifs];
+ v->uv_dst_addr = allpim6routers_group;
+ v->uv_subnetmask = mask;
+ strncpy ( v->uv_name , ifr.ifr_name,IFNAMSIZ);
+ v->uv_ifindex = if_nametoindex(v->uv_name);
+ add_phaddr(v,&addr,&mask);
+
+ /* prefix local calc. (and what about add_phaddr?...) */
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ v->uv_prefix.sin6_addr.s6_addr[i] =
+ addr.sin6_addr.s6_addr[i] & mask.s6_addr[i];
+
+ if(flags & IFF_POINTOPOINT)
+ v->uv_flags |=(VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT);
+
+ /*
+ * Disable multicast routing on loopback interfaces and
+ * interfaces that do not support multicast. But they are
+ * still necessary, since global addresses maybe assigned only
+ * on such interfaces.
+ */
+ if ((flags & IFF_LOOPBACK) != 0 || (flags & IFF_MULTICAST) == 0)
+ v->uv_flags |= VIFF_DISABLED;
+
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,
+ "Installing %s (%s on subnet %s) ,"
+ "as vif #%u - rate = %d",
+ v->uv_name,inet6_fmt(&addr.sin6_addr),
+ net6name(&v->uv_prefix.sin6_addr,&mask),
+ numvifs,v->uv_rate_limit);
+
+ ++numvifs;
+
+
+ if( !(flags & IFF_UP))
+ {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+
+ }
+}
+
+void
+add_phaddr(struct uvif *v,struct sockaddr_in6 *addr,struct in6_addr *mask)
+{
+ struct phaddr *pa;
+ int i;
+
+ if( (pa=malloc(sizeof(*pa))) == NULL)
+ log(LOG_ERR, 0, "add_phaddr: memory exhausted");
+
+
+ memset(pa,0,sizeof(*pa));
+ pa->pa_addr= *addr;
+ pa->pa_subnetmask = *mask;
+
+ for(i = 0; i < sizeof(struct in6_addr); i++)
+ pa->pa_prefix.sin6_addr.s6_addr[i] =
+ addr->sin6_addr.s6_addr[i] & mask->s6_addr[i];
+ pa->pa_prefix.sin6_scope_id = addr->sin6_scope_id;
+
+
+ if(IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
+ if(v->uv_linklocal)
+ log(LOG_WARNING, 0,
+ "add_phaddr: found more than one link-local "
+ "address on %s",
+ v->uv_name);
+
+ v->uv_linklocal = pa;
+ }
+
+ pa->pa_next = v->uv_addrs;
+ v->uv_addrs = pa;
+}
+
+void
+config_vifs_from_file()
+{
+ FILE *f;
+ char linebuf[100];
+ char *w,*s;
+ struct ifconf ifc;
+ int option;
+ char ifbuf[BUFSIZ];
+ u_int8 *data_ptr;
+
+ if((f=fopen(configfilename,"r"))==NULL)
+ {
+ if( errno != ENOENT)
+ log(LOG_ERR,errno,"Can't open %s",configfilename);
+ log(LOG_WARNING,errno,"Can't open %s",configfilename);
+ return;
+ }
+ /*
+ * Note that sizeof(pim6_enocd_uni_addr_t) might be larger than
+ * the length of the Encoded-Unicast-address field(18 byte) due to
+ * some padding put in the compiler. However, it doesn't matter
+ * since we use the space just as a buffer(i.e not as the message).
+ */
+ cand_rp_adv_message.buffer = (u_int8 *)malloc( 4 + sizeof(pim6_encod_uni_addr_t) +
+ 255*sizeof(pim6_encod_grp_addr_t));
+ if(cand_rp_adv_message.buffer == NULL)
+ log(LOG_ERR,errno,"Candrpadv Buffer allocation");
+
+ cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer;
+
+ /* By default, if no group_prefix configured, then prefix_cnt == 0
+ * implies group_prefix = ff::/8 and masklen = 8.
+ */
+
+ *cand_rp_adv_message.prefix_cnt_ptr = 0;
+ cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer;
+
+ /* TODO: XXX: HARDCODING!!! */
+ cand_rp_adv_message.insert_data_ptr += (4 + 18);
+
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if(ioctl(udp_socket,SIOCGIFCONF,(char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ while( fgets(linebuf, sizeof(linebuf),f) != NULL )
+ {
+ s = linebuf;
+ w = next_word(&s);
+ option = wordToOption(w);
+ switch(option)
+ {
+ case EMPTY:
+ continue;
+ break;
+ case PHYINT:
+ parse_phyint(s);
+ break;
+ case CANDIDATE_RP:
+ parse_candidateRP(s);
+ break;
+ case GROUP_PREFIX:
+ parse_group_prefix(s);
+ break;
+ case BOOTSTRAP_RP:
+ parseBSR(s);
+ break;
+ case REG_THRESHOLD:
+ parse_reg_threshold(s);
+ break;
+ case DATA_THRESHOLD:
+ parse_data_threshold(s);
+ break;
+ case DEFAULT_SOURCE_METRIC:
+ parse_default_source_metric(s);
+ break;
+ case DEFAULT_SOURCE_PREFERENCE :
+ parse_default_source_preference(s);
+ break;
+ case HELLO_PERIOD :
+ parse_hello_period(s);
+ break;
+ case GRANULARITY :
+ parse_granularity(s);
+ break;
+ case JOIN_PRUNE_PERIOD :
+ parse_jp_period(s);
+ break;
+ case DATA_TIMEOUT :
+ parse_data_timeout(s);
+ break;
+ case REGISTER_SUPPRESSION_TIMEOUT :
+ parse_register_suppression_timeout(s);
+ break;
+ case PROBE_TIME :
+ parse_probe_time(s);
+ break;
+ case ASSERT_TIMEOUT:
+ parse_assert_timeout(s);
+ break;
+ default:
+ log(LOG_WARNING, 0, "unknown command '%s' in %s",
+ w, configfilename);
+
+ }
+ }
+ cand_rp_adv_message.message_size = cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer;
+ if (cand_rp_flag != FALSE)
+ {
+ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period;
+
+ /* TODO: HARDCODING! */
+ data_ptr = cand_rp_adv_message.buffer + 1; /* WARNING */
+ PUT_BYTE(my_cand_rp_priority,data_ptr);
+ PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr);
+ PUT_EUADDR6(my_cand_rp_address.sin6_addr,data_ptr);
+ IF_DEBUG(DEBUG_PIM_CAND_RP)
+ {
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP address is : %s",
+ inet6_fmt(&my_cand_rp_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP priority is : %u",my_cand_rp_priority);
+ log(LOG_DEBUG, 0,
+ "Local Cand-RP advertisement period is : %u sec.",
+ my_cand_rp_adv_period);
+ }
+ }
+
+
+ if( cand_bsr_flag!=FALSE)
+ {
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP)
+ {
+ log(LOG_DEBUG, 0,
+ "Local BSR address: %s",
+ inet6_fmt(&my_bsr_address.sin6_addr));
+ log(LOG_DEBUG, 0,
+ "Local BSR priority : %u",my_bsr_priority);
+ log(LOG_DEBUG,0,
+ "Local BSR period is : %u sec.",
+ my_bsr_period);
+
+ }
+
+ }
+
+ IF_DEBUG(DEBUG_SWITCH)
+ {
+ log(LOG_DEBUG,0,"reg_rate_limit set to %u (bits/s)" , pim_reg_rate_bytes);
+ log(LOG_DEBUG,0,"reg_rate_interval set to %u s.",pim_reg_rate_check_interval);
+ log(LOG_DEBUG,0,"data_rate_limit set to %u (bits/s)" , pim_data_rate_bytes);
+ log(LOG_DEBUG,0,"data_rate_interval set to %u s.",pim_data_rate_check_interval);
+ }
+
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ {
+ log(LOG_DEBUG,0, "pim_hello_period set to: %u", pim_hello_period);
+ log(LOG_DEBUG,0, "pim_hello_holdtime set to: %u", pim_hello_holdtime);
+ }
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG,0, "pim_join_prune_period set to: %u", pim_join_prune_period);
+ log(LOG_DEBUG,0, "pim_join_prune_holdtime set to: %u", pim_join_prune_holdtime);
+ }
+
+ fclose(f);
+
+}
+/*
+ * function name: wordToOption
+ * input: char *word, a pointer to the word
+ * output: int; a number corresponding to the code of the word
+ * operation: converts the result of the string comparisons into numerics.
+ * comments: called by config_vifs_from_file()
+ */
+
+int wordToOption(char *word)
+{
+ if (EQUAL(word, ""))
+ return EMPTY;
+ if (EQUAL(word, "phyint"))
+ return PHYINT;
+ if (EQUAL(word, "cand_rp"))
+ return CANDIDATE_RP;
+ if (EQUAL(word, "group_prefix"))
+ return GROUP_PREFIX;
+ if (EQUAL(word, "cand_bootstrap_router"))
+ return BOOTSTRAP_RP;
+ if (EQUAL(word, "switch_register_threshold"))
+ return REG_THRESHOLD;
+ if (EQUAL(word, "switch_data_threshold"))
+ return DATA_THRESHOLD;
+ if (EQUAL(word, "default_source_metric"))
+ return DEFAULT_SOURCE_METRIC;
+ if (EQUAL(word, "default_source_preference"))
+ return DEFAULT_SOURCE_PREFERENCE;
+ if (EQUAL(word, "hello_period"))
+ return HELLO_PERIOD;
+ if (EQUAL(word, "granularity"))
+ return GRANULARITY;
+ if (EQUAL(word, "join_prune_period"))
+ return JOIN_PRUNE_PERIOD;
+ if (EQUAL(word, "data_timeout"))
+ return DATA_TIMEOUT;
+ if (EQUAL(word, "register_suppression_timeout"))
+ return REGISTER_SUPPRESSION_TIMEOUT;
+ if (EQUAL(word, "probe_time"))
+ return PROBE_TIME;
+ if (EQUAL(word, "assert_timeout"))
+ return PROBE_TIME;
+ return UNKNOWN;
+}
+
+/*
+ * function name: parse_phyint
+ * input: char *s, pointing to the parsing point of the file
+ * output: int (TRUE if the parsing was successful, o.w. FALSE)
+ * operation: parses the physical interface file configurations, if any.
+ * The general form is:
+ * phyint <ifname> [disable] [preference <p>] [metric <m>]
+ */
+
+
+int parse_phyint(char *s)
+{
+ char *w,c,*ifname;
+ vifi_t vifi;
+ struct uvif *v;
+ u_int n;
+
+ if(EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0, "Missing phyint name in %s", configfilename);
+ return FALSE;
+ }
+ ifname = w;
+
+ for (vifi = 0,v=uvifs;vifi <= numvifs ; ++vifi , ++v)
+ {
+ if(vifi == numvifs)
+ {
+ log(LOG_WARNING, 0,
+ "Invalid phyint name (maybe not configured..) '%s' "
+ "in %s", w, configfilename);
+ return FALSE;
+ }
+
+ if(strcmp(v->uv_name,ifname))
+ continue;
+
+ while(!EQUAL((w = next_word(&s)),""))
+ {
+ if(EQUAL(w,"disable"))
+ v->uv_flags |=VIFF_DISABLED;
+ else if (EQUAL(w, "nolistener"))
+ v->uv_flags |= VIFF_NOLISTENER;
+ else
+ {
+ if(EQUAL(w,"preference"))
+ {
+ if(EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0,
+ "Missing preference for "
+ "phyint %s in %s",
+ ifname, configfilename);
+ }
+ else
+ {
+ if (sscanf(w,"%u%c",&n,&c) != 1 ||
+ n < 1 || n > 255 )
+ {
+ log(LOG_WARNING, 0,
+ "Invalid preference "
+ "'%s' for phyint %s "
+ "in %s",
+ w, ifname,
+ configfilename);
+ }
+ else
+ {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,"Config setting default local preference on %d to %s",n,ifname);
+ v->uv_local_pref = n;
+ }
+ }
+ }
+ else
+ {
+ if(EQUAL(w, "metric"))
+ {
+ if(EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing metric for "
+ "phyint %s in %s",
+ ifname,
+ configfilename);
+ }
+ else
+ {
+ if (sscanf(w, "%u%c", &n, &c) != 1 ||
+ n < 1 || n > 1024 )
+ {
+ log(LOG_WARNING,0,
+ "Invalid metric '%s' for phyint %s in %s",
+ w, ifname,configfilename);
+ }
+ else
+ {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG,0,
+ "Config setting default local metric on %d to %s.",
+ n,ifname);
+ v->uv_local_metric = n;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ return(TRUE);
+}
+
+/*
+ * function name: parse_candidateRP
+ * input: char *s
+ * output: int (TRUE if the parsing was successful, o.w. FALSE)
+ * operation: parses the candidate RP information.
+ * The general form is:
+ * 'cand_rp <ifname> [priority <number>] [time <number>]'.
+ */
+int
+parse_candidateRP(char *s)
+{
+ char *w;
+ struct sockaddr_in6 *sa6_rp;
+ u_int time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ u_int priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+
+ sa6_rp = NULL;
+ cand_rp_flag = FALSE;
+
+ my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+
+ while(!EQUAL((w = next_word(&s)),""))
+ {
+ if((!EQUAL(w,"priority")) && (!EQUAL(w,"time")))
+ {
+ /*
+ * if the interface is specified and is valid
+ * we take the max global address of the interface
+ * (aliasing) else look at the end of the function.
+ */
+ sa6_rp = local_iface(w);
+ if(!sa6_rp)
+ log(LOG_WARNING, 0,
+ "cand_rp '%s' in %s is not configured."
+ "take the max local address the router..",
+ w, configfilename);
+ }
+ else
+ {
+ if (EQUAL(w,"priority"))
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing priority ; set to default "
+ ": %d (0 is highest )",priority);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&priority)!= 1 )
+ {
+ priority = PIM_DEFAULT_CAND_RP_PRIORITY;
+ log(LOG_WARNING, 0,
+ "Invalid priority '%' "
+ "for cand_rp;set to default "
+ "(0 is highest) : %d",
+ w, priority);
+ }
+ }
+ }
+ else
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0,
+ "Missing cand_adv period ;"
+ "set to default : %d",time);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&time)!= 1 )
+ {
+ time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ log(LOG_WARNING, 0,
+ "Invalid cand_adv_period "
+ "'%s';set to default : %d",
+ w,time);
+
+ }
+ else
+ {
+ if( time > (my_cand_rp_adv_period = ~0))
+ time = my_cand_rp_adv_period;
+ else
+ if(time <10)
+ time = 10;
+ else
+ if (time > PIM_DEFAULT_CAND_RP_ADV_PERIOD)
+ time = PIM_DEFAULT_CAND_RP_ADV_PERIOD;
+ my_cand_rp_adv_period = time;
+ }
+ }
+ }
+ }
+ }
+
+ if(!sa6_rp)
+ sa6_rp= max_global_address();
+
+ my_cand_rp_address=*sa6_rp;
+ my_cand_rp_priority = priority;
+ my_cand_rp_adv_period = time;
+ cand_rp_flag = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * function name: parse_group_prefix
+ * input: char *s
+ * output: int
+ * operation: parse group_prefix configured information.
+ * General form: 'group_prefix <group-addr>/<prefix_len>'.
+ */
+int
+parse_group_prefix(char *s)
+{
+ char *w;
+ struct in6_addr group_addr;
+ u_int32 masklen=PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+
+ w=next_word(&s);
+ if (EQUAL(w,""))
+ {
+ log(LOG_WARNING, 0,
+ "Configuration error for 'group_prefix' in %s: no group_addr. "
+ "Ignoring...", configfilename);
+ return FALSE;
+ }
+
+ w=strtok(w,"/");
+
+ if ( inet_pton(AF_INET6,w,(void *)&group_addr) != 1 )
+ {
+ log(LOG_WARNING, 0,
+ "Config error in %s : Bad ddress formatt.Ignoring..",
+ configfilename);
+ return FALSE;
+ }
+ if (!IN6_IS_ADDR_MULTICAST(&group_addr))
+ {
+ log(LOG_WARNING,0,
+ "Config error in %s: '%s' is not a mcast addr.Ignoring",
+ configfilename,
+ inet6_fmt(&group_addr));
+ return FALSE;
+ }
+ if (!(~(*cand_rp_adv_message.prefix_cnt_ptr)))
+ {
+ log(LOG_WARNING, 0,
+ "Too many group_prefix configured. Truncating...");
+ return FALSE;
+ }
+
+ w=strtok(NULL,"/");
+ if(w==NULL)
+ {
+ log(LOG_WARNING,0,
+ "Config error in %s : missing group prefix.Ignoring..",
+ configfilename);
+ return FALSE;
+ }
+ if ( sscanf(w,"%u",&masklen) ==1 )
+ {
+ if (masklen > (sizeof(group_addr) * 8))
+ masklen = (sizeof(group_addr)*8);
+ else
+ if (masklen <PIM_GROUP_PREFIX_DEFAULT_MASKLEN)
+ masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN;
+ }
+
+
+ PUT_EGADDR6(group_addr, (u_int8)masklen, 0,
+ cand_rp_adv_message.insert_data_ptr);
+ (*cand_rp_adv_message.prefix_cnt_ptr)++;
+
+ return TRUE;
+
+}
+/*
+ * function name: parseBSR
+ * input: char *s
+ * output: int
+ * operation: parse the candidate BSR configured information.
+ * General form:
+ * 'cand_bootstrap_router <ifname> [priority <number>]'.
+ * this function is similar to parse_candrp
+ */
+
+int
+parseBSR(char *s)
+{
+ char *w;
+ struct sockaddr_in6 *sa6_bsr;
+ u_int32 priority = PIM_DEFAULT_BSR_PRIORITY;
+ u_int time = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ my_bsr_period = PIM_DEFAULT_BOOTSTRAP_PERIOD;
+
+ sa6_bsr = NULL;
+ cand_bsr_flag = FALSE;
+
+ while(!EQUAL((w = next_word(&s)),""))
+ {
+ if((!EQUAL(w,"priority")) && (!EQUAL(w,"time")))
+ {
+
+ sa6_bsr = local_iface(w);
+ if(!sa6_bsr)
+ {
+ log(LOG_WARNING,0,
+ "cand_bootstrap_router '%s' in %s is not "
+ "configured.Take the max router address.",
+ w,configfilename);
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"priority"))
+ {
+ if (EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING, 0,
+ "Missing priority for the bsr;set to "
+ "default (0 is lowest): %d",priority);
+ }
+ else
+ {
+ if (sscanf(w,"%u",&priority)!= 1 )
+ {
+ priority = PIM_DEFAULT_BSR_PRIORITY;
+ log(LOG_WARNING, 0,
+ "Invalid priority '%s'for "
+ "the bsr;set to default : %d",
+ w, priority);
+ }
+ else
+ {
+ if( priority > (my_bsr_priority = ~0))
+ priority = my_bsr_priority;
+ my_bsr_priority = (u_int8)priority;
+ }
+ }
+ }
+ else
+ {
+ if( EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing bsr period ;"
+ "set to default : %d ",time);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_DEFAULT_BOOTSTRAP_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid bsr period"
+ "'%s';set to default : %d",
+ w,time);
+ }
+ else
+ my_bsr_period=time;
+ }
+ }
+ }
+ }
+
+ if(!sa6_bsr)
+ sa6_bsr = max_global_address();
+
+ my_bsr_address=*sa6_bsr;
+ my_bsr_priority = priority;
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN,my_bsr_hash_mask);
+ cand_bsr_flag = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * function name: parse_reg_threshold
+ * input: char *s
+ * output: int (TRUE if successful, FALSE o.w.)
+ * operation: reads and assigns the switch to the spt threshold
+ * due to registers for the router, if used as RP.
+ * Maybe extended to support different thresholds
+ * for different groups(prefixes).
+ * General form:
+ * 'switch_register_threshold [rate <number> interval <number>]'.
+ * comments: called by config_vifs_from_file()
+ */
+
+
+int parse_reg_threshold(char *s)
+{
+ char *w;
+ u_int rate=PIM_DEFAULT_REG_RATE;
+ u_int interval= PIM_DEFAULT_REG_RATE_INTERVAL;
+
+ while(!EQUAL((w=next_word(&s)),""))
+ {
+ if(EQUAL(w,"rate"))
+ {
+ if(EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "switch_register_threshold : missing rate ; "
+ "set to default : %u (bits/s)",
+ rate);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&rate)!=1)
+ {
+ rate = PIM_DEFAULT_REG_RATE;
+ log(LOG_WARNING, 0,
+ "switch_register_threshold : "
+ "Invalid rate '%s' , set to defaut :"
+ " %u (bits/s)",
+ w,rate);
+ }
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"interval"))
+ {
+ if(EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_register_threshold : missing interval ; set to default : %u s.",
+ interval);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&interval) != 1)
+ {
+ interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+ log(LOG_WARNING,0,"switch_register_threshold : Invalid interval '%s' ; set to default : %u s.",
+ w,interval);
+ }
+ }
+ }
+ else
+ {
+ log(LOG_WARNING,0,"swhitch_register_threshold : Invalid parameter %s",w);
+ }
+ }
+ }
+
+ if( interval < timer_interval)
+ {
+ interval = timer_interval;
+ log(LOG_WARNING,0,"switch_register_threshold : Interval too short , set to default : %u s.",
+ interval);
+ }
+
+ pim_reg_rate_bytes = (rate * interval ) /10;
+ pim_reg_rate_check_interval = interval;
+
+ return TRUE;
+
+}
+/*
+ * function name: parse_data_threshold
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the switch to the spt threshold
+ * due to data packets, if used as DR.
+ * General form:
+ * 'switch_data_threshold [rate <number> interval <number>]'.
+ * similar to register_threshold...
+ */
+
+int parse_data_threshold(char *s)
+{
+ char *w;
+ u_int rate=PIM_DEFAULT_DATA_RATE;
+ u_int interval= PIM_DEFAULT_DATA_RATE_INTERVAL;
+
+ while(!EQUAL((w=next_word(&s)),""))
+ {
+ if(EQUAL(w,"rate"))
+ {
+ if(EQUAL((w=next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_data_threshold : missing rate value ; set to defaut : %u (bits/s)",
+ rate);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&rate)!=1)
+ {
+ rate = PIM_DEFAULT_DATA_RATE;
+ log(LOG_WARNING,0,"switch_data_threshold : Invalid rate '%s' ; set to default : %u (bits/s)",
+ w,rate);
+ }
+ }
+ }
+ else
+ {
+ if(EQUAL(w,"interval"))
+ {
+ if(EQUAL((w = next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,"switch_data_threshold : missing interval value ; set to default : %u s.",
+ interval);
+ }
+ else
+ {
+ if(sscanf(w,"%u",&interval) != 1)
+ {
+ interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+ log(LOG_WARNING,0,"switch_data_threshold : Invalid interval '%s' ; set to default : %u s.",
+ w,interval);
+ }
+ }
+ }
+ else
+ {
+ log(LOG_WARNING,0,"swhitch_data_threshold :Invalid Parameter %s",w);
+ }
+ }
+ }
+
+ if( interval < timer_interval)
+ {
+ interval = timer_interval;
+ log(LOG_WARNING,0,"switch_data_threshold : interval too short set to default : %u s.",
+ interval);
+ }
+
+ pim_data_rate_bytes = (rate * interval ) /10;
+ pim_data_rate_check_interval = interval;
+
+ return TRUE;
+
+}
+
+/*
+ * function name: parse_default_source_metric
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source metric, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_metric <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+ */
+
+
+int parse_default_source_metric(char *s)
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_METRIC;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing source metric value ; set to default %u",
+ value);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &value) != 1)
+ {
+ value = DEFAULT_LOCAL_METRIC;
+ log(LOG_WARNING,0,
+ "Invalid source metric value '%s' ;set to default %u",
+ w,value);
+ }
+ default_source_metric = value;
+ log(LOG_INFO,0, "Default_source_metric is : %u", default_source_metric);
+
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v)
+ {
+ v->uv_local_metric = default_source_metric;
+ }
+
+
+ }
+ return(TRUE);
+}
+
+/*
+ * function name: parse_default_source_preference
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source preference, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_preference <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+ */
+
+int parse_default_source_preference(char *s)
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_PREF;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing source preference ; set to default %u",
+ value);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &value) != 1)
+ {
+ value = DEFAULT_LOCAL_PREF;
+ log(LOG_WARNING,0,
+ "Invalid source preference value '%s' ;set to default %u",
+ w,value);
+ }
+ default_source_preference = value;
+ log(LOG_INFO,0, "default_source_preference set to: %u", default_source_preference);
+
+ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v)
+ {
+ v->uv_local_pref = default_source_preference;
+ }
+
+
+ }
+ return(TRUE);
+}
+
+/*
+ * function name: parse_hello_period
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the hello period for a pim router
+ * General form:
+ * 'hello_period <number> <coef>'.
+ * number is the period in second between 2 hello messages
+ * and coef is the coef to deterimine the hello holdtime
+ * default : 3.5
+ */
+
+int parse_hello_period(char *s)
+{
+ char *w;
+ u_int hellop;
+ float coef;
+
+ hellop = PIM_TIMER_HELLO_PERIOD;
+ coef = 3.5;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing hello period ; set to default %u",
+ hellop);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &hellop) != 1)
+ {
+ hellop = PIM_TIMER_HELLO_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid hello period value '%s' ;set to default %u",
+ w,hellop);
+ }
+ pim_hello_period = hellop;
+
+ if (!EQUAL((w=next_word(&s)),""))
+ {
+ if (sscanf(w, "%f", &coef) != 1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "Invalid hello period coef '%s' ;set to default %.1f",
+ w,coef);
+ }
+ if(coef<=1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "for hello period coef must be > 1;set to default %.1f",
+ coef);
+ }
+
+ }
+
+
+ }
+ pim_hello_holdtime = coef*pim_hello_period;
+ return(TRUE);
+}
+/*
+ * function name: parse_jp_period
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the join/prune period for a pim router
+ * General form:
+ * 'join_prune_period <number> <coef>'.
+ * number is the period in second between 2 join/prune messages
+ * and coef is the coef to deterimine the join/prune holdtime
+ * default : 3.5
+ * This function is similar to the function above
+ */
+
+int parse_jp_period(char *s)
+{
+ char *w;
+ u_int jpp;
+ float coef;
+
+ jpp = PIM_JOIN_PRUNE_PERIOD;
+ coef = 3.5;
+
+ if (EQUAL((w = next_word(&s)), ""))
+ {
+ log(LOG_WARNING,0,
+ "Missing join/prune period ; set to default %u",
+ jpp);
+ }
+ else
+ {
+ if (sscanf(w, "%u", &jpp) != 1)
+ {
+ jpp = PIM_JOIN_PRUNE_PERIOD;
+ log(LOG_WARNING,0,
+ "Invalid join/prune period value '%s' ;set to default %u",
+ w,jpp);
+ }
+
+ pim_join_prune_period = jpp;
+
+ if (!EQUAL((w=next_word(&s)),""))
+ {
+ if (sscanf(w, "%f", &coef) != 1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "Invalid join/prune period coef '%s' ;set to default %.1f",
+ w,coef);
+ }
+ if(coef<=1)
+ {
+ coef = 3.5;
+ log(LOG_WARNING,0,
+ "for join/prune period coef must be > 1;set to default %.1f",
+ coef);
+ }
+
+ }
+
+
+ }
+ pim_join_prune_holdtime = coef*pim_join_prune_period;
+ return(TRUE);
+}
+
+
+/* function name : parse_granularity
+ * input char *s
+ * output int
+ * operation : reads and assigns the granularity of the demon's timer
+ * General form :
+ * 'granularity <number>
+ * number is the period in seconds between each "tics" of the virtual time.
+ * default : 5 s.
+ */
+int parse_granularity(char *s)
+{
+ char *w;
+ u_int granu;
+
+ granu = DEFAULT_TIMER_INTERVAL;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing timer granularity ; set to default %u",
+ granu);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&granu)!=1)
+ {
+ granu=DEFAULT_TIMER_INTERVAL;
+ log(LOG_WARNING,0,
+ "Invalid timer granularity value '%s' ; set to default %u",
+ w,granu);
+ }
+ timer_interval = granu;
+ if(granu < 1)
+ {
+ granu = DEFAULT_TIMER_INTERVAL;
+ log(LOG_WARNING,0,
+ "Timer granularity MUST be > 1! ; set to default %u",
+ granu);
+ }
+ }
+
+ timer_interval = granu;
+ return TRUE;
+}
+
+/* function name : parse_data_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the data_timeout of each (S,G)
+ * General form :
+ * 'data_timeout <number>
+ * default : 210 s.
+ */
+int parse_data_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_DATA_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing data timeout ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_DATA_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid data timeout value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_data_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_DATA_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Data timeout must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_data_timeout = time;
+ return TRUE;
+}
+
+/* function name : parse_register_suppression_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the register_suppression_timeout
+ * General form :
+ * 'register_suppression_timeout <number>
+ * default : 60 s.
+ */
+int parse_register_suppression_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing register suppression timeout ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid register suppression timeout value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_register_suppression_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Register suppression timeout must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_register_suppression_timeout = time;
+ return TRUE;
+}
+
+/* function name : parse_probe_time
+ * input char *s
+ * output int
+ * operation : reads and assigns the probe_time for null-register
+ * General form :
+ * 'probe_time <number>
+ * default : 5 s.
+ */
+int parse_probe_time(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_REGISTER_PROBE_TIME;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing register probe time ; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_REGISTER_PROBE_TIME;
+ log(LOG_WARNING,0,
+ "Invalid register probe time value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_register_probe_time = time;
+ if(time < 1)
+ {
+ time = PIM_REGISTER_SUPPRESSION_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Register probe time must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_register_probe_time = time;
+ return TRUE;
+}
+
+/* function name : parse_assert_timeout
+ * input char *s
+ * output int
+ * operation : reads and assigns the assert timeout
+ * General form :
+ * 'assert_timeout <number>
+ * default : 180 s.
+ */
+int parse_assert_timeout(char *s)
+{
+ char *w;
+ u_int time;
+
+ time = PIM_ASSERT_TIMEOUT;
+
+ if( EQUAL((w= next_word(&s)),""))
+ {
+ log(LOG_WARNING,0,
+ "Missing assert time out; set to default %u",
+ time);
+ return FALSE;
+ }
+ else
+ {
+ if( sscanf(w,"%u",&time)!=1)
+ {
+ time=PIM_ASSERT_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Invalid assert time out value '%s' ; set to default %u",
+ w,time);
+ }
+ pim_assert_timeout = time;
+ if(time < 1)
+ {
+ time = PIM_ASSERT_TIMEOUT;
+ log(LOG_WARNING,0,
+ "Assert time out must be > 1! ; set to default %u",
+ time);
+ }
+ }
+
+ pim_assert_timeout = time;
+ return TRUE;
+}
+
+
+
+
+char *next_word(char **s)
+{
+ char *w;
+
+ w = *s;
+ while (*w == ' ' || *w == '\t')
+ w++;
+
+ *s = w;
+ for(;;) {
+ switch (**s) {
+ case ' ' :
+ case '\t' :
+ **s = '\0';
+ (*s)++;
+ return(w);
+ case '\n' :
+ case '#' :
+ **s = '\0';
+ return(w);
+ case '\0' :
+ return(w);
+ default :
+ if (isascii(**s) && isupper(**s))
+ **s = tolower(**s);
+ (*s)++;
+ }
+ }
+}
diff --git a/usr.sbin/pim6sd/config.h b/usr.sbin/pim6sd/config.h
new file mode 100644
index 0000000..deffe3f3
--- /dev/null
+++ b/usr.sbin/pim6sd/config.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define UNKNOWN -1
+#define EMPTY 1
+#define PHYINT 2
+#define CANDIDATE_RP 3
+#define GROUP_PREFIX 4
+#define BOOTSTRAP_RP 5
+#define REG_THRESHOLD 6
+#define DATA_THRESHOLD 7
+#define DEFAULT_SOURCE_METRIC 8
+#define DEFAULT_SOURCE_PREFERENCE 9
+#define HELLO_PERIOD 10
+#define GRANULARITY 11
+#define JOIN_PRUNE_PERIOD 12
+#define DATA_TIMEOUT 13
+#define REGISTER_SUPPRESSION_TIMEOUT 14
+#define PROBE_TIME 15
+#define ASSERT_TIMEOUT 16
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+void config_vifs_from_kernel();
+void config_vifs_from_file();
+
+#endif
diff --git a/usr.sbin/pim6sd/crc.c b/usr.sbin/pim6sd/crc.c
new file mode 100644
index 0000000..917371e
--- /dev/null
+++ b/usr.sbin/pim6sd/crc.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ * $FreeBSD$
+ */
+/* CRC implantation : stolen from RFC 2083 section 15.*/
+
+/* Table of CRCs of all 8-bit messages. */
+ unsigned long crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+ int crc_table_computed = 0;
+
+
+/* Make the table for a fast CRC. */
+
+void make_crc_table(void)
+{
+ unsigned long c;
+ int n, k;
+ for (n = 0; n < 256; n++)
+ {
+ c = (unsigned long) n;
+ for (k = 0; k < 8; k++)
+ {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+ crc_table_computed = 1;
+}
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+unsigned long update_crc(unsigned long crc, unsigned char *buf,
+ int len)
+{
+ unsigned long c = crc;
+ int n;
+
+ if (!crc_table_computed)
+ make_crc_table();
+ for (n = 0; n < len; n++)
+ {
+ c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
+ }
+ return c;
+}
+
+
+/* Return the CRC of the bytes buf[0..len-1]. */
+
+unsigned long crc(unsigned char *buf, int len)
+{
+ return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
+}
diff --git a/usr.sbin/pim6sd/crc.h b/usr.sbin/pim6sd/crc.h
new file mode 100644
index 0000000..e55dbc8
--- /dev/null
+++ b/usr.sbin/pim6sd/crc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#ifndef CRC_H
+#define CRC_H
+
+extern long crc __P((unsigned char *buf, int len));
+
+#endif
diff --git a/usr.sbin/pim6sd/debug.c b/usr.sbin/pim6sd/debug.c
new file mode 100644
index 0000000..9349cce
--- /dev/null
+++ b/usr.sbin/pim6sd/debug.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/icmp6.h>
+#include <netinet6/pim6.h>
+#include "pathnames.h"
+#include "defs.h"
+#include "pimd.h"
+#include "debug.h"
+#include "mrt.h"
+#include "vif.h"
+#include "rp.h"
+#include "inet6.h"
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+extern char *progname;
+
+int log_nmsgs = 0;
+unsigned long debug = 0x00000000; /* If (long) is smaller than 4 bytes,
+ * then we are in trouble. */
+static char dumpfilename[] = _PATH_PIM6D_DUMP;
+static char cachefilename[] = _PATH_PIM6D_CACHE; /* TODO: notused */
+static char statfilename[] = _PATH_PIM6D_STAT;
+
+char *
+packet_kind(proto, type, code)
+ u_int proto,
+ type,
+ code;
+{
+ static char unknown[60];
+
+ switch (proto)
+ {
+ case IPPROTO_ICMPV6:
+ switch (type)
+ {
+ case MLD6_LISTENER_QUERY:
+ return "Multicast Listener Query ";
+ case MLD6_LISTENER_REPORT:
+ return "Multicast Listener Report ";
+ case MLD6_LISTENER_DONE:
+ return "Multicast Listener Done ";
+ default:
+ sprintf(unknown,
+ "UNKNOWN ICMPv6 message: type = 0x%02x, code = 0x%02x ",
+ type, code);
+ return unknown;
+ }
+
+ case IPPROTO_PIM: /* PIM v2 */
+ switch (type)
+ {
+ case PIM_V2_HELLO:
+ return "PIM v2 Hello ";
+ case PIM_V2_REGISTER:
+ return "PIM v2 Register ";
+ case PIM_V2_REGISTER_STOP:
+ return "PIM v2 Register_Stop ";
+ case PIM_V2_JOIN_PRUNE:
+ return "PIM v2 Join/Prune ";
+ case PIM_V2_BOOTSTRAP:
+ return "PIM v2 Bootstrap ";
+ case PIM_V2_ASSERT:
+ return "PIM v2 Assert ";
+ case PIM_V2_GRAFT:
+ return "PIM-DM v2 Graft ";
+ case PIM_V2_GRAFT_ACK:
+ return "PIM-DM v2 Graft_Ack ";
+ case PIM_V2_CAND_RP_ADV:
+ return "PIM v2 Cand. RP Adv. ";
+ default:
+ sprintf(unknown, "UNKNOWN PIM v2 message type =%3d ", type);
+ return unknown;
+ }
+ default:
+ sprintf(unknown, "UNKNOWN proto =%3d ", proto);
+ return unknown;
+ }
+}
+
+
+/*
+ * Used for debugging particular type of messages.
+ */
+int
+debug_kind(proto, type, code)
+ u_int proto,
+ type,
+ code;
+{
+ switch (proto)
+ {
+ case IPPROTO_ICMPV6:
+ switch (type)
+ {
+ case MLD6_LISTENER_QUERY:
+ return DEBUG_MLD;
+ case MLD6_LISTENER_REPORT:
+ return DEBUG_MLD;
+ case MLD6_LISTENER_DONE:
+ return DEBUG_MLD;
+ default:
+ return DEBUG_MLD;
+ }
+ case IPPROTO_PIM: /* PIM v2 */
+ /* TODO: modify? */
+ switch (type)
+ {
+ case PIM_V2_HELLO:
+ return DEBUG_PIM;
+ case PIM_V2_REGISTER:
+ return DEBUG_PIM_REGISTER;
+ case PIM_V2_REGISTER_STOP:
+ return DEBUG_PIM_REGISTER;
+ case PIM_V2_JOIN_PRUNE:
+ return DEBUG_PIM;
+ case PIM_V2_BOOTSTRAP:
+ return DEBUG_PIM_BOOTSTRAP;
+ case PIM_V2_ASSERT:
+ return DEBUG_PIM;
+ case PIM_V2_GRAFT:
+ return DEBUG_PIM;
+ case PIM_V2_GRAFT_ACK:
+ return DEBUG_PIM;
+ case PIM_V2_CAND_RP_ADV:
+ return DEBUG_PIM_CAND_RP;
+ default:
+ return DEBUG_PIM;
+ }
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+
+/*
+ * Some messages are more important than others. This routine determines the
+ * logging level at which to log a send error (often "No route to host").
+ * This is important when there is asymmetric reachability and someone is
+ * trying to, i.e., mrinfo me periodically.
+ */
+int
+log_level(proto, type, code)
+ u_int proto,
+ type,
+ code;
+{
+ switch (proto)
+ {
+ case IPPROTO_ICMPV6:
+ switch (type)
+ {
+ default:
+ return LOG_WARNING;
+ }
+
+ case IPPROTO_PIM:
+ /* PIM v2 */
+ switch (type)
+ {
+ default:
+ return LOG_INFO;
+ }
+ default:
+ return LOG_WARNING;
+ }
+
+ return LOG_WARNING;
+}
+
+
+/*
+ * Dump internal data structures to stderr.
+ */
+/*
+ * TODO: currently not used void dump(int i) { dump_vifs(stderr);
+ * dump_pim_mrt(stderr); }
+ */
+
+/*
+ * Dump internal data structures to a file.
+ */
+void
+fdump(i)
+ int i;
+{
+ FILE *fp;
+ fp = fopen(dumpfilename, "w");
+ if (fp != NULL)
+ {
+ dump_vifs(fp);
+ dump_nbrs(fp);
+ dump_pim_mrt(fp);
+ dump_rp_set(fp);
+ (void) fclose(fp);
+ }
+}
+
+/* TODO: dummy, to be used in the future. */
+/*
+ * Dump local cache contents to a file.
+ */
+void
+cdump(i)
+ int i;
+{
+ FILE *fp;
+
+ fp = fopen(cachefilename, "w");
+ if (fp != NULL)
+ {
+ /*
+ * TODO: implement it: dump_cache(fp);
+ */
+ (void) fclose(fp);
+ }
+}
+
+void
+dump_stat()
+{
+ FILE *fp;
+ vifi_t vifi;
+ register struct uvif *v;
+
+ fp = fopen(statfilename, "w");
+ if (fp == NULL) {
+ log(LOG_WARNING, errno, "dump_stat: can't open file(%s)",
+ statfilename);
+ return;
+ }
+
+ fprintf(fp, "pim6sd per-interface statistics\n");
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+#if 0 /* is it better to skip them? */
+ if ((v->uv_flags & (VIFF_DISABLED|VIFF_DOWN)) != 0)
+ continue;
+#endif
+ fprintf(fp, " Mif=%d, PhyIF=%s\n", vifi, v->uv_name);
+ fprintf(fp, "\t%qu pim6 hello received\n", v->uv_in_pim6_hello);
+ fprintf(fp, "\t%qu pim6 join-prune received\n",
+ v->uv_in_pim6_join_prune);
+ fprintf(fp, "\t%qu pim6 bootstrap received\n",
+ v->uv_in_pim6_bootsrap);
+ fprintf(fp, "\t%qu pim6 assert received\n", v->uv_in_pim6_assert);
+
+ fprintf(fp, "\t%qu pim6 hello sent\n", v->uv_out_pim6_hello);
+ fprintf(fp, "\t%qu pim6 join-prune sent\n",
+ v->uv_out_pim6_join_prune);
+ fprintf(fp, "\t%qu pim6 bootstrap sent\n",
+ v->uv_out_pim6_bootsrap);
+ fprintf(fp, "\t%qu pim6 assert sent\n", v->uv_out_pim6_assert);
+
+ fprintf(fp, "\t%qu MLD query received\n", v->uv_in_mld_query);
+ fprintf(fp, "\t%qu MLD report received\n", v->uv_in_mld_report);
+ fprintf(fp, "\t%qu MLD done received\n", v->uv_in_mld_done);
+
+ fprintf(fp, "\t%qu MLD query sent\n", v->uv_out_mld_query);
+ fprintf(fp, "\t%qu MLD report sent\n", v->uv_out_mld_report);
+ fprintf(fp, "\t%qu MLD done sent\n", v->uv_out_mld_done);
+
+ fprintf(fp, "\t%qu forwarding cache miss\n", v->uv_cache_miss);
+ fprintf(fp, "\t%qu forwarding cache miss and not created\n",
+ v->uv_cache_notcreated);
+
+ fprintf(fp, "\t%qu PIM neighbor timeouts\n", v->uv_pim6_nbr_timo);
+ fprintf(fp, "\t%qu MLD listener timeouts\n", v->uv_listener_timo);
+ fprintf(fp, "\t%qu out-I/F timeouts\n", v->uv_outif_timo);
+ }
+
+ fprintf(fp, "\npim6sd interface independent statistics\n");
+
+ fprintf(fp, "\t%qu pim6 register received\n", pim6dstat.in_pim6_register);
+ fprintf(fp, "\t%qu pim6 register-stop received\n",
+ pim6dstat.in_pim6_register_stop);
+ fprintf(fp, "\t%qu pim6 cand-RP received\n", pim6dstat.in_pim6_cand_rp);
+ fprintf(fp, "\t%qu pim6 graft received\n", pim6dstat.in_pim6_graft);
+ fprintf(fp, "\t%qu pim6 graft ack received\n",
+ pim6dstat.in_pim6_graft_ack);
+
+ fprintf(fp, "\t%qu pim6 register sent\n", pim6dstat.out_pim6_register);
+ fprintf(fp, "\t%qu pim6 register-stop sent\n",
+ pim6dstat.out_pim6_register_stop);
+ fprintf(fp, "\t%qu pim6 cand-RP sent\n", pim6dstat.out_pim6_cand_rp);
+
+ fprintf(fp, "\t%qu transitions of forwarder initiated SPT\n",
+ pim6dstat.pim6_trans_spt_forward);
+ fprintf(fp, "\t%qu transitions of RP initiated SPT\n",
+ pim6dstat.pim6_trans_spt_rp);
+
+ fprintf(fp, "\t%qu pim6 bootstrap timeouts\n",
+ pim6dstat.pim6_bootstrap_timo);
+ fprintf(fp, "\t%qu pim6 RP group entry timeouts\n",
+ pim6dstat.pim6_rpgrp_timo);
+ fprintf(fp, "\t%qu pim6 routing entry timeouts\n",
+ pim6dstat.pim6_rtentry_timo);
+
+ fprintf(fp, "\t%qu kernel cache additions\n", pim6dstat.kern_add_cache);
+ fprintf(fp, "\t%qu kernel cache addition failures\n",
+ pim6dstat.kern_add_cache_fail);
+ fprintf(fp, "\t%qu kernel cache deletions\n", pim6dstat.kern_del_cache);
+ fprintf(fp, "\t%qu kernel cache deletion failures\n",
+ pim6dstat.kern_del_cache_fail);
+ fprintf(fp, "\t%qu failures of getting kernel cache\n",
+ pim6dstat.kern_sgcnt_fail);
+
+ fclose(fp);
+}
+
+void
+dump_vifs(fp)
+ FILE *fp;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ struct phaddr *pa;
+
+ fprintf(fp, "\nMulticast Interface Table\n %-4s %-6s %-43s %5s %-14s\n",
+ "Mif", " PhyIF", "Local-Address/Prefixlen","Scope", "Flags");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ {
+ int firstaddr = 1;
+ for (pa = v->uv_addrs; pa; pa = pa->pa_next)
+ {
+ if (!firstaddr)
+ {
+ fprintf(fp, " %3s %6s %-43s", "", "",
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ fprintf(fp," %-5d\n", pa->pa_addr.sin6_scope_id);
+ continue;
+ }
+
+ firstaddr = 0;
+ fprintf(fp, " %-3u %6s %-43s", vifi,
+ (v->uv_flags & MIFF_REGISTER)?"regist":v->uv_name,
+ net6name(&pa->pa_addr.sin6_addr,
+ &pa->pa_subnetmask));
+ fprintf(fp," %-5d", pa->pa_addr.sin6_scope_id);
+
+ if (v->uv_flags & MIFF_REGISTER)
+ fprintf(fp, " REGISTER");
+ if (v->uv_flags & VIFF_DISABLED)
+ fprintf(fp, " DISABLED");
+ if (v->uv_flags & VIFF_NOLISTENER)
+ fprintf(fp, " NOLISTENER");
+ if (v->uv_flags & VIFF_DOWN)
+ fprintf(fp, " DOWN");
+ if (v->uv_flags & VIFF_DR)
+ fprintf(fp, " DR");
+ if (v->uv_flags & VIFF_PIM_NBR)
+ fprintf(fp, " PIM");
+#if 0 /* impossible */
+ if (v->uv_flags & VIFF_DVMRP_NBR)
+ {
+ fprintf(fp, " DVMRP");
+ }
+#endif
+ if (v->uv_flags & VIFF_NONBRS)
+ fprintf(fp, " %-12s", "NO-NBR");
+
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp, " %3s %6s ", "", "");
+ fprintf(fp, "Timers: PIM hello = %d:%02d, MLD query = %d:%02d\n",
+ v->uv_pim_hello_timer / 60, v->uv_pim_hello_timer % 60,
+ v->uv_gq_timer / 60, v->uv_gq_timer % 60);
+ }
+ fprintf(fp, "\n");
+}
+
+void
+dump_nbrs(fp)
+ FILE *fp;
+{
+ struct uvif *v;
+ vifi_t vifi;
+ pim_nbr_entry_t *n;
+
+ fprintf(fp, "PIM Neighbor List\n");
+ fprintf(fp, " %-3s %6s %-40s %-5s\n",
+ "Mif", "PhyIF", "Address", "Timer");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if ((n = v->uv_pim_neighbors) != NULL) {
+ int first = 1;
+
+ fprintf(fp, " %-3u %6s", vifi,
+ (v->uv_flags & MIFF_REGISTER)?"regist":
+ v->uv_name);
+ for (; n != NULL; n = n->next) {
+ if (first)
+ first = 0;
+ else
+ fprintf(fp, " %3s %6s", "", "");
+ fprintf(fp, " %-40s %-5u\n",
+ inet6_fmt(&n->address.sin6_addr),
+ n->timer);
+ }
+ }
+ }
+
+ fprintf(fp, "\n");
+}
+
+/*
+ * Log errors and other messages to the system log daemon and to stderr,
+ * according to the severity of the message and the current debug level. For
+ * errors of severity LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format,...)
+{
+ va_list ap;
+ static char fmt[211] = "warning - ";
+ char *msg;
+ struct timeval now;
+ struct tm *thyme;
+
+ va_start(ap, format);
+#else
+/* VARARGS3 */
+void
+log(severity, syserr, format, va_alist)
+ int severity,
+ syserr;
+ char *format;
+va_dcl
+{
+ va_list ap;
+ static char fmt[311] = "warning - ";
+ char *msg;
+ char tbuf[20];
+ struct timeval now;
+ struct tm *thyme;
+
+ va_start(ap);
+#endif
+ vsprintf(&fmt[10], format, ap);
+ va_end(ap);
+ msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
+
+ /*
+ * Log to stderr if we haven't forked yet and it's a warning or worse, or
+ * if we're debugging.
+ */
+ if (debug || severity <= LOG_WARNING)
+ {
+ time_t t;
+ FILE *fp = log_fp ? log_fp : stderr;
+
+ gettimeofday(&now, NULL);
+ t = (time_t)now.tv_sec;
+ thyme = localtime(&t);
+ if (!debug)
+ fprintf(fp, "%s: ", progname);
+ fprintf(fp, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ if (syserr == 0)
+ fprintf(fp, "\n");
+ else
+ if (syserr < sys_nerr)
+ fprintf(fp, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(fp, ": errno %d\n", syserr);
+ }
+
+ /*
+ * Always log things that are worse than warnings, no matter what the
+ * log_nmsgs rate limiter says. Only count things worse than debugging in
+ * the rate limiter (since if you put daemon.debug in syslog.conf you
+ * probably actually want to log the debugging messages so they shouldn't
+ * be rate-limited)
+ */
+ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS))
+ {
+ if (severity < LOG_DEBUG)
+ log_nmsgs++;
+ if (syserr != 0)
+ {
+ errno = syserr;
+ syslog(severity, "%s: %m", msg);
+ }
+ else
+ syslog(severity, "%s", msg);
+ }
+
+ if (severity <= LOG_ERR)
+ exit(-1);
+}
+
+/* TODO: format the output for better readability */
+void
+dump_pim_mrt(fp)
+ FILE *fp;
+{
+ grpentry_t *g;
+ register mrtentry_t *r;
+ register vifi_t vifi;
+ int i;
+ u_int number_of_cache_mirrors = 0;
+ u_int number_of_groups = 0;
+ char joined_oifs[(sizeof(if_set) << 3) + 1];
+ char asserted_oifs[(sizeof(if_set) << 3) + 1];
+ cand_rp_t *rp;
+ kernel_cache_t *kernel_cache;
+ char oifs[(sizeof(if_set) << 3) + 1];
+ char pruned_oifs[(sizeof(if_set) << 3) + 1];
+ char leaves_oifs[(sizeof(if_set) << 3) + 1];
+ char incoming_iif[(sizeof(if_set) << 3) + 1];
+
+ fprintf(fp, "Multicast Routing Table\n%s",
+ " Source Group RP-addr Flags\n");
+
+ /* TODO: remove the dummy 0:: group (first in the chain) */
+ for (g = grplist->next; g != (grpentry_t *) NULL; g = g->next)
+ {
+ number_of_groups++;
+ if ((r = g->grp_route) != (mrtentry_t *) NULL)
+ {
+ if (r->flags & MRTF_KERNEL_CACHE)
+ {
+ for (kernel_cache = r->kernel_cache;
+ kernel_cache != (kernel_cache_t *) NULL;
+ kernel_cache = kernel_cache->next)
+ number_of_cache_mirrors++;
+ }
+
+ /* Print the (*,G) routing info */
+ fprintf(fp, "---------------------------(*,G)----------------------------\n");
+ fprintf(fp, " %-15s", "IN6ADDR_ANY");
+ fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr));
+ fprintf(fp, " %-15s",
+ g->active_rp_grp ? inet6_fmt(&g->rpaddr.sin6_addr) : "NULL");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_oifs[vifi] = 0x0;
+ incoming_iif[vifi] = 0x0;
+ incoming_iif[r->incoming] = 'I';
+
+ /* TODO: don't need some of the flags */
+ if (r->flags & MRTF_SPT)
+ fprintf(fp, " SPT");
+ if (r->flags & MRTF_WC)
+ fprintf(fp, " WC");
+ if (r->flags & MRTF_RP)
+ fprintf(fp, " RP");
+ if (r->flags & MRTF_REGISTER)
+ fprintf(fp, " REG");
+ if (r->flags & MRTF_IIF_REGISTER)
+ fprintf(fp, " IIF_REG");
+ if (r->flags & MRTF_NULL_OIF)
+ fprintf(fp, " NULL_OIF");
+ if (r->flags & MRTF_KERNEL_CACHE)
+ fprintf(fp, " CACHE");
+ if (r->flags & MRTF_ASSERTED)
+ fprintf(fp, " ASSERTED");
+ if (r->flags & MRTF_REG_SUPP)
+ fprintf(fp, " REG_SUPP");
+ if (r->flags & MRTF_SG)
+ fprintf(fp, " SG");
+ if (r->flags & MRTF_PMBR)
+ fprintf(fp, " PMBR");
+ fprintf(fp, "\n");
+
+ fprintf(fp, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs);
+ fprintf(fp, "Outgoing oifs: %-20s\n", oifs);
+ fprintf(fp, "Incoming : %-20s\n", incoming_iif);
+
+ fprintf(fp, "Upstream nbr: %s\n",
+ r->upstream ? inet6_fmt(&r->upstream->address.sin6_addr) : "NONE");
+
+ fprintf(fp, "\nTIMERS: Entry=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+
+ /* Print all (S,G) routing info */
+
+ for (r = g->mrtlink; r != (mrtentry_t *) NULL; r = r->grpnext)
+ {
+ fprintf(fp, "---------------------------(S,G)----------------------------\n");
+ if (r->flags & MRTF_KERNEL_CACHE)
+ number_of_cache_mirrors++;
+
+ /* Print the routing info */
+ fprintf(fp, " %-15s", inet6_fmt(&r->source->address.sin6_addr));
+ fprintf(fp, " %-15s", inet6_fmt(&g->group.sin6_addr));
+ fprintf(fp, " %-15s",
+ g->active_rp_grp ? inet6_fmt(&g->rpaddr.sin6_addr) : "NULL");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_oifs[vifi] = 0x0;
+ incoming_iif[vifi] = 0x0;
+ incoming_iif[r->incoming] = 'I';
+
+ /* TODO: don't need some of the flags */
+ if (r->flags & MRTF_SPT)
+ fprintf(fp, " SPT");
+ if (r->flags & MRTF_WC)
+ fprintf(fp, " WC");
+ if (r->flags & MRTF_RP)
+ fprintf(fp, " RP");
+ if (r->flags & MRTF_REGISTER)
+ fprintf(fp, " REG");
+ if (r->flags & MRTF_IIF_REGISTER)
+ fprintf(fp, " IIF_REG");
+ if (r->flags & MRTF_NULL_OIF)
+ fprintf(fp, " NULL_OIF");
+ if (r->flags & MRTF_KERNEL_CACHE)
+ fprintf(fp, " CACHE");
+ if (r->flags & MRTF_ASSERTED)
+ fprintf(fp, " ASSERTED");
+ if (r->flags & MRTF_REG_SUPP)
+ fprintf(fp, " REG_SUPP");
+ if (r->flags & MRTF_SG)
+ fprintf(fp, " SG");
+ if (r->flags & MRTF_PMBR)
+ fprintf(fp, " PMBR");
+ fprintf(fp, "\n");
+
+ fprintf(fp, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs);
+ fprintf(fp, "Outgoing oifs: %-20s\n", oifs);
+ fprintf(fp, "Incoming : %-20s\n", incoming_iif);
+
+ fprintf(fp, "Upstream nbr: %s\n",
+ r->upstream ? inet6_fmt(&r->upstream->address.sin6_addr) : "NONE");
+
+ fprintf(fp, "\nTIMERS: Entry=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+ } /* for all groups */
+
+ /* Print the (*,*,R) routing entries */
+ fprintf(fp, "--------------------------(*,*,RP)--------------------------\n");
+ for (rp = cand_rp_list; rp != (cand_rp_t *) NULL; rp = rp->next)
+ {
+ if ((r = rp->rpentry->mrtlink) != (mrtentry_t *) NULL)
+ {
+ if (r->flags & MRTF_KERNEL_CACHE)
+ {
+ for (kernel_cache = r->kernel_cache;
+ kernel_cache != (kernel_cache_t *) NULL;
+ kernel_cache = kernel_cache->next)
+ number_of_cache_mirrors++;
+ }
+
+ /* Print the (*,*,RP) routing info */
+ fprintf(fp, " RP = %-15s", inet6_fmt(&r->source->address.sin6_addr));
+ fprintf(fp, " %-15s", "IN6ADDR_ANY");
+
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ oifs[vifi] =
+ IF_ISSET(vifi, &r->oifs) ? 'o' : '.';
+ joined_oifs[vifi] =
+ IF_ISSET(vifi, &r->joined_oifs) ? 'j' : '.';
+ pruned_oifs[vifi] =
+ IF_ISSET(vifi, &r->pruned_oifs) ? 'p' : '.';
+ leaves_oifs[vifi] =
+ IF_ISSET(vifi, &r->leaves) ? 'l' : '.';
+ asserted_oifs[vifi] =
+ IF_ISSET(vifi, &r->asserted_oifs) ? 'a' : '.';
+ incoming_iif[vifi] = '.';
+ }
+ oifs[vifi] = 0x0; /* End of string */
+ joined_oifs[vifi] = 0x0;
+ pruned_oifs[vifi] = 0x0;
+ leaves_oifs[vifi] = 0x0;
+ asserted_oifs[vifi] = 0x0;
+ incoming_iif[vifi] = 0x0;
+ incoming_iif[r->incoming] = 'I';
+
+ /* TODO: don't need some of the flags */
+ if (r->flags & MRTF_SPT)
+ fprintf(fp, " SPT");
+ if (r->flags & MRTF_WC)
+ fprintf(fp, " WC");
+ if (r->flags & MRTF_RP)
+ fprintf(fp, " RP");
+ if (r->flags & MRTF_REGISTER)
+ fprintf(fp, " REG");
+ if (r->flags & MRTF_IIF_REGISTER)
+ fprintf(fp, " IIF_REG");
+ if (r->flags & MRTF_NULL_OIF)
+ fprintf(fp, " NULL_OIF");
+ if (r->flags & MRTF_KERNEL_CACHE)
+ fprintf(fp, " CACHE");
+ if (r->flags & MRTF_ASSERTED)
+ fprintf(fp, " ASSERTED");
+ if (r->flags & MRTF_REG_SUPP)
+ fprintf(fp, " REG_SUPP");
+ if (r->flags & MRTF_SG)
+ fprintf(fp, " SG");
+ if (r->flags & MRTF_PMBR)
+ fprintf(fp, " PMBR");
+ fprintf(fp, "\n");
+
+ fprintf(fp, "Joined oifs: %-20s\n", joined_oifs);
+ fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs);
+ fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs);
+ fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs);
+ fprintf(fp, "Outgoing oifs: %-20s\n", oifs);
+ fprintf(fp, "Incoming : %-20s\n", incoming_iif);
+
+ fprintf(fp, "\nTIMERS: Entry=%d JP=%d RS=%d Assert=%d\n",
+ r->timer, r->jp_timer, r->rs_timer, r->assert_timer);
+
+ fprintf(fp, " MIF 0 1 2 3 4 5 6 7 8 9\n");
+ for (vifi = 0, i = 0; vifi < numvifs && i <= numvifs / 10; i++) {
+ int j;
+
+ fprintf(fp, " %4d", i);
+ for (j = 0; j < 10 && vifi < numvifs; j++, vifi++)
+ fprintf(fp, " %3d", r->vif_timers[vifi]);
+ fprintf(fp, "\n");
+ }
+ }
+ } /* For all (*,*,RP) */
+
+ fprintf(fp, "Number of Groups: %u\n", number_of_groups);
+ fprintf(fp, "Number of Cache MIRRORs: %u\n\n", number_of_cache_mirrors);
+}
+
+
+/* TODO: modify the output for better redability */
+/*
+ * Dumps the local Cand-RP-set
+ */
+int
+dump_rp_set(fp)
+ FILE *fp;
+{
+ cand_rp_t *rp;
+ rp_grp_entry_t *rp_grp_entry;
+ grp_mask_t *grp_mask;
+
+ fprintf(fp, "---------------------------RP-Set----------------------------\n");
+ fprintf(fp, "Current BSR address: %s Prio: %d Timeout: %d\n",
+ inet6_fmt(&curr_bsr_address.sin6_addr), curr_bsr_priority,
+ pim_bootstrap_timer);
+ fprintf(fp, "%-40s %-3s Group prefix Prio Hold Age\n",
+ "RP-address", "IN");
+
+ for (rp = cand_rp_list; rp != (cand_rp_t *) NULL; rp = rp->next)
+ {
+
+ fprintf(fp, "%-40s %-3d ",
+ inet6_fmt(&rp->rpentry->address.sin6_addr),
+ rp->rpentry->incoming);
+ if ((rp_grp_entry = rp->rp_grp_next) != (rp_grp_entry_t *) NULL)
+ {
+ grp_mask = rp_grp_entry->group;
+ fprintf(fp, "%-16.16s %-4u %-4u %-3u\n",
+ net6name(&grp_mask->group_addr.sin6_addr,
+ &grp_mask->group_mask),
+ rp_grp_entry->priority, rp_grp_entry->advholdtime,
+ rp_grp_entry->holdtime);
+
+ for (rp_grp_entry = rp_grp_entry->rp_grp_next;
+ rp_grp_entry != (rp_grp_entry_t *) NULL;
+ rp_grp_entry = rp_grp_entry->rp_grp_next)
+ {
+ grp_mask = rp_grp_entry->group;
+ fprintf(fp, "%59.16s %-4u %-4u %-3u\n", /* XXX: hardcoding */
+ net6name(&grp_mask->group_addr.sin6_addr,
+ &grp_mask->group_mask),
+ rp_grp_entry->priority,
+ rp_grp_entry->advholdtime, rp_grp_entry->holdtime);
+ }
+ }
+ }
+ return (TRUE);
+}
diff --git a/usr.sbin/pim6sd/debug.h b/usr.sbin/pim6sd/debug.h
new file mode 100644
index 0000000..094ffd1
--- /dev/null
+++ b/usr.sbin/pim6sd/debug.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+
+#ifndef DEBUG_H
+#define DEBUG_H
+#include <sys/types.h>
+#include <stdio.h>
+
+extern unsigned long debug;
+extern int log_nmsgs;
+extern FILE *log_fp;
+#define IF_DEBUG(l) if (debug && debug & (l))
+
+#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
+#define LOG_SHUT_UP 600 /* shut up for 10 minutes */
+
+
+/* Debug values definition */
+/* DVMRP reserved for future use */
+
+#define DEBUG_DVMRP_PRUNE 0x00000001
+#define DEBUG_DVMRP_ROUTE 0x00000002
+#define DEBUG_DVMRP_PEER 0x00000004
+#define DEBUG_DVMRP_TIMER 0x00000008
+#define DEBUG_DVMRP_DETAIL 0x01000000
+#define DEBUG_DVMRP ( DEBUG_DVMRP_PRUNE | DEBUG_DVMRP_ROUTE | \
+ DEBUG_DVMRP_PEER )
+
+/* MLD related */
+
+#define DEBUG_MLD_PROTO 0x00000010
+#define DEBUG_MLD_TIMER 0x00000020
+#define DEBUG_MLD_MEMBER 0x00000040
+#define DEBUG_MEMBER DEBUG_MLD_MEMBER
+#define DEBUG_MLD ( DEBUG_MLD_PROTO | DEBUG_MLD_TIMER | \
+ DEBUG_MLD_MEMBER )
+
+/* Misc */
+
+#define DEBUG_TRACE 0x00000080
+#define DEBUG_TIMEOUT 0x00000100
+#define DEBUG_PKT 0x00000200
+
+
+/* Kernel related */
+
+#define DEBUG_IF 0x00000400
+#define DEBUG_KERN 0x00000800
+#define DEBUG_MFC 0x00001000
+#define DEBUG_RSRR 0x00002000
+
+/* PIM related */
+
+#define DEBUG_PIM_HELLO 0x00004000
+#define DEBUG_PIM_REGISTER 0x00008000
+#define DEBUG_PIM_JOIN_PRUNE 0x00010000
+#define DEBUG_PIM_BOOTSTRAP 0x00020000
+#define DEBUG_PIM_ASSERT 0x00040000
+#define DEBUG_PIM_CAND_RP 0x00080000
+#define DEBUG_PIM_MRT 0x00100000
+#define DEBUG_PIM_TIMER 0x00200000
+#define DEBUG_PIM_RPF 0x00400000
+#define DEBUG_RPF DEBUG_PIM_RPF
+#define DEBUG_PIM_DETAIL 0x00800000
+#define DEBUG_PIM ( DEBUG_PIM_HELLO | DEBUG_PIM_REGISTER | \
+ DEBUG_PIM_JOIN_PRUNE | DEBUG_PIM_BOOTSTRAP | \
+ DEBUG_PIM_ASSERT | DEBUG_PIM_CAND_RP | \
+ DEBUG_PIM_MRT | DEBUG_PIM_TIMER | \
+ DEBUG_PIM_RPF )
+
+#define DEBUG_MRT ( DEBUG_DVMRP_ROUTE | DEBUG_PIM_MRT )
+#define DEBUG_NEIGHBORS ( DEBUG_DVMRP_PEER | DEBUG_PIM_HELLO )
+#define DEBUG_TIMER ( DEBUG_MLD_TIMER | DEBUG_DVMRP_TIMER | \
+ DEBUG_PIM_TIMER )
+#define DEBUG_ASSERT ( DEBUG_PIM_ASSERT )
+
+/* CONFIG related */
+#define DEBUG_CONF 0x01000000
+
+#define DEBUG_ALL 0xffffffff
+#define DEBUG_SWITCH 0x80000000
+
+#define DEBUG_DEFAULT 0xffffffff/* default if "-d" given without value */
+
+#if defined(YIPS_DEBUG)
+#define YIPSDEBUG(lev,arg) if ((debug & (lev)) == (lev)) { arg; }
+#else
+#define YIPSDEBUG(lev,arg)
+#endif /* defined(YIPS_DEBUG) */
+
+extern char *packet_kind __P((u_int proto, u_int type,
+ u_int code));
+extern int debug_kind __P((u_int proto, u_int type,
+ u_int code));
+extern void log __P((int, int, char *, ...));
+extern int log_level __P((u_int proto, u_int type,
+ u_int code));
+extern void dump __P((int i));
+extern void fdump __P((int i));
+extern void cdump __P((int i));
+extern void dump_vifs __P((FILE *fp));
+extern void dump_nbrs __P((FILE *fp));
+extern void dump_pim_mrt __P((FILE *fp));
+extern int dump_rp_set __P((FILE *fp));
+extern void dump_stat __P(());
+
+#endif
diff --git a/usr.sbin/pim6sd/defs.h b/usr.sbin/pim6sd/defs.h
new file mode 100644
index 0000000..b9c3389
--- /dev/null
+++ b/usr.sbin/pim6sd/defs.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#include <sys/types.h>
+
+#define TRUE 1
+#define FALSE 0
+#define ELSE else /* To make emacs cc-mode happy */
+
+#define max( a , b ) ( ( a )<( b )?( b ):( a ) )
+
+typedef void ( *ihfunc_t ) __P( ( int , fd_set * ) );
+typedef void ( *cfunc_t ) __P( ( void * ) );
+
+int register_input_handler __P((int fd,ihfunc_t func));
+
+/* CONFIGCONFIGCONFIGCONFIG */
+
+#define HAVE_ROUTING_SOCKETS
+#define HAVE_SA_LEN
+#define RANDOM() random()
+
+#define PRINTF printf
+#define ALL_MCAST_GROUPS_LENGTH 8
+
+
+typedef u_int u_int32;
+typedef u_short u_int16;
+typedef u_char u_int8;
+
+
+extern char configfilename[];
+
+#endif
diff --git a/usr.sbin/pim6sd/inet6.c b/usr.sbin/pim6sd/inet6.c
new file mode 100644
index 0000000..ff34532
--- /dev/null
+++ b/usr.sbin/pim6sd/inet6.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include "defs.h"
+#include "vif.h"
+#include "inet6.h"
+#include <arpa/inet.h>
+
+/* flag if address to hostname resolution should be perfomed */
+int numerichost = TRUE;
+
+int
+inet6_uvif2scopeid(struct sockaddr_in6 * sa, struct uvif * v)
+{
+ if (IN6_IS_ADDR_MULTICAST(&sa->sin6_addr))
+ {
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&sa->sin6_addr))
+ return (v->uv_ifindex);
+ if (IN6_IS_ADDR_MC_SITELOCAL(&sa->sin6_addr))
+ return (v->uv_siteid);
+ }
+ else
+ {
+ if (IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
+ return (v->uv_ifindex);
+
+ if (IN6_IS_ADDR_SITELOCAL(&sa->sin6_addr))
+ return (v->uv_siteid);
+ }
+
+ return (0);
+}
+
+int
+inet6_localif_address(struct sockaddr_in6 * sa, struct uvif * v)
+{
+ struct phaddr *pa;
+
+ for (pa = v->uv_addrs; pa; pa = pa->pa_next)
+ if (inet6_equal(sa, &pa->pa_addr))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+int
+inet6_valid_host(struct sockaddr_in6 * addr)
+{
+ if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+int
+inet6_equal(struct sockaddr_in6 * sa1, struct sockaddr_in6 * sa2)
+{
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id &&
+ IN6_ARE_ADDR_EQUAL(&sa1->sin6_addr, &sa2->sin6_addr))
+ return (1);
+
+ return (0);
+}
+
+int
+inet6_lessthan(struct sockaddr_in6 * sa1, struct sockaddr_in6 * sa2)
+{
+ u_int32_t s32_1,
+ s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id < sa2->sin6_scope_id)
+ return (1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 > s32_2)
+ return (0);
+ if (s32_1 < s32_2)
+ return (1);
+
+ /* otherwide, continue to compare */
+ }
+ }
+
+ return (0);
+}
+
+int
+inet6_greaterthan(struct sockaddr_in6 * sa1, struct sockaddr_in6 * sa2)
+{
+ u_int32_t s32_1,
+ s32_2;
+ int i;
+
+ if (sa1->sin6_scope_id > sa2->sin6_scope_id)
+ return (1);
+ if (sa1->sin6_scope_id == sa2->sin6_scope_id)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ s32_1 = ntohl(*(u_int32_t *)&sa1->sin6_addr.s6_addr[i * 4]);
+ s32_2 = ntohl(*(u_int32_t *)&sa2->sin6_addr.s6_addr[i * 4]);
+
+ if (s32_1 < s32_2)
+ return (0);
+ if (s32_1 > s32_2)
+ return (1);
+
+ /* otherwide, continue to compare */
+ }
+ }
+
+ return (0);
+}
+
+int
+inet6_match_prefix(sa1, sa2, mask)
+ struct sockaddr_in6 *sa1,
+ *sa2;
+ struct in6_addr *mask;
+{
+ int i;
+
+ if (sa1->sin6_scope_id != sa2->sin6_scope_id)
+ return (0);
+
+ for (i = 0; i < 16; i++)
+ {
+ if ((sa1->sin6_addr.s6_addr[i] ^ sa2->sin6_addr.s6_addr[i]) &
+ mask->s6_addr[i])
+ return (0);
+ }
+
+ return (1);
+}
+
+char *
+inet6_fmt(struct in6_addr * addr)
+{
+ static char ip6buf[8][MAXHOSTNAMELEN];
+ static int ip6round = 0;
+ char *cp;
+ struct sockaddr_in6 sa6;
+ int flags = NI_WITHSCOPEID;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_len = sizeof(sa6);
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_addr = *addr;
+ sa6.sin6_scope_id = 0; /* XXX */
+
+ if (numerichost)
+ flags |= NI_NUMERICHOST;
+ getnameinfo((struct sockaddr *)&sa6, sa6.sin6_len, cp, MAXHOSTNAMELEN,
+ NULL, 0, flags);
+
+ return(cp);
+}
+
+char *
+ifindex2str(int ifindex)
+{
+ static char ifname[IFNAMSIZ];
+
+ return (if_indextoname(ifindex, ifname));
+}
+
+int
+inet6_mask2plen(struct in6_addr * mask)
+{
+ int masklen;
+ u_char *p = (u_char *) mask;
+ u_char *lim = p + 16;
+
+ for (masklen = 0; p < lim; p++)
+ {
+ switch (*p)
+ {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ }
+ }
+
+ return (masklen);
+}
+
+char *
+net6name(struct in6_addr * prefix, struct in6_addr * mask)
+{
+ static char ip6buf[8][INET6_ADDRSTRLEN + 4]; /* length of addr/plen */
+ static int ip6round = 0;
+ char *cp;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ inet_ntop(AF_INET6, prefix, cp, INET6_ADDRSTRLEN);
+ cp += strlen(cp);
+ *cp = '/';
+ cp++;
+ sprintf(cp, "%d", inet6_mask2plen(mask));
+
+ return (ip6buf[ip6round]);
+}
diff --git a/usr.sbin/pim6sd/inet6.h b/usr.sbin/pim6sd/inet6.h
new file mode 100644
index 0000000..21ba6dc
--- /dev/null
+++ b/usr.sbin/pim6sd/inet6.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef INET6_H
+#define INET6_H
+#include "vif.h"
+
+extern int numerichost;
+
+extern int inet6_equal __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_lessthan __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_localif_address __P((struct sockaddr_in6 *sa,
+ struct uvif *v));
+extern int inet6_greaterthan __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2));
+extern int inet6_match_prefix __P((struct sockaddr_in6 *sa1,
+ struct sockaddr_in6 *sa2,
+ struct in6_addr *mask));
+extern int inet6_mask2plen __P((struct in6_addr *mask));
+extern int inet6_uvif2scopeid __P((struct sockaddr_in6 *sa, struct uvif *v));
+extern int inet6_valid_host __P((struct sockaddr_in6 *addr));
+extern char *inet6_fmt __P((struct in6_addr *addr));
+extern char *ifindex2str __P((int ifindex));
+extern char *net6name __P((struct in6_addr *prefix,
+ struct in6_addr *mask));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/kern.c b/usr.sbin/pim6sd/kern.c
new file mode 100644
index 0000000..73f610d
--- /dev/null
+++ b/usr.sbin/pim6sd/kern.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet6/ip6_mroute.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "inet6.h"
+#include "vif.h"
+#include "mrt.h"
+#include "debug.h"
+
+
+/*
+ * Open/init the multicast routing in the kernel and sets the MRT_ASSERT
+ * flag in the kernel.
+ *
+ */
+
+
+void
+k_init_pim(int socket)
+{
+ int v = 1;
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_INIT, (char *) &v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "cannot enable multicast routing in kernel");
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_PIM, (char *) &v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "Pim kernel initialization");
+}
+
+/*
+ * Stops the multicast routing in the kernel and resets the MRT_ASSERT flag
+ * in the kernel.
+ */
+
+void
+k_stop_pim(socket)
+ int socket;
+{
+ int v = 0;
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_PIM,
+ (char *) &v, sizeof(int)) < 0)
+ log(LOG_ERR, errno, "Cannot reset PIM flag in kernel");
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DONE, (char *) NULL, 0) < 0)
+ log(LOG_ERR, errno, "cannot disable multicast routing in kernel");
+
+}
+
+/*
+ * Set the socket receiving buffer. `bufsize` is the preferred size,
+ * `minsize` is the smallest acceptable size.
+ */
+
+void
+k_set_rcvbuf(int socket, int bufsize, int minsize)
+{
+ int delta = bufsize / 2;
+ int iter = 0;
+
+ /*
+ * Set the socket buffer. If we can't set it as large as we
+ * want, search around to try to find the highest acceptable
+ * value. The highest acceptable value being smaller than
+ * minsize is a fatal error.
+ */
+
+
+ if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, sizeof(bufsize)) < 0)
+ {
+ bufsize -= delta;
+ while (1)
+ {
+ iter++;
+ if (delta > 1)
+ delta /= 2;
+ if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, sizeof(bufsize)) < 0)
+ bufsize -= delta;
+ else
+ {
+ if (delta < 1024)
+ break;
+ bufsize += delta;
+ }
+ }
+ if (bufsize < minsize)
+ log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
+ bufsize, minsize);
+ /*NOTREACHED*/
+
+
+ }
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG,0,"Buffer reception size for socket %d : %d in %d iterations",socket, bufsize, iter);
+}
+
+/*
+ * Set the default Hop Limit for the multicast packets outgoing from this
+ * socket.
+ */
+
+void
+k_set_hlim(int socket, int h)
+{
+ int hlim = h;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &hlim, sizeof(hlim)) < 0)
+ log(LOG_ERR,errno,"k_set_hlim");
+
+}
+
+/*
+ * Set/reset the IPV6_MULTICAST_LOOP. Set/reset is specified by "flag".
+ */
+
+
+void
+k_set_loop(int socket, int flag)
+{
+ u_int loop;
+
+ loop = flag;
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &loop, sizeof(loop)) < 0)
+ log(LOG_ERR,errno,"k_set_loop");
+}
+
+/*
+ * Set the IPV6_MULTICAST_IF option on local interface which has the
+ * specified index.
+ */
+
+
+void
+k_set_if(int socket, u_int ifindex)
+{
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ (char *) &ifindex, sizeof(ifindex)) < 0)
+ log(LOG_ERR, errno, "setsockopt IPV6_MULTICAST_IF for %s",
+ ifindex2str(ifindex));
+
+}
+
+/*
+ * Join a multicast grp group on local interface ifa.
+ */
+
+void
+k_join(int socket, struct in6_addr * grp, u_int ifindex)
+{
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = *grp;
+ mreq.ipv6mr_interface = ifindex;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *) &mreq, sizeof(mreq)) < 0)
+ syslog(LOG_WARNING, "Cannot join group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+/*
+ * Leave a multicats grp group on local interface ifa.
+ */
+
+void
+k_leave(int socket, struct in6_addr * grp, u_int ifindex)
+{
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = *grp;
+ mreq.ipv6mr_interface = ifindex;
+
+ if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char *) &mreq, sizeof(mreq)) < 0)
+ syslog(LOG_WARNING, "Cannot leave group %s on interface %s",
+ inet6_fmt(grp), ifindex2str(ifindex));
+}
+
+/*
+ * Add a virtual interface in the kernel.
+ */
+
+void
+k_add_vif(int socket, vifi_t vifi, struct uvif * v)
+{
+ struct mif6ctl mc;
+
+ mc.mif6c_mifi = vifi;
+ mc.mif6c_flags = v->uv_flags;
+
+ mc.mif6c_pifi = v->uv_ifindex;
+
+ if ((v->uv_flags & MIFF_REGISTER))
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ log(LOG_DEBUG,0,"register vifi : %d , register pifi : %d ", vifi, v->uv_ifindex);
+
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MIF,
+ (char *) &mc, sizeof(mc)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT6_ADD_MIF on mif %d", vifi);
+}
+
+/*
+ * Delete a virtual interface in the kernel.
+ */
+
+void
+k_del_vif(int socket, vifi_t vifi)
+{
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MIF,
+ (char *) &vifi, sizeof(vifi)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT6_DEL_MIF on mif %d", vifi);
+}
+
+/*
+ * Delete all MFC entries for particular routing entry from the kernel.
+ */
+
+int
+k_del_mfc(int socket, struct sockaddr_in6 * source, struct sockaddr_in6 * group)
+{
+ struct mf6cctl mc;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+
+ pim6dstat.kern_del_cache++;
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_DEL_MFC, (char *) &mc, sizeof(mc)) < 0)
+ {
+ pim6dstat.kern_del_cache_fail++;
+ log(LOG_WARNING, errno, "setsockopt MRT6_DEL_MFC");
+ return FALSE;
+ }
+
+ syslog(LOG_DEBUG, "Deleted MFC entry : src %s ,grp %s", inet6_fmt(&source->sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ return TRUE;
+}
+
+/*
+ * Install/modify a MFC entry in the kernel
+ */
+
+int
+k_chg_mfc(socket, source, group, iif, oifs, rp_addr)
+ int socket;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ vifi_t iif;
+ if_set *oifs;
+ struct sockaddr_in6 *rp_addr;
+{
+ struct mf6cctl mc;
+ vifi_t vifi;
+ struct uvif *v;
+
+ mc.mf6cc_origin = *source;
+ mc.mf6cc_mcastgrp = *group;
+ mc.mf6cc_parent = iif;
+
+
+ IF_ZERO(&mc.mf6cc_ifset);
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
+ {
+ if (IF_ISSET(vifi, oifs))
+ IF_SET(vifi, &mc.mf6cc_ifset);
+ else
+ IF_CLR(vifi, &mc.mf6cc_ifset);
+ }
+
+#ifdef PIM_REG_KERNEL_ENCAP
+ mc.mf6cc_rp_addr.s_addr = rp_addr;
+#endif
+
+ pim6dstat.kern_add_cache++;
+ if (setsockopt(socket, IPPROTO_IPV6, MRT6_ADD_MFC, (char *) &mc,
+ sizeof(mc)) < 0)
+ {
+ pim6dstat.kern_add_cache_fail++;
+ log(LOG_WARNING, errno,
+ "setsockopt MRT_ADD_MFC for source %s and group %s",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Get packet counters for particular interface
+ */
+/*
+ * XXX: TODO: currently not used, but keep just in case we need it later.
+ */
+
+int
+k_get_vif_count(vifi, retval)
+ vifi_t vifi;
+ struct vif_count *retval;
+{
+ struct sioc_mif_req6 mreq;
+
+ mreq.mifi = vifi;
+ if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *) &mreq) < 0)
+ {
+ log(LOG_WARNING, errno, "SIOCGETMIFCNT_IN6 on vif %d", vifi);
+ retval->icount = retval->ocount = retval->ibytes =
+ retval->obytes = 0xffffffff;
+ return (1);
+ }
+ retval->icount = mreq.icount;
+ retval->ocount = mreq.ocount;
+ retval->ibytes = mreq.ibytes;
+ retval->obytes = mreq.obytes;
+ return (0);
+}
+
+
+/*
+ * Gets the number of packets, bytes, and number of packets arrived on wrong
+ * if in the kernel for particular (S,G) entry.
+ */
+
+int
+k_get_sg_cnt(socket, source, group, retval)
+ int socket; /* udp_socket */
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ struct sg_count *retval;
+{
+ struct sioc_sg_req6 sgreq;
+
+ sgreq.src = *source;
+ sgreq.grp = *group;
+ if (ioctl(socket, SIOCGETSGCNT_IN6, (char *) &sgreq) < 0)
+ {
+ pim6dstat.kern_sgcnt_fail++;
+ log(LOG_WARNING, errno, "SIOCGETSGCNT_IN6 on (%s %s)",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0; /* XXX */
+ return (1);
+ }
+ retval->pktcnt = sgreq.pktcnt;
+ retval->bytecnt = sgreq.bytecnt;
+ retval->wrong_if = sgreq.wrong_if;
+ return (0);
+}
diff --git a/usr.sbin/pim6sd/kern.h b/usr.sbin/pim6sd/kern.h
new file mode 100644
index 0000000..f58503c
--- /dev/null
+++ b/usr.sbin/pim6sd/kern.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef KERN_H
+#define KERN_H
+#include "vif.h"
+#include "mrt.h"
+
+extern void k_set_rcvbuf __P((int socket, int bufsize, int minsize));
+extern void k_set_hlim __P((int socket, int t));
+extern void k_set_loop __P((int socket, int l));
+extern void k_set_if __P((int socket, u_int ifindex));
+extern void k_join __P((int socket, struct in6_addr *grp,
+ u_int ifindex));
+extern void k_leave __P((int socket, struct in6_addr *grp,
+ u_int ifindex));
+extern void k_init_pim __P(());
+extern void k_stop_pim __P(());
+extern int k_del_mfc __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern int k_chg_mfc __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group, vifi_t iif,
+ if_set *oifs, struct sockaddr_in6 *rp_addr));
+extern void k_add_vif __P((int socket, vifi_t vifi, struct uvif *v));
+extern void k_del_vif __P((int socket, vifi_t vifi));
+extern int k_get_vif_count __P((vifi_t vifi, struct vif_count *retval));
+extern int k_get_sg_cnt __P((int socket, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group,
+ struct sg_count *retval));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/main.c b/usr.sbin/pim6sd/main.c
new file mode 100644
index 0000000..3ad1e7a
--- /dev/null
+++ b/usr.sbin/pim6sd/main.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include "pathnames.h"
+#include "defs.h"
+#include "debug.h"
+#include "mld6.h"
+#include "pim6.h"
+#include "vif.h"
+#include "routesock.h"
+#include "callout.h"
+#include "mrt.h"
+#include "timer.h"
+#include "rp.h"
+#include "kern.h"
+#include "cfparse.h"
+
+char configfilename[256] = _PATH_PIM6D_CONF;
+char versionstring[100];
+char logfilename[256] = _PATH_PIM6D_LOGFILE;
+
+/* TODO: not used
+static char genidfilename[] = _PATH_PIM6D_GENID;
+*/
+static char pidfilename[] = _PATH_PIM6D_PID;
+
+FILE *log_fp = stderr;
+char *progname;
+
+static int foreground = 0;
+static int sighandled = 0;
+
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+#define GOT_SIGALRM 0x10
+#define GOT_SIGINFO 0x20
+
+
+#define NHANDLERS 3
+
+static struct ihandler
+{
+ int fd; /* File descriptor */
+ ihfunc_t func; /* Function to call with &fd_set */
+} ihandlers[NHANDLERS];
+
+static int nhandlers = 0;
+
+static struct debugname
+{
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+ { "mld_proto", DEBUG_MLD_PROTO, 5 },
+ { "mld_timer", DEBUG_MLD_TIMER, 5 },
+ { "mld_member", DEBUG_MLD_MEMBER, 5 },
+ { "mld", DEBUG_MLD, 3 },
+ { "switch", DEBUG_SWITCH, 2 },
+ { "trace", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "timeout", DEBUG_TIMEOUT, 2 },
+ { "callout", DEBUG_TIMEOUT, 3 },
+ { "pkt", DEBUG_PKT, 2 },
+ { "packets", DEBUG_PKT, 2 },
+ { "interfaces", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "kernel", DEBUG_KERN, 2 },
+ { "cache", DEBUG_MFC, 1 },
+ { "mfc", DEBUG_MFC, 2 },
+ { "k_cache", DEBUG_MFC, 2 },
+ { "k_mfc", DEBUG_MFC, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "pim_detail", DEBUG_PIM_DETAIL, 5 },
+ { "pim_hello", DEBUG_PIM_HELLO, 5 },
+ { "pim_neighbors", DEBUG_PIM_HELLO, 5 },
+ { "pim_register", DEBUG_PIM_REGISTER, 5 },
+ { "registers", DEBUG_PIM_REGISTER, 2 },
+ { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 },
+ { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 },
+ { "bsr", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 },
+ { "pim_asserts", DEBUG_PIM_ASSERT, 5 },
+ { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 },
+ { "pim_rp", DEBUG_PIM_CAND_RP, 6 },
+ { "rp", DEBUG_PIM_CAND_RP, 2 },
+ { "pim_routes", DEBUG_PIM_MRT, 6 },
+ { "pim_routing", DEBUG_PIM_MRT, 6 },
+ { "pim_mrt", DEBUG_PIM_MRT, 5 },
+ { "pim_timers", DEBUG_PIM_TIMER, 5 },
+ { "pim_rpf", DEBUG_PIM_RPF, 6 },
+ { "rpf", DEBUG_RPF, 3 },
+ { "pim", DEBUG_PIM, 1 },
+ { "routes", DEBUG_MRT, 1 },
+ { "routing", DEBUG_MRT, 1 },
+ { "mrt", DEBUG_MRT, 1 },
+ { "routers", DEBUG_NEIGHBORS, 6 },
+ { "mrouters", DEBUG_NEIGHBORS, 7 },
+ { "neighbors", DEBUG_NEIGHBORS, 1 },
+ { "timers", DEBUG_TIMER, 1 },
+ { "asserts", DEBUG_ASSERT, 1 },
+ { "all", DEBUG_ALL, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
+
+/*
+ * Forward declarations.
+ */
+
+static void handler __P((int));
+static void timer __P((void *));
+static void cleanup __P((void));
+static void restart __P((int));
+static void cleanup __P((void));
+
+
+/* To shut up gcc -Wstrict-prototypes */
+
+int main __P((int argc, char **argv));
+
+int
+register_input_handler(fd, func)
+ int fd;
+ ihfunc_t func;
+{
+ if (nhandlers >= NHANDLERS)
+ return -1;
+
+ ihandlers[nhandlers].fd = fd;
+ ihandlers[nhandlers++].func = func;
+
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int dummy,
+ dummysigalrm;
+ FILE *fp;
+ struct timeval tv,
+ difftime,
+ curtime,
+ lasttime,
+ *timeout;
+ fd_set rfds,
+ readers;
+ int nfds=0,
+ n,
+ i,
+ secs;
+ extern char todaysversion[];
+ struct sigaction sa;
+ struct debugname *d;
+ char c;
+ int tmpd;
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0)
+ {
+ fprintf(stderr, "pim6sd: must be root\n");
+ exit(1);
+ }
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ argv++;
+ argc--;
+ while (argc > 0 && *argv[0] == '-')
+ {
+ if (strcmp(*argv, "-d") == 0)
+ {
+ if (argc > 1 && *(argv + 1)[0] != '-')
+ {
+ char *p,
+ *q;
+ int i,
+ len;
+ struct debugname *d;
+ int no=0;
+
+ argv++;
+ argc--;
+ debug = 0;
+ p = *argv;
+ q = NULL;
+ while (p)
+ {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ if(p[0]=='-')
+ {
+ no=1;
+ p++;
+ }
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0]))
+ {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ {
+ if ((j & d->level) == d->level)
+ {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ goto usage;
+ }
+ if(no)
+ {
+ debug &=~d->level;
+ no=0;
+ }
+ else
+ debug |= d->level;
+ p = q;
+ }
+ }
+ else
+ debug = DEBUG_DEFAULT;
+ }
+ else if (strcmp(*argv, "-c") == 0) {
+ if (argc > 1)
+ {
+ argv++;
+ argc--;
+ strcpy(configfilename, *argv);
+ }
+ else
+ goto usage;
+ }
+ else if (strcmp(*argv, "-f") == 0)
+ foreground = 1;
+ else
+ goto usage;
+
+ argv++;
+ argc--;
+ }
+
+ if (argc > 0)
+ {
+usage:
+ tmpd = 0xffffffff;
+ fprintf(stderr, "usage: pim6sd [-c configfile] [-d [debug_level][,debug_level]]\n");
+
+ fprintf(stderr, "debug levels: ");
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++)
+ {
+ if ((tmpd & d->level) == d->level)
+ {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ exit(1);
+ }
+
+ if (debug != 0)
+ {
+ tmpd = debug;
+ fprintf(stderr, "debug level 0x%lx ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++)
+ {
+ if ((tmpd & d->level) == d->level)
+ {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef LOG_DAEMON
+ (void) openlog("pim6sd", LOG_PID, LOG_DAEMON);
+// (void) setlogmask(LOG_UPTO(LOG_NOTICE));
+#else
+ (void) openlog("pim6sd", LOG_PID);
+#endif /* LOG_DAEMON */
+ /* open a log file */
+ if ((log_fp = fopen(logfilename, "w")) == NULL)
+ log(LOG_ERR, errno, "fopen(%s)", logfilename);
+ setlinebuf(log_fp);
+
+ sprintf(versionstring, "pim6sd version %s", todaysversion);
+
+ log(LOG_INFO, 0, "%s starting", versionstring);
+
+ /*
+ * TODO: XXX: use a combination of time and hostid to initialize the
+ * random generator.
+ */
+
+#ifdef SYSV
+ srand48(time(NULL));
+#else
+ srandom(time(NULL));
+#endif
+
+ callout_init();
+ init_mld6();
+ init_pim6();
+
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+
+ init_pim6_mrt();
+ init_timers();
+
+ /* TODO: check the kernel DVMRP/MROUTED/PIM support version */
+
+ init_vifs();
+ init_rp6_and_bsr6(); /* Must be after init_vifs() */
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
+ sigaction(SIGINFO, &sa, NULL);
+
+ FD_ZERO(&readers);
+ FD_SET(mld6_socket, &readers);
+ for (i = 0; i < nhandlers; i++)
+ {
+ FD_SET(ihandlers[i].fd, &readers);
+ if (ihandlers[i].fd >= nfds)
+ nfds = ihandlers[i].fd + 1;
+ }
+
+ IF_DEBUG(DEBUG_IF)
+ dump_vifs(log_fp);
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(log_fp);
+
+ /* schedule first timer interrupt */
+ timer_setTimer(timer_interval, timer, NULL);
+
+ if (foreground == 0)
+ {
+ /* Detach from the terminal */
+#ifdef TIOCNOTTY
+ int t;
+#endif /* TIOCNOTTY */
+
+ if (fork())
+ exit(0);
+
+#ifdef HAVE_ROUTING_SOCKETS
+ pid = getpid();
+#endif
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+#if defined(SYSV) || defined(linux)
+ (void) setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open("/dev/tty", 2);
+ if (t >= 0)
+ {
+ (void) ioctl(t, TIOCNOTTY, (char *) 0);
+ (void) close(t);
+ }
+#else
+ if (setsid() < 0)
+ perror("setsid");
+#endif /* TIOCNOTTY */
+#endif /* SYSV */
+ } /* End of child process code */
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", (int) getpid());
+ (void) fclose(fp);
+ }
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ dummysigalrm = SIGALRM;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
+ for (;;)
+ {
+ bcopy((char *) &readers, (char *) &rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else
+ {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
+
+ if (sighandled)
+ {
+ if (sighandled & GOT_SIGINT)
+ {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP)
+ {
+ sighandled &= ~GOT_SIGHUP;
+ restart(SIGHUP);
+ }
+ if (sighandled & GOT_SIGINFO)
+ {
+ sighandled &= ~GOT_SIGINFO;
+ dump_stat();
+ }
+ if (sighandled & GOT_SIGUSR1)
+ {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump(SIGUSR1);
+ }
+ if (sighandled & GOT_SIGUSR2)
+ {
+ sighandled &= ~GOT_SIGUSR2;
+#ifdef notyet
+ cdump(SIGUSR2);
+#else
+ cfparse(0, 1); /* reset debug level */
+#endif
+ }
+ if (sighandled & GOT_SIGALRM)
+ {
+ sighandled &= ~GOT_SIGALRM;
+ timer(&dummysigalrm);
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0)
+ {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second, or if there is
+ * a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the time doesn't
+ * drift too badly.
+ *
+ * If the timeout handlers took more than 1 second, age the timeout
+ * queue again. XXX This introduces the potential for infinite
+ * loops!
+ */
+ do
+ {
+ /*
+ * If the select timed out, then there's no other activity to
+ * account for and we don't need to call gettimeofday.
+ */
+ if (n == 0)
+ {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ }
+ else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "TIMEOUT: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec);
+#endif
+ while (difftime.tv_usec > 1000000)
+ {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0)
+ {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0)
+ if (secs == 0 || difftime.tv_sec > 0)
+ {
+#ifdef TIMERDEBUG
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "\taging callouts: secs %d, diff secs %d, diff usecs %d", secs, difftime.tv_sec, difftime.tv_usec);
+#endif
+ age_callout_queue(difftime.tv_sec);
+ }
+ secs = -1;
+ } while (difftime.tv_sec > 0);
+
+ /* Handle sockets */
+ if (n > 0)
+ {
+ for (i = 0; i < nhandlers; i++)
+ {
+ if (FD_ISSET(ihandlers[i].fd, &rfds))
+ {
+ (*ihandlers[i].func) (ihandlers[i].fd, &rfds);
+ }
+ }
+ }
+
+ } /* Main loop */
+
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
+}
+
+/*
+ * The 'virtual_time' variable is initialized to a value that will cause the
+ * first invocation of timer() to send a probe or route report to all vifs
+ * and send group membership queries to all subnets for which this router is
+ * querier. This first invocation occurs approximately timer_interval
+ * seconds after the router starts up. Note that probes for neighbors and
+ * queries for group memberships are also sent at start-up time, as part of
+ * initial- ization. This repetition after a short interval is desirable for
+ * quickly building up topology and membership information in the presence of
+ * possible packet loss.
+ *
+ * 'virtual_time' advances at a rate that is only a crude approximation of real
+ * time, because it does not take into account any time spent processing, and
+ * because the timer intervals are sometimes shrunk by a random amount to
+ * avoid unwanted synchronization with other routers.
+ */
+
+u_long virtual_time = 0;
+
+/*
+ * Timer routine. Performs all perodic functions: aging interfaces, quering
+ * neighbors and members, etc... The granularity is equal to timer_interval.
+ * this granularity is configurable ( see file pim6sd.conf.sample)
+ */
+
+static void
+timer(i)
+ void *i;
+{
+ age_vifs(); /* Timeout neighbors and groups */
+ age_routes(); /* Timeout routing entries */
+ age_misc(); /* Timeout the rest (Cand-RP list, etc) */
+
+ virtual_time += timer_interval;
+ timer_setTimer(timer_interval, timer, NULL);
+}
+
+/*
+ * Performs all necessary functions to quit gracefully
+ */
+/* TODO: implement all necessary stuff */
+
+static void
+cleanup()
+{
+
+ /*
+ * TODO: XXX (not in the spec): if I am the BSR, somehow inform the other
+ * routers I am going down and need to elect another BSR? (probably by
+ * sending a the Cand-RP-set with my_priority=LOWEST?)
+ *
+ */
+
+ k_stop_pim(mld6_socket);
+}
+
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived so that the
+ * main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig)
+ {
+ case SIGALRM:
+ sighandled |= GOT_SIGALRM;
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
+
+ case SIGINFO:
+ sighandled |= GOT_SIGINFO;
+ break;
+ }
+}
+
+
+/* TODO: not verified */
+/*
+ * Restart the daemon
+ */
+
+static void
+restart(i)
+ int i;
+{
+
+ log(LOG_NOTICE, 0, "% restart", versionstring);
+
+ /*
+ * reset all the entries
+ */
+ /*
+ * TODO: delete?
+ free_all_routes();
+ */
+
+ free_all_callouts();
+ stop_all_vifs();
+ nhandlers=0;
+ k_stop_pim(mld6_socket);
+ close(mld6_socket);
+ close(pim6_socket);
+ close(udp_socket);
+
+ /*
+ * start processing again
+ */
+
+ init_mld6();
+ init_pim6();
+
+#ifdef HAVE_ROUTING_SOCKETS
+ init_routesock();
+#endif /* HAVE_ROUTING_SOCKETS */
+
+ init_pim6_mrt();
+ init_vifs();
+
+ /* schedule timer interrupts */
+ timer_setTimer(timer_interval, timer, NULL);
+}
diff --git a/usr.sbin/pim6sd/mld6.c b/usr.sbin/pim6sd/mld6.c
new file mode 100644
index 0000000..9c9c109
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "mld6.h"
+#include "kern.h"
+#include "defs.h"
+#include "inet6.h"
+#include "debug.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "trace.h"
+
+/*
+ * Exported variables.
+ */
+
+char *mld6_recv_buf; /* input packet buffer */
+char *mld6_send_buf; /* output packet buffer */
+int mld6_socket; /* socket for all network I/O */
+struct sockaddr_in6 allrouters_group = {sizeof(struct sockaddr_in6), AF_INET6};
+struct sockaddr_in6 allnodes_group = {sizeof(struct sockaddr_in6), AF_INET6};
+
+/* Extenals */
+
+extern struct in6_addr in6addr_linklocal_allnodes;
+
+/* local variables. */
+static struct sockaddr_in6 dst = {sizeof(dst), AF_INET6};
+static struct msghdr sndmh,
+ rcvmh;
+static struct iovec sndiov[2];
+static struct iovec rcviov[2];
+static struct sockaddr_in6 from;
+static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int))];
+#ifndef USE_RFC2292BIS
+u_int8_t raopt[IP6OPT_RTALERT_LEN];
+#endif
+static char *sndcmsgbuf;
+static int ctlbuflen = 0;
+static u_short rtalert_code;
+
+/* local functions */
+
+static void mld6_read __P((int i, fd_set * fds));
+static void accept_mld6 __P((int len));
+
+#ifndef IP6OPT_ROUTER_ALERT /* XXX to be compatible older systems */
+#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
+#endif
+
+/*
+ * Open and initialize the MLD socket.
+ */
+void
+init_mld6()
+{
+ struct icmp6_filter filt;
+ int on;
+
+ rtalert_code = htons(IP6OPT_RTALERT_MLD);
+ if (!mld6_recv_buf && (mld6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR, 0, "malloca failed");
+ if (!mld6_send_buf && (mld6_send_buf = malloc(RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR, 0, "malloca failed");
+
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE);
+
+ if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+ log(LOG_ERR, errno, "MLD6 socket");
+
+ k_set_rcvbuf(mld6_socket, SO_RECV_BUF_SIZE_MAX,
+ SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
+ k_set_hlim(mld6_socket, MINHLIM); /* restrict multicasts to one hop */
+ k_set_loop(mld6_socket, FALSE); /* disable multicast loopback */
+
+ /* address initialization */
+ allnodes_group.sin6_addr = in6addr_linklocal_allnodes;
+ if (inet_pton(AF_INET6, "ff02::2",
+ (void *) &allrouters_group.sin6_addr) != 1)
+ log(LOG_ERR, 0, "inet_pton failed for ff02::2");
+
+ /* filter all non-MLD ICMP messages */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filt);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE, &filt);
+ if (setsockopt(mld6_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)");
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
+#else /* old adv. API */
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)");
+#else /* old adv. API */
+ if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)");
+#endif
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t) mld6_recv_buf;
+ rcviov[0].iov_len = RECV_BUF_SIZE;
+ rcvmh.msg_name = (caddr_t) & from;
+ rcvmh.msg_namelen = sizeof(from);
+ rcvmh.msg_iov = rcviov;
+ rcvmh.msg_iovlen = 1;
+ rcvmh.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
+
+ /* initialize msghdr for sending packets */
+ sndiov[0].iov_base = (caddr_t)mld6_send_buf;
+ sndmh.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmh.msg_iov = sndiov;
+ sndmh.msg_iovlen = 1;
+ /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
+#ifndef USE_RFC2292BIS
+ raopt[0] = IP6OPT_ROUTER_ALERT;
+ raopt[1] = IP6OPT_RTALERT_LEN - 2;
+ memcpy(&raopt[2], (caddr_t) & rtalert_code, sizeof(u_short));
+#endif
+
+ /* register MLD message handler */
+ if (register_input_handler(mld6_socket, mld6_read) < 0)
+ log(LOG_ERR, 0,
+ "Couldn't register mld6_read as an input handler");
+}
+
+/* Read an MLD message */
+static void
+mld6_read(i, rfd)
+ int i;
+ fd_set *rfd;
+{
+ register int mld6_recvlen;
+
+ mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0);
+
+ if (mld6_recvlen < 0)
+ {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "MLD6 recvmsg");
+ return;
+ }
+
+ /* TODO: make it as a thread in the future releases */
+ accept_mld6(mld6_recvlen);
+}
+
+/*
+ * Process a newly received MLD6 packet that is sitting in the input packet
+ * buffer.
+ */
+static void
+accept_mld6(recvlen)
+int recvlen;
+{
+ struct in6_addr *group, *dst = NULL;
+ struct mld6_hdr *mldh;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ int *hlimp = NULL;
+ int ifindex = 0;
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name;
+
+ /*
+ * If control length is zero, it must be an upcall from the kernel
+ * multicast forwarding engine.
+ * XXX: can we trust it?
+ */
+ if (rcvmh.msg_controllen == 0) {
+ /* XXX: msg_controllen must be reset in this case. */
+ rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
+
+ process_kernel_call();
+ return;
+ }
+
+ if (recvlen < sizeof(struct mld6_hdr))
+ {
+ log(LOG_WARNING, 0,
+ "received packet too short (%u bytes) for MLD header",
+ recvlen);
+ return;
+ }
+ mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base;
+ group = &mldh->mld6_addr;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh);
+ cm;
+ cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm))
+ {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
+ {
+ pi = (struct in6_pktinfo *) (CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = &pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *) CMSG_DATA(cm);
+ }
+ if (hlimp == NULL)
+ {
+ log(LOG_WARNING, 0,
+ "failed to get receiving hop limit");
+ return;
+ }
+
+ /* TODO: too noisy. Remove it? */
+//#define NOSUCHDEF
+#ifdef NOSUCHDEF
+ IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type,
+ mldh->mld6_code))
+ log(LOG_DEBUG, 0, "RECV %s from %s to %s",
+ packet_kind(IPPROTO_ICMPV6,
+ mldh->mld6_type, mldh->mld6_code),
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
+#endif /* NOSUCHDEF */
+
+ /* for an mtrace message, we don't need strict checks */
+ if (mldh->mld6_type == MLD6_MTRACE) {
+ accept_mtrace(src, dst, group, ifindex, (char *)(mldh + 1),
+ mldh->mld6_code, recvlen - sizeof(struct mld6_hdr));
+ return;
+ }
+
+ /* hop limit check */
+ if (*hlimp != 1)
+ {
+ log(LOG_WARNING, 0,
+ "received an MLD6 message with illegal hop limit(%d) from %s",
+ *hlimp, inet6_fmt(&src->sin6_addr));
+ /* but accept the packet */
+ }
+ if (ifindex == 0)
+ {
+ log(LOG_WARNING, 0, "failed to get receiving interface");
+ return;
+ }
+
+ /* scope check */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr))
+ {
+ log(LOG_INFO, 0,
+ "RECV %s with an invalid scope: %s from %s",
+ inet6_fmt(&mldh->mld6_addr),
+ inet6_fmt(&src->sin6_addr));
+ return; /* discard */
+ }
+
+ /* source address check */
+ if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ {
+ log(LOG_INFO, 0,
+ "RECV %s from a non link local address: %s",
+ packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
+ mldh->mld6_code),
+ inet6_fmt(&src->sin6_addr));
+ return;
+ }
+
+ switch (mldh->mld6_type)
+ {
+ case MLD6_LISTENER_QUERY:
+ accept_listener_query(src, dst, group,
+ ntohs(mldh->mld6_maxdelay));
+ return;
+
+ case MLD6_LISTENER_REPORT:
+ accept_listener_report(src, dst, group);
+ return;
+
+ case MLD6_LISTENER_DONE:
+ accept_listener_done(src, dst, group);
+ return;
+
+ default:
+ /* This must be impossible since we set a type filter */
+ log(LOG_INFO, 0,
+ "ignoring unknown ICMPV6 message type %x from %s to %s",
+ mldh->mld6_type, inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ return;
+ }
+}
+
+static void
+make_mld6_msg(type, code, src, dst, group, ifindex, delay, datalen, alert)
+ int type, code, ifindex, delay, datalen, alert;
+ struct sockaddr_in6 *src, *dst;
+ struct in6_addr *group;
+{
+ static struct sockaddr_in6 dst_sa = {sizeof(dst_sa), AF_INET6};
+ struct mld6_hdr *mhp = (struct mld6_hdr *)mld6_send_buf;
+ int ctllen, hbhlen = 0;
+
+ switch(type) {
+ case MLD6_MTRACE:
+ case MLD6_MTRACE_RESP:
+ sndmh.msg_name = (caddr_t)dst;
+ break;
+ default:
+ if (IN6_IS_ADDR_UNSPECIFIED(group))
+ dst_sa.sin6_addr = allnodes_group.sin6_addr;
+ else
+ dst_sa.sin6_addr = *group;
+ sndmh.msg_name = (caddr_t)&dst_sa;
+ datalen = sizeof(struct mld6_hdr);
+ break;
+ }
+
+ bzero(mhp, sizeof(*mhp));
+ mhp->mld6_type = type;
+ mhp->mld6_code = code;
+ mhp->mld6_maxdelay = htons(delay);
+ mhp->mld6_addr = *group;
+
+ sndiov[0].iov_len = datalen;
+
+ /* estimate total ancillary data length */
+ ctllen = 0;
+ if (ifindex != -1 || src)
+ ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
+ if (alert) {
+#ifdef USE_RFC2292BIS
+ if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_init(0) failed");
+ if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
+ 2, NULL)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_append(0) failed");
+ if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_finish(0) failed");
+#else /* old advanced API */
+ hbhlen = inet6_option_space(sizeof(raopt));
+#endif
+ ctllen += CMSG_SPACE(hbhlen);
+ }
+ /* extend ancillary data space (if necessary) */
+ if (ctlbuflen < ctllen) {
+ if (sndcmsgbuf)
+ free(sndcmsgbuf);
+ if ((sndcmsgbuf = malloc(ctllen)) == NULL)
+ log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */
+ ctlbuflen = ctllen;
+ }
+ /* store ancillary data */
+ if ((sndmh.msg_controllen = ctllen) > 0) {
+ struct cmsghdr *cmsgp;
+
+ sndmh.msg_control = sndcmsgbuf;
+ cmsgp = CMSG_FIRSTHDR(&sndmh);
+
+ if (ifindex != -1 || src) {
+ struct in6_pktinfo *pktinfo;
+
+ cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ memset((caddr_t)pktinfo, 0, sizeof(*pktinfo));
+ if (ifindex != -1)
+ pktinfo->ipi6_ifindex = ifindex;
+ if (src)
+ pktinfo->ipi6_addr = src->sin6_addr;
+ cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
+ }
+ if (alert) {
+#ifdef USE_RFC2292BIS
+ int currentlen;
+ void *hbhbuf, *optp = NULL;
+
+ cmsgp->cmsg_len = CMSG_SPACE(hbhlen);
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPOPTS;
+ hbhbuf = CMSG_DATA(cmsgp);
+
+ if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
+ log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed",
+ hbhlen);
+ if ((currentlen = inet6_opt_append(hbhbuf, hbhlen,
+ currentlen,
+ IP6OPT_ROUTER_ALERT, 2,
+ 2, &optp)) == -1)
+ log(LOG_ERR, 0,
+ "inet6_opt_append(len = %d) failed",
+ currentlen, hbhlen);
+ (void)inet6_opt_set_val(optp, 0, &rtalert_code,
+ sizeof(rtalert_code));
+ if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1)
+ log(LOG_ERR, 0, "inet6_opt_finish(buf) failed");
+#else /* old advanced API */
+ if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
+ log(LOG_ERR, 0, /* assert */
+ "make_mld6_msg: inet6_option_init failed");
+ if (inet6_option_append(cmsgp, raopt, 4, 0))
+ log(LOG_ERR, 0, /* assert */
+ "make_mld6_msg: inet6_option_append failed");
+#endif
+ cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
+ }
+ }
+ else
+ sndmh.msg_control = NULL; /* clear for safety */
+}
+
+void
+send_mld6(type, code, src, dst, group, index, delay, datalen, alert)
+ int type;
+ int code; /* for trace packets only */
+ struct sockaddr_in6 *src;
+ struct sockaddr_in6 *dst; /* may be NULL */
+ struct in6_addr *group;
+ int index, delay, alert;
+ int datalen; /* for trace packets only */
+{
+ int setloop = 0;
+ struct sockaddr_in6 *dstp;
+
+ make_mld6_msg(type, code, src, dst, group, index, delay, datalen, alert);
+ dstp = (struct sockaddr_in6 *)sndmh.msg_name;
+ if (IN6_ARE_ADDR_EQUAL(&dstp->sin6_addr, &allnodes_group.sin6_addr)) {
+ setloop = 1;
+ k_set_loop(mld6_socket, TRUE);
+ }
+ if (sendmsg(mld6_socket, &sndmh, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(log_level(IPPROTO_ICMPV6, type, 0), errno,
+ "sendmsg to %s with src %s on %s",
+ inet6_fmt(&dstp->sin6_addr),
+ src ? inet6_fmt(&src->sin6_addr) : "(unspec)",
+ ifindex2str(index));
+
+ if (setloop)
+ k_set_loop(mld6_socket, FALSE);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, 0))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ packet_kind(IPPROTO_ICMPV6, type, 0),
+ src ? inet6_fmt(&src->sin6_addr) : "unspec",
+ inet6_fmt(&dstp->sin6_addr));
+}
diff --git a/usr.sbin/pim6sd/mld6.h b/usr.sbin/pim6sd/mld6.h
new file mode 100644
index 0000000..dcc43f1
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MLD6_H
+#define MLD6_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#define RECV_BUF_SIZE 64*1024
+#define SO_RECV_BUF_SIZE_MAX 256*1024
+#define SO_RECV_BUF_SIZE_MIN 48*1024
+#define MINHLIM 1
+
+
+/*
+ * Constans for Multicast Listener Discovery protocol for IPv6.
+ */
+#define MLD6_ROBUSTNESS_VARIABLE 2
+#define MLD6_QUERY_INTERVAL 125 /* in seconds */
+#define MLD6_QUERY_RESPONSE_INTERVAL 10000 /* in milliseconds */
+#ifndef MLD6_TIMER_SCALE
+#define MLD6_TIMER_SCALE 1000
+
+#endif
+#define MLD6_LISTENER_INTERVAL (MLD6_ROBUSTNESS_VARIABLE * \
+ MLD6_QUERY_INTERVAL + \
+ MLD6_QUERY_RESPONSE_INTERVAL / MLD6_TIMER_SCALE)
+#define MLD6_LAST_LISTENER_QUERY_INTERVAL 1000 /* in milliseconds */
+#define MLD6_LAST_LISTENER_QUERY_COUNT MLD6_ROBUSTNESS_VARIABLE
+
+extern int mld6_socket;
+extern char *mld6_recv_buf;
+extern struct sockaddr_in6 allrouters_group;
+extern struct sockaddr_in6 allnodes_group;
+extern char *mld6_send_buf;
+
+void init_mld6 __P(());
+void send_mld6 __P((int type, int code, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, struct in6_addr *group,
+ int index, int delay, int datalen, int alert));
+
+#endif
diff --git a/usr.sbin/pim6sd/mld6_proto.c b/usr.sbin/pim6sd/mld6_proto.c
new file mode 100644
index 0000000..4533aac
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6_proto.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet/icmp6.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "mld6.h"
+#include "vif.h"
+#include "debug.h"
+#include "inet6.h"
+#include "route.h"
+#include "callout.h"
+#include "timer.h"
+
+extern struct in6_addr in6addr_any;
+
+typedef struct
+{
+ mifi_t mifi;
+ struct listaddr *g;
+ int q_time;
+} cbk_t;
+
+
+/*
+ * Forward declarations.
+ */
+static void DelVif __P((void *arg));
+static int SetTimer __P((int mifi, struct listaddr * g));
+static int DeleteTimer __P((int id));
+static void SendQuery __P((void *arg));
+static int SetQueryTimer
+__P((struct listaddr * g, int mifi, int to_expire,
+ int q_time));
+
+/*
+ * Send group membership queries on that interface if I am querier.
+ */
+void
+query_groups(v)
+ register struct uvif *v;
+{
+ register struct listaddr *g;
+
+ v->uv_gq_timer = MLD6_QUERY_INTERVAL;
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, (struct in6_addr *)&in6addr_any, v->uv_ifindex,
+ MLD6_QUERY_RESPONSE_INTERVAL, 0, 1);
+ v->uv_out_mld_query++;
+ }
+
+ /*
+ * Decrement the old-hosts-present timer for each active group on that
+ * vif.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (g->al_old > timer_interval)
+ g->al_old -= timer_interval;
+ else
+ g->al_old = 0;
+}
+
+
+/*
+ * Process an incoming host membership query
+ */
+void
+accept_listener_query(src, dst, group, tmo)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst,
+ *group;
+ int tmo;
+{
+ register int mifi;
+ register struct uvif *v;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ /* Ignore my own membership query */
+ if (local_address(src) != NO_VIF)
+ return;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_query: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accepting multicast listener query: "
+ "src %s, dst %s, grp %s",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
+ inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_query++;
+
+ if (v->uv_querier == NULL || inet6_equal(&v->uv_querier->al_addr, src))
+ {
+ /*
+ * This might be: - A query from a new querier, with a lower source
+ * address than the current querier (who might be me) - A query from
+ * a new router that just started up and doesn't know who the querier
+ * is. - A query from the current querier
+ */
+
+ if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
+ : &v->uv_linklocal->pa_addr)))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0, "new querier %s (was %s) "
+ "on mif %d",
+ inet6_fmt(&src->sin6_addr),
+ v->uv_querier ?
+ inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
+ "me", mifi);
+ if (!v->uv_querier)
+ {
+ v->uv_querier = (struct listaddr *)malloc(sizeof(struct listaddr));
+ v->uv_querier->al_next = (struct listaddr *) NULL;
+ v->uv_querier->al_timer = 0;
+ v->uv_querier->al_genid = 0;
+ v->uv_querier->al_pv = 0;
+ v->uv_querier->al_mv = 0;
+ v->uv_querier->al_old = 0;
+ v->uv_querier->al_index = 0;
+ v->uv_querier->al_timerid = 0;
+ v->uv_querier->al_query = 0;
+ v->uv_querier->al_flags = 0;
+
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ v->uv_querier->al_addr = *src;
+ time(&v->uv_querier->al_ctime);
+ }
+ }
+
+ /*
+ * Reset the timer since we've received a query.
+ */
+ if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
+ v->uv_querier->al_timer = 0;
+
+ /*
+ * If this is a Group-Specific query which we did not source, we must set
+ * our membership timer to [Last Member Query Count] * the [Max Response
+ * Time] in the packet.
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
+ inet6_equal(src, &v->uv_linklocal->pa_addr))
+ {
+ register struct listaddr *g;
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "%s for %s from %s on mif %d, timer %d",
+ "Group-specific membership query",
+ inet6_fmt(group),
+ inet6_fmt(&src->sin6_addr), mifi, tmo);
+
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(&group_sa, &g->al_addr)
+ && g->al_query == 0)
+ {
+ /* setup a timeout to remove the group membership */
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
+ tmo / MLD6_TIMER_SCALE;
+ /*
+ * use al_query to record our presence in last-member state
+ */
+ g->al_query = -1;
+ g->al_timerid = SetTimer(mifi, g);
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "timer for grp %s on mif %d "
+ "set to %d",
+ inet6_fmt(group),
+ mifi, g->al_timer);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void
+accept_listener_report(src, dst, group)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst,
+ *group;
+{
+ register mifi_t mifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_report: group(%s) has the "
+ "link-local scope. discard", inet6_fmt(group));
+ return;
+ }
+
+ if ((mifi = find_vif_direct_local(src)) == NO_VIF)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_report: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accepting multicast listener report: "
+ "src %s,dst %s, grp %s",
+ inet6_fmt(&src->sin6_addr),inet6_fmt(dst),
+ inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_report++;
+
+ /*
+ * Look for the group in our group list; if found, reset its timer.
+ */
+
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(&group_sa, &g->al_addr))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG,0,
+ "The group already exist");
+
+ g->al_reporter = *src;
+
+ /* delete old timers, set a timer for expiration */
+
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ if (g->al_query)
+ g->al_query = DeleteTimer(g->al_query);
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timerid = SetTimer(mifi, g);
+ add_leaf(mifi, NULL, &group_sa);
+ break;
+ }
+ }
+
+ /*
+ * If not found, add it to the list and update kernel cache.
+ */
+ if (g == NULL)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG,0,
+ "The group don't exist , trying to add it");
+
+ g = (struct listaddr *) malloc(sizeof(struct listaddr));
+ if (g == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ g->al_addr = group_sa;
+ g->al_old = 0;
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = MLD6_LISTENER_INTERVAL;
+ g->al_reporter = *src;
+ g->al_timerid = SetTimer(mifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+ time(&g->al_ctime);
+
+ add_leaf(mifi, NULL, &group_sa);
+ }
+}
+
+
+/* TODO: send PIM prune message if the last member? */
+void
+accept_listener_done(src, dst, group)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst,
+ *group;
+{
+ register mifi_t mifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+ struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
+
+ /* Don't create routing entries for the LAN scoped addresses */
+
+ if (IN6_IS_ADDR_MC_NODELOCAL(group)) /* sanity? */
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_done: address multicast node local(%s),"
+ " ignore it...", inet6_fmt(group));
+ return;
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(group))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "accept_listener_done: address multicast link local(%s), "
+ "ignore it ...", inet6_fmt(group));
+ return;
+ }
+
+ if ((mifi = find_vif_direct_local(src)) == NO_VIF)
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accept_listener_done: can't find a mif");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_INFO, 0,
+ "accepting listener done message: src %s, dst% s, grp %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst), inet6_fmt(group));
+
+ v = &uvifs[mifi];
+ v->uv_in_mld_done++;
+
+ if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
+ return;
+
+ /*
+ * Look for the group in our group list in order to set up a
+ * short-timeout query.
+ */
+ group_sa.sin6_addr = *group;
+ group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(&group_sa, &g->al_addr))
+ {
+ IF_DEBUG(DEBUG_MLD)
+ log(LOG_DEBUG, 0,
+ "[accept_done_message] %d %d \n",
+ g->al_old, g->al_query);
+
+ /*
+ * Ignore the done message if there are old hosts present
+ */
+ if (g->al_old)
+ return;
+
+ /*
+ * still waiting for a reply to a query, ignore the done
+ */
+ if (g->al_query)
+ return;
+
+ /** delete old timer set a timer for expiration **/
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+
+ /** send a group specific querry **/
+ g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE) *
+ (MLD6_LAST_LISTENER_QUERY_COUNT + 1);
+ if (v->uv_flags & VIFF_QUERIER &&
+ (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0,
+ &v->uv_linklocal->pa_addr, NULL,
+ &g->al_addr.sin6_addr,
+ v->uv_ifindex,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
+ v->uv_out_mld_query++;
+ }
+ g->al_query = SetQueryTimer(g, mifi,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE,
+ MLD6_LAST_LISTENER_QUERY_INTERVAL);
+ g->al_timerid = SetTimer(mifi, g);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Time out record of a group membership on a vif
+ */
+static void
+DelVif(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *) arg;
+ mifi_t mifi = cbk->mifi;
+ struct uvif *v = &uvifs[mifi];
+ struct listaddr *a,
+ **anp,
+ *g = cbk->g;
+
+ /*
+ * Group has expired delete all kernel cache entries with this group
+ */
+ if (g->al_query)
+ DeleteTimer(g->al_query);
+
+ delete_leaf(mifi, NULL, &g->al_addr);
+
+ /* increment statistics */
+ v->uv_listener_timo++;
+
+ anp = &(v->uv_groups);
+ while ((a = *anp) != NULL)
+ {
+ if (a == g)
+ {
+ *anp = a->al_next;
+ free((char *) a);
+ }
+ else
+ {
+ anp = &a->al_next;
+ }
+ }
+
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to delete the record of a group membership on a vif.
+ */
+static int
+SetTimer(mifi, g)
+ mifi_t mifi;
+ struct listaddr *g;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->mifi = mifi;
+ cbk->g = g;
+ return timer_setTimer(g->al_timer, DelVif, cbk);
+}
+
+
+/*
+ * Delete a timer that was set above.
+ */
+static int
+DeleteTimer(id)
+ int id;
+{
+ timer_clearTimer(id);
+ return 0;
+}
+
+
+/*
+ * Send a group-specific query.
+ */
+static void
+SendQuery(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *) arg;
+ register struct uvif *v = &uvifs[cbk->mifi];
+
+ if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
+ send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
+ NULL, &cbk->g->al_addr.sin6_addr, v->uv_ifindex,
+ cbk->q_time, 0, 1);
+ v->uv_out_mld_query++;
+ }
+ cbk->g->al_query = 0;
+ free(cbk);
+}
+
+
+/*
+ * Set a timer to send a group-specific query.
+ */
+static int
+SetQueryTimer(g, mifi, to_expire, q_time)
+ struct listaddr *g;
+ mifi_t mifi;
+ int to_expire;
+ int q_time;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->q_time = q_time;
+ cbk->mifi = mifi;
+ return timer_setTimer(to_expire, SendQuery, cbk);
+}
+
+/*
+ * Checks for MLD listener: returns TRUE if there is a receiver for the group
+ * on the given uvif, or returns FALSE otherwise.
+ */
+int
+check_multicast_listener(v, group)
+ struct uvif *v;
+ struct sockaddr_in6 *group;
+{
+ register struct listaddr *g;
+
+ /*
+ * Look for the group in our listener list;
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ {
+ if (inet6_equal(group, &g->al_addr))
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/usr.sbin/pim6sd/mld6_proto.h b/usr.sbin/pim6sd/mld6_proto.h
new file mode 100644
index 0000000..644eb8d
--- /dev/null
+++ b/usr.sbin/pim6sd/mld6_proto.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MLD6_PROTO_H
+#define MLD6_PROTO_H
+
+#include "vif.h"
+
+extern void query_groups __P((struct uvif *v));
+extern int check_grp_membership __P((struct uvif *v,
+ struct sockaddr_in6 *group));
+extern void accept_listener_query __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group,
+ int tmo));
+extern void accept_listener_report __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group));
+extern void accept_listener_done __P((struct sockaddr_in6 *src,
+ struct in6_addr *dst,
+ struct in6_addr *group));
+extern int check_multicast_listener __P((struct uvif *v,
+ struct sockaddr_in6 *group));
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/mrt.c b/usr.sbin/pim6sd/mrt.c
new file mode 100644
index 0000000..ae21aa3
--- /dev/null
+++ b/usr.sbin/pim6sd/mrt.c
@@ -0,0 +1,1495 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mrt.h"
+#include "vif.h"
+#include "rp.h"
+#include "pimd.h"
+#include "debug.h"
+#include "mld6.h"
+#include "inet6.h"
+#include "timer.h"
+#include "route.h"
+#include "kern.h"
+
+srcentry_t *srclist;
+grpentry_t *grplist;
+
+/*
+ * Local functions definition
+ */
+static srcentry_t *create_srcentry __P((struct sockaddr_in6 *source));
+static int search_srclist __P((struct sockaddr_in6 *source ,
+ srcentry_t ** sourceEntry));
+
+static int search_srcmrtlink __P((srcentry_t * srcentry_ptr,
+ struct sockaddr_in6 *group,
+ mrtentry_t ** mrtPtr));
+
+static void insert_srcmrtlink __P((mrtentry_t * elementPtr,
+ mrtentry_t * insertPtr,
+ srcentry_t * srcListPtr));
+
+static grpentry_t *create_grpentry __P((struct sockaddr_in6 *group));
+
+static int search_grplist __P((struct sockaddr_in6 *group,
+ grpentry_t ** groupEntry));
+
+static int search_grpmrtlink __P((grpentry_t * grpentry_ptr,
+ struct sockaddr_in6 *source,
+ mrtentry_t ** mrtPtr));
+
+static void insert_grpmrtlink __P((mrtentry_t * elementPtr,
+ mrtentry_t * insertPtr,
+ grpentry_t * grpListPtr));
+
+static mrtentry_t *alloc_mrtentry __P((srcentry_t * srcentry_ptr,
+ grpentry_t * grpentry_ptr));
+
+static mrtentry_t *create_mrtentry __P((srcentry_t * srcentry_ptr,
+ grpentry_t * grpentry_ptr,
+ u_int16 flags));
+
+static void move_kernel_cache __P((mrtentry_t * mrtentry_ptr,
+ u_int16 flags));
+
+void
+init_pim6_mrt()
+{
+
+ /* TODO: delete any existing routing table */
+
+ /* Initialize the source list */
+ /* The first entry has address 'IN6ADDR_ANY' and is not used */
+ /* The order is the smallest address first. */
+
+ srclist = (srcentry_t *) malloc(sizeof(srcentry_t));
+ srclist->next = (srcentry_t *) NULL;
+ srclist->prev = (srcentry_t *) NULL;
+ memset(&srclist->address, 0, sizeof(struct sockaddr_in6));
+ srclist->address.sin6_len = sizeof(struct sockaddr_in6);
+ srclist->address.sin6_family = AF_INET6;
+ srclist->mrtlink = (mrtentry_t *) NULL;
+ srclist->incoming = NO_VIF;
+ srclist->upstream = (pim_nbr_entry_t *) NULL;
+ srclist->metric = 0;
+ srclist->preference = 0;
+ RESET_TIMER(srclist->timer);
+ srclist->cand_rp = (cand_rp_t *) NULL;
+
+ /* Initialize the group list */
+ /* The first entry has address 'IN6ADDR_ANY' and is not used */
+ /* The order is the smallest address first. */
+
+ grplist = (grpentry_t *) malloc(sizeof(grpentry_t));
+ grplist->next = (grpentry_t *) NULL;
+ grplist->prev = (grpentry_t *) NULL;
+ grplist->rpnext = (grpentry_t *) NULL;
+ grplist->rpprev = (grpentry_t *) NULL;
+ memset(&grplist->group, 0, sizeof(struct sockaddr_in6));
+ grplist->group.sin6_len = sizeof(struct sockaddr_in6);
+ grplist->group.sin6_family = AF_INET6;
+ memset(&grplist->rpaddr, 0, sizeof(struct sockaddr_in6));
+ grplist->rpaddr.sin6_len = sizeof(struct sockaddr_in6);
+ grplist->rpaddr.sin6_family = AF_INET6;
+ grplist->mrtlink = (mrtentry_t *) NULL;
+ grplist->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grplist->grp_route = (mrtentry_t *) NULL;
+}
+
+
+grpentry_t *
+find_group(group)
+ struct sockaddr_in6 *group;
+{
+ grpentry_t *grpentry_ptr;
+
+ if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
+ return (grpentry_t *) NULL;
+
+ if (search_grplist(group, &grpentry_ptr) == TRUE)
+ {
+ /* Group found! */
+ return (grpentry_ptr);
+ }
+ return (grpentry_t *) NULL;
+}
+
+
+srcentry_t *
+find_source(source)
+ struct sockaddr_in6 *source;
+{
+ srcentry_t *srcentry_ptr;
+
+ if (!inet6_valid_host(source))
+ return (srcentry_t *) NULL;
+
+ if (search_srclist(source, &srcentry_ptr) == TRUE)
+ {
+ /* Source found! */
+ return (srcentry_ptr);
+ }
+ return (srcentry_t *) NULL;
+}
+
+
+mrtentry_t *
+find_route(source, group, flags, create)
+ struct sockaddr_in6 *source,
+ *group;
+ u_int16 flags;
+ char create;
+{
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_ptr_wc;
+ mrtentry_t *mrtentry_ptr_pmbr;
+ mrtentry_t *mrtentry_ptr_2;
+ rpentry_t *rpentry_ptr=NULL;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+ if (!IN6_IS_ADDR_MULTICAST(&group->sin6_addr))
+ return (mrtentry_t *) NULL;
+ }
+
+ if (flags & MRTF_SG)
+ if (!inet6_valid_host(source))
+ return (mrtentry_t *) NULL;
+
+ if (create == DONT_CREATE)
+ {
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+ if (search_grplist(group, &grpentry_ptr) == FALSE)
+ {
+ /* Group not found. Return the (*,*,RP) entry */
+ if (flags & MRTF_PMBR)
+ {
+ rpentry_ptr = rp_match(group);
+ if (rpentry_ptr != (rpentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ }
+ return (mrtentry_t *) NULL;
+ }
+ /* Search for the source */
+ if (flags & MRTF_SG)
+ {
+ if (search_grpmrtlink(grpentry_ptr, source,
+ &mrtentry_ptr) == TRUE)
+ {
+ /* Exact (S,G) entry found */
+ return (mrtentry_ptr);
+ }
+ }
+ /* No (S,G) entry. Return the (*,G) entry (if exist) */
+ if ((flags & MRTF_WC) &&
+ (grpentry_ptr->grp_route != (mrtentry_t *) NULL))
+ return (grpentry_ptr->grp_route);
+ }
+
+ /* Return the (*,*,RP) entry */
+
+ if (flags & MRTF_PMBR)
+ {
+ rpentry_ptr = (rpentry_t *) NULL;
+ if (group != NULL)
+ rpentry_ptr = rp_match(group);
+ else
+ if (source != NULL)
+ rpentry_ptr = rp_find(source);
+ if (rpentry_ptr != (rpentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+
+ /* Creation allowed */
+
+ if (flags & (MRTF_SG | MRTF_WC))
+ {
+
+ grpentry_ptr = create_grpentry(group);
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ {
+ return (mrtentry_t *) NULL;
+ }
+
+ if (grpentry_ptr->active_rp_grp == (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr = rp_grp_match(group);
+
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ {
+ if ((grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route == (mrtentry_t *) NULL))
+ {
+ /* New created grpentry. Delete it. */
+
+ delete_grpentry(grpentry_ptr);
+ }
+
+ return (mrtentry_t *) NULL;
+ }
+
+ rpentry_ptr = rp_grp_entry_ptr->rp->rpentry;
+ grpentry_ptr->active_rp_grp = rp_grp_entry_ptr;
+ grpentry_ptr->rpaddr = rpentry_ptr->address;
+
+ /* Link to the top of the rp_grp_chain */
+
+ grpentry_ptr->rpnext = rp_grp_entry_ptr->grplink;
+ rp_grp_entry_ptr->grplink = grpentry_ptr;
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr;
+ }
+ else
+ rpentry_ptr = grpentry_ptr->active_rp_grp->rp->rpentry;
+ }
+
+ mrtentry_ptr_wc = mrtentry_ptr_pmbr = (mrtentry_t *) NULL;
+
+ if (flags & MRTF_WC)
+ {
+ /* Setup the (*,G) routing entry */
+
+ mrtentry_ptr_wc = create_mrtentry((srcentry_t *) NULL, grpentry_ptr,
+ MRTF_WC);
+
+ if (mrtentry_ptr_wc == (mrtentry_t *) NULL)
+ {
+ if (grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ {
+ /* New created grpentry. Delete it. */
+
+ delete_grpentry(grpentry_ptr);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+ if (mrtentry_ptr_wc->flags & MRTF_NEW)
+ {
+ mrtentry_ptr_pmbr = rpentry_ptr->mrtlink;
+
+ /* Copy the oif list from the (*,*,RP) entry */
+
+ if (mrtentry_ptr_pmbr != (mrtentry_t *) NULL)
+ {
+ VOIF_COPY(mrtentry_ptr_pmbr, mrtentry_ptr_wc);
+ }
+
+ mrtentry_ptr_wc->incoming = rpentry_ptr->incoming;
+ mrtentry_ptr_wc->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr_wc->metric = rpentry_ptr->metric;
+ mrtentry_ptr_wc->preference = rpentry_ptr->preference;
+ move_kernel_cache(mrtentry_ptr_wc, 0);
+
+#ifdef RSRR
+ rsrr_cache_bring_up(mrtentry_ptr_wc);
+#endif /* RSRR */
+
+ }
+
+ if (!(flags & MRTF_SG))
+ {
+ return (mrtentry_ptr_wc);
+ }
+ }
+
+ if (flags & MRTF_SG)
+ {
+ /* Setup the (S,G) routing entry */
+ srcentry_ptr = create_srcentry(source);
+ if (srcentry_ptr == (srcentry_t *) NULL)
+ {
+ /* TODO: XXX: The MRTF_NEW flag check may be misleading?? check */
+
+ if (((grpentry_ptr->grp_route == (mrtentry_t *) NULL)
+ || ((grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route->flags & MRTF_NEW)))
+ && (grpentry_ptr->mrtlink == (mrtentry_t *) NULL))
+ {
+ /* New created grpentry. Delete it. */
+ delete_grpentry(grpentry_ptr);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+ mrtentry_ptr = create_mrtentry(srcentry_ptr, grpentry_ptr, MRTF_SG);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ if (((grpentry_ptr->grp_route == (mrtentry_t *) NULL)
+ || ((grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (grpentry_ptr->grp_route->flags & MRTF_NEW)))
+ && (grpentry_ptr->mrtlink == (mrtentry_t *) NULL))
+ {
+ /* New created grpentry. Delete it. */
+ delete_grpentry(grpentry_ptr);
+ }
+ if (srcentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ {
+ /* New created srcentry. Delete it. */
+ delete_srcentry(srcentry_ptr);
+ }
+ return (mrtentry_t *) NULL;
+ }
+
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ {
+ if ((mrtentry_ptr_2 = grpentry_ptr->grp_route)
+ == (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr_2 = rpentry_ptr->mrtlink;
+ }
+ /* Copy the oif list from the existing (*,G) or (*,*,RP) entry */
+ if (mrtentry_ptr_2 != (mrtentry_t *) NULL)
+ {
+ VOIF_COPY(mrtentry_ptr_2, mrtentry_ptr);
+ if (flags & MRTF_RP)
+ {
+ /* ~(S,G) prune entry */
+ mrtentry_ptr->incoming = mrtentry_ptr_2->incoming;
+ mrtentry_ptr->upstream = mrtentry_ptr_2->upstream;
+ mrtentry_ptr->metric = mrtentry_ptr_2->metric;
+ mrtentry_ptr->preference = mrtentry_ptr_2->preference;
+ mrtentry_ptr->flags |= MRTF_RP;
+ }
+ }
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ mrtentry_ptr->incoming = srcentry_ptr->incoming;
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+ }
+ move_kernel_cache(mrtentry_ptr, 0);
+#ifdef RSRR
+ rsrr_cache_bring_up(mrtentry_ptr);
+#endif /* RSRR */
+ }
+ return (mrtentry_ptr);
+ }
+
+ if (flags & MRTF_PMBR)
+ {
+ /* Get/return the (*,*,RP) routing entry */
+
+ if (group != NULL)
+ rpentry_ptr = rp_match(group);
+ else
+ if (source != NULL)
+ {
+ rpentry_ptr = rp_find(source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ {
+ return (mrtentry_t *) NULL;
+ }
+ }
+ else
+ return (mrtentry_t *) NULL; /* source == group ==
+ * IN6ADDR_ANY */
+
+ if (rpentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ return (rpentry_ptr->mrtlink);
+ mrtentry_ptr = create_mrtentry(rpentry_ptr, (grpentry_t *) NULL,
+ MRTF_PMBR);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ mrtentry_ptr->incoming = rpentry_ptr->incoming;
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ return (mrtentry_ptr);
+ }
+
+ return (mrtentry_t *) NULL;
+}
+
+
+void
+delete_srcentry(srcentry_ptr)
+ srcentry_t *srcentry_ptr;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_next;
+
+ if (srcentry_ptr == (srcentry_t *) NULL)
+ return;
+
+ /* TODO: XXX: the first entry is unused and always there */
+
+ srcentry_ptr->prev->next = srcentry_ptr->next;
+ if (srcentry_ptr->next != (srcentry_t *) NULL)
+ srcentry_ptr->next->prev = srcentry_ptr->prev;
+
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_next)
+ {
+ mrtentry_next = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ /* Delete the kernel cache first */
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ if (mrtentry_ptr->grpprev != (mrtentry_t *) NULL)
+ mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
+ else
+ {
+ mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
+ if ((mrtentry_ptr->grpnext == (mrtentry_t *) NULL)
+ && (mrtentry_ptr->group->grp_route == (mrtentry_t *) NULL))
+ {
+ /* Delete the group entry if it has no (*,G) routing entry */
+ delete_grpentry(mrtentry_ptr->group);
+ }
+ }
+ if (mrtentry_ptr->grpnext != (mrtentry_t *) NULL)
+ mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
+ FREE_MRTENTRY(mrtentry_ptr);
+ }
+ free((char *) srcentry_ptr);
+}
+
+
+void
+delete_grpentry(grpentry_ptr)
+ grpentry_t *grpentry_ptr;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_next;
+
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ return;
+
+ /* TODO: XXX: the first entry is unused and always there */
+
+ grpentry_ptr->prev->next = grpentry_ptr->next;
+ if (grpentry_ptr->next != (grpentry_t *) NULL)
+ grpentry_ptr->next->prev = grpentry_ptr->prev;
+
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ {
+ if (grpentry_ptr->grp_route->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(grpentry_ptr->grp_route);
+ FREE_MRTENTRY(grpentry_ptr->grp_route);
+ }
+
+ /* Delete from the rp_grp_entry chain */
+ if (grpentry_ptr->active_rp_grp != (rp_grp_entry_t *) NULL)
+ {
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr->rpprev;
+ if (grpentry_ptr->rpprev != (grpentry_t *) NULL)
+ grpentry_ptr->rpprev->rpnext = grpentry_ptr->rpnext;
+ else
+ grpentry_ptr->active_rp_grp->grplink = grpentry_ptr->rpnext;
+ }
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_next)
+ {
+ mrtentry_next = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ /* Delete the kernel cache first */
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ if (mrtentry_ptr->srcprev != (mrtentry_t *) NULL)
+ mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
+ else
+ {
+ mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->srcnext == (mrtentry_t *) NULL)
+ {
+ /* Delete the srcentry if this was the last routing entry */
+ delete_srcentry(mrtentry_ptr->source);
+ }
+ }
+ if (mrtentry_ptr->srcnext != (mrtentry_t *) NULL)
+ mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
+ FREE_MRTENTRY(mrtentry_ptr);
+ }
+ free((char *) grpentry_ptr);
+}
+
+
+void
+delete_mrtentry(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_wc;
+ mrtentry_t *mrtentry_rp;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ /* Delete the kernel cache first */
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+
+#ifdef RSRR
+ /* Tell the reservation daemon */
+ rsrr_cache_clean(mrtentry_ptr);
+#endif /* RSRR */
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) mrtentry */
+ mrtentry_ptr->source->mrtlink = (mrtentry_t *) NULL;
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) mrtentry */
+ /* Delete from the grpentry MRT chain */
+ if (mrtentry_ptr->grpprev != (mrtentry_t *) NULL)
+ mrtentry_ptr->grpprev->grpnext = mrtentry_ptr->grpnext;
+ else
+ {
+ mrtentry_ptr->group->mrtlink = mrtentry_ptr->grpnext;
+ if (mrtentry_ptr->grpnext == (mrtentry_t *) NULL)
+ {
+ /*
+ * All (S,G) MRT entries are gone. Allow creating (*,G)
+ * MFC entries.
+ */
+ mrtentry_rp
+ = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ mrtentry_wc = mrtentry_ptr->group->grp_route;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ mrtentry_rp->flags &= ~MRTF_MFC_CLONE_SG;
+ if (mrtentry_wc != (mrtentry_t *) NULL)
+ mrtentry_wc->flags &= ~MRTF_MFC_CLONE_SG;
+ else
+ {
+ /*
+ * Delete the group entry if it has no (*,G) routing
+ * entry
+ */
+ delete_grpentry(mrtentry_ptr->group);
+ }
+ }
+ }
+ if (mrtentry_ptr->grpnext != (mrtentry_t *) NULL)
+ mrtentry_ptr->grpnext->grpprev = mrtentry_ptr->grpprev;
+
+ /* Delete from the srcentry MRT chain */
+ if (mrtentry_ptr->srcprev != (mrtentry_t *) NULL)
+ mrtentry_ptr->srcprev->srcnext = mrtentry_ptr->srcnext;
+ else
+ {
+ mrtentry_ptr->source->mrtlink = mrtentry_ptr->srcnext;
+ if (mrtentry_ptr->srcnext == (mrtentry_t *) NULL)
+ {
+ /* Delete the srcentry if this was the last routing entry */
+ delete_srcentry(mrtentry_ptr->source);
+ }
+ }
+ if (mrtentry_ptr->srcnext != (mrtentry_t *) NULL)
+ mrtentry_ptr->srcnext->srcprev = mrtentry_ptr->srcprev;
+ }
+ else
+ {
+ /* This mrtentry should be (*,G) */
+ grpentry_ptr = mrtentry_ptr->group;
+ grpentry_ptr->grp_route = (mrtentry_t *) NULL;
+
+ if (grpentry_ptr->mrtlink == (mrtentry_t *) NULL)
+ /* Delete the group entry if it has no (S,G) entries */
+ delete_grpentry(grpentry_ptr);
+ }
+
+ FREE_MRTENTRY(mrtentry_ptr);
+}
+
+
+static int
+search_srclist(source, sourceEntry)
+ struct sockaddr_in6 *source;
+ register srcentry_t **sourceEntry;
+{
+ register srcentry_t *s_prev,
+ *s;
+
+ for (s_prev = srclist, s = s_prev->next; s != (srcentry_t *) NULL;
+ s_prev = s, s = s->next)
+ {
+ /*
+ * The srclist is ordered with the smallest addresses first. The
+ * first entry is not used.
+ */
+ if (inet6_lessthan(&s->address, source))
+ continue;
+ if (inet6_equal(&s->address, source))
+ {
+ *sourceEntry = s;
+ return (TRUE);
+ }
+ break;
+ }
+ *sourceEntry = s_prev; /* The insertion point is between s_prev and
+ * s */
+ return (FALSE);
+}
+
+
+static int
+search_grplist(group, groupEntry)
+ struct sockaddr_in6 *group;
+ register grpentry_t **groupEntry;
+{
+ register grpentry_t *g_prev,
+ *g;
+
+ for (g_prev = grplist, g = g_prev->next; g != (grpentry_t *) NULL;
+ g_prev = g, g = g->next)
+ {
+ /*
+ * The grplist is ordered with the smallest address first. The first
+ * entry is not used.
+ */
+
+ if (inet6_lessthan(&g->group, group))
+ continue;
+ if (inet6_equal(&g->group, group))
+ {
+ *groupEntry = g;
+ return (TRUE);
+ }
+ break;
+ }
+ *groupEntry = g_prev; /* The insertion point is between g_prev and
+ * g */
+ return (FALSE);
+}
+
+
+static srcentry_t *
+create_srcentry(source)
+ struct sockaddr_in6 *source;
+{
+ register srcentry_t *srcentry_ptr;
+ srcentry_t *srcentry_prev;
+
+ if (search_srclist(source, &srcentry_prev) == TRUE)
+ return (srcentry_prev);
+
+ srcentry_ptr = (srcentry_t *) malloc(sizeof(srcentry_t));
+ if (srcentry_ptr == (srcentry_t *) NULL)
+ {
+ log(LOG_WARNING, 0, "Memory allocation error for srcentry %s",
+ inet6_fmt(&source->sin6_addr));
+ return (srcentry_t *) NULL;
+ }
+
+ srcentry_ptr->address = *source;
+ /*
+ * Free the memory if there is error getting the iif and the next hop
+ * (upstream) router.
+ */
+
+ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE)
+ {
+ free((char *) srcentry_ptr);
+ return (srcentry_t *) NULL;
+ }
+ srcentry_ptr->mrtlink = (mrtentry_t *) NULL;
+ RESET_TIMER(srcentry_ptr->timer);
+ srcentry_ptr->cand_rp = (cand_rp_t *) NULL;
+ srcentry_ptr->next = srcentry_prev->next;
+ srcentry_prev->next = srcentry_ptr;
+ srcentry_ptr->prev = srcentry_prev;
+ if (srcentry_ptr->next != (srcentry_t *) NULL)
+ srcentry_ptr->next->prev = srcentry_ptr;
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "create source entry, source %s",
+ inet6_fmt(&source->sin6_addr));
+ return (srcentry_ptr);
+}
+
+
+static grpentry_t *
+create_grpentry(group)
+ struct sockaddr_in6 *group;
+{
+ register grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_prev;
+
+ if (search_grplist(group, &grpentry_prev) == TRUE)
+ return (grpentry_prev);
+
+ grpentry_ptr = (grpentry_t *) malloc(sizeof(grpentry_t));
+
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ {
+ log(LOG_WARNING, 0, "Memory allocation error for grpentry %s",
+ inet6_fmt(&group->sin6_addr));
+ return (grpentry_t *) NULL;
+ }
+
+ /*
+ * TODO: XXX: Note that this is NOT a (*,G) routing entry, but simply a
+ * group entry, probably used to search the routing table (to find (S,G)
+ * entries for example.) To become (*,G) routing entry, we must setup
+ * grpentry_ptr->grp_route
+ */
+
+ grpentry_ptr->group = *group;
+ memset(&grpentry_ptr->rpaddr, 0, sizeof(struct sockaddr_in6));
+ grpentry_ptr->rpaddr.sin6_len = sizeof(struct sockaddr_in6);
+ grpentry_ptr->rpaddr.sin6_family = AF_INET6;
+ grpentry_ptr->mrtlink = (mrtentry_t *) NULL;
+ grpentry_ptr->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grpentry_ptr->grp_route = (mrtentry_t *) NULL;
+ grpentry_ptr->rpnext = (grpentry_t *) NULL;
+ grpentry_ptr->rpprev = (grpentry_t *) NULL;
+
+ /* Now it is safe to include the new group entry */
+
+ grpentry_ptr->next = grpentry_prev->next;
+ grpentry_prev->next = grpentry_ptr;
+ grpentry_ptr->prev = grpentry_prev;
+ if (grpentry_ptr->next != (grpentry_t *) NULL)
+ grpentry_ptr->next->prev = grpentry_ptr;
+
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "create group entry, group %s", inet6_fmt(&group->sin6_addr));
+ return (grpentry_ptr);
+}
+
+
+/*
+ * Return TRUE if the entry is found and then *mrtPtr is set to point to that
+ * entry. Otherwise return FALSE and *mrtPtr points the the previous entry
+ * (or NULL if first in the chain.
+ */
+static int
+search_srcmrtlink(srcentry_ptr, group, mrtPtr)
+ srcentry_t *srcentry_ptr;
+ struct sockaddr_in6 *group;
+ mrtentry_t **mrtPtr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ register mrtentry_t *m_prev = (mrtentry_t *) NULL;
+
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->srcnext)
+ {
+ /*
+ * The entries are ordered with the smaller group address first. The
+ * addresses are in network order.
+ */
+
+ if (inet6_lessthan(&mrtentry_ptr->group->group, group))
+ continue;
+ if (inet6_equal(&mrtentry_ptr->group->group, group))
+ {
+ *mrtPtr = mrtentry_ptr;
+ return (TRUE);
+ }
+ break;
+ }
+ *mrtPtr = m_prev;
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if the entry is found and then *mrtPtr is set to point to that
+ * entry. Otherwise return FALSE and *mrtPtr points the the previous entry
+ * (or NULL if first in the chain.
+ */
+static int
+search_grpmrtlink(grpentry_ptr, source, mrtPtr)
+ grpentry_t *grpentry_ptr;
+ struct sockaddr_in6 *source;
+ mrtentry_t **mrtPtr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ register mrtentry_t *m_prev = (mrtentry_t *) NULL;
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ m_prev = mrtentry_ptr, mrtentry_ptr = mrtentry_ptr->grpnext)
+ {
+ /*
+ * The entries are ordered with the smaller source address first. The
+ * addresses are in network order.
+ */
+
+ if (inet6_lessthan(&mrtentry_ptr->source->address, source))
+ continue;
+
+
+ if (inet6_equal(source, &mrtentry_ptr->source->address))
+ {
+ *mrtPtr = mrtentry_ptr;
+ return (TRUE);
+ }
+ break;
+ }
+ *mrtPtr = m_prev;
+ return (FALSE);
+}
+
+
+static void
+insert_srcmrtlink(mrtentry_new, mrtentry_prev, srcentry_ptr)
+ mrtentry_t *mrtentry_new;
+ mrtentry_t *mrtentry_prev;
+ srcentry_t *srcentry_ptr;
+{
+ if (mrtentry_prev == (mrtentry_t *) NULL)
+ {
+ /* Has to be insert as the head entry for this source */
+
+ mrtentry_new->srcnext = srcentry_ptr->mrtlink;
+ mrtentry_new->srcprev = (mrtentry_t *) NULL;
+ srcentry_ptr->mrtlink = mrtentry_new;
+ }
+ else
+ {
+ /* Insert right after the mrtentry_prev */
+
+ mrtentry_new->srcnext = mrtentry_prev->srcnext;
+ mrtentry_new->srcprev = mrtentry_prev;
+ mrtentry_prev->srcnext = mrtentry_new;
+ }
+ if (mrtentry_new->srcnext != (mrtentry_t *) NULL)
+ mrtentry_new->srcnext->srcprev = mrtentry_new;
+}
+
+
+static void
+insert_grpmrtlink(mrtentry_new, mrtentry_prev, grpentry_ptr)
+ mrtentry_t *mrtentry_new;
+ mrtentry_t *mrtentry_prev;
+ grpentry_t *grpentry_ptr;
+{
+ if (mrtentry_prev == (mrtentry_t *) NULL)
+ {
+ /* Has to be insert as the head entry for this group */
+
+ mrtentry_new->grpnext = grpentry_ptr->mrtlink;
+ mrtentry_new->grpprev = (mrtentry_t *) NULL;
+ grpentry_ptr->mrtlink = mrtentry_new;
+ }
+ else
+ {
+ /* Insert right after the mrtentry_prev */
+
+ mrtentry_new->grpnext = mrtentry_prev->grpnext;
+ mrtentry_new->grpprev = mrtentry_prev;
+ mrtentry_prev->grpnext = mrtentry_new;
+ }
+ if (mrtentry_new->grpnext != (mrtentry_t *) NULL)
+ mrtentry_new->grpnext->grpprev = mrtentry_new;
+}
+
+
+static mrtentry_t *
+alloc_mrtentry(srcentry_ptr, grpentry_ptr)
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+{
+ register mrtentry_t *mrtentry_ptr;
+ u_int16 i,
+ *i_ptr;
+ u_int8 vif_numbers;
+
+ mrtentry_ptr = (mrtentry_t *) malloc(sizeof(mrtentry_t));
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
+ return (mrtentry_t *) NULL;
+ }
+
+ /*
+ * grpnext, grpprev, srcnext, srcprev will be setup when we link the
+ * mrtentry to the source and group chains
+ */
+ mrtentry_ptr->source = srcentry_ptr;
+ mrtentry_ptr->group = grpentry_ptr;
+ mrtentry_ptr->incoming = NO_VIF;
+ IF_ZERO(&mrtentry_ptr->joined_oifs);
+ IF_ZERO(&mrtentry_ptr->leaves);
+ IF_ZERO(&mrtentry_ptr->pruned_oifs);
+ IF_ZERO(&mrtentry_ptr->asserted_oifs);
+ IF_ZERO(&mrtentry_ptr->oifs);
+ mrtentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ mrtentry_ptr->metric = 0;
+ mrtentry_ptr->preference = 0;
+ mrtentry_ptr->pmbr_addr.sin6_addr = in6addr_any;
+ mrtentry_ptr->pmbr_addr.sin6_len = sizeof(struct sockaddr_in6);
+ mrtentry_ptr->pmbr_addr.sin6_family = AF_INET6;
+
+#ifdef RSRR
+ mrtentry_ptr->rsrr_cache = (struct rsrr_cache *) NULL;
+#endif /* RSRR */
+
+ /*
+ * XXX: TODO: if we are short in memory, we can reserve as few as
+ * possible space for vif timers (per group and/or routing entry), but
+ * then everytime when a new interfaces is configured, the router will be
+ * restarted and will delete the whole routing table. The "memory is
+ * cheap" solution is to reserve timer space for all potential vifs in
+ * advance and then no need to delete the routing table and disturb the
+ * forwarding.
+ */
+
+#ifdef SAVE_MEMORY
+ mrtentry_ptr->vif_timers = (u_int16 *) malloc(sizeof(u_int16) * numvifs);
+ mrtentry_ptr->vif_deletion_delay =
+ (u_int16 *) malloc(sizeof(u_int16) * numvifs);
+ vif_numbers = numvifs;
+#else
+ mrtentry_ptr->vif_timers =
+ (u_int16 *) malloc(sizeof(u_int16) * total_interfaces);
+ mrtentry_ptr->vif_deletion_delay =
+ (u_int16 *) malloc(sizeof(u_int16) * total_interfaces);
+ vif_numbers = total_interfaces;
+#endif /* SAVE_MEMORY */
+ if ((mrtentry_ptr->vif_timers == (u_int16 *) NULL) ||
+ (mrtentry_ptr->vif_deletion_delay == (u_int16 *) NULL))
+ {
+ log(LOG_WARNING, 0, "alloc_mrtentry(): out of memory");
+ FREE_MRTENTRY(mrtentry_ptr);
+ return (mrtentry_t *) NULL;
+ }
+ /* Reset the timers */
+ for (i = 0, i_ptr = mrtentry_ptr->vif_timers; i < vif_numbers;
+ i++, i_ptr++)
+ RESET_TIMER(*i_ptr);
+ for (i = 0, i_ptr = mrtentry_ptr->vif_deletion_delay; i < vif_numbers;
+ i++, i_ptr++)
+ RESET_TIMER(*i_ptr);
+
+ mrtentry_ptr->flags = MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->timer);
+ RESET_TIMER(mrtentry_ptr->jp_timer);
+ RESET_TIMER(mrtentry_ptr->rs_timer);
+ RESET_TIMER(mrtentry_ptr->assert_timer);
+ RESET_TIMER(mrtentry_ptr->assert_rate_timer);
+ mrtentry_ptr->kernel_cache = (kernel_cache_t *) NULL;
+
+ return (mrtentry_ptr);
+}
+
+
+static mrtentry_t *
+create_mrtentry(srcentry_ptr, grpentry_ptr, flags)
+ srcentry_t *srcentry_ptr;
+ grpentry_t *grpentry_ptr;
+ u_int16 flags;
+{
+ mrtentry_t *r_new;
+ mrtentry_t *r_grp_insert,
+ *r_src_insert; /* pointers to insert */
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+
+
+ if (flags & MRTF_SG)
+ {
+ /* (S,G) entry */
+
+ source = &srcentry_ptr->address;
+ group = &grpentry_ptr->group;
+
+ if (search_grpmrtlink(grpentry_ptr, source, &r_grp_insert) == TRUE)
+ {
+ return (r_grp_insert);
+ }
+ if (search_srcmrtlink(srcentry_ptr, group, &r_src_insert) == TRUE)
+ {
+ /*
+ * Hmmm, search_grpmrtlink() didn't find the entry, but
+ * search_srcmrtlink() did find it! Shoudn't happen. Panic!
+ */
+
+ log(LOG_ERR, 0, "MRT inconsistency for src %s and grp %s\n",
+ inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr));
+ /* not reached but to make lint happy */
+ return (mrtentry_t *) NULL;
+ }
+ /*
+ * Create and insert in group mrtlink and source mrtlink chains.
+ */
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ /*
+ * r_new has to be insert right after r_grp_insert in the grp mrtlink
+ * chain and right after r_src_insert in the src mrtlink chain
+ */
+ insert_grpmrtlink(r_new, r_grp_insert, grpentry_ptr);
+ insert_srcmrtlink(r_new, r_src_insert, srcentry_ptr);
+ r_new->flags |= MRTF_SG;
+ return (r_new);
+ }
+
+ if (flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ return (grpentry_ptr->grp_route);
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ grpentry_ptr->grp_route = r_new;
+ r_new->flags |= (MRTF_WC | MRTF_RP);
+ return (r_new);
+ }
+
+ if (flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+
+ if (srcentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ return (srcentry_ptr->mrtlink);
+ r_new = alloc_mrtentry(srcentry_ptr, grpentry_ptr);
+ if (r_new == (mrtentry_t *) NULL)
+ return (mrtentry_t *) NULL;
+ srcentry_ptr->mrtlink = r_new;
+ r_new->flags |= (MRTF_PMBR | MRTF_RP);
+ return (r_new);
+ }
+
+ return (mrtentry_t *) NULL;
+}
+
+
+/*
+ * Delete all kernel cache for this mrtentry
+ */
+void
+delete_mrtentry_all_kernel_cache(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ kernel_cache_t *kernel_cache_prev;
+ kernel_cache_t *kernel_cache_ptr;
+
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ {
+ return;
+ }
+
+ /* Free all kernel_cache entries */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;)
+ {
+ kernel_cache_prev = kernel_cache_ptr;
+ kernel_cache_ptr = kernel_cache_ptr->next;
+ k_del_mfc(mld6_socket, &kernel_cache_prev->source,
+ &kernel_cache_prev->group);
+ free((char *) kernel_cache_prev);
+ }
+ mrtentry_ptr->kernel_cache = (kernel_cache_t *) NULL;
+
+ /* turn off the cache flag(s) */
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+}
+
+
+void
+delete_single_kernel_cache(mrtentry_ptr, kernel_cache_ptr)
+ mrtentry_t *mrtentry_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+{
+ if (kernel_cache_ptr->prev == (kernel_cache_t *) NULL)
+ {
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr->next;
+ if (mrtentry_ptr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ }
+ else
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s",
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr),
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr));
+ k_del_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ free((char *) kernel_cache_ptr);
+}
+
+
+void
+delete_single_kernel_cache_addr(mrtentry_ptr, source, group)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ kernel_cache_t *kernel_cache_ptr;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+
+ /* Find the exact (S,G) kernel_cache entry */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ if (inet6_lessthan(&kernel_cache_ptr->group, group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->group, group))
+ return; /* Not found */
+ if (inet6_lessthan(&kernel_cache_ptr->source, source))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->source, source))
+ return; /* Not found */
+ /* Found exact match */
+ break;
+ }
+
+ if (kernel_cache_ptr == (kernel_cache_t *) NULL)
+ return;
+
+ /* Found. Delete it */
+ if (kernel_cache_ptr->prev == (kernel_cache_t *) NULL)
+ {
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr->next;
+ if (mrtentry_ptr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_ptr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ }
+ else
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s",
+ inet6_fmt(&kernel_cache_ptr->source.sin6_addr),
+ inet6_fmt(&kernel_cache_ptr->group.sin6_addr));
+ k_del_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ free((char *) kernel_cache_ptr);
+}
+
+
+/*
+ * Installs kernel cache for (source, group). Assumes mrtentry_ptr is the
+ * correct entry.
+ */
+void
+add_kernel_cache(mrtentry_ptr, source, group, flags)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ u_int16 flags;
+{
+ kernel_cache_t *kernel_cache_next;
+ kernel_cache_t *kernel_cache_prev;
+ kernel_cache_t *kernel_cache_new;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ move_kernel_cache(mrtentry_ptr, flags);
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) */
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ return;
+ kernel_cache_new = (kernel_cache_t *) malloc(sizeof(kernel_cache_t));
+ kernel_cache_new->next = (kernel_cache_t *) NULL;
+ kernel_cache_new->prev = (kernel_cache_t *) NULL;
+ kernel_cache_new->source = *source;
+ kernel_cache_new->group = *group;
+ kernel_cache_new->sg_count.pktcnt = 0;
+ kernel_cache_new->sg_count.bytecnt = 0;
+ kernel_cache_new->sg_count.wrong_if = 0;
+ mrtentry_ptr->kernel_cache = kernel_cache_new;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+ return;
+ }
+
+ kernel_cache_prev = (kernel_cache_t *) NULL;
+
+ for (kernel_cache_next = mrtentry_ptr->kernel_cache;
+ kernel_cache_next != (kernel_cache_t *) NULL;
+ kernel_cache_prev = kernel_cache_next,
+ kernel_cache_next = kernel_cache_next->next)
+ {
+ if (inet6_lessthan(&kernel_cache_next->group , group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_next->group , group))
+ break;
+ if (inet6_lessthan(&kernel_cache_next->source , source))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_next->source , source))
+ break;
+ /* Found exact match. Nothing to change. */
+ return;
+ }
+
+ /*
+ * The new entry must be placed between kernel_cache_prev and
+ * kernel_cache_next
+ */
+ kernel_cache_new = (kernel_cache_t *) malloc(sizeof(kernel_cache_t));
+ if (kernel_cache_prev != (kernel_cache_t *) NULL)
+ kernel_cache_prev->next = kernel_cache_new;
+ else
+ mrtentry_ptr->kernel_cache = kernel_cache_new;
+ if (kernel_cache_next != (kernel_cache_t *) NULL)
+ kernel_cache_next->prev = kernel_cache_new;
+ kernel_cache_new->prev = kernel_cache_prev;
+ kernel_cache_new->next = kernel_cache_next;
+ kernel_cache_new->source = *source;
+ kernel_cache_new->group = *group;
+ kernel_cache_new->sg_count.pktcnt = 0;
+ kernel_cache_new->sg_count.bytecnt = 0;
+ kernel_cache_new->sg_count.wrong_if = 0;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+}
+
+/*
+ * Bring the kernel cache "UP": from the (*,*,RP) to (*,G) or (S,G)
+ */
+static void
+move_kernel_cache(mrtentry_ptr, flags)
+ mrtentry_t *mrtentry_ptr;
+ u_int16 flags;
+{
+ kernel_cache_t *kernel_cache_ptr;
+ kernel_cache_t *insert_kernel_cache_ptr;
+ kernel_cache_t *first_kernel_cache_ptr;
+ kernel_cache_t *last_kernel_cache_ptr;
+ kernel_cache_t *prev_kernel_cache_ptr;
+ mrtentry_t *mrtentry_pmbr;
+ mrtentry_t *mrtentry_rp;
+ int found;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ return;
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* Move the cache info from (*,*,RP) to (*,G) */
+ mrtentry_pmbr =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_pmbr == (mrtentry_t *) NULL)
+ return; /* Nothing to move */
+
+ first_kernel_cache_ptr = last_kernel_cache_ptr =
+ (kernel_cache_t *) NULL;
+ for (kernel_cache_ptr = mrtentry_pmbr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ /*
+ * The order is: (1) smaller group; (2) smaller source within
+ * group
+ */
+ if (inet6_lessthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ continue;
+ if (!inet6_equal(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ break;
+ /* Select the kernel_cache entries to move */
+ if (first_kernel_cache_ptr == (kernel_cache_t *) NULL)
+ {
+ first_kernel_cache_ptr = last_kernel_cache_ptr =
+ kernel_cache_ptr;
+ }
+ else
+ last_kernel_cache_ptr = kernel_cache_ptr;
+ }
+
+ if (first_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ {
+ /* Fix the old chain */
+ if (first_kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ {
+ first_kernel_cache_ptr->prev->next =
+ last_kernel_cache_ptr->next;
+ }
+ else
+ mrtentry_pmbr->kernel_cache = last_kernel_cache_ptr->next;
+ if (last_kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ last_kernel_cache_ptr->next->prev =
+ first_kernel_cache_ptr->prev;
+ if (mrtentry_pmbr->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_pmbr->flags
+ &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+
+ /* Insert in the new place */
+ prev_kernel_cache_ptr = (kernel_cache_t *) NULL;
+ last_kernel_cache_ptr->next = (kernel_cache_t *) NULL;
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;)
+ {
+ if (first_kernel_cache_ptr == (kernel_cache_t *) NULL)
+ break; /* All entries have been inserted */
+ if (inet6_greaterthan(&kernel_cache_ptr->source,&first_kernel_cache_ptr->source))
+ {
+ /* Insert the entry before kernel_cache_ptr */
+ insert_kernel_cache_ptr = first_kernel_cache_ptr;
+ first_kernel_cache_ptr = first_kernel_cache_ptr->next;
+ if (kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->prev->next =
+ insert_kernel_cache_ptr;
+ else
+ mrtentry_ptr->kernel_cache =
+ insert_kernel_cache_ptr;
+ insert_kernel_cache_ptr->prev =
+ kernel_cache_ptr->prev;
+ insert_kernel_cache_ptr->next = kernel_cache_ptr;
+ kernel_cache_ptr->prev = insert_kernel_cache_ptr;
+ }
+ prev_kernel_cache_ptr = kernel_cache_ptr;
+ kernel_cache_ptr = kernel_cache_ptr->next;
+ }
+ if (first_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ {
+ /* Place all at the end after prev_kernel_cache_ptr */
+ if (prev_kernel_cache_ptr != (kernel_cache_t *) NULL)
+ prev_kernel_cache_ptr->next = first_kernel_cache_ptr;
+ else
+ mrtentry_ptr->kernel_cache = first_kernel_cache_ptr;
+ first_kernel_cache_ptr->prev = prev_kernel_cache_ptr;
+ }
+ }
+ return;
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /*
+ * (S,G) entry. Move the whole group cache from (*,*,RP) to (*,G) and
+ * then get the necessary entry from (*,G). TODO: Not optimized! The
+ * particular entry is moved first to (*,G), then we have to search
+ * again (*,G) to find it and move to (S,G).
+ */
+ /* TODO: XXX: No need for this? Thinking.... */
+ /* move_kernel_cache(mrtentry_ptr->group->grp_route, flags); */
+
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp == (mrtentry_t *) NULL)
+ return;
+
+ if (mrtentry_rp->incoming != mrtentry_ptr->incoming)
+ {
+ /*
+ * XXX: the (*,*,RP) (or (*,G)) iif is different from the (S,G)
+ * iif. No need to move the cache, because (S,G) don't need it.
+ * After the first packet arrives on the shortest path, the
+ * correct cache entry will be created. If (flags &
+ * MFC_MOVE_FORCE) then we must move the cache. This usually
+ * happens when switching to the shortest path. The calling
+ * function will immediately call k_chg_mfc() to modify the
+ * kernel cache.
+ */
+ if (!(flags & MFC_MOVE_FORCE))
+ return;
+ }
+
+ /* Find the exact entry */
+
+ found = FALSE;
+ for (kernel_cache_ptr = mrtentry_rp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ {
+ if (inet6_lessthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->group, &mrtentry_ptr->group->group))
+ break;
+ if (inet6_lessthan(&kernel_cache_ptr->source, &mrtentry_ptr->source->address))
+ continue;
+ if (inet6_greaterthan(&kernel_cache_ptr->source, &mrtentry_ptr->source->address))
+ break;
+ /* We found it! */
+ if (kernel_cache_ptr->prev != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->prev->next = kernel_cache_ptr->next;
+ else
+ {
+ mrtentry_rp->kernel_cache = kernel_cache_ptr->next;
+ }
+ if (kernel_cache_ptr->next != (kernel_cache_t *) NULL)
+ kernel_cache_ptr->next->prev = kernel_cache_ptr->prev;
+ found = TRUE;
+ break;
+ }
+
+ if (found == TRUE)
+ {
+ if (mrtentry_rp->kernel_cache == (kernel_cache_t *) NULL)
+ mrtentry_rp->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG);
+ if (mrtentry_ptr->kernel_cache != (kernel_cache_t *) NULL)
+ free((char *) mrtentry_ptr->kernel_cache);
+ mrtentry_ptr->flags |= MRTF_KERNEL_CACHE;
+ mrtentry_ptr->kernel_cache = kernel_cache_ptr;
+ kernel_cache_ptr->prev = (kernel_cache_t *) NULL;
+ kernel_cache_ptr->next = (kernel_cache_t *) NULL;
+ }
+ }
+}
diff --git a/usr.sbin/pim6sd/mrt.h b/usr.sbin/pim6sd/mrt.h
new file mode 100644
index 0000000..dbe31ca
--- /dev/null
+++ b/usr.sbin/pim6sd/mrt.h
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef MRT_H
+#define MRT_H
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet6/ip6_mroute.h>
+#include <string.h>
+#include "defs.h"
+
+
+/* flags for the mrt entries */
+
+#define MRTF_SPT 0x0001 /* iif toward source */
+#define MRTF_WC 0x0002 /* (*,G) entry */
+#define MRTF_RP 0x0004 /* iif toward RP */
+#define MRTF_NEW 0x0008 /* new created routing entry */
+#define MRTF_IIF_REGISTER 0x0020 /* ??? */
+#define MRTF_REGISTER 0x0080 /* ??? */
+#define MRTF_KERNEL_CACHE 0x0200 /* a mirror for the kernel cache */
+#define MRTF_NULL_OIF 0x0400 /* null oif cache.. ??? */
+#define MRTF_REG_SUPP 0x0800 /* register suppress ??? */
+#define MRTF_ASSERTED 0x1000 /* upstream is not that of src ??? */
+#define MRTF_SG 0x2000 /* (S,G) pure, not hanging off of (*,G)*/
+#define MRTF_PMBR 0x4000 /* (*,*,RP) entry (for interop) */
+#define MRTF_MFC_CLONE_SG 0x8000 /* clone (S,G) MFC from (*,G) or (*,*,RP) */
+
+#define CREATE TRUE
+#define DONT_CREATE FALSE
+
+
+#define MFC_MOVE_FORCE 0x1
+#define MFC_UPDATE_FORCE 0x2
+
+
+/* Macro to duplicate oif info (oif bits, timers) */
+
+#define VOIF_COPY(from , to ) \
+ do { \
+ IF_COPY(&from->joined_oifs , &to->joined_oifs); \
+ IF_COPY(&from->oifs ,&to->oifs ); \
+ IF_COPY(&from->leaves , &to->leaves); \
+ IF_COPY(&from->pruned_oifs , &to->leaves ); \
+ IF_COPY(&from->asserted_oifs ,&to->asserted_oifs); \
+ bcopy(from->vif_timers , to->vif_timers , \
+ numvifs*sizeof(from->vif_timers[0])); \
+ bcopy(from->vif_deletion_delay , to->vif_deletion_delay , \
+ numvifs*sizeof(from->vif_deletion_delay[0])); \
+ } while (0)
+
+
+#define FREE_MRTENTRY(mrtentry_ptr) \
+ do { \
+ kernel_cache_t *prev; \
+ kernel_cache_t *next; \
+ \
+ free( (char *)( (mrtentry_ptr)->vif_timers ) ); \
+ free( (char *)( (mrtentry_ptr)->vif_deletion_delay ) ); \
+ for( next=(mrtentry_ptr)->kernel_cache ; next!=(kernel_cache_t *)NULL ; ) \
+ { \
+ prev=next; \
+ next=next->next; \
+ free(prev); \
+ } \
+ free( (char *)( (mrtentry_ptr)->kernel_cache ) ); \
+ free( (char *)(mrtentry_ptr) ); \
+ } while (0)
+
+
+/*
+ * The complicated structure used by the more complicated Join/Prune
+ * message building
+ */
+
+
+typedef struct build_jp_message {
+ struct build_jp_message *next; /* Used to chain the free entries */
+ u_int8 *jp_message; /* The Join/Prune message */
+ u_int32 jp_message_size; /* Size of the Join/Prune message (in bytes) */
+ u_int16 holdtime; /* Join/Prune message holdtime field */
+ struct sockaddr_in6 curr_group; /* Current group address */
+ u_int8 curr_group_msklen; /* Current group masklen */
+ u_int8 *join_list; /* The working area for the join addresses */
+ u_int32 join_list_size; /* The size of the join_list (in bytes) */
+ u_int16 join_addr_number; /* Number of the join addresses in join_list */
+ u_int8 *prune_list; /* The working area for the prune addresses */
+ u_int32 prune_list_size; /* The size of the prune_list (in bytes) */
+ u_int16 prune_addr_number; /* Number of the prune addresses in prune_list*/
+ u_int8 *rp_list_join; /* The working area for RP join addresses */
+ u_int32 rp_list_join_size; /* The size of the rp_list_join (in bytes) */
+ u_int16 rp_list_join_number; /* Number of RP addresses in rp_list_join */
+ u_int8 *rp_list_prune; /* The working area for RP prune addresses */
+ u_int32 rp_list_prune_size; /* The size of the rp_list_prune (in bytes) */
+ u_int16 rp_list_prune_number; /* Number of RP addresses in rp_list_prune */
+ u_int8 *num_groups_ptr; /* Pointer to number_of_groups in jp_message */
+} build_jp_message_t;
+
+
+typedef struct pim_nbr_entry {
+ struct pim_nbr_entry *next; /* link to next neighbor */
+ struct pim_nbr_entry *prev; /* link to prev neighbor */
+ struct sockaddr_in6 address; /* neighbor address */
+ vifi_t vifi; /* which interface */
+ u_int16 timer; /* for timing out neighbor */
+ build_jp_message_t *build_jp_message; /* A structure for fairly
+ * complicated Join/Prune
+ * message construction.
+ */
+
+} pim_nbr_entry_t;
+
+typedef struct srcentry {
+ struct srcentry *next; /* link to next entry */
+ struct srcentry *prev; /* link to prev entry */
+ struct sockaddr_in6 address; /* source or RP address */
+ struct mrtentry *mrtlink; /* link to routing entries */
+ vifi_t incoming; /* incoming vif */
+ struct pim_nbr_entry *upstream; /* upstream router */
+ u_int32 metric; /* Unicast Routing Metric to the source */
+ u_int32 preference; /* The metric preference (for assers)*/
+ u_int16 timer; /* Entry timer??? Delete? */
+ struct cand_rp *cand_rp; /* Used if this is rpentry_t */
+} srcentry_t;
+typedef srcentry_t rpentry_t;
+
+/* (RP<->group) matching table related structures */
+
+typedef struct cand_rp {
+ struct cand_rp *next; /* Next candidate RP */
+ struct cand_rp *prev; /* Previous candidate RP */
+ struct rp_grp_entry *rp_grp_next; /* The rp_grp_entry chain for that RP*/
+ rpentry_t *rpentry; /* Pointer to the RP entry */
+} cand_rp_t;
+
+typedef struct grp_mask {
+ struct grp_mask *next;
+ struct grp_mask *prev;
+ struct rp_grp_entry *grp_rp_next;
+ struct sockaddr_in6 group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr hash_mask;
+ u_int16 fragment_tag; /* Used for garbage collection */
+ u_int8 group_rp_number; /* Used when assembling segments */
+} grp_mask_t;
+
+typedef struct rp_grp_entry {
+ struct rp_grp_entry *rp_grp_next; /* Next entry for same RP */
+ struct rp_grp_entry *rp_grp_prev; /* Prev entry for same RP */
+ struct rp_grp_entry *grp_rp_next; /* Next entry for same grp prefix */
+ struct rp_grp_entry *grp_rp_prev; /* Prev entry for same grp prefix */
+ struct grpentry *grplink; /* Link to all grps via this entry*/
+ u_int16 advholdtime; /* The advertised holdtime */
+ u_int16 holdtime; /* The RP holdtime (will be aged) */
+ u_int16 fragment_tag; /* The fragment tag from the
+ * received BSR message
+ */
+
+ u_int8 priority; /* The RP priority */
+ grp_mask_t *group; /* Pointer to (group,mask) entry */
+ cand_rp_t *rp; /* Pointer to the RP */
+} rp_grp_entry_t;
+
+typedef struct grpentry {
+ struct grpentry *next; /* link to next entry */
+ struct grpentry *prev; /* link to prev entry */
+ struct grpentry *rpnext; /* next grp for the same RP */
+ struct grpentry *rpprev; /* prev grp for the same RP */
+ struct sockaddr_in6 group; /* subnet group of multicasts */
+ struct sockaddr_in6 rpaddr; /* The IPv6 address of the RP */
+ struct mrtentry *mrtlink; /* link to (S,G) routing entries */
+ rp_grp_entry_t *active_rp_grp; /* Pointer to the active rp_grp entry*/
+ struct mrtentry *grp_route; /* Pointer to the (*,G) routing entry*/
+} grpentry_t;
+
+typedef struct mrtentry {
+ struct mrtentry *grpnext; /* next entry of same group */
+ struct mrtentry *grpprev; /* prev entry of same group */
+ struct mrtentry *srcnext; /* next entry of same source */
+ struct mrtentry *srcprev; /* prev entry of same source */
+ struct grpentry *group; /* pointer to group entry */
+ struct srcentry *source; /* pointer to source entry (or RP) */
+ vifi_t incoming; /* the iif (either toward S or RP) */
+ if_set oifs; /* The current result oifs */
+ if_set joined_oifs; /* The joined oifs (Join received) */
+ if_set pruned_oifs; /* The pruned oifs (Prune received) */
+ if_set asserted_oifs; /* The asserted oifs (lost Assert) */
+ if_set leaves; /* Has directly connected members */
+ struct pim_nbr_entry *upstream; /* upstream router, needed because
+ * of the asserts it may be different
+ * than the source (or RP) upstream
+ * router.
+ */
+
+ u_int32 metric; /* Routing Metric for this entry */
+ u_int32 preference; /* The metric preference value */
+ struct sockaddr_in6 pmbr_addr; /* The PMBR address (for interop) */
+ u_int16 *vif_timers; /* vifs timer list */
+ u_int16 *vif_deletion_delay; /* vifs deletion delay list */
+
+ u_int16 flags; /* The MRTF_* flags */
+ u_int16 timer; /* entry timer */
+ u_int16 jp_timer; /* The Join/Prune timer */
+ u_int16 rs_timer; /* Register-Suppression Timer */
+ u_int assert_timer;
+ u_int assert_rate_timer;
+ struct kernel_cache *kernel_cache; /* List of the kernel cache entries */
+#ifdef RSRR
+ struct rsrr_cache *rsrr_cache; /* Used to save RSRR requests for
+ * routes change notification.
+ */
+#endif /* RSRR */
+
+} mrtentry_t;
+
+
+/*
+ * Used to get forwarded data related counts (number of packet, number of
+ * bits, etc)
+ */
+
+struct sg_count {
+ u_quad_t pktcnt; /* Number of packets for (s,g) */
+ u_quad_t bytecnt; /* Number of bytes for (s,g) */
+ u_quad_t wrong_if; /* Number of packets received on wrong iif for (s,g) */
+};
+
+/*
+ * Structure to keep track of existing (S,G) MFC entries in the kernel
+ * for particular (*,G) or (*,*,RP) entry. We must keep track for
+ * each active source which doesn't have (S,G) entry in the daemon's
+ * routing table. We need to keep track of such sources for two reasons:
+ *
+ * (1) If the kernel does not support (*,G) MFC entries (currently, the
+ * "official" mcast code doesn't), we must know all installed (s,G) entries
+ * in the kernel and modify them if the iif or oif for the (*,G) changes.
+ *
+ * (2) By checking periodically the traffic coming from the shared tree,
+ * we can either delete the idle sources or switch to the shortest path.
+ *
+ * Note that even if we have (*,G) implemented in the kernel, we still
+ * need to have this structure because of (2)
+ */
+
+
+typedef struct kernel_cache {
+ struct kernel_cache *next;
+ struct kernel_cache *prev;
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ struct sg_count sg_count; /* The (s,g) data retated counters (see above) */
+} kernel_cache_t;
+
+struct vif_count {
+ u_long icount; /* Input packet count on vif */
+ u_long ocount; /* Output packet count on vif */
+ u_long ibytes; /* Input byte count on vif */
+ u_long obytes; /* Output byte count on vif */
+};
+
+/* globals and functions exportations */
+
+extern srcentry_t *srclist;
+extern grpentry_t *grplist;
+
+extern void init_pim6_mrt __P(());
+extern mrtentry_t *find_route __P((struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group,
+ u_int16 flags, char create));
+extern grpentry_t *find_group __P((struct sockaddr_in6 *group));
+extern srcentry_t *find_source __P((struct sockaddr_in6 *source));
+extern void delete_mrtentry __P((mrtentry_t *mrtentry_ptr));
+extern void delete_srcentry __P((srcentry_t *srcentry_ptr));
+extern void delete_grpentry __P((grpentry_t *grpentry_ptr));
+extern void delete_mrtentry_all_kernel_cache __P((mrtentry_t *mrtentry_ptr));
+extern void delete_single_kernel_cache __P((mrtentry_t *mrtentry_ptr,
+ kernel_cache_t *kernel_cache_ptr));
+extern void delete_single_kernel_cache_addr __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern void add_kernel_cache __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *source, struct sockaddr_in6 *group,
+ u_int16 flags));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/mtrace6/Makefile b/usr.sbin/pim6sd/mtrace6/Makefile
new file mode 100644
index 0000000..2c198b8
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/Makefile
@@ -0,0 +1,86 @@
+# Copyright (c) 1999 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Copyright (c) 1998 by the University of Oregon.
+# All rights reserved.
+#
+# Permission to use, copy, modify, and distribute this software and
+# its documentation in source and binary forms for lawful
+# purposes and without fee is hereby granted, provided
+# that the above copyright notice appear in all copies and that both
+# the copyright notice and this permission notice appear in supporting
+# documentation, and that any documentation, advertising materials,
+# and other materials related to such distribution and use acknowledge
+# that the software was developed by the University of Oregon.
+# The name of the University of Oregon may not be used to endorse or
+# promote products derived from this software without specific prior
+# written permission.
+#
+# THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
+# ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+# PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+# NON-INFRINGEMENT.
+#
+# IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+# TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+# THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Other copyrights might apply to parts of this software and are so
+# noted when applicable.
+#
+#
+# Questions concerning this software should be directed to
+# Kurt Windisch (kurtw@antc.uoregon.edu)
+#
+# $Id: Makefile,v 1.2 1999/09/13 01:23:22 jinmei Exp $
+# $FreeBSD$
+#
+#
+#Part of this program has been derived from PIM sparse-mode pimd.
+#The pimd program is covered by the license in the accompanying file
+#named "LICENSE.pimd".
+#
+#The pimd program is COPYRIGHT 1998 by University of Southern California.
+#
+#Part of this program has been derived from mrouted.
+#The mrouted program is covered by the license in the accompanying file
+#named "LICENSE.mrouted".
+#
+#The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+#Leland Stanford Junior University.
+
+PROG= mtrace6
+
+CFLAGS+=-Wall
+CFLAGS+= -I$(.CURDIR)/..
+
+MAN8= mtrace6.8
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/pim6sd/mtrace6/mtrace6.8 b/usr.sbin/pim6sd/mtrace6/mtrace6.8
new file mode 100644
index 0000000..1beab02
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/mtrace6.8
@@ -0,0 +1,115 @@
+.\" Copyright (C) 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: mtrace6.8,v 1.3 1999/09/12 17:03:18 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd Sep 12, 1999
+.Dt MTRACE6 8
+.Os KAME
+.Sh NAME
+.Nm mtrace6
+.Nd print IPv6 multicast path from a source to
+a receiver
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar destination
+.Op Fl g Ar gateway
+.Op Fl h Ar hops
+.Op Fl i Ar interface
+.Op Fl m Ar maxhops
+.Op Fl n
+.Op Fl r Ar response_addr
+.Op Fl w Ar waittime
+.Ar source
+.Ar group
+.Sh DESCRIPTION
+.Nm
+utilizes a tracing feature implemented in multicast routers that is
+accessed via an extension to the MLD protocol. A trace query is
+passed hop-by-hop along the reverse path from the
+.Ar destination
+to the
+.Ar source ,
+collecting hop addresses, packet counts, and routing error conditions
+along the path, and then the response is returned to the requestor.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d Ar destination
+Specifies the multicast receiver that the query wants to trace.
+It is the host running
+.Nm mtrace6
+by default.
+.It Fl g Ar gateway
+Send the trace query via unicast directly to the multicast router
+.Ar gateway .
+The unicast router must be the last-hop router on the path from the
+intended source to the receiver.
+.Ar gateway
+can also be a multicast address that the last hop router joins.
+.It Fl h Ar hops
+Set
+.Ar hops
+to the IPv6 hop limit field of query packets. The default is 64.
+.It Fl i Ar interface
+Specifies the local interface (on a multi-homed host) for sending
+the trace query and as the default for the receiver and the response
+destination.
+.It Fl m Ar maxhops
+Set to
+.Ar maxhops
+to the maximum number of hops that will be traced from the receiver
+back toward the source. The default is 127 hops.
+.It Fl n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each router found on
+the path).
+.It Fl r Ar response_addr
+Specify the host that the trace response sends to.
+By default, the response will send to the host running
+.Nm mtrace6 .
+.It Fl w Ar waittime
+Set the time to wait for a trace response to
+.Ar waittime
+seconds. The default is 3 seconds.
+.El
+.Sh SEE ALSO
+.Xr pim6dd 8 ,
+.Xr pim6sd 8 ,
+.Xr mtrace 8
+.Sh BUGS
+Multicast trace for IPv6 is experimental. MLD types for query and
+response, and packet format are not officially defined.
+.Pp
+.Ar waittime
+specified by the
+.Fl w
+option is currently meaningless.
+.Sh HISTORY
+The
+.Nm mtrace6
+command first appeared in WIDE/KAME IPv6 protocol stack kit.
diff --git a/usr.sbin/pim6sd/mtrace6/mtrace6.c b/usr.sbin/pim6sd/mtrace6/mtrace6.c
new file mode 100644
index 0000000..f2befc6
--- /dev/null
+++ b/usr.sbin/pim6sd/mtrace6/mtrace6.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/icmp6.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <err.h>
+
+#include "trace.h"
+
+static void usage(), open_socket(), make_packet(), mtrace_loop(), show_result();
+
+static char *gateway, *intface, *source, *group, *receiver, *destination;
+static int mldsoc, hops = 64, maxhops = 127, waittime = 3, querylen, opt_n;
+static struct sockaddr *gw_sock, *src_sock, *grp_sock, *dst_sock, *rcv_sock;
+static char *querypacket;
+static char frombuf[1024]; /* XXX: enough size? */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int op;
+
+ /* get parameters */
+ while((op = getopt(argc, argv, "d:g:h:i:m:nr:w:")) != -1) {
+ switch(op) {
+ case 'd':
+ destination = optarg;
+ break;
+ case 'g':
+ gateway = optarg;
+ break;
+ case 'h':
+ hops = atoi(optarg);
+ if (hops < 0 || hops > 255) {
+ warnx("query/response hops must be between 0 and 255");
+ usage();
+ }
+ break;
+ case 'i':
+ intface = optarg;
+ break;
+ case 'm':
+ maxhops = atoi(optarg);
+ if (maxhops < 0 || maxhops > 255) {
+ warnx("maxhops must be between 0 and 255");
+ usage();
+ }
+ break;
+ case 'n':
+ opt_n = 1;
+ break;
+ case 'r':
+ receiver = optarg;
+ break;
+ case 'w':
+ waittime = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage();
+ source = argv[0];
+ group = argv[1];
+
+ /* examine addresses and open a socket */
+ open_socket();
+
+ /* construct a query packet according to the specified parameters */
+ make_packet();
+
+ mtrace_loop();
+}
+
+static char *
+proto_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case PROTO_DVMRP:
+ return ("DVMRP");
+ case PROTO_MOSPF:
+ return ("MOSPF");
+ case PROTO_PIM:
+ return ("PIM");
+ case PROTO_CBT:
+ return ("CBT");
+ case PROTO_PIM_SPECIAL:
+ return ("PIM/Special");
+ case PROTO_PIM_STATIC:
+ return ("PIM/Static");
+ case PROTO_DVMRP_STATIC:
+ return ("DVMRP/Static");
+ case 0:
+ return ("None");
+ default:
+ (void) sprintf(buf, "Unknown protocol code %d", type);
+ return (buf);
+ }
+}
+
+static char *
+pr_addr(addr, numeric)
+ struct sockaddr_in6 *addr;
+ int numeric;
+{
+ static char buf[MAXHOSTNAMELEN];
+ int flag = 0;
+
+ if (numeric)
+ flag |= NI_NUMERICHOST;
+ flag |= NI_WITHSCOPEID;
+
+ getnameinfo((struct sockaddr *)addr, addr->sin6_len, buf, sizeof(buf),
+ NULL, 0, flag);
+
+ return (buf);
+}
+
+static void
+setqid(family, query)
+ int family;
+ char *query;
+{
+ struct tr6_query *q6;
+
+ switch(family) {
+ case AF_INET6:
+ q6 = (struct tr6_query *)((struct mld6_hdr *)query + 1);
+ q6->tr_qid = (u_int32_t)random();
+ }
+}
+
+static void
+mtrace_loop()
+{
+ int nsoc, fromlen, rcvcc;
+ struct timeval tv, tv_wait;
+ struct fd_set fds;
+ struct sockaddr_storage from_ss;
+ struct sockaddr *from_sock = (struct sockaddr *)&from_ss;
+
+ /* initializa random number of query ID */
+ gettimeofday(&tv, 0);
+ srandom(tv.tv_usec);
+
+ while(1) { /* XXX */
+ setqid(gw_sock->sa_family, querypacket);
+
+ if (sendto(mldsoc, (void *)querypacket, querylen, 0, gw_sock,
+ gw_sock->sa_len) < 0)
+ err(1, "sendto");
+
+ tv_wait.tv_sec = waittime;
+ tv_wait.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(mldsoc, &fds);
+
+ if ((nsoc = select(mldsoc + 1, &fds, NULL, NULL, &tv_wait)) < 0)
+ err(1, "select");
+
+ if (nsoc == 0) {
+ printf("Timeout\n");
+ exit(0); /* XXX try again? */
+ }
+
+ fromlen = sizeof(from_ss);
+ if ((rcvcc = recvfrom(mldsoc, frombuf, sizeof(frombuf), 0,
+ from_sock, &fromlen))
+ < 0)
+ err(1, "recvfrom");
+
+ show_result(from_sock, rcvcc);
+ exit(0); /* XXX */
+ }
+}
+
+char *fwd_code[] = {"NOERR", "WRONGIF", "SPRUNE", "RPRUNE", "SCOPED", "NORT",
+ "WRONGLH", "NOFWD", "RP", "RPFIF", "NOMC", "HIDDEN"};
+char *fwd_errcode[] = {"", "NOSPC", "OLD", "ADMIN"};
+
+static char *
+str_rflags(flag)
+ int flag;
+{
+ if (0x80 & flag) { /* fatal error */
+ flag &= ~0x80;
+ if (flag >= sizeof(fwd_errcode) / sizeof(char *) ||
+ flag == 0) {
+ warnx("unknown error code(%d)", flag);
+ return("UNKNOWN");
+ }
+ return(fwd_errcode[flag]);
+ }
+
+ /* normal code */
+ if (flag >= sizeof(fwd_code) / sizeof(char *)) {
+ warnx("unknown forward code(%d)", flag);
+ return("UNKNOWN");
+ }
+ return(fwd_code[flag]);
+}
+
+static void
+show_ip6_result(from6, datalen)
+ struct sockaddr_in6 *from6;
+ int datalen;
+{
+ struct mld6_hdr *mld6_tr_resp = (struct mld6_hdr *)frombuf;
+ struct mld6_hdr *mld6_tr_query = (struct mld6_hdr *)querypacket;
+ struct tr6_query *tr6_rquery = (struct tr6_query *)(mld6_tr_resp + 1);
+ struct tr6_query *tr6_query = (struct tr6_query *)(mld6_tr_query + 1);
+ struct tr6_resp *tr6_resp = (struct tr6_resp *)(tr6_rquery + 1),
+ *rp, *rp_end;
+ int i;
+
+ if (datalen < sizeof(*mld6_tr_resp) + sizeof(*tr6_rquery) +
+ sizeof(*tr6_resp)) {
+ warnx("show_ip6_result: receive data length(%d) is short",
+ datalen);
+ return;
+ }
+
+ switch(mld6_tr_resp->mld6_type) {
+ case MLD6_MTRACE_RESP:
+ if ((datalen - sizeof(*mld6_tr_resp) - sizeof(*tr6_rquery)) %
+ sizeof(*tr6_resp)) {
+ warnx("show_ip6_result: incomplete response (%d bytes)",
+ datalen);
+ return;
+ }
+ rp_end = (struct tr6_resp *)((char *)mld6_tr_resp + datalen);
+
+ /* sanity check for the response */
+ if (tr6_query->tr_qid != tr6_rquery->tr_qid ||
+ !IN6_ARE_ADDR_EQUAL(&tr6_query->tr_src, &tr6_rquery->tr_src) ||
+ !IN6_ARE_ADDR_EQUAL(&tr6_query->tr_dst, &tr6_rquery->tr_dst))
+ return; /* XXX: bark here? */
+
+ for (i = 0, rp = tr6_resp; rp < rp_end; i++, rp++) {
+ struct sockaddr_in6 sa_resp, sa_upstream;
+
+ /* reinitialize the sockaddr. paranoid? */
+ memset((void *)&sa_resp, 0, sizeof(sa_resp));
+ sa_resp.sin6_family = AF_INET6;
+ sa_resp.sin6_len = sizeof(sa_resp);
+ memset((void *)&sa_upstream, 0, sizeof(sa_upstream));
+ sa_upstream.sin6_family = AF_INET6;
+ sa_upstream.sin6_len = sizeof(sa_upstream);
+
+ sa_resp.sin6_addr = rp->tr_lcladdr;
+ sa_upstream.sin6_addr = rp->tr_rmtaddr;
+
+ /* print information for the router */
+ printf("%3d ", -i);/* index */
+ /* router address and incoming/outgoing interface */
+ printf("%s", pr_addr((struct sockaddr *)&sa_resp, opt_n));
+ printf("(%s/%ld->%ld) ",
+ pr_addr((struct sckaddr *)&sa_upstream),
+ ntohl(rp->tr_inifid), ntohl(rp->tr_outifid));
+ /* multicast routing protocol type */
+ printf("%s ", proto_type(rp->tr_rproto));
+ /* forwarding error code */
+ printf("%s", str_rflags(rp->tr_rflags & 0xff));
+
+ putchar('\n');
+ }
+
+ break;
+ default: /* impossible... */
+ warnx("show_ip6_result: invalid ICMPv6 type(%d)",
+ mld6_tr_resp->mld6_type); /* assert? */
+ break;
+ }
+}
+
+static void
+show_result(from, datalen)
+ struct sockaddr *from;
+ int datalen;
+{
+ switch(from->sa_family) {
+ case AF_INET6:
+ show_ip6_result((struct sockaddr_in6 *)from, datalen);
+ break;
+ default:
+ errx(1, "show_result: illegal AF(%d) on recv", from->sa_family);
+ }
+}
+
+static void
+set_sockaddr(addrname, hints, sap)
+ char *addrname;
+ struct addrinfo *hints;
+ struct sockaddr *sap;
+{
+ struct addrinfo *res;
+ int ret_ga;
+
+ ret_ga = getaddrinfo(addrname, NULL, hints, &res);
+ if (ret_ga)
+ errx(1, "getaddrinfo faild: %s", gai_strerror(ret_ga));
+ if (!res->ai_addr)
+ errx(1, "getaddrinfo failed");
+ memcpy((void *)sap, (void *)res->ai_addr, res->ai_addr->sa_len);
+
+ freeaddrinfo(res);
+}
+
+static int
+is_multicast(sa)
+ struct sockaddr *sa;
+{
+ switch(sa->sa_family) {
+ case AF_INET6:
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sa)->sin6_addr))
+ return 1;
+ else
+ return 0;
+ break;
+ default:
+ return 0; /* XXX: support IPv4? */
+ }
+}
+
+static char *
+all_routers_str(family)
+ int family;
+{
+ switch(family) {
+ case AF_INET6:
+ return("ff02::1");
+ default:
+ errx(1, "all_routers_str: unknown AF(%d)", family);
+ }
+}
+
+int
+ip6_validaddr(ifname, addr)
+ char *ifname;
+ struct sockaddr_in6 *addr;
+{
+ int s;
+ struct in6_ifreq ifr6;
+ u_int32_t flags6;
+
+ /* we need a global address only...XXX: should be flexible? */
+ if (IN6_IS_ADDR_LOOPBACK(&addr->sin6_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&addr->sin6_addr))
+ return(0);
+
+ /* get IPv6 dependent flags and examine them */
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "ip6_validaddr: socket");
+
+ strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = *addr;
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0)
+ err(1, "ioctl(SIOCGIFAFLAG_IN6)");
+ close(s);
+ flags6 = ifr6.ifr_ifru.ifru_flags6;
+ if (flags6 & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE |
+ IN6_IFF_DUPLICATED | IN6_IFF_DETACHED))
+ return(0);
+
+ return(1);
+}
+
+int
+get_my_sockaddr(family, addrp)
+ int family;
+ struct sockaddr *addrp;
+{
+#define IF_BUFSIZE 8192 /* XXX: adhoc...should be customizable? */
+ int i, s;
+ struct ifconf ifconf;
+ struct ifreq *ifrp;
+ static char ifbuf[IF_BUFSIZE];
+
+ if ((s = socket(family, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ ifconf.ifc_buf = ifbuf;
+ ifconf.ifc_len = sizeof(ifbuf);
+
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifconf) < 0)
+ err(1, "ioctl(SIOCGIFCONF)");
+ close(s);
+
+ for (i = 0; i < ifconf.ifc_len; ) {
+ ifrp = (struct ifreq *)(ifbuf + i);
+ if (ifrp->ifr_addr.sa_family == family) {
+ switch(family) {
+ case AF_INET6:
+ if (ip6_validaddr(ifrp->ifr_name,
+ (struct sockaddr_in6 *)&ifrp->ifr_addr))
+ goto found;
+ }
+ }
+
+ i += IFNAMSIZ;
+ /* i += max(sizeof(sockaddr), ifr_addr.sa_len) */
+ if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr))
+ i += ifrp->ifr_addr.sa_len;
+ else
+ i += sizeof(struct sockaddr);
+ }
+
+ return(-1); /* not found */
+
+ found:
+ memcpy((void *)addrp, (void *)&ifrp->ifr_addr, ifrp->ifr_addr.sa_len);
+ return(0);
+#undef IF_BUFSIZE
+}
+
+static void
+set_hlim(s, addr, hops)
+ int s, hops;
+ struct sockaddr *addr;
+{
+ struct sockaddr_in6 *sin6;
+ int opt;
+
+ switch(addr->sa_family) {
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)addr;
+ opt = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) ?
+ IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS;
+ if (setsockopt(s, IPPROTO_IPV6, opt, (char *)&hops,
+ sizeof(hops)) == -1)
+ err(1, "setsockopt(%s)",
+ (opt == IPV6_MULTICAST_HOPS) ?
+ "IPV6_MULTICAST_HOPS" : "IPV6_UNICAST_HOPS");
+ break;
+ }
+}
+
+static void
+set_join(s, ifname, group)
+ int s;
+ char *ifname;
+ struct sockaddr *group;
+{
+ struct ipv6_mreq mreq6;
+ u_int ifindex;
+
+ switch(group->sa_family) {
+ case AF_INET6:
+ if ((ifindex = if_nametoindex(ifname)) == 0)
+ err(1, "set_join: if_nametoindex failed for %s", ifname);
+ mreq6.ipv6mr_multiaddr =
+ ((struct sockaddr_in6 *)group)->sin6_addr;
+ mreq6.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
+ sizeof(mreq6)) < 0)
+ err(1, "setsockopt(IPV6_JOIN_GROUP)");
+ break;
+ }
+}
+
+static void
+set_filter(s, family)
+{
+ struct icmp6_filter filter6;
+
+ switch(family) {
+ case AF_INET6:
+ ICMP6_FILTER_SETBLOCKALL(&filter6);
+ ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filter6);
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter6,
+ sizeof(filter6)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+ break;
+ }
+}
+
+static void
+open_socket()
+{
+ struct addrinfo hints;
+ static struct sockaddr_storage gw_ss, src_ss, grp_ss, dst_ss, rcv_ss;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6; /* to be independent of AF? */
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ /* multicast group(must be specified) */
+ grp_sock = (struct sockaddr *)&grp_ss;
+ set_sockaddr(group, &hints, grp_sock);
+ if (!is_multicast(grp_sock))
+ errx(1, "group(%s) is not a multicast address", group);
+
+ /* multicast source(must be specified) */
+ src_sock = (struct sockaddr *)&src_ss;
+ set_sockaddr(source, &hints, src_sock);
+ if (is_multicast(src_sock))
+ errx(1, "source(%s) is not a unicast address", source);
+
+ /* last hop gateway for the destination(if specified) */
+ gw_sock = (struct sockaddr *)&gw_ss;
+ if (gateway) /* can be either multicast or unicast */
+ set_sockaddr(gateway, &hints, gw_sock);
+ else {
+ char *r = all_routers_str(grp_sock->sa_family);
+
+ set_sockaddr(r, &hints, gw_sock);
+ }
+
+ /* destination address for the trace */
+ dst_sock = (struct sockaddr *)&dst_ss;
+ if (destination) {
+ set_sockaddr(destination, &hints, dst_sock);
+ if (is_multicast(dst_sock))
+ errx(1, "destination(%s) is not a unicast address",
+ destination);
+ }
+ else {
+ /* XXX: consider interface? */
+ get_my_sockaddr(grp_sock->sa_family, dst_sock);
+ }
+
+ /* response receiver(if specified) */
+ rcv_sock = (struct sockaddr *)&rcv_ss;
+ if (receiver) { /* can be either multicast or unicast */
+ set_sockaddr(receiver, &hints, rcv_sock);
+ if (is_multicast(rcv_sock) &&
+ intface == NULL) {
+#ifdef notyet
+ warnx("receive I/F is not specified for multicast"
+ "response(%s)", receiver);
+ intface = default_intface;
+#else
+ errx(1, "receive I/F is not specified for multicast"
+ "response(%s)", receiver);
+#endif
+ }
+ }
+ else {
+ /* XXX: consider interface? */
+ get_my_sockaddr(grp_sock->sa_family, rcv_sock);
+ }
+
+ if ((mldsoc = socket(hints.ai_family, hints.ai_socktype,
+ hints.ai_protocol)) < 0)
+ err(1, "socket");
+
+ /* set necessary socket options */
+ if (hops)
+ set_hlim(mldsoc, gw_sock, hops);
+ if (receiver && is_multicast(rcv_sock))
+ set_join(mldsoc, intface, rcv_sock);
+ set_filter(mldsoc, grp_sock->sa_family);
+}
+
+static void
+make_ip6_packet()
+{
+ struct mld6_hdr *mld6_tr_query;
+ struct tr6_query *tr6_query;
+
+ querylen = sizeof(*mld6_tr_query) + sizeof(*tr6_query);
+ if ((querypacket = malloc(querylen)) == NULL)
+ errx(1, "make_ip6_packet: malloc failed");
+ memset(querypacket, 0, querylen);
+
+ /* fill in MLD header */
+ mld6_tr_query = (struct mld6_hdr *)querypacket;
+ mld6_tr_query->mld6_type = MLD6_MTRACE;
+ mld6_tr_query->mld6_code = maxhops & 0xff;
+ mld6_tr_query->mld6_addr = ((struct sockaddr_in6 *)grp_sock)->sin6_addr;
+
+ /* fill in mtrace query fields */
+ tr6_query = (struct tr6_query *)(mld6_tr_query + 1);
+ tr6_query->tr_src = ((struct sockaddr_in6 *)src_sock)->sin6_addr;
+ tr6_query->tr_dst = ((struct sockaddr_in6 *)dst_sock)->sin6_addr;
+ tr6_query->tr_raddr = ((struct sockaddr_in6 *)rcv_sock)->sin6_addr;
+ tr6_query->tr_rhlim = 0xff & hops;
+}
+
+static void
+make_packet()
+{
+ switch(grp_sock->sa_family) {
+ case AF_INET6:
+ make_ip6_packet();
+ break;
+ default:
+ errx(1, "make_packet: unsupported AF(%d)", grp_sock->sa_family);
+ }
+}
+
+static void
+usage()
+{
+ errx(1,
+ "[-d destination] [-g gateway] [-h hops] [-i interface] "
+ "[-m maxhops] [-n] [-r response_addr] [-w waittime] source group");
+}
diff --git a/usr.sbin/pim6sd/pathnames.h b/usr.sbin/pim6sd/pathnames.h
new file mode 100644
index 0000000..bacd832
--- /dev/null
+++ b/usr.sbin/pim6sd/pathnames.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#define _PATH_PIM6D_CONF "/usr/local/v6/etc/pim6sd.conf"
+#define _PATH_PIM6D_LOGFILE "/var/log/pim6sd.log"
+
+#if (defined(BSD) && (BSD >= 199103))
+ #define _PATH_PIM6D_PID "/var/run/pim6sd.pid"
+ #define _PATH_PIM6D_GENID "/var/run/pim6sd.genid"
+ #define _PATH_PIM6D_DUMP "/var/run/pim6sd.dump"
+ #define _PATH_PIM6D_CACHE "/var/run/pim6sd.cache"
+ #define _PATH_PIM6D_STAT "/var/run/pim6sd.stat"
+#else
+ #define _PATH_PIM6D_PID "/etc/pim6sd.pid"
+ #define _PATH_PIM6D_GENID "/etc/pim6sd.genid"
+ #define _PATH_PIM6D_DUMP "/etc/pim6sd.dump"
+ #define _PATH_PIM6D_CACHE "/etc/pim6sd.cache"
+ #define _PATH_PIM6D_STAT "/etc/pim6sd.stat"
+#endif
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6.c b/usr.sbin/pim6sd/pim6.c
new file mode 100644
index 0000000..117179d
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/pim6.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <signal.h>
+#include "mld6.h"
+#include "defs.h"
+#include "kern.h"
+#include "pim6.h"
+#include "pimd.h"
+#include "pim6_proto.h"
+#include "inet6.h"
+#include "debug.h"
+
+struct sockaddr_in6 allpim6routers_group;
+int pim6_socket;
+char *pim6_recv_buf;
+char *pim6_send_buf;
+
+static struct sockaddr_in6 from;
+static struct iovec sndiovpim[2];
+static struct iovec rcviovpim[2];
+static struct msghdr sndmhpim,
+ rcvmhpim;
+static u_char sndcmsgbufpim[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+static u_char rcvcmsgbufpim[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+
+
+/*
+ * Local function definitions.
+ */
+static void pim6_read __P((int f, fd_set *rfd));
+static void accept_pim6 __P((int recvlen));
+static int pim6_cksum __P((u_short *, struct in6_addr *,
+ struct in6_addr *, int));
+
+
+
+void init_pim6()
+{
+ struct cmsghdr *cmsgp;
+ int on;
+
+ if ( (pim6_recv_buf = malloc( RECV_BUF_SIZE)) == NULL ||
+ (pim6_send_buf = malloc (RECV_BUF_SIZE)) == NULL)
+ log(LOG_ERR,errno,"pim6 buffer allocation");
+
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer pim6",RECV_BUF_SIZE);
+
+ if( (pim6_socket = socket(AF_INET6,SOCK_RAW,IPPROTO_PIM)) < 0 )
+ log(LOG_ERR,errno,"pim6_socket");
+
+ k_set_rcvbuf(pim6_socket,SO_RECV_BUF_SIZE_MAX,SO_RECV_BUF_SIZE_MIN);
+ k_set_hlim(pim6_socket,MINHLIM);
+ k_set_loop(pim6_socket,FALSE);
+
+ memset(&allpim6routers_group, 0, sizeof(allpim6routers_group));
+ allpim6routers_group.sin6_len = sizeof(allpim6routers_group);
+ allpim6routers_group.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, "ff02::d",
+ (void *)&allpim6routers_group.sin6_addr) != 1 )
+ log(LOG_ERR, 0, "inet_pton failed for ff02::d");
+ memset(&sockaddr6_d, 0, sizeof(sockaddr6_d));
+ sockaddr6_d.sin6_len = sizeof(sockaddr6_d);
+ sockaddr6_d.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, "ff00::",
+ (void *)&sockaddr6_d.sin6_addr) != 1)
+ log(LOG_ERR, 0, "inet_pton failed for ff00::");
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(pim6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
+#else
+ if (setsockopt(pim6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
+#endif
+
+ /* initialize msghdr for receiving packets */
+ rcviovpim[0].iov_base = (caddr_t) pim6_recv_buf;
+ rcviovpim[0].iov_len = RECV_BUF_SIZE;
+ rcvmhpim.msg_name = (caddr_t ) &from;
+ rcvmhpim.msg_namelen = sizeof (from);
+ rcvmhpim.msg_iov = rcviovpim;
+ rcvmhpim.msg_iovlen = 1;
+ rcvmhpim.msg_control = (caddr_t ) rcvcmsgbufpim;
+ rcvmhpim.msg_controllen = sizeof (rcvcmsgbufpim);
+
+
+ sndmhpim.msg_namelen=sizeof(struct sockaddr_in6);
+ sndmhpim.msg_iov=sndiovpim;
+ sndmhpim.msg_iovlen=1;
+ sndmhpim.msg_control=(caddr_t)sndcmsgbufpim;
+ sndmhpim.msg_controllen=sizeof(sndcmsgbufpim);
+ cmsgp=(struct cmsghdr *)sndcmsgbufpim;
+ cmsgp->cmsg_len=CMSG_SPACE(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level=IPPROTO_IPV6;
+ cmsgp->cmsg_type=IPV6_PKTINFO;
+
+ if ( register_input_handler(pim6_socket, pim6_read) <0)
+ log(LOG_ERR,0,"Registering pim6 socket");
+
+ /* Initialize the building Join/Prune messages working area */
+ build_jp_message_pool = (build_jp_message_t *)NULL;
+ build_jp_message_pool_counter = 0;
+}
+
+/* Read a PIM message */
+
+static void
+pim6_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int pim6_recvlen;
+
+#ifdef SYSV
+ sigset_t block, oblock;
+#else
+ register int omask;
+#endif
+
+ pim6_recvlen = recvmsg(pim6_socket,&rcvmhpim,0);
+
+ if (pim6_recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "PIM6 recvmsg");
+ return;
+ }
+
+#ifdef SYSV
+ (void)sigemptyset(&block);
+ (void)sigaddset(&block, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
+ log(LOG_ERR, errno, "sigprocmask");
+#else
+ /* Use of omask taken from main() */
+ omask = sigblock(sigmask(SIGALRM));
+#endif /* SYSV */
+
+ accept_pim6(pim6_recvlen);
+
+#ifdef SYSV
+ (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
+#else
+ (void)sigsetmask(omask);
+#endif /* SYSV */
+}
+
+static void
+accept_pim6(pimlen)
+ int pimlen;
+{
+ register struct pim *pim;
+ struct sockaddr_in6 dst;
+ struct in6_pktinfo *pi=NULL;
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *)rcvmhpim.msg_name;
+ struct cmsghdr *cm;
+ int ifindex=0;
+
+ /* sanity check */
+ if (pimlen < sizeof(pim)) {
+ log(LOG_WARNING, 0,
+ "data field too short (%u bytes) for PIM header, from %s",
+ pimlen, inet6_fmt(&src->sin6_addr));
+ return;
+ }
+ pim = (struct pim *)rcvmhpim.msg_iov[0].iov_base;
+
+ /* extract vital information via Advanced API */
+ for(cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhpim);
+ cm;
+ cm =(struct cmsghdr *)CMSG_NXTHDR(&rcvmhpim , cm ))
+ {
+
+ if( cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
+ {
+ pi=(struct in6_pktinfo *)(CMSG_DATA(cm));
+ dst.sin6_addr=pi->ipi6_addr;
+ ifindex = pi->ipi6_ifindex;
+ if (IN6_IS_ADDR_LINKLOCAL(&dst.sin6_addr))
+ dst.sin6_scope_id = ifindex;
+ else
+ dst.sin6_scope_id = 0;
+ }
+ }
+
+ if(pi==NULL)
+ log(LOG_ERR,0,"pim6_socket : unable to get destination packet");
+
+ if(ifindex==0)
+ log(LOG_ERR,0,"pim6_socket : unable to get ifindex");
+
+#define NOSUCHDEF
+#ifdef NOSUCHDEF /* TODO: delete. Too noisy */
+ IF_DEBUG(DEBUG_PIM_DETAIL) {
+ IF_DEBUG(DEBUG_PIM) {
+ log(LOG_DEBUG, 0, "Receiving %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ }
+ }
+#endif /* NOSUCHDEF */
+
+
+ /* Check of PIM version is already done in the kernel */
+
+ /*
+ * TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address)
+ * is it necessary?
+ */
+ /* Checksum verification is done in the kernel. */
+
+ switch (pim->pim_type) {
+ case PIM_HELLO:
+ receive_pim6_hello(src, (char *)(pim), pimlen);
+ break;
+ case PIM_REGISTER:
+ receive_pim6_register(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_REGISTER_STOP:
+ receive_pim6_register_stop(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_JOIN_PRUNE:
+ receive_pim6_join_prune(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_BOOTSTRAP:
+ receive_pim6_bootstrap(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_ASSERT:
+ receive_pim6_assert(src, &dst, (char *)(pim), pimlen);
+ break;
+ case PIM_GRAFT:
+ pim6dstat.in_pim6_graft++;
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_GRAFT_ACK:
+ pim6dstat.in_pim6_graft_ack++;
+ log(LOG_INFO, 0, "ignore %s from %s",
+ packet_kind(IPPROTO_PIM, pim->pim_type, 0),
+ inet6_fmt(&src->sin6_addr));
+ break;
+ case PIM_CAND_RP_ADV:
+ receive_pim6_cand_rp_adv(src, &dst, (char *)(pim), pimlen);
+ break;
+ default:
+ log(LOG_INFO, 0,
+ "ignore unknown PIM message code %u from %s",
+ pim->pim_type,
+ inet6_fmt(&src->sin6_addr));
+ break;
+ }
+}
+
+void
+send_pim6(char *buf, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, int type, int datalen)
+{
+ struct pim *pim;
+ int setloop=0;
+ int ifindex=0;
+ int sendlen=sizeof(struct pim)+datalen;
+ struct cmsghdr *cmsgp;
+ struct in6_pktinfo *sndpktinfo;
+
+ sndiovpim[0].iov_base=(caddr_t)buf;
+ sndiovpim[0].iov_len=datalen+sizeof(struct pim);
+ cmsgp=(struct cmsghdr *)sndcmsgbufpim;
+ sndpktinfo=(struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ sndmhpim.msg_name=(caddr_t)dst;
+
+ pim = (struct pim *)buf;
+ pim->pim_type = type;
+ pim->pim_ver = PIM_PROTOCOL_VERSION;
+ pim->pim_rsv = 0;
+ pim->pim_cksum = 0;
+
+ if(pim->pim_type == PIM_REGISTER)
+ {
+ sendlen = sizeof(struct pim)+sizeof(pim_register_t);
+
+ }
+
+ pim->pim_cksum = pim6_cksum((u_int16 *)pim,
+ &src->sin6_addr, &dst->sin6_addr,
+ sendlen);
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ {
+ if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ {
+ log(LOG_WARNING, 0,
+ "trying to send pim multicast packet "
+ "with non linklocal src(%s), ignoring",
+ inet6_fmt(&src->sin6_addr));
+ return;
+ }
+ sndmhpim.msg_control=NULL;
+ sndmhpim.msg_controllen=0;
+ ifindex=src->sin6_scope_id;
+
+ k_set_if(pim6_socket , ifindex);
+ if( IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allnodes_group.sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allrouters_group.sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&dst->sin6_addr,
+ &allpim6routers_group.sin6_addr))
+ {
+ setloop=1;
+ k_set_loop(pim6_socket, TRUE);
+ }
+ }
+ else
+ {
+ sndmhpim.msg_control=(caddr_t)sndcmsgbufpim;
+ sndmhpim.msg_controllen=sizeof(sndcmsgbufpim);
+ sndpktinfo->ipi6_ifindex=src->sin6_scope_id;
+ memcpy(&sndpktinfo->ipi6_addr, &src->sin6_addr,
+ sizeof(sndpktinfo->ipi6_addr));
+ }
+ if( sendmsg(pim6_socket, &sndmhpim, 0) <0 )
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno, "sendmsg from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(&dst->sin6_addr));
+
+ if(setloop)
+ k_set_loop(pim6_socket, FALSE);
+
+ return;
+}
+
+/* ============================== */
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+static union {
+ u_short phs[4];
+ struct {
+ u_long ph_len;
+ u_char ph_zero[3];
+ u_char ph_nxt;
+ } ph;
+} uph;
+
+/*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+int pim6_cksum(u_short *addr, struct in6_addr *src ,struct in6_addr *dst , int len )
+{
+ register int nleft = len;
+ register u_short *w;
+ register int sum = 0;
+ u_short answer = 0;
+
+ /*
+ * First create IP6 pseudo header and calculate a summary.
+ */
+ w = (u_short *)src;
+ uph.ph.ph_len = htonl(len);
+ uph.ph.ph_nxt = IPPROTO_PIM;
+
+ /* IPv6 source address */
+ sum += w[0];
+ /* XXX: necessary? */
+ if (!(IN6_IS_ADDR_LINKLOCAL(src) || IN6_IS_ADDR_MC_LINKLOCAL(src)))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* IPv6 destination address */
+ w = (u_short *)dst;
+ sum += w[0];
+ /* XXX: necessary? */
+ if (!(IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst)))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* Payload length and upper layer identifier */
+ sum += uph.phs[0]; sum += uph.phs[1];
+ sum += uph.phs[2]; sum += uph.phs[3];
+
+ /*
+ * Secondly calculate a summary of the first mbuf excluding offset.
+ */
+ w = addr;
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(u_char *)(&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
diff --git a/usr.sbin/pim6sd/pim6.h b/usr.sbin/pim6sd/pim6.h
new file mode 100644
index 0000000..d57bf4b
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIM6_H
+#define PIM6_H
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netinet6/ip6_mroute.h>
+
+extern struct sockaddr_in6 allpim6routers_group;
+extern char *pim6_send_buf;
+extern int pim6_socket;
+
+void init_pim6();
+extern void send_pim6 __P((char *buf, struct sockaddr_in6 *src,
+ struct sockaddr_in6 *dst, int type,
+ int datalen));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6_proto.c b/usr.sbin/pim6sd/pim6_proto.c
new file mode 100644
index 0000000..9de795e
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6_proto.c
@@ -0,0 +1,4126 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet6/pim6.h>
+#include <netinet/ip6.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include "mrt.h"
+#include "defs.h"
+#include "vif.h"
+#include "debug.h"
+#include "pim6.h"
+#include "pim6_proto.h"
+#include "pimd.h"
+#include "rp.h"
+#include "mld6.h"
+#include "timer.h"
+#include "route.h"
+#include "inet6.h"
+#include "kern.h"
+#include "routesock.h"
+
+/*
+ * Local functions definitions.
+ */
+
+static int parse_pim6_hello __P((char *pktPtr , int datalen , struct sockaddr_in6 *src,
+ u_int16 *holdtime));
+
+static int send_pim6_register_stop __P((struct sockaddr_in6 *reg_src , struct sockaddr_in6 *reg_dst ,
+ struct sockaddr_in6 *inner_source,
+ struct sockaddr_in6 *inner_grp));
+
+static build_jp_message_t *get_jp6_working_buff __P(());
+static void return_jp6_working_buff __P((pim_nbr_entry_t * pim_nbr));
+static void pack_jp6_message __P((pim_nbr_entry_t * pim_nbr));
+static void send_jp6_message __P((pim_nbr_entry_t * pim_nbr));
+static int compare_metrics __P((u_int32 local_preference,
+ u_int32 local_metric,
+ struct sockaddr_in6 *local_address,
+ u_int32 remote_preference,
+ u_int32 remote_metric,
+ struct sockaddr_in6 *remote_address));
+
+build_jp_message_t *build_jp_message_pool;
+int build_jp_message_pool_counter;
+struct sockaddr_in6 sockaddr6_any = {sizeof(struct sockaddr_in6) , AF_INET6 ,0,0, IN6ADDR_ANY_INIT};
+struct sockaddr_in6 sockaddr6_d;
+
+struct pim6dstat pim6dstat;
+
+/************************************************************************
+ * PIM_HELLO
+ ************************************************************************/
+int
+receive_pim6_hello(src, pim_message, datalen)
+ struct sockaddr_in6 *src;
+ register char *pim_message;
+ int datalen;
+{
+ mifi_t mifi;
+ struct uvif *v;
+ register pim_nbr_entry_t *nbr,
+ *prev_nbr,
+ *new_nbr;
+ u_int16 holdtime;
+ int bsr_length;
+ u_int8 *data_ptr;
+ srcentry_t *srcentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF)
+ {
+ /*
+ * Either a local vif or somehow received PIM_HELLO from non-directly
+ * connected router. Ignore it.
+ */
+
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ v = &uvifs[mifi];
+ v->uv_in_pim6_hello++; /* increment statistacs */
+ if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
+ return (FALSE); /* Shoudn't come on this interface */
+
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+
+ /* Get the Holdtime (in seconds) from the message. Return if error. */
+
+ if (parse_pim6_hello(pim_message, datalen, src, &holdtime) == FALSE)
+ return (FALSE);
+ IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
+ log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
+ inet6_fmt(&src->sin6_addr), holdtime);
+
+ for (prev_nbr = (pim_nbr_entry_t *) NULL, nbr = v->uv_pim_neighbors;
+ nbr != (pim_nbr_entry_t *) NULL;
+ prev_nbr = nbr, nbr = nbr->next)
+ {
+ /*
+ * The PIM neighbors are sorted in decreasing order of the network
+ * addresses (note that to be able to compare them correctly we must
+ * translate the addresses in host order.
+ */
+
+ if (inet6_lessthan(src, &nbr->address))
+ continue;
+ if (inet6_equal(src, &nbr->address))
+ {
+ /* We already have an entry for this host */
+
+ if (0 == holdtime)
+ {
+ /*
+ * Looks like we have a nice neighbor who is going down and
+ * wants to inform us by sending "holdtime=0". Thanks buddy
+ * and see you again!
+ */
+
+ log(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
+ inet6_fmt(&src->sin6_addr));
+ delete_pim6_nbr(nbr);
+ return (TRUE);
+ }
+ SET_TIMER(nbr->timer, holdtime);
+ return (TRUE);
+ }
+ else
+ /*
+ * No entry for this neighbor. Exit the loop and create an entry
+ * for it.
+ */
+ break;
+ }
+
+ /*
+ * This is a new neighbor. Create a new entry for it. It must be added
+ * right after `prev_nbr`
+ */
+
+ new_nbr = (pim_nbr_entry_t *) malloc(sizeof(pim_nbr_entry_t));
+ new_nbr->address = *src;
+ new_nbr->vifi = mifi;
+ SET_TIMER(new_nbr->timer, holdtime);
+ new_nbr->build_jp_message = (build_jp_message_t *) NULL;
+ new_nbr->next = nbr;
+ new_nbr->prev = prev_nbr;
+
+ if (prev_nbr != (pim_nbr_entry_t *) NULL)
+ prev_nbr->next = new_nbr;
+ else
+ v->uv_pim_neighbors = new_nbr;
+ if (new_nbr->next != (pim_nbr_entry_t *) NULL)
+ new_nbr->next->prev = new_nbr;
+
+ v->uv_flags &= ~VIFF_NONBRS;
+ v->uv_flags |= VIFF_PIM_NBR;
+
+ /* Since a new neighbour has come up, let it know your existence */
+ /*
+ * XXX: TODO: not in the spec, but probably should send the message after
+ * a short random period?
+ */
+
+ send_pim6_hello(v, pim_hello_holdtime);
+
+ if (v->uv_flags & VIFF_DR)
+ {
+ /*
+ * If I am the current DR on that interface, so send an RP-Set
+ * message to the new neighbor.
+ */
+
+ if ((bsr_length = create_pim6_bootstrap_message(pim6_send_buf)))
+ send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr , src , PIM_BOOTSTRAP,
+ bsr_length);
+
+
+ /* The router with highest network address is the elected DR */
+ if (inet6_lessthan(&v->uv_linklocal->pa_addr,&v->uv_pim_neighbors->address))
+ {
+ /*
+ * I was the DR, but not anymore. Remove all register_vif from
+ * oif list for all directly connected sources (for vifi).
+ */
+ /* TODO: XXX: first entry is not used! */
+ for (srcentry_ptr = srclist->next;
+ srcentry_ptr != (srcentry_t *) NULL;
+ srcentry_ptr = srcentry_ptr->next)
+ {
+ /* If not directly connected source for vifi */
+
+ if ((srcentry_ptr->incoming != mifi)
+ || (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL))
+ continue;
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_SG))
+ continue; /* This is not (S,G) entry */
+ /* Remove the register oif */
+ IF_CLR(reg_vif_num, &mrtentry_ptr->joined_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+ v->uv_flags &= ~VIFF_DR;
+ v->uv_flags &= ~VIFF_QUERIER;
+
+ }
+ }
+
+ /*
+ * TODO: XXX: does a new neighbor change any routing entries info? Need
+ * to trigger joins?
+ */
+
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG,0,"I'have got a new neighbor %s on vif %s",inet6_fmt(&src->sin6_addr),v->uv_name);
+ return (TRUE);
+}
+
+
+void
+delete_pim6_nbr(nbr_delete)
+ pim_nbr_entry_t *nbr_delete;
+{
+ srcentry_t *srcentry_ptr;
+ srcentry_t *srcentry_ptr_next;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ grpentry_t *grpentry_ptr;
+ pim_nbr_entry_t *new_nbr;
+ cand_rp_t *cand_rp_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rpentry_t *rpentry_ptr;
+ struct uvif *v;
+
+ v = &uvifs[nbr_delete->vifi];
+
+ /* Delete the entry from the pim_nbrs chain */
+
+ if (nbr_delete->prev != (pim_nbr_entry_t *) NULL)
+ nbr_delete->prev->next = nbr_delete->next;
+ else
+ v->uv_pim_neighbors = nbr_delete->next;
+ if (nbr_delete->next != (pim_nbr_entry_t *) NULL)
+ nbr_delete->next->prev = nbr_delete->prev;
+
+ return_jp6_working_buff(nbr_delete);
+
+ if (v->uv_pim_neighbors == (pim_nbr_entry_t *) NULL)
+ {
+ /* This was our last neighbor. */
+ v->uv_flags &= ~VIFF_PIM_NBR;
+ v->uv_flags |= (VIFF_NONBRS | VIFF_DR);
+ }
+ else
+ {
+ if (inet6_greaterthan(&v->uv_linklocal->pa_addr,
+ &v->uv_pim_neighbors->address))
+
+ /*
+ * The first address is the new potential remote DR address, but
+ * the local address is the winner.
+ */
+
+ v->uv_flags |= VIFF_DR;
+ v->uv_flags |= VIFF_QUERIER;
+ }
+
+ /* Update the source entries */
+ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *) NULL;
+ srcentry_ptr = srcentry_ptr_next)
+ {
+ srcentry_ptr_next = srcentry_ptr->next;
+
+ if (srcentry_ptr->upstream != nbr_delete)
+ continue;
+
+ /* Reset the next hop (PIM) router */
+
+ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE)
+ {
+ /*
+ * Coudn't reset it. Sorry, the hext hop router toward that
+ * source is probably not a PIM router, or cannot find route at
+ * all, hence I cannot handle this source and have to delete it.
+ */
+ delete_srcentry(srcentry_ptr);
+ }
+ else
+ if (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL)
+ {
+ /* Ignore the local or directly connected sources */
+ /*
+ * Browse all MRT entries for this source and reset the
+ * upstream router. Note that the upstream router is not
+ * always toward the source: it could be toward the RP for
+ * example.
+ */
+ new_nbr = srcentry_ptr->upstream;
+ for (mrtentry_ptr = srcentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->srcnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ mrtentry_ptr->upstream = srcentry_ptr->upstream;
+ mrtentry_ptr->metric = srcentry_ptr->metric;
+ mrtentry_ptr->preference = srcentry_ptr->preference;
+ change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+ }
+ }
+
+ /* Update the RP entries */
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if (cand_rp_ptr->rpentry->upstream != nbr_delete)
+ continue;
+ rpentry_ptr = cand_rp_ptr->rpentry;
+ /* Reset the RP entry iif */
+ /* TODO: check if error setting the iif! */
+ if (local_address(&rpentry_ptr->address) == NO_VIF)
+ {
+ set_incoming(rpentry_ptr, PIM_IIF_RP);
+ }
+ else
+ {
+ rpentry_ptr->incoming = reg_vif_num;
+ rpentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ }
+ mrtentry_ptr = rpentry_ptr->mrtlink;
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr,
+ rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ /* Update the group entries for this RP */
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ mrtentry_ptr = grpentry_ptr->grp_route;
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr,
+ rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ /* Update only the (S,G)RPbit entries for this group */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_srcs,
+ rpentry_ptr->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ }
+ }
+ }
+ }
+
+ free((char *) nbr_delete);
+}
+
+
+/* TODO: simplify it! */
+static int
+parse_pim6_hello(pim_message, datalen, src, holdtime)
+ char *pim_message;
+ int datalen;
+ struct sockaddr_in6 *src;
+ u_int16 *holdtime;
+{
+ u_int8 *pim_hello_message;
+ u_int8 *data_ptr;
+ u_int16 option_type;
+ u_int16 option_length;
+ int holdtime_received_ok = FALSE;
+ int option_total_length;
+
+ pim_hello_message = (u_int8 *) (pim_message + sizeof(struct pim));
+ datalen -= sizeof(struct pim);
+ for (; datalen >= sizeof(pim_hello_t);)
+ {
+ /* Ignore any data if shorter than (pim_hello header) */
+ data_ptr = pim_hello_message;
+ GET_HOSTSHORT(option_type, data_ptr);
+ GET_HOSTSHORT(option_length, data_ptr);
+ switch (option_type)
+ {
+ case PIM_MESSAGE_HELLO_HOLDTIME:
+ if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length)
+ {
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG, 0,
+ "PIM HELLO Holdtime from %s: invalid OptionLength = %u",
+ inet6_fmt(&src->sin6_addr), option_length);
+ return (FALSE);
+ }
+ GET_HOSTSHORT(*holdtime, data_ptr);
+ holdtime_received_ok = TRUE;
+ break;
+ default:
+ /* Ignore any unknown options */
+ break;
+ }
+
+ /* Move to the next option */
+ /*
+ * XXX: TODO: If we are padding to the end of the 32 bit boundary,
+ * use the first method to move to the next option, otherwise simply
+ * (sizeof(pim_hello_t) + option_length).
+ */
+
+#ifdef BOUNDARY_32_BIT
+ option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) +
+ ((option_length & 0x3) ? 4 : 0));
+#else
+ option_total_length = (sizeof(pim_hello_t) + option_length);
+#endif /* BOUNDARY_32_BIT */
+ datalen -= option_total_length;
+ pim_hello_message += option_total_length;
+ }
+ return (holdtime_received_ok);
+}
+
+
+int
+send_pim6_hello(v, holdtime)
+ struct uvif *v;
+ u_int16 holdtime;
+{
+ char *buf;
+ u_int8 *data_ptr;
+
+ int datalen;
+
+ buf = pim6_send_buf + sizeof(struct pim);
+ data_ptr = (u_int8 *) buf;
+ PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr);
+ PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr);
+ PUT_HOSTSHORT(holdtime, data_ptr);
+
+ datalen = data_ptr - (u_int8 *) buf;
+
+ send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_HELLO, datalen);
+ SET_TIMER(v->uv_pim_hello_timer, pim_hello_period);
+
+ v->uv_out_pim6_hello++;
+ return (TRUE);
+}
+
+/************************************************************************
+ * PIM_REGISTER
+ ************************************************************************/
+/*
+ * TODO: XXX: IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM
+ * USER SPACE AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE
+ * KERNEL.
+ */
+
+int
+receive_pim6_register(reg_src, reg_dst, pim_message, datalen)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ char *pim_message;
+ int datalen;
+{
+ struct sockaddr_in6 inner_src,
+ inner_grp;
+ pim_register_t *register_p;
+ struct ip6_hdr *ip;
+ u_int32 borderBit,
+ nullRegisterBit;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_ptr2;
+ if_set oifs;
+
+ pim6dstat.in_pim6_register++;
+
+ register_p = (pim_register_t *) (pim_message + sizeof(struct pim));
+
+ borderBit = ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_BORDER_BIT;
+ nullRegisterBit =
+ ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT;
+
+ /* initialize the pointer to the encapsulated packet */
+ ip = (struct ip6_hdr *) (register_p + 1);
+
+ /*
+ * We are keeping all addresses in network order,
+ * so no need for byte order translation.
+ */
+ inner_src.sin6_addr = ip->ip6_src;
+ inner_grp.sin6_addr = ip->ip6_dst;
+
+ /* scope validation of the inner source and destination addresses */
+ if (IN6_IS_ADDR_LINKLOCAL(&ip->ip6_src)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_register: inner source(%s) has invalid scope",
+ inet6_fmt(&ip->ip6_src));
+ }
+#ifdef notyet
+ if (IN6_IS_ADDR_SITELOCAL)
+ inner_src.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+ inner_src.sin6_scope_id = 0;
+
+ if (IN6_IS_ADDR_MC_NODELOCAL(&ip->ip6_dst) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&ip->ip6_dst)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_register: inner group(%s) has invalid scope",
+ inet6_fmt(&ip->ip6_dst));
+ return(FALSE); /* XXX: can we discard it? */
+ }
+#ifdef notyet
+ if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
+ inner_grp.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+ inner_grp.sin6_scope_id = 0;
+#endif
+ inner_grp.sin6_scope_id = 0;
+
+ mrtentry_ptr = find_route(&inner_src, &inner_grp,
+ MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+
+ /* No routing entry. Send REGISTER_STOP and return. */
+
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ log(LOG_DEBUG, 0,
+ "No routing entry for source %s and/or group %s",
+ inet6_fmt(&inner_src.sin6_addr), inet6_fmt(&inner_grp.sin6_addr));
+
+ /* TODO: XXX: shouldn't be inner_src=IN6ADDR_ANY? Not in the spec. */
+
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+
+ /* XXX: not in the spec: check if I am the RP for that group */
+ if (!inet6_equal(&my_cand_rp_address, reg_dst)
+ || (check_mrtentry_rp(mrtentry_ptr, &my_cand_rp_address) == FALSE))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+
+ /* (S,G) found */
+ /* TODO: check the timer again */
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout); /* restart timer */
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ /* The SPT bit is not set */
+
+ if (!nullRegisterBit)
+ {
+ calc_oifs(mrtentry_ptr, &oifs);
+ if (IF_ISEMPTY(&oifs)
+ && (mrtentry_ptr->incoming == reg_vif_num))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp,
+ &inner_src);
+ return (TRUE);
+ }
+
+ /*
+ * TODO: XXX: BUG!!! The data will be forwarded by the kernel
+ * MFC!!! Need to set a special flag for this routing entry
+ * so after a cache miss occur, the multicast packet will be
+ * forwarded from user space and won't install entry in the
+ * kernel MFC. The problem is that the kernel MFC doesn't
+ * know the PMBR address and simply sets the multicast
+ * forwarding cache to accept/forward all data coming from
+ * the register_vif.
+ */
+
+ if (borderBit)
+ {
+ if (!inet6_equal(&mrtentry_ptr->pmbr_addr,reg_src))
+ {
+ send_pim6_register_stop(reg_dst, reg_src,
+ &inner_grp, &inner_src);
+ return (TRUE);
+
+ }
+ }
+ return (TRUE);
+ }
+ /* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..? */
+ return (TRUE);
+ }
+ else
+ {
+ /* The SPT bit is set */
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+ }
+ }
+ if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))
+ {
+ if (borderBit)
+ {
+ /*
+ * Create (S,G) state. The oifs will be the copied from the
+ * existing (*,G) or (*,*,RP) entry.
+ */
+
+ mrtentry_ptr2 = find_route(&inner_src, &inner_grp, MRTF_SG,
+ CREATE);
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr2->pmbr_addr = *reg_src;
+
+ /* Clear the SPT flag */
+
+ mrtentry_ptr2->flags &= ~(MRTF_SPT | MRTF_NEW);
+ SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
+
+ /* TODO: explicitly call the Join/Prune send function? */
+
+ FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Send the Join
+ * immediately */
+ /*
+ * TODO: explicitly call this function?
+ * send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
+ * mrtentry_ptr2->upstream, pim_join_prune_holdtime);
+ */
+ }
+ }
+ }
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+
+ calc_oifs(mrtentry_ptr, &oifs);
+ if (IF_ISEMPTY(&oifs))
+ {
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &sockaddr6_any);
+ return (FALSE);
+ }
+
+ /* XXX: TODO: check with the spec again */
+
+ else
+ {
+ if (!nullRegisterBit)
+ {
+ /* Install cache entry in the kernel */
+ /*
+ * TODO: XXX: probably redundant here, because the
+ * decapsulated mcast packet in the kernel will result in
+ * CACHE_MISS
+ */
+
+ struct sockaddr_in6 *mfc_source = &inner_src;
+
+
+#ifdef KERNEL_MFC_WC_G
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = NULL;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
+ k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
+ mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
+ &mrtentry_ptr->group->rpaddr);
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+ if (!nullRegisterBit)
+ {
+ struct sockaddr_in6 *mfc_source = &inner_src;
+
+ /*
+ * XXX: have to create either (S,G) or (*,G). The choice below is
+ * (*,G)
+ */
+
+ mrtentry_ptr2 = find_route(NULL, &inner_grp, MRTF_WC,
+ CREATE);
+ if (mrtentry_ptr2 == (mrtentry_t *) NULL)
+ return (FALSE);
+ if (mrtentry_ptr2->flags & MRTF_NEW)
+ {
+ /* TODO: something else? Have the feeling sth is missing */
+
+ mrtentry_ptr2->flags &= ~MRTF_NEW;
+
+ /* TODO: XXX: copy the timer from the (*,*,RP) entry? */
+
+ COPY_TIMER(mrtentry_ptr->timer, mrtentry_ptr2->timer);
+ }
+ /* Install cache entry in the kernel */
+
+#ifdef KERNEL_MFC_WC_G
+
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = NULL;
+#endif /* KERNEL_MFC_WC_G */
+ add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
+ k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
+ mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
+ &mrtentry_ptr2->group->rpaddr);
+
+ return (TRUE);
+ }
+ }
+
+ /* Shoudn't happen: invalid routing entry? */
+ /* XXX: TODO: shoudn't be inner_src=IN6ADDR_ANY? Not in the spec. */
+
+ send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
+ return (TRUE);
+}
+
+
+int
+send_pim6_register(pkt)
+ char *pkt;
+{
+ register struct ip6_hdr *ip6;
+ static struct sockaddr_in6 source= {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 group= {sizeof(group) , AF_INET6 };
+ mifi_t mifi;
+ rpentry_t *rpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_ptr2;
+
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ int pktlen = 0;
+ char *buf;
+
+ ip6=(struct ip6_hdr *)pkt;
+
+ group.sin6_addr = ip6->ip6_dst;
+ source.sin6_addr = ip6->ip6_src;
+
+ if ((mifi = find_vif_direct_local(&source)) == NO_VIF)
+ return (FALSE);
+
+ if (!(uvifs[mifi].uv_flags & VIFF_DR))
+ return (FALSE); /* I am not the DR for that subnet */
+
+
+
+ rpentry_ptr = rp_match(&group);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ return (FALSE); /* No RP for this group */
+ if (local_address(&rpentry_ptr->address) != NO_VIF)
+ /* TODO: XXX: not sure it is working! */
+ return (FALSE); /* I am the RP for this group */
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (FALSE); /* Cannot create (S,G) state */
+
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ {
+ /* A new entry */
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->rs_timer); /* Reset the
+ * Register-Suppression timer */
+ if ((mrtentry_ptr2 = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_ptr2 =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Timeout the
+ * Join/Prune timer */
+ /*
+ * TODO: explicitly call this function?
+ * send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
+ * mrtentry_ptr2->upstream, pim_join_prune_holdtime);
+ */
+ }
+ }
+ /* Restart the (S,G) Entry-timer */
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+
+ IF_TIMER_NOT_SET(mrtentry_ptr->rs_timer)
+ {
+ /*
+ * The Register-Suppression Timer is not running. Encapsulate the
+ * data and send to the RP.
+ */
+ buf = pim6_send_buf + sizeof(struct pim);
+
+ bzero(buf, sizeof(pim_register_t)); /* No flags set */
+ buf += sizeof(pim_register_t);
+
+ /* Copy the data packet at the back of the register packet */
+ /* TODO: check pktlen. ntohs? */
+
+ pktlen = ntohs(ip6->ip6_plen);
+ pktlen +=sizeof(struct ip6_hdr); /* XXX */
+
+ bcopy((char *) ip6, buf, pktlen);
+ pktlen += sizeof(pim_register_t);
+ reg_src = max_global_address();
+ reg_dst = &mrtentry_ptr->group->rpaddr;
+
+ send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER,
+ pktlen);
+
+ pim6dstat.out_pim6_register++;
+
+ return (TRUE);
+ }
+ return (TRUE);
+}
+
+int
+send_pim6_null_register(mrtentry_ptr)
+ mrtentry_t *mrtentry_ptr;
+{
+ struct ip6_hdr *ip;
+ pim_register_t *pim_register;
+ int pktlen = 0;
+ mifi_t mifi;
+ struct sockaddr_in6 *reg_source,
+ *dest;
+
+ /* No directly connected source; no local address */
+
+ if ((mifi = find_vif_direct_local(&mrtentry_ptr->source->address)) == NO_VIF)
+ return (FALSE);
+
+ pim_register = (pim_register_t *) (pim6_send_buf + sizeof(struct pim));
+ bzero((char *) pim_register, sizeof(pim_register_t));
+ pim_register->reg_flags = htonl(pim_register->reg_flags
+ | PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT);
+
+ /* include the dummy ip header */
+ ip = (struct ip6_hdr *) (pim_register +1);
+ ip->ip6_plen= 0;
+ ip->ip6_flow=0;
+ ip->ip6_vfc = 0x60;
+ ip->ip6_hlim = MINHLIM;
+ ip->ip6_nxt = IPPROTO_NONE;
+ ip->ip6_src = mrtentry_ptr->source->address.sin6_addr;
+ ip->ip6_dst = mrtentry_ptr->group->group.sin6_addr;
+
+ pktlen = sizeof(pim_register_t) + sizeof(struct ip6_hdr);
+
+ dest = &mrtentry_ptr->group->rpaddr;
+ reg_source = max_global_address();
+
+ send_pim6(pim6_send_buf, reg_source , dest, PIM_REGISTER,
+ pktlen);
+ pim6dstat.out_pim6_register++; /* should be counted separately? */
+
+ return (TRUE);
+}
+
+
+/************************************************************************
+ * PIM_REGISTER_STOP
+ ************************************************************************/
+int
+receive_pim6_register_stop(reg_src, reg_dst, pim_message, datalen)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst;
+ char *pim_message;
+ register int datalen;
+{
+ pim_register_stop_t *pim_regstop_p;
+ pim6_encod_grp_addr_t encod_grp;
+ pim6_encod_uni_addr_t encod_unisrc;
+ struct sockaddr_in6 source,
+ group;
+ u_int8 *data_ptr;
+ mrtentry_t *mrtentry_ptr;
+ if_set pruned_oifs;
+ mifi_t mifi;
+ struct uvif *v;
+
+ pim6dstat.in_pim6_register_stop++;
+
+ pim_regstop_p = (pim_register_stop_t *) (pim_message +
+ sizeof(struct pim));
+ data_ptr = (u_int8 *) & pim_regstop_p->encod_grp;
+ GET_EGADDR6(&encod_grp, data_ptr);
+ GET_EUADDR6(&encod_unisrc, data_ptr);
+
+ group.sin6_addr = encod_grp.mcast_addr;
+
+ /* scope validation of the inner source and destination addresses */
+
+#ifdef notyet
+ if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
+ group.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+
+ group.sin6_scope_id = 0;
+ source.sin6_addr = encod_unisrc.unicast_addr;
+
+ /* the source address must be global...but is it always true? */
+
+#ifdef notyet
+ if (IN6_IS_ADDR_SITELOCAL)
+ source.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
+ reg_src, reg_dst);
+ else
+#endif
+
+ source.sin6_scope_id = 0;
+
+ if((mifi= find_vif_direct_local(&source))==NO_VIF)
+ {
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ {
+ log(LOG_WARNING,0,
+ "Received PIM_REGISTER_STOP from RP %s for a non "
+ "direct-connect source %s",
+ inet6_fmt(&reg_src->sin6_addr),
+ inet6_fmt(&encod_unisrc.unicast_addr));
+ }
+ return FALSE;
+ }
+
+ v=&uvifs[mifi];
+
+
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+
+ IF_DEBUG(DEBUG_PIM_REGISTER)
+ {
+ log(LOG_DEBUG, 0,
+ "Received PIM_REGISTER_STOP from RP %s to %s "
+ "source : %s group : %s",
+ inet6_fmt(&reg_src->sin6_addr),
+ inet6_fmt(&reg_dst->sin6_addr),
+ inet6_fmt(&encod_unisrc.unicast_addr),
+ inet6_fmt(&encod_grp.mcast_addr));
+ }
+
+ /* TODO: apply the group mask and do register_stop for all grp addresses */
+ /* TODO: check for SourceAddress == 0 */
+
+
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG, DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ return (FALSE);
+ }
+
+ /*
+ * XXX: not in the spec: check if the PIM_REGISTER_STOP originator is
+ * really the RP
+ */
+
+
+ if (check_mrtentry_rp(mrtentry_ptr, reg_src) == FALSE)
+ {
+ return (FALSE);
+ }
+
+ /* restart the Register-Suppression timer */
+
+ SET_TIMER(mrtentry_ptr->rs_timer, (0.5 * pim_register_suppression_timeout)
+ + (RANDOM() % (pim_register_suppression_timeout + 1)));
+ /* Prune the register_vif from the outgoing list */
+
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &pruned_oifs);
+ IF_SET(reg_vif_num, &pruned_oifs);
+ change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs, &pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ return (TRUE);
+}
+
+
+/* TODO: optional rate limiting is not implemented yet */
+/* Unicasts a REGISTER_STOP message to the DR */
+
+static int
+send_pim6_register_stop(reg_src, reg_dst, inner_grp, inner_src)
+ struct sockaddr_in6 *reg_src,
+ *reg_dst,
+ *inner_grp,
+ *inner_src;
+{
+ char *buf;
+ u_int8 *data_ptr;
+
+ buf = pim6_send_buf + sizeof(struct pim);
+ data_ptr = (u_int8 *) buf;
+ PUT_EGADDR6(inner_grp->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
+ PUT_EUADDR6(inner_src->sin6_addr, data_ptr);
+
+ send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER_STOP,
+ data_ptr-(u_int8 *) buf);
+ pim6dstat.out_pim6_register_stop++;
+
+ return (TRUE);
+}
+
+/************************************************************************
+ * PIM_JOIN_PRUNE
+ ************************************************************************/
+int
+join_or_prune(mrtentry_ptr, upstream_router)
+ mrtentry_t *mrtentry_ptr;
+ pim_nbr_entry_t *upstream_router;
+{
+ if_set entry_oifs;
+ mrtentry_t *mrtentry_grp;
+
+ if ((mrtentry_ptr == (mrtentry_t *) NULL))
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join_or_prune : mrtentry_ptr is null");
+ return (PIM_ACTION_NOTHING);
+ }
+ if( upstream_router == (pim_nbr_entry_t *) NULL)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join_or_prune : upstream_router is null");
+ return (PIM_ACTION_NOTHING);
+ }
+
+ calc_oifs(mrtentry_ptr, &entry_oifs);
+ if (mrtentry_ptr->flags & (MRTF_PMBR | MRTF_WC))
+ {
+ /* (*,*,RP) or (*,G) entry */
+ /* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */
+
+ if (upstream_router != mrtentry_ptr->upstream)
+ return (PIM_ACTION_NOTHING);
+
+ /* TODO: XXX: Can we have (*,*,RP) prune? */
+
+ if (IF_ISEMPTY(&entry_oifs))
+ {
+ /* NULL oifs */
+ if (!(uvifs[mrtentry_ptr->incoming].uv_flags & VIFF_DR))
+ {
+ /* I am not the DR for that subnet. */
+ return (PIM_ACTION_PRUNE);
+ }
+ if (IF_ISSET(mrtentry_ptr->incoming, &mrtentry_ptr->leaves))
+ /* I am the DR and have local leaves */
+ return (PIM_ACTION_JOIN);
+ /* Probably the last local member hast timeout */
+ return (PIM_ACTION_PRUNE);
+ }
+ return (PIM_ACTION_JOIN);
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry */
+ /* TODO: check again */
+ if (mrtentry_ptr->upstream == upstream_router)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ {
+ /* Upstream router toward S */
+ if (IF_ISEMPTY(&entry_oifs))
+ {
+ if (mrtentry_ptr->group->active_rp_grp != (rp_grp_entry_t *)NULL &&
+ inet6_equal(&mrtentry_ptr->group->rpaddr,
+ &my_cand_rp_address))
+ {
+ /*
+ * (S,G) at the RP. Don't send Join/Prune (see the
+ * end of Section 3.3.2)
+ */
+
+ return (PIM_ACTION_NOTHING);
+ }
+ return (PIM_ACTION_PRUNE);
+ }
+ else
+ return (PIM_ACTION_JOIN);
+ }
+ else
+ {
+ /* Upstream router toward RP */
+ if (IF_ISEMPTY(&entry_oifs))
+ return (PIM_ACTION_PRUNE);
+ }
+ }
+
+ /*
+ * Looks like the case when the upstream router toward S is different
+ * from the upstream router toward RP
+ */
+
+ if (mrtentry_ptr->group->active_rp_grp == (rp_grp_entry_t *) NULL)
+ return (PIM_ACTION_NOTHING);
+ mrtentry_grp = mrtentry_ptr->group->grp_route;
+ if (mrtentry_grp == (mrtentry_t *) NULL)
+ mrtentry_grp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_grp == (mrtentry_t *) NULL)
+ return (PIM_ACTION_NOTHING);
+ if (mrtentry_grp->upstream != upstream_router)
+ return (PIM_ACTION_NOTHING); /* XXX: shoudn't happen */
+
+ if ((!(mrtentry_ptr->flags & MRTF_RP))
+ && (mrtentry_ptr->flags & MRTF_SPT))
+ {
+ return (PIM_ACTION_PRUNE);
+ }
+ }
+ return (PIM_ACTION_NOTHING);
+}
+
+/* TODO: too long, simplify it! */
+#define PIM6_JOIN_PRUNE_MINLEN (4 + PIM6_ENCODE_UNI_ADDR_LEN + 4)
+
+int
+receive_pim6_join_prune(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ char *pim_message;
+ register int datalen;
+{
+ mifi_t mifi;
+ struct uvif *v;
+ pim6_encod_uni_addr_t uni_target_addr;
+ pim6_encod_grp_addr_t encod_group;
+ pim6_encod_src_addr_t encod_src;
+ u_int8 *data_ptr;
+ u_int8 *data_ptr_start;
+ u_int8 num_groups;
+ u_int8 num_groups_tmp;
+ int star_star_rp_found;
+ u_int16 holdtime;
+ u_int16 num_j_srcs;
+ u_int16 num_j_srcs_tmp;
+ u_int16 num_p_srcs;
+ struct sockaddr_in6 source;
+ struct sockaddr_in6 group;
+ struct sockaddr_in6 target;
+ struct in6_addr s_mask;
+ struct in6_addr g_mask;
+ u_int8 s_flags;
+ u_int8 reserved;
+ rpentry_t *rpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_rp;
+ grpentry_t *grpentry_ptr;
+ u_int16 jp_value;
+ pim_nbr_entry_t *upstream_router;
+ int my_action;
+ int ignore_group;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ u_int8 *data_ptr_group_j_start;
+ u_int8 *data_ptr_group_p_start;
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF)
+ {
+ /*
+ * Either a local vif or somehow received PIM_JOIN_PRUNE from
+ * non-directly connected router. Ignore it.
+ */
+
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ v = &uvifs[mifi];
+ v->uv_in_pim6_join_prune++;
+ if (uvifs[mifi].uv_flags &
+ (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
+ {
+ return (FALSE); /* Shoudn't come on this interface */
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_JOIN_PRUNE_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message size(%u) is"
+ " too short from %s on %s",
+ datalen, inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= PIM6_JOIN_PRUNE_MINLEN;
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+
+ /* Get the target address */
+ GET_EUADDR6(&uni_target_addr, data_ptr);
+ GET_BYTE(reserved, data_ptr);
+ GET_BYTE(num_groups, data_ptr);
+ if (num_groups == 0)
+ return (FALSE); /* No indication for groups in the message */
+ GET_HOSTSHORT(holdtime, data_ptr);
+ target.sin6_len = sizeof(target);
+ target.sin6_family = AF_INET6;
+ target.sin6_addr = uni_target_addr.unicast_addr;
+ target.sin6_scope_id = inet6_uvif2scopeid(&target, v);
+
+ /* Sanity check for the message length through all the groups */
+ num_groups_tmp = num_groups;
+ data_ptr_start = data_ptr;
+ while (num_groups_tmp--) {
+ int srclen;
+
+ /* group addr + #join + #src */
+ if (datalen < PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t)) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message from %s on %s is"
+ " too short to contain enough data",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= (PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t));
+ data_ptr += PIM6_ENCODE_GRP_ADDR_LEN;
+
+ /* joined source addresses and pruned source addresses */
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ srclen = (num_j_srcs + num_p_srcs) * PIM6_ENCODE_SRC_ADDR_LEN;
+ if (datalen < srclen) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_join_prune: Join/Prune message from %s on %s is"
+ " too short to contain enough data",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+ return(FALSE);
+ }
+ datalen -= srclen;
+ data_ptr += srclen;
+ }
+ data_ptr = data_ptr_start;
+ num_groups_tmp = num_groups;
+
+ if (!inet6_localif_address(&target, v) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr))
+ {
+
+ /* if I am not the targer of the join message */
+ /*
+ * Join/Prune suppression code. This either modifies the J/P timers
+ * or triggers an overriding Join.
+ */
+ /*
+ * Note that if we have (S,G) prune and (*,G) Join, we must send them
+ * in the same message. We don't bother to modify both timers here.
+ * The Join/Prune sending function will take care of that.
+ */
+
+ upstream_router = find_pim6_nbr(&target);
+ if (upstream_router == (pim_nbr_entry_t *) NULL)
+ return (FALSE); /* I have no such neighbor */
+
+ group.sin6_len = sizeof(group);
+ group.sin6_family = AF_INET6;
+ source.sin6_len = sizeof(source);
+ source.sin6_family = AF_INET6;
+
+ while (num_groups--)
+ {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ if (encod_group.masklen > (sizeof(struct in6_addr) << 3))
+ continue;
+ MASKLEN_TO_MASK6(encod_group.masklen, g_mask);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr))
+ {
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue; /* Ignore this group and jump to the next */
+ }
+
+ if (inet6_equal(&group,&sockaddr6_d) &&
+ (encod_src.masklen == STAR_STAR_RP_MSK6LEN))
+ {
+ /* (*,*,RP) Join suppression */
+
+ while (num_j_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+ /* sanity checks */
+ if (!inet6_valid_host(&source))
+ continue;
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_RP_BIT) &&
+ (s_flags & USADDR_WC_BIT))
+ {
+ /* This is the RP address. */
+ rpentry_ptr = rp_find(&source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue; /* Don't have such RP. Ignore */
+ mrtentry_rp = rpentry_ptr->mrtlink;
+ my_action = join_or_prune(mrtentry_rp,
+ upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_rp->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_rp->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+
+ /*
+ * Set the Join/Prune suppression timer for this
+ * routing entry by increasing the current Join/Prune
+ * timer.
+ */
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_rp->jp_timer < jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ } /* num_j_srcs */
+
+ while (num_p_srcs--)
+ {
+ /*
+ * TODO: XXX: Can we have (*,*,RP) prune message? Not in
+ * the spec, but anyway, the code below can handle them:
+ * either suppress the local (*,*,RP) prunes or override
+ * the prunes by sending (*,*,RP) and/or (*,G) and/or
+ * (S,G) Join.
+ */
+
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+ if (!inet6_valid_host(&source))
+ continue;
+
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_RP_BIT) &&
+ (s_flags & USADDR_WC_BIT))
+ {
+ /* This is the RP address. */
+ rpentry_ptr = rp_find(&source);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue; /* Don't have such RP. Ignore */
+ mrtentry_rp = rpentry_ptr->mrtlink;
+ my_action = join_or_prune(mrtentry_rp,
+ upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_rp->jp_timer < holdtime)
+ || ((mrtentry_rp->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ /* Suppress the Prune */
+ jp_value = pim_join_prune_period+
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_rp->jp_timer < jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_rp->jp_timer > jp_value)
+ SET_TIMER(mrtentry_rp->jp_timer, jp_value);
+ }
+ /*
+ * Check all (*,G) and (S,G) matching to this RP. If
+ * my_action == JOIN, then send a Join and override
+ * the (*,*,RP) Prune.
+ */
+ for (grpentry_ptr =
+ rpentry_ptr->cand_rp->rp_grp_next->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ my_action = join_or_prune(grpentry_ptr->grp_route,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (grpentry_ptr->grp_route->jp_timer >
+ jp_value)
+ SET_TIMER(grpentry_ptr->grp_route->jp_timer, jp_value);
+ }
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ my_action = join_or_prune(mrtentry_srcs,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_srcs->jp_timer > jp_value)
+ SET_TIMER(mrtentry_srcs->jp_timer, jp_value);
+ }
+ } /* For all (S,G) */
+ } /* For all (*,G) */
+ }
+ } /* num_p_srcs */
+ continue; /* This was (*,*,RP) suppression */
+ }
+
+ /* (*,G) or (S,G) suppression */
+ /*
+ * TODO: XXX: currently, accumulated groups (i.e. group_masklen <
+ * group_address_lengt) are not implemented. Just need to create
+ * a loop and apply the procedure below for all groups matching
+ * the prefix.
+ */
+
+ while (num_j_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,v);
+
+ if (!inet6_valid_host(&source))
+ continue;
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) JOIN_REQUEST (toward the RP) */
+ mrtentry_ptr = find_route(&sockaddr6_any , &group, MRTF_WC,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+ /* (*,G) Join suppresion */
+ if (!inet6_equal(&source,&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address))
+ continue; /* The RP address doesn't match.
+ * Ignore. */
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ continue;
+ } /* End of (*,G) Join suppression */
+
+ /* (S,G) Join suppresion */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action != PIM_ACTION_JOIN)
+ continue;
+
+ /* Check the holdtime */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > holdtime)
+ continue;
+ if ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
+ continue;
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ continue;
+ }
+
+ /* Prunes suppression */
+ while (num_p_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+ if (encod_src.masklen >
+ (sizeof(struct in6_addr) << 3))
+ continue;
+
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) prune suppression */
+ rpentry_ptr = rp_match(&source);
+ if ((rpentry_ptr == (rpentry_t *) NULL)
+ || (!inet6_equal(&rpentry_ptr->address , &source)))
+ continue; /* No such RP or it is different.
+ * Ignore */
+ mrtentry_ptr = find_route(&sockaddr6_any, &group, MRTF_WC,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->jp_timer < holdtime)
+ || ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ /* Suppress the Prune */
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+
+ /*
+ * Check all (S,G) entries for this group. If my_action
+ * == JOIN, then send the Join and override the (*,G)
+ * Prune.
+ */
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ my_action = join_or_prune(mrtentry_srcs,
+ upstream_router);
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ } /* For all (S,G) */
+ continue; /* End of (*,G) prune suppression */
+ }
+
+ /* (S,G) prune suppression */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ my_action = join_or_prune(mrtentry_ptr, upstream_router);
+ if (my_action == PIM_ACTION_PRUNE)
+ {
+ /* Suppress the (S,G) Prune */
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->jp_timer < holdtime)
+ || ((mrtentry_ptr->jp_timer == holdtime)
+ && (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
+ {
+ jp_value = pim_join_prune_period +
+ 0.5 * (RANDOM() % pim_join_prune_period);
+ if (mrtentry_ptr->jp_timer < jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ }
+ else
+ if (my_action == PIM_ACTION_JOIN)
+ {
+ /* Override the Prune by scheduling a Join */
+ jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->jp_timer > jp_value)
+ SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
+ }
+ } /* while (num_p_srcs--) */
+ } /* while (num_groups--) */
+ return (TRUE);
+ } /* End of Join/Prune suppression code */
+
+ /* I am the target of this join, so process the message */
+
+ /*
+ * The spec says that if there is (*,G) Join, it has priority over old
+ * existing ~(S,G) prunes in the routing table. However, if the (*,G)
+ * Join and the ~(S,G) prune are in the same message, ~(S,G) has the
+ * priority. The spec doesn't say it, but I think the same is true for
+ * (*,*,RP) and ~(S,G) prunes.
+ *
+ * The code below do: (1) Check the whole message for (*,*,RP) Joins. (1.1)
+ * If found, clean all pruned_oifs for all (*,G) and all (S,G) for each
+ * RP in the list, but do not update the kernel cache. Then go back to
+ * the beginning of the message and start processing for each group: (2)
+ * Check for Prunes. If no prunes, process the Joins. (3) If there are
+ * Prunes: (3.1) Scan the Join part for existing (*,G) Join. (3.1.1) If
+ * there is (*,G) Join, clear join interface from the pruned_oifs for all
+ * (S,G), but DO NOT flush the change to the kernel (by using
+ * change_interfaces() for example) (3.2) After the pruned_oifs are
+ * eventually cleared in (3.1.1), process the Prune part of the message
+ * normally (setting the prune_oifs and flashing the changes to the
+ * (kernel). (3.3) After the Prune part is processed, process the Join
+ * part normally (by applying any changes to the kernel) (4) If there
+ * were (*,*,RP) Join/Prune, process them.
+ *
+ * If the Join/Prune list is too long, it may result in long processing
+ * overhead. The idea above is not to place any wrong info in the kernel,
+ * because it may result in short-time existing traffic forwarding on
+ * wrong interface. Hopefully, in the future will find a better way to
+ * implement it.
+ */
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"I'm the target of the JOIN/PRUNE message");
+
+ num_groups_tmp = num_groups;
+ data_ptr_start = data_ptr;
+ star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Number of groups to process : %d",num_groups_tmp);
+
+ while (num_groups_tmp--)
+ {
+ /* Search for (*,*,RP) Join */
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG, 0,
+ "Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
+ log(LOG_DEBUG, 0,
+ "Number of join : %d",num_j_srcs );
+ log(LOG_DEBUG, 0,
+ "Number of prune : %d",num_p_srcs );
+ }
+
+ if (!(inet6_equal(&group,&sockaddr6_d))
+ || (encod_src.masklen != STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is not (*,*,RP). Jump to the next group. */
+ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG, 0,
+ "I'm looking for the (*,*,RP) entry , skip to next entry");
+ }
+ continue;
+ }
+
+ /*
+ * (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear
+ * the pruned oif, but do not update the kernel.
+ */
+
+ star_star_rp_found = TRUE;
+ while (num_j_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ rpentry_ptr = rp_find(&source);
+
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue;
+ for (rp_grp_entry_ptr = rpentry_ptr->cand_rp->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ IF_CLR(mifi, &grpentry_ptr->grp_route->pruned_oifs);
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ }
+ }
+ }
+ data_ptr += (num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ }
+
+ /*
+ * Start processing the groups. If this is (*,*,RP), skip it, but process
+ * it at the end.don't forget to reinit data_ptr!
+ */
+
+ data_ptr = data_ptr_start;
+ num_groups_tmp = num_groups;
+
+ while (num_groups_tmp--)
+ {
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ {
+ log(LOG_DEBUG,0,"Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
+ log(LOG_DEBUG,0,"Number of join : %d",num_j_srcs );
+ log(LOG_DEBUG,0,"Number of prune : %d",num_p_srcs );
+ }
+
+ if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr))
+ {
+ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue; /* Ignore this group and jump to the next one */
+ }
+
+
+ if (inet6_equal(&group, &sockaddr6_d)
+ && (encod_group.masklen == STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is (*,*,RP). Jump to the next group. */
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) {
+ log(LOG_DEBUG, 0, "This is (*,*,RP). Jump to next.");
+ }
+ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue;
+ }
+
+ rpentry_ptr = rp_match(&group);
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ continue;
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"The rp for this JOIN/PRUNE is %s",inet6_fmt(&rpentry_ptr->address.sin6_addr));
+
+ data_ptr_group_j_start = data_ptr;
+ data_ptr_group_p_start = data_ptr + num_j_srcs * sizeof(pim6_encod_src_addr_t);
+
+ /*
+ * Scan the Join part for (*,G) Join and then clear the particular
+ * interface from pruned_oifs for all (S,G). If the RP address in the
+ * Join message is different from the local match, ignore the whole
+ * group.
+ */
+
+ num_j_srcs_tmp = num_j_srcs;
+ ignore_group = FALSE;
+
+ while (num_j_srcs_tmp--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr=encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,v);
+
+ if ((encod_src.flags & USADDR_RP_BIT)
+ && (encod_src.flags & USADDR_WC_BIT))
+ {
+ /*
+ * This is the RP address, i.e. (*,G) Join. Check if the
+ * RP-mapping is consistent and if "yes", then Reset the
+ * pruned_oifs for all (S,G) entries.
+ */
+
+ if(!inet6_equal(&rpentry_ptr->address, &source))
+ {
+ ignore_group = TRUE;
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"And I'm not the RP for this address");
+ break;
+ }
+
+ mrtentry_ptr = find_route(&sockaddr6_any, &group,
+ MRTF_WC, DONT_CREATE);
+
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ IF_CLR(mifi, &mrtentry_srcs->pruned_oifs);
+ }
+ break;
+ }
+ }
+
+ if (ignore_group == TRUE)
+ continue;
+
+ data_ptr = data_ptr_group_p_start;
+
+ /* Process the Prune part first */
+
+ while (num_p_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
+ {
+ /* (S,G) prune sent toward S */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue; /* I don't have (S,G) to prune. Ignore. */
+ /*
+ * If the link is point-to-point, timeout the oif
+ * immediately, otherwise decrease the timer to allow other
+ * downstream routers to override the prune.
+ */
+ /* TODO: XXX: increase the entry timer? */
+
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ if ((s_flags & USADDR_RP_BIT)
+ && (!(s_flags & USADDR_WC_BIT)))
+ {
+ /* ~(S,G)RPbit prune sent toward the RP */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+ /* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_RP,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
+
+ /*
+ * TODO: XXX: The spec doens't say what value to use for
+ * the entry time. Use the J/P holdtime.
+ */
+
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+
+ /*
+ * TODO: XXX: The spec says to delete the oif. However,
+ * its timer only should be lowered, so the prune can be
+ * overwritten on multiaccess LAN. Spec BUG.
+ */
+
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
+ {
+ /* (*,G) Prune */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ {
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /*
+ * TODO: XXX: Should check the whole Prune list in
+ * advance for (*,G) prune and if the RP address does
+ * not match the local RP-map, then ignore the whole
+ * group, not only this particular (*,G) prune.
+ */
+ if (!inet6_equal(&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address, &source ))
+ continue; /* The RP address doesn't match. */
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ continue;
+ }
+ /* No (*,G) entry, but found (*,*,RP). Create (*,G) */
+ if (!inet6_equal(&mrtentry_ptr->source->address, &source))
+ continue; /* The RP address doesn't match. */
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
+
+ /*
+ * TODO: XXX: should only lower the oif timer, so it can
+ * be overwritten on multiaccess LAN. Spec bug.
+ */
+
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ } /* (*,G) or (*,*,RP) found */
+ } /* (*,G) prune */
+ } /* while(num_p_srcs--) */
+
+ /* End of (S,G) and (*,G) Prune handling */
+
+ /* Jump back to the Join part and process it */
+ data_ptr = data_ptr_group_j_start;
+ while (num_j_srcs--)
+ {
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ if ((s_flags & USADDR_WC_BIT)
+ && (s_flags & USADDR_RP_BIT))
+ {
+ /* (*,G) Join toward RP */
+ /*
+ * It has been checked already that this RP address is the
+ * same as the local RP-maping.
+ */
+ mrtentry_ptr = find_route(NULL, &group, MRTF_WC,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ /*
+ * Need to update the (S,G) entries, because of the previous
+ * cleaning of the pruned_oifs. The reason is that if the
+ * oifs for (*,G) weren't changed, the (S,G) entries won't be
+ * updated by change_interfaces()
+ */
+
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ continue;
+ }
+
+ if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
+ {
+ /* (S,G) Join toward S */
+ if (mifi == get_iif(&source))
+ continue; /* Ignore this (S,G) Join */
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ /*
+ * TODO: if this is a new entry, send immediately the Join
+ * message toward S. The Join/Prune timer for new entries is
+ * 0, but it does not means the message will be sent
+ * immediately.
+ */
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ /*
+ * Note that we must create (S,G) without the RPbit set. If
+ * we already had such entry, change_interfaces() will reset
+ * the RPbit propertly.
+ */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->source->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ continue;
+ }
+ } /* while(num_j_srcs--) */
+ } /* for all groups */
+
+ /* Now process the (*,*,RP) Join/Prune */
+
+ if (star_star_rp_found == TRUE)
+ return (TRUE);
+ data_ptr = data_ptr_start;
+ while (num_groups--)
+ {
+ /*
+ * The conservative approach is to scan again the whole message, just
+ * in case if we have more than one (*,*,RP) requests.
+ */
+ GET_EGADDR6(&encod_group, data_ptr);
+ GET_HOSTSHORT(num_j_srcs, data_ptr);
+ GET_HOSTSHORT(num_p_srcs, data_ptr);
+
+ group.sin6_addr = encod_group.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ if (!inet6_equal(&group,&sockaddr6_d)
+ || (encod_group.masklen != STAR_STAR_RP_MSK6LEN))
+ {
+ /* This is not (*,*,RP). Jump to the next group. */
+ data_ptr +=
+ (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
+ continue;
+ }
+ /* (*,*,RP) found */
+ while (num_j_srcs--)
+ {
+ /* TODO: XXX: check that the iif is different from the Join oifs */
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ mrtentry_ptr = find_route(&source, NULL, MRTF_PMBR,
+ CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ IF_SET(mifi, &mrtentry_ptr->joined_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] < holdtime)
+ {
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
+ mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
+ }
+ if (mrtentry_ptr->timer < holdtime)
+ SET_TIMER(mrtentry_ptr->timer, holdtime);
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+
+ /*
+ * Need to update the (S,G) and (*,G) entries, because of the
+ * previous cleaning of the pruned_oifs. The reason is that if
+ * the oifs for (*,*,RP) weren't changed, the (*,G) and (S,G)
+ * entries won't be updated by change_interfaces()
+ */
+
+ for (rp_grp_entry_ptr = mrtentry_ptr->source->cand_rp->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ /* Update the (*,G) entry */
+ change_interfaces(grpentry_ptr->grp_route,
+ grpentry_ptr->grp_route->incoming,
+ &grpentry_ptr->grp_route->joined_oifs,
+ &grpentry_ptr->grp_route->pruned_oifs,
+ &grpentry_ptr->grp_route->leaves,
+ &grpentry_ptr->grp_route->asserted_oifs, 0);
+ /* Update the (S,G) entries */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ continue;
+ }
+
+ while (num_p_srcs--)
+ {
+ /* TODO: XXX: can we have (*,*,RP) Prune? */
+ GET_ESADDR6(&encod_src, data_ptr);
+ source.sin6_addr = encod_src.src_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source,
+ v);
+
+ if (!inet6_valid_host(&source))
+ continue;
+ s_flags = encod_src.flags;
+ MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
+ mrtentry_ptr = find_route(&source, NULL , MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ continue;
+ /*
+ * If the link is point-to-point, timeout the oif immediately,
+ * otherwise decrease the timer to allow other downstream routers
+ * to override the prune.
+ */
+ /* TODO: XXX: increase the entry timer? */
+ if (v->uv_flags & VIFF_POINT_TO_POINT)
+ {
+ FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->vif_timers[mifi] >
+ mrtentry_ptr->vif_deletion_delay[mifi])
+ SET_TIMER(mrtentry_ptr->vif_timers[mifi],
+ mrtentry_ptr->vif_deletion_delay[mifi]);
+ }
+ IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
+ {
+ IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
+ IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
+ IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+
+ }
+ } /* For all groups processing (*,*,R) */
+
+ return (TRUE);
+}
+
+
+/*
+ * TODO: NOT USED, probably buggy, but may need it in the future.
+ */
+/*
+ * TODO: create two functions: periodic which timeout the timers and
+ * non-periodic which only check but don't timeout the timers.
+ */
+/*
+ * Create and send Join/Prune messages per interface. Only the entries which
+ * have the Join/Prune timer expired are included. In the special case when
+ * we have ~(S,G)RPbit Prune entry, we must include any (*,G) or (*,*,RP)
+ * Currently the whole table is scanned. In the future will have all routing
+ * entries linked in a chain with the corresponding upstream pim_nbr_entry.
+ *
+ * If pim_nbr is not NULL, then send to only this particular PIM neighbor,
+ */
+int
+send_periodic_pim6_join_prune(mifi, pim_nbr, holdtime)
+ mifi_t mifi;
+ pim_nbr_entry_t *pim_nbr;
+ u_int16 holdtime;
+{
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_ptr;
+ rpentry_t *rpentry_ptr;
+ struct sockaddr_in6 src_addr;
+ struct uvif *v;
+ pim_nbr_entry_t *pim_nbr_ptr;
+ cand_rp_t *cand_rp_ptr;
+
+ /*
+ * Walk through all routing entries. The iif must match to include the
+ * entry. Check first the (*,G) entry and then all associated (S,G). At
+ * the end of the message will add any (*,*,RP) entries. TODO: check
+ * other PIM-SM implementations and decide the more appropriate place to
+ * put the (*,*,RP) entries: in the beginning of the message or at the
+ * end.
+ */
+
+ v = &uvifs[mifi];
+
+ /* Check the (*,G) and (S,G) entries */
+ for (grpentry_ptr = grplist; grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->next)
+ {
+ mrtentry_ptr = grpentry_ptr->grp_route;
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr != (mrtentry_t *) NULL)
+ && (mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (mrtentry_ptr->upstream != pim_nbr))
+ continue;
+
+ /* TODO: XXX: The J/P suppression timer is not in the spec! */
+ if (!IF_ISEMPTY(&mrtentry_ptr->joined_oifs) ||
+ (v->uv_flags & VIFF_DR))
+ {
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group,
+ SINGLE_GRP_MSK6LEN,
+ &grpentry_ptr->rpaddr,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
+ }
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
+ && (!(v->uv_flags & VIFF_DR))
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ {
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &grpentry_ptr->rpaddr,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
+ }
+ }
+
+ /* Check the (S,G) entries */
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (mrtentry_ptr->upstream != pim_nbr))
+ continue;
+
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /* RPbit set */
+
+ src_addr = mrtentry_ptr->source->address;
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
+ || ((find_vif_direct_local(&src_addr) != NO_VIF)
+ && grpentry_ptr->grp_route != (mrtentry_t *) NULL))
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((grpentry_ptr->grp_route->incoming == mifi)
+ && (grpentry_ptr->grp_route->jp_timer
+ <= timer_interval))
+ /* S is directly connected. Send toward RP */
+ add_jp_entry(grpentry_ptr->grp_route->upstream,
+ holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &src_addr, SINGLE_SRC_MSK6LEN,
+ MRTF_RP, PIM_ACTION_PRUNE);
+ }
+ else
+ {
+ /* RPbit cleared */
+ if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs))
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
+ }
+ else
+ {
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->incoming == mifi)
+ && (mrtentry_ptr->jp_timer <= timer_interval))
+ add_jp_entry(mrtentry_ptr->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
+ }
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((mrtentry_ptr->flags & MRTF_SPT)
+ && (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ && (mrtentry_ptr->incoming !=
+ grpentry_ptr->grp_route->incoming)
+ && (grpentry_ptr->grp_route->incoming == mifi)
+ && (grpentry_ptr->grp_route->jp_timer
+ <= timer_interval))
+ add_jp_entry(grpentry_ptr->grp_route->upstream, holdtime,
+ &grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
+ &mrtentry_ptr->source->address,
+ SINGLE_SRC_MSK6LEN, MRTF_RP,
+ PIM_ACTION_PRUNE);
+ }
+ }
+ }
+
+ /* Check the (*,*,RP) entries */
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ rpentry_ptr = cand_rp_ptr->rpentry;
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (rpentry_ptr->upstream != pim_nbr))
+ continue;
+
+
+ /* TODO: XXX: TIMER implem. dependency! */
+ if ((rpentry_ptr->mrtlink != (mrtentry_t *) NULL)
+ && (rpentry_ptr->incoming == mifi)
+ && (rpentry_ptr->mrtlink->jp_timer <= timer_interval))
+ {
+ add_jp_entry(rpentry_ptr->upstream, holdtime,
+ &sockaddr6_d, STAR_STAR_RP_MSK6LEN,
+ &rpentry_ptr->address,
+ SINGLE_SRC_MSK6LEN, MRTF_RP | MRTF_WC,
+ PIM_ACTION_JOIN);
+ }
+ }
+
+ /* Send all pending Join/Prune messages */
+ for (pim_nbr_ptr = v->uv_pim_neighbors;
+ pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
+ pim_nbr_ptr = pim_nbr->next)
+ {
+
+ /* If join/prune to a particular neighbor only was specified */
+ if ((pim_nbr != (pim_nbr_entry_t *) NULL)
+ && (pim_nbr_ptr != pim_nbr))
+ continue;
+
+ pack_and_send_jp6_message(pim_nbr_ptr);
+ }
+
+ return (TRUE);
+}
+
+
+int
+add_jp_entry(pim_nbr, holdtime, group, grp_msklen, source, src_msklen,
+ addr_flags, join_prune)
+ pim_nbr_entry_t *pim_nbr;
+ u_int16 holdtime;
+ struct sockaddr_in6 *group;
+ u_int8 grp_msklen;
+ struct sockaddr_in6 *source;
+ u_int8 src_msklen;
+ u_int16 addr_flags;
+ u_int8 join_prune;
+{
+ build_jp_message_t *bjpm;
+ u_int8 *data_ptr;
+ u_int8 flags = 0;
+ int rp_flag;
+
+
+ bjpm = pim_nbr->build_jp_message;
+
+ if (bjpm != (build_jp_message_t *) NULL)
+ {
+ if ((bjpm->jp_message_size + bjpm->join_list_size +
+ bjpm->prune_list_size + bjpm->rp_list_join_size +
+ bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE)
+ || (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE)
+ || (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE)
+ || (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE)
+ || (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE))
+ {
+ /*
+ * TODO: XXX: BUG: If the list is getting too large, must be
+ * careful with the fragmentation.
+ */
+ pack_and_send_jp6_message(pim_nbr);
+ bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
+ }
+ }
+
+ if (bjpm != (build_jp_message_t *) NULL)
+ {
+ if ((!inet6_equal(&bjpm->curr_group, group)
+ || (bjpm->curr_group_msklen != grp_msklen)
+ || (bjpm->holdtime != holdtime)))
+ {
+ pack_jp6_message(pim_nbr);
+ }
+ }
+
+ if (bjpm == (build_jp_message_t *) NULL)
+ {
+ bjpm = get_jp6_working_buff();
+ pim_nbr->build_jp_message = bjpm;
+ data_ptr = bjpm->jp_message;
+ PUT_EUADDR6(pim_nbr->address.sin6_addr, data_ptr);
+ PUT_BYTE(0, data_ptr); /* Reserved */
+ bjpm->num_groups_ptr = data_ptr++; /* The pointer for numgroups */
+ *(bjpm->num_groups_ptr) = 0; /* Zero groups */
+ PUT_HOSTSHORT(holdtime, data_ptr);
+ bjpm->holdtime = holdtime;
+ bjpm->jp_message_size = data_ptr - bjpm->jp_message;
+ }
+
+ /* TODO: move somewhere else, only when it is a new group */
+ bjpm->curr_group = *group;
+ bjpm->curr_group_msklen = grp_msklen;
+
+ if (inet6_equal(group, &sockaddr6_d) &&
+ (grp_msklen == STAR_STAR_RP_MSK6LEN))
+ rp_flag = TRUE;
+ else
+ rp_flag = FALSE;
+
+ switch (join_prune)
+ {
+ case PIM_ACTION_JOIN:
+ if (rp_flag == TRUE)
+ data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
+ else
+ data_ptr = bjpm->join_list + bjpm->join_list_size;
+ break;
+ case PIM_ACTION_PRUNE:
+ if (rp_flag == TRUE)
+ data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
+ else
+ data_ptr = bjpm->prune_list + bjpm->prune_list_size;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */
+ if (addr_flags & MRTF_RP)
+ flags |= USADDR_RP_BIT;
+ if (addr_flags & MRTF_WC)
+ flags |= USADDR_WC_BIT;
+ PUT_ESADDR6(source->sin6_addr, src_msklen, flags, data_ptr);
+
+ switch (join_prune)
+ {
+ case PIM_ACTION_JOIN:
+ if (rp_flag == TRUE)
+ {
+ bjpm->rp_list_join_size = data_ptr - bjpm->rp_list_join;
+ bjpm->rp_list_join_number++;
+ }
+ else
+ {
+ bjpm->join_list_size = data_ptr - bjpm->join_list;
+ bjpm->join_addr_number++;
+ }
+ break;
+ case PIM_ACTION_PRUNE:
+ if (rp_flag == TRUE)
+ {
+ bjpm->rp_list_prune_size = data_ptr - bjpm->rp_list_prune;
+ bjpm->rp_list_prune_number++;
+ }
+ else
+ {
+ bjpm->prune_list_size = data_ptr - bjpm->prune_list;
+ bjpm->prune_addr_number++;
+ }
+ break;
+ default:
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/* TODO: check again the size of the buffers */
+
+static build_jp_message_t *
+get_jp6_working_buff()
+{
+ build_jp_message_t *bjpm_ptr;
+
+ if (build_jp_message_pool_counter == 0)
+ {
+ bjpm_ptr = (build_jp_message_t *) malloc(sizeof(build_jp_message_t));
+ bjpm_ptr->next = (build_jp_message_t *) NULL;
+ bjpm_ptr->jp_message =
+ (u_int8 *) malloc(MAX_JP_MESSAGE_SIZE +
+ sizeof(pim_jp_encod_grp_t) +
+ 2 * sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->jp_message_size = 0;
+ bjpm_ptr->join_list_size = 0;
+ bjpm_ptr->join_addr_number = 0;
+ bjpm_ptr->join_list = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->prune_list_size = 0;
+ bjpm_ptr->prune_addr_number = 0;
+ bjpm_ptr->prune_list = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->rp_list_join_size = 0;
+ bjpm_ptr->rp_list_join_number = 0;
+ bjpm_ptr->rp_list_join = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->rp_list_prune_size = 0;
+ bjpm_ptr->rp_list_prune_number = 0;
+ bjpm_ptr->rp_list_prune = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
+ sizeof(pim6_encod_src_addr_t));
+ bjpm_ptr->curr_group = sockaddr6_any;
+ bjpm_ptr->curr_group_msklen = 0;
+ bjpm_ptr->holdtime = 0;
+ return bjpm_ptr;
+ }
+ else
+ {
+ bjpm_ptr = build_jp_message_pool;
+ build_jp_message_pool = build_jp_message_pool->next;
+ build_jp_message_pool_counter--;
+ bjpm_ptr->jp_message_size = 0;
+ bjpm_ptr->join_list_size = 0;
+ bjpm_ptr->join_addr_number = 0;
+ bjpm_ptr->prune_list_size = 0;
+ bjpm_ptr->prune_addr_number = 0;
+ bjpm_ptr->curr_group = sockaddr6_any;
+ bjpm_ptr->curr_group_msklen = 0;
+ return (bjpm_ptr);
+ }
+}
+
+
+static void
+return_jp6_working_buff(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ build_jp_message_t *bjpm_ptr = pim_nbr->build_jp_message;
+
+ if (bjpm_ptr == (build_jp_message_t *) NULL)
+ return;
+ /* Don't waste memory by keeping too many free buffers */
+ /* TODO: check/modify the definitions for POOL_NUMBER and size */
+ if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER)
+ {
+ free((void *) bjpm_ptr->jp_message);
+ free((void *) bjpm_ptr->join_list);
+ free((void *) bjpm_ptr->prune_list);
+ free((void *) bjpm_ptr->rp_list_join);
+ free((void *) bjpm_ptr->rp_list_prune);
+ free((void *) bjpm_ptr);
+ }
+ else
+ {
+ bjpm_ptr->next = build_jp_message_pool;
+ build_jp_message_pool = bjpm_ptr;
+ build_jp_message_pool_counter++;
+ }
+ pim_nbr->build_jp_message = (build_jp_message_t *) NULL;
+}
+
+
+/*
+ * TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the Join/Prune
+ * message. However, this particular implementation of PIM processes the
+ * Join/Prune messages faster if (*,*,RP) is at the beginning. Modify some of
+ * the functions below such that the outgoing messages place (*,*,RP) at the
+ * beginning, not at the end.
+ */
+
+static void
+pack_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ build_jp_message_t *bjpm;
+ u_int8 *data_ptr;
+
+ bjpm = pim_nbr->build_jp_message;
+ if ((bjpm == (build_jp_message_t *) NULL)
+ || (inet6_equal(&bjpm->curr_group,&sockaddr6_any)))
+ return;
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+ PUT_EGADDR6(bjpm->curr_group.sin6_addr, bjpm->curr_group_msklen, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->join_addr_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->prune_addr_number, data_ptr);
+ bcopy(bjpm->join_list, data_ptr, bjpm->join_list_size);
+ data_ptr += bjpm->join_list_size;
+ bcopy(bjpm->prune_list, data_ptr, bjpm->prune_list_size);
+ data_ptr += bjpm->prune_list_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->join_list_size = 0;
+ bjpm->join_addr_number = 0;
+#if 0 /* isn't this necessary? */
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+#endif
+ bjpm->prune_list_size = 0;
+ bjpm->prune_addr_number = 0;
+#if 0 /* isn't this necessary? */
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+#endif
+ (*bjpm->num_groups_ptr)++;
+ bjpm->curr_group = sockaddr6_any;
+ bjpm->curr_group_msklen = 0;
+ if (*bjpm->num_groups_ptr == ((u_int8) ~ 0 - 1))
+ {
+ if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
+ {
+ /* Add the (*,*,RP) at the end */
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+ PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
+ bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
+ data_ptr += bjpm->rp_list_join_size;
+ bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
+ data_ptr += bjpm->rp_list_prune_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+ (*bjpm->num_groups_ptr)++;
+ }
+ send_jp6_message(pim_nbr);
+ }
+}
+
+void
+pack_and_send_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ u_int8 *data_ptr;
+ build_jp_message_t *bjpm;
+
+
+ if ((pim_nbr == (pim_nbr_entry_t *) NULL)
+ || ((bjpm = pim_nbr->build_jp_message) == (build_jp_message_t *) NULL))
+ {
+ return;
+ }
+ pack_jp6_message(pim_nbr);
+
+
+ if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
+ {
+ /* Add the (*,*,RP) at the end */
+ data_ptr = bjpm->jp_message + bjpm->jp_message_size;
+
+ PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
+ PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
+ bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
+ data_ptr += bjpm->rp_list_join_size;
+ bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
+ data_ptr += bjpm->rp_list_prune_size;
+ bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
+ bjpm->rp_list_join_size = 0;
+ bjpm->rp_list_join_number = 0;
+ bjpm->rp_list_prune_size = 0;
+ bjpm->rp_list_prune_number = 0;
+ (*bjpm->num_groups_ptr)++;
+ }
+ send_jp6_message(pim_nbr);
+}
+
+static void
+send_jp6_message(pim_nbr)
+ pim_nbr_entry_t *pim_nbr;
+{
+ u_int16 datalen;
+ mifi_t mifi;
+
+ datalen = pim_nbr->build_jp_message->jp_message_size;
+ mifi = pim_nbr->vifi;
+ bcopy(pim_nbr->build_jp_message->jp_message,
+ pim6_send_buf+sizeof(struct pim), datalen);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group , PIM_JOIN_PRUNE, datalen);
+ uvifs[mifi].uv_out_pim6_join_prune++;
+ return_jp6_working_buff(pim_nbr);
+}
+
+/************************************************************************
+ * PIM_ASSERT
+ ************************************************************************/
+int
+receive_pim6_assert(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ register char *pim_message;
+ int datalen;
+{
+ mifi_t mifi;
+ pim6_encod_uni_addr_t eusaddr;
+ pim6_encod_grp_addr_t egaddr;
+ struct sockaddr_in6 source,
+ group;
+ mrtentry_t *mrtentry_ptr,
+ *mrtentry_ptr2;
+ u_int8 *data_ptr;
+ struct uvif *v;
+ u_int32 assert_preference;
+ u_int32 assert_metric;
+ u_int32 assert_rptbit;
+ u_int32 local_metric;
+ u_int32 local_preference;
+ u_int8 local_rptbit;
+ u_int8 local_wins;
+ pim_nbr_entry_t *original_upstream_router;
+
+
+ if ((mifi = find_vif_direct(src)) == NO_VIF)
+ {
+ /*
+ * Either a local vif or somehow received PIM_ASSERT from
+ * non-directly connected router. Ignore it.
+ */
+
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_ASSERT from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ v = &uvifs[mifi];
+ v->uv_in_pim6_assert++;
+ if (uvifs[mifi].uv_flags &
+ (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
+ return (FALSE); /* Shoudn't come on this interface */
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+
+ /* Get the group and source addresses */
+ GET_EGADDR6(&egaddr, data_ptr);
+ GET_EUADDR6(&eusaddr, data_ptr);
+
+ /* Get the metric related info */
+ GET_HOSTLONG(assert_preference, data_ptr);
+ GET_HOSTLONG(assert_metric, data_ptr);
+ assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT;
+
+ source.sin6_addr = eusaddr.unicast_addr;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
+
+ group.sin6_addr = egaddr.mcast_addr;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
+
+ /* Find the longest "active" entry, i.e. the one with a kernel mirror */
+ if (assert_rptbit)
+ {
+ mrtentry_ptr = find_route(NULL, &group,
+ MRTF_WC | MRTF_PMBR, DONT_CREATE);
+ if (mrtentry_ptr != (mrtentry_t *) NULL)
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ mrtentry_ptr =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ }
+ else
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
+ if ((mrtentry_ptr != (mrtentry_t *) NULL))
+ if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
+ {
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ mrtentry_ptr2 = mrtentry_ptr->group->grp_route;
+ if ((mrtentry_ptr2 != (mrtentry_t *) NULL)
+ && (mrtentry_ptr2->flags & MRTF_KERNEL_CACHE))
+ mrtentry_ptr = mrtentry_ptr2;
+ else
+ mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_WC)
+ mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ }
+ }
+ if ((mrtentry_ptr == (mrtentry_t *) NULL)
+ || (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)))
+ /* No routing entry or not "active" entry. Ignore the assert */
+ return (FALSE);
+
+ /* Prepare the local preference and metric */
+ if ((mrtentry_ptr->flags & MRTF_PMBR)
+ || ((mrtentry_ptr->flags & MRTF_SG)
+ && (!(mrtentry_ptr->flags & MRTF_RP))))
+ {
+ /* Either (S,G) (toward S) or (*,*,RP). */
+ /* TODO: XXX: get the info from mrtentry, or source or from kernel ? */
+ /*
+ * local_metric = mrtentry_ptr->source->metric; local_preference =
+ * mrtentry_ptr->source->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+ else
+ {
+ /*
+ * Should be (*,G) or (S,G)RPbit entry. Get what we need from the RP
+ * info.
+ */
+ /* TODO: get the info from mrtentry, RP-entry or kernel? */
+ /*
+ * local_metric =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
+ * local_preference =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference;
+ */
+
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+
+ local_rptbit = (mrtentry_ptr->flags & MRTF_RP);
+ if (local_rptbit)
+ /* Make the RPT bit the most significant one */
+ local_preference |= PIM_ASSERT_RPT_BIT;
+
+
+ if (IF_ISSET(mifi, &mrtentry_ptr->oifs))
+ {
+ /* The ASSERT has arrived on oif */
+
+ /*
+ * TODO: XXX: here the processing order is different from the spec.
+ * The spec requires first eventually to create a routing entry (see
+ * 3.5.2.1(1) and then compare the metrics. Here we compare first the
+ * metrics with the existing longest match entry and if we lose then
+ * create a new entry and compare again. This saves us the
+ * unnecessary creating of a routing entry if we anyway are going to
+ * lose: for example the local (*,*,RP) vs the remote (*,*,RP) or
+ * (*,G)
+ */
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &v->uv_linklocal->pa_addr, assert_preference,
+ assert_metric, src);
+
+ if (local_wins == TRUE)
+ {
+ /* TODO: verify the parameters */
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ return (TRUE);
+ }
+
+ /* Create a "better" routing entry and try again */
+
+ if ((assert_rptbit) && (mrtentry_ptr->flags & MRTF_PMBR))
+ {
+ /* The matching entry was (*,*,RP). Create (*,G) */
+ mrtentry_ptr2 = find_route(NULL, &group, MRTF_WC, CREATE);
+ }
+ else
+ if ((!assert_rptbit) &&
+ (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR)))
+ {
+ /* create (S,G) */
+ mrtentry_ptr2 = find_route(&source, &group, MRTF_SG, CREATE);
+ }
+ else
+ {
+ /* We have no chance to win. Give up and prune the oif */
+ mrtentry_ptr2 = (mrtentry_t *) NULL;
+ }
+
+ if (mrtentry_ptr2 != (mrtentry_t *) NULL)
+ {
+ mrtentry_ptr2->flags &= ~MRTF_NEW;
+
+ /*
+ * TODO: XXX: The spec doesn't say what entry timer value to use
+ * when the routing entry is created because of asserts.
+ */
+
+ SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
+ if (mrtentry_ptr2->flags & MRTF_RP)
+ {
+ /*
+ * Either (*,G) or (S,G)RPbit entry. Get what we need from
+ * the RP info.
+ */
+ /* TODO: where to get the metric+preference from? */
+ /*
+ * local_metric =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
+ * local_preference =
+ * mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference
+ * ;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ local_preference |= PIM_ASSERT_RPT_BIT;
+ }
+ else
+ {
+ /* (S,G) toward the source */
+ /* TODO: where to get the metric from ? */
+ /*
+ * local_metric = mrtentry_ptr->source->metric;
+ * local_preference = mrtentry_ptr->source->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ }
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &v->uv_linklocal->pa_addr, assert_preference,
+ assert_metric, src);
+
+ if (local_wins == TRUE)
+ {
+ /* TODO: verify the parameters */
+ send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
+ return (TRUE);
+ }
+ /* We lost, but have created the entry which has to be pruned */
+ mrtentry_ptr = mrtentry_ptr2;
+ }
+
+ /* Have to remove that outgoing vifi from mrtentry_ptr */
+ IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
+ /* TODO: XXX: TIMER implem. dependency! */
+ if (mrtentry_ptr->timer < pim_assert_timeout)
+ SET_TIMER(mrtentry_ptr->timer, pim_assert_timeout);
+ /*
+ * TODO: XXX: check that the timer of all affected routing entries
+ * has been restarted.
+ */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ return (FALSE); /* Doesn't matter the return value */
+ } /* End of assert received on oif */
+
+
+ if (mrtentry_ptr->incoming == mifi)
+ {
+ /* Assert received on iif */
+ if (assert_rptbit)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ return (TRUE); /* The locally used upstream router will win
+ * the assert, so don't change it. */
+ }
+
+ /*
+ * TODO: where to get the local metric and preference from? system
+ * call or mrtentry is fine?
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+ if (mrtentry_ptr->flags & MRTF_RP)
+ local_preference |= PIM_ASSERT_RPT_BIT;
+
+ local_wins = compare_metrics(local_preference, local_metric,
+ &mrtentry_ptr->upstream->address,
+ assert_preference, assert_metric, src);
+
+ if (local_wins == TRUE)
+ return (TRUE); /* return whatever */
+
+ /* The upstream must be changed to the winner */
+ mrtentry_ptr->preference = assert_preference;
+ mrtentry_ptr->metric = assert_metric;
+ mrtentry_ptr->upstream = find_pim6_nbr(src);
+
+ /* Check if the upstream router is different from the original one */
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ original_upstream_router = mrtentry_ptr->source->upstream;
+ else
+ if (mrtentry_ptr->flags & MRTF_RP)
+ original_upstream_router =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->upstream;
+ else
+ original_upstream_router = mrtentry_ptr->source->upstream;
+ if (mrtentry_ptr->upstream != original_upstream_router)
+ {
+ mrtentry_ptr->flags |= MRTF_ASSERTED;
+ SET_TIMER(mrtentry_ptr->assert_timer, pim_assert_timeout);
+ }
+ else
+ mrtentry_ptr->flags &= ~MRTF_ASSERTED;
+ }
+
+ return (TRUE);
+}
+
+
+int
+send_pim6_assert(source, group, mifi, mrtentry_ptr)
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+ mifi_t mifi;
+ mrtentry_t *mrtentry_ptr;
+{
+ u_int8 *data_ptr;
+ u_int8 *data_start_ptr;
+ u_int32 local_preference;
+ u_int32 local_metric;
+ srcentry_t *srcentry_ptr;
+
+ /* Don't send assert if the outgoing interface a tunnel or register vif */
+ if (uvifs[mifi].uv_flags & (MIFF_REGISTER | VIFF_TUNNEL))
+ return (FALSE);
+
+ data_ptr = (u_int8 *) (pim6_send_buf + sizeof(struct pim));
+ data_start_ptr = data_ptr;
+ PUT_EGADDR6(group->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
+ PUT_EUADDR6(source->sin6_addr, data_ptr);
+
+ /*
+ * TODO: XXX: where to get the metric from: srcentry_ptr or mrtentry_ptr
+ * or from the kernel?
+ */
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) */
+ srcentry_ptr = mrtentry_ptr->source;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
+ */
+ }
+ else
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /* (*,G) or (S,G)RPbit (iif toward RP) */
+ srcentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
+ */
+ }
+ else
+ {
+ /* (S,G) toward S */
+ srcentry_ptr = mrtentry_ptr->source;
+ /*
+ * TODO: set_incoming(srcentry_ptr, PIM_IIF_SOURCE);
+ */
+ }
+
+ /*
+ * TODO: check again! local_metric = srcentry_ptr->metric;
+ * local_preference = srcentry_ptr->preference;
+ */
+ local_metric = mrtentry_ptr->metric;
+ local_preference = mrtentry_ptr->preference;
+
+ if (mrtentry_ptr->flags & MRTF_RP)
+ local_preference |= PIM_ASSERT_RPT_BIT;
+ PUT_HOSTLONG(local_preference, data_ptr);
+ PUT_HOSTLONG(local_metric, data_ptr);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_ASSERT,
+ data_ptr - data_start_ptr);
+ uvifs[mifi].uv_out_pim6_assert++;
+
+ return (TRUE);
+}
+
+
+/* Return TRUE if the local win, otherwise FALSE */
+static int
+compare_metrics(local_preference, local_metric, local_address,
+ remote_preference, remote_metric, remote_address)
+ u_int32 local_preference;
+ u_int32 local_metric;
+ struct sockaddr_in6 *local_address;
+ u_int32 remote_preference;
+ u_int32 remote_metric;
+ struct sockaddr_in6 *remote_address;
+{
+ /* Now lets see who has a smaller gun (aka "asserts war") */
+ /*
+ * FYI, the smaller gun...err metric wins, but if the same caliber, then
+ * the bigger network address wins. The order of threatment is:
+ * preference, metric, address.
+ */
+ /*
+ * The RPT bits are already included as the most significant bits of the
+ * preferences.
+ */
+ if (remote_preference > local_preference)
+ return TRUE;
+ if (remote_preference < local_preference)
+ return FALSE;
+ if (remote_metric > local_metric)
+ return TRUE;
+ if (remote_metric < local_metric)
+ return FALSE;
+ if (inet6_greaterthan(local_address, remote_address))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/************************************************************************
+ * PIM_BOOTSTRAP
+ ************************************************************************/
+#define PIM6_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
+
+int
+receive_pim6_bootstrap(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ char *pim_message;
+ int datalen;
+{
+ u_int8 *data_ptr;
+ u_int8 *max_data_ptr;
+ u_int16 new_bsr_fragment_tag;
+ u_int8 new_bsr_hash_masklen;
+ u_int8 new_bsr_priority;
+ pim6_encod_uni_addr_t new_bsr_uni_addr;
+ struct sockaddr_in6 new_bsr_address;
+ struct rpfctl rpfc;
+ pim_nbr_entry_t *n,
+ *rpf_neighbor;
+ struct sockaddr_in6 neighbor_addr;
+ mifi_t mifi,
+ incoming = NO_VIF;
+ int min_datalen;
+ pim6_encod_grp_addr_t curr_group_addr;
+ pim6_encod_uni_addr_t curr_rp_addr;
+ u_int8 curr_rp_count;
+ u_int8 curr_frag_rp_count;
+ u_int16 reserved_short;
+ u_int16 curr_rp_holdtime;
+ u_int8 curr_rp_priority;
+ u_int8 reserved_byte;
+ struct in6_addr curr_group_mask;
+ grp_mask_t *grp_mask_ptr;
+ grp_mask_t *grp_mask_next;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+ struct sockaddr_in6 prefix_h,
+ prefix_h2,
+ group_,
+ rpp_;
+ int i;
+ struct uvif *v;
+
+
+ if ((mifi=find_vif_direct(src)) == NO_VIF)
+ {
+ /*
+ * Either a local vif or somehow received PIM_BOOTSTRAP from
+ * non-directly connected router. Ignore it.
+ */
+ if (local_address(src) == NO_VIF)
+ log(LOG_INFO, 0,
+ "Ignoring PIM_BOOTSTRAP from non-neighbor router %s",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_BOOTSTRAP_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap message size(%u) is"
+ " too short from %s",
+ datalen, inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+
+ v = &uvifs[mifi];
+ v->uv_in_pim6_bootsrap++;
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+
+ /* Parse the PIM_BOOTSTRAP message */
+ GET_HOSTSHORT(new_bsr_fragment_tag, data_ptr);
+ GET_BYTE(new_bsr_hash_masklen, data_ptr);
+ GET_BYTE(new_bsr_priority, data_ptr);
+ GET_EUADDR6(&new_bsr_uni_addr, data_ptr);
+
+ /*
+ * BSR address must be a global unicast address.
+ * [draft-ietf-pim-ipv6-01.txt sec 4.5]
+ */
+ if (IN6_IS_ADDR_MULTICAST(&new_bsr_uni_addr.unicast_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&new_bsr_uni_addr.unicast_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&new_bsr_uni_addr.unicast_addr)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_bootstrap: invalid BSR address: %s",
+ inet6_fmt(&new_bsr_uni_addr.unicast_addr));
+ return(FALSE);
+ }
+
+ new_bsr_address.sin6_addr = new_bsr_uni_addr.unicast_addr;
+ new_bsr_address.sin6_len = sizeof(new_bsr_address);
+ new_bsr_address.sin6_family = AF_INET6;
+ new_bsr_address.sin6_scope_id = inet6_uvif2scopeid(&new_bsr_address, v);
+
+ if (local_address(&new_bsr_address) != NO_VIF)
+ {
+ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
+ log(LOG_DEBUG, 0,
+ "receive_pim6_bootstrap: Bootstrap from myself(%s), ignored.",
+ inet6_fmt(&new_bsr_address.sin6_addr));
+ return (FALSE); /* The new BSR is one of my local addresses */
+ }
+
+ /*
+ * Compare the current BSR priority with the priority of the BSR included
+ * in the message.
+ */
+ /*
+ * TODO: if I am just starting and will become the BSR, I should accept
+ * the message coming from the current BSR and get the current
+ * Cand-RP-Set.
+ */
+
+ if ((curr_bsr_priority > new_bsr_priority) ||
+ ((curr_bsr_priority == new_bsr_priority)
+ && (inet6_greaterthan(&curr_bsr_address, &new_bsr_address))))
+ {
+ /* The message's BSR is less preferred than the current BSR */
+ log(LOG_DEBUG, 0,
+ "receive_pim6_bootstrap: BSR(%s, prio=%d) is less preferred"
+ " than the current BSR(%s, prio=%d)",
+ inet6_fmt(&new_bsr_address.sin6_addr), new_bsr_priority,
+ inet6_fmt(&curr_bsr_address.sin6_addr), curr_bsr_priority);
+ return (FALSE); /* Ignore the received BSR message */
+ }
+
+ /* Check the iif, if this was PIM-ROUTERS multicast */
+ if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &allpim6routers_group.sin6_addr))
+ {
+ k_req_incoming(&new_bsr_address, &rpfc);
+ if ((rpfc.iif == NO_VIF) ||
+ IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ {
+ /* coudn't find a route to the BSR */
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: can't find a route to the BSR(%s)",
+ inet6_fmt(&new_bsr_address.sin6_addr));
+ return (FALSE);
+ }
+
+ neighbor_addr = *src;
+ incoming = rpfc.iif;
+
+ if (uvifs[incoming].uv_flags &
+ (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from an invalid interface(%s)",
+ uvifs[incoming].uv_name);
+ return (FALSE); /* Shoudn't arrive on that interface */
+ }
+
+ /* Find the upstream router */
+
+ for (n = uvifs[incoming].uv_pim_neighbors; n != NULL; n = n->next)
+ {
+ if (inet6_lessthan(&neighbor_addr, &n->address))
+ continue;
+ if (inet6_equal(&neighbor_addr, &n->address))
+ {
+ rpf_neighbor = n;
+ break;
+ }
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from an unrecognized "
+ "neighbor(%s) on %s",
+ inet6_fmt(&neighbor_addr.sin6_addr), uvifs[incoming].uv_name);
+ return (FALSE); /* No neighbor toward BSR found */
+ }
+
+ /* redundant checks? */
+ if ((n == (pim_nbr_entry_t *) NULL ))
+ {
+ return (FALSE); /* Sender of this message is not the RPF*/
+ }
+ /* neighbor */
+ if(!(inet6_equal(&n->address, src)))
+ {
+ return (FALSE);
+ }
+ }
+ else
+ {
+ if (local_address(dst) == NO_VIF)
+ /*
+ * TODO: XXX: this situation should be handled earlier: The
+ * destination is neither ALL_PIM_ROUTERS nor me
+ */
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap with an invalid dst(%s)",
+ inet6_fmt(&dst->sin6_addr));
+ return (FALSE);
+
+ /* Probably unicasted from the current DR */
+ if (cand_rp_list != (cand_rp_t *) NULL)
+ {
+ /*
+ * Hmmm, I do have a Cand-RP-list, but some neighbor has a
+ * different opinion and is unicasting it to me. Ignore this guy.
+ */
+ log(LOG_INFO, 0,
+ "receive_pim6_bootstrap: Bootstrap received but we already "
+ "have RPs. ignored.");
+ return (FALSE);
+ }
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER))
+ continue;
+ if (inet6_equal(&uvifs[mifi].uv_linklocal->pa_addr,dst))
+ {
+ incoming = mifi;
+ break;
+ }
+ }
+ if (incoming == NO_VIF)
+ {
+ /* Cannot find the receiving iif toward that DR */
+ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
+ log(LOG_DEBUG, 0,
+ "Unicast boostrap message from %s to %s ignored: "
+ "cannot find iif",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(&dst->sin6_addr));
+ return (FALSE);
+ }
+ /*
+ * TODO: check the sender is directly connected and I am really the
+ * DR.
+ */
+ }
+
+ if (cand_rp_flag == TRUE)
+ {
+ /* If change in the BSR address, send immediately Cand-RP-Adv */
+ /* TODO: use some random delay? */
+
+ if (!inet6_equal(&new_bsr_address , &curr_bsr_address))
+ {
+ send_pim6_cand_rp_adv();
+ SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
+ }
+ }
+
+ /* Forward the BSR Message first and then update the RP-set list */
+ /* XXX: should we do sanity checks before forwarding?? */
+ /* TODO: if the message was unicasted to me, resend? */
+
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (mifi == incoming)
+ continue;
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER | VIFF_TUNNEL | VIFF_NONBRS))
+ continue;
+
+ bcopy(pim_message, (char *)(pim6_send_buf), datalen);
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_BOOTSTRAP,
+ datalen - sizeof(struct pim));
+ }
+
+ max_data_ptr = (u_int8 *) pim_message + datalen;
+
+ /*
+ * TODO: XXX: this 24 is HARDCODING!!! Do a bunch of definitions and make
+ * it stylish!
+ * 24 = Encoded-Group Address(20) + RP-cound(1) + Frag-RP(1) + Reserved(2)
+ */
+ min_datalen = 24;
+
+ if ((new_bsr_fragment_tag != curr_bsr_fragment_tag) ||
+ (inet6_equal(&new_bsr_address, &curr_bsr_address)))
+ {
+ /* Throw away the old segment */
+ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
+ }
+
+ curr_bsr_address = new_bsr_address;
+ curr_bsr_priority = new_bsr_priority;
+ curr_bsr_fragment_tag = new_bsr_fragment_tag;
+ MASKLEN_TO_MASK6(new_bsr_hash_masklen, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+
+ while (data_ptr + min_datalen <= max_data_ptr)
+ {
+ GET_EGADDR6(&curr_group_addr, data_ptr);
+ GET_BYTE(curr_rp_count, data_ptr);
+ GET_BYTE(curr_frag_rp_count, data_ptr);
+ GET_HOSTSHORT(reserved_short, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ if (curr_rp_count == 0)
+ {
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ delete_grp_mask(&cand_rp_list, &grp_mask_list,
+ &group_, curr_group_mask);
+ continue;
+ }
+ if (curr_rp_count == curr_frag_rp_count)
+ {
+ /* Add all RPs */
+ while (curr_frag_rp_count--)
+ {
+ /*
+ * Sanity for the data length; the data packet must contain
+ * Encoded-Unicast-RP-Address(18) + RP-Holdtime(2) +
+ * RP-Priority(1) + Reserved(1).
+ */
+ if (data_ptr + PIM6_ENCODE_UNI_ADDR_LEN + sizeof(u_int32_t)
+ > max_data_ptr) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_bootstrap: Bootstrap from %s on %s "
+ "does not have enough length to contatin RP information",
+ inet6_fmt(&src->sin6_addr), v->uv_name);
+
+ /*
+ * Ignore the rest of the message.
+ * XXX: should we discard the entire message?
+ */
+ goto garbage_collect;
+ }
+
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ rpp_.sin6_len = sizeof(rpp_);
+ rpp_.sin6_family = AF_INET6;
+ /*
+ * The cand_rp address scope should be global.
+ * XXX: however, is a site-local RP sometimes useful?
+ * we currently discard such RP...
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&rpp_.sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&rpp_.sin6_addr)) {
+ log(LOG_WARNING, 0,
+ "receive_pim6_bootstrap: invalid RP address: %s",
+ inet6_fmt(&rpp_.sin6_addr));
+ continue;
+ }
+ rpp_.sin6_scope_id = 0;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_len = sizeof(group_);
+ group_.sin6_family = AF_INET6;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, curr_rp_priority,
+ curr_rp_holdtime, &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ continue;
+ }
+ /*
+ * This is a partial list of the RPs for this group prefix. Save
+ * until all segments arrive.
+ */
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ prefix_h.sin6_addr.s6_addr[i] =
+ curr_group_addr.mcast_addr.s6_addr[i]
+ & curr_group_mask.s6_addr[i];
+ }
+
+ for (grp_mask_ptr = segmented_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ prefix_h2.sin6_addr.s6_addr[i] =
+ grp_mask_ptr->group_addr.sin6_addr.s6_addr[i]
+ & grp_mask_ptr->group_mask.s6_addr[i];
+ }
+
+ if (inet6_lessthan(&prefix_h2, &prefix_h))
+ continue;
+ else
+ break;
+ }
+ if ((grp_mask_ptr != (grp_mask_t *) NULL)
+ && (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_addr.sin6_addr,
+ &curr_group_addr.mcast_addr))
+ && (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_mask, &curr_group_mask))
+ && (grp_mask_ptr->group_rp_number + curr_frag_rp_count
+ == curr_rp_count))
+ {
+ /* All missing PRs have arrived. Add all RP entries */
+ while (curr_frag_rp_count--)
+ {
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ rpp_.sin6_scope_id=0;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &rpp_,
+ curr_rp_priority,
+ curr_rp_holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ /* Add the rest from the previously saved segments */
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &grp_rp_entry_ptr->rp->rpentry->address,
+ grp_rp_entry_ptr->priority,
+ grp_rp_entry_ptr->holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ delete_grp_mask(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ &group_,
+ curr_group_mask);
+ }
+ else
+ {
+ /* Add the partially received RP-list to the group of pending RPs */
+ while (curr_frag_rp_count--)
+ {
+ GET_EUADDR6(&curr_rp_addr, data_ptr);
+ GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
+ GET_BYTE(curr_rp_priority, data_ptr);
+ GET_BYTE(reserved_byte, data_ptr);
+ MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
+ rpp_.sin6_addr = curr_rp_addr.unicast_addr;
+ group_.sin6_addr = curr_group_addr.mcast_addr;
+ group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
+ add_rp_grp_entry(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ &rpp_,
+ curr_rp_priority,
+ curr_rp_holdtime,
+ &group_,
+ curr_group_mask,
+ curr_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ }
+ }
+
+
+ garbage_collect:
+ /*
+ * Garbage collection. Check all group prefixes and if the fragment_tag
+ * for a group_prefix is the same as curr_bsr_fragment_tag, then remove
+ * all RPs for this group_prefix which have different fragment tag.
+ */
+
+ for (grp_mask_ptr = grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
+ {
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
+ delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ grp_rp_entry_ptr);
+ }
+ }
+ }
+
+ /* Cleanup also the list used by incompleted segments */
+
+ for (grp_mask_ptr = segmented_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
+ {
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
+ delete_rp_grp_entry(&segmented_cand_rp_list,
+ &segmented_grp_mask_list,
+ grp_rp_entry_ptr);
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+void
+send_pim6_bootstrap()
+{
+ int datalen;
+ mifi_t mifi;
+
+ if ((datalen = create_pim6_bootstrap_message(pim6_send_buf)))
+ {
+ for (mifi = 0; mifi < numvifs; mifi++)
+ {
+ if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
+ MIFF_REGISTER | VIFF_TUNNEL))
+ continue;
+
+ send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
+ &allpim6routers_group, PIM_BOOTSTRAP, datalen);
+ uvifs[mifi].uv_out_pim6_bootsrap++;
+ }
+ }
+}
+
+/************************************************************************
+ * PIM_CAND_RP_ADV
+ ************************************************************************/
+/*
+ * minimum length of a cand. RP adv. message;
+ * length of PIM header + prefix-cnt(1) + priority(1) + holdtime(2) +
+ * encoded unicast RP addr(18)
+ */
+#define PIM6_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
+
+/*
+ * If I am the Bootstrap router, process the advertisement, otherwise ignore
+ * it.
+ */
+int
+receive_pim6_cand_rp_adv(src, dst, pim_message, datalen)
+ struct sockaddr_in6 *src,
+ *dst;
+ char *pim_message;
+ register int datalen;
+{
+ u_int8 prefix_cnt;
+ u_int8 priority;
+ u_int16 holdtime;
+ pim6_encod_uni_addr_t cand_rp_addr;
+ pim6_encod_grp_addr_t encod_grp_addr;
+ u_int8 *data_ptr;
+ struct in6_addr grp_mask;
+ struct sockaddr_in6 group_, rpp_;
+
+ pim6dstat.in_pim6_cand_rp++;
+
+ /* if I am not the bootstrap RP, then do not accept the message */
+ if ((cand_bsr_flag != FALSE) &&
+ !inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: receive cand_RP from %s "
+ "but I'm not the BSR",
+ inet6_fmt(&src->sin6_addr));
+ return (FALSE);
+ }
+
+ /* sanity check for the minimum length */
+ if (datalen < PIM6_CAND_RP_ADV_MINLEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: cand_RP message size(%u) is"
+ " too short from %s",
+ datalen, inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+ datalen -= PIM6_CAND_RP_ADV_MINLEN;
+
+ data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
+ /* Parse the CAND_RP_ADV message */
+ GET_BYTE(prefix_cnt, data_ptr);
+ GET_BYTE(priority, data_ptr);
+ GET_HOSTSHORT(holdtime, data_ptr);
+ GET_EUADDR6(&cand_rp_addr, data_ptr);
+
+ /*
+ * The RP Address field is set to the globally reachable IPv6 address
+ * [draft-ietf-pim-ipv6].
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&cand_rp_addr.unicast_addr)) {
+ /* XXX: prohibit a site-local address as well? */
+ log(LOG_WARNING, 0,
+ "receive_pim6_cand_rp_adv: non global address(%s) as RP",
+ inet6_fmt(&cand_rp_addr.unicast_addr));
+ return(FALSE);
+ }
+
+ memset(&rpp_, 0, sizeof(rpp_));
+ if (prefix_cnt == 0)
+ {
+ /* The default ff:: and masklen of 8 */
+ MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
+ rpp_.sin6_addr = cand_rp_addr.unicast_addr;
+ /*
+ * note that we don't have to take care of scope id, since
+ * the address should be global(see above).
+ */
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, priority, holdtime,
+ &sockaddr6_d, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ return (TRUE);
+ }
+ while (prefix_cnt--)
+ {
+ /*
+ * Sanity check for the message length.
+ * XXX: do we have to do the check at an earlier stage and
+ * discard the whole message (instead of adopting a part of it)
+ * if it's bogus?
+ */
+ if (datalen < PIM6_ENCODE_GRP_ADDR_LEN) {
+ log(LOG_NOTICE, 0,
+ "receive_pim6_cand_rp_adv: cand_RP message from %s is"
+ " too short to contain enough groups",
+ inet6_fmt(&src->sin6_addr));
+ return(FALSE);
+ }
+ datalen -= PIM6_ENCODE_GRP_ADDR_LEN;
+
+ GET_EGADDR6(&encod_grp_addr, data_ptr);
+ MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
+ group_.sin6_addr = encod_grp_addr.mcast_addr;
+ group_.sin6_scope_id = 0; /* XXX: what if for scoped multicast addr? */
+ rpp_.sin6_addr = cand_rp_addr.unicast_addr;
+ /* see above note on scope id */
+
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &rpp_, priority, holdtime,
+ &group_, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+
+ return (TRUE);
+}
+
+int
+send_pim6_cand_rp_adv()
+{
+ u_int8 prefix_cnt;
+ struct in6_addr grp_mask;
+ pim6_encod_grp_addr_t encod_grp_addr;
+ u_int8 *data_ptr;
+ struct sockaddr_in6 group_;
+
+ if (!inet6_valid_host(&curr_bsr_address))
+ return (FALSE); /* No BSR yet */
+
+ if( inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ /* I am the BSR and have to include my own group_prefix stuff */
+ prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr;
+ if (prefix_cnt == 0)
+ {
+ /* The default ff00:: and masklen of 8 */
+ MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
+ add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ &my_cand_rp_address, my_cand_rp_priority,
+ my_cand_rp_holdtime,
+ &sockaddr6_d,
+ grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ return (TRUE);
+ }
+ /* TODO: hardcoding!! */
+ /* 18 = sizeof(pim6_encod_uni_addr_t) without padding */
+ data_ptr = cand_rp_adv_message.buffer + (4 + 18);
+
+ while (prefix_cnt--)
+ {
+ GET_EGADDR6(&encod_grp_addr, data_ptr);
+ MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
+ group_.sin6_addr = encod_grp_addr.mcast_addr;
+ group_.sin6_scope_id = 0; /*XXX */
+ add_rp_grp_entry(&cand_rp_list,
+ &grp_mask_list,
+ &my_cand_rp_address, my_cand_rp_priority,
+ my_cand_rp_holdtime,
+ &group_, grp_mask,
+ my_bsr_hash_mask,
+ curr_bsr_fragment_tag);
+ }
+ return (TRUE);
+ }
+
+ data_ptr = (u_int8 *) (pim6_send_buf + sizeof(struct pim));
+
+ bcopy((char *)cand_rp_adv_message.buffer, (char *) data_ptr,
+ cand_rp_adv_message.message_size);
+
+ send_pim6(pim6_send_buf, &my_cand_rp_address, &curr_bsr_address ,
+ PIM_CAND_RP_ADV, cand_rp_adv_message.message_size);
+ pim6dstat.out_pim6_cand_rp++;
+
+ return TRUE;
+}
diff --git a/usr.sbin/pim6sd/pim6_proto.h b/usr.sbin/pim6sd/pim6_proto.h
new file mode 100644
index 0000000..a02e3d3
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6_proto.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/* Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIM6_PROTO_H
+#define PIM6_PROTO_H
+#include "defs.h"
+#include "vif.h"
+#include "mrt.h"
+
+extern build_jp_message_t *build_jp_message_pool;
+extern int build_jp_message_pool_counter;
+extern struct sockaddr_in6 sockaddr6_any;
+extern struct sockaddr_in6 sockaddr6_d;
+
+extern int receive_pim6_hello __P((struct sockaddr_in6 *src,
+ char *pim_message, int datalen));
+
+extern int send_pim6_hello __P((struct uvif *v, u_int16 holdtime));
+extern void delete_pim6_nbr __P((pim_nbr_entry_t *nbr_delete));
+
+extern int receive_pim6_register __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_null_register __P((mrtentry_t *r));
+extern int receive_pim6_register_stop __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message,
+ int datalen));
+extern int send_pim6_register __P((char *pkt));
+extern int receive_pim6_join_prune __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int join_or_prune __P((mrtentry_t *mrtentry_ptr,
+ pim_nbr_entry_t *upstream_router));
+extern int receive_pim6_assert __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_assert __P((struct sockaddr_in6 *source, struct sockaddr_in6 *group,
+ vifi_t vifi,
+ mrtentry_t *mrtentry_ptr));
+extern int send_periodic_pim6_join_prune __P((vifi_t vifi,
+ pim_nbr_entry_t *pim_nbr,
+ u_int16 holdtime));
+extern int add_jp_entry __P((pim_nbr_entry_t *pim_nbr,
+ u_int16 holdtime, struct sockaddr_in6 *group,
+ u_int8 grp_msklen, struct sockaddr_in6 *source,
+ u_int8 src_msklen,
+ u_int16 addr_flags,
+ u_int8 join_prune));
+extern void pack_and_send_jp6_message __P((pim_nbr_entry_t *pim_nbr));
+extern int receive_pim6_cand_rp_adv __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int receive_pim6_bootstrap __P((struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
+ char *pim_message, int datalen));
+extern int send_pim6_cand_rp_adv __P(());
+extern void send_pim6_bootstrap __P(());
+
+
+#endif
diff --git a/usr.sbin/pim6sd/pim6sd.8 b/usr.sbin/pim6sd/pim6sd.8
new file mode 100644
index 0000000..5417ef8
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.8
@@ -0,0 +1,149 @@
+.\" Copyright (C) 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pim6sd.8,v 1.4 1999/12/16 05:38:06 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 10, 1999
+.Dt PIM6SD 8
+.Os KAME
+.Sh NAME
+.Nm pim6sd
+.Nd PIM for IPv6 sparse mode daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d Op debug_level Op ,debug_level
+.Op Fl f
+.Sh DESCRIPTION
+.Nm Pim6sd
+is an IPv6 multicast routing daemon, which supports
+PIMv2(Protocol Independent Multicast Version 2) sparse mode
+for IPv6.
+.Pp
+Options supported by
+.Nm pim6sd :
+.Bl -tag -width Ds
+.It Fl c Ar configfile
+Specify alternate location,
+.Ar configfile ,
+for configuration file.
+By default,
+.Pa /usr/local/v6/etc/pim6sd.conf
+is used.
+.It Fl d
+Specify debug levels. If this option is specified without any arguments,
+all debug messages will be printed out.
+A subset of the messages to be printed out can be specified
+as arguments of the option.
+Valid debug levels are
+.Ic mld_proto, mld_timer, mld_member, mld, switch, trace, mtrace, traceroute,
+.Ic timeout, callout, pkt, packets, interfaces, vif, kernel, cache, mfc,
+.Ic k_cache, k_mfc, rsrr, pim_detail, pim_hello, pim_neighbors, pim_register,
+.Ic registers, pim_join_prune, pim_j_p, pim_jp, pim_bootstrap, pim_bsr, bsr,
+.Ic bootstrap, pim_asserts, pim_cand_rp, pim_c_rp, pim_rp, rp, pim_routes,
+.Ic pim_routing, pim_mrt, pim_timers, pim_rpf, rpf, pim, routes, routing,
+.Ic mrt, routers, mrouters, neighbors, timers,
+and
+.Ic asserts.
+.It Fl f
+Do not become daemon, run in foreground. This option is for debugging
+use.
+.El
+.Pp
+.Nm Pim6sd
+automatically configures itself to forward on all multicast-capable
+interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
+the "loopback interface").
+To override the default configuration,
+configuration commands may be placed in
+.Pa /usr/local/v6/etc/pim6sd.conf
+(or an alternative file, specified by the "\-c" option).
+.Pp
+The
+.Nm
+program dumps its current routing information to a dump file when
+it receives a SIGUSR1 signal.
+The information includes a list of PIM neighbors,
+.Nm
+internal multicast routing table, and
+BSR and RP related information. Also, the program dumps its internal
+statistics to a file when it receives a SIGINFO signal.
+.Pp
+When
+.Nm
+receives a SIGUSR2 signal, it rereads the configuration file and
+reset its debug level.
+.Pp
+The
+.Nm
+program puts its logs to a separate file
+.Pa (/var/log/pim6sd.log).
+The log level can be configured by the
+.Fl d
+command line option or the configuration file.
+.\"
+.Sh FILES
+.Bl -tag -width /usr/local/v6/etc/pim6sd.conf -compact
+.It Pa /usr/local/v6/etc/pim6sd.conf
+The default configuration file.
+.It Pa /var/run/pim6sd.dump
+The file to which
+.Nm
+dumps its internal status.
+.It Pa /var/run/pim6sd.stat
+The file to which
+.Nm
+dumps its internal statistics.
+.It Pa /var/log/pim6sd.log
+The pim6sd specific log file.
+.El
+.Sh SEE ALSO
+.Xr daemon 3 ,
+.Xr pim6sd.conf 5
+.Sh HISTORY
+The
+.Nm
+command is developed by Mickael Hoerdt at LSIIT Laboratory.
+It is based on IPv4 PIM sparse-mode
+.Nm pimd
+developed at University of Southern California,
+which has also been derived from
+.Nm mrouted.
+.Nm Mrouted
+is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
+.\"
+.Sh BUGS
+.Nm Pim6sd
+does not contain any unicast routing engine, so a unicast routing
+daemon needs to run on the system.
+.Pp
+The kernel unicast routing table is periodically polled by
+.Nm
+in order to follow changes of existing unicast routes.
+.\"
diff --git a/usr.sbin/pim6sd/pim6sd.conf.5 b/usr.sbin/pim6sd/pim6sd.conf.5
new file mode 100644
index 0000000..ea27c06
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.conf.5
@@ -0,0 +1,330 @@
+.\" Copyright (C) 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pim6sd.conf.5,v 1.7 1999/12/03 07:31:45 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd Oct 6, 1999
+.Dt PIM6SD.CONF 5
+.Os KAME
+.Sh NAME
+.Nm pim6sd.conf
+.Nd config file for pim6sd, PIM-SM daemon for IPv6
+.\"
+.Sh DESCRIPTION
+The
+.Nm pim6sd
+configuration file consists of a sequence of statements terminated
+by a semi-colon (`;'), each of which specifies how the daemon treats
+each interface on the system, specifies some parameters of the PIM
+protocol, and requires some special behavior defined by the protocol.
+.Pp
+Each statement can be constructed by multiple lines.
+.Pp
+Lines beginning with
+.Ql #
+are comments.
+.\".Pp
+.\"Note that
+.\".Nm pim6sd
+.\"works even without the configuration file, although the daemon
+.\"will warn that there is no configuration file.
+.\"In such a case, the daemon will automatically set the default value
+.\"to each configurable parameter.
+.\"
+.Pp
+The following statements can be specified in the configuration file.
+.Pp
+.Bl -tag -width Ds -compact
+.It Xo
+.Ic log
+.Ar option...
+.Ic ;
+.Xc
+Specify debug messages to be printed out. Each
+.Ar option
+usually specifies a subset of the messages to be printed.
+If an
+.Ar option
+begins with
+.Ic no ,
+it means that the set of the messages that are specified by the option
+will not be printed. For example,
+.Ic `all nomld'
+means that all the messages except MLD related ones will be printed.
+Valid options are
+.Ic mld_proto, mld_timer, mld_member, mld, switch, trace, mtrace, traceroute,
+.Ic timeout, callout, pkt, packets, interfaces, vif, kernel, cache, mfc,
+.Ic k_cache, k_mfc, rsrr, pim_detail, pim_hello, pim_neighbors, pim_register,
+.Ic registers, pim_join_prune, pim_j_p, pim_jp, pim_bootstrap, pim_bsr, bsr,
+.Ic bootstrap, pim_asserts, pim_cand_rp, pim_c_rp, pim_rp, rp, pim_routes,
+.Ic pim_routing, pim_mrt, pim_timers, pim_rpf, rpf, pim, routes, routing,
+.Ic mrt, routers, mrouters, neighbors, timers, asserts,
+and
+.Ic all .
+.\"
+.It Xo
+.Ic reverselookup (yes \(ba no);
+.Xc
+Specifies if a hostname for an IPv6 address should be resolved
+on logging.
+.Ic yes
+means a hostname should be resolved, and
+.Ic no
+means should not.
+By default, a hostname is not resolved.
+.\"
+.It Xo
+.Ic phyint Ar interface
+.Op disable
+.Ic ;
+.Xc
+Specifies
+.Nm
+to ignore the interface even if the interface is multicast-capable.
+Note that PIM will be activated on all interfaces by default(including
+the case where there is no configuration file).
+Interfaces are specified in the form of "name unit", such as
+.Ar gif0
+and
+.Ar ep1.
+.\"
+.It Xo
+.Ic phyint Ar interface
+.Op preference Ar preference
+.Op metric Ar metric
+.Op nolistener
+.Ic ;
+.Xc
+Specifies the preference and/or metric values when sending a PIM
+assert message on the interface.
+If another optional parameter
+.Ic nolistener
+is specified,
+.Nm pim6sd
+will not send any MLD packets on the interface.
+This option is usually meaningless but will be useful when
+MLD messages are noisy (e.g. when debugging) and there is surely no
+listner on the interface.
+.\"
+.It Xo
+.Ic default_source_preference Ar preference;
+.Xc
+Specifies a default preference value when sending a PIM assert message.
+Preferences are used by assert elections to determine upstream routers.
+Currently
+.Nm pim6sd
+cannot reliably obtain preferences and metrics from the
+unicast routing protocols, so a default value may be configured.
+The default preference is 1024.
+.\"
+.It Ic default_source_metric Ar metric;
+Specifies a default metric value when sending a PIM assert message.
+It is recommended that preferences be set such that metrics are never
+consulted. However, default metrics may also be set and will default to
+1024.
+.\"
+.It Xo
+.Ic granularity Ar second;
+.Xc
+Specifies timer granularity in seconds.
+The default value is 5.
+.\"
+.It Xo
+.Ic hello_period Ar period Ar coef;
+.Xc
+.Ar Period
+specifies the period in second between 2 hello messages.
+.Ar Coef
+is the coefficient to determine the hello holdtime;
+the holdtime will be
+.Ar period
+*
+.Ar coef .
+The default values of the period and the coefficient are 30 and 3.5,
+respectively. The default holdtime is 105 seconds as a result.
+.\"
+.It Xo
+.Ic join_prune_period Ar period Ar coef;
+.Xc
+.Ar Period
+specifies the period in second between 2 join/prune messages.
+.Ar Coef
+is the coefficient to determine the join/prune holdtime;
+the holdtime will be
+.Ar period
+*
+.Ar coef .
+The default values of the period and the coefficient are 60 and 3.5,
+respectively. Consequently, the default holdtime is 210 seconds.
+.\"
+.It Xo
+.Ic data_timeout Ar timer;
+.Xc
+Specifies the time after which (S,G) state for a silent source will be
+deleted.
+The default value is 210.
+.\"
+.It Xo
+.Ic register_suppression_timeout Ar interval;
+.Xc
+.Ar Interval
+specifies the interval between receiving a Register-Stop and allowing
+PIM Register to be send again.
+The default value is 60.
+.\"
+.It Xo
+.Ic probe_time Ar timer;
+.Xc
+.Ar Timer
+specifies the time between sending a null Register and the
+Register-Suppression-Timer expiring unless it is restarted by
+receiving a Register-Stop.
+The default value is 5.
+.\"
+.It Xo
+.Ic assert_timeout Ar interval;
+.Xc
+.Ar Interval
+specifies the interval between the last time an Assert is received and
+the time at which the assert is timeout.
+The default value is 180.
+.\"
+.It Xo
+.Ic cand_rp
+.Op Ar interface
+.Op Ic time Ar time
+.Op Ic priority Ar priority
+.Ic ;
+.Xc
+Specifies to act as a candidate Rendezvous Point(RP).
+It is recommended to specify
+.Ic cand_rp
+only in typical usage.
+All other parameters are optional and will be set automatically.
+If an
+.Ar interface
+is specified,
+.Nm pim6sd
+will search for a global address on the specified interface
+and set the address in Candidate RP Advertisements.
+An optional parameter
+.Ic time
+specifies the interval of two succeeding advertisements in seconds.
+Its default value is 60.
+2.5 *
+.Ar time
+will be set to Candidate-RP-Advertisement messages.
+Another optional parameter
+.Ic priority
+specifies the priority of the RP.
+The default value is 0, which means the highest priority.
+.\"
+.It Xo
+.Ic group_prefix Ar prefix;
+.Xc
+When acting as a Rendezvous Point(RP),
+.Ar prefix
+specifies a group prefix that the RP will handle.
+.\"
+.It Xo
+.Ic cand_bootstrap_router
+.Op Ar interface
+.Op Ic time Ar time
+.Op Ic priority Ar priority
+.Ic ;
+.Xc
+Specifies to act as a candidate bootstrap router(BSR).
+It is recommended to specify
+.Ic cand_bootstrap_router
+only in typical usage.
+All other parameters are optional and will be set automatically.
+If an
+.Ar interface
+is specified,
+.Nm pim6sd
+will search for a global address on the specified interface
+and set the address in Bootstrap messages.
+An optional parameter
+.Ic time
+specifies the interval of two succeeding bootstraps in seconds.
+Its default value is 60.
+Another optional parameter
+.Ic priority
+specifies the priority of the RP.
+The default value is 0, which means the lowest priority.
+.\"
+.It Xo
+.Ic switch_register_threshold Ic rate Ar rate Ic interval Ar interval;
+.Xc
+Specifies the threshold that a Rendezvous Point(RP) switches to a shortest
+path tree, which is valid only when acting as an RP.
+.Ic rate
+specifies the threshold in bits per second, and
+.Ic interval
+specifies the interval of checking the rate in seconds.
+The default values are 50000 and 20, respectively.
+\"
+.It Xo
+.Ic switch_data_threshold Ic rate Ar rate Ic interval Ar interval;
+.Xc
+Specifies the threshold that a last hop router switches to a shortest
+path tree.
+.Ic rate
+specifies the threshold in bits per second, and
+.Ic interval
+specifies the interval of checking the rate in seconds.
+The default values are 50000 and 20, respectively.
+.El
+.\"
+.Sh EXAMPLE
+.Bd -literal -offset
+#phyint gif0 disable;
+#phyint ep0 preference 101;
+phyint de0 disable;
+#
+#followings are for a candidate Rendezvous Point, which should usually
+#be disabled.
+cand_bootstrap_router;
+cand_rp;
+.Ed
+.Sh SEE ALSO
+.Xr pim6sd 8
+.Sh HISTORY
+The
+.Nm pim6sd
+command is developed by Mickael Hoerdt at LSIIT Laboratory.
+It is based on IPv4 PIM sparse-mode
+.Nm pimd
+developed at University of Southern California,
+which has also been derived from
+.Nm mrouted.
+.Nm Mrouted
+is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/pim6sd/pim6sd.conf.sample b/usr.sbin/pim6sd/pim6sd.conf.sample
new file mode 100644
index 0000000..21cf8e4
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6sd.conf.sample
@@ -0,0 +1,107 @@
+# $FreeBSD$
+#
+#The timer granularity.
+#More this value is small,more pim6sd will be accurate
+#default if not specified : 5
+#BE SURE to have to same granularity on ALL routers,
+#otherwise....
+
+#granularity 5;
+
+#syntax : phyint <interface> <disable> <metric> [metric] <preference> [preference]
+#metric and pref are for the asserts messages
+#samples :
+
+#phyint ed1 disable;
+#phyint de0 disable;
+#phyint ed0 disable;
+#phyint gif0 disable;
+
+#---------------Protocol timer specifications---------------------------#
+#Notes : theses value are the default spec value!
+#do not touch it if you don't know what you do!!
+#you MUST change theses values according to the granularity value!
+#syntax : 'hello_period <number> <coef>'.
+# number is the period in second between 2 hello messages
+# and coef is the coef to deterimine the hello holdtime=hello_period*coef
+# default if not specified: 30 3.5
+
+#hello_period 30 3.5;
+
+#syntax : 'join_prune_period <number> <coef>'.
+# number is the period in second between 2 join/prune messages
+# and coef is the coef to deterimine the join/prune holdtime=join_prune_period*coef
+# default if not specified : 60 3.5
+
+#join_prune_period 60 3.5;
+
+#syntax : 'data_timeout <number>'.
+# number is the time after which (S,G) state for a silent source will be deleted
+# default if not specified : 210
+
+#data_timeout 210;
+
+#syntax : 'register_suppression_timeout <number>'.
+# This is the mean interval between receiving a Register-Stop and allowing
+#Register to be send again.
+# default if not specified : 60
+
+#register_suppression_timeout 60;
+
+#syntax : 'probe_time <number>'.
+#This is the time between sending a null Register and the Register-Suppression-Timer
+#expiring unless it is restarted by receiving a Register-Stop.
+#default if not specified : 5
+
+#probe_time 5;
+
+#syntax : 'assert_timeout <number>'.
+#this is the interval between the last time an Assert is received and the time at wich the
+#assert is timeout
+#default if not specified : 180
+
+#assert_timeout 180;
+
+#syntax : <cand_rp> <interface> <time> [time] <priority> [priority]
+#and time can't be < 10
+#you can just type cand_rp,
+#samples :
+#cand_rp;
+#cand_rp de0;
+#cand_rp ed0 priority 0 time 6;
+
+#syntax : <group_prefix> <multicast address>/<prefix length>
+#group_prefix ff06::15
+#default if not specified : ff00::/8
+#samples:
+#group_prefix ff1e::15/128;
+#group_prefix ff2e::/16;
+
+#syntax : <cand_bootstrap_router> <interface> <priority> [priority] <time> [time]
+#Typically, you can simply set cand_bootstrap_router for a candidate bootstrap
+#router. All other parameters are optional.
+#the bootstrap period is configurable, BUT the holdtime of a bootstrap
+#router is not in the fields of a bootstrap message : it is hardcoded
+#in the pim6sd include file!
+#So be sure to have a time < PIM_BOOSTRAP_TIMEOUT (file pimd.h )
+#cand_bootstrap_router de0 priority 15 time 5;
+
+#syntax : <switch_register_threshold> <rate> [number] <interval> [number]
+#default rate = 50000 interval = 20s
+#samples :
+#TODO : not tested
+#switch_register_threshold rate 54389 interval 45;
+#switch_register_threshold;
+
+#syntax : <switch_data_threshold> <rate> [number] <interval> [number]
+#default rate = 50000 interval = 20s
+#TODO : not tested
+#samples:
+#switch_data_threshold interval 100 rate 1000;
+
+#syntax : <default_source_metric> [number]
+
+#default_source_metric 1243;
+#syntax : <default_source_preference> [number]
+
+#default_source_preference 123 ;
diff --git a/usr.sbin/pim6sd/pim6stat b/usr.sbin/pim6sd/pim6stat
new file mode 100755
index 0000000..667ee4a
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6stat
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+# Copyright (c) 1999 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+# get options
+while getopts "df:p:sw:" option
+do
+ case $option in
+ d)
+ densemode="YES";;
+ f)
+ dumpfile="${OPTARG}";;
+ p)
+ pidfile="${OPTARG}";;
+ s)
+ statmode="YES";;
+ w)
+ waittime="${OPTARG}";;
+ *) # (error msg printed by getopts)
+ echo usage: pim6stat [-d][-f dumpfile][-p pidfile][-w waitsec]
+ exit 2;;
+ esac
+done
+
+# set parameters
+if [ X"${pidfile}" = X ]; then
+ if [ X"${densemode}" = X"YES" ]; then
+ pidfile=/var/run/pim6dd.pid
+ else
+ pidfile=/var/run/pim6sd.pid
+ fi
+fi
+
+if [ X"${waittime}" = X ]; then
+ waittime=1
+fi
+
+if [ X"${statmode}" = X"YES" ]; then
+ signame=-INFO
+else
+ signame=-USR1
+fi
+
+if [ X"${dumpfile}" = X ]; then
+ if [ X"${statmode}" = X"YES" ]; then
+ if [ X"${densemode}" = X"YES" ]; then
+ dumpfile=/var/run/pim6dd.stat
+ else
+ dumpfile=/var/run/pim6sd.stat
+ fi
+ else
+ if [ X"${densemode}" = X"YES" ]; then
+ dumpfile=/var/run/pim6dd.dump
+ else
+ dumpfile=/var/run/pim6sd.dump
+ fi
+ fi
+fi
+
+# execution
+kill ${signame} `cat ${pidfile}`
+sleep ${waittime}
+cat ${dumpfile}
diff --git a/usr.sbin/pim6sd/pim6stat.1 b/usr.sbin/pim6sd/pim6stat.1
new file mode 100644
index 0000000..fac61b1
--- /dev/null
+++ b/usr.sbin/pim6sd/pim6stat.1
@@ -0,0 +1,92 @@
+.\" Copyright (C) 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pim6stat.1,v 1.3 1999/12/16 05:38:06 jinmei Exp $
+.\" $FreeBSD$
+.\"
+.Dd July 28, 1999
+.Dt PIM6STAT 1
+.Os KAME
+.Sh NAME
+.Nm pim6stat
+.Nd show PIM for IPv6 status
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f dumpfile
+.Op Fl p pidfile
+.Op Fl s
+.Op Fl w waitsec
+.Sh DESCRIPTION
+.Nm Pim6stat
+shows status or statistics of the PIM for IPv6 daemon currently running.
+When invoked, it sends a signal to let the daemon dump its internal
+status to a file, waits for dumping, and outputs the content of the file
+to standard output.
+.Pp
+Options supported by
+.Nm pim6stat :
+.Bl -tag -width Ds
+.It Fl d
+specifies to show the status of a dense mode daemon (if running).
+By default,
+.Nm
+assumes sparse mode.
+.It Fl f
+specifies the dumpfile to which the PIM daemon dumps its status.
+.It Fl p
+specifies the PID file of the currently running daemon.
+.It Fl s
+specifies to dump statistics instead of status (for sparse mode only).
+.It Fl w
+specifies the wait period in seconds between sending a signal to the
+daemon and outputs the dumpfile.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/pim6sd.pid -compact
+.It Pa /var/run/pim6sd.pid
+The default PID file for a sparse mode daemon.
+.It Pa /var/run/pim6dd.pid
+The default PID file for a dense mode daemon.
+.It Pa /var/run/pim6sd.dump
+The default dump file for a sparse mode daemon.
+.It Pa /var/run/pim6dd.dump
+The default dump file for a dense mode daemon.
+.It Pa /var/run/pim6sd.stat
+The default statistics dump file for a sparse mode daemon.
+.El
+.Sh SEE ALSO
+.Xr pim6sd 8 ,
+.Xr pim6dd 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in KAME IPv6 protocol stack kit.
+.Sh BUGS
+.Nm Pim6stat
+needs superuser privilege.
+.\"
diff --git a/usr.sbin/pim6sd/pimd.h b/usr.sbin/pim6sd/pimd.h
new file mode 100644
index 0000000..5ad8347
--- /dev/null
+++ b/usr.sbin/pim6sd/pimd.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/* Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef PIMD_H
+#define PIMD_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "defs.h"
+
+#define PIM_PROTOCOL_VERSION 2
+
+/* PIM protocol timers (in seconds) */
+#define PIM_REGISTER_SUPPRESSION_TIMEOUT 60
+#define PIM_REGISTER_PROBE_TIME 5 /* Used to send NULL_REGISTER */
+#define PIM_DATA_TIMEOUT 210
+
+#define PIM_TIMER_HELLO_PERIOD 30
+#define PIM_JOIN_PRUNE_PERIOD 60
+
+#define PIM_JOIN_PRUNE_HOLDTIME (3.5 * PIM_JOIN_PRUNE_PERIOD)
+#define PIM_RANDOM_DELAY_JOIN_TIMEOUT 4.5
+
+#define PIM_DEFAULT_CAND_RP_ADV_PERIOD 60
+#define PIM_DEFAULT_BOOTSTRAP_PERIOD 60
+
+#define PIM_BOOTSTRAP_TIMEOUT (2.5 * PIM_DEFAULT_BOOTSTRAP_PERIOD + 10)
+#define PIM_TIMER_HELLO_HOLDTIME (3.5 * PIM_TIMER_HELLO_PERIOD)
+#define PIM_ASSERT_TIMEOUT 180
+
+
+/* Misc definitions */
+#define PIM_DEFAULT_CAND_RP_PRIORITY 0 /* 0 is the highest. Don't know
+ * why this is the default.
+ * See the PS version (Mar' 97),
+ * pp.22 bottom of the spec.
+ */
+
+#define PIM_DEFAULT_BSR_PRIORITY 0 /* 0 is the lowest */
+#define RP_DEFAULT_IPV6_HASHMASKLEN 126 /* the default group msklen used
+ * by the hash function to
+ * calculate the group-to-RP
+ * mapping
+ */
+
+#define SINGLE_SRC_MSK6LEN 128 /* the single source mask length */
+#define SINGLE_GRP_MSK6LEN 128 /* the single group mask length */
+
+/* TODO: change? */
+#define PIM_GROUP_PREFIX_DEFAULT_MASKLEN 8 /* The default group masklen if
+ * omitted in the config file.
+ */
+
+/* Datarate related definitions */
+/* REG_RATE is used by the RP to switch to the shortest path instead of
+ * decapsulating Registers.
+ * DATA_RATE is the threshold for the last hop router to initiate
+ * switching to the shortest path.
+ */
+/* TODO: XXX: probably no need for two different intervals.
+ */
+
+#define PIM_DEFAULT_REG_RATE 50000 /* max # of register bits/s */
+#define PIM_DEFAULT_REG_RATE_INTERVAL 20 /* regrate probe interval */
+#define PIM_DEFAULT_DATA_RATE 50000 /* max # of data bits/s */
+#define PIM_DEFAULT_DATA_RATE_INTERVAL 20 /* datarate check interval */
+
+#define DATA_RATE_CHECK_INTERVAL 20 /* Data rate check interval */
+#define REG_RATE_CHECK_INTERVAL 20 /* PIM Reg. rate check interval*/
+
+#define UCAST_ROUTING_CHECK_INTERVAL 20 /* Unfortunately, if the unicast
+ * routing changes, the kernel
+ * or any of the existing
+ * unicast routing daemons
+ * don't send us a signal.
+ * Have to ask periodically the
+ * kernel for any route changes.
+ * Default: every 20 seconds.
+ * Sigh.
+ */
+
+#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */
+#define DEFAULT_REG_RATE_LIMIT 0 /* default register_vif rate limit */
+
+/**************************************************************************
+ * PIM Encoded-Unicast, Encoded-Group and Encoded-Source Address formats *
+ *************************************************************************/
+/* Address families definition */
+#define ADDRF_RESERVED 0
+#define ADDRF_IPv4 1
+#define ADDRF_IPv6 2
+#define ADDRF_NSAP 3
+#define ADDRF_HDLC 4
+#define ADDRF_BBN1822 5
+#define ADDRF_802 6
+#define ADDRF_ETHERNET ADDRF_802
+#define ADDRF_E163 7
+#define ADDRF_E164 8
+#define ADDRF_SMDS ADDRF_E164
+#define ADDRF_ATM ADDRF_E164
+#define ADDRF_F69 9
+#define ADDRF_TELEX ADDRF_F69
+#define ADDRF_X121 10
+#define ADDRF_X25 ADDRF_X121
+#define ADDRF_IPX 11
+#define ADDRF_APPLETALK 12
+#define ADDRF_DECNET_IV 13
+#define ADDRF_BANYAN 14
+#define ADDRF_E164_NSAP 15
+
+/* Addresses Encoding Type (specific for each Address Family */
+#define ADDRT_IPv6 0
+
+
+/* Encoded-Unicast: 18 bytes long */
+typedef struct pim6_encod_uni_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ struct in6_addr unicast_addr; /* XXX: Note the 32-bit boundary
+ * misalignment for the unicast
+ * address when placed in the
+ * memory. Must read it byte-by-byte!
+ */
+} pim6_encod_uni_addr_t;
+/* XXX: sizeof(pim6_encod_uni_addr_t) does not work due to misalignment */
+#define PIM6_ENCODE_UNI_ADDR_LEN 18
+
+/* Encoded-Group */
+typedef struct pim6_encod_grp_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 reserved;
+ u_int8 masklen;
+ struct in6_addr mcast_addr;
+} pim6_encod_grp_addr_t;
+/* XXX: sizeof(pim6_encod_grp_addr_t) MAY NOT work due to an alignment problem */
+#define PIM6_ENCODE_GRP_ADDR_LEN 20
+
+/* Encoded-Source */
+typedef struct pim6_encod_src_addr_ {
+ u_int8 addr_family;
+ u_int8 encod_type;
+ u_int8 flags;
+ u_int8 masklen;
+ struct in6_addr src_addr;
+} pim6_encod_src_addr_t;
+/* XXX: sizeof(pim6_encod_src_addr_t) MAY NOT work due to an alignment problem */
+#define PIM6_ENCODE_SRC_ADDR_LEN 20
+
+#define USADDR_RP_BIT 0x1
+#define USADDR_WC_BIT 0x2
+#define USADDR_S_BIT 0x4
+
+/**************************************************************************
+ * PIM Messages formats *
+ *************************************************************************/
+
+/* PIM Hello */
+typedef struct pim_hello_ {
+ u_int16 option_type; /* Option type */
+ u_int16 option_length; /* Length of the Option Value field in bytes */
+} pim_hello_t;
+
+/* PIM Register */
+typedef struct pim_register_ {
+ u_int32 reg_flags;
+} pim_register_t;
+
+/* PIM Register-Stop */
+typedef struct pim_register_stop_ {
+ pim6_encod_grp_addr_t encod_grp;
+ pim6_encod_uni_addr_t encod_src; /* XXX: 18 bytes long, misaligned */
+} pim_register_stop_t;
+
+/* PIM Join/Prune: XXX: all 128-bit addresses misaligned! */
+typedef struct pim_jp_header_ {
+ pim6_encod_uni_addr_t encod_upstream_nbr;
+ u_int8 reserved;
+ u_int8 num_groups;
+ u_int16 holdtime;
+} pim_jp_header_t;
+
+
+typedef struct pim_jp_encod_grp_ {
+ pim6_encod_grp_addr_t encod_grp;
+ u_int16 number_join_src;
+ u_int16 number_prune_src;
+} pim_jp_encod_grp_t;
+
+
+#define PIM_ACTION_NOTHING 0
+#define PIM_ACTION_JOIN 1
+#define PIM_ACTION_PRUNE 2
+
+#define PIM_IIF_SOURCE 1
+#define PIM_IIF_RP 2
+
+#define PIM_ASSERT_RPT_BIT 0x80000000
+
+/* PIM messages type */
+
+#define PIM_HELLO 0
+#define PIM_REGISTER_STOP 2
+#define PIM_JOIN_PRUNE 3
+#define PIM_BOOTSTRAP 4
+#define PIM_ASSERT 5
+#define PIM_GRAFT 6
+#define PIM_GRAFT_ACK 7
+#define PIM_CAND_RP_ADV 8
+
+#define PIM_V2_HELLO PIM_HELLO
+#define PIM_V2_REGISTER PIM_REGISTER
+#define PIM_V2_REGISTER_STOP PIM_REGISTER_STOP
+#define PIM_V2_JOIN_PRUNE PIM_JOIN_PRUNE
+#define PIM_V2_BOOTSTRAP PIM_BOOTSTRAP
+#define PIM_V2_ASSERT PIM_ASSERT
+#define PIM_V2_GRAFT PIM_GRAFT
+#define PIM_V2_GRAFT_ACK PIM_GRAFT_ACK
+#define PIM_V2_CAND_RP_ADV PIM_CAND_RP_ADV
+
+
+/* Vartious options from PIM messages definitions */
+/* PIM_HELLO definitions */
+
+#define PIM_MESSAGE_HELLO_HOLDTIME 1
+#define PIM_MESSAGE_HELLO_HOLDTIME_LENGTH 2
+#define PIM_MESSAGE_HELLO_HOLDTIME_FOREVER 0xffff
+
+/* PIM_REGISTER definitions */
+#define PIM_MESSAGE_REGISTER_BORDER_BIT 0x80000000
+#define PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT 0x40000000
+
+#define MASK_TO_MASKLEN6(mask , masklen) \
+do { \
+ register u_int32 tmp_mask; \
+ register u_int8 tmp_masklen = sizeof((mask)) <<3; \
+ int i; \
+ int kl; \
+ for(i=0;i<4;i++) \
+ { \
+ tmp_mask=ntohl(*(u_int32_t *)&mask.s6_addr[i * 4]); \
+ for(kl=32; tmp_masklen >0 && kl>0 ; tmp_masklen--, kl-- , tmp_mask >>=1) \
+ if( tmp_mask & 0x1) \
+ break; \
+ } \
+ (masklen) =tmp_masklen; \
+ } while (0)
+
+#define MASKLEN_TO_MASK6(masklen, mask6) \
+ do {\
+ u_char maskarray[8] = \
+ {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \
+ int bytelen, bitlen, i; \
+ memset(&(mask6), 0, sizeof(mask6));\
+ bytelen = (masklen) / 8;\
+ bitlen = (masklen) % 8;\
+ for (i = 0; i < bytelen; i++) \
+ (mask6).s6_addr[i] = 0xff;\
+ if (bitlen) \
+ (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \
+ }while(0);
+
+/*
+ * A bunch of macros because of the lack of 32-bit boundary alignment.
+ * All because of one misalligned address format. Hopefully this will be
+ * fixed in PIMv3. (cp) must be (u_int8 *) .
+ */
+/* Originates from Eddy Rusty's (eddy@isi.edu) PIM-SM implementation for
+ * gated.
+ */
+
+/* PUT_NETLONG puts "network ordered" data to the datastream.
+ * PUT_HOSTLONG puts "host ordered" data to the datastream.
+ * GET_NETLONG gets the data and keeps it in "network order" in the memory
+ * GET_HOSTLONG gets the data, but in the memory it is in "host order"
+ * The same for all {PUT,GET}_{NET,HOST}{SHORT,LONG}
+ */
+
+#define GET_BYTE(val, cp) ((val) = *(cp)++)
+#define PUT_BYTE(val, cp) (*(cp)++ = (u_int8)(val))
+
+#define GET_HOSTSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (*(cp)++) << 8; \
+ Xv |= *(cp)++; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_HOSTSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (u_int16)(val); \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)Xv; \
+ } while (0)
+
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+#define GET_NETSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = *(cp)++; \
+ Xv |= (*(cp)++) << 8; \
+ (val) = Xv; \
+ } while (0)
+#define PUT_NETSHORT(val, cp) \
+ do { \
+ register u_int16 Xv; \
+ Xv = (u_int16)(val); \
+ *(cp)++ = (u_int8)Xv; \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ } while (0)
+#else
+#define GET_NETSHORT(val, cp) GET_HOSTSHORT(val, cp)
+#define PUT_NETSHORT(val, cp) PUT_HOSTSHORT(val, cp)
+#endif /* {GET,PUT}_NETSHORT */
+
+#define GET_HOSTLONG(val, cp) \
+ do { \
+ register u_long Xv; \
+ Xv = (*(cp)++) << 24; \
+ Xv |= (*(cp)++) << 16; \
+ Xv |= (*(cp)++) << 8; \
+ Xv |= *(cp)++; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_HOSTLONG(val, cp) \
+ do { \
+ register u_int32 Xv; \
+ Xv = (u_int32)(val); \
+ *(cp)++ = (u_int8)(Xv >> 24); \
+ *(cp)++ = (u_int8)(Xv >> 16); \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)Xv; \
+ } while (0)
+
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+#define GET_NETLONG(val, cp) \
+ do { \
+ register u_long Xv; \
+ Xv = *(cp)++; \
+ Xv |= (*(cp)++) << 8; \
+ Xv |= (*(cp)++) << 16; \
+ Xv |= (*(cp)++) << 24; \
+ (val) = Xv; \
+ } while (0)
+
+#define PUT_NETLONG(val, cp) \
+ do { \
+ register u_int32 Xv; \
+ Xv = (u_int32)(val); \
+ *(cp)++ = (u_int8)Xv; \
+ *(cp)++ = (u_int8)(Xv >> 8); \
+ *(cp)++ = (u_int8)(Xv >> 16); \
+ *(cp)++ = (u_int8)(Xv >> 24); \
+ } while (0)
+#else
+#define GET_NETLONG(val, cp) GET_HOSTLONG(val, cp)
+#define PUT_NETLONG(val, cp) PUT_HOSTLONG(val, cp)
+#endif /* {GET,PUT}_HOSTLONG */
+
+#define GET_ESADDR6(esa, cp) /* XXX: hard coding */ \
+ do { \
+ (esa)->addr_family = *(cp)++; \
+ (esa)->encod_type = *(cp)++; \
+ (esa)->flags = *(cp)++; \
+ (esa)->masklen = *(cp)++; \
+ memcpy(&(esa)->src_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_ESADDR6(addr, masklen, flags, cp) \
+ do { \
+ int i; \
+ struct in6_addr maskaddr; \
+ MASKLEN_TO_MASK6(masklen, maskaddr); \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ *(cp)++ = (flags); /* flags */ \
+ *(cp)++ = (masklen); \
+ for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \
+ *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \
+ } while(0)
+
+#define GET_EGADDR6(ega, cp) /* XXX: hard coding */ \
+ do { \
+ (ega)->addr_family = *(cp)++; \
+ (ega)->encod_type = *(cp)++; \
+ (ega)->reserved = *(cp)++; \
+ (ega)->masklen = *(cp)++; \
+ memcpy(&(ega)->mcast_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_EGADDR6(addr, masklen, reserved, cp) \
+ do { \
+ int i; \
+ struct in6_addr maskaddr; \
+ MASKLEN_TO_MASK6(masklen, maskaddr); \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ *(cp)++ = (reserved); /* reserved; should be 0 */ \
+ *(cp)++ = (masklen); \
+ for (i = 0; i < sizeof(struct in6_addr); i++, (cp)++) \
+ *(cp) = maskaddr.s6_addr[i] & (addr).s6_addr[i]; \
+ } while(0)
+
+
+#define GET_EUADDR6(eua, cp) /* XXX hard conding */ \
+ do { \
+ (eua)->addr_family = *(cp)++; \
+ (eua)->encod_type = *(cp)++; \
+ memcpy(&(eua)->unicast_addr, (cp), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+#define PUT_EUADDR6(addr, cp) \
+ do { \
+ *(cp)++ = ADDRF_IPv6; /* family */ \
+ *(cp)++ = ADDRT_IPv6; /* type */ \
+ memcpy((cp), &(addr), sizeof(struct in6_addr)); \
+ (cp) += sizeof(struct in6_addr); \
+ } while(0)
+
+/* Used if no relaible unicast routing information available */
+#define UCAST_DEFAULT_SOURCE_METRIC 1024
+#define UCAST_DEFAULT_SOURCE_PREFERENCE 1024
+
+
+#define DEFAULT_LOCAL_PREF 101 /* assert pref par defaut */
+#define DEFAULT_LOCAL_METRIC 1024 /* assert metrique par default */
+
+
+/*
+ * TODO: recalculate the messages sizes, probably with regard to the MTU
+ * TODO: cleanup
+ */
+
+#define MAX_JP_MESSAGE_SIZE 8192
+#define MAX_JP_MESSAGE_POOL_NUMBER 8
+#define MAX_JOIN_LIST_SIZE 1500
+#define MAX_PRUNE_LIST_SIZE 1500
+
+#define STAR_STAR_RP_MSK6LEN 8 /* Masklen for
+ * ff00 ::
+ * to encode (*,*,RP)
+ */
+
+/* interface independent statistics */
+struct pim6dstat {
+ /* incoming PIM6 packets on this interface */
+ u_quad_t in_pim6_register;
+ u_quad_t in_pim6_register_stop;
+ u_quad_t in_pim6_cand_rp;
+ u_quad_t in_pim6_graft; /* for dense mode only */
+ u_quad_t in_pim6_graft_ack; /* for dense mode only */
+ /* outgoing PIM6 packets on this interface */
+ u_quad_t out_pim6_register;
+ u_quad_t out_pim6_register_stop;
+ u_quad_t out_pim6_cand_rp;
+ /* SPT transition */
+ u_quad_t pim6_trans_spt_forward;
+ u_quad_t pim6_trans_spt_rp;
+ /* occurrences of timeouts */
+ u_quad_t pim6_bootstrap_timo;/* pim_bootstrap_timer */
+ u_quad_t pim6_rpgrp_timo; /* rp_grp_entry_ptr->holdtime */
+ u_quad_t pim6_rtentry_timo; /* routing entry */
+ /* kernel internals */
+ u_quad_t kern_add_cache;
+ u_quad_t kern_add_cache_fail;
+ u_quad_t kern_del_cache;
+ u_quad_t kern_del_cache_fail;
+ u_quad_t kern_sgcnt_fail;
+};
+
+extern struct pim6dstat pim6dstat;
+#endif
diff --git a/usr.sbin/pim6sd/route.c b/usr.sbin/pim6sd/route.c
new file mode 100644
index 0000000..c455a58
--- /dev/null
+++ b/usr.sbin/pim6sd/route.c
@@ -0,0 +1,1180 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <sys/types.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "vif.h"
+#include "mrt.h"
+#include "debug.h"
+#include "pim6_proto.h"
+#include "route.h"
+#include "mld6.h"
+#include "rp.h"
+#include "kern.h"
+#include "timer.h"
+#include "inet6.h"
+#include <netinet6/ip6_mroute.h>
+#include <netinet/ip6.h>
+#include "routesock.h"
+
+static void process_cache_miss __P((struct mrt6msg * im));
+static void process_wrong_iif __P((struct mrt6msg * im));
+static void process_whole_pkt __P((char *buf));
+
+u_int32 default_source_metric = UCAST_DEFAULT_SOURCE_METRIC;
+u_int32 default_source_preference = UCAST_DEFAULT_SOURCE_PREFERENCE;
+
+
+/* Return the iif for given address */
+
+vifi_t
+get_iif(address)
+ struct sockaddr_in6 *address;
+{
+ struct rpfctl rpfc;
+
+ k_req_incoming(address, &rpfc);
+ if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ return (NO_VIF);
+ return (rpfc.iif);
+}
+
+/* Return the PIM neighbor toward a source */
+/*
+ * If route not found or if a local source or if a directly connected source,
+ * but is not PIM router, or if the first hop router is not a PIM router,
+ * then return NULL.
+ */
+pim_nbr_entry_t *
+find_pim6_nbr(source)
+ struct sockaddr_in6 *source;
+{
+ struct rpfctl rpfc;
+ pim_nbr_entry_t *pim_nbr;
+ struct sockaddr_in6 next_hop_router_addr;
+
+ if (local_address(source) != NO_VIF)
+ return (pim_nbr_entry_t *) NULL;
+ k_req_incoming(source, &rpfc);
+ if((IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ || (rpfc.iif == NO_VIF))
+ return (pim_nbr_entry_t *) NULL;
+ next_hop_router_addr = rpfc.rpfneighbor;
+ for (pim_nbr = uvifs[rpfc.iif].uv_pim_neighbors;
+ pim_nbr != (pim_nbr_entry_t *) NULL;
+ pim_nbr = pim_nbr->next)
+ if (inet6_equal(&pim_nbr->address,&next_hop_router_addr))
+ return (pim_nbr);
+ return (pim_nbr_entry_t *) NULL;
+}
+
+
+/*
+ * TODO: check again the exact setup if the source is local or directly
+ * connected!!! Yes Really for Ipv6!!
+ */
+/*
+ * TODO: XXX: change the metric and preference for all (S,G) entries per
+ * source or RP?
+ */
+/*
+ * TODO - If possible, this would be the place to correct set the source's
+ * preference and metric to that obtained from the kernel and/or unicast
+ * routing protocol. For now, set it to the configured default for local
+ * pref/metric.
+ */
+
+/*
+ * Set the iif, upstream router, preference and metric for the route toward
+ * the source. Return TRUE is the route was found, othewise FALSE. If
+ * srctype==PIM_IIF_SOURCE and if the source is directly connected then the
+ * "upstream" is set to NULL. If srcentry==PIM_IIF_RP, then "upstream" in
+ * case of directly connected "source" will be that "source" (if it is also
+ * PIM router).,
+ */
+int
+set_incoming(srcentry_ptr, srctype)
+ srcentry_t *srcentry_ptr;
+ int srctype;
+{
+ struct rpfctl rpfc;
+ struct sockaddr_in6 source = srcentry_ptr->address;
+ struct sockaddr_in6 neighbor_addr;
+ register struct uvif *v;
+ register pim_nbr_entry_t *n;
+
+ /* Preference will be 0 if directly connected */
+
+ srcentry_ptr->metric = 0;
+ srcentry_ptr->preference = 0;
+
+ if ((srcentry_ptr->incoming = local_address(&source)) != NO_VIF)
+ {
+ /* The source is a local address */
+ /* TODO: set the upstream to myself? */
+ srcentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ return (TRUE);
+ }
+
+ if ((srcentry_ptr->incoming = find_vif_direct(&source)) != NO_VIF)
+ {
+ /*
+ * The source is directly connected. Check whether we are looking for
+ * real source or RP
+ */
+
+ if (srctype == PIM_IIF_SOURCE)
+ {
+ srcentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+ return (TRUE);
+ }
+ else
+ {
+ /* PIM_IIF_RP */
+ neighbor_addr = source;
+ }
+ }
+ else
+ {
+ /* TODO: probably need to check the case if the iif is disabled */
+ /* Use the lastest resource: the kernel unicast routing table */
+ k_req_incoming(&source, &rpfc);
+ if ((rpfc.iif == NO_VIF) ||
+ IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
+ {
+ /* couldn't find a route */
+ IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF)
+ log(LOG_DEBUG, 0, "NO ROUTE found for %s",
+ inet6_fmt(&source.sin6_addr));
+ return (FALSE);
+ }
+ srcentry_ptr->incoming = rpfc.iif;
+ neighbor_addr = rpfc.rpfneighbor;
+ /* set the preference for sources that aren't directly connected. */
+ v = &uvifs[srcentry_ptr->incoming];
+ srcentry_ptr->preference = v->uv_local_pref;
+ srcentry_ptr->metric = v->uv_local_metric;
+ }
+
+ /*
+ * The upstream router must be a (PIM router) neighbor, otherwise we are
+ * in big trouble ;-).
+ * Yes but the neighbors are link-local and the rp is global ipv6..
+ */
+/* WARNING WARNING WARNING WARNING */
+/* If the router is directly connected to the RP and the RP is the BSR , the next hop is
+ * the globally reachable addresse of the RP : NOT link local neighbor but
+ * a ipv6 global neighbor...
+ * the upstream router is the globally reachable router...
+ *
+ */
+/* WARNING WARNING WARNING WARNING */
+
+ v = &uvifs[srcentry_ptr->incoming];
+ if (inet6_equal(&source,&neighbor_addr))
+ {
+ srcentry_ptr->upstream=v->uv_pim_neighbors;
+ return (TRUE);
+ }
+
+ for (n = v->uv_pim_neighbors; n != NULL; n = n->next)
+ {
+ if (inet6_lessthan(&neighbor_addr,&n->address))
+ continue;
+ if (inet6_equal(&neighbor_addr,&n->address))
+ {
+ /*
+ * The upstream router is found in the list of neighbors. We are
+ * safe!
+ */
+
+ srcentry_ptr->upstream = n;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "For src %s, iif is %d, next hop router is %s",
+ inet6_fmt(&source.sin6_addr), srcentry_ptr->incoming,
+ inet6_fmt(&neighbor_addr.sin6_addr));
+ return (TRUE);
+ }
+ else
+ break;
+ }
+
+ /* TODO: control the number of messages! */
+ log(LOG_INFO, 0,
+ "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER",
+ inet6_fmt(&source.sin6_addr), srcentry_ptr->incoming,
+ inet6_fmt(&neighbor_addr.sin6_addr));
+
+ srcentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+
+ return (FALSE);
+}
+
+
+/*
+ * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
+ * we have source-specific Join/Prune.
+ */
+
+void
+add_leaf(vifi, source, group)
+ vifi_t vifi;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ mrtentry_t *mrtentry_ptr;
+ if_set old_oifs;
+ if_set new_oifs;
+ if_set new_leaves;
+
+
+ mrtentry_ptr = find_route(&sockaddr6_any, group, MRTF_WC, CREATE);
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if ((mrtentry_ptr->incoming == vifi)
+ && (!(uvifs[vifi].uv_flags & VIFF_DR)))
+ {
+ /*
+ * The report is received on the iif for this routing entry and I am
+ * not the DR for that subnet. Ignore it.
+ */
+
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ delete_mrtentry(mrtentry_ptr);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_MRT)
+ log(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi,
+ inet6_fmt(&group->sin6_addr));
+
+ if (IF_ISSET(vifi, &mrtentry_ptr->leaves))
+ return; /* Already a leaf */
+ calc_oifs(mrtentry_ptr, &old_oifs);
+ IF_COPY(&mrtentry_ptr->leaves, &new_leaves);
+ IF_SET(vifi, &new_leaves); /* Add the leaf */
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &new_leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ calc_oifs(mrtentry_ptr, &new_oifs);
+
+ if ((mrtentry_ptr->flags & MRTF_NEW)
+ || (IF_ISEMPTY(&old_oifs) && (!IF_ISEMPTY(&new_oifs))))
+ {
+
+ /*
+ * A new created entry or the oifs have changed from NULL to
+ * non-NULL.
+ */
+
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune
+ * timer */
+ /*
+ * TODO: explicitly call the function below?
+ * send_pim6_join_prune(mrtentry_ptr->upstream->vifi,
+ * mrtentry_ptr->upstream, pim_join_prune_holdtime);
+ */
+ }
+}
+
+
+/*
+ * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where we
+ * have source-specific joins/prunes.
+ */
+void
+delete_leaf(vifi, source, group)
+ vifi_t vifi;
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ if_set new_oifs;
+ if_set old_oifs;
+ if_set new_leaves;
+
+ mrtentry_ptr = find_route(&sockaddr6_any, group, MRTF_WC, DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return;
+
+ if (!IF_ISSET(vifi, &mrtentry_ptr->leaves))
+ return; /* This interface wasn't leaf */
+
+ calc_oifs(mrtentry_ptr, &old_oifs);
+ IF_COPY(&mrtentry_ptr->leaves, &new_leaves);
+ IF_CLR(vifi, &new_leaves);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &new_leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ calc_oifs(mrtentry_ptr, &new_oifs);
+ if ((!IF_ISEMPTY(&old_oifs)) && IF_ISEMPTY(&new_oifs))
+ {
+ /* The result oifs have changed from non-NULL to NULL */
+ FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune
+ * timer */
+ /*
+ * TODO: explicitly call the function?
+ * send_pim6_join_prune(mrtentry_ptr->upstream->vifi,
+ * mrtentry_ptr->upstream, pim_join_prune_holdtime);
+ */
+ }
+ /*
+ * Check all (S,G) entries and clear the inherited "leaf" flag. TODO:
+ * XXX: This won't work for IGMPv3, because there we don't know whether
+ * the (S,G) leaf oif was inherited from the (*,G) entry or was created
+ * by source specific IGMP join.
+ */
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
+ IF_CLR(vifi, &new_leaves);
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &new_leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+
+}
+
+
+void
+calc_oifs(mrtentry_ptr, oifs_ptr)
+ mrtentry_t *mrtentry_ptr;
+ if_set *oifs_ptr;
+{
+ if_set oifs;
+ mrtentry_t *grp_route;
+ mrtentry_t *rp_route;
+
+ /*
+ * oifs = (((copied_outgoing + my_join) - my_prune) + my_leaves) -
+ * my_asserted_oifs - incoming_interface, i.e. `leaves` have higher
+ * priority than `prunes`, but lower priority than `asserted`. The
+ * incoming interface is always deleted from the oifs
+ */
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ IF_ZERO(oifs_ptr);
+ return;
+ }
+ IF_ZERO(&oifs);
+ if (!(mrtentry_ptr->flags & MRTF_PMBR))
+ {
+ /* Either (*,G) or (S,G). Merge with the oifs from the (*,*,RP) */
+ if ((rp_route =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink)
+ != (mrtentry_t *) NULL)
+ {
+ IF_MERGE(&oifs, &rp_route->joined_oifs, &oifs);
+ IF_CLR_MASK(&oifs, &rp_route->pruned_oifs);
+ IF_MERGE(&oifs, &rp_route->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &rp_route->asserted_oifs);
+ }
+ }
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry. Merge with the oifs from (*,G) */
+ if ((grp_route = mrtentry_ptr->group->grp_route)
+ != (mrtentry_t *) NULL)
+ {
+ IF_MERGE(&oifs, &grp_route->joined_oifs, &oifs);
+ IF_CLR_MASK(&oifs, &grp_route->pruned_oifs);
+ IF_MERGE(&oifs, &grp_route->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &grp_route->asserted_oifs);
+ }
+ }
+
+ /* Calculate my own stuff */
+ IF_MERGE(&oifs, &mrtentry_ptr->joined_oifs, &oifs);
+ IF_CLR_MASK(&oifs, &mrtentry_ptr->pruned_oifs);
+ IF_MERGE(&oifs, &mrtentry_ptr->leaves, &oifs);
+ IF_CLR_MASK(&oifs, &mrtentry_ptr->asserted_oifs);
+
+ IF_CLR(mrtentry_ptr->incoming, &oifs);
+ IF_COPY(&oifs, oifs_ptr);
+}
+
+/*
+ * Set the iif, join/prune/leaves/asserted interfaces. Calculate and set the
+ * oifs. Return 1 if oifs change from NULL to not-NULL. Return -1 if oifs
+ * change from non-NULL to NULL else return 0 If the iif change or if the
+ * oifs change from NULL to non-NULL or vice-versa, then schedule that
+ * mrtentry join/prune timer to timeout immediately.
+ */
+int
+change_interfaces(mrtentry_ptr, new_iif, new_joined_oifs_, new_pruned_oifs,
+ new_leaves_, new_asserted_oifs, flags)
+ mrtentry_t *mrtentry_ptr;
+ vifi_t new_iif;
+ if_set *new_joined_oifs_;
+ if_set *new_pruned_oifs;
+ if_set *new_leaves_;
+ if_set *new_asserted_oifs;
+ u_int16 flags;
+{
+ if_set new_joined_oifs; /* The oifs for that particular
+ * mrtentry */
+ if_set old_joined_oifs;
+ if_set old_pruned_oifs;
+ if_set old_leaves;
+ if_set new_leaves;
+ if_set old_asserted_oifs;
+ if_set new_real_oifs; /* The result oifs */
+ if_set old_real_oifs;
+ vifi_t old_iif;
+ rpentry_t *rpentry_ptr;
+ cand_rp_t *cand_rp_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ grpentry_t *grpentry_ptr;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_wc;
+ mrtentry_t *mrtentry_rp;
+ int delete_mrtentry_flag;
+ int return_value;
+ int fire_timer_flag;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (0);
+
+ IF_COPY(new_joined_oifs_, &new_joined_oifs);
+ IF_COPY(new_leaves_, &new_leaves);
+
+ old_iif = mrtentry_ptr->incoming;
+ IF_COPY(&mrtentry_ptr->joined_oifs, &old_joined_oifs);
+ IF_COPY(&mrtentry_ptr->leaves, &old_leaves);
+ IF_COPY(&mrtentry_ptr->pruned_oifs, &old_pruned_oifs);
+ IF_COPY(&mrtentry_ptr->asserted_oifs, &old_asserted_oifs);
+
+ IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs);
+
+ mrtentry_ptr->incoming = new_iif;
+ IF_COPY(&new_joined_oifs, &mrtentry_ptr->joined_oifs);
+ IF_COPY(new_pruned_oifs, &mrtentry_ptr->pruned_oifs);
+ IF_COPY(&new_leaves, &mrtentry_ptr->leaves);
+ IF_COPY(new_asserted_oifs, &mrtentry_ptr->asserted_oifs);
+ calc_oifs(mrtentry_ptr, &new_real_oifs);
+
+ if (IF_ISEMPTY(&old_real_oifs))
+ {
+ if (IF_ISEMPTY(&new_real_oifs))
+ return_value = 0;
+ else
+ return_value = 1;
+ }
+ else
+ {
+ if (IF_ISEMPTY(&new_real_oifs))
+ return_value = -1;
+ else
+ return_value = 0;
+ }
+
+ if ((IF_SAME(&new_real_oifs, &old_real_oifs))
+ && (new_iif == old_iif)
+ && !(flags & MFC_UPDATE_FORCE))
+ return 0; /* Nothing to change */
+
+ if ((return_value != 0) || (new_iif != old_iif)
+ || (flags & MFC_UPDATE_FORCE))
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+
+ IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
+
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ {
+ /* (*,*,RP) entry */
+ rpentry_ptr = mrtentry_ptr->source;
+ if (rpentry_ptr == (rpentry_t *) NULL)
+ return (0); /* Shoudn't happen */
+ rpentry_ptr->incoming = new_iif;
+ cand_rp_ptr = rpentry_ptr->cand_rp;
+
+ if (IF_ISEMPTY(&new_real_oifs))
+ {
+ delete_mrtentry_flag = TRUE;
+ }
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ /* Update the kernel MFC entries */
+ if (delete_mrtentry_flag == TRUE)
+ /*
+ * XXX: no need to send RSRR message. Will do it when delete
+ * the mrtentry.
+ */
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ /* here mrtentry_ptr->source->address is the RP address */
+ k_chg_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group, new_iif,
+ &new_real_oifs, &mrtentry_ptr->source->address);
+ }
+ }
+
+ /*
+ * Update all (*,G) entries associated with this RP. The particular
+ * (*,G) outgoing are not changed, but the change in the (*,*,RP)
+ * oifs may have affect the real oifs.
+ */
+ fire_timer_flag = FALSE;
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr->rpnext)
+ {
+ if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
+ {
+ if (change_interfaces(grpentry_ptr->grp_route, new_iif,
+ &grpentry_ptr->grp_route->joined_oifs,
+ &grpentry_ptr->grp_route->pruned_oifs,
+ &grpentry_ptr->grp_route->leaves,
+ &grpentry_ptr->grp_route->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ /* Change all (S,G) entries if no (*,G) */
+ for (mrtentry_srcs = grpentry_ptr->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ if (change_interfaces(mrtentry_srcs, new_iif,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ if (change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs,
+ flags))
+ fire_timer_flag = TRUE;
+ }
+ }
+ }
+ }
+ }
+ if (fire_timer_flag == TRUE)
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /*
+ * TODO: XXX: trigger a Prune message? Don't delete now, it will
+ * be automatically timed out. If want to delete now, don't
+ * reference to it anymore! delete_mrtentry(mrtentry_ptr);
+ */
+ }
+ return (return_value); /* (*,*,RP) */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_WC)
+ {
+ /* (*,G) entry */
+ if (IF_ISEMPTY(&new_real_oifs))
+ delete_mrtentry_flag = TRUE;
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ if (delete_mrtentry_flag == TRUE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_ptr->next)
+ k_chg_mfc(mld6_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group, new_iif,
+ &new_real_oifs, &mrtentry_ptr->group->rpaddr);
+ }
+ }
+ /*
+ * Update all (S,G) entries for this group. For the (S,G)RPbit
+ * entries the iif is the iif toward the RP; The particular (S,G)
+ * oifs are not changed, but the change in the (*,G) oifs may affect
+ * the real oifs.
+ */
+ fire_timer_flag = FALSE;
+ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
+ mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs->grpnext)
+ {
+ if (mrtentry_srcs->flags & MRTF_RP)
+ {
+ if (change_interfaces(mrtentry_srcs, new_iif,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, flags))
+ fire_timer_flag = TRUE;
+ }
+ else
+ {
+ if (change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, flags))
+ fire_timer_flag = TRUE;
+ }
+ }
+
+ if (fire_timer_flag == TRUE)
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /* TODO: XXX: the oifs are NULL. Send a Prune message? */
+ }
+ return (return_value); /* (*,G) */
+ }
+
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* (S,G) entry */
+#ifdef KERNEL_MFC_WC_G
+ if_set tmp_oifs;
+ mrtentry_t *mrtentry_tmp;
+#endif /* KERNEL_MFC_WC_G */
+
+ mrtentry_rp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ mrtentry_wc = mrtentry_ptr->group->grp_route;
+#ifdef KERNEL_MFC_WC_G
+ /*
+ * Check whether (*,*,RP) or (*,G) have different (iif,oifs) from the
+ * (S,G). If "yes", then forbid creating (*,G) MFC.
+ */
+ for (mrtentry_tmp = mrtentry_rp; 1; mrtentry_tmp = mrtentry_wc)
+ {
+ for (; 1;)
+ {
+ if (mrtentry_tmp == (mrtentry_t *) NULL)
+ break;
+ if (mrtentry_tmp->flags & MRTF_MFC_CLONE_SG)
+ break;
+ if (mrtentry_tmp->incoming != mrtentry_ptr->incoming)
+ {
+ delete_single_kernel_cache_addr(mrtentry_tmp, IN6ADDR_ANY_N,
+ mrtentry_ptr->group->group);
+ mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
+ break;
+ }
+ calc_oifs(mrtentry_tmp, &tmp_oifs);
+ if (!(IF_SAME(&new_real_oifs, &tmp_oifs)))
+ mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
+ break;
+ }
+ if (mrtentry_tmp == mrtentry_wc)
+ break;
+ }
+#endif /* KERNEL_MFC_WC_G */
+
+ if (IF_ISEMPTY(&new_real_oifs))
+ delete_mrtentry_flag = TRUE;
+ else
+ {
+ delete_mrtentry_flag = FALSE;
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ }
+ if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE)
+ {
+ if (delete_mrtentry_flag == TRUE)
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ else
+ {
+ k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
+ &mrtentry_ptr->group->group, new_iif, &new_real_oifs,
+ &mrtentry_ptr->group->rpaddr);
+ }
+ }
+ if (old_iif != new_iif)
+ {
+ if (new_iif == mrtentry_ptr->source->incoming)
+ {
+ /*
+ * For example, if this was (S,G)RPbit with iif toward the
+ * RP, and now switch to the Shortest Path. The setup of
+ * MRTF_SPT flag must be done by the external calling
+ * function (triggered only by receiving of a data from the
+ * source.)
+ */
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ /*
+ * TODO: XXX: delete? Check again where will be the best
+ * place to set it. mrtentry_ptr->flags |= MRTF_SPT;
+ */
+ }
+ if (((mrtentry_wc != (mrtentry_t *) NULL)
+ && (mrtentry_wc->incoming == new_iif))
+ || ((mrtentry_rp != (mrtentry_t *) NULL)
+ && (mrtentry_rp->incoming == new_iif)))
+ {
+ /*
+ * If the new iif points toward the RP, reset the SPT flag.
+ * (PIM-SM-spec-10.ps pp. 11, 2.10, last sentence of first
+ * paragraph.
+ */
+ /* TODO: XXX: check again! */
+
+ mrtentry_ptr->flags &= ~MRTF_SPT;
+ mrtentry_ptr->flags |= MRTF_RP;
+ }
+ }
+ /*
+ * TODO: XXX: if this is (S,G)RPbit entry and the oifs==(*,G)oifs,
+ * then delete the (S,G) entry?? The same if we have (*,*,RP) ?
+ */
+ if (delete_mrtentry_flag == TRUE)
+ {
+ /* TODO: XXX: the oifs are NULL. Send a Prune message ? */
+ }
+ /* TODO: XXX: have the feeling something is missing.... */
+ return (return_value); /* (S,G) */
+ }
+ return (return_value);
+}
+
+
+/*
+ * TODO: implement it. Required to allow changing of the physical interfaces
+ * configuration without need to restart pimd.
+ */
+int
+delete_vif_from_mrt(vifi)
+ vifi_t vifi;
+{
+ return TRUE;
+}
+
+
+void
+process_kernel_call()
+{
+ register struct mrt6msg *im; /* igmpmsg control struct */
+
+ im = (struct mrt6msg *) mld6_recv_buf;
+
+ switch (im->im6_msgtype)
+ {
+ case MRT6MSG_NOCACHE:
+ process_cache_miss(im);
+ break;
+ case MRT6MSG_WRONGMIF:
+ process_wrong_iif(im);
+ break;
+ case MRT6MSG_WHOLEPKT:
+ process_whole_pkt(mld6_recv_buf);
+ break;
+ default:
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Unknown kernel_call code");
+ break;
+ }
+}
+
+
+/*
+ * TODO: when cache miss, check the iif, because probably ASSERTS shoult take
+ * place
+ */
+
+static void
+process_cache_miss(im)
+ struct mrt6msg *im;
+{
+ static struct sockaddr_in6 source = {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 mfc_source = {sizeof(source) , AF_INET6 };
+ static struct sockaddr_in6 group = {sizeof(group) , AF_INET6 };
+
+ static struct sockaddr_in6 rp_addr = {sizeof(source) , AF_INET6 };
+ vifi_t iif;
+ mrtentry_t *mrtentry_ptr;
+ mrtentry_t *mrtentry_rp;
+
+ /*
+ * When there is a cache miss, we check only the header of the packet
+ * (and only it should be sent up by the kernel.)
+ */
+ group.sin6_addr = im->im6_dst;
+ group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]);
+ source.sin6_addr = mfc_source.sin6_addr = im->im6_src;
+ source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]);
+ iif = im->im6_mif;
+
+ uvifs[iif].uv_cache_miss++;
+ IF_DEBUG(DEBUG_MFC)
+ log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s, iif %d",
+ inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr), iif);
+
+ /*
+ * TODO: XXX: check whether the kernel generates cache miss for the LAN
+ * scoped addresses
+ */
+
+ /* Don't create routing entries for the LAN scoped addresses */
+
+ if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
+ IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
+ goto fail;
+
+ /* TODO: check if correct in case the source is one of my addresses */
+ /*
+ * If I am the DR for this source, create (S,G) and add the register_vif
+ * to the oifs.
+ */
+ if ((uvifs[iif].uv_flags & VIFF_DR)
+ && (find_vif_direct_local(&source) == iif))
+ {
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ {
+ goto fail;
+ }
+
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+
+ /* set reg_vif_num as outgoing interface ONLY if I am not the RP */
+
+ if (!inet6_equal(&mrtentry_ptr->group->rpaddr, &my_cand_rp_address))
+ IF_SET(reg_vif_num, &mrtentry_ptr->joined_oifs);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ else
+ {
+ mrtentry_ptr = find_route(&source, &group,
+ MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ goto fail;
+ }
+
+ /*
+ * TODO: if there are too many cache miss for the same (S,G), install
+ * negative cache entry in the kernel (oif==NULL) to prevent too many
+ * upcalls.
+ */
+ if (mrtentry_ptr->incoming == iif)
+ {
+ if (!IF_ISEMPTY(&mrtentry_ptr->oifs))
+ {
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ /* TODO: check that the RPbit is not set? */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_ptr->timer < pim_data_timeout)
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ /*
+ * Check if the (S,G) iif is different from the (*,G)
+ * or (*,*,RP) iif
+ */
+ if ((mrtentry_ptr->incoming != mrtentry_rp->incoming)
+ || (mrtentry_ptr->upstream != mrtentry_rp->upstream))
+ {
+ mrtentry_ptr->flags |= MRTF_SPT;
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ }
+ }
+ }
+ }
+ if (mrtentry_ptr->flags & MRTF_PMBR)
+ rp_addr = mrtentry_ptr->source->address;
+ else
+ rp_addr = mrtentry_ptr->group->rpaddr;
+ mfc_source = source;
+// TODO
+#ifdef KERNEL_MFC_WC_G
+ if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))
+ if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = IN6ADDR_ANY_N;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_ptr, &mfc_source, &group, MFC_MOVE_FORCE);
+ k_chg_mfc(mld6_socket, &mfc_source, &group, iif, &mrtentry_ptr->oifs,
+ &rp_addr);
+ /*
+ * TODO: XXX: No need for RSRR message, because nothing has
+ * changed.
+ */
+ }
+ return; /* iif match */
+ }
+
+ /* The iif doesn't match */
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ if (mrtentry_ptr->flags & MRTF_SPT)
+ /* Arrived on wrong interface */
+ goto fail;
+ if ((mrtentry_rp = mrtentry_ptr->group->grp_route) ==
+ (mrtentry_t *) NULL)
+ mrtentry_rp =
+ mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ if (mrtentry_rp->incoming == iif)
+ {
+ /* Forward on (*,G) or (*,*,RP) */
+
+#ifdef KERNEL_MFC_WC_G
+ if (!(mrtentry_rp->flags & MRTF_MFC_CLONE_SG))
+ mfc_source = IN6ADDR_ANY_N;
+#endif /* KERNEL_MFC_WC_G */
+
+ add_kernel_cache(mrtentry_rp, &mfc_source, &group, 0);
+ k_chg_mfc(mld6_socket, &mfc_source, &group, iif,
+ &mrtentry_rp->oifs, &mrtentry_ptr->group->rpaddr);
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_rp, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+
+ return;
+ }
+ }
+ goto fail;
+ }
+
+ fail:
+ uvifs[iif].uv_cache_notcreated++;
+}
+
+
+/*
+ * A multicast packet has been received on wrong iif by the kernel. Check for
+ * a matching entry. If there is (S,G) with reset SPTbit and the packet was
+ * received on the iif toward the source, this completes the switch to the
+ * shortest path and triggers (S,G) prune toward the RP (unless I am the RP).
+ * Otherwise, if the packet's iif is in the oiflist of the routing entry,
+ * trigger an Assert.
+ */
+
+static void
+process_wrong_iif(im)
+ struct mrt6msg *im;
+{
+ static struct sockaddr_in6 source= {sizeof(source) , AF_INET6};
+ static struct sockaddr_in6 group = {sizeof(group) , AF_INET6};
+ vifi_t iif;
+ mrtentry_t *mrtentry_ptr;
+
+ group.sin6_addr = im->im6_dst;
+ source.sin6_addr = im->im6_src;
+ iif = im->im6_mif;
+
+
+ /* Don't create routing entries for the LAN scoped addresses */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
+ IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
+ return;
+
+
+ /*
+ * Ignore if it comes on register vif. register vif is neither SPT iif,
+ * neither is used to send asserts out.
+ */
+ if (uvifs[iif].uv_flags & MIFF_REGISTER)
+ return;
+
+ mrtentry_ptr = find_route(&source, &group, MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ if (mrtentry_ptr == (mrtentry_t *)NULL)
+ return;
+
+ /*
+ * TODO: check again!
+ */
+ if (mrtentry_ptr->flags & MRTF_SG)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_SPT))
+ {
+ if (mrtentry_ptr->source->incoming == iif)
+ {
+ /* Switch to the Shortest Path */
+ mrtentry_ptr->flags |= MRTF_SPT;
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ add_kernel_cache(mrtentry_ptr, &source, &group, MFC_MOVE_FORCE);
+ k_chg_mfc(mld6_socket, &source, &group, iif,
+ &mrtentry_ptr->oifs, &mrtentry_ptr->group->rpaddr);
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+#ifdef RSRR
+ rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
+#endif /* RSRR */
+ return;
+ }
+ }
+ }
+
+ /* Trigger an Assert */
+ if (IF_ISSET(iif, &mrtentry_ptr->oifs))
+ send_pim6_assert(&source, &group, iif, mrtentry_ptr);
+}
+
+/*
+ * Receives whole packets from the register vif entries in the kernel, and
+ * calls the send_pim_register procedure to encapsulate the packets and
+ * unicasts them to the RP.
+ */
+static void
+process_whole_pkt(buf)
+ char *buf;
+{
+
+ send_pim6_register((char *) (buf + sizeof(struct mrt6msg)));
+
+}
+
+
+mrtentry_t *
+switch_shortest_path(source, group)
+ struct sockaddr_in6 *source;
+ struct sockaddr_in6 *group;
+{
+ mrtentry_t *mrtentry_ptr;
+
+ /* TODO: XXX: prepare and send immediately the (S,G) join? */
+ if ((mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE)) !=
+ (mrtentry_t *) NULL)
+ {
+ if (mrtentry_ptr->flags & MRTF_NEW)
+ {
+ mrtentry_ptr->flags &= ~MRTF_NEW;
+ }
+ else
+ {
+ if (mrtentry_ptr->flags & MRTF_RP)
+ {
+ /*
+ * (S,G)RPbit with iif toward RP. Reset to (S,G) with iif
+ * toward S. Delete the kernel cache (if any), because
+ * change_interfaces() will reset it with iif toward S and no
+ * data will arrive from RP before the switch really occurs.
+ */
+ mrtentry_ptr->flags &= ~MRTF_RP;
+ mrtentry_ptr->incoming = mrtentry_ptr->source->incoming;
+ mrtentry_ptr->upstream = mrtentry_ptr->source->upstream;
+ delete_mrtentry_all_kernel_cache(mrtentry_ptr);
+ change_interfaces(mrtentry_ptr,
+ mrtentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, 0);
+ }
+ }
+
+ SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
+ FIRE_TIMER(mrtentry_ptr->jp_timer);
+ }
+ return (mrtentry_ptr);
+}
diff --git a/usr.sbin/pim6sd/route.h b/usr.sbin/pim6sd/route.h
new file mode 100644
index 0000000..2adb1fe
--- /dev/null
+++ b/usr.sbin/pim6sd/route.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef ROUTE_H
+#define ROUTE_H
+
+#include "mrt.h"
+
+extern u_int32 default_source_preference;
+extern u_int32 default_source_metric;
+
+int change_interfaces( mrtentry_t *mrtentry_ptr,vifi_t new_iif,
+ if_set *new_joined_oifs,if_set *new_pruned_oifs,if_set *new_leaves_ , if_set *asserted ,
+ u_int16 flags);
+
+extern void process_kernel_call __P(());
+extern int set_incoming __P((srcentry_t *srcentry_ptr,
+ int srctype));
+extern vifi_t get_iif __P((struct sockaddr_in6 *source));
+extern int add_sg_oif __P((mrtentry_t *mrtentry_ptr,
+ vifi_t vifi,
+ u_int16 holdtime,
+ int update_holdtime));
+extern void add_leaf __P((vifi_t vifi, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+extern void delete_leaf __P((vifi_t vifi, struct sockaddr_in6 *source,
+ struct sockaddr_in6 *group));
+
+
+
+
+extern pim_nbr_entry_t *find_pim6_nbr __P((struct sockaddr_in6 *source));
+extern void calc_oifs __P((mrtentry_t *mrtentry_ptr,
+ if_set *oifs_ptr));
+extern void process_kernel_call __P(());
+extern int delete_vif_from_mrt __P((vifi_t vifi));
+extern mrtentry_t *switch_shortest_path __P((struct sockaddr_in6 *source, struct sockaddr_in6 *group));
+
+
+#endif
diff --git a/usr.sbin/pim6sd/routesock.c b/usr.sbin/pim6sd/routesock.c
new file mode 100644
index 0000000..aced736
--- /dev/null
+++ b/usr.sbin/pim6sd/routesock.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "defs.h"
+#include <sys/socket.h>
+#include <net/route.h>
+#ifdef HAVE_ROUTING_SOCKETS
+#include <net/if_dl.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "vif.h"
+#include "debug.h"
+
+#ifdef HAVE_ROUTING_SOCKETS
+union sockunion
+{
+ struct sockaddr sa;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_dl sdl;
+} so_dst, so_ifp;
+typedef union sockunion *sup;
+int routing_socket;
+int rtm_addrs,
+ pid;
+struct rt_metrics rt_metrics;
+u_long rtm_inits;
+
+/*
+ * Local functions definitions.
+ */
+static int getmsg
+__P((register struct rt_msghdr *, int,
+ struct rpfctl * rpfinfo));
+
+/*
+ * TODO: check again!
+ */
+#ifdef IRIX
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \
+ : sizeof(__uint64_t))
+#else
+#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \
+ : sizeof(long))
+#endif /* IRIX */
+
+#ifdef HAVE_SA_LEN
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+#else
+#define ADVANCE(x, n) (x += ROUNDUP(4)) /* TODO: a hack!! */
+#endif
+
+/* Open and initialize the routing socket */
+int
+init_routesock()
+{
+ pid = getpid();
+ routing_socket = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
+ if (routing_socket < 0)
+ {
+ log(LOG_ERR, 0, "\nRouting socket error");
+ return -1;
+ }
+ if (fcntl(routing_socket, F_SETFL, O_NONBLOCK) == -1)
+ {
+ log(LOG_ERR, 0, "\n Routing socket error");
+ return -1;
+ }
+// TODO : UTILITY ?
+#if 0
+ {
+ int off;
+
+ off = 0;
+ if (setsockopt(routing_socket, SOL_SOCKET,
+ SO_USELOOPBACK, (char *) &off,
+ sizeof(off)) < 0)
+ {
+ log(LOG_ERR, 0, "\n setsockopt(SO_USELOOPBACK,0)");
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+
+struct
+{
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+
+/* get the rpf neighbor info */
+int
+k_req_incoming(source, rpfp)
+ struct sockaddr_in6 *source;
+ struct rpfctl *rpfp;
+{
+ int flags = RTF_STATIC;
+ register sup su;
+ static int seq;
+ int rlen;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+ struct rpfctl rpfinfo;
+
+ /* TODO: a hack!!!! */
+#ifdef HAVE_SA_LEN
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l = ROUNDUP(u.sa.sa_len); bcopy((char *)&(u), cp, l); cp += l;\
+ }
+#else
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l = ROUNDUP(4); bcopy((char *)&(u), cp, l); cp += l;\
+ }
+#endif /* HAVE_SA_LEN */
+
+ /* initialize */
+ memset(&rpfp->rpfneighbor, 0, sizeof(rpfp->rpfneighbor));
+ rpfp->source = *source;
+
+ /*
+ * check if local address or directly connected before calling the
+ * routing socket
+ */
+
+ if ((rpfp->iif = find_vif_direct_local(source)) != NO_VIF)
+ {
+ rpfp->rpfneighbor = *source;
+ return (TRUE);
+ }
+
+ /* prepare the routing socket params */
+ rtm_addrs |= RTA_DST;
+ rtm_addrs |= RTA_IFP;
+ su = &so_dst;
+ su->sin6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ su->sin6.sin6_addr = source->sin6_addr;
+ su->sin6.sin6_scope_id = source->sin6_scope_id;
+ so_ifp.sa.sa_family = AF_LINK;
+#ifdef HAVE_SA_LEN
+ so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
+#endif
+ flags |= RTF_UP;
+ flags |= RTF_HOST;
+ flags |= RTF_GATEWAY;
+ errno = 0;
+ bzero((char *) &m_rtmsg, sizeof(m_rtmsg));
+
+#define rtm m_rtmsg.m_rtm
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = flags;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+ rtm.rtm_rmx = rt_metrics;
+ rtm.rtm_inits = rtm_inits;
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_IFP, so_ifp);
+ rtm.rtm_msglen = l = cp - (char *) &m_rtmsg;
+
+ if ((rlen = write(routing_socket, (char *) &m_rtmsg, l)) < 0)
+ {
+ IF_DEBUG(DEBUG_RPF | DEBUG_KERN)
+ {
+ if (errno == ESRCH)
+ log(LOG_DEBUG, 0,
+ "Writing to routing socket: no such route\n");
+ else
+ log(LOG_DEBUG, 0, "Error writing to routing socket");
+ }
+ return (FALSE);
+ }
+
+ do
+ {
+ l = read(routing_socket, (char *) &m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ if (l < 0)
+ {
+ IF_DEBUG(DEBUG_RPF | DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Read from routing socket failed: %s", strerror(errno));
+ return (FALSE);
+ }
+
+ if (getmsg(&rtm, l, &rpfinfo))
+ {
+ rpfp->rpfneighbor = rpfinfo.rpfneighbor;
+ rpfp->iif = rpfinfo.iif;
+ }
+#undef rtm
+ return (TRUE);
+}
+
+
+/*
+ * Returns TRUE on success, FALSE otherwise. rpfinfo contains the result.
+ */
+int
+getmsg(rtm, msglen, rpfinfop)
+ register struct rt_msghdr *rtm;
+ int msglen;
+ struct rpfctl *rpfinfop;
+{
+ struct sockaddr *dst = NULL,
+ *gate = NULL,
+ *mask = NULL;
+
+ struct sockaddr_dl *ifp = NULL;
+ register struct sockaddr *sa;
+ register char *cp;
+ register int i;
+ struct sockaddr_in6 *sin6;
+ vifi_t vifi;
+ struct uvif *v;
+ char in6txt[INET6_ADDRSTRLEN];
+
+ if (rpfinfop == (struct rpfctl *) NULL)
+ return (FALSE);
+
+ sin6 = (struct sockaddr_in6 *) & so_dst;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, "route to: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ cp = ((char *) (rtm + 1));
+ if (rtm->rtm_addrs)
+ for (i = 1; i; i <<= 1)
+ if (i & rtm->rtm_addrs)
+ {
+ sa = (struct sockaddr *) cp;
+ switch (i)
+ {
+ case RTA_DST:
+ dst = sa;
+ break;
+ case RTA_GATEWAY:
+ gate = sa;
+ break;
+ case RTA_NETMASK:
+ mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *) sa)->sdl_nlen)
+ ifp = (struct sockaddr_dl *) sa;
+ break;
+#if 0
+ default:
+ /*
+ * There are some defined flags other than above 4,
+ * but we are not interested in them.
+ */
+ log(LOG_WARNING, 0,
+ "Routesock.c (getmsg) unknown flag : %d",i);
+#endif
+ }
+ ADVANCE(cp, sa);
+ }
+
+ if (!ifp)
+ { /* No incoming interface */
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "No incoming interface for destination %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ return (FALSE);
+ }
+ if (dst && mask)
+ mask->sa_family = dst->sa_family;
+ if (dst)
+ {
+ sin6 = (struct sockaddr_in6 *) dst;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " destination is: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ }
+
+ if (gate && rtm->rtm_flags & RTF_GATEWAY)
+ {
+ sin6 = (struct sockaddr_in6 *) gate;
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " gateway is: %s",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ rpfinfop->rpfneighbor = *sin6;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ {
+#if 0
+ rpfinfop->rpfneighbor.sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+#endif
+ rpfinfop->rpfneighbor.sin6_scope_id = ifp->sdl_index;
+ /*
+ * XXX: KAME kernel embeds the interface index to the address.
+ * Clear the index for safety.
+ */
+ rpfinfop->rpfneighbor.sin6_addr.s6_addr[2] = 0;
+ rpfinfop->rpfneighbor.sin6_addr.s6_addr[3] = 0;
+ }
+ }
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ /* get the number of the interface by matching the name */
+ if ((strlen(v->uv_name) == ifp->sdl_nlen) &&
+ !(strncmp(v->uv_name,ifp->sdl_data,ifp->sdl_nlen)))
+ break;
+
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0, " iif is %d", vifi);
+
+ rpfinfop->iif = vifi;
+
+ if (vifi >= numvifs)
+ {
+ IF_DEBUG(DEBUG_RPF)
+ log(LOG_DEBUG, 0,
+ "Invalid incoming interface for destination %s, because of invalid virtual interface",
+ inet_ntop(AF_INET6, &sin6->sin6_addr, in6txt, INET6_ADDRSTRLEN));
+ return (FALSE); /* invalid iif */
+ }
+
+ return (TRUE);
+}
+
+
+#else /* HAVE_ROUTING_SOCKETS */
+
+
+/*
+ * Return in rpfcinfo the incoming interface and the next hop router toward
+ * source.
+ */
+/* TODO: check whether next hop router address is in network or host order */
+int
+k_req_incoming(source, rpfcinfo)
+ struct sockaddr_in6 *source;
+ struct rpfctl *rpfcinfo;
+{
+ rpfcinfo->source = *source;
+ rpfcinfo->iif = NO_VIF; /* just initialized, will be */
+ /* changed in kernel */
+ memset(&rpfcinfo->rpfneighbor, 0, sizeof(rpfcinfo->rpfneighbor)); /* initialized */
+
+ if (ioctl(udp_socket, SIOCGETRPF, (char *) rpfcinfo) < 0)
+ {
+ log(LOG_ERR, errno, "ioctl SIOCGETRPF k_req_incoming");
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+#endif /* HAVE_ROUTING_SOCKETS */
diff --git a/usr.sbin/pim6sd/routesock.h b/usr.sbin/pim6sd/routesock.h
new file mode 100644
index 0000000..0d3f9f7
--- /dev/null
+++ b/usr.sbin/pim6sd/routesock.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef ROUTESOCK_H
+#define ROUTESOCK_H
+
+void init_routesock();
+extern int pid;
+extern int k_req_incoming __P((struct sockaddr_in6 *source,
+ struct rpfctl *rpfp));
+
+#endif
diff --git a/usr.sbin/pim6sd/rp.c b/usr.sbin/pim6sd/rp.c
new file mode 100644
index 0000000..48936e4
--- /dev/null
+++ b/usr.sbin/pim6sd/rp.c
@@ -0,0 +1,1210 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include "rp.h"
+#include "pim6_proto.h"
+#include "pimd.h"
+#include <netinet6/pim6.h>
+#include "timer.h"
+#include "inet6.h"
+#include "route.h"
+#include "pimd.h"
+#include "debug.h"
+#include "crc.h"
+
+/*
+ * The hash function. Stollen from Eddy's (eddy@isi.edu) implementation (for
+ * compatibility ;)
+ */
+
+#define SEED1 1103515245
+#define SEED2 12345
+#define RP_HASH_VALUE(G, M, C) (((SEED1) * (((SEED1) * ((G) & (M)) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000)
+#define RP_HASH_VALUE2(P, C) (((SEED1) * (((SEED1) * (P) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000)
+
+cand_rp_t *cand_rp_list;
+grp_mask_t *grp_mask_list;
+cand_rp_t *segmented_cand_rp_list;
+grp_mask_t *segmented_grp_mask_list;
+u_int16 curr_bsr_fragment_tag;
+u_int8 curr_bsr_priority;
+struct sockaddr_in6 curr_bsr_address;
+struct in6_addr curr_bsr_hash_mask;
+u_int16 pim_bootstrap_timer; /* For electing the BSR and sending
+ * Cand-RP-set msgs */
+u_int8 my_bsr_priority;
+struct sockaddr_in6 my_bsr_address;
+struct in6_addr my_bsr_hash_mask;
+u_int8 cand_bsr_flag = FALSE; /* Set to TRUE if I am a candidate
+ * BSR */
+struct sockaddr_in6 my_cand_rp_address;
+u_int8 my_cand_rp_priority;
+u_int16 my_cand_rp_holdtime;
+u_int16 my_cand_rp_adv_period; /* The locally configured Cand-RP
+ * adv. period. */
+u_int16 my_bsr_period; /* The locally configured BSR
+ period */
+u_int16 pim_cand_rp_adv_timer;
+u_int8 cand_rp_flag = FALSE; /* Candidate RP flag */
+struct cand_rp_adv_message_ cand_rp_adv_message;
+struct in6_addr rp_my_ipv6_hashmask;
+
+
+/*
+ * Local functions definition.
+ */
+static cand_rp_t *add_cand_rp __P((cand_rp_t **used_cand_rp_list ,
+ struct sockaddr_in6 *address));
+
+static grp_mask_t *add_grp_mask __P((grp_mask_t ** used_grp_mask_list,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask,
+ struct in6_addr hash_mask));
+
+static void delete_grp_mask_entry __P((cand_rp_t ** used_cand_rp_list,
+ grp_mask_t ** used_grp_mask_list,
+ grp_mask_t * grp_mask_delete));
+
+static void delete_rp_entry __P((cand_rp_t ** used_cand_rp_list,
+ grp_mask_t ** used_grp_mask_list,
+ cand_rp_t * cand_rp_ptr));
+
+
+void
+init_rp6_and_bsr6()
+{
+ /* TODO: if the grplist is not NULL, remap all groups ASAP! */
+
+ delete_rp_list(&cand_rp_list, &grp_mask_list);
+ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
+
+ if (cand_bsr_flag == FALSE)
+ {
+ /*
+ * If I am not candidat BSR, initialize the "current BSR" as having
+ * the lowest priority.
+ */
+
+ curr_bsr_fragment_tag = 0;
+ curr_bsr_priority = 0; /* Lowest priority */
+ curr_bsr_address = sockaddr6_any; /* Lowest priority */
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+ }
+ else
+ {
+ curr_bsr_fragment_tag = RANDOM();
+ curr_bsr_priority = my_bsr_priority;
+ curr_bsr_address = my_bsr_address;
+ curr_bsr_hash_mask = my_bsr_hash_mask;
+ SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
+ }
+
+ if (cand_rp_flag != FALSE)
+ {
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, rp_my_ipv6_hashmask);
+ /* Setup the Cand-RP-Adv-Timer */
+ SET_TIMER(pim_cand_rp_adv_timer, RANDOM() % my_cand_rp_adv_period);
+ }
+}
+
+
+/*
+ * XXX: This implementation is based on section 6.2 of RFC 2362, which
+ * is highly dependent on IPv4.
+ * We'll have to rewrite the function...
+ */
+u_int16
+bootstrap_initial_delay()
+{
+// long AddrDelay;
+// long Delay;
+// long log_mask;
+// int log_of_2;
+// u_int8 bestPriority;
+
+ /*
+ * The bootstrap timer initial value (if Cand-BSR). It depends of the
+ * bootstrap router priority: higher priority has shorter value:
+ *
+ * Delay = 5 + 2*log_2(1 + bestPriority - myPriority) + AddrDelay;
+ *
+ * bestPriority = Max(storedPriority, myPriority); if (bestPriority ==
+ * myPriority) AddrDelay = log_2(bestAddr - myAddr)/16; else AddrDelay =
+ * 2 - (myAddr/2^31);
+ */
+
+// bestPriority = max(curr_bsr_priority, my_bsr_priority);
+// if (bestPriority == my_bsr_priority)
+ // {
+// AddrDelay = ntohl(curr_bsr_address) - ntohl(my_bsr_address);
+ /* Calculate the integer part of log_2 of (bestAddr - myAddr) */
+ /*
+ * To do so, have to find the position number of the first bit from
+ * left which is `1`
+ */
+// log_mask = sizeof(AddrDelay) << 3;
+// log_mask = (1 << (log_mask - 1)); /* Set the leftmost bit to
+// * `1` */
+/* for (log_of_2 = (sizeof(AddrDelay) << 3) - 1; log_of_2; log_of_2--)
+ {
+ if (AddrDelay & log_mask)
+ break;
+ else
+*/
+// log_mask >>= 1; /* Start shifting `1` on right */
+/* }
+ AddrDelay = log_of_2 / 16;
+ }
+ else
+ AddrDelay = 2 - (ntohl(my_bsr_address) / (1 << 31));
+
+ Delay = 1 + bestPriority - my_bsr_priority;
+ */
+ /* Calculate log_2(Delay) */
+// log_mask = sizeof(Delay) << 3;
+// log_mask = (1 << (log_mask - 1));
+/* Set the leftmost bit to `1`
+*/
+
+ /* for (log_of_2 = (sizeof(Delay) << 3) - 1; log_of_2; log_of_2--)
+ {
+ if (Delay & log_mask)
+ break;
+ else
+*/
+// log_mask >>= 1; /* Start shifting `1` on right */
+
+/* }
+
+ Delay = 5 + 2 * Delay + AddrDelay;
+ return (u_int16) Delay;
+*/
+
+ /* Temporary implementation */
+ return (RANDOM()%my_bsr_period);
+}
+
+
+static cand_rp_t *
+add_cand_rp(used_cand_rp_list, address)
+ cand_rp_t **used_cand_rp_list;
+ struct sockaddr_in6 *address;
+{
+ cand_rp_t *cand_rp_prev = (cand_rp_t *) NULL;
+ cand_rp_t *cand_rp;
+ cand_rp_t *cand_rp_new;
+ rpentry_t *rpentry_ptr;
+
+ /* The ordering is the bigger first */
+ for (cand_rp = *used_cand_rp_list; cand_rp != (cand_rp_t *) NULL;
+ cand_rp_prev = cand_rp, cand_rp = cand_rp->next)
+ {
+
+ if (inet6_greaterthan(&cand_rp->rpentry->address, address))
+ continue;
+ if (inet6_equal(&cand_rp->rpentry->address , address))
+ return (cand_rp);
+ else
+ break;
+ }
+
+ /* Create and insert the new entry between cand_rp_prev and cand_rp */
+ cand_rp_new = (cand_rp_t *) malloc(sizeof(cand_rp_t));
+ cand_rp_new->rp_grp_next = (rp_grp_entry_t *) NULL;
+ cand_rp_new->next = cand_rp;
+ cand_rp_new->prev = cand_rp_prev;
+ if (cand_rp != (cand_rp_t *) NULL)
+ cand_rp->prev = cand_rp_new;
+ if (cand_rp_prev == (cand_rp_t *) NULL)
+ {
+ *used_cand_rp_list = cand_rp_new;
+ }
+ else
+ {
+ cand_rp_prev->next = cand_rp_new;
+ }
+
+ rpentry_ptr = (rpentry_t *) malloc(sizeof(rpentry_t));
+ cand_rp_new->rpentry = rpentry_ptr;
+ rpentry_ptr->next = (srcentry_t *) NULL;
+ rpentry_ptr->prev = (srcentry_t *) NULL;
+ rpentry_ptr->address = *address;
+ rpentry_ptr->mrtlink = (mrtentry_t *) NULL;
+ rpentry_ptr->incoming = NO_VIF;
+ rpentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
+
+ /* TODO: setup the metric and the preference as ~0 (the lowest)? */
+
+ rpentry_ptr->metric = ~0;
+ rpentry_ptr->preference = ~0;
+ RESET_TIMER(rpentry_ptr->timer);
+ rpentry_ptr->cand_rp = cand_rp_new;
+
+ /*
+ * TODO: XXX: check whether there is a route to that RP: if return value
+ * is FALSE, then no route.
+ */
+
+ if (local_address(&rpentry_ptr->address) == NO_VIF)
+ {
+ /* TODO: check for error and delete */
+ set_incoming(rpentry_ptr, PIM_IIF_RP);
+ }
+ else
+ {
+ /* TODO: XXX: CHECK!!! */
+ rpentry_ptr->incoming = reg_vif_num;
+ }
+
+ return (cand_rp_new);
+}
+
+
+static grp_mask_t *
+add_grp_mask(used_grp_mask_list, group_addr, group_mask, hash_mask)
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr hash_mask;
+{
+ grp_mask_t *grp_mask_prev = (grp_mask_t *) NULL;
+ grp_mask_t *grp_mask;
+ grp_mask_t *grp_mask_tmp;
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ /* I compare on the adresses, inet6_equal use the scope, too */
+ prefix_h.sin6_scope_id = prefix_h2.sin6_scope_id = 0;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] =
+ group_addr->sin6_addr.s6_addr[i] & group_mask.s6_addr[i];
+
+ /* The ordering is: smaller first */
+ for (grp_mask = *used_grp_mask_list; grp_mask != (grp_mask_t *) NULL;
+ grp_mask_prev = grp_mask, grp_mask = grp_mask->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ (grp_mask->group_addr.sin6_addr.s6_addr[i] &
+ grp_mask->group_mask.s6_addr[i]);
+ if (inet6_lessthan(&prefix_h2, &prefix_h) )
+ continue;
+ if (inet6_equal(&prefix_h2, &prefix_h))
+ return (grp_mask);
+ else
+ break;
+ }
+
+ grp_mask_tmp = (grp_mask_t *) malloc(sizeof(grp_mask_t));
+ grp_mask_tmp->grp_rp_next = (rp_grp_entry_t *) NULL;
+ grp_mask_tmp->next = grp_mask;
+ grp_mask_tmp->prev = grp_mask_prev;
+ if (grp_mask != (grp_mask_t *) NULL)
+ grp_mask->prev = grp_mask_tmp;
+ if (grp_mask_prev == (grp_mask_t *) NULL)
+ {
+ *used_grp_mask_list = grp_mask_tmp;
+ }
+ else
+ {
+ grp_mask_prev->next = grp_mask_tmp;
+ }
+
+ grp_mask_tmp->group_addr = *group_addr;
+ grp_mask_tmp->group_mask = group_mask;
+ grp_mask_tmp->hash_mask = hash_mask;
+ grp_mask_tmp->group_rp_number = 0;
+ grp_mask_tmp->fragment_tag = 0;
+ return (grp_mask_tmp);
+}
+
+
+/*
+ * TODO: XXX: BUG: a remapping for some groups currently using some other
+ * grp_mask may be required by the addition of the new entry!!! Remapping all
+ * groups might be a costly process...
+ */
+rp_grp_entry_t *
+add_rp_grp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_addr, rp_priority, rp_holdtime, group_addr, group_mask,
+ bsr_hash_mask,
+ fragment_tag)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *rp_addr;
+ u_int8 rp_priority;
+ u_int16 rp_holdtime;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+ struct in6_addr bsr_hash_mask;
+ u_int16 fragment_tag;
+{
+ cand_rp_t *cand_rp_ptr;
+ grp_mask_t *grp_mask_ptr;
+ rpentry_t *rpentry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+ rp_grp_entry_t *grp_rp_entry_new;
+ rp_grp_entry_t *grp_rp_entry_prev = (rp_grp_entry_t *) NULL;
+ grpentry_t *grpentry_ptr_prev;
+ grpentry_t *grpentry_ptr_next;
+ u_int8 old_highest_priority = ~0; /* Smaller value means
+ * "higher" */
+
+ /* Input data verification */
+ if (!inet6_valid_host(rp_addr))
+ return (rp_grp_entry_t *) NULL;
+
+ if (!IN6_IS_ADDR_MULTICAST(&group_addr->sin6_addr))
+ {
+ return (rp_grp_entry_t *) NULL;
+ }
+ grp_mask_ptr = add_grp_mask(used_grp_mask_list, group_addr, group_mask,
+ bsr_hash_mask);
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ /* TODO: delete */
+#if 0
+ if (grp_mask_ptr->grp_rp_next != (rp_grp_entry_t *) NULL)
+ {
+ /* Check for obsolete grp_rp chain */
+ if ((my_bsr_address != curr_bsr_address)
+ && (grp_mask_ptr->grp_rp_next->fragment_tag != fragment_tag))
+ {
+ /* This grp_rp chain is obsolete. Delete it. */
+ delete_grp_mask(used_cand_rp_list, used_grp_mask_list,
+ group_addr, group_mask);
+ grp_mask_ptr = add_grp_mask(used_grp_mask_list, group_addr,
+ group_mask, bsr_hash_mask);
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+ }
+ }
+#endif /* 0 */
+
+ cand_rp_ptr = add_cand_rp(used_cand_rp_list, rp_addr);
+ if (cand_rp_ptr == (cand_rp_t *) NULL)
+ {
+ if (grp_mask_ptr->grp_rp_next == (rp_grp_entry_t *) NULL)
+ delete_grp_mask(used_cand_rp_list, used_grp_mask_list,
+ group_addr, group_mask);
+ return (rp_grp_entry_t *) NULL;
+ }
+
+ rpentry_ptr = cand_rp_ptr->rpentry;
+ SET_TIMER(rpentry_ptr->timer, rp_holdtime);
+ grp_mask_ptr->fragment_tag = fragment_tag; /* For garbage collection */
+
+ grp_rp_entry_prev = (rp_grp_entry_t *) NULL;
+ grp_rp_entry_next = grp_mask_ptr->grp_rp_next;
+
+ /* TODO: improve it */
+
+ if (grp_rp_entry_next != (rp_grp_entry_t *) NULL)
+ old_highest_priority = grp_rp_entry_next->priority;
+ for (; grp_rp_entry_next != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_prev = grp_rp_entry_next,
+ grp_rp_entry_next = grp_rp_entry_next->grp_rp_next)
+ {
+ /*
+ * Smaller value means higher priority. The entries are sorted with
+ * the highest priority first.
+ */
+ if (grp_rp_entry_next->priority < rp_priority)
+ continue;
+ if (grp_rp_entry_next->priority > rp_priority)
+ break;
+
+ /*
+ * Here we don't care about higher/lower addresses, because higher
+ * address does not guarantee higher hash_value, but anyway we do
+ * order with the higher address first, so it will be easier to find
+ * an existing entry and update the holdtime.
+ */
+
+ if (inet6_greaterthan(&grp_rp_entry_next->rp->rpentry->address , rp_addr))
+ continue;
+ if (inet6_lessthan(&grp_rp_entry_next->rp->rpentry->address , rp_addr))
+ break;
+
+ /* We already have this entry. Update the holdtime */
+ /*
+ * TODO: We shoudn't have old existing entry, because with the
+ * current implementation all of them will be deleted (different
+ * fragment_tag). Debug and check and eventually delete.
+ */
+
+ grp_rp_entry_next->holdtime = rp_holdtime;
+ grp_rp_entry_next->advholdtime = rp_holdtime;
+ grp_rp_entry_next->fragment_tag = fragment_tag;
+ return (grp_rp_entry_next);
+ }
+
+ /* Create and link the new entry */
+
+ grp_rp_entry_new = (rp_grp_entry_t *) malloc(sizeof(rp_grp_entry_t));
+ grp_rp_entry_new->grp_rp_next = grp_rp_entry_next;
+ grp_rp_entry_new->grp_rp_prev = grp_rp_entry_prev;
+ if (grp_rp_entry_next != (rp_grp_entry_t *) NULL)
+ grp_rp_entry_next->grp_rp_prev = grp_rp_entry_new;
+ if (grp_rp_entry_prev == (rp_grp_entry_t *) NULL)
+ grp_mask_ptr->grp_rp_next = grp_rp_entry_new;
+ else
+ grp_rp_entry_prev->grp_rp_next = grp_rp_entry_new;
+
+ /*
+ * The rp_grp_entry chain is not ordered, so just plug the new entry at
+ * the head.
+ */
+
+ grp_rp_entry_new->rp_grp_next = cand_rp_ptr->rp_grp_next;
+ if (cand_rp_ptr->rp_grp_next != (rp_grp_entry_t *) NULL)
+ cand_rp_ptr->rp_grp_next->rp_grp_prev = grp_rp_entry_new;
+ grp_rp_entry_new->rp_grp_prev = (rp_grp_entry_t *) NULL;
+ cand_rp_ptr->rp_grp_next = grp_rp_entry_new;
+
+ grp_rp_entry_new->holdtime = rp_holdtime;
+ grp_rp_entry_new->advholdtime = rp_holdtime;
+ grp_rp_entry_new->fragment_tag = fragment_tag;
+ grp_rp_entry_new->priority = rp_priority;
+ grp_rp_entry_new->group = grp_mask_ptr;
+ grp_rp_entry_new->rp = cand_rp_ptr;
+ grp_rp_entry_new->grplink = (grpentry_t *) NULL;
+
+ grp_mask_ptr->group_rp_number++;
+
+ if (grp_mask_ptr->grp_rp_next->priority == rp_priority)
+ {
+ /* The first entries are with the best priority. */
+ /* Adding this rp_grp_entry may result in group_to_rp remapping */
+ for (grp_rp_entry_next = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_next != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_next = grp_rp_entry_next->grp_rp_next)
+ {
+ if (grp_rp_entry_next->priority > old_highest_priority)
+ break;
+ for (grpentry_ptr_prev = grp_rp_entry_next->grplink;
+ grpentry_ptr_prev != (grpentry_t *) NULL;)
+ {
+ grpentry_ptr_next = grpentry_ptr_prev->rpnext;
+ remap_grpentry(grpentry_ptr_prev);
+ grpentry_ptr_prev = grpentry_ptr_next;
+ }
+ }
+ }
+
+ return (grp_rp_entry_new);
+}
+
+
+void
+delete_rp_grp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ rp_grp_entry_t *rp_grp_entry_delete;
+{
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+
+ if (rp_grp_entry_delete == (rp_grp_entry_t *) NULL)
+ return;
+ rp_grp_entry_delete->group->group_rp_number--;
+ /* Free the rp_grp* and grp_rp* links */
+ if (rp_grp_entry_delete->rp_grp_prev != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->rp_grp_prev->rp_grp_next =
+ rp_grp_entry_delete->rp_grp_next;
+ else
+ rp_grp_entry_delete->rp->rp_grp_next =
+ rp_grp_entry_delete->rp_grp_next;
+ if (rp_grp_entry_delete->rp_grp_next != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->rp_grp_next->rp_grp_prev =
+ rp_grp_entry_delete->rp_grp_prev;
+
+ if (rp_grp_entry_delete->grp_rp_prev != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->grp_rp_prev->grp_rp_next =
+ rp_grp_entry_delete->grp_rp_next;
+ else
+ rp_grp_entry_delete->group->grp_rp_next =
+ rp_grp_entry_delete->grp_rp_next;
+ if (rp_grp_entry_delete->grp_rp_next != (rp_grp_entry_t *) NULL)
+ rp_grp_entry_delete->grp_rp_next->grp_rp_prev =
+ rp_grp_entry_delete->grp_rp_prev;
+
+ /* Delete Cand-RP or Group-prefix if useless */
+ if (rp_grp_entry_delete->group->grp_rp_next ==
+ (rp_grp_entry_t *) NULL)
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete->group);
+ if (rp_grp_entry_delete->rp->rp_grp_next ==
+ (rp_grp_entry_t *) NULL)
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_delete->rp);
+
+ /* Remap all affected groups */
+ for (grpentry_ptr = rp_grp_entry_delete->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+ }
+
+ free((char *) rp_grp_entry_delete);
+}
+
+/*
+ * TODO: XXX: the affected group entries will be partially setup, because may
+ * have group routing entry, but NULL pointers to RP. After the call to this
+ * function, must remap all group entries ASAP.
+ */
+
+void
+delete_rp_list(used_cand_rp_list, used_grp_mask_list)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+{
+ cand_rp_t *cand_rp_ptr,
+ *cand_rp_next;
+ grp_mask_t *grp_mask_ptr,
+ *grp_mask_next;
+ rp_grp_entry_t *rp_grp_entry_ptr,
+ *rp_grp_entry_next;
+ grpentry_t *grpentry_ptr,
+ *grpentry_ptr_next;
+
+ for (cand_rp_ptr = *used_cand_rp_list;
+ cand_rp_ptr != (cand_rp_t *) NULL;)
+ {
+ cand_rp_next = cand_rp_ptr->next;
+ /* Free the mrtentry (if any) for this RP */
+ if (cand_rp_ptr->rpentry->mrtlink != (mrtentry_t *) NULL)
+ {
+ if (cand_rp_ptr->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(cand_rp_ptr->rpentry->mrtlink);
+ FREE_MRTENTRY(cand_rp_ptr->rpentry->mrtlink);
+ }
+ free(cand_rp_ptr->rpentry);
+
+ /* Free the whole chain of rp_grp_entry for this RP */
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->rp_grp_next;
+ /* Clear the RP related invalid pointers for all group entries */
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ grpentry_ptr->rpnext = (grpentry_t *) NULL;
+ grpentry_ptr->rpprev = (grpentry_t *) NULL;
+ grpentry_ptr->active_rp_grp = (rp_grp_entry_t *) NULL;
+ grpentry_ptr->rpaddr = sockaddr6_any;
+ }
+ free(rp_grp_entry_ptr);
+ }
+ cand_rp_ptr = cand_rp_next;
+ }
+ *used_cand_rp_list = (cand_rp_t *) NULL;
+
+ for (grp_mask_ptr = *used_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ grp_mask_next = grp_mask_ptr->next;
+ free(grp_mask_ptr);
+ }
+ *used_grp_mask_list = (grp_mask_t *) NULL;
+}
+
+
+void
+delete_grp_mask(used_cand_rp_list, used_grp_mask_list, group_addr, group_mask)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *group_addr;
+ struct in6_addr group_mask;
+{
+ grp_mask_t *grp_mask_ptr;
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] = group_addr->sin6_addr.s6_addr[i]&group_mask.s6_addr[i];
+
+ for (grp_mask_ptr = *used_grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ grp_mask_ptr->group_addr.sin6_addr.s6_addr[i]&grp_mask_ptr->group_mask.s6_addr[i];
+
+ if (inet6_lessthan(&prefix_h2, &prefix_h))
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_addr.sin6_addr,
+ &group_addr->sin6_addr) &&
+ IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_mask, &group_mask))
+ break;
+ else
+ return; /* Not found */
+ }
+
+ if (grp_mask_ptr == (grp_mask_t *) NULL)
+ return; /* Not found */
+
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ grp_mask_ptr);
+}
+
+static void
+delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list, grp_mask_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ grp_mask_t *grp_mask_delete;
+{
+ grpentry_t *grpentry_ptr,
+ *grpentry_ptr_next;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *grp_rp_entry_next;
+
+ if (grp_mask_delete == (grp_mask_t *) NULL)
+ return;
+
+ /* Remove from the grp_mask_list first */
+
+ if (grp_mask_delete->prev != (grp_mask_t *) NULL)
+ grp_mask_delete->prev->next = grp_mask_delete->next;
+ else
+ *used_grp_mask_list = grp_mask_delete->next;
+
+ if (grp_mask_delete->next != (grp_mask_t *) NULL)
+ grp_mask_delete->next->prev = grp_mask_delete->prev;
+
+ /* Remove all grp_rp entries for this grp_mask */
+ for (grp_rp_entry_ptr = grp_mask_delete->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_next)
+ {
+ grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
+ /* Remap all related grpentry */
+ for (grpentry_ptr = grp_rp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+
+ }
+ if (grp_rp_entry_ptr->rp_grp_prev != (rp_grp_entry_t *) NULL)
+ {
+ grp_rp_entry_ptr->rp_grp_prev->rp_grp_next =
+ grp_rp_entry_ptr->rp_grp_next;
+ }
+ else
+ {
+ grp_rp_entry_ptr->rp->rp_grp_next = grp_rp_entry_ptr->rp_grp_next;
+ }
+ if (grp_rp_entry_ptr->rp_grp_next != (rp_grp_entry_t *) NULL)
+ grp_rp_entry_ptr->rp_grp_next->rp_grp_prev =
+ grp_rp_entry_ptr->rp_grp_prev;
+ if (grp_rp_entry_ptr->rp->rp_grp_next == (rp_grp_entry_t *) NULL)
+ {
+ /* Delete the RP entry */
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list,
+ grp_rp_entry_ptr->rp);
+ }
+ free(grp_rp_entry_ptr);
+ }
+}
+
+/*
+ * TODO: currently not used.
+ */
+
+void
+delete_rp(used_cand_rp_list, used_grp_mask_list, rp_addr)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ struct sockaddr_in6 *rp_addr;
+{
+ cand_rp_t *cand_rp_ptr;
+
+ for (cand_rp_ptr = *used_cand_rp_list;
+ cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if (inet6_greaterthan(&cand_rp_ptr->rpentry->address, rp_addr))
+ continue;
+ if (inet6_equal(&cand_rp_ptr->rpentry->address,rp_addr))
+ break;
+ else
+ return; /* Not found */
+ }
+
+ if (cand_rp_ptr == (cand_rp_t *) NULL)
+ return; /* Not found */
+ delete_rp_entry(used_cand_rp_list, used_grp_mask_list, cand_rp_ptr);
+}
+
+
+static void
+delete_rp_entry(used_cand_rp_list, used_grp_mask_list, cand_rp_delete)
+ cand_rp_t **used_cand_rp_list;
+ grp_mask_t **used_grp_mask_list;
+ cand_rp_t *cand_rp_delete;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rp_grp_entry_t *rp_grp_entry_next;
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+
+ if (cand_rp_delete == (cand_rp_t *) NULL)
+ return;
+
+ /* Remove from the cand-RP chain */
+ if (cand_rp_delete->prev != (cand_rp_t *) NULL)
+ cand_rp_delete->prev->next = cand_rp_delete->next;
+ else
+ *used_cand_rp_list = cand_rp_delete->next;
+
+ if (cand_rp_delete->next != (cand_rp_t *) NULL)
+ cand_rp_delete->next->prev = cand_rp_delete->prev;
+
+ if (cand_rp_delete->rpentry->mrtlink != (mrtentry_t *) NULL)
+ {
+ if (cand_rp_delete->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE)
+ delete_mrtentry_all_kernel_cache(cand_rp_delete->rpentry->mrtlink);
+ FREE_MRTENTRY(cand_rp_delete->rpentry->mrtlink);
+ }
+ free((char *) cand_rp_delete->rpentry);
+
+ /* Remove all rp_grp entries for this RP */
+ for (rp_grp_entry_ptr = cand_rp_delete->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->rp_grp_next;
+ rp_grp_entry_ptr->group->group_rp_number--;
+
+ /* First take care of the grp_rp chain */
+ if (rp_grp_entry_ptr->grp_rp_prev != (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr->grp_rp_prev->grp_rp_next =
+ rp_grp_entry_ptr->grp_rp_next;
+ }
+ else
+ {
+ rp_grp_entry_ptr->group->grp_rp_next =
+ rp_grp_entry_ptr->grp_rp_next;
+ }
+ if (rp_grp_entry_ptr->grp_rp_next != (rp_grp_entry_t *) NULL)
+ {
+ rp_grp_entry_ptr->grp_rp_next->grp_rp_prev =
+ rp_grp_entry_ptr->grp_rp_prev;
+ }
+
+ if (rp_grp_entry_ptr->grp_rp_next == (rp_grp_entry_t *) NULL)
+ {
+ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list,
+ rp_grp_entry_ptr->group);
+ }
+
+ /* Remap the related groups */
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ remap_grpentry(grpentry_ptr);
+ }
+ free(rp_grp_entry_ptr);
+ }
+ free((char *) cand_rp_delete);
+}
+
+
+/*
+ * Rehash the RP for the group. XXX: currently, every time when
+ * remap_grpentry() is called, there has being a good reason to change the
+ * RP, so for performancy reasons no check is performed whether the RP will
+ * be really different one.
+ */
+
+int
+remap_grpentry(grpentry_ptr)
+ grpentry_t *grpentry_ptr;
+{
+ rpentry_t *rpentry_ptr;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ mrtentry_t *grp_route;
+ mrtentry_t *mrtentry_ptr;
+
+ if (grpentry_ptr == (grpentry_t *) NULL)
+ return (FALSE);
+
+ /* Remove from the list of all groups matching to the same RP */
+ if (grpentry_ptr->rpprev != (grpentry_t *) NULL)
+ grpentry_ptr->rpprev->rpnext = grpentry_ptr->rpnext;
+ else
+ {
+ if (grpentry_ptr->active_rp_grp != (rp_grp_entry_t *) NULL)
+ grpentry_ptr->active_rp_grp->grplink = grpentry_ptr->rpnext;
+ }
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr->rpprev;
+
+ rp_grp_entry_ptr = rp_grp_match(&grpentry_ptr->group);
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ {
+ /* If cannot remap, delete the group */
+ delete_grpentry(grpentry_ptr);
+ return (FALSE);
+ }
+ rpentry_ptr = rp_grp_entry_ptr->rp->rpentry;
+
+ /* Add to the new chain of all groups mapping to the same RP */
+ grpentry_ptr->rpaddr = rpentry_ptr->address;
+ grpentry_ptr->active_rp_grp = rp_grp_entry_ptr;
+ grpentry_ptr->rpnext = rp_grp_entry_ptr->grplink;
+ if (grpentry_ptr->rpnext != (grpentry_t *) NULL)
+ grpentry_ptr->rpnext->rpprev = grpentry_ptr;
+ grpentry_ptr->rpprev = (grpentry_t *) NULL;
+ rp_grp_entry_ptr->grplink = grpentry_ptr;
+
+ if ((grp_route = grpentry_ptr->grp_route) != (mrtentry_t *) NULL)
+ {
+ grp_route->upstream = rpentry_ptr->upstream;
+ grp_route->metric = rpentry_ptr->metric;
+ grp_route->preference = rpentry_ptr->preference;
+ change_interfaces(grp_route, rpentry_ptr->incoming,
+ &grp_route->joined_oifs,
+ &grp_route->pruned_oifs,
+ &grp_route->leaves,
+ &grp_route->asserted_oifs, MFC_UPDATE_FORCE);
+ }
+
+ for (mrtentry_ptr = grpentry_ptr->mrtlink;
+ mrtentry_ptr != (mrtentry_t *) NULL;
+ mrtentry_ptr = mrtentry_ptr->grpnext)
+ {
+ if (!(mrtentry_ptr->flags & MRTF_RP))
+ continue;
+ mrtentry_ptr->upstream = rpentry_ptr->upstream;
+ mrtentry_ptr->metric = rpentry_ptr->metric;
+ mrtentry_ptr->preference = rpentry_ptr->preference;
+ change_interfaces(mrtentry_ptr, rpentry_ptr->incoming,
+ &mrtentry_ptr->joined_oifs,
+ &mrtentry_ptr->pruned_oifs,
+ &mrtentry_ptr->leaves,
+ &mrtentry_ptr->asserted_oifs, MFC_UPDATE_FORCE);
+ }
+
+ return (TRUE);
+}
+
+
+rpentry_t *
+rp_match(group)
+ struct sockaddr_in6 *group;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ rp_grp_entry_ptr = rp_grp_match(group);
+ if (rp_grp_entry_ptr != (rp_grp_entry_t *) NULL)
+ return (rp_grp_entry_ptr->rp->rpentry);
+ else
+ return (rpentry_t *) NULL;
+}
+
+
+rp_grp_entry_t *
+rp_grp_match(group)
+ struct sockaddr_in6 *group;
+{
+ grp_mask_t *grp_mask_ptr;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ rp_grp_entry_t *best_entry = (rp_grp_entry_t *) NULL;
+ u_int8 best_priority = ~0; /* Smaller is better */
+ u_int32 best_hash_value = 0; /* Bigger is better */
+ struct sockaddr_in6 best_address_h; /* Bigger is better */
+ u_int32 curr_hash_value = 0;
+ struct sockaddr_in6 curr_address_h;
+
+ struct sockaddr_in6 prefix_h;
+ struct sockaddr_in6 prefix_h2;
+ int i;
+
+ if (grp_mask_list == (grp_mask_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ /* XXX :I compare on the adresses , inet6_equal use the scope too */
+ prefix_h.sin6_scope_id = prefix_h2.sin6_scope_id = 0;
+
+ for (grp_mask_ptr = grp_mask_list; grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h2.sin6_addr.s6_addr[i] =
+ (grp_mask_ptr->group_addr.sin6_addr.s6_addr[i] &
+ grp_mask_ptr->group_mask.s6_addr[i]);
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ prefix_h.sin6_addr.s6_addr[i] =
+ (group->sin6_addr.s6_addr[i] &
+ grp_mask_ptr->group_mask.s6_addr[i]);
+
+ /* Search the grp_mask (group_prefix) list */
+ if ((inet6_greaterthan(&prefix_h, &prefix_h2)))
+ continue;
+ if ((inet6_lessthan(&prefix_h, &prefix_h2)))
+ break;
+
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+
+ if (best_priority < grp_rp_entry_ptr->priority)
+ break;
+
+ curr_address_h = grp_rp_entry_ptr->rp->rpentry->address;
+#if 0
+ curr_hash_value = RP_HASH_VALUE(crc((char *)&group->sin6_addr,
+ sizeof(struct in6_addr)),
+ crc((char *)&grp_mask_ptr->hash_mask,
+ sizeof(struct in6_addr)),
+ crc((char *)&curr_address_h.sin6_addr,
+ sizeof(struct in6_addr)));
+#else
+ {
+ struct in6_addr masked_grp;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ masked_grp.s6_addr[i] =
+ group->sin6_addr.s6_addr[i] &
+ grp_mask_ptr->hash_mask.s6_addr[i];
+ curr_hash_value = RP_HASH_VALUE2(crc((char *)&masked_grp,
+ sizeof(struct in6_addr)),
+ crc((char *)&curr_address_h.sin6_addr,
+ sizeof(struct in6_addr)));
+ }
+#endif
+
+ if (best_priority == grp_rp_entry_ptr->priority)
+ {
+ /* Compare the hash_value and then the addresses */
+
+ if (curr_hash_value < best_hash_value)
+ continue;
+ if (curr_hash_value == best_hash_value)
+ if (inet6_lessthan(&curr_address_h ,&best_address_h))
+ continue;
+ }
+
+ /* The current entry in the loop is preferred */
+
+ best_entry = grp_rp_entry_ptr;
+ best_priority = best_entry->priority;
+ best_address_h = curr_address_h;
+ best_hash_value = curr_hash_value;
+ }
+ }
+
+
+ if (best_entry == (rp_grp_entry_t *) NULL)
+ return (rp_grp_entry_t *) NULL;
+
+ IF_DEBUG(DEBUG_PIM_CAND_RP)
+ log(LOG_DEBUG,0,"Rp_grp_match found %s for group %s",
+ inet6_fmt(&best_entry->rp->rpentry->address.sin6_addr),
+ inet6_fmt(&group->sin6_addr));
+
+ return (best_entry);
+}
+
+
+rpentry_t *
+rp_find(rp_address)
+ struct sockaddr_in6 *rp_address;
+{
+ cand_rp_t *cand_rp_ptr;
+
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+ if( inet6_greaterthan(&cand_rp_ptr->rpentry->address,rp_address))
+ continue;
+ if( inet6_equal(&cand_rp_ptr->rpentry->address,rp_address))
+ return (cand_rp_ptr->rpentry);
+ return (rpentry_t *) NULL;
+ }
+
+ return (rpentry_t *) NULL;
+}
+
+
+/*
+ * Create a bootstrap message in "send_buff" and returns the data size
+ * (excluding the IP header and the PIM header) Can be used both by the
+ * Bootstrap router to multicast the RP-set or by the DR to unicast it to a
+ * new neighbor. It DOES NOT change any timers.
+ */
+
+int
+create_pim6_bootstrap_message(send_buff)
+ char *send_buff;
+{
+ u_int8 *data_ptr;
+ grp_mask_t *grp_mask_ptr;
+ rp_grp_entry_t *grp_rp_entry_ptr;
+ int datalen;
+ u_int8 masklen=0;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&curr_bsr_address.sin6_addr))
+ return (0);
+
+ data_ptr = (u_int8 *) (send_buff + sizeof(struct pim));
+
+ if( inet6_equal(&curr_bsr_address , &my_bsr_address ))
+ curr_bsr_fragment_tag++;
+ PUT_HOSTSHORT(curr_bsr_fragment_tag, data_ptr);
+ MASK_TO_MASKLEN6(curr_bsr_hash_mask, masklen);
+ PUT_BYTE(masklen, data_ptr);
+ PUT_BYTE(curr_bsr_priority, data_ptr);
+ PUT_EUADDR6(curr_bsr_address.sin6_addr, data_ptr);
+
+ /* TODO: XXX: No fragmentation support (yet) */
+
+ for (grp_mask_ptr = grp_mask_list; grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_ptr->next)
+ {
+ MASK_TO_MASKLEN6(grp_mask_ptr->group_mask, masklen);
+ PUT_EGADDR6(grp_mask_ptr->group_addr.sin6_addr, masklen, 0, data_ptr);
+ PUT_BYTE(grp_mask_ptr->group_rp_number, data_ptr);
+ PUT_BYTE(grp_mask_ptr->group_rp_number, data_ptr); /* TODO: if frag. */
+ PUT_HOSTSHORT(0, data_ptr);
+ for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
+ grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
+ {
+ PUT_EUADDR6(grp_rp_entry_ptr->rp->rpentry->address.sin6_addr, data_ptr);
+ PUT_HOSTSHORT(grp_rp_entry_ptr->advholdtime, data_ptr);
+ PUT_BYTE(grp_rp_entry_ptr->priority, data_ptr);
+ PUT_BYTE(0, data_ptr); /* The reserved field */
+ }
+ }
+
+ datalen = (data_ptr - (u_int8 *) send_buff) - sizeof(struct pim);
+
+ return (datalen);
+}
+
+
+/*
+ * Check if the rp_addr is the RP for the group corresponding to
+ * mrtentry_ptr. Return TRUE or FALSE.
+ */
+
+int
+check_mrtentry_rp(mrtentry_ptr, rp_addr)
+ mrtentry_t *mrtentry_ptr;
+ struct sockaddr_in6 *rp_addr;
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+
+ if (mrtentry_ptr == (mrtentry_t *) NULL)
+ return (FALSE);
+ if (IN6_IS_ADDR_UNSPECIFIED(&rp_addr->sin6_addr))
+ return (FALSE);
+ rp_grp_entry_ptr = mrtentry_ptr->group->active_rp_grp;
+ if (rp_grp_entry_ptr == (rp_grp_entry_t *) NULL)
+ return (FALSE);
+ if (inet6_equal(&mrtentry_ptr->group->rpaddr,rp_addr))
+ return (TRUE);
+ return (FALSE);
+}
diff --git a/usr.sbin/pim6sd/rp.h b/usr.sbin/pim6sd/rp.h
new file mode 100644
index 0000000..56817f1
--- /dev/null
+++ b/usr.sbin/pim6sd/rp.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef RP_H
+#define RP_H
+
+#include "defs.h"
+#include "mrt.h"
+
+extern cand_rp_t *cand_rp_list;
+extern grp_mask_t *grp_mask_list;
+extern cand_rp_t *segmented_cand_rp_list;
+extern grp_mask_t *segmented_grp_mask_list;
+
+extern u_int8 cand_rp_flag;
+extern u_int8 cand_bsr_flag;
+extern u_int8 my_cand_rp_priority;
+extern u_int8 my_bsr_priority;
+extern u_int16 my_cand_rp_adv_period;
+extern u_int16 my_bsr_period;
+extern u_int16 my_cand_rp_holdtime;
+extern struct sockaddr_in6 my_cand_rp_address;
+extern struct sockaddr_in6 my_bsr_address;
+extern struct in6_addr my_bsr_hash_mask;
+extern struct in6_addr curr_bsr_hash_mask;
+extern struct sockaddr_in6 curr_bsr_address;
+extern u_int16 curr_bsr_fragment_tag;
+extern u_int8 curr_bsr_priority;
+extern u_int16 pim_bootstrap_timer;
+extern u_int16 pim_cand_rp_adv_timer;
+
+extern struct cand_rp_adv_message_ {
+ u_int8 *buffer;
+ u_int8 *insert_data_ptr;
+ u_int8 *prefix_cnt_ptr;
+ u_int16 message_size;
+} cand_rp_adv_message;
+
+
+extern void init_rp6_and_bsr6 __P(());
+void delete_rp_list( cand_rp_t **used_cand_rp_list , grp_mask_t **used_grp_mask_list );
+u_int16 bootstrap_initial_delay();
+extern rpentry_t *rp_match __P((struct sockaddr_in6 *group));
+extern rp_grp_entry_t *rp_grp_match __P((struct sockaddr_in6 *group));
+extern int create_pim6_bootstrap_message __P((char *send_buff));
+
+extern rp_grp_entry_t *add_rp_grp_entry __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *rp_addr,
+ u_int8 rp_priority,
+ u_int16 rp_holdtime,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask,
+ struct in6_addr bsr_hash_mask,
+ u_int16 fragment_tag));
+extern void delete_rp_grp_entry __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ rp_grp_entry_t *rp_grp_entry_delete));
+extern void delete_grp_mask __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *group_addr,
+ struct in6_addr group_mask));
+extern void delete_rp __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list,
+ struct sockaddr_in6 *rp_addr));
+extern void delete_rp_list __P((cand_rp_t **used_cand_rp_list,
+ grp_mask_t **used_grp_mask_list));
+extern rpentry_t *rp_match __P((struct sockaddr_in6 *group));
+extern rp_grp_entry_t *rp_grp_match __P((struct sockaddr_in6 *group));
+extern rpentry_t *rp_find __P((struct sockaddr_in6 *rp_address));
+extern int remap_grpentry __P((grpentry_t *grpentry_ptr));
+extern int check_mrtentry_rp __P((mrtentry_t *mrtentry_ptr,
+ struct sockaddr_in6 *rp_addr));
+
+
+
+
+#endif
diff --git a/usr.sbin/pim6sd/timer.c b/usr.sbin/pim6sd/timer.c
new file mode 100644
index 0000000..af74952
--- /dev/null
+++ b/usr.sbin/pim6sd/timer.c
@@ -0,0 +1,1286 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include "pimd.h"
+#include "mrt.h"
+#include "vif.h"
+#include <netinet6/ip6_mroute.h>
+#include "timer.h"
+#include "debug.h"
+#include "rp.h"
+#include "pim6_proto.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "kern.h"
+#include "debug.h"
+#include "inet6.h"
+
+/*
+ * Global variables
+ */
+/*
+ * XXX: The RATE is in bits/s. To include the header overhead, the
+ * approximation is 1 byte/s = 10 bits/s `whatever_bytes` is the maximum
+ * number of bytes within the test interval.
+ */
+
+u_int32 timer_interval=DEFAULT_TIMER_INTERVAL;
+u_int32 pim_reg_rate_bytes =
+(PIM_DEFAULT_REG_RATE * PIM_DEFAULT_REG_RATE_INTERVAL) / 10;
+u_int32 pim_reg_rate_check_interval = PIM_DEFAULT_REG_RATE_INTERVAL;
+u_int32 pim_data_rate_bytes =
+(PIM_DEFAULT_DATA_RATE * PIM_DEFAULT_DATA_RATE_INTERVAL) / 10;
+u_int32 pim_data_rate_check_interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
+u_int32 pim_hello_period = PIM_TIMER_HELLO_PERIOD;
+u_int32 pim_hello_holdtime = PIM_TIMER_HELLO_HOLDTIME;
+u_int32 pim_join_prune_period = PIM_JOIN_PRUNE_PERIOD;
+u_int32 pim_join_prune_holdtime = PIM_JOIN_PRUNE_HOLDTIME;
+u_int32 pim_data_timeout=PIM_DATA_TIMEOUT;
+u_int32 pim_register_suppression_timeout=PIM_REGISTER_SUPPRESSION_TIMEOUT;
+u_int32 pim_register_probe_time=PIM_REGISTER_PROBE_TIME;
+u_int32 pim_assert_timeout=PIM_ASSERT_TIMEOUT;
+
+
+/*
+ * Local functions definitions.
+ */
+
+
+/*
+ * Local variables
+ */
+u_int16 unicast_routing_timer; /* Used to check periodically for any
+ * change in the unicast routing. */
+u_int16 unicast_routing_check_interval;
+u_int8 ucast_flag; /* Used to indicate there was a timeout */
+
+u_int16 pim_data_rate_timer; /* Used to check periodically the
+ * datarate of the active sources and
+ * eventually switch to the shortest
+ * path (if forwarder) */
+u_int8 pim_data_rate_flag; /* Used to indicate there was a
+ * timeout */
+
+u_int16 pim_reg_rate_timer; /* The same as above, but used by the
+ * RP to switch to the shortest path
+ * and avoid the PIM registers. */
+u_int8 pim_reg_rate_flag;
+u_int8 rate_flag;
+
+/*
+ * TODO: XXX: the timers below are not used. Instead, the data rate timer is
+ * used.
+ */
+u_int16 kernel_cache_timer; /* Used to timeout the kernel cache
+ * entries for idle sources */
+u_int16 kernel_cache_check_interval;
+
+/* to request and compare any route changes */
+
+srcentry_t srcentry_save;
+rpentry_t rpentry_save;
+
+/*
+ * Init some timers
+ */
+void
+init_timers()
+{
+ unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL;
+ SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
+
+ /*
+ * The routing_check and the rate_check timers are interleaved to reduce
+ * the amount of work that has to be done at once.
+ */
+ /* XXX: for simplicity, both the intervals are the same */
+
+ if (pim_data_rate_check_interval < pim_reg_rate_check_interval)
+ pim_reg_rate_check_interval = pim_data_rate_check_interval;
+
+ SET_TIMER(pim_data_rate_timer, 3 * pim_data_rate_check_interval / 2);
+ SET_TIMER(pim_reg_rate_timer, 3 * pim_reg_rate_check_interval / 2);
+
+ /*
+ * Initialize the srcentry and rpentry used to save the old routes during
+ * unicast routing change discovery process.
+ */
+
+ srcentry_save.prev = (srcentry_t *) NULL;
+ srcentry_save.next = (srcentry_t *) NULL;
+ memset(&srcentry_save.address, 0, sizeof(struct sockaddr_in6));
+ srcentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
+ srcentry_save.address.sin6_family= AF_INET6;
+ srcentry_save.mrtlink = (mrtentry_t *) NULL;
+ srcentry_save.incoming = NO_VIF;
+ srcentry_save.upstream = (pim_nbr_entry_t *) NULL;
+ srcentry_save.metric = ~0;
+ srcentry_save.preference = ~0;
+ RESET_TIMER(srcentry_save.timer);
+ srcentry_save.cand_rp = (cand_rp_t *) NULL;
+
+ rpentry_save.prev = (rpentry_t *) NULL;
+ rpentry_save.next = (rpentry_t *) NULL;
+ memset(&rpentry_save.address, 0, sizeof(struct sockaddr_in6));
+ rpentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
+ rpentry_save.address.sin6_family= AF_INET6;
+ rpentry_save.mrtlink = (mrtentry_t *) NULL;
+ rpentry_save.incoming = NO_VIF;
+ rpentry_save.upstream = (pim_nbr_entry_t *) NULL;
+ rpentry_save.metric = ~0;
+ rpentry_save.preference = ~0;
+ RESET_TIMER(rpentry_save.timer);
+ rpentry_save.cand_rp = (cand_rp_t *) NULL;
+
+}
+
+
+/*
+ * On every timer interrupt, advance (i.e. decrease) the timer for each
+ * neighbor and group entry for each vif.
+ */
+void
+age_vifs()
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register pim_nbr_entry_t *next_nbr,
+ *curr_nbr;
+
+ /*
+ * XXX: TODO: currently, sending to qe* interface which is DOWN doesn't
+ * return error (ENETDOWN) on my Solaris machine, so have to check
+ * periodically the interfaces status. If this is fixed, just remove the
+ * defs around the "if (vifs_down)" line.
+ */
+
+#if (!((defined SunOS) && (SunOS >= 50)))
+ if (vifs_down)
+#endif /* Solaris */
+ check_vif_state();
+
+ /* Age many things */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ /* Timeout neighbors */
+
+ for (curr_nbr = v->uv_pim_neighbors; curr_nbr != NULL;
+ curr_nbr = next_nbr)
+ {
+ next_nbr = curr_nbr->next;
+
+ /*
+ * Never timeout neighbors with holdtime = 0xffff. This may be
+ * used with ISDN lines to avoid keeping the link up with
+ * periodic Hello messages.
+ */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer)
+ continue;
+ IF_NOT_TIMEOUT(curr_nbr->timer)
+ continue;
+
+ v->uv_pim6_nbr_timo++;
+ IF_DEBUG(DEBUG_PIM_HELLO)
+ log(LOG_DEBUG, 0,
+ "%s on %s is dead , delete it",
+ inet6_fmt(&curr_nbr->address.sin6_addr),
+ uvifs[curr_nbr->vifi].uv_name);
+ delete_pim6_nbr(curr_nbr);
+ }
+
+ /* PIM_HELLO periodic */
+ IF_TIMEOUT(v->uv_pim_hello_timer)
+ send_pim6_hello(v, pim_hello_holdtime);
+
+ /* MLD6 query periodic */
+ IF_TIMEOUT(v->uv_gq_timer)
+ query_groups(v);
+ }
+
+ IF_DEBUG(DEBUG_IF) {
+ dump_vifs(log_fp);
+ ;
+ }
+}
+
+/*
+ * Scan the whole routing table and timeout a bunch of timers:
+ * - oifs timers
+ * - Join/Prune timer
+ * - routing entry
+ * - Assert timer
+ * - Register-Suppression timer
+ *
+ * - If the global timer for checking the unicast routing has expired, perform
+ * also iif/upstream router change verification
+ * - If the global timer for checking the data rate has expired, check the
+ * number of bytes forwarded after the lastest timeout. If bigger than
+ * a given threshold, then switch to the shortest path.
+ * If `number_of_bytes == 0`, then delete the kernel cache entry.
+ *
+ * Only the entries which have the Join/Prune timer expired are sent.
+ * In the special case when we have ~(S,G)RPbit Prune entry, we must
+ * include any (*,G) or (*,*,RP) XXX: ???? what and why?
+ *
+ * Below is a table which summarizes the segmantic rules.
+ *
+ * On the left side is "if A must be included in the J/P message".
+ * On the top is "shall/must include B?"
+ * "Y" means "MUST include"
+ * "SY" means "SHOULD include"
+ * "N" means "NO NEED to include"
+ * (G is a group that matches to RP)
+ *
+ * -----------||-----------||-----------
+ * || (*,*,RP) || (*,G) || (S,G) ||
+ * ||-----------||-----------||-----------||
+ * || J | P || J | P || J | P ||
+ * ==================================================||
+ * J || n/a | n/a || N | Y || N | Y ||
+ * (*,*,RP) -----------------------------------------||
+ * P || n/a | n/a || SY | N || SY | N ||
+ * ==================================================||
+ * J || N | N || n/a | n/a || N | Y ||
+ * (*,G) -----------------------------------------||
+ * P || N | N || n/a | n/a || SY | N ||
+ * ==================================================||
+ * J || N | N || N | N || n/a | n/a ||
+ * (S,G) -----------------------------------------||
+ * P || N | N || N | N || n/a | n/a ||
+ * ==================================================
+ *
+ */
+
+void
+age_routes()
+{
+ cand_rp_t *cand_rp_ptr;
+ grpentry_t *grpentry_ptr;
+ grpentry_t *grpentry_ptr_next;
+ mrtentry_t *mrtentry_grp;
+ mrtentry_t *mrtentry_rp;
+ mrtentry_t *mrtentry_wide;
+ mrtentry_t *mrtentry_srcs;
+ mrtentry_t *mrtentry_srcs_next;
+ struct uvif *v;
+ vifi_t vifi;
+ pim_nbr_entry_t *pim_nbr_ptr;
+ int change_flag;
+ int rp_action,
+ grp_action,
+ src_action=0,
+ src_action_rp=0;
+ int dont_calc_action;
+ int did_switch_flag;
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ kernel_cache_t *kernel_cache_ptr;
+ kernel_cache_t *kernel_cache_next;
+ u_long curr_bytecnt;
+ rpentry_t *rpentry_ptr;
+ int update_rp_iif;
+ int update_src_iif;
+ if_set new_pruned_oifs;
+
+ /*
+ * Timing out of the global `unicast_routing_timer` and `data_rate_timer`
+ */
+
+ IF_TIMEOUT(unicast_routing_timer)
+ {
+ ucast_flag = TRUE;
+ SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
+ }
+ ELSE
+ {
+ ucast_flag = FALSE;
+ }
+
+ IF_TIMEOUT(pim_data_rate_timer)
+ {
+ pim_data_rate_flag = TRUE;
+ SET_TIMER(pim_data_rate_timer, pim_data_rate_check_interval);
+ }
+ ELSE
+ {
+ pim_data_rate_flag = FALSE;
+ }
+
+ IF_TIMEOUT(pim_reg_rate_timer)
+ {
+ pim_reg_rate_flag = TRUE;
+ SET_TIMER(pim_reg_rate_timer, pim_reg_rate_check_interval);
+ }
+ ELSE
+ {
+ pim_reg_rate_flag = FALSE;
+ }
+
+ rate_flag = pim_data_rate_flag | pim_reg_rate_flag;
+
+ /* Scan the (*,*,RP) entries */
+
+ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
+ cand_rp_ptr = cand_rp_ptr->next)
+ {
+
+ rpentry_ptr = cand_rp_ptr->rpentry;
+
+ /*
+ * Need to save only `incoming` and `upstream` to discover unicast
+ * route changes. `metric` and `preference` are not interesting for
+ * us.
+ */
+
+ rpentry_save.incoming = rpentry_ptr->incoming;
+ rpentry_save.upstream = rpentry_ptr->upstream;
+
+ update_rp_iif = FALSE;
+ if ((ucast_flag == TRUE) &&
+ (!inet6_equal(&rpentry_ptr->address ,&my_cand_rp_address)))
+ {
+ /*
+ * I am not the RP. If I was the RP, then the iif is register_vif
+ * and no need to reset it.
+ */
+
+ if (set_incoming(rpentry_ptr, PIM_IIF_RP) != TRUE)
+ {
+ /*
+ * TODO: XXX: no route to that RP. Panic? There is a high
+ * probability the network is partitioning so immediately
+ * remapping to other RP is not a good idea. Better wait the
+ * Bootstrap mechanism to take care of it and provide me with
+ * correct Cand-RP-Set.
+ */
+ ;
+ }
+ else
+ {
+ if ((rpentry_save.upstream != rpentry_ptr->upstream)
+ || (rpentry_save.incoming != rpentry_ptr->incoming))
+ {
+ /*
+ * Routing change has occur. Update all (*,G) and
+ * (S,G)RPbit iifs mapping to that RP
+ */
+ update_rp_iif = TRUE;
+ }
+ }
+ }
+
+ rp_action = PIM_ACTION_NOTHING;
+ mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
+
+ if (mrtentry_rp != (mrtentry_t *) NULL)
+ {
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_rp->joined_oifs))
+ IF_TIMEOUT(mrtentry_rp->vif_timers[vifi])
+ {
+ uvifs[vifi].uv_outif_timo++;
+ IF_CLR(vifi, &mrtentry_rp->joined_oifs);
+ change_flag = TRUE;
+ }
+ }
+ if ((change_flag == TRUE) || (update_rp_iif == TRUE))
+ {
+ change_interfaces(mrtentry_rp,
+ rpentry_ptr->incoming,
+ &mrtentry_rp->joined_oifs,
+ &mrtentry_rp->pruned_oifs,
+ &mrtentry_rp->leaves,
+ &mrtentry_rp->asserted_oifs, 0);
+ mrtentry_rp->upstream = rpentry_ptr->upstream;
+ }
+
+ if (rate_flag == TRUE)
+ {
+ /* Check the activity for this entry */
+
+ /*
+ * XXX: the spec says to start monitoring first the total
+ * traffic for all senders for particular (*,*,RP) or (*,G)
+ * and if the total traffic exceeds some predefined
+ * threshold, then start monitoring the data traffic for each
+ * particular sender for this group: (*,G) or (*,*,RP).
+ * However, because the kernel cache/traffic info is of the
+ * form (S,G), it is easier if we are simply collecting (S,G)
+ * traffic all the time.
+ *
+ * For (*,*,RP) if the number of bytes received between the last
+ * check and now exceeds some precalculated value (based on
+ * interchecking period and datarate threshold AND if there
+ * are directly connected members (i.e. we are their last
+ * hop(e) router), then create (S,G) and start initiating
+ * (S,G) Join toward the source. The same applies for (*,G).
+ * The spec does not say that if the datarate goes below a
+ * given threshold, then will switch back to the shared tree,
+ * hence after a switch to the source-specific tree occurs, a
+ * source with low datarate, but periodically sending will
+ * keep the (S,G) states.
+ *
+ * If a source with kernel cache entry has been idle after the
+ * last time a check of the datarate for the whole routing
+ * table, then delete its kernel cache entry.
+ */
+ for (kernel_cache_ptr = mrtentry_rp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket, &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for some reason there is no such routing
+ * entry or that particular (s,g) was idle. Delete
+ * the routing entry from the kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ continue;
+ }
+ /*
+ * Check if the datarate was high enough to switch to
+ * source specific tree.
+ */
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_rp->leaves,
+ &mrtentry_rp->oifs) == TRUE)
+ {
+#ifdef KERNEL_MFC_WC_G
+// TODO (one day... :))
+ if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_forward++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+
+ /* RP initiated switch */
+
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_rp->incoming == reg_vif_num)
+
+#ifdef KERNEL_MFC_WC_G
+// TODO (one day :))
+ if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_rp,
+ kernel_cache_ptr);
+ mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_rp++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ }
+ }
+ }
+
+ /* Join/Prune timer */
+ IF_TIMEOUT(mrtentry_rp->jp_timer)
+ {
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join/Prune timer expired");
+
+ rp_action = join_or_prune(mrtentry_rp,
+ mrtentry_rp->upstream);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"rp_action = %d",rp_action);
+
+ if (rp_action != PIM_ACTION_NOTHING)
+ add_jp_entry(mrtentry_rp->upstream,
+ pim_join_prune_holdtime,
+ &sockaddr6_d,
+ STAR_STAR_RP_MSK6LEN,
+ &mrtentry_rp->source->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP | MRTF_WC,
+ rp_action);
+ SET_TIMER(mrtentry_rp->jp_timer, pim_join_prune_period);
+ }
+
+ /* Assert timer */
+ if (mrtentry_rp->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_rp->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_rp->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is running,
+ * install a negative cache entry in the kernel?
+ */
+ /*
+ * TODO: can we have Register-Suppression timer for (*,*,RP)?
+ * Currently no...
+ */
+
+ IF_TIMEOUT(mrtentry_rp->rs_timer)
+ ;
+ /* routing entry */
+
+ if ((TIMEOUT(mrtentry_rp->timer))
+ && (IF_ISEMPTY(&mrtentry_rp->leaves)))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+ delete_mrtentry(mrtentry_rp);
+ }
+ } /* mrtentry_rp != NULL */
+
+ /* Just in case if that (*,*,RP) was deleted */
+
+ mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
+
+ /* Check the (*,G) and (S,G) entries */
+
+ for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
+ {
+
+ for (grpentry_ptr = rp_grp_entry_ptr->grplink;
+ grpentry_ptr != (grpentry_t *) NULL;
+ grpentry_ptr = grpentry_ptr_next)
+ {
+ grpentry_ptr_next = grpentry_ptr->rpnext;
+ mrtentry_grp = grpentry_ptr->grp_route;
+ mrtentry_srcs = grpentry_ptr->mrtlink;
+
+ grp_action = PIM_ACTION_NOTHING;
+ if (mrtentry_grp != (mrtentry_t *) NULL)
+ {
+ /* The (*,G) entry */
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_grp->joined_oifs))
+ IF_TIMEOUT(mrtentry_grp->vif_timers[vifi])
+ {
+ IF_CLR(vifi, &mrtentry_grp->joined_oifs);
+ uvifs[vifi].uv_outif_timo++;
+ change_flag = TRUE;
+ }
+ }
+
+ if ((change_flag == TRUE) || (update_rp_iif == TRUE))
+ {
+ change_interfaces(mrtentry_grp,
+ rpentry_ptr->incoming,
+ &mrtentry_grp->joined_oifs,
+ &mrtentry_grp->pruned_oifs,
+ &mrtentry_grp->leaves,
+ &mrtentry_grp->asserted_oifs, 0);
+ mrtentry_grp->upstream = rpentry_ptr->upstream;
+ }
+
+ /* Check the sources activity */
+
+ if (rate_flag == TRUE)
+ {
+ for (kernel_cache_ptr = mrtentry_grp->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt =
+ kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket,
+ &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for whatever reason there is no
+ * such routing entry or that particular
+ * (s,g) was idle. Delete the routing entry
+ * from the kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_grp,
+ kernel_cache_ptr);
+ continue;
+ }
+
+ /*
+ * Check if the datarate was high enough to
+ * switch to source specific tree.
+ */
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_grp->leaves,
+ &mrtentry_grp->oifs) == TRUE)
+ {
+#ifdef KERNEL_MFC_WC_G
+// TODO
+ if (kernel_cache_ptr->source
+ == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
+ mrtentry_grp->flags
+ |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+
+ pim6dstat.pim6_trans_spt_forward++;
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+
+ /* RP initiated switch */
+
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_grp->incoming == reg_vif_num)
+#ifdef KERNEL_MFC_WC_G
+// TODO
+ if (kernel_cache_ptr->source
+ == IN6ADDR_ANY_N)
+ {
+ delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
+ mrtentry_grp->flags
+ |= MRTF_MFC_CLONE_SG;
+ continue;
+ }
+#endif /* KERNEL_MFC_WC_G */
+ pim6dstat.pim6_trans_spt_rp++;
+ switch_shortest_path(&kernel_cache_ptr->source,
+ &kernel_cache_ptr->group);
+ }
+ }
+ }
+
+ dont_calc_action = FALSE;
+ if (rp_action != PIM_ACTION_NOTHING)
+ {
+ grp_action = join_or_prune(mrtentry_grp,
+ mrtentry_grp->upstream);
+ dont_calc_action = TRUE;
+ if (((rp_action == PIM_ACTION_JOIN)
+ && (grp_action == PIM_ACTION_PRUNE))
+ || ((rp_action == PIM_ACTION_PRUNE)
+ && (grp_action == PIM_ACTION_JOIN)))
+ FIRE_TIMER(mrtentry_grp->jp_timer);
+ }
+
+ /* Join/Prune timer */
+
+ IF_TIMEOUT(mrtentry_grp->jp_timer)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"Join/Prune timer expired");
+
+ if (dont_calc_action != TRUE)
+ grp_action = join_or_prune(mrtentry_grp,
+ mrtentry_grp->upstream);
+
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"grp_action = %d",grp_action);
+
+ if (grp_action != PIM_ACTION_NOTHING)
+ {
+ add_jp_entry(mrtentry_grp->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_grp->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &cand_rp_ptr->rpentry->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP | MRTF_WC,
+ grp_action);
+ }
+
+ SET_TIMER(mrtentry_grp->jp_timer, pim_join_prune_period);
+ }
+
+ /* Assert timer */
+ if (mrtentry_grp->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_grp->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_grp->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is
+ * running, install a negative cache entry in the kernel?
+ */
+ /*
+ * TODO: currently cannot have Register-Suppression timer
+ * for (*,G) entry, but keep this around.
+ */
+ IF_TIMEOUT(mrtentry_grp->rs_timer)
+ ;
+
+ /* routing entry */
+
+ if ((TIMEOUT(mrtentry_grp->timer))
+ && (IF_ISEMPTY(&mrtentry_grp->leaves)))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+ delete_mrtentry(mrtentry_grp);
+ }
+ } /* if (mrtentry_grp != NULL) */
+
+
+ /* For all (S,G) for this group */
+ /* XXX: mrtentry_srcs was set before */
+ for (; mrtentry_srcs != (mrtentry_t *) NULL;
+ mrtentry_srcs = mrtentry_srcs_next)
+ {
+ /* routing entry */
+ mrtentry_srcs_next = mrtentry_srcs->grpnext;
+
+ /* outgoing interfaces timers */
+
+ change_flag = FALSE;
+ for (vifi = 0; vifi < numvifs; vifi++)
+ {
+ if (IF_ISSET(vifi, &mrtentry_srcs->joined_oifs))
+ {
+ /* TODO: checking for reg_num_vif is slow! */
+
+ if (vifi != reg_vif_num)
+ {
+ IF_TIMEOUT(mrtentry_srcs->vif_timers[vifi])
+ {
+ IF_CLR(vifi,
+ &mrtentry_srcs->joined_oifs);
+ change_flag = TRUE;
+ uvifs[vifi].uv_outif_timo++;
+ }
+ }
+ }
+ }
+
+ update_src_iif = FALSE;
+ if (ucast_flag == TRUE)
+ {
+ if (!(mrtentry_srcs->flags & MRTF_RP))
+ {
+ /* iif toward the source */
+ srcentry_save.incoming =
+ mrtentry_srcs->source->incoming;
+ srcentry_save.upstream =
+ mrtentry_srcs->source->upstream;
+ if (set_incoming(mrtentry_srcs->source,
+ PIM_IIF_SOURCE) != TRUE)
+ {
+
+ /*
+ * XXX: not in the spec! Cannot find route
+ * toward that source. This is bad. Delete
+ * the entry.
+ */
+
+ delete_mrtentry(mrtentry_srcs);
+ continue;
+ }
+ else
+ {
+
+ /* iif info found */
+
+ if ((srcentry_save.incoming !=
+ mrtentry_srcs->incoming)
+ || (srcentry_save.upstream !=
+ mrtentry_srcs->upstream))
+ {
+ /* Route change has occur */
+ update_src_iif = TRUE;
+ mrtentry_srcs->incoming =
+ mrtentry_srcs->source->incoming;
+ mrtentry_srcs->upstream =
+ mrtentry_srcs->source->upstream;
+ }
+ }
+ }
+ else
+ {
+ /* (S,G)RPBit with iif toward RP */
+ if ((rpentry_save.upstream !=
+ mrtentry_srcs->upstream)
+ || (rpentry_save.incoming !=
+ mrtentry_srcs->incoming))
+ {
+ update_src_iif = TRUE; /* XXX: a hack */
+ /* XXX: setup the iif now! */
+ mrtentry_srcs->incoming =
+ rpentry_ptr->incoming;
+ mrtentry_srcs->upstream =
+ rpentry_ptr->upstream;
+ }
+ }
+ }
+
+ if ((change_flag == TRUE) || (update_src_iif == TRUE))
+ /* Flush the changes */
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &mrtentry_srcs->pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+
+ if (rate_flag == TRUE)
+ {
+ for (kernel_cache_ptr = mrtentry_srcs->kernel_cache;
+ kernel_cache_ptr != (kernel_cache_t *) NULL;
+ kernel_cache_ptr = kernel_cache_next)
+ {
+ kernel_cache_next = kernel_cache_ptr->next;
+ curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
+ if (k_get_sg_cnt(udp_socket,
+ &kernel_cache_ptr->source,
+ &kernel_cache_ptr->group,
+ &kernel_cache_ptr->sg_count)
+ || (curr_bytecnt ==
+ kernel_cache_ptr->sg_count.bytecnt))
+ {
+ /*
+ * Either for some reason there is no such
+ * routing entry or that particular (s,g) was
+ * idle. Delete the routing entry from the
+ * kernel.
+ */
+
+ delete_single_kernel_cache(mrtentry_srcs,
+ kernel_cache_ptr);
+ continue;
+ }
+ /*
+ * Check if the datarate was high enough to
+ * switch to source specific tree. Need to check
+ * only when we have (S,G)RPbit in the forwarder
+ * or the RP itself.
+ */
+
+ if (!(mrtentry_srcs->flags & MRTF_RP))
+ continue;
+
+ /* Forwarder initiated switch */
+
+ did_switch_flag = FALSE;
+ if (curr_bytecnt + pim_data_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt)
+ {
+ if (vif_forwarder(&mrtentry_srcs->leaves,
+ &mrtentry_srcs->oifs)
+ == TRUE)
+ {
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ did_switch_flag = TRUE;
+ }
+ }
+ /* RP initiated switch */
+ if ((did_switch_flag == FALSE)
+ && (curr_bytecnt + pim_reg_rate_bytes
+ < kernel_cache_ptr->sg_count.bytecnt))
+ {
+ if (mrtentry_srcs->incoming == reg_vif_num)
+ switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
+ }
+
+ /*
+ * XXX: currentry the spec doesn't say to switch
+ * back to the shared tree if low datarate, but
+ * if needed to implement, the check must be done
+ * here. Don't forget to check whether I am a
+ * forwarder for that source.
+ */
+ }
+ }
+
+ mrtentry_wide = mrtentry_srcs->group->grp_route;
+ if (mrtentry_wide == (mrtentry_t *) NULL)
+ mrtentry_wide = mrtentry_rp;
+
+ dont_calc_action = FALSE;
+ if ((rp_action != PIM_ACTION_NOTHING)
+ || (grp_action != PIM_ACTION_NOTHING))
+ {
+ src_action_rp = join_or_prune(mrtentry_srcs,
+ rpentry_ptr->upstream);
+ src_action = src_action_rp;
+ dont_calc_action = TRUE;
+ if (src_action_rp == PIM_ACTION_JOIN)
+ {
+ if ((grp_action == PIM_ACTION_PRUNE)
+ || (rp_action == PIM_ACTION_PRUNE))
+ FIRE_TIMER(mrtentry_srcs->jp_timer);
+ }
+ else
+ if (src_action_rp == PIM_ACTION_PRUNE)
+ {
+ if ((grp_action == PIM_ACTION_JOIN)
+ || (rp_action == PIM_ACTION_JOIN))
+ FIRE_TIMER(mrtentry_srcs->jp_timer);
+ }
+ }
+
+ /* Join/Prune timer */
+
+ IF_TIMEOUT(mrtentry_srcs->jp_timer)
+ {
+ if ((dont_calc_action != TRUE)
+ || (rpentry_ptr->upstream
+ != mrtentry_srcs->upstream))
+ src_action = join_or_prune(mrtentry_srcs,
+ mrtentry_srcs->upstream);
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"src_action = %d",src_action);
+
+ if (src_action != PIM_ACTION_NOTHING)
+ add_jp_entry(mrtentry_srcs->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_srcs->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &mrtentry_srcs->source->address,
+ SINGLE_SRC_MSK6LEN,
+ mrtentry_srcs->flags & MRTF_RP,
+ src_action);
+ if (mrtentry_wide != (mrtentry_t *) NULL)
+ {
+ /*
+ * Have both (S,G) and (*,G) (or (*,*,RP)). Check
+ * if need to send (S,G) PRUNE toward RP
+ */
+
+ if (mrtentry_srcs->upstream
+ != mrtentry_wide->upstream)
+ {
+ if (dont_calc_action != TRUE)
+ src_action_rp =
+ join_or_prune(mrtentry_srcs,
+ mrtentry_wide->upstream);
+ /*
+ * XXX: TODO: do error check if src_action ==
+ * PIM_ACTION_JOIN, which should be an error.
+ */
+
+ if (src_action_rp == PIM_ACTION_PRUNE)
+ {
+ IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
+ log(LOG_DEBUG,0,"src_action = %d",src_action);
+ add_jp_entry(mrtentry_wide->upstream,
+ pim_join_prune_holdtime,
+ &mrtentry_srcs->group->group,
+ SINGLE_GRP_MSK6LEN,
+ &mrtentry_srcs->source->address,
+ SINGLE_SRC_MSK6LEN,
+ MRTF_RP,
+ src_action_rp);
+ }
+ }
+ }
+ SET_TIMER(mrtentry_srcs->jp_timer, pim_join_prune_period);
+ }
+ /* Assert timer */
+ if (mrtentry_srcs->flags & MRTF_ASSERTED)
+ {
+ IF_TIMEOUT(mrtentry_srcs->assert_timer)
+ {
+ /* TODO: XXX: reset the upstream router now */
+ mrtentry_srcs->flags &= ~MRTF_ASSERTED;
+ }
+ }
+ /* Register-Suppression timer */
+ /*
+ * TODO: to reduce the kernel calls, if the timer is
+ * running, install a negative cache entry in the kernel?
+ */
+
+ IF_TIMER_SET(mrtentry_srcs->rs_timer)
+ {
+ IF_TIMEOUT(mrtentry_srcs->rs_timer)
+ {
+ /* Start encapsulating the packets */
+ IF_COPY(&mrtentry_srcs->pruned_oifs,
+ &new_pruned_oifs);
+ IF_CLR(reg_vif_num, &new_pruned_oifs);
+ change_interfaces(mrtentry_srcs,
+ mrtentry_srcs->incoming,
+ &mrtentry_srcs->joined_oifs,
+ &new_pruned_oifs,
+ &mrtentry_srcs->leaves,
+ &mrtentry_srcs->asserted_oifs, 0);
+ }
+ ELSE
+ {
+ /*
+ * The register suppression timer is running.
+ * Check whether it is time to send
+ * PIM_NULL_REGISTER.
+ */
+ /* TODO: XXX: TIMER implem. dependency! */
+
+ if (mrtentry_srcs->rs_timer
+ <= pim_register_probe_time)
+ {
+ /* Time to send a PIM_NULL_REGISTER */
+ /*
+ * XXX: a (bad) hack! This will be sending
+ * periodically NULL_REGISTERS between
+ * pim_register_probe_time and 0. Well,
+ * because PROBE_TIME is 5 secs , it will
+ * happen only once ( if granularity is 5 and prob 5!)
+ * , so it helps to avoid
+ * adding a flag to the routing entry whether
+ * a NULL_REGISTER was sent.
+ */
+ send_pim6_null_register(mrtentry_srcs);
+ }
+ }
+ }
+
+ /* routing entry */
+ if (TIMEOUT(mrtentry_srcs->timer))
+ {
+ pim6dstat.pim6_rtentry_timo++;
+
+ if (IF_ISEMPTY(&mrtentry_srcs->leaves))
+ {
+ delete_mrtentry(mrtentry_srcs);
+ continue;
+ }
+ /*
+ * XXX: if DR, Register suppressed, and leaf oif
+ * inherited from (*,G), the directly connected
+ * source is not active anymore, this (S,G) entry
+ * won't timeout. Check if the leaf oifs are
+ * inherited from (*,G); if true. delete the (S,G)
+ * entry.
+ */
+
+ if (mrtentry_srcs->group->grp_route
+ != (mrtentry_t *) NULL)
+ {
+ if_set r_and, r_xor;
+ vif_and(&mrtentry_srcs->group->grp_route->leaves,
+ &mrtentry_srcs->leaves,
+ &r_and);
+ vif_xor(&r_and ,&mrtentry_srcs->leaves,
+ &r_xor);
+ if (IF_ISEMPTY(&r_xor))
+ {
+ delete_mrtentry(mrtentry_srcs);
+ continue;
+ }
+ }
+ }
+ } /* End of (S,G) loop */
+ } /* End of (*,G) loop */
+ }
+ } /* For all cand RPs */
+
+ /* TODO: check again! */
+ for (vifi = 0, v = &uvifs[0]; vifi < numvifs; vifi++, v++)
+ {
+ /* Send all pending Join/Prune messages */
+ for (pim_nbr_ptr = v->uv_pim_neighbors;
+ pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
+ pim_nbr_ptr = pim_nbr_ptr->next)
+ {
+ pack_and_send_jp6_message(pim_nbr_ptr);
+ }
+ }
+
+ IF_DEBUG(DEBUG_PIM_MRT)
+ dump_pim_mrt(log_fp);
+ return;
+}
+
+
+/*
+ * TODO: timeout the RP-group mapping entries during the scan of the whole
+ * routing table?
+ */
+void
+age_misc()
+{
+ rp_grp_entry_t *rp_grp_entry_ptr;
+ rp_grp_entry_t *rp_grp_entry_next;
+ grp_mask_t *grp_mask_ptr;
+ grp_mask_t *grp_mask_next;
+
+ /* Timeout the Cand-RP-set entries */
+ for (grp_mask_ptr = grp_mask_list;
+ grp_mask_ptr != (grp_mask_t *) NULL;
+ grp_mask_ptr = grp_mask_next)
+ {
+ /*
+ * If we timeout an entry, the grp_mask_ptr entry might be removed.
+ */
+ grp_mask_next = grp_mask_ptr->next;
+ for (rp_grp_entry_ptr = grp_mask_ptr->grp_rp_next;
+ rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
+ rp_grp_entry_ptr = rp_grp_entry_next)
+ {
+ rp_grp_entry_next = rp_grp_entry_ptr->grp_rp_next;
+ IF_TIMEOUT(rp_grp_entry_ptr->holdtime) {
+ delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
+ rp_grp_entry_ptr);
+ pim6dstat.pim6_rpgrp_timo++;
+ }
+ }
+ }
+
+ /* Cand-RP-Adv timer */
+ if (cand_rp_flag == TRUE)
+ {
+ IF_TIMEOUT(pim_cand_rp_adv_timer)
+ {
+ send_pim6_cand_rp_adv();
+ SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
+ }
+ }
+
+ /* bootstrap-timer */
+
+ IF_TIMEOUT(pim_bootstrap_timer)
+ {
+ pim6dstat.pim6_bootstrap_timo++;
+
+ if (cand_bsr_flag == FALSE)
+ {
+ /*
+ * If I am not Cand-BSR, start accepting Bootstrap messages from
+ * anyone. XXX: Even if the BSR has timeout, the existing
+ * Cand-RP-Set is kept.
+ */
+ curr_bsr_fragment_tag = 0;
+ curr_bsr_priority = 0; /* Lowest priority */
+ memset(&curr_bsr_address, 0, sizeof(struct sockaddr_in6));
+ curr_bsr_address.sin6_len = sizeof(struct sockaddr_in6);
+ curr_bsr_address.sin6_family = AF_INET6;
+ MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, curr_bsr_hash_mask);
+ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
+ }
+ else
+ {
+ /* I am Cand-BSR, so set the current BSR to me */
+ if (inet6_equal(&curr_bsr_address, &my_bsr_address))
+ {
+ SET_TIMER(pim_bootstrap_timer, my_bsr_period);
+ send_pim6_bootstrap();
+ }
+ else
+ {
+ /*
+ * Short delay before becoming the BSR and start sending of
+ * the Cand-RP set (to reduce the transient control
+ * overhead).
+ */
+ SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
+ curr_bsr_fragment_tag = RANDOM();
+ curr_bsr_priority = my_bsr_priority;
+ curr_bsr_address = my_bsr_address;
+ memcpy(&curr_bsr_hash_mask , &my_bsr_hash_mask , sizeof(struct in6_addr));
+ }
+ }
+ }
+
+
+ IF_DEBUG(DEBUG_PIM_BOOTSTRAP | DEBUG_PIM_CAND_RP)
+ dump_rp_set(log_fp);
+ /* TODO: XXX: anything else to timeout */
+}
diff --git a/usr.sbin/pim6sd/timer.h b/usr.sbin/pim6sd/timer.h
new file mode 100644
index 0000000..132ff5a
--- /dev/null
+++ b/usr.sbin/pim6sd/timer.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 1999 LSIIT Laboratory.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+
+
+#ifndef TIMER_H
+#define TIMER_H
+
+/* the default granularity if not specified in the config file */
+
+#define DEFAULT_TIMER_INTERVAL 5
+
+/* For timeout. The timers count down */
+
+#define SET_TIMER(timer, value) (timer) = (value)
+#define RESET_TIMER(timer) (timer) = 0
+#define COPY_TIMER(timer_1, timer_2) (timer_2) = (timer_1)
+#define IF_TIMER_SET(timer) if ((timer) > 0)
+#define IF_TIMER_NOT_SET(timer) if ((timer) <= 0)
+#define FIRE_TIMER(timer) (timer) = 0
+
+
+#define IF_TIMER_NOT_SET(timer) if ((timer) <= 0)
+
+#define IF_TIMEOUT(timer) \
+ if (!((timer) -= (MIN(timer, timer_interval))))
+
+#define IF_NOT_TIMEOUT(timer) \
+ if ((timer) -= (MIN(timer, timer_interval)))
+
+#define TIMEOUT(timer) \
+ (!((timer) -= (MIN(timer, timer_interval))))
+
+#define NOT_TIMEOUT(timer) \
+ ((timer) -= (MIN(timer, timer_interval)))
+
+
+extern u_int32 pim_reg_rate_bytes;
+extern u_int32 pim_reg_rate_check_interval;
+extern u_int32 pim_data_rate_bytes;
+extern u_int32 pim_data_rate_check_interval;
+extern u_int32 pim_hello_period;
+extern u_int32 pim_hello_holdtime;
+extern u_int32 timer_interval;
+extern u_int32 pim_join_prune_period;
+extern u_int32 pim_join_prune_holdtime;
+extern u_int32 pim_data_timeout;
+extern u_int32 pim_register_suppression_timeout;
+extern u_int32 pim_register_probe_time;
+extern u_int32 pim_assert_timeout;
+
+extern void init_timers __P(());
+extern void init_timers __P(());
+extern void age_vifs __P(());
+extern void age_routes __P(());
+extern void age_misc __P(());
+
+
+#endif
diff --git a/usr.sbin/pim6sd/trace.c b/usr.sbin/pim6sd/trace.c
new file mode 100644
index 0000000..7c54628
--- /dev/null
+++ b/usr.sbin/pim6sd/trace.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * non-commercial purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.c,v 1.7 1999/09/16 08:45:45 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "vif.h"
+#include "inet6.h"
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/in6_var.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "defs.h"
+#include "mld6.h"
+#include "kern.h"
+#include "debug.h"
+#include "mld6_proto.h"
+#include "route.h"
+#include "rp.h"
+#include "trace.h"
+
+/* TODO */
+/*
+ * Traceroute function which returns traceroute replies to the requesting
+ * router. Also forwards the request to downstream routers.
+ */
+void
+accept_mtrace(src, dst, group, ifindex, data, no, datalen)
+ struct sockaddr_in6 *src;
+ struct in6_addr *dst;
+ struct in6_addr *group;
+ int ifindex;
+ char *data;
+ u_int no; /* promoted u_char */
+ int datalen;
+{
+ u_char type;
+ mrtentry_t *mrt;
+ struct tr6_query *qry;
+ struct tr6_resp *resp;
+ int vifi, ovifi;
+ char *p;
+ int rcount;
+ int errcode = TR_NO_ERR;
+ int resptype;
+ struct timeval tp;
+ struct sioc_mif_req6 mreq;
+ struct in6_addr parent_address;
+ struct sockaddr_in6 src_sa6 = {sizeof(src_sa6), AF_INET6};
+ struct sockaddr_in6 dst_sa6 = {sizeof(dst_sa6), AF_INET6};
+ struct sockaddr_in6 resp_sa6 = {sizeof(resp_sa6), AF_INET6};
+ struct sockaddr_in6 grp_sa6 = {sizeof(grp_sa6), AF_INET6};
+ struct sockaddr_in6 *sa_global;
+ rpentry_t *rpentry_ptr;
+
+ /* Remember qid across invocations */
+ static u_int32 oqid = 0;
+
+ /* timestamp the request/response */
+ gettimeofday(&tp, 0);
+
+ /*
+ * Check if it is a query or a response
+ */
+ if (datalen == QLEN) {
+ type = QUERY;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Initial traceroute query rcvd "
+ "from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ }
+ else if ((datalen - QLEN) % RLEN == 0) {
+ type = RESP;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "In-transit traceroute query rcvd "
+ "from %s to %s",
+ inet6_fmt(&src->sin6_addr),
+ inet6_fmt(dst));
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Dropping multicast response");
+ return;
+ }
+ }
+ else {
+ log(LOG_WARNING, 0, "%s from %s to %s",
+ "Non decipherable traceroute request recieved",
+ inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
+ return;
+ }
+
+ qry = (struct tr6_query *)data;
+ src_sa6.sin6_addr = qry->tr_src;
+ src_sa6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&qry->tr_src)
+ || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_src)) ? ifindex : 0;
+ dst_sa6.sin6_addr = qry->tr_dst;
+ dst_sa6.sin6_scope_id =
+ (IN6_IS_ADDR_LINKLOCAL(&qry->tr_dst)
+ || IN6_IS_ADDR_MC_LINKLOCAL(&qry->tr_dst)) ? ifindex : 0;
+ grp_sa6.sin6_addr = *group;
+ grp_sa6.sin6_scope_id = 0;
+
+ /*
+ * if it is a packet with all reports filled, drop it
+ */
+ if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_TRACE) {
+ log(LOG_DEBUG, 0, "s: %s g: %s d: %s ",
+ inet6_fmt(&qry->tr_src),
+ inet6_fmt(group), inet6_fmt(&qry->tr_dst));
+ log(LOG_DEBUG, 0, "rhlim: %d rd: %s", qry->tr_rhlim,
+ inet6_fmt(&qry->tr_raddr));
+ log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
+ }
+
+ /* determine the routing table entry for this traceroute */
+ mrt = find_route(&src_sa6, &grp_sa6, MRTF_SG | MRTF_WC | MRTF_PMBR,
+ DONT_CREATE);
+ IF_DEBUG(DEBUG_TRACE) {
+ if (mrt != (mrtentry_t *)NULL) {
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address = mrt->upstream->address.sin6_addr;
+ else
+ parent_address = in6addr_any;
+ log(LOG_DEBUG, 0,
+ "mrt parent mif: %d rtr: %s metric: %d",
+ mrt->incoming,
+ inet6_fmt(&parent_address), mrt->metric);
+ /* TODO
+ log(LOG_DEBUG, 0, "mrt origin %s",
+ RT_FMT(rt, s1));
+ */
+ } else
+ log(LOG_DEBUG, 0, "...no route");
+ }
+
+ /*
+ * Query type packet - check if rte exists
+ * Check if the query destination is a vif connected to me.
+ * and if so, whether I should start response back
+ */
+ if (type == QUERY) {
+ if (oqid == qry->tr_qid) {
+ /*
+ * If the multicast router is a member of the group
+ * being queried, and the query is multicasted,
+ * then the router can recieve multiple copies of
+ * the same query. If we have already replied to
+ * this traceroute, just ignore it this time.
+ *
+ * This is not a total solution, but since if this
+ * fails you only get N copies, N <= the number of
+ * interfaces on the router, it is not fatal.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "ignoring duplicate traceroute packet");
+ return;
+ }
+
+ if (mrt == (mrtentry_t *)NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Mcast traceroute: no route entry %s",
+ inet6_fmt(&qry->tr_src));
+#if 0
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+#endif
+ }
+ vifi = find_vif_direct(&dst_sa6);
+
+ if (vifi == NO_VIF) {
+ /*
+ * The traceroute destination is not on one of
+ * my subnet vifs.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not an interface",
+ inet6_fmt(&qry->tr_dst));
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+ errcode = TR_WRONG_IF;
+ } else if (mrt != (mrtentry_t *)NULL &&
+ !IF_ISSET(vifi, &mrt->oifs)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not on forwarding tree "
+ "for src %s",
+ inet6_fmt(&qry->tr_dst),
+ inet6_fmt(&qry->tr_src));
+ if (IN6_IS_ADDR_MULTICAST(dst))
+ return;
+ errcode = TR_WRONG_IF;
+ }
+ }
+ else {
+ /*
+ * determine which interface the packet came in on
+ * RESP packets travel hop-by-hop so this either traversed
+ * a tunnel or came from a directly attached mrouter.
+ */
+ if ((vifi = find_vif_direct(src)) == NO_VIF) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Wrong interface for packet");
+ errcode = TR_WRONG_IF;
+ }
+ }
+
+ /* Now that we've decided to send a response, save the qid */
+ oqid = qry->tr_qid;
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending traceroute response");
+
+ /* copy the packet to the sending buffer */
+ p = mld6_send_buf + sizeof(struct mld6_hdr);
+
+ bcopy(data, p, datalen);
+
+ p += datalen;
+
+ /*
+ * If there is no room to insert our reply, coopt the previous hop
+ * error indication to relay this fact.
+ */
+ if (p + sizeof(struct tr6_resp) > mld6_send_buf + RECV_BUF_SIZE) {
+ resp = (struct tr6_resp *)p - 1;
+ resp->tr_rflags = TR_NO_SPACE;
+ mrt = NULL;
+ goto sendit;
+ }
+
+ /*
+ * fill in initial response fields
+ */
+ resp = (struct tr6_resp *)p;
+ bzero(resp, sizeof(struct tr6_resp));
+ datalen += (RLEN + sizeof(struct mld6_hdr));
+
+ resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec << 10) / 15625));
+
+ resp->tr_rproto = PROTO_PIM;
+ resp->tr_outifid = (vifi == NO_VIF) ? TR_NO_VIF : htonl(vifi);
+ resp->tr_rflags = errcode;
+ if ((sa_global = max_global_address()) == NULL) /* impossible */
+ log(LOG_ERR, 0, "acept_mtrace: max_global_address returns NULL");
+ resp->tr_lcladdr = sa_global->sin6_addr;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ mreq.mifi = vifi;
+ if (vifi != NO_VIF &&
+ ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
+ resp->tr_vifout = htonl(mreq.ocount);
+ else
+ resp->tr_vifout = 0xffffffff;
+
+ /*
+ * fill in scoping & pruning information
+ */
+ /* TODO */
+#if 0
+ if (mrt != (mrtentry_t *)NULL)
+ for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
+ if (gt->gt_mcastgrp >= group)
+ break;
+ }
+ else
+ gt = NULL;
+
+ if (gt && gt->gt_mcastgrp == group) {
+ struct stable *st;
+
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (qry->tr_src == st->st_origin)
+ break;
+
+ sg_req.src.s_addr = qry->tr_src;
+ sg_req.grp.s_addr = group;
+ if (st && st->st_ctime != 0 &&
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
+ else
+ resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
+
+ if (VIFM_ISSET(vifi, gt->gt_scope))
+ resp->tr_rflags = TR_SCOPED;
+ else if (gt->gt_prsent_timer)
+ resp->tr_rflags = TR_PRUNED;
+ else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
+ if (VIFM_ISSET(vifi, rt->rt_children) &&
+ NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap,
+ rt->rt_subordinates)) /*XXX*/
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if (scoped_addr(vifi, group))
+ resp->tr_rflags = TR_SCOPED;
+ else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
+ resp->tr_rflags = TR_NO_FWD;
+ }
+#endif /* 0 */
+
+ /*
+ * if no rte exists, set NO_RTE error
+ */
+ if (mrt == (mrtentry_t *)NULL) {
+ src->sin6_addr = *dst; /* the dst address of resp. pkt */
+ resp->tr_inifid = TR_NO_VIF;
+ resp->tr_rflags = TR_NO_RTE;
+ memset(&resp->tr_rmtaddr, 0, sizeof(struct in6_addr));
+ } else {
+ /* get # of packets in on interface */
+ mreq.mifi = mrt->incoming;
+ if (ioctl(udp_socket, SIOCGETMIFCNT_IN6, (char *)&mreq) >= 0)
+ resp->tr_vifin = htonl(mreq.icount);
+ else
+ resp->tr_vifin = 0xffffffff;
+
+ /*
+ * TODO
+ * MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
+ */
+ resp->tr_inifid = htonl(mrt->incoming);
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address = mrt->upstream->address.sin6_addr;
+ else
+ parent_address = in6addr_any;
+
+ resp->tr_rmtaddr = parent_address;
+ if (!IF_ISSET(vifi, &mrt->oifs)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Destination %s not on forwarding tree "
+ "for src %s",
+ inet6_fmt(&qry->tr_dst),
+ inet6_fmt(&qry->tr_src));
+ resp->tr_rflags = TR_WRONG_IF;
+ }
+#if 0
+ if (rt->rt_metric >= UNREACHABLE) {
+ resp->tr_rflags = TR_NO_RTE;
+ /* Hack to send reply directly */
+ rt = NULL;
+ }
+#endif /* 0 */
+ }
+
+ /*
+ * If we're the RP for the trace group, note it.
+ */
+ rpentry_ptr = rp_match(&grp_sa6);
+ if (rpentry_ptr && local_address(&rpentry_ptr->address) != NO_VIF)
+ resp->tr_rflags = TR_RP;
+
+ sendit:
+ /*
+ * if metric is 1 or no. of reports is 1, send response to requestor
+ * else send to upstream router. If the upstream router can't handle
+ * mtrace, set an error code and send to requestor anyway.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
+
+ ovifi = NO_VIF; /* unspecified */
+ if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) {
+ resptype = MLD6_MTRACE_RESP;
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ if ((ovifi = find_vif_direct(&dst_sa6)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "can't determine outgoing i/f for mtrace "
+ "response.");
+ return;
+ }
+ }
+ } else
+ /* TODO */
+ {
+#if 0
+ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ resp->tr_rflags = TR_OLD_ROUTER;
+ resptype = MLD6_MTRACE_RESP;
+ } else
+#endif /* 0 */
+ if (mrt->incoming &&
+ (uvifs[mrt->incoming].uv_flags & MIFF_REGISTER)) {
+ log(LOG_DEBUG, 0,
+ "incoming i/f is for register. "
+ "Can't be forwarded anymore.");
+ resp_sa6.sin6_addr = qry->tr_raddr;
+ resptype = MLD6_MTRACE_RESP;
+ } else {
+ if (mrt->upstream != (pim_nbr_entry_t *)NULL)
+ parent_address =
+ mrt->upstream->address.sin6_addr;
+ else
+ parent_address = allrouters_group.sin6_addr;
+ resp_sa6.sin6_addr = parent_address;
+ ovifi = mrt->incoming;
+ resptype = MLD6_MTRACE;
+ }
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&resp_sa6.sin6_addr)) {
+ struct sockaddr_in6 *sa6;
+
+ /*
+ * Send the reply on a known multicast capable vif.
+ * If we don't have one, we can't source any
+ * multicasts anyway.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
+ ifindex = uvifs[ovifi].uv_ifindex;
+ }
+ else {
+ if (phys_vif != -1 &&
+ (sa6 = uv_global(phys_vif)) != NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0,
+ "Sending reply to %s from %s",
+ inet6_fmt(dst),
+ inet6_fmt(&sa6->sin6_addr));
+ ifindex = uvifs[phys_vif].uv_ifindex;
+ }
+ else {
+ log(LOG_INFO, 0, "No enabled phyints -- %s",
+ "dropping traceroute reply");
+ return;
+ }
+ }
+ k_set_hlim(mld6_socket, qry->tr_rhlim);
+ send_mld6(resptype, no, sa6, &resp_sa6, group,
+ ifindex, 0, datalen, 0);
+ k_set_hlim(mld6_socket, 1);
+ } else {
+ struct sockaddr_in6 *sa6 = NULL;
+ ifindex = -1; /* unspecified by default */
+
+ if (IN6_IS_ADDR_LINKLOCAL(&resp_sa6.sin6_addr)) {
+ /* ovifi must be valid in this case */
+ ifindex = uvifs[ovifi].uv_ifindex;
+ sa6 = &uvifs[ovifi].uv_linklocal->pa_addr;
+ }
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending %s to %s from %s",
+ resptype == MLD6_MTRACE_RESP ?
+ "reply" : "request on",
+ inet6_fmt(dst),
+ sa6 ? inet6_fmt(&sa6->sin6_addr) : "unspecified");
+
+ send_mld6(resptype, no, sa6, &resp_sa6, group, ifindex,
+ 0, datalen, 0);
+ }
+ return;
+}
diff --git a/usr.sbin/pim6sd/trace.h b/usr.sbin/pim6sd/trace.h
new file mode 100644
index 0000000..465acc6
--- /dev/null
+++ b/usr.sbin/pim6sd/trace.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: trace.h,v 1.2 1999/09/09 15:47:11 jinmei Exp $
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr6_query {
+ struct in6_addr tr_src; /* traceroute source */
+ struct in6_addr tr_dst; /* traceroute destination */
+ struct in6_addr tr_raddr; /* traceroute response address */
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ struct {
+ u_int32_t qid : 24; /* traceroute query id */
+ u_int32_t rhlim : 8; /* traceroute response ttl */
+ } q;
+#else
+ struct {
+ u_int32_t rhlim : 8; /* traceroute response ttl */
+ u_int32_t qid : 24; /* traceroute query id */
+ } q;
+#endif /* BYTE_ORDER */
+};
+
+#define tr_rhlim q.rhlim
+#define tr_qid q.qid
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr6_resp {
+ u_int32_t tr_qarr; /* query arrival time */
+#if 0
+ struct in6_addr tr_inaddr; /* incoming interface address */
+ struct in6_addr tr_outaddr; /* outgoing interface address */
+#endif
+ u_int32_t tr_inifid; /* incoming interface identifier */
+ u_int32_t tr_outifid; /* outgoing interface identifier */
+ struct in6_addr tr_lcladdr; /* router's address(must have largest scope) */
+ struct in6_addr tr_rmtaddr; /* parent address in source tree */
+ u_int32_t tr_vifin; /* input packet count on interface */
+ u_int32_t tr_vifout; /* output packet count on interface */
+ u_int32_t tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+#if 0
+ u_char tr_fhlim; /* hop limit required to forward on outvif */
+#endif
+ u_char tr_flags; /* flags */
+ u_char tr_plen; /* prefix length for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QUERY 1
+#define RESP 2
+#define QLEN sizeof(struct tr6_query)
+#define RLEN sizeof(struct tr6_resp)
+
+/* fields for tr_inifid and tr_outifid */
+#define TR_NO_VIF 0xffffffff/* interface can't be determined */
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0 /* No error */
+#define TR_WRONG_IF 1 /* traceroute arrived on non-oif */
+#define TR_PRUNED 2 /* router has sent a prune upstream */
+#define TR_OPRUNED 3 /* stop forw. after request from next hop rtr*/
+#define TR_SCOPED 4 /* group adm. scoped at this hop */
+#define TR_NO_RTE 5 /* no route for the source */
+#define TR_NO_LHR 6 /* not the last-hop router */
+#define TR_NO_FWD 7 /* not forwarding for this (S,G). Reason = ? */
+#define TR_RP 8 /* I am the RP/Core */
+#define TR_IIF 9 /* request arrived on the iif */
+#define TR_NO_MULTI 0x0a /* multicast disabled on that interface */
+#define TR_NO_SPACE 0x81 /* no space to insert responce data block */
+#define TR_OLD_ROUTER 0x82 /* previous hop does not support traceroute */
+#define TR_ADMIN_PROHIB 0x83 /* traceroute adm. prohibited */
+
+/* fields for tr_flags */
+#define TR_SUBNET_COUNT 0x80 /* pkt count for (S,G) is for source network */
+
+/* fields for r_plen */
+#define TR_GROUP_ONLY 0xff /* forwarding solely on group state */
+
+/* fields for packets count */
+#define TR_CANT_COUNT 0xffffffff /* no count can be reported */
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+#define PROTO_PIM_SPECIAL 5
+#define PROTO_PIM_STATIC 6
+#define PROTO_DVMRP_STATIC 7
+
+#define MASK_TO_VAL(x, i) { \
+ u_int32_t _x = ntohl(x); \
+ (i) = 1; \
+ while ((_x) <<= 1) \
+ (i)++; \
+ };
+
+#define VAL_TO_MASK(x, i) { \
+ x = htonl(~((1 << (32 - (i))) - 1)); \
+ };
+
+#define MASKLEN_TO_MASK6(masklen, mask6) \
+ do {\
+ u_char maskarray[8] = \
+ {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; \
+ int bytelen, bitlen, i; \
+ memset(&(mask6), 0, sizeof(mask6));\
+ bytelen = (masklen) / 8;\
+ bitlen = (masklen) % 8;\
+ for (i = 0; i < bytelen; i++) \
+ (mask6).s6_addr[i] = 0xff;\
+ if (bitlen) \
+ (mask6).s6_addr[bytelen] = maskarray[bitlen - 1]; \
+ }while(0);
+
+/* obnoxious gcc gives an extraneous warning about this constant... */
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
+
+#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv)
+
+void accept_mtrace __P((struct sockaddr_in6 *, struct in6_addr *,
+ struct in6_addr *, int, char *, u_int, int));
+#endif /* TRACE_H */
diff --git a/usr.sbin/pim6sd/var.h b/usr.sbin/pim6sd/var.h
new file mode 100644
index 0000000..999ba44
--- /dev/null
+++ b/usr.sbin/pim6sd/var.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/* YIPS @(#)$Id: var.h,v 1.1 1999/10/29 09:04:54 jinmei Exp $ */
+
+#if !defined(_VAR_H_)
+#define _VAR_H_
+
+#include <sys/socket.h>
+
+#define MAX3(a,b,c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
+
+#define CALLOC(size, cast) (cast)calloc(1, (size))
+
+#define ISSET(exp, bit) (((exp) & (bit)) == (bit))
+
+#define ATOX(c) \
+ (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
+
+#define LALIGN(a) \
+ ((a) > 0 ? ((a) &~ (sizeof(long) - 1)) : sizeof(long))
+
+#define RNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#define BUFADDRSIZE 128
+#define INET_NTOP(addr, buf) \
+ inet_ntop(((struct sockaddr *)(addr))->sa_family, _INADDRBYSA(addr), buf, sizeof(buf))
+
+#define GETNAMEINFO(x, y, z) \
+ getnameinfo((x), (x)->sa_len, (y), sizeof(y), (z), sizeof(z), \
+ NI_NUMERICHOST | NI_NUMERICSERV)
+
+#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif /*!defined(_VAR_H_)*/
diff --git a/usr.sbin/pim6sd/vers.c b/usr.sbin/pim6sd/vers.c
new file mode 100644
index 0000000..5264529
--- /dev/null
+++ b/usr.sbin/pim6sd/vers.c
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+char todaysversion[]="2.1.0-alpha23";
diff --git a/usr.sbin/pim6sd/vif.c b/usr.sbin/pim6sd/vif.c
new file mode 100644
index 0000000..e7ef2d9
--- /dev/null
+++ b/usr.sbin/pim6sd/vif.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "vif.h"
+#include "mld6.h"
+#include "pim6.h"
+#include "pimd.h"
+#include "route.h"
+#include "config.h"
+#include "inet6.h"
+#include "kern.h"
+#include "mld6_proto.h"
+#include "pim6_proto.h"
+#include "mrt.h"
+#include "debug.h"
+#include "timer.h"
+
+struct uvif uvifs[MAXMIFS]; /*the list of virtualsinterfaces */
+vifi_t numvifs; /*total number of interface */
+int vifs_down;
+vifi_t reg_vif_num; /*register interface*/
+int phys_vif; /* An enabled vif that has a global address */
+int udp_socket;
+int total_interfaces;
+if_set if_nullset;
+if_set if_result;
+
+int init_reg_vif();
+void start_all_vifs();
+void start_vif( vifi_t vifi );
+void stop_vif( vifi_t vivi );
+int update_reg_vif( vifi_t register_vifi);
+
+extern int cfparse(int, int);
+
+void init_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ int enabled_vifs;
+
+ numvifs = 0;
+ reg_vif_num = NO_VIF;
+
+ /*
+ * Configure the vifs based on the interface configuration of
+ * the kernel and the contents of the configuration file.
+ * (Open a UDP socket for ioctl use in the config procedures if
+ * the kernel can't handle IOCTL's on the MLD socket.)
+ */
+#ifdef IOCTL_OK_ON_RAW_SOCKET
+ udp_socket = mld6_socket;
+#else
+ if ((udp_socket = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "UDP6 socket");
+#endif
+
+ /* clean all the interfaces ... */
+
+ for(vifi = 0,v=uvifs; vifi < MAXVIFS; ++ vifi, ++v)
+ {
+ memset(v,0,sizeof(*v)); /* everything is zeroed => NULL , pointer NULL , addrANY ...) */
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+ strncpy(v->uv_name,"",IFNAMSIZ);
+ v->uv_local_pref = default_source_preference;
+ v->uv_local_metric = default_source_metric;
+ }
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Interfaces world initialized...");
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Getting vifs from kernel");
+ config_vifs_from_kernel();
+ if (max_global_address() == NULL)
+ log(LOG_ERR, 0, "There's no global address");
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Getting vifs from %s",configfilename);
+
+ /* read config from file */
+ if (cfparse(1, 0) != 0)
+ log(LOG_ERR, 0, "fatal error in parsing the config file");
+
+ enabled_vifs = 0;
+ phys_vif = -1;
+
+ for( vifi = 0, v = uvifs ; vifi < numvifs ; ++ vifi,++v)
+ {
+ if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ if(v->uv_linklocal == NULL)
+ log(LOG_ERR,0,"there is no link-local address on vif %s",v->uv_name);
+ if (phys_vif == -1) {
+ struct phaddr *p;
+
+ /*
+ * If this vif has a global address, set its id
+ * to phys_vif.
+ */
+ for(p = v->uv_addrs; p; p = p->pa_next) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
+ phys_vif = vifi;
+ break;
+ }
+ }
+ }
+ enabled_vifs++;
+ }
+ if (enabled_vifs < 2)
+ log(LOG_ERR,0,"can't forward: %s",
+ enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif" );
+
+ memset(&if_nullset,0,sizeof(if_nullset));
+ k_init_pim(mld6_socket);
+ IF_DEBUG(DEBUG_PIM_DETAIL)
+ log(LOG_DEBUG,0,"Pim kernel initialization done");
+
+
+ /* Add a dummy virtual interface to support Registers in the kernel. */
+ init_reg_vif();
+
+ start_all_vifs();
+
+}
+int init_reg_vif()
+{
+ struct uvif *v;
+ vifi_t i;
+
+ v = &uvifs[numvifs];
+ if (( numvifs+1 ) == MAXVIFS )
+ {
+ /* Exit the program! The PIM router must have a Register vif */
+ log(LOG_ERR, 0,
+ "cannot install the Register vif: too many interfaces");
+ /* To make lint happy */
+ return (FALSE);
+ }
+
+ /*
+ * So far in PIM we need only one register vif and we save its number in
+ * the global reg_vif_num.
+ */
+
+
+ reg_vif_num = numvifs;
+
+ /* Use the address of the first available physical interface to
+ * create the register vif.
+ */
+
+ for(i =0 ; i < numvifs ; i++)
+ {
+ if(uvifs[i].uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
+ continue;
+ else
+ break;
+ }
+ if( i >= numvifs)
+ {
+ log(LOG_ERR, 0, "No physical interface enabled");
+ return -1;
+ }
+
+
+ memcpy(v,&uvifs[i],sizeof(*v));
+ strncpy(v->uv_name,"register_mif0",IFNAMSIZ);
+ v->uv_flags = MIFF_REGISTER;
+
+#ifdef PIM_EXPERIMENTAL
+ v->uv_flags |= MIFF_REGISTER_KERNEL_ENCAP;
+#endif
+
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"Interface %s (subnet %s) ,installed on vif #%u - rate = %d",
+ v->uv_name,net6name(&v->uv_prefix.sin6_addr,&v->uv_subnetmask),
+ reg_vif_num,v->uv_rate_limit);
+
+ numvifs++;
+ total_interfaces++;
+ return 0;
+}
+
+void start_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_int action;
+
+
+ /* Start first the NON-REGISTER vifs */
+
+ for(action=0; ;action = MIFF_REGISTER )
+ {
+ for(vifi= 0,v = uvifs;vifi < numvifs ; ++vifi, ++v)
+ {
+ if (( v->uv_flags & MIFF_REGISTER ) ^ action )
+ /* If starting non-registers but the vif is a register
+ * or if starting registers, but the interface is not
+ * a register, then just continue.
+ */
+ continue;
+
+ if ( v->uv_flags & (VIFF_DISABLED | VIFF_DOWN ))
+ {
+ IF_DEBUG(DEBUG_IF)
+ {
+ if ( v-> uv_flags & VIFF_DISABLED)
+ log(LOG_DEBUG,0,"%s is DISABLED ; vif #%u out of service",v->uv_name,vifi);
+ else
+ log(LOG_DEBUG,0,"%s is DOWN ; vif #%u out of service",v->uv_name,vifi);
+ }
+ }
+ else
+ start_vif(vifi);
+ }
+ if ( action == MIFF_REGISTER)
+ break;
+ }
+}
+
+/*
+ * Initialize the vif and add to the kernel. The vif can be either
+ * physical, register or tunnel (tunnels will be used in the future
+ * when this code becomes PIM multicast boarder router.
+ */
+
+
+void start_vif (vifi_t vifi)
+{
+ struct uvif *v;
+
+ v = &uvifs[vifi];
+
+ /* Initialy no router on any vif */
+
+ if( v-> uv_flags & MIFF_REGISTER)
+ v->uv_flags = v->uv_flags & ~VIFF_DOWN;
+ else
+ {
+ v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~ VIFF_DOWN;
+ v->uv_pim_hello_timer = 1 + RANDOM() % pim_hello_period;
+ v->uv_jp_timer = 1 + RANDOM() % pim_join_prune_period;
+ }
+
+ /* Tell kernel to add, i.e. start this vif */
+
+ k_add_vif(mld6_socket,vifi,&uvifs[vifi]);
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG,0,"%s comes up ,vif #%u now in service",v->uv_name,vifi);
+
+ if(!(v->uv_flags & MIFF_REGISTER))
+ {
+
+ /*
+ * Join the PIM multicast group on the interface.
+ */
+
+
+ k_join(mld6_socket,&allpim6routers_group.sin6_addr,v->uv_ifindex);
+ /*
+ * Join the ALL-ROUTERS multicast group on the interface.
+ * This allows mtrace requests to loop back if they are run
+ * on the multicast router.this allow receiving mld6 messages too.
+ */
+
+ k_join(mld6_socket,&allrouters_group.sin6_addr,v->uv_ifindex);
+
+ /*
+ * Until neighbors are discovered, assume responsibility for sending
+ * periodic group membership queries to the subnet. Send the first
+ * query.
+ */
+
+
+ v->uv_flags |= VIFF_QUERIER;
+ query_groups(v);
+
+ /*
+ * Send a probe via the new vif to look for neighbors.
+ */
+
+ send_pim6_hello( v , pim_hello_holdtime );
+ }
+}
+
+/*
+ * Stop a vif (either physical interface, tunnel or
+ * register.) If we are running only PIM we don't have tunnels.
+ */
+
+
+void stop_vif( vifi_t vifi )
+{
+ struct uvif *v;
+ struct listaddr *a;
+ register pim_nbr_entry_t *n;
+ register pim_nbr_entry_t *next;
+ struct vif_acl *acl;
+
+
+ /*
+ * TODO: make sure that the kernel viftable is
+ * consistent with the daemon table
+ */
+
+ v=&uvifs[vifi];
+ if( !( v->uv_flags&MIFF_REGISTER ) )
+ {
+ k_leave( mld6_socket , &allpim6routers_group.sin6_addr , v->uv_ifindex );
+ k_leave( mld6_socket , &allrouters_group.sin6_addr , v->uv_ifindex );
+ /*
+ * Discard all group addresses. (No need to tell kernel;
+ * the k_del_vif() call will clean up kernel state.)
+ */
+
+ while( v->uv_groups!=NULL )
+ {
+ a=v->uv_groups;
+ v->uv_groups=a->al_next;
+ free((char *)a);
+ }
+ }
+
+ /*
+ * TODO: inform (eventually) the neighbors I am going down by sending
+ * PIM_HELLO with holdtime=0 so someone else should become a DR.
+ */
+ /* TODO: dummy! Implement it!! Any problems if don't use it? */
+ delete_vif_from_mrt(vifi);
+
+ /*
+ * Delete the interface from the kernel's vif structure.
+ */
+
+ k_del_vif( mld6_socket , vifi );
+ v->uv_flags=(v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS) | VIFF_DOWN;
+ if( !(v->uv_flags & MIFF_REGISTER ))
+ {
+ RESET_TIMER(v->uv_pim_hello_timer);
+ RESET_TIMER(v->uv_jp_timer);
+ RESET_TIMER(v->uv_gq_timer);
+
+ for( n=v->uv_pim_neighbors ; n!=NULL ; n = next )
+ {
+ next=n->next; /* Free the space for each neighbour */
+ free((char *)n);
+ }
+ v->uv_pim_neighbors=NULL;
+ }
+
+
+ /* TODO: currently not used */
+ /* The Access Control List (list with the scoped addresses) */
+
+ while( v->uv_acl!=NULL )
+ {
+ acl=v->uv_acl;
+ v->uv_acl=acl->acl_next;
+ free((char *)acl);
+ }
+
+ vifs_down=TRUE;
+
+ IF_DEBUG(DEBUG_IF)
+ log( LOG_DEBUG ,0,"%s goes down , vif #%u out of service" , v->uv_name , vifi);
+}
+
+/*
+ * Update the register vif in the multicast routing daemon and the
+ * kernel because the interface used initially to get its local address
+ * is DOWN. register_vifi is the index to the Register vif which needs
+ * to be updated. As a result the Register vif has a new uv_lcl_addr and
+ * is UP (virtually :))
+ */
+int
+update_reg_vif( vifi_t register_vifi )
+{
+ register struct uvif *v;
+ register vifi_t vifi;
+
+ /* Find the first useable vif with solid physical background */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL
+ | MIFF_REGISTER))
+ continue;
+ /* Found. Stop the bogus Register vif first */
+ stop_vif(register_vifi);
+ uvifs[register_vifi].uv_linklocal->pa_addr =
+ uvifs[vifi].uv_linklocal->pa_addr;
+ start_vif(register_vifi);
+ IF_DEBUG(DEBUG_PIM_REGISTER | DEBUG_IF)
+ log(LOG_NOTICE, 0, "%s has come up; vif #%u now in service",
+ uvifs[register_vifi].uv_name, register_vifi);
+ return 0;
+ }
+ vifs_down = TRUE;
+ log(LOG_WARNING, 0, "Cannot start Register vif: %s",
+ uvifs[vifi].uv_name);
+ return(-1);
+}
+
+/*
+ * return the max global Ipv6 address of an UP and ENABLED interface
+ * other than the MIFF_REGISTER interface.
+*/
+struct sockaddr_in6 *
+max_global_address()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ struct phaddr *p;
+ struct phaddr *pmax = NULL;
+
+ for(vifi=0,v=uvifs;vifi< numvifs;++vifi,++v)
+ {
+ if(v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ /*
+ * take first the max global address of the interface
+ * (without link local) => aliasing
+ */
+ for(p=v->uv_addrs;p!=NULL;p=p->pa_next)
+ {
+ /*
+ * If this is the first global address, take it anyway.
+ */
+ if (pmax == NULL) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ pmax = p;
+ }
+ else {
+ if (inet6_lessthan(&pmax->pa_addr,
+ &p->pa_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ pmax=p;
+ }
+ }
+ }
+
+ return(pmax ? &pmax->pa_addr : NULL);
+}
+
+struct sockaddr_in6 *
+uv_global(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v = &uvifs[vifi];
+ struct phaddr *p;
+
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr) &&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr))
+ return(&p->pa_addr);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Check if the interface exists in the mif table. If true
+ * return the highest address of the interface else return NULL.
+ */
+struct sockaddr_in6 *
+local_iface(char *ifname)
+{
+ register struct uvif *v;
+ vifi_t vifi;
+ struct phaddr *p;
+ struct phaddr *pmax = NULL;
+
+ for(vifi=0,v=uvifs;vifi<numvifs;++vifi,++v)
+ {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ if(EQUAL(v->uv_name, ifname))
+ {
+ for(p=v->uv_addrs; p!=NULL; p=p->pa_next)
+ {
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->pa_addr.sin6_addr)&&
+ !IN6_IS_ADDR_SITELOCAL(&p->pa_addr.sin6_addr)) {
+ /*
+ * If this is the first global address
+ * or larger than the current MAX global
+ * address, remember it.
+ */
+ if (pmax == NULL ||
+ inet6_lessthan(&pmax->pa_addr,
+ &p->pa_addr))
+ pmax = p;
+ }
+ }
+ if (pmax)
+ return(&pmax->pa_addr);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points. Ignore interfaces that have been administratively
+ * disabled.
+ */
+void
+check_vif_state()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ struct ifreq ifr;
+ static int checking_vifs=0;
+
+ /*
+ * XXX: TODO: True only for DVMRP?? Check.
+ * If we get an error while checking, (e.g. two interfaces go down
+ * at once, and we decide to send a prune out one of the failed ones)
+ * then don't go into an infinite loop!
+ */
+ if( checking_vifs )
+ return;
+
+ vifs_down=FALSE;
+ checking_vifs=TRUE;
+
+ /* TODO: Check all potential interfaces!!! */
+ /* Check the physical and tunnels only */
+ for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
+ {
+ if( v->uv_flags & ( VIFF_DISABLED|MIFF_REGISTER ) )
+ continue;
+
+ strncpy( ifr.ifr_name , v->uv_name , IFNAMSIZ );
+
+ /* get the interface flags */
+ if( ioctl( udp_socket , SIOCGIFFLAGS , (char *)&ifr )<0 )
+ log(LOG_ERR, errno,
+ "check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+ if( v->uv_flags & VIFF_DOWN )
+ {
+ if ( ifr.ifr_flags & IFF_UP )
+ {
+ start_vif( vifi );
+ }
+ else
+ vifs_down=TRUE;
+ }
+ else
+ {
+ if( !( ifr.ifr_flags & IFF_UP ))
+ {
+ log( LOG_NOTICE ,0,
+ "%s has gone down ; vif #%u taken out of service",
+ v->uv_name , vifi );
+ stop_vif ( vifi );
+ vifs_down = TRUE;
+ }
+ }
+ }
+
+ /* Check the register(s) vif(s) */
+ for( vifi=0 , v=uvifs ; vifi<numvifs ; ++vifi , ++v )
+ {
+ register vifi_t vifi2;
+ register struct uvif *v2;
+ int found;
+
+ if( !(v->uv_flags & MIFF_REGISTER ) )
+ continue;
+ else
+ {
+ found=0;
+
+ /* Find a physical vif with the same IP address as the
+ * Register vif.
+ */
+ for( vifi2=0 , v2=uvifs ; vifi2<numvifs ; ++vifi2 , ++v2 )
+ {
+ if( v2->uv_flags & ( VIFF_DISABLED|VIFF_DOWN|VIFF_TUNNEL|MIFF_REGISTER ))
+ continue;
+ if( IN6_ARE_ADDR_EQUAL( &v->uv_linklocal->pa_addr.sin6_addr,
+ &v2->uv_linklocal->pa_addr.sin6_addr ))
+ {
+ found=1;
+ break;
+ }
+ }
+ if(!found)
+ /* The physical interface with the IP address as the Register
+ * vif is probably DOWN. Get a replacement.
+ */
+ update_reg_vif( vifi );
+ }
+ }
+ checking_vifs=0;
+}
+
+/*
+ * If the source is directly connected to us, find the vif number for
+ * the corresponding physical interface (tunnels excluded).
+ * Local addresses are excluded.
+ * Return the vif number or NO_VIF if not found.
+ */
+
+vifi_t
+find_vif_direct(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
+ {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL|MIFF_REGISTER))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next)
+ {
+ if (inet6_equal(src, &p->pa_addr))
+ return(NO_VIF);
+ if (inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
+ return(vifi);
+ }
+ }
+
+ return (NO_VIF);
+}
+
+/*
+ * Checks if src is local address. If "yes" return the vif index,
+ * otherwise return value is NO_VIF.
+ */
+
+vifi_t
+local_address(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (inet6_equal(src, &p->pa_addr))
+ return(vifi);
+ }
+ }
+ /* Returning NO_VIF means not a local address */
+ return (NO_VIF);
+}
+
+
+/*
+ * If the source is directly connected, or is local address,
+ * find the vif number for the corresponding physical interface
+ * (tunnels excluded).
+ * Return the vif number or NO_VIF if not found.
+ */
+
+vifi_t
+find_vif_direct_local(src)
+ struct sockaddr_in6 *src;
+{
+ vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_TUNNEL |MIFF_REGISTER))
+ continue;
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ if (inet6_equal(src, &p->pa_addr) ||
+ inet6_match_prefix(src, &p->pa_prefix, &p->pa_subnetmask))
+ return(vifi);
+ }
+ }
+ return (NO_VIF);
+}
+
+int
+vif_forwarder(if_set *p1 , if_set *p2)
+{
+ int idx;
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ if (p1->ifs_bits[idx] & p2->ifs_bits[idx])
+ return(TRUE);
+
+ }
+
+ /* (p1 & p2) is empty. We're not the forwarder */
+ return(FALSE);
+}
+
+if_set *
+vif_and(if_set *p1 , if_set *p2, if_set *result)
+{
+ int idx;
+
+ IF_ZERO(result);
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ result->ifs_bits[idx] = p1->ifs_bits[idx] & p2->ifs_bits[idx];
+ }
+
+ return(result);
+}
+
+if_set *
+vif_xor(if_set *p1 , if_set *p2, if_set *result)
+{
+ int idx;
+
+ IF_ZERO(result);
+
+ for(idx=0 ; idx < sizeof(*p1)/sizeof(fd_mask) ; idx++)
+ {
+ result->ifs_bits[idx] =
+ p1->ifs_bits[idx] ^ p2->ifs_bits[idx];
+ }
+
+ return(result);
+}
+/*
+ * stop all vifs
+ */
+void
+stop_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ for (vifi = 0, v=uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ stop_vif(vifi);
+ }
+ }
+}
+
+struct uvif *
+find_vif(ifname)
+ char *ifname;
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs ; ++vifi , ++v) {
+ if (strcasecmp(v->uv_name, ifname) == 0)
+ return(v);
+ }
+
+ return(NULL);
+}
diff --git a/usr.sbin/pim6sd/vif.h b/usr.sbin/pim6sd/vif.h
new file mode 100644
index 0000000..f3e3495
--- /dev/null
+++ b/usr.sbin/pim6sd/vif.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 1998 by the University of Southern California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation in source and binary forms for lawful
+ * purposes and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both
+ * the copyright notice and this permission notice appear in supporting
+ * documentation, and that any documentation, advertising materials,
+ * and other materials related to such distribution and use acknowledge
+ * that the software was developed by the University of Southern
+ * California and/or Information Sciences Institute.
+ * The name of the University of Southern California may not
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
+ * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
+ * NON-INFRINGEMENT.
+ *
+ * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
+ * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
+ * THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
+ *
+ */
+/*
+ * This program has been derived from pim6dd.
+ * The pim6dd program is covered by the license in the accompanying file
+ * named "LICENSE.pim6dd".
+ */
+/*
+ * This program has been derived from pimd.
+ * The pimd program is covered by the license in the accompanying file
+ * named "LICENSE.pimd".
+ *
+ */
+/*
+ * Part of this program has been derived from mrouted.
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE.mrouted".
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ */
+
+#ifndef VIF_H
+#define VIF_H
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet/ip_mroute.h>
+#include "defs.h"
+
+extern int total_interfaces;
+extern int udp_socket;
+extern struct uvif uvifs[];
+extern vifi_t numvifs;
+extern int vifs_down;
+extern int phys_vif;
+extern vifi_t reg_vif_num;
+
+#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
+#define DEFAULT_METRIC 1
+#define VIFF_DOWN 0x000100
+#define VIFF_DISABLED 0x000200
+#define VIFF_QUERIER 0x000400
+#define VIFF_REXMIT_PRUNES 0x004000
+#define VIFF_DR 0x040000
+#define VIFF_NONBRS 0x080000
+#define VIFF_PIM_NBR 0x200000
+#define VIFF_POINT_TO_POINT 0x400000
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) *8
+
+
+extern if_set if_nullset;
+#define IF_ISEMPTY(p) (memcmp((p), &if_nullset, sizeof(if_nullset)) == 0)
+#define IF_SAME(p1, p2) (memcmp((p1),(p2),sizeof(*(p1))) == 0)
+#define IF_CLR_MASK(p, mask) \
+ {\
+ int idx;\
+ for (idx = 0; idx < sizeof(*(p))/sizeof(fd_mask); idx++) {\
+ (p)->ifs_bits[idx] &= ~((mask)->ifs_bits[idx]);\
+ }\
+ }
+#define IF_MERGE(p1, p2, result) \
+ {\
+ int idx;\
+ for (idx = 0; idx < sizeof(*(p1))/sizeof(fd_mask); idx++) {\
+ (result)->ifs_bits[idx] = (p1)->ifs_bits[idx]|(p2)->ifs_bits[idx]; \
+ }\
+ }
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ struct sockaddr_in6 *vfe_addr;
+ struct in6_addr vfe_mask;
+ int vfe_flags;
+#define VFRF_EXACT 0x0001
+};
+
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+#define VFF_BIDIR 1
+
+struct vif_filter {
+ int vf_type;
+ int vf_flags;
+ struct vf_element *vf_filter;
+};
+
+struct listaddr {
+ struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
+ struct sockaddr_in6 al_addr; /* local group or neighbor address */
+ u_long al_timer; /* for timing out group or neighbor */
+ time_t al_ctime; /* entry creation time */
+ union {
+ u_int32 alu_genid; /* generation id for neighbor */
+ struct sockaddr_in6 alu_reporter; /* a host which reported membership */
+ } al_alu;
+ u_char al_pv; /* router protocol version */
+ u_char al_mv; /* router mrouted version */
+ u_char al_old; /* time since heard old report */
+ u_char al_index; /* neighbor index */
+ u_long al_timerid; /* timer for group membership */
+ u_long al_query; /* timer for repeated leave query */
+ u_int16 al_flags; /* flags related to this neighbor */
+};
+
+#define al_genid al_alu.alu_genid
+#define al_reporter al_alu.alu_reporter
+
+/*
+ * User level Virtual Interface structure
+ *
+ * A "virtual interface" is either a physical, multicast-capable interface
+ * (called a "phyint"), a virtual point-to-point link (called a "tunnel")
+ * or a "register vif" used by PIM. The register vif is used by the
+ * Designated Router (DR) to send encapsulated data packets to the
+ * Rendevous Point (RP) for a particular group. The data packets are
+ * encapsulated in PIM messages (IPPROTO_PIM = 103) and then unicast to
+ * the RP.
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ */
+struct uvif {
+ u_int uv_flags;
+ u_char uv_metric; /* VIFF_ flags defined below */
+ u_char uv_admetric; /* advertised cost of this vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
+
+ struct phaddr *uv_linklocal; /* link-local address of this vif */
+ struct sockaddr_in6 uv_rmt_addr;/* remote end-point addr (tunnels only) */
+ struct sockaddr_in6 uv_dst_addr;/* destination for PIM messages */
+ struct sockaddr_in6 uv_prefix; /* prefix (phyints only) */
+ struct in6_addr uv_subnetmask; /* subnet mask (phyints only) */
+
+ char uv_name[IFNAMSIZ]; /* interface name */
+ u_int uv_ifindex; /* index of the interface */
+ u_int uv_siteid; /* index of the site on the interface */
+
+ struct listaddr *uv_groups; /* list of local groups (phyints only) */
+ struct lisaddr *uv_dvmrp_neighbors;
+ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */
+ struct listaddr *uv_querier; /* MLD querier on vif */
+ int uv_prune_lifetime; /* Prune lifetime or 0 for default */
+ struct vif_acl *uv_acl; /* access control list of groups */
+ int uv_leaftimer; /* time until this vif is considrd leaf */
+ struct phaddr *uv_addrs; /* Additional addresses on this vif */
+ struct vif_filter *uvfilter; /* Route filters on this vif */
+ u_int16 uv_pim_hello_timer; /* timer for sending PIM hello msgs */
+ u_int16 uv_gq_timer; /* Group Query timer */
+ u_int16 uv_jp_timer; /* Join/Prune timer */
+ int uv_local_pref; /* default local preference for assert */
+ int uv_local_metric; /* default local metric for assert */
+ struct pim_nbr_entry *uv_pim_neighbors; /* list of PIM nbr routers */
+
+ void *config_attr; /* temporary buffer while parsing config */
+
+ /* the followings are to collect statistics */
+ /* incoming PIM6 packets on this interface */
+ u_quad_t uv_in_pim6_hello;
+ u_quad_t uv_in_pim6_join_prune;
+ u_quad_t uv_in_pim6_bootsrap;
+ u_quad_t uv_in_pim6_assert;
+ /* outgoing PIM6 packets on this interface */
+ u_quad_t uv_out_pim6_hello;
+ u_quad_t uv_out_pim6_join_prune;
+ u_quad_t uv_out_pim6_bootsrap;
+ u_quad_t uv_out_pim6_assert;
+ /* incoming MLD packets on this interface */
+ u_quad_t uv_in_mld_query;
+ u_quad_t uv_in_mld_report;
+ u_quad_t uv_in_mld_done;
+ /* outgoing MLD packets on this interface */
+ u_quad_t uv_out_mld_query;
+ u_quad_t uv_out_mld_report;
+ u_quad_t uv_out_mld_done;
+ /* statistics about the forwarding cache in kernel */
+ u_quad_t uv_cache_miss;
+ u_quad_t uv_cache_notcreated;
+ /* occurrences of timeouts */
+ u_quad_t uv_pim6_nbr_timo;
+ u_quad_t uv_listener_timo;
+ u_quad_t uv_outif_timo; /* outgoing interfaces timers */
+};
+
+struct phaddr {
+ struct phaddr *pa_next;
+ struct sockaddr_in6 pa_addr;
+ struct sockaddr_in6 pa_prefix;
+ struct in6_addr pa_subnetmask;
+};
+
+
+/* The Access Control List (list with scoped addresses) member */
+#define VIFF_NOLISTENER 0x800000 /* no listener on the link */
+
+struct vif_acl {
+ struct vif_acl *acl_next;
+ struct sockaddr_in6 acl_addr;
+ struct in6_addr acl_mask;
+};
+
+/*
+ * Used to get the RPF neighbor and IIF info
+ * for a given source from the unicast routing table.
+ */
+
+struct rpfctl {
+ struct sockaddr_in6 source; /* the source for which we want iif and rpfnbr */
+ struct sockaddr_in6 rpfneighbor;/* next hop towards the source */
+ vifi_t iif; /* the incoming interface to reach the next hop */
+};
+
+
+
+
+extern void init_vifs __P(());
+extern void stop_all_vifs __P(());
+extern void check_vif_state __P(());
+struct sockaddr_in6 * max_global_address();
+struct sockaddr_in6 * uv_global __P(());
+extern vifi_t local_address __P((struct sockaddr_in6 *src));
+struct sockaddr_in6 * local_iface( char *ifname );
+extern vifi_t find_vif_direct __P((struct sockaddr_in6 *src));
+extern vifi_t find_vif_direct_local __P((struct sockaddr_in6 *src));
+extern int vif_forwarder __P((if_set *p1 ,if_set *p2));
+extern if_set *vif_and __P((if_set *p1, if_set *p2, if_set *result));
+extern if_set *vif_xor __P((if_set *p1, if_set *p2, if_set *result));
+extern struct uvif *find_vif __P((char *ifname));
+#endif
diff --git a/usr.sbin/pim6sd/vmbuf.h b/usr.sbin/pim6sd/vmbuf.h
new file mode 100644
index 0000000..01fa8cd
--- /dev/null
+++ b/usr.sbin/pim6sd/vmbuf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/* YIPS @(#)$Id: vmbuf.h,v 1.1 1999/10/29 09:04:55 jinmei Exp $ */
+
+typedef struct _vchar_ {
+ u_int32_t t; /* type of the value */
+ size_t l; /* length of the value */
+ caddr_t v; /* place holder to the value in buffer */
+#if 0
+ caddr_t v0; /* pointer to the buffer - not used any more */
+#endif
+} vchar_t;
+
+extern vchar_t *vmalloc(size_t);
+extern vchar_t *vrealloc(vchar_t *, size_t);
+extern void vfree(vchar_t *);
+extern vchar_t *vdup(vchar_t *);
+extern int pvdump(vchar_t *);
+
+#define VREALLOC(ptr, size) ((ptr) = vrealloc((ptr), (size)))
+
OpenPOWER on IntegriCloud