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