freebsd-src/usr.sbin/pim6sd/pim6_proto.c
Yoshinobu Inoue 0fea3d5165 IPv6 multicast routing.
kernel IPv6 multicast routing support.
  pim6 dense mode daemon
  pim6 sparse mode daemon
  netstat support of IPv6 multicast routing statistics

  Merging to the current and testing with other existing multicast routers
  is done by Tatsuya Jinmei <jinmei@kame.net>, who writes and maintainances
  the base code in KAME distribution.

  Make world check and kernel build check was also successful.
2000-01-28 05:10:56 +00:00

4127 lines
125 KiB
C

/*
* Copyright (C) 1999 LSIIT Laboratory.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (C) 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1998 by the University of Southern California.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and
* its documentation in source and binary forms for lawful
* purposes and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both
* the copyright notice and this permission notice appear in supporting
* documentation, and that any documentation, advertising materials,
* and other materials related to such distribution and use acknowledge
* that the software was developed by the University of Southern
* California and/or Information Sciences Institute.
* The name of the University of Southern California may not
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
* NON-INFRINGEMENT.
*
* IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Other copyrights might apply to parts of this software and are so
* noted when applicable.
*
* $FreeBSD$
*/
/*
* Questions concerning this software should be directed to
* Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
*
*/
/*
* This program has been derived from pim6dd.
* The pim6dd program is covered by the license in the accompanying file
* named "LICENSE.pim6dd".
*/
/*
* This program has been derived from pimd.
* The pimd program is covered by the license in the accompanying file
* named "LICENSE.pimd".
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet6/pim6.h>
#include <netinet/ip6.h>
#include <syslog.h>
#include <stdlib.h>
#include "mrt.h"
#include "defs.h"
#include "vif.h"
#include "debug.h"
#include "pim6.h"
#include "pim6_proto.h"
#include "pimd.h"
#include "rp.h"
#include "mld6.h"
#include "timer.h"
#include "route.h"
#include "inet6.h"
#include "kern.h"
#include "routesock.h"
/*
* Local functions definitions.
*/
static int parse_pim6_hello __P((char *pktPtr , int datalen , struct sockaddr_in6 *src,
u_int16 *holdtime));
static int send_pim6_register_stop __P((struct sockaddr_in6 *reg_src , struct sockaddr_in6 *reg_dst ,
struct sockaddr_in6 *inner_source,
struct sockaddr_in6 *inner_grp));
static build_jp_message_t *get_jp6_working_buff __P(());
static void return_jp6_working_buff __P((pim_nbr_entry_t * pim_nbr));
static void pack_jp6_message __P((pim_nbr_entry_t * pim_nbr));
static void send_jp6_message __P((pim_nbr_entry_t * pim_nbr));
static int compare_metrics __P((u_int32 local_preference,
u_int32 local_metric,
struct sockaddr_in6 *local_address,
u_int32 remote_preference,
u_int32 remote_metric,
struct sockaddr_in6 *remote_address));
build_jp_message_t *build_jp_message_pool;
int build_jp_message_pool_counter;
struct sockaddr_in6 sockaddr6_any = {sizeof(struct sockaddr_in6) , AF_INET6 ,0,0, IN6ADDR_ANY_INIT};
struct sockaddr_in6 sockaddr6_d;
struct pim6dstat pim6dstat;
/************************************************************************
* PIM_HELLO
************************************************************************/
int
receive_pim6_hello(src, pim_message, datalen)
struct sockaddr_in6 *src;
register char *pim_message;
int datalen;
{
mifi_t mifi;
struct uvif *v;
register pim_nbr_entry_t *nbr,
*prev_nbr,
*new_nbr;
u_int16 holdtime;
int bsr_length;
u_int8 *data_ptr;
srcentry_t *srcentry_ptr;
mrtentry_t *mrtentry_ptr;
if ((mifi = find_vif_direct(src)) == NO_VIF)
{
/*
* Either a local vif or somehow received PIM_HELLO from non-directly
* connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
log(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
inet6_fmt(&src->sin6_addr));
return (FALSE);
}
v = &uvifs[mifi];
v->uv_in_pim6_hello++; /* increment statistacs */
if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | MIFF_REGISTER))
return (FALSE); /* Shoudn't come on this interface */
data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
/* Get the Holdtime (in seconds) from the message. Return if error. */
if (parse_pim6_hello(pim_message, datalen, src, &holdtime) == FALSE)
return (FALSE);
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
inet6_fmt(&src->sin6_addr), holdtime);
for (prev_nbr = (pim_nbr_entry_t *) NULL, nbr = v->uv_pim_neighbors;
nbr != (pim_nbr_entry_t *) NULL;
prev_nbr = nbr, nbr = nbr->next)
{
/*
* The PIM neighbors are sorted in decreasing order of the network
* addresses (note that to be able to compare them correctly we must
* translate the addresses in host order.
*/
if (inet6_lessthan(src, &nbr->address))
continue;
if (inet6_equal(src, &nbr->address))
{
/* We already have an entry for this host */
if (0 == holdtime)
{
/*
* Looks like we have a nice neighbor who is going down and
* wants to inform us by sending "holdtime=0". Thanks buddy
* and see you again!
*/
log(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
inet6_fmt(&src->sin6_addr));
delete_pim6_nbr(nbr);
return (TRUE);
}
SET_TIMER(nbr->timer, holdtime);
return (TRUE);
}
else
/*
* No entry for this neighbor. Exit the loop and create an entry
* for it.
*/
break;
}
/*
* This is a new neighbor. Create a new entry for it. It must be added
* right after `prev_nbr`
*/
new_nbr = (pim_nbr_entry_t *) malloc(sizeof(pim_nbr_entry_t));
new_nbr->address = *src;
new_nbr->vifi = mifi;
SET_TIMER(new_nbr->timer, holdtime);
new_nbr->build_jp_message = (build_jp_message_t *) NULL;
new_nbr->next = nbr;
new_nbr->prev = prev_nbr;
if (prev_nbr != (pim_nbr_entry_t *) NULL)
prev_nbr->next = new_nbr;
else
v->uv_pim_neighbors = new_nbr;
if (new_nbr->next != (pim_nbr_entry_t *) NULL)
new_nbr->next->prev = new_nbr;
v->uv_flags &= ~VIFF_NONBRS;
v->uv_flags |= VIFF_PIM_NBR;
/* Since a new neighbour has come up, let it know your existence */
/*
* XXX: TODO: not in the spec, but probably should send the message after
* a short random period?
*/
send_pim6_hello(v, pim_hello_holdtime);
if (v->uv_flags & VIFF_DR)
{
/*
* If I am the current DR on that interface, so send an RP-Set
* message to the new neighbor.
*/
if ((bsr_length = create_pim6_bootstrap_message(pim6_send_buf)))
send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr , src , PIM_BOOTSTRAP,
bsr_length);
/* The router with highest network address is the elected DR */
if (inet6_lessthan(&v->uv_linklocal->pa_addr,&v->uv_pim_neighbors->address))
{
/*
* I was the DR, but not anymore. Remove all register_vif from
* oif list for all directly connected sources (for vifi).
*/
/* TODO: XXX: first entry is not used! */
for (srcentry_ptr = srclist->next;
srcentry_ptr != (srcentry_t *) NULL;
srcentry_ptr = srcentry_ptr->next)
{
/* If not directly connected source for vifi */
if ((srcentry_ptr->incoming != mifi)
|| (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL))
continue;
for (mrtentry_ptr = srcentry_ptr->mrtlink;
mrtentry_ptr != (mrtentry_t *) NULL;
mrtentry_ptr = mrtentry_ptr->srcnext)
{
if (!(mrtentry_ptr->flags & MRTF_SG))
continue; /* This is not (S,G) entry */
/* Remove the register oif */
IF_CLR(reg_vif_num, &mrtentry_ptr->joined_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
}
v->uv_flags &= ~VIFF_DR;
v->uv_flags &= ~VIFF_QUERIER;
}
}
/*
* TODO: XXX: does a new neighbor change any routing entries info? Need
* to trigger joins?
*/
IF_DEBUG(DEBUG_PIM_HELLO)
log(LOG_DEBUG,0,"I'have got a new neighbor %s on vif %s",inet6_fmt(&src->sin6_addr),v->uv_name);
return (TRUE);
}
void
delete_pim6_nbr(nbr_delete)
pim_nbr_entry_t *nbr_delete;
{
srcentry_t *srcentry_ptr;
srcentry_t *srcentry_ptr_next;
mrtentry_t *mrtentry_ptr;
mrtentry_t *mrtentry_srcs;
grpentry_t *grpentry_ptr;
pim_nbr_entry_t *new_nbr;
cand_rp_t *cand_rp_ptr;
rp_grp_entry_t *rp_grp_entry_ptr;
rpentry_t *rpentry_ptr;
struct uvif *v;
v = &uvifs[nbr_delete->vifi];
/* Delete the entry from the pim_nbrs chain */
if (nbr_delete->prev != (pim_nbr_entry_t *) NULL)
nbr_delete->prev->next = nbr_delete->next;
else
v->uv_pim_neighbors = nbr_delete->next;
if (nbr_delete->next != (pim_nbr_entry_t *) NULL)
nbr_delete->next->prev = nbr_delete->prev;
return_jp6_working_buff(nbr_delete);
if (v->uv_pim_neighbors == (pim_nbr_entry_t *) NULL)
{
/* This was our last neighbor. */
v->uv_flags &= ~VIFF_PIM_NBR;
v->uv_flags |= (VIFF_NONBRS | VIFF_DR);
}
else
{
if (inet6_greaterthan(&v->uv_linklocal->pa_addr,
&v->uv_pim_neighbors->address))
/*
* The first address is the new potential remote DR address, but
* the local address is the winner.
*/
v->uv_flags |= VIFF_DR;
v->uv_flags |= VIFF_QUERIER;
}
/* Update the source entries */
for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *) NULL;
srcentry_ptr = srcentry_ptr_next)
{
srcentry_ptr_next = srcentry_ptr->next;
if (srcentry_ptr->upstream != nbr_delete)
continue;
/* Reset the next hop (PIM) router */
if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE)
{
/*
* Coudn't reset it. Sorry, the hext hop router toward that
* source is probably not a PIM router, or cannot find route at
* all, hence I cannot handle this source and have to delete it.
*/
delete_srcentry(srcentry_ptr);
}
else
if (srcentry_ptr->upstream != (pim_nbr_entry_t *) NULL)
{
/* Ignore the local or directly connected sources */
/*
* Browse all MRT entries for this source and reset the
* upstream router. Note that the upstream router is not
* always toward the source: it could be toward the RP for
* example.
*/
new_nbr = srcentry_ptr->upstream;
for (mrtentry_ptr = srcentry_ptr->mrtlink;
mrtentry_ptr != (mrtentry_t *) NULL;
mrtentry_ptr = mrtentry_ptr->srcnext)
{
if (!(mrtentry_ptr->flags & MRTF_RP))
{
mrtentry_ptr->upstream = srcentry_ptr->upstream;
mrtentry_ptr->metric = srcentry_ptr->metric;
mrtentry_ptr->preference = srcentry_ptr->preference;
change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
}
}
}
/* Update the RP entries */
for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
cand_rp_ptr = cand_rp_ptr->next)
{
if (cand_rp_ptr->rpentry->upstream != nbr_delete)
continue;
rpentry_ptr = cand_rp_ptr->rpentry;
/* Reset the RP entry iif */
/* TODO: check if error setting the iif! */
if (local_address(&rpentry_ptr->address) == NO_VIF)
{
set_incoming(rpentry_ptr, PIM_IIF_RP);
}
else
{
rpentry_ptr->incoming = reg_vif_num;
rpentry_ptr->upstream = (pim_nbr_entry_t *) NULL;
}
mrtentry_ptr = rpentry_ptr->mrtlink;
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
mrtentry_ptr->upstream = rpentry_ptr->upstream;
mrtentry_ptr->metric = rpentry_ptr->metric;
mrtentry_ptr->preference = rpentry_ptr->preference;
change_interfaces(mrtentry_ptr,
rpentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
/* Update the group entries for this RP */
for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
{
for (grpentry_ptr = rp_grp_entry_ptr->grplink;
grpentry_ptr != (grpentry_t *) NULL;
grpentry_ptr = grpentry_ptr->rpnext)
{
mrtentry_ptr = grpentry_ptr->grp_route;
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
mrtentry_ptr->upstream = rpentry_ptr->upstream;
mrtentry_ptr->metric = rpentry_ptr->metric;
mrtentry_ptr->preference = rpentry_ptr->preference;
change_interfaces(mrtentry_ptr,
rpentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
/* Update only the (S,G)RPbit entries for this group */
for (mrtentry_srcs = grpentry_ptr->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
{
if (mrtentry_srcs->flags & MRTF_RP)
{
mrtentry_ptr->upstream = rpentry_ptr->upstream;
mrtentry_ptr->metric = rpentry_ptr->metric;
mrtentry_ptr->preference = rpentry_ptr->preference;
change_interfaces(mrtentry_srcs,
rpentry_ptr->incoming,
&mrtentry_srcs->joined_oifs,
&mrtentry_srcs->pruned_oifs,
&mrtentry_srcs->leaves,
&mrtentry_srcs->asserted_oifs, 0);
}
}
}
}
}
free((char *) nbr_delete);
}
/* TODO: simplify it! */
static int
parse_pim6_hello(pim_message, datalen, src, holdtime)
char *pim_message;
int datalen;
struct sockaddr_in6 *src;
u_int16 *holdtime;
{
u_int8 *pim_hello_message;
u_int8 *data_ptr;
u_int16 option_type;
u_int16 option_length;
int holdtime_received_ok = FALSE;
int option_total_length;
pim_hello_message = (u_int8 *) (pim_message + sizeof(struct pim));
datalen -= sizeof(struct pim);
for (; datalen >= sizeof(pim_hello_t);)
{
/* Ignore any data if shorter than (pim_hello header) */
data_ptr = pim_hello_message;
GET_HOSTSHORT(option_type, data_ptr);
GET_HOSTSHORT(option_length, data_ptr);
switch (option_type)
{
case PIM_MESSAGE_HELLO_HOLDTIME:
if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length)
{
IF_DEBUG(DEBUG_PIM_HELLO)
log(LOG_DEBUG, 0,
"PIM HELLO Holdtime from %s: invalid OptionLength = %u",
inet6_fmt(&src->sin6_addr), option_length);
return (FALSE);
}
GET_HOSTSHORT(*holdtime, data_ptr);
holdtime_received_ok = TRUE;
break;
default:
/* Ignore any unknown options */
break;
}
/* Move to the next option */
/*
* XXX: TODO: If we are padding to the end of the 32 bit boundary,
* use the first method to move to the next option, otherwise simply
* (sizeof(pim_hello_t) + option_length).
*/
#ifdef BOUNDARY_32_BIT
option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) +
((option_length & 0x3) ? 4 : 0));
#else
option_total_length = (sizeof(pim_hello_t) + option_length);
#endif /* BOUNDARY_32_BIT */
datalen -= option_total_length;
pim_hello_message += option_total_length;
}
return (holdtime_received_ok);
}
int
send_pim6_hello(v, holdtime)
struct uvif *v;
u_int16 holdtime;
{
char *buf;
u_int8 *data_ptr;
int datalen;
buf = pim6_send_buf + sizeof(struct pim);
data_ptr = (u_int8 *) buf;
PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr);
PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr);
PUT_HOSTSHORT(holdtime, data_ptr);
datalen = data_ptr - (u_int8 *) buf;
send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr,
&allpim6routers_group, PIM_HELLO, datalen);
SET_TIMER(v->uv_pim_hello_timer, pim_hello_period);
v->uv_out_pim6_hello++;
return (TRUE);
}
/************************************************************************
* PIM_REGISTER
************************************************************************/
/*
* TODO: XXX: IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM
* USER SPACE AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE
* KERNEL.
*/
int
receive_pim6_register(reg_src, reg_dst, pim_message, datalen)
struct sockaddr_in6 *reg_src,
*reg_dst;
char *pim_message;
int datalen;
{
struct sockaddr_in6 inner_src,
inner_grp;
pim_register_t *register_p;
struct ip6_hdr *ip;
u_int32 borderBit,
nullRegisterBit;
mrtentry_t *mrtentry_ptr;
mrtentry_t *mrtentry_ptr2;
if_set oifs;
pim6dstat.in_pim6_register++;
register_p = (pim_register_t *) (pim_message + sizeof(struct pim));
borderBit = ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_BORDER_BIT;
nullRegisterBit =
ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT;
/* initialize the pointer to the encapsulated packet */
ip = (struct ip6_hdr *) (register_p + 1);
/*
* We are keeping all addresses in network order,
* so no need for byte order translation.
*/
inner_src.sin6_addr = ip->ip6_src;
inner_grp.sin6_addr = ip->ip6_dst;
/* scope validation of the inner source and destination addresses */
if (IN6_IS_ADDR_LINKLOCAL(&ip->ip6_src)) {
log(LOG_WARNING, 0,
"receive_pim6_register: inner source(%s) has invalid scope",
inet6_fmt(&ip->ip6_src));
}
#ifdef notyet
if (IN6_IS_ADDR_SITELOCAL)
inner_src.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
reg_src, reg_dst);
else
#endif
inner_src.sin6_scope_id = 0;
if (IN6_IS_ADDR_MC_NODELOCAL(&ip->ip6_dst) ||
IN6_IS_ADDR_MC_LINKLOCAL(&ip->ip6_dst)) {
log(LOG_WARNING, 0,
"receive_pim6_register: inner group(%s) has invalid scope",
inet6_fmt(&ip->ip6_dst));
return(FALSE); /* XXX: can we discard it? */
}
#ifdef notyet
if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
inner_grp.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
reg_src, reg_dst);
else
inner_grp.sin6_scope_id = 0;
#endif
inner_grp.sin6_scope_id = 0;
mrtentry_ptr = find_route(&inner_src, &inner_grp,
MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
{
/* No routing entry. Send REGISTER_STOP and return. */
IF_DEBUG(DEBUG_PIM_REGISTER)
log(LOG_DEBUG, 0,
"No routing entry for source %s and/or group %s",
inet6_fmt(&inner_src.sin6_addr), inet6_fmt(&inner_grp.sin6_addr));
/* TODO: XXX: shouldn't be inner_src=IN6ADDR_ANY? Not in the spec. */
send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
return (TRUE);
}
/* XXX: not in the spec: check if I am the RP for that group */
if (!inet6_equal(&my_cand_rp_address, reg_dst)
|| (check_mrtentry_rp(mrtentry_ptr, &my_cand_rp_address) == FALSE))
{
send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
return (TRUE);
}
if (mrtentry_ptr->flags & MRTF_SG)
{
/* (S,G) found */
/* TODO: check the timer again */
SET_TIMER(mrtentry_ptr->timer, pim_data_timeout); /* restart timer */
if (!(mrtentry_ptr->flags & MRTF_SPT))
{
/* The SPT bit is not set */
if (!nullRegisterBit)
{
calc_oifs(mrtentry_ptr, &oifs);
if (IF_ISEMPTY(&oifs)
&& (mrtentry_ptr->incoming == reg_vif_num))
{
send_pim6_register_stop(reg_dst, reg_src, &inner_grp,
&inner_src);
return (TRUE);
}
/*
* TODO: XXX: BUG!!! The data will be forwarded by the kernel
* MFC!!! Need to set a special flag for this routing entry
* so after a cache miss occur, the multicast packet will be
* forwarded from user space and won't install entry in the
* kernel MFC. The problem is that the kernel MFC doesn't
* know the PMBR address and simply sets the multicast
* forwarding cache to accept/forward all data coming from
* the register_vif.
*/
if (borderBit)
{
if (!inet6_equal(&mrtentry_ptr->pmbr_addr,reg_src))
{
send_pim6_register_stop(reg_dst, reg_src,
&inner_grp, &inner_src);
return (TRUE);
}
}
return (TRUE);
}
/* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..? */
return (TRUE);
}
else
{
/* The SPT bit is set */
send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
return (TRUE);
}
}
if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))
{
if (borderBit)
{
/*
* Create (S,G) state. The oifs will be the copied from the
* existing (*,G) or (*,*,RP) entry.
*/
mrtentry_ptr2 = find_route(&inner_src, &inner_grp, MRTF_SG,
CREATE);
if (mrtentry_ptr2 != (mrtentry_t *) NULL)
{
mrtentry_ptr2->pmbr_addr = *reg_src;
/* Clear the SPT flag */
mrtentry_ptr2->flags &= ~(MRTF_SPT | MRTF_NEW);
SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
/* TODO: explicitly call the Join/Prune send function? */
FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Send the Join
* immediately */
/*
* TODO: explicitly call this function?
* send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
* mrtentry_ptr2->upstream, pim_join_prune_holdtime);
*/
}
}
}
if (mrtentry_ptr->flags & MRTF_WC)
{
/* (*,G) entry */
calc_oifs(mrtentry_ptr, &oifs);
if (IF_ISEMPTY(&oifs))
{
send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &sockaddr6_any);
return (FALSE);
}
/* XXX: TODO: check with the spec again */
else
{
if (!nullRegisterBit)
{
/* Install cache entry in the kernel */
/*
* TODO: XXX: probably redundant here, because the
* decapsulated mcast packet in the kernel will result in
* CACHE_MISS
*/
struct sockaddr_in6 *mfc_source = &inner_src;
#ifdef KERNEL_MFC_WC_G
if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
mfc_source = NULL;
#endif /* KERNEL_MFC_WC_G */
add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
&mrtentry_ptr->group->rpaddr);
return (TRUE);
}
}
return (TRUE);
}
if (mrtentry_ptr->flags & MRTF_PMBR)
{
/* (*,*,RP) entry */
if (!nullRegisterBit)
{
struct sockaddr_in6 *mfc_source = &inner_src;
/*
* XXX: have to create either (S,G) or (*,G). The choice below is
* (*,G)
*/
mrtentry_ptr2 = find_route(NULL, &inner_grp, MRTF_WC,
CREATE);
if (mrtentry_ptr2 == (mrtentry_t *) NULL)
return (FALSE);
if (mrtentry_ptr2->flags & MRTF_NEW)
{
/* TODO: something else? Have the feeling sth is missing */
mrtentry_ptr2->flags &= ~MRTF_NEW;
/* TODO: XXX: copy the timer from the (*,*,RP) entry? */
COPY_TIMER(mrtentry_ptr->timer, mrtentry_ptr2->timer);
}
/* Install cache entry in the kernel */
#ifdef KERNEL_MFC_WC_G
if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG))
mfc_source = NULL;
#endif /* KERNEL_MFC_WC_G */
add_kernel_cache(mrtentry_ptr, mfc_source, &inner_grp, 0);
k_chg_mfc(mld6_socket, mfc_source, &inner_grp,
mrtentry_ptr->incoming, &mrtentry_ptr->oifs,
&mrtentry_ptr2->group->rpaddr);
return (TRUE);
}
}
/* Shoudn't happen: invalid routing entry? */
/* XXX: TODO: shoudn't be inner_src=IN6ADDR_ANY? Not in the spec. */
send_pim6_register_stop(reg_dst, reg_src, &inner_grp, &inner_src);
return (TRUE);
}
int
send_pim6_register(pkt)
char *pkt;
{
register struct ip6_hdr *ip6;
static struct sockaddr_in6 source= {sizeof(source) , AF_INET6 };
static struct sockaddr_in6 group= {sizeof(group) , AF_INET6 };
mifi_t mifi;
rpentry_t *rpentry_ptr;
mrtentry_t *mrtentry_ptr;
mrtentry_t *mrtentry_ptr2;
struct sockaddr_in6 *reg_src,
*reg_dst;
int pktlen = 0;
char *buf;
ip6=(struct ip6_hdr *)pkt;
group.sin6_addr = ip6->ip6_dst;
source.sin6_addr = ip6->ip6_src;
if ((mifi = find_vif_direct_local(&source)) == NO_VIF)
return (FALSE);
if (!(uvifs[mifi].uv_flags & VIFF_DR))
return (FALSE); /* I am not the DR for that subnet */
rpentry_ptr = rp_match(&group);
if (rpentry_ptr == (rpentry_t *) NULL)
return (FALSE); /* No RP for this group */
if (local_address(&rpentry_ptr->address) != NO_VIF)
/* TODO: XXX: not sure it is working! */
return (FALSE); /* I am the RP for this group */
mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
return (FALSE); /* Cannot create (S,G) state */
if (mrtentry_ptr->flags & MRTF_NEW)
{
/* A new entry */
mrtentry_ptr->flags &= ~MRTF_NEW;
RESET_TIMER(mrtentry_ptr->rs_timer); /* Reset the
* Register-Suppression timer */
if ((mrtentry_ptr2 = mrtentry_ptr->group->grp_route) ==
(mrtentry_t *) NULL)
mrtentry_ptr2 =
mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
if (mrtentry_ptr2 != (mrtentry_t *) NULL)
{
FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Timeout the
* Join/Prune timer */
/*
* TODO: explicitly call this function?
* send_pim6_join_prune(mrtentry_ptr2->upstream->vifi,
* mrtentry_ptr2->upstream, pim_join_prune_holdtime);
*/
}
}
/* Restart the (S,G) Entry-timer */
SET_TIMER(mrtentry_ptr->timer, pim_data_timeout);
IF_TIMER_NOT_SET(mrtentry_ptr->rs_timer)
{
/*
* The Register-Suppression Timer is not running. Encapsulate the
* data and send to the RP.
*/
buf = pim6_send_buf + sizeof(struct pim);
bzero(buf, sizeof(pim_register_t)); /* No flags set */
buf += sizeof(pim_register_t);
/* Copy the data packet at the back of the register packet */
/* TODO: check pktlen. ntohs? */
pktlen = ntohs(ip6->ip6_plen);
pktlen +=sizeof(struct ip6_hdr); /* XXX */
bcopy((char *) ip6, buf, pktlen);
pktlen += sizeof(pim_register_t);
reg_src = max_global_address();
reg_dst = &mrtentry_ptr->group->rpaddr;
send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER,
pktlen);
pim6dstat.out_pim6_register++;
return (TRUE);
}
return (TRUE);
}
int
send_pim6_null_register(mrtentry_ptr)
mrtentry_t *mrtentry_ptr;
{
struct ip6_hdr *ip;
pim_register_t *pim_register;
int pktlen = 0;
mifi_t mifi;
struct sockaddr_in6 *reg_source,
*dest;
/* No directly connected source; no local address */
if ((mifi = find_vif_direct_local(&mrtentry_ptr->source->address)) == NO_VIF)
return (FALSE);
pim_register = (pim_register_t *) (pim6_send_buf + sizeof(struct pim));
bzero((char *) pim_register, sizeof(pim_register_t));
pim_register->reg_flags = htonl(pim_register->reg_flags
| PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT);
/* include the dummy ip header */
ip = (struct ip6_hdr *) (pim_register +1);
ip->ip6_plen= 0;
ip->ip6_flow=0;
ip->ip6_vfc = 0x60;
ip->ip6_hlim = MINHLIM;
ip->ip6_nxt = IPPROTO_NONE;
ip->ip6_src = mrtentry_ptr->source->address.sin6_addr;
ip->ip6_dst = mrtentry_ptr->group->group.sin6_addr;
pktlen = sizeof(pim_register_t) + sizeof(struct ip6_hdr);
dest = &mrtentry_ptr->group->rpaddr;
reg_source = max_global_address();
send_pim6(pim6_send_buf, reg_source , dest, PIM_REGISTER,
pktlen);
pim6dstat.out_pim6_register++; /* should be counted separately? */
return (TRUE);
}
/************************************************************************
* PIM_REGISTER_STOP
************************************************************************/
int
receive_pim6_register_stop(reg_src, reg_dst, pim_message, datalen)
struct sockaddr_in6 *reg_src,
*reg_dst;
char *pim_message;
register int datalen;
{
pim_register_stop_t *pim_regstop_p;
pim6_encod_grp_addr_t encod_grp;
pim6_encod_uni_addr_t encod_unisrc;
struct sockaddr_in6 source,
group;
u_int8 *data_ptr;
mrtentry_t *mrtentry_ptr;
if_set pruned_oifs;
mifi_t mifi;
struct uvif *v;
pim6dstat.in_pim6_register_stop++;
pim_regstop_p = (pim_register_stop_t *) (pim_message +
sizeof(struct pim));
data_ptr = (u_int8 *) & pim_regstop_p->encod_grp;
GET_EGADDR6(&encod_grp, data_ptr);
GET_EUADDR6(&encod_unisrc, data_ptr);
group.sin6_addr = encod_grp.mcast_addr;
/* scope validation of the inner source and destination addresses */
#ifdef notyet
if (IN6_IS_ADDR_MC_SITELOCAL(&ip->ip6_dst))
group.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
reg_src, reg_dst);
else
#endif
group.sin6_scope_id = 0;
source.sin6_addr = encod_unisrc.unicast_addr;
/* the source address must be global...but is it always true? */
#ifdef notyet
if (IN6_IS_ADDR_SITELOCAL)
source.sin6_scope_id = addr2scopeid(&ip->ip6_src, &ip->ip6_dst,
reg_src, reg_dst);
else
#endif
source.sin6_scope_id = 0;
if((mifi= find_vif_direct_local(&source))==NO_VIF)
{
IF_DEBUG(DEBUG_PIM_REGISTER)
{
log(LOG_WARNING,0,
"Received PIM_REGISTER_STOP from RP %s for a non "
"direct-connect source %s",
inet6_fmt(&reg_src->sin6_addr),
inet6_fmt(&encod_unisrc.unicast_addr));
}
return FALSE;
}
v=&uvifs[mifi];
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
IF_DEBUG(DEBUG_PIM_REGISTER)
{
log(LOG_DEBUG, 0,
"Received PIM_REGISTER_STOP from RP %s to %s "
"source : %s group : %s",
inet6_fmt(&reg_src->sin6_addr),
inet6_fmt(&reg_dst->sin6_addr),
inet6_fmt(&encod_unisrc.unicast_addr),
inet6_fmt(&encod_grp.mcast_addr));
}
/* TODO: apply the group mask and do register_stop for all grp addresses */
/* TODO: check for SourceAddress == 0 */
mrtentry_ptr = find_route(&source, &group,
MRTF_SG, DONT_CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
{
return (FALSE);
}
/*
* XXX: not in the spec: check if the PIM_REGISTER_STOP originator is
* really the RP
*/
if (check_mrtentry_rp(mrtentry_ptr, reg_src) == FALSE)
{
return (FALSE);
}
/* restart the Register-Suppression timer */
SET_TIMER(mrtentry_ptr->rs_timer, (0.5 * pim_register_suppression_timeout)
+ (RANDOM() % (pim_register_suppression_timeout + 1)));
/* Prune the register_vif from the outgoing list */
IF_COPY(&mrtentry_ptr->pruned_oifs, &pruned_oifs);
IF_SET(reg_vif_num, &pruned_oifs);
change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs, &pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
return (TRUE);
}
/* TODO: optional rate limiting is not implemented yet */
/* Unicasts a REGISTER_STOP message to the DR */
static int
send_pim6_register_stop(reg_src, reg_dst, inner_grp, inner_src)
struct sockaddr_in6 *reg_src,
*reg_dst,
*inner_grp,
*inner_src;
{
char *buf;
u_int8 *data_ptr;
buf = pim6_send_buf + sizeof(struct pim);
data_ptr = (u_int8 *) buf;
PUT_EGADDR6(inner_grp->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
PUT_EUADDR6(inner_src->sin6_addr, data_ptr);
send_pim6(pim6_send_buf, reg_src , reg_dst , PIM_REGISTER_STOP,
data_ptr-(u_int8 *) buf);
pim6dstat.out_pim6_register_stop++;
return (TRUE);
}
/************************************************************************
* PIM_JOIN_PRUNE
************************************************************************/
int
join_or_prune(mrtentry_ptr, upstream_router)
mrtentry_t *mrtentry_ptr;
pim_nbr_entry_t *upstream_router;
{
if_set entry_oifs;
mrtentry_t *mrtentry_grp;
if ((mrtentry_ptr == (mrtentry_t *) NULL))
{
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"Join_or_prune : mrtentry_ptr is null");
return (PIM_ACTION_NOTHING);
}
if( upstream_router == (pim_nbr_entry_t *) NULL)
{
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"Join_or_prune : upstream_router is null");
return (PIM_ACTION_NOTHING);
}
calc_oifs(mrtentry_ptr, &entry_oifs);
if (mrtentry_ptr->flags & (MRTF_PMBR | MRTF_WC))
{
/* (*,*,RP) or (*,G) entry */
/* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */
if (upstream_router != mrtentry_ptr->upstream)
return (PIM_ACTION_NOTHING);
/* TODO: XXX: Can we have (*,*,RP) prune? */
if (IF_ISEMPTY(&entry_oifs))
{
/* NULL oifs */
if (!(uvifs[mrtentry_ptr->incoming].uv_flags & VIFF_DR))
{
/* I am not the DR for that subnet. */
return (PIM_ACTION_PRUNE);
}
if (IF_ISSET(mrtentry_ptr->incoming, &mrtentry_ptr->leaves))
/* I am the DR and have local leaves */
return (PIM_ACTION_JOIN);
/* Probably the last local member hast timeout */
return (PIM_ACTION_PRUNE);
}
return (PIM_ACTION_JOIN);
}
if (mrtentry_ptr->flags & MRTF_SG)
{
/* (S,G) entry */
/* TODO: check again */
if (mrtentry_ptr->upstream == upstream_router)
{
if (!(mrtentry_ptr->flags & MRTF_RP))
{
/* Upstream router toward S */
if (IF_ISEMPTY(&entry_oifs))
{
if (mrtentry_ptr->group->active_rp_grp != (rp_grp_entry_t *)NULL &&
inet6_equal(&mrtentry_ptr->group->rpaddr,
&my_cand_rp_address))
{
/*
* (S,G) at the RP. Don't send Join/Prune (see the
* end of Section 3.3.2)
*/
return (PIM_ACTION_NOTHING);
}
return (PIM_ACTION_PRUNE);
}
else
return (PIM_ACTION_JOIN);
}
else
{
/* Upstream router toward RP */
if (IF_ISEMPTY(&entry_oifs))
return (PIM_ACTION_PRUNE);
}
}
/*
* Looks like the case when the upstream router toward S is different
* from the upstream router toward RP
*/
if (mrtentry_ptr->group->active_rp_grp == (rp_grp_entry_t *) NULL)
return (PIM_ACTION_NOTHING);
mrtentry_grp = mrtentry_ptr->group->grp_route;
if (mrtentry_grp == (mrtentry_t *) NULL)
mrtentry_grp =
mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
if (mrtentry_grp == (mrtentry_t *) NULL)
return (PIM_ACTION_NOTHING);
if (mrtentry_grp->upstream != upstream_router)
return (PIM_ACTION_NOTHING); /* XXX: shoudn't happen */
if ((!(mrtentry_ptr->flags & MRTF_RP))
&& (mrtentry_ptr->flags & MRTF_SPT))
{
return (PIM_ACTION_PRUNE);
}
}
return (PIM_ACTION_NOTHING);
}
/* TODO: too long, simplify it! */
#define PIM6_JOIN_PRUNE_MINLEN (4 + PIM6_ENCODE_UNI_ADDR_LEN + 4)
int
receive_pim6_join_prune(src, dst, pim_message, datalen)
struct sockaddr_in6 *src,
*dst;
char *pim_message;
register int datalen;
{
mifi_t mifi;
struct uvif *v;
pim6_encod_uni_addr_t uni_target_addr;
pim6_encod_grp_addr_t encod_group;
pim6_encod_src_addr_t encod_src;
u_int8 *data_ptr;
u_int8 *data_ptr_start;
u_int8 num_groups;
u_int8 num_groups_tmp;
int star_star_rp_found;
u_int16 holdtime;
u_int16 num_j_srcs;
u_int16 num_j_srcs_tmp;
u_int16 num_p_srcs;
struct sockaddr_in6 source;
struct sockaddr_in6 group;
struct sockaddr_in6 target;
struct in6_addr s_mask;
struct in6_addr g_mask;
u_int8 s_flags;
u_int8 reserved;
rpentry_t *rpentry_ptr;
mrtentry_t *mrtentry_ptr;
mrtentry_t *mrtentry_srcs;
mrtentry_t *mrtentry_rp;
grpentry_t *grpentry_ptr;
u_int16 jp_value;
pim_nbr_entry_t *upstream_router;
int my_action;
int ignore_group;
rp_grp_entry_t *rp_grp_entry_ptr;
u_int8 *data_ptr_group_j_start;
u_int8 *data_ptr_group_p_start;
if ((mifi = find_vif_direct(src)) == NO_VIF)
{
/*
* Either a local vif or somehow received PIM_JOIN_PRUNE from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
log(LOG_INFO, 0,
"Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
inet6_fmt(&src->sin6_addr));
return (FALSE);
}
v = &uvifs[mifi];
v->uv_in_pim6_join_prune++;
if (uvifs[mifi].uv_flags &
(VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
{
return (FALSE); /* Shoudn't come on this interface */
}
/* sanity check for the minimum length */
if (datalen < PIM6_JOIN_PRUNE_MINLEN) {
log(LOG_NOTICE, 0,
"receive_pim6_join_prune: Join/Prune message size(%u) is"
" too short from %s on %s",
datalen, inet6_fmt(&src->sin6_addr), v->uv_name);
return(FALSE);
}
datalen -= PIM6_JOIN_PRUNE_MINLEN;
data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
/* Get the target address */
GET_EUADDR6(&uni_target_addr, data_ptr);
GET_BYTE(reserved, data_ptr);
GET_BYTE(num_groups, data_ptr);
if (num_groups == 0)
return (FALSE); /* No indication for groups in the message */
GET_HOSTSHORT(holdtime, data_ptr);
target.sin6_len = sizeof(target);
target.sin6_family = AF_INET6;
target.sin6_addr = uni_target_addr.unicast_addr;
target.sin6_scope_id = inet6_uvif2scopeid(&target, v);
/* Sanity check for the message length through all the groups */
num_groups_tmp = num_groups;
data_ptr_start = data_ptr;
while (num_groups_tmp--) {
int srclen;
/* group addr + #join + #src */
if (datalen < PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t)) {
log(LOG_NOTICE, 0,
"receive_pim6_join_prune: Join/Prune message from %s on %s is"
" too short to contain enough data",
inet6_fmt(&src->sin6_addr), v->uv_name);
return(FALSE);
}
datalen -= (PIM6_ENCODE_GRP_ADDR_LEN + sizeof(u_int32_t));
data_ptr += PIM6_ENCODE_GRP_ADDR_LEN;
/* joined source addresses and pruned source addresses */
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
srclen = (num_j_srcs + num_p_srcs) * PIM6_ENCODE_SRC_ADDR_LEN;
if (datalen < srclen) {
log(LOG_NOTICE, 0,
"receive_pim6_join_prune: Join/Prune message from %s on %s is"
" too short to contain enough data",
inet6_fmt(&src->sin6_addr), v->uv_name);
return(FALSE);
}
datalen -= srclen;
data_ptr += srclen;
}
data_ptr = data_ptr_start;
num_groups_tmp = num_groups;
if (!inet6_localif_address(&target, v) &&
!IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr))
{
/* if I am not the targer of the join message */
/*
* Join/Prune suppression code. This either modifies the J/P timers
* or triggers an overriding Join.
*/
/*
* Note that if we have (S,G) prune and (*,G) Join, we must send them
* in the same message. We don't bother to modify both timers here.
* The Join/Prune sending function will take care of that.
*/
upstream_router = find_pim6_nbr(&target);
if (upstream_router == (pim_nbr_entry_t *) NULL)
return (FALSE); /* I have no such neighbor */
group.sin6_len = sizeof(group);
group.sin6_family = AF_INET6;
source.sin6_len = sizeof(source);
source.sin6_family = AF_INET6;
while (num_groups--)
{
GET_EGADDR6(&encod_group, data_ptr);
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
if (encod_group.masklen > (sizeof(struct in6_addr) << 3))
continue;
MASKLEN_TO_MASK6(encod_group.masklen, g_mask);
group.sin6_addr = encod_group.mcast_addr;
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr))
{
data_ptr +=
(num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
continue; /* Ignore this group and jump to the next */
}
if (inet6_equal(&group,&sockaddr6_d) &&
(encod_src.masklen == STAR_STAR_RP_MSK6LEN))
{
/* (*,*,RP) Join suppression */
while (num_j_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
/* sanity checks */
if (!inet6_valid_host(&source))
continue;
if (encod_src.masklen >
(sizeof(struct in6_addr) << 3))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) &&
(s_flags & USADDR_WC_BIT))
{
/* This is the RP address. */
rpentry_ptr = rp_find(&source);
if (rpentry_ptr == (rpentry_t *) NULL)
continue; /* Don't have such RP. Ignore */
mrtentry_rp = rpentry_ptr->mrtlink;
my_action = join_or_prune(mrtentry_rp,
upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_rp->jp_timer > holdtime)
continue;
if ((mrtentry_rp->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
continue;
/*
* Set the Join/Prune suppression timer for this
* routing entry by increasing the current Join/Prune
* timer.
*/
jp_value = pim_join_prune_period +
0.5 * (RANDOM() % pim_join_prune_period);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_rp->jp_timer < jp_value)
SET_TIMER(mrtentry_rp->jp_timer, jp_value);
}
} /* num_j_srcs */
while (num_p_srcs--)
{
/*
* TODO: XXX: Can we have (*,*,RP) prune message? Not in
* the spec, but anyway, the code below can handle them:
* either suppress the local (*,*,RP) prunes or override
* the prunes by sending (*,*,RP) and/or (*,G) and/or
* (S,G) Join.
*/
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
if (!inet6_valid_host(&source))
continue;
if (encod_src.masklen >
(sizeof(struct in6_addr) << 3))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) &&
(s_flags & USADDR_WC_BIT))
{
/* This is the RP address. */
rpentry_ptr = rp_find(&source);
if (rpentry_ptr == (rpentry_t *) NULL)
continue; /* Don't have such RP. Ignore */
mrtentry_rp = rpentry_ptr->mrtlink;
my_action = join_or_prune(mrtentry_rp,
upstream_router);
if (my_action == PIM_ACTION_PRUNE)
{
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_rp->jp_timer < holdtime)
|| ((mrtentry_rp->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
{
/* Suppress the Prune */
jp_value = pim_join_prune_period+
0.5 * (RANDOM() % pim_join_prune_period);
if (mrtentry_rp->jp_timer < jp_value)
SET_TIMER(mrtentry_rp->jp_timer, jp_value);
}
}
else
if (my_action == PIM_ACTION_JOIN)
{
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_rp->jp_timer > jp_value)
SET_TIMER(mrtentry_rp->jp_timer, jp_value);
}
/*
* Check all (*,G) and (S,G) matching to this RP. If
* my_action == JOIN, then send a Join and override
* the (*,*,RP) Prune.
*/
for (grpentry_ptr =
rpentry_ptr->cand_rp->rp_grp_next->grplink;
grpentry_ptr != (grpentry_t *) NULL;
grpentry_ptr = grpentry_ptr->rpnext)
{
my_action = join_or_prune(grpentry_ptr->grp_route,
upstream_router);
if (my_action == PIM_ACTION_JOIN)
{
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (grpentry_ptr->grp_route->jp_timer >
jp_value)
SET_TIMER(grpentry_ptr->grp_route->jp_timer, jp_value);
}
for (mrtentry_srcs = grpentry_ptr->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
{
my_action = join_or_prune(mrtentry_srcs,
upstream_router);
if (my_action == PIM_ACTION_JOIN)
{
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_srcs->jp_timer > jp_value)
SET_TIMER(mrtentry_srcs->jp_timer, jp_value);
}
} /* For all (S,G) */
} /* For all (*,G) */
}
} /* num_p_srcs */
continue; /* This was (*,*,RP) suppression */
}
/* (*,G) or (S,G) suppression */
/*
* TODO: XXX: currently, accumulated groups (i.e. group_masklen <
* group_address_lengt) are not implemented. Just need to create
* a loop and apply the procedure below for all groups matching
* the prefix.
*/
while (num_j_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,v);
if (!inet6_valid_host(&source))
continue;
if (encod_src.masklen >
(sizeof(struct in6_addr) << 3))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
{
/* (*,G) JOIN_REQUEST (toward the RP) */
mrtentry_ptr = find_route(&sockaddr6_any , &group, MRTF_WC,
DONT_CREATE);
my_action = join_or_prune(mrtentry_ptr, upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* (*,G) Join suppresion */
if (!inet6_equal(&source,&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address))
continue; /* The RP address doesn't match.
* Ignore. */
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->jp_timer > holdtime)
continue;
if ((mrtentry_ptr->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
continue;
jp_value = pim_join_prune_period +
0.5 * (RANDOM() % pim_join_prune_period);
if (mrtentry_ptr->jp_timer < jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
continue;
} /* End of (*,G) Join suppression */
/* (S,G) Join suppresion */
mrtentry_ptr = find_route(&source, &group, MRTF_SG,
DONT_CREATE);
my_action = join_or_prune(mrtentry_ptr, upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->jp_timer > holdtime)
continue;
if ((mrtentry_ptr->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr)))
continue;
jp_value = pim_join_prune_period +
0.5 * (RANDOM() % pim_join_prune_period);
if (mrtentry_ptr->jp_timer < jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
continue;
}
/* Prunes suppression */
while (num_p_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
if (encod_src.masklen >
(sizeof(struct in6_addr) << 3))
continue;
if (!inet6_valid_host(&source))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
{
/* (*,G) prune suppression */
rpentry_ptr = rp_match(&source);
if ((rpentry_ptr == (rpentry_t *) NULL)
|| (!inet6_equal(&rpentry_ptr->address , &source)))
continue; /* No such RP or it is different.
* Ignore */
mrtentry_ptr = find_route(&sockaddr6_any, &group, MRTF_WC,
DONT_CREATE);
my_action = join_or_prune(mrtentry_ptr, upstream_router);
if (my_action == PIM_ACTION_PRUNE)
{
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr->jp_timer < holdtime)
|| ((mrtentry_ptr->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
{
/* Suppress the Prune */
jp_value = pim_join_prune_period +
0.5 * (RANDOM() % pim_join_prune_period);
if (mrtentry_ptr->jp_timer < jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
}
}
else
if (my_action == PIM_ACTION_JOIN)
{
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->jp_timer > jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
}
/*
* Check all (S,G) entries for this group. If my_action
* == JOIN, then send the Join and override the (*,G)
* Prune.
*/
for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
{
my_action = join_or_prune(mrtentry_srcs,
upstream_router);
if (my_action == PIM_ACTION_JOIN)
{
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->jp_timer > jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
}
} /* For all (S,G) */
continue; /* End of (*,G) prune suppression */
}
/* (S,G) prune suppression */
mrtentry_ptr = find_route(&source, &group, MRTF_SG,
DONT_CREATE);
my_action = join_or_prune(mrtentry_ptr, upstream_router);
if (my_action == PIM_ACTION_PRUNE)
{
/* Suppress the (S,G) Prune */
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr->jp_timer < holdtime)
|| ((mrtentry_ptr->jp_timer == holdtime)
&& (inet6_greaterthan(src, &v->uv_linklocal->pa_addr))))
{
jp_value = pim_join_prune_period +
0.5 * (RANDOM() % pim_join_prune_period);
if (mrtentry_ptr->jp_timer < jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
}
}
else
if (my_action == PIM_ACTION_JOIN)
{
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % 11) / (10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->jp_timer > jp_value)
SET_TIMER(mrtentry_ptr->jp_timer, jp_value);
}
} /* while (num_p_srcs--) */
} /* while (num_groups--) */
return (TRUE);
} /* End of Join/Prune suppression code */
/* I am the target of this join, so process the message */
/*
* The spec says that if there is (*,G) Join, it has priority over old
* existing ~(S,G) prunes in the routing table. However, if the (*,G)
* Join and the ~(S,G) prune are in the same message, ~(S,G) has the
* priority. The spec doesn't say it, but I think the same is true for
* (*,*,RP) and ~(S,G) prunes.
*
* The code below do: (1) Check the whole message for (*,*,RP) Joins. (1.1)
* If found, clean all pruned_oifs for all (*,G) and all (S,G) for each
* RP in the list, but do not update the kernel cache. Then go back to
* the beginning of the message and start processing for each group: (2)
* Check for Prunes. If no prunes, process the Joins. (3) If there are
* Prunes: (3.1) Scan the Join part for existing (*,G) Join. (3.1.1) If
* there is (*,G) Join, clear join interface from the pruned_oifs for all
* (S,G), but DO NOT flush the change to the kernel (by using
* change_interfaces() for example) (3.2) After the pruned_oifs are
* eventually cleared in (3.1.1), process the Prune part of the message
* normally (setting the prune_oifs and flashing the changes to the
* (kernel). (3.3) After the Prune part is processed, process the Join
* part normally (by applying any changes to the kernel) (4) If there
* were (*,*,RP) Join/Prune, process them.
*
* If the Join/Prune list is too long, it may result in long processing
* overhead. The idea above is not to place any wrong info in the kernel,
* because it may result in short-time existing traffic forwarding on
* wrong interface. Hopefully, in the future will find a better way to
* implement it.
*/
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"I'm the target of the JOIN/PRUNE message");
num_groups_tmp = num_groups;
data_ptr_start = data_ptr;
star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"Number of groups to process : %d",num_groups_tmp);
while (num_groups_tmp--)
{
/* Search for (*,*,RP) Join */
GET_EGADDR6(&encod_group, data_ptr);
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
group.sin6_addr = encod_group.mcast_addr;
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
{
log(LOG_DEBUG, 0,
"Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
log(LOG_DEBUG, 0,
"Number of join : %d",num_j_srcs );
log(LOG_DEBUG, 0,
"Number of prune : %d",num_p_srcs );
}
if (!(inet6_equal(&group,&sockaddr6_d))
|| (encod_src.masklen != STAR_STAR_RP_MSK6LEN))
{
/* This is not (*,*,RP). Jump to the next group. */
data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
{
log(LOG_DEBUG, 0,
"I'm looking for the (*,*,RP) entry , skip to next entry");
}
continue;
}
/*
* (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear
* the pruned oif, but do not update the kernel.
*/
star_star_rp_found = TRUE;
while (num_j_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
rpentry_ptr = rp_find(&source);
if (rpentry_ptr == (rpentry_t *) NULL)
continue;
for (rp_grp_entry_ptr = rpentry_ptr->cand_rp->rp_grp_next;
rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
{
for (grpentry_ptr = rp_grp_entry_ptr->grplink;
grpentry_ptr != (grpentry_t *) NULL;
grpentry_ptr = grpentry_ptr->rpnext)
{
if (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
IF_CLR(mifi, &grpentry_ptr->grp_route->pruned_oifs);
for (mrtentry_ptr = grpentry_ptr->mrtlink;
mrtentry_ptr != (mrtentry_t *) NULL;
mrtentry_ptr = mrtentry_ptr->grpnext)
IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
}
}
}
data_ptr += (num_p_srcs) * sizeof(pim6_encod_src_addr_t);
}
/*
* Start processing the groups. If this is (*,*,RP), skip it, but process
* it at the end.don't forget to reinit data_ptr!
*/
data_ptr = data_ptr_start;
num_groups_tmp = num_groups;
while (num_groups_tmp--)
{
GET_EGADDR6(&encod_group, data_ptr);
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
group.sin6_addr = encod_group.mcast_addr;
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
{
log(LOG_DEBUG,0,"Group to process : %s",inet6_fmt(&encod_group.mcast_addr));
log(LOG_DEBUG,0,"Number of join : %d",num_j_srcs );
log(LOG_DEBUG,0,"Number of prune : %d",num_p_srcs );
}
if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr))
{
data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
continue; /* Ignore this group and jump to the next one */
}
if (inet6_equal(&group, &sockaddr6_d)
&& (encod_group.masklen == STAR_STAR_RP_MSK6LEN))
{
/* This is (*,*,RP). Jump to the next group. */
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) {
log(LOG_DEBUG, 0, "This is (*,*,RP). Jump to next.");
}
data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
continue;
}
rpentry_ptr = rp_match(&group);
if (rpentry_ptr == (rpentry_t *) NULL)
continue;
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"The rp for this JOIN/PRUNE is %s",inet6_fmt(&rpentry_ptr->address.sin6_addr));
data_ptr_group_j_start = data_ptr;
data_ptr_group_p_start = data_ptr + num_j_srcs * sizeof(pim6_encod_src_addr_t);
/*
* Scan the Join part for (*,G) Join and then clear the particular
* interface from pruned_oifs for all (S,G). If the RP address in the
* Join message is different from the local match, ignore the whole
* group.
*/
num_j_srcs_tmp = num_j_srcs;
ignore_group = FALSE;
while (num_j_srcs_tmp--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr=encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,v);
if ((encod_src.flags & USADDR_RP_BIT)
&& (encod_src.flags & USADDR_WC_BIT))
{
/*
* This is the RP address, i.e. (*,G) Join. Check if the
* RP-mapping is consistent and if "yes", then Reset the
* pruned_oifs for all (S,G) entries.
*/
if(!inet6_equal(&rpentry_ptr->address, &source))
{
ignore_group = TRUE;
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
log(LOG_DEBUG,0,"And I'm not the RP for this address");
break;
}
mrtentry_ptr = find_route(&sockaddr6_any, &group,
MRTF_WC, DONT_CREATE);
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
IF_CLR(mifi, &mrtentry_srcs->pruned_oifs);
}
break;
}
}
if (ignore_group == TRUE)
continue;
data_ptr = data_ptr_group_p_start;
/* Process the Prune part first */
while (num_p_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
if (!inet6_valid_host(&source))
continue;
s_flags = encod_src.flags;
if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
{
/* (S,G) prune sent toward S */
mrtentry_ptr = find_route(&source, &group, MRTF_SG,
DONT_CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue; /* I don't have (S,G) to prune. Ignore. */
/*
* If the link is point-to-point, timeout the oif
* immediately, otherwise decrease the timer to allow other
* downstream routers to override the prune.
*/
/* TODO: XXX: increase the entry timer? */
if (v->uv_flags & VIFF_POINT_TO_POINT)
{
FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
}
else
{
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] >
mrtentry_ptr->vif_deletion_delay[mifi])
SET_TIMER(mrtentry_ptr->vif_timers[mifi],
mrtentry_ptr->vif_deletion_delay[mifi]);
}
IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
{
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
continue;
}
if ((s_flags & USADDR_RP_BIT)
&& (!(s_flags & USADDR_WC_BIT)))
{
/* ~(S,G)RPbit prune sent toward the RP */
mrtentry_ptr = find_route(&source, &group, MRTF_SG,
DONT_CREATE);
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
SET_TIMER(mrtentry_ptr->timer, holdtime);
if (v->uv_flags & VIFF_POINT_TO_POINT)
{
FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
}
else
{
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] >
mrtentry_ptr->vif_deletion_delay[mifi])
SET_TIMER(mrtentry_ptr->vif_timers[mifi],
mrtentry_ptr->vif_deletion_delay[mifi]);
}
IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
{
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
continue;
}
/* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */
mrtentry_ptr = find_route(NULL, &group,
MRTF_WC | MRTF_PMBR,
DONT_CREATE);
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
mrtentry_ptr = find_route(&source, &group,
MRTF_SG | MRTF_RP,
CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
mrtentry_ptr->flags &= ~MRTF_NEW;
RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
/*
* TODO: XXX: The spec doens't say what value to use for
* the entry time. Use the J/P holdtime.
*/
SET_TIMER(mrtentry_ptr->timer, holdtime);
/*
* TODO: XXX: The spec says to delete the oif. However,
* its timer only should be lowered, so the prune can be
* overwritten on multiaccess LAN. Spec BUG.
*/
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
continue;
}
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT))
{
/* (*,G) Prune */
mrtentry_ptr = find_route(NULL, &group,
MRTF_WC | MRTF_PMBR,
DONT_CREATE);
if (mrtentry_ptr != (mrtentry_t *) NULL)
{
if (mrtentry_ptr->flags & MRTF_WC)
{
/*
* TODO: XXX: Should check the whole Prune list in
* advance for (*,G) prune and if the RP address does
* not match the local RP-map, then ignore the whole
* group, not only this particular (*,G) prune.
*/
if (!inet6_equal(&mrtentry_ptr->group->active_rp_grp->rp->rpentry->address, &source ))
continue; /* The RP address doesn't match. */
if (v->uv_flags & VIFF_POINT_TO_POINT)
{
FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
}
else
{
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] >
mrtentry_ptr->vif_deletion_delay[mifi])
SET_TIMER(mrtentry_ptr->vif_timers[mifi],
mrtentry_ptr->vif_deletion_delay[mifi]);
}
IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
{
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
continue;
}
/* No (*,G) entry, but found (*,*,RP). Create (*,G) */
if (!inet6_equal(&mrtentry_ptr->source->address, &source))
continue; /* The RP address doesn't match. */
mrtentry_ptr = find_route(NULL, &group,
MRTF_WC, CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
mrtentry_ptr->flags &= ~MRTF_NEW;
RESET_TIMER(mrtentry_ptr->vif_timers[mifi]);
/*
* TODO: XXX: should only lower the oif timer, so it can
* be overwritten on multiaccess LAN. Spec bug.
*/
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
} /* (*,G) or (*,*,RP) found */
} /* (*,G) prune */
} /* while(num_p_srcs--) */
/* End of (S,G) and (*,G) Prune handling */
/* Jump back to the Join part and process it */
data_ptr = data_ptr_group_j_start;
while (num_j_srcs--)
{
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
if (!inet6_valid_host(&source))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
if ((s_flags & USADDR_WC_BIT)
&& (s_flags & USADDR_RP_BIT))
{
/* (*,G) Join toward RP */
/*
* It has been checked already that this RP address is the
* same as the local RP-maping.
*/
mrtentry_ptr = find_route(NULL, &group, MRTF_WC,
CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
IF_SET(mifi, &mrtentry_ptr->joined_oifs);
IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] < holdtime)
{
SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
}
if (mrtentry_ptr->timer < holdtime)
SET_TIMER(mrtentry_ptr->timer, holdtime);
mrtentry_ptr->flags &= ~MRTF_NEW;
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
/*
* Need to update the (S,G) entries, because of the previous
* cleaning of the pruned_oifs. The reason is that if the
* oifs for (*,G) weren't changed, the (S,G) entries won't be
* updated by change_interfaces()
*/
for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
change_interfaces(mrtentry_srcs,
mrtentry_srcs->incoming,
&mrtentry_srcs->joined_oifs,
&mrtentry_srcs->pruned_oifs,
&mrtentry_srcs->leaves,
&mrtentry_srcs->asserted_oifs, 0);
continue;
}
if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT)))
{
/* (S,G) Join toward S */
if (mifi == get_iif(&source))
continue; /* Ignore this (S,G) Join */
mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
IF_SET(mifi, &mrtentry_ptr->joined_oifs);
IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] < holdtime)
{
SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
}
if (mrtentry_ptr->timer < holdtime)
SET_TIMER(mrtentry_ptr->timer, holdtime);
/*
* TODO: if this is a new entry, send immediately the Join
* message toward S. The Join/Prune timer for new entries is
* 0, but it does not means the message will be sent
* immediately.
*/
mrtentry_ptr->flags &= ~MRTF_NEW;
/*
* Note that we must create (S,G) without the RPbit set. If
* we already had such entry, change_interfaces() will reset
* the RPbit propertly.
*/
change_interfaces(mrtentry_ptr,
mrtentry_ptr->source->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
continue;
}
} /* while(num_j_srcs--) */
} /* for all groups */
/* Now process the (*,*,RP) Join/Prune */
if (star_star_rp_found == TRUE)
return (TRUE);
data_ptr = data_ptr_start;
while (num_groups--)
{
/*
* The conservative approach is to scan again the whole message, just
* in case if we have more than one (*,*,RP) requests.
*/
GET_EGADDR6(&encod_group, data_ptr);
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
group.sin6_addr = encod_group.mcast_addr;
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
if (!inet6_equal(&group,&sockaddr6_d)
|| (encod_group.masklen != STAR_STAR_RP_MSK6LEN))
{
/* This is not (*,*,RP). Jump to the next group. */
data_ptr +=
(num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t);
continue;
}
/* (*,*,RP) found */
while (num_j_srcs--)
{
/* TODO: XXX: check that the iif is different from the Join oifs */
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
if (!inet6_valid_host(&source))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
mrtentry_ptr = find_route(&source, NULL, MRTF_PMBR,
CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
IF_SET(mifi, &mrtentry_ptr->joined_oifs);
IF_CLR(mifi, &mrtentry_ptr->pruned_oifs);
IF_CLR(mifi, &mrtentry_ptr->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] < holdtime)
{
SET_TIMER(mrtentry_ptr->vif_timers[mifi], holdtime);
mrtentry_ptr->vif_deletion_delay[mifi] = holdtime / 3;
}
if (mrtentry_ptr->timer < holdtime)
SET_TIMER(mrtentry_ptr->timer, holdtime);
mrtentry_ptr->flags &= ~MRTF_NEW;
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
/*
* Need to update the (S,G) and (*,G) entries, because of the
* previous cleaning of the pruned_oifs. The reason is that if
* the oifs for (*,*,RP) weren't changed, the (*,G) and (S,G)
* entries won't be updated by change_interfaces()
*/
for (rp_grp_entry_ptr = mrtentry_ptr->source->cand_rp->rp_grp_next;
rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
for (grpentry_ptr = rp_grp_entry_ptr->grplink;
grpentry_ptr != (grpentry_t *) NULL;
grpentry_ptr = grpentry_ptr->rpnext)
{
/* Update the (*,G) entry */
change_interfaces(grpentry_ptr->grp_route,
grpentry_ptr->grp_route->incoming,
&grpentry_ptr->grp_route->joined_oifs,
&grpentry_ptr->grp_route->pruned_oifs,
&grpentry_ptr->grp_route->leaves,
&grpentry_ptr->grp_route->asserted_oifs, 0);
/* Update the (S,G) entries */
for (mrtentry_srcs = grpentry_ptr->mrtlink;
mrtentry_srcs != (mrtentry_t *) NULL;
mrtentry_srcs = mrtentry_srcs->grpnext)
change_interfaces(mrtentry_srcs,
mrtentry_srcs->incoming,
&mrtentry_srcs->joined_oifs,
&mrtentry_srcs->pruned_oifs,
&mrtentry_srcs->leaves,
&mrtentry_srcs->asserted_oifs, 0);
}
continue;
}
while (num_p_srcs--)
{
/* TODO: XXX: can we have (*,*,RP) Prune? */
GET_ESADDR6(&encod_src, data_ptr);
source.sin6_addr = encod_src.src_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source,
v);
if (!inet6_valid_host(&source))
continue;
s_flags = encod_src.flags;
MASKLEN_TO_MASK6(encod_src.masklen, s_mask);
mrtentry_ptr = find_route(&source, NULL , MRTF_PMBR,
DONT_CREATE);
if (mrtentry_ptr == (mrtentry_t *) NULL)
continue;
/*
* If the link is point-to-point, timeout the oif immediately,
* otherwise decrease the timer to allow other downstream routers
* to override the prune.
*/
/* TODO: XXX: increase the entry timer? */
if (v->uv_flags & VIFF_POINT_TO_POINT)
{
FIRE_TIMER(mrtentry_ptr->vif_timers[mifi]);
}
else
{
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->vif_timers[mifi] >
mrtentry_ptr->vif_deletion_delay[mifi])
SET_TIMER(mrtentry_ptr->vif_timers[mifi],
mrtentry_ptr->vif_deletion_delay[mifi]);
}
IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[mifi])
{
IF_CLR(mifi, &mrtentry_ptr->joined_oifs);
IF_SET(mifi, &mrtentry_ptr->pruned_oifs);
IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
}
}
} /* For all groups processing (*,*,R) */
return (TRUE);
}
/*
* TODO: NOT USED, probably buggy, but may need it in the future.
*/
/*
* TODO: create two functions: periodic which timeout the timers and
* non-periodic which only check but don't timeout the timers.
*/
/*
* Create and send Join/Prune messages per interface. Only the entries which
* have the Join/Prune timer expired are included. In the special case when
* we have ~(S,G)RPbit Prune entry, we must include any (*,G) or (*,*,RP)
* Currently the whole table is scanned. In the future will have all routing
* entries linked in a chain with the corresponding upstream pim_nbr_entry.
*
* If pim_nbr is not NULL, then send to only this particular PIM neighbor,
*/
int
send_periodic_pim6_join_prune(mifi, pim_nbr, holdtime)
mifi_t mifi;
pim_nbr_entry_t *pim_nbr;
u_int16 holdtime;
{
grpentry_t *grpentry_ptr;
mrtentry_t *mrtentry_ptr;
rpentry_t *rpentry_ptr;
struct sockaddr_in6 src_addr;
struct uvif *v;
pim_nbr_entry_t *pim_nbr_ptr;
cand_rp_t *cand_rp_ptr;
/*
* Walk through all routing entries. The iif must match to include the
* entry. Check first the (*,G) entry and then all associated (S,G). At
* the end of the message will add any (*,*,RP) entries. TODO: check
* other PIM-SM implementations and decide the more appropriate place to
* put the (*,*,RP) entries: in the beginning of the message or at the
* end.
*/
v = &uvifs[mifi];
/* Check the (*,G) and (S,G) entries */
for (grpentry_ptr = grplist; grpentry_ptr != (grpentry_t *) NULL;
grpentry_ptr = grpentry_ptr->next)
{
mrtentry_ptr = grpentry_ptr->grp_route;
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr != (mrtentry_t *) NULL)
&& (mrtentry_ptr->incoming == mifi)
&& (mrtentry_ptr->jp_timer <= timer_interval))
{
/* If join/prune to a particular neighbor only was specified */
if ((pim_nbr != (pim_nbr_entry_t *) NULL)
&& (mrtentry_ptr->upstream != pim_nbr))
continue;
/* TODO: XXX: The J/P suppression timer is not in the spec! */
if (!IF_ISEMPTY(&mrtentry_ptr->joined_oifs) ||
(v->uv_flags & VIFF_DR))
{
add_jp_entry(mrtentry_ptr->upstream, holdtime,
&grpentry_ptr->group,
SINGLE_GRP_MSK6LEN,
&grpentry_ptr->rpaddr,
SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
}
/* TODO: XXX: TIMER implem. dependency! */
if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
&& (!(v->uv_flags & VIFF_DR))
&& (mrtentry_ptr->jp_timer <= timer_interval))
{
add_jp_entry(mrtentry_ptr->upstream, holdtime,
&grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
&grpentry_ptr->rpaddr,
SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
}
}
/* Check the (S,G) entries */
for (mrtentry_ptr = grpentry_ptr->mrtlink;
mrtentry_ptr != (mrtentry_t *) NULL;
mrtentry_ptr = mrtentry_ptr->grpnext)
{
/* If join/prune to a particular neighbor only was specified */
if ((pim_nbr != (pim_nbr_entry_t *) NULL)
&& (mrtentry_ptr->upstream != pim_nbr))
continue;
if (mrtentry_ptr->flags & MRTF_RP)
{
/* RPbit set */
src_addr = mrtentry_ptr->source->address;
if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs)
|| ((find_vif_direct_local(&src_addr) != NO_VIF)
&& grpentry_ptr->grp_route != (mrtentry_t *) NULL))
/* TODO: XXX: TIMER implem. dependency! */
if ((grpentry_ptr->grp_route->incoming == mifi)
&& (grpentry_ptr->grp_route->jp_timer
<= timer_interval))
/* S is directly connected. Send toward RP */
add_jp_entry(grpentry_ptr->grp_route->upstream,
holdtime,
&grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
&src_addr, SINGLE_SRC_MSK6LEN,
MRTF_RP, PIM_ACTION_PRUNE);
}
else
{
/* RPbit cleared */
if (IF_ISEMPTY(&mrtentry_ptr->joined_oifs))
{
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr->incoming == mifi)
&& (mrtentry_ptr->jp_timer <= timer_interval))
add_jp_entry(mrtentry_ptr->upstream, holdtime,
&grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
&mrtentry_ptr->source->address,
SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_PRUNE);
}
else
{
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr->incoming == mifi)
&& (mrtentry_ptr->jp_timer <= timer_interval))
add_jp_entry(mrtentry_ptr->upstream, holdtime,
&grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
&mrtentry_ptr->source->address,
SINGLE_SRC_MSK6LEN, 0, PIM_ACTION_JOIN);
}
/* TODO: XXX: TIMER implem. dependency! */
if ((mrtentry_ptr->flags & MRTF_SPT)
&& (grpentry_ptr->grp_route != (mrtentry_t *) NULL)
&& (mrtentry_ptr->incoming !=
grpentry_ptr->grp_route->incoming)
&& (grpentry_ptr->grp_route->incoming == mifi)
&& (grpentry_ptr->grp_route->jp_timer
<= timer_interval))
add_jp_entry(grpentry_ptr->grp_route->upstream, holdtime,
&grpentry_ptr->group, SINGLE_GRP_MSK6LEN,
&mrtentry_ptr->source->address,
SINGLE_SRC_MSK6LEN, MRTF_RP,
PIM_ACTION_PRUNE);
}
}
}
/* Check the (*,*,RP) entries */
for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
cand_rp_ptr = cand_rp_ptr->next)
{
rpentry_ptr = cand_rp_ptr->rpentry;
/* If join/prune to a particular neighbor only was specified */
if ((pim_nbr != (pim_nbr_entry_t *) NULL)
&& (rpentry_ptr->upstream != pim_nbr))
continue;
/* TODO: XXX: TIMER implem. dependency! */
if ((rpentry_ptr->mrtlink != (mrtentry_t *) NULL)
&& (rpentry_ptr->incoming == mifi)
&& (rpentry_ptr->mrtlink->jp_timer <= timer_interval))
{
add_jp_entry(rpentry_ptr->upstream, holdtime,
&sockaddr6_d, STAR_STAR_RP_MSK6LEN,
&rpentry_ptr->address,
SINGLE_SRC_MSK6LEN, MRTF_RP | MRTF_WC,
PIM_ACTION_JOIN);
}
}
/* Send all pending Join/Prune messages */
for (pim_nbr_ptr = v->uv_pim_neighbors;
pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
pim_nbr_ptr = pim_nbr->next)
{
/* If join/prune to a particular neighbor only was specified */
if ((pim_nbr != (pim_nbr_entry_t *) NULL)
&& (pim_nbr_ptr != pim_nbr))
continue;
pack_and_send_jp6_message(pim_nbr_ptr);
}
return (TRUE);
}
int
add_jp_entry(pim_nbr, holdtime, group, grp_msklen, source, src_msklen,
addr_flags, join_prune)
pim_nbr_entry_t *pim_nbr;
u_int16 holdtime;
struct sockaddr_in6 *group;
u_int8 grp_msklen;
struct sockaddr_in6 *source;
u_int8 src_msklen;
u_int16 addr_flags;
u_int8 join_prune;
{
build_jp_message_t *bjpm;
u_int8 *data_ptr;
u_int8 flags = 0;
int rp_flag;
bjpm = pim_nbr->build_jp_message;
if (bjpm != (build_jp_message_t *) NULL)
{
if ((bjpm->jp_message_size + bjpm->join_list_size +
bjpm->prune_list_size + bjpm->rp_list_join_size +
bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE)
|| (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE)
|| (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE)
|| (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE)
|| (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE))
{
/*
* TODO: XXX: BUG: If the list is getting too large, must be
* careful with the fragmentation.
*/
pack_and_send_jp6_message(pim_nbr);
bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
}
}
if (bjpm != (build_jp_message_t *) NULL)
{
if ((!inet6_equal(&bjpm->curr_group, group)
|| (bjpm->curr_group_msklen != grp_msklen)
|| (bjpm->holdtime != holdtime)))
{
pack_jp6_message(pim_nbr);
}
}
if (bjpm == (build_jp_message_t *) NULL)
{
bjpm = get_jp6_working_buff();
pim_nbr->build_jp_message = bjpm;
data_ptr = bjpm->jp_message;
PUT_EUADDR6(pim_nbr->address.sin6_addr, data_ptr);
PUT_BYTE(0, data_ptr); /* Reserved */
bjpm->num_groups_ptr = data_ptr++; /* The pointer for numgroups */
*(bjpm->num_groups_ptr) = 0; /* Zero groups */
PUT_HOSTSHORT(holdtime, data_ptr);
bjpm->holdtime = holdtime;
bjpm->jp_message_size = data_ptr - bjpm->jp_message;
}
/* TODO: move somewhere else, only when it is a new group */
bjpm->curr_group = *group;
bjpm->curr_group_msklen = grp_msklen;
if (inet6_equal(group, &sockaddr6_d) &&
(grp_msklen == STAR_STAR_RP_MSK6LEN))
rp_flag = TRUE;
else
rp_flag = FALSE;
switch (join_prune)
{
case PIM_ACTION_JOIN:
if (rp_flag == TRUE)
data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
else
data_ptr = bjpm->join_list + bjpm->join_list_size;
break;
case PIM_ACTION_PRUNE:
if (rp_flag == TRUE)
data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size;
else
data_ptr = bjpm->prune_list + bjpm->prune_list_size;
break;
default:
return (FALSE);
}
flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */
if (addr_flags & MRTF_RP)
flags |= USADDR_RP_BIT;
if (addr_flags & MRTF_WC)
flags |= USADDR_WC_BIT;
PUT_ESADDR6(source->sin6_addr, src_msklen, flags, data_ptr);
switch (join_prune)
{
case PIM_ACTION_JOIN:
if (rp_flag == TRUE)
{
bjpm->rp_list_join_size = data_ptr - bjpm->rp_list_join;
bjpm->rp_list_join_number++;
}
else
{
bjpm->join_list_size = data_ptr - bjpm->join_list;
bjpm->join_addr_number++;
}
break;
case PIM_ACTION_PRUNE:
if (rp_flag == TRUE)
{
bjpm->rp_list_prune_size = data_ptr - bjpm->rp_list_prune;
bjpm->rp_list_prune_number++;
}
else
{
bjpm->prune_list_size = data_ptr - bjpm->prune_list;
bjpm->prune_addr_number++;
}
break;
default:
return (FALSE);
}
return (TRUE);
}
/* TODO: check again the size of the buffers */
static build_jp_message_t *
get_jp6_working_buff()
{
build_jp_message_t *bjpm_ptr;
if (build_jp_message_pool_counter == 0)
{
bjpm_ptr = (build_jp_message_t *) malloc(sizeof(build_jp_message_t));
bjpm_ptr->next = (build_jp_message_t *) NULL;
bjpm_ptr->jp_message =
(u_int8 *) malloc(MAX_JP_MESSAGE_SIZE +
sizeof(pim_jp_encod_grp_t) +
2 * sizeof(pim6_encod_src_addr_t));
bjpm_ptr->jp_message_size = 0;
bjpm_ptr->join_list_size = 0;
bjpm_ptr->join_addr_number = 0;
bjpm_ptr->join_list = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
sizeof(pim6_encod_src_addr_t));
bjpm_ptr->prune_list_size = 0;
bjpm_ptr->prune_addr_number = 0;
bjpm_ptr->prune_list = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
sizeof(pim6_encod_src_addr_t));
bjpm_ptr->rp_list_join_size = 0;
bjpm_ptr->rp_list_join_number = 0;
bjpm_ptr->rp_list_join = (u_int8 *) malloc(MAX_JOIN_LIST_SIZE +
sizeof(pim6_encod_src_addr_t));
bjpm_ptr->rp_list_prune_size = 0;
bjpm_ptr->rp_list_prune_number = 0;
bjpm_ptr->rp_list_prune = (u_int8 *) malloc(MAX_PRUNE_LIST_SIZE +
sizeof(pim6_encod_src_addr_t));
bjpm_ptr->curr_group = sockaddr6_any;
bjpm_ptr->curr_group_msklen = 0;
bjpm_ptr->holdtime = 0;
return bjpm_ptr;
}
else
{
bjpm_ptr = build_jp_message_pool;
build_jp_message_pool = build_jp_message_pool->next;
build_jp_message_pool_counter--;
bjpm_ptr->jp_message_size = 0;
bjpm_ptr->join_list_size = 0;
bjpm_ptr->join_addr_number = 0;
bjpm_ptr->prune_list_size = 0;
bjpm_ptr->prune_addr_number = 0;
bjpm_ptr->curr_group = sockaddr6_any;
bjpm_ptr->curr_group_msklen = 0;
return (bjpm_ptr);
}
}
static void
return_jp6_working_buff(pim_nbr)
pim_nbr_entry_t *pim_nbr;
{
build_jp_message_t *bjpm_ptr = pim_nbr->build_jp_message;
if (bjpm_ptr == (build_jp_message_t *) NULL)
return;
/* Don't waste memory by keeping too many free buffers */
/* TODO: check/modify the definitions for POOL_NUMBER and size */
if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER)
{
free((void *) bjpm_ptr->jp_message);
free((void *) bjpm_ptr->join_list);
free((void *) bjpm_ptr->prune_list);
free((void *) bjpm_ptr->rp_list_join);
free((void *) bjpm_ptr->rp_list_prune);
free((void *) bjpm_ptr);
}
else
{
bjpm_ptr->next = build_jp_message_pool;
build_jp_message_pool = bjpm_ptr;
build_jp_message_pool_counter++;
}
pim_nbr->build_jp_message = (build_jp_message_t *) NULL;
}
/*
* TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the Join/Prune
* message. However, this particular implementation of PIM processes the
* Join/Prune messages faster if (*,*,RP) is at the beginning. Modify some of
* the functions below such that the outgoing messages place (*,*,RP) at the
* beginning, not at the end.
*/
static void
pack_jp6_message(pim_nbr)
pim_nbr_entry_t *pim_nbr;
{
build_jp_message_t *bjpm;
u_int8 *data_ptr;
bjpm = pim_nbr->build_jp_message;
if ((bjpm == (build_jp_message_t *) NULL)
|| (inet6_equal(&bjpm->curr_group,&sockaddr6_any)))
return;
data_ptr = bjpm->jp_message + bjpm->jp_message_size;
PUT_EGADDR6(bjpm->curr_group.sin6_addr, bjpm->curr_group_msklen, 0, data_ptr);
PUT_HOSTSHORT(bjpm->join_addr_number, data_ptr);
PUT_HOSTSHORT(bjpm->prune_addr_number, data_ptr);
bcopy(bjpm->join_list, data_ptr, bjpm->join_list_size);
data_ptr += bjpm->join_list_size;
bcopy(bjpm->prune_list, data_ptr, bjpm->prune_list_size);
data_ptr += bjpm->prune_list_size;
bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
bjpm->join_list_size = 0;
bjpm->join_addr_number = 0;
#if 0 /* isn't this necessary? */
bjpm->rp_list_join_size = 0;
bjpm->rp_list_join_number = 0;
#endif
bjpm->prune_list_size = 0;
bjpm->prune_addr_number = 0;
#if 0 /* isn't this necessary? */
bjpm->rp_list_prune_size = 0;
bjpm->rp_list_prune_number = 0;
#endif
(*bjpm->num_groups_ptr)++;
bjpm->curr_group = sockaddr6_any;
bjpm->curr_group_msklen = 0;
if (*bjpm->num_groups_ptr == ((u_int8) ~ 0 - 1))
{
if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
{
/* Add the (*,*,RP) at the end */
data_ptr = bjpm->jp_message + bjpm->jp_message_size;
PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
data_ptr += bjpm->rp_list_join_size;
bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
data_ptr += bjpm->rp_list_prune_size;
bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
bjpm->rp_list_join_size = 0;
bjpm->rp_list_join_number = 0;
bjpm->rp_list_prune_size = 0;
bjpm->rp_list_prune_number = 0;
(*bjpm->num_groups_ptr)++;
}
send_jp6_message(pim_nbr);
}
}
void
pack_and_send_jp6_message(pim_nbr)
pim_nbr_entry_t *pim_nbr;
{
u_int8 *data_ptr;
build_jp_message_t *bjpm;
if ((pim_nbr == (pim_nbr_entry_t *) NULL)
|| ((bjpm = pim_nbr->build_jp_message) == (build_jp_message_t *) NULL))
{
return;
}
pack_jp6_message(pim_nbr);
if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number)
{
/* Add the (*,*,RP) at the end */
data_ptr = bjpm->jp_message + bjpm->jp_message_size;
PUT_EGADDR6(sockaddr6_d.sin6_addr, STAR_STAR_RP_MSK6LEN, 0, data_ptr);
PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr);
PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr);
bcopy(bjpm->rp_list_join, data_ptr, bjpm->rp_list_join_size);
data_ptr += bjpm->rp_list_join_size;
bcopy(bjpm->rp_list_prune, data_ptr, bjpm->rp_list_prune_size);
data_ptr += bjpm->rp_list_prune_size;
bjpm->jp_message_size = (data_ptr - bjpm->jp_message);
bjpm->rp_list_join_size = 0;
bjpm->rp_list_join_number = 0;
bjpm->rp_list_prune_size = 0;
bjpm->rp_list_prune_number = 0;
(*bjpm->num_groups_ptr)++;
}
send_jp6_message(pim_nbr);
}
static void
send_jp6_message(pim_nbr)
pim_nbr_entry_t *pim_nbr;
{
u_int16 datalen;
mifi_t mifi;
datalen = pim_nbr->build_jp_message->jp_message_size;
mifi = pim_nbr->vifi;
bcopy(pim_nbr->build_jp_message->jp_message,
pim6_send_buf+sizeof(struct pim), datalen);
send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
&allpim6routers_group , PIM_JOIN_PRUNE, datalen);
uvifs[mifi].uv_out_pim6_join_prune++;
return_jp6_working_buff(pim_nbr);
}
/************************************************************************
* PIM_ASSERT
************************************************************************/
int
receive_pim6_assert(src, dst, pim_message, datalen)
struct sockaddr_in6 *src,
*dst;
register char *pim_message;
int datalen;
{
mifi_t mifi;
pim6_encod_uni_addr_t eusaddr;
pim6_encod_grp_addr_t egaddr;
struct sockaddr_in6 source,
group;
mrtentry_t *mrtentry_ptr,
*mrtentry_ptr2;
u_int8 *data_ptr;
struct uvif *v;
u_int32 assert_preference;
u_int32 assert_metric;
u_int32 assert_rptbit;
u_int32 local_metric;
u_int32 local_preference;
u_int8 local_rptbit;
u_int8 local_wins;
pim_nbr_entry_t *original_upstream_router;
if ((mifi = find_vif_direct(src)) == NO_VIF)
{
/*
* Either a local vif or somehow received PIM_ASSERT from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
log(LOG_INFO, 0,
"Ignoring PIM_ASSERT from non-neighbor router %s",
inet6_fmt(&src->sin6_addr));
return (FALSE);
}
v = &uvifs[mifi];
v->uv_in_pim6_assert++;
if (uvifs[mifi].uv_flags &
(VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | MIFF_REGISTER))
return (FALSE); /* Shoudn't come on this interface */
data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
/* Get the group and source addresses */
GET_EGADDR6(&egaddr, data_ptr);
GET_EUADDR6(&eusaddr, data_ptr);
/* Get the metric related info */
GET_HOSTLONG(assert_preference, data_ptr);
GET_HOSTLONG(assert_metric, data_ptr);
assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT;
source.sin6_addr = eusaddr.unicast_addr;
source.sin6_scope_id = inet6_uvif2scopeid(&source, v);
group.sin6_addr = egaddr.mcast_addr;
group.sin6_scope_id = inet6_uvif2scopeid(&group, v);
/* Find the longest "active" entry, i.e. the one with a kernel mirror */
if (assert_rptbit)
{
mrtentry_ptr = find_route(NULL, &group,
MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrtentry_ptr != (mrtentry_t *) NULL)
if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
if (mrtentry_ptr->flags & MRTF_WC)
{
mrtentry_ptr =
mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
}
}
else
{
mrtentry_ptr = find_route(&source, &group,
MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
if ((mrtentry_ptr != (mrtentry_t *) NULL))
if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE))
{
if (mrtentry_ptr->flags & MRTF_SG)
{
mrtentry_ptr2 = mrtentry_ptr->group->grp_route;
if ((mrtentry_ptr2 != (mrtentry_t *) NULL)
&& (mrtentry_ptr2->flags & MRTF_KERNEL_CACHE))
mrtentry_ptr = mrtentry_ptr2;
else
mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
}
else
if (mrtentry_ptr->flags & MRTF_WC)
mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
}
}
if ((mrtentry_ptr == (mrtentry_t *) NULL)
|| (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)))
/* No routing entry or not "active" entry. Ignore the assert */
return (FALSE);
/* Prepare the local preference and metric */
if ((mrtentry_ptr->flags & MRTF_PMBR)
|| ((mrtentry_ptr->flags & MRTF_SG)
&& (!(mrtentry_ptr->flags & MRTF_RP))))
{
/* Either (S,G) (toward S) or (*,*,RP). */
/* TODO: XXX: get the info from mrtentry, or source or from kernel ? */
/*
* local_metric = mrtentry_ptr->source->metric; local_preference =
* mrtentry_ptr->source->preference;
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
}
else
{
/*
* Should be (*,G) or (S,G)RPbit entry. Get what we need from the RP
* info.
*/
/* TODO: get the info from mrtentry, RP-entry or kernel? */
/*
* local_metric =
* mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
* local_preference =
* mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference;
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
}
local_rptbit = (mrtentry_ptr->flags & MRTF_RP);
if (local_rptbit)
/* Make the RPT bit the most significant one */
local_preference |= PIM_ASSERT_RPT_BIT;
if (IF_ISSET(mifi, &mrtentry_ptr->oifs))
{
/* The ASSERT has arrived on oif */
/*
* TODO: XXX: here the processing order is different from the spec.
* The spec requires first eventually to create a routing entry (see
* 3.5.2.1(1) and then compare the metrics. Here we compare first the
* metrics with the existing longest match entry and if we lose then
* create a new entry and compare again. This saves us the
* unnecessary creating of a routing entry if we anyway are going to
* lose: for example the local (*,*,RP) vs the remote (*,*,RP) or
* (*,G)
*/
local_wins = compare_metrics(local_preference, local_metric,
&v->uv_linklocal->pa_addr, assert_preference,
assert_metric, src);
if (local_wins == TRUE)
{
/* TODO: verify the parameters */
send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
return (TRUE);
}
/* Create a "better" routing entry and try again */
if ((assert_rptbit) && (mrtentry_ptr->flags & MRTF_PMBR))
{
/* The matching entry was (*,*,RP). Create (*,G) */
mrtentry_ptr2 = find_route(NULL, &group, MRTF_WC, CREATE);
}
else
if ((!assert_rptbit) &&
(mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR)))
{
/* create (S,G) */
mrtentry_ptr2 = find_route(&source, &group, MRTF_SG, CREATE);
}
else
{
/* We have no chance to win. Give up and prune the oif */
mrtentry_ptr2 = (mrtentry_t *) NULL;
}
if (mrtentry_ptr2 != (mrtentry_t *) NULL)
{
mrtentry_ptr2->flags &= ~MRTF_NEW;
/*
* TODO: XXX: The spec doesn't say what entry timer value to use
* when the routing entry is created because of asserts.
*/
SET_TIMER(mrtentry_ptr2->timer, pim_data_timeout);
if (mrtentry_ptr2->flags & MRTF_RP)
{
/*
* Either (*,G) or (S,G)RPbit entry. Get what we need from
* the RP info.
*/
/* TODO: where to get the metric+preference from? */
/*
* local_metric =
* mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric;
* local_preference =
* mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference
* ;
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
local_preference |= PIM_ASSERT_RPT_BIT;
}
else
{
/* (S,G) toward the source */
/* TODO: where to get the metric from ? */
/*
* local_metric = mrtentry_ptr->source->metric;
* local_preference = mrtentry_ptr->source->preference;
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
}
local_wins = compare_metrics(local_preference, local_metric,
&v->uv_linklocal->pa_addr, assert_preference,
assert_metric, src);
if (local_wins == TRUE)
{
/* TODO: verify the parameters */
send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
return (TRUE);
}
/* We lost, but have created the entry which has to be pruned */
mrtentry_ptr = mrtentry_ptr2;
}
/* Have to remove that outgoing vifi from mrtentry_ptr */
IF_SET(mifi, &mrtentry_ptr->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrtentry_ptr->timer < pim_assert_timeout)
SET_TIMER(mrtentry_ptr->timer, pim_assert_timeout);
/*
* TODO: XXX: check that the timer of all affected routing entries
* has been restarted.
*/
change_interfaces(mrtentry_ptr,
mrtentry_ptr->incoming,
&mrtentry_ptr->joined_oifs,
&mrtentry_ptr->pruned_oifs,
&mrtentry_ptr->leaves,
&mrtentry_ptr->asserted_oifs, 0);
return (FALSE); /* Doesn't matter the return value */
} /* End of assert received on oif */
if (mrtentry_ptr->incoming == mifi)
{
/* Assert received on iif */
if (assert_rptbit)
{
if (!(mrtentry_ptr->flags & MRTF_RP))
return (TRUE); /* The locally used upstream router will win
* the assert, so don't change it. */
}
/*
* TODO: where to get the local metric and preference from? system
* call or mrtentry is fine?
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
if (mrtentry_ptr->flags & MRTF_RP)
local_preference |= PIM_ASSERT_RPT_BIT;
local_wins = compare_metrics(local_preference, local_metric,
&mrtentry_ptr->upstream->address,
assert_preference, assert_metric, src);
if (local_wins == TRUE)
return (TRUE); /* return whatever */
/* The upstream must be changed to the winner */
mrtentry_ptr->preference = assert_preference;
mrtentry_ptr->metric = assert_metric;
mrtentry_ptr->upstream = find_pim6_nbr(src);
/* Check if the upstream router is different from the original one */
if (mrtentry_ptr->flags & MRTF_PMBR)
original_upstream_router = mrtentry_ptr->source->upstream;
else
if (mrtentry_ptr->flags & MRTF_RP)
original_upstream_router =
mrtentry_ptr->group->active_rp_grp->rp->rpentry->upstream;
else
original_upstream_router = mrtentry_ptr->source->upstream;
if (mrtentry_ptr->upstream != original_upstream_router)
{
mrtentry_ptr->flags |= MRTF_ASSERTED;
SET_TIMER(mrtentry_ptr->assert_timer, pim_assert_timeout);
}
else
mrtentry_ptr->flags &= ~MRTF_ASSERTED;
}
return (TRUE);
}
int
send_pim6_assert(source, group, mifi, mrtentry_ptr)
struct sockaddr_in6 *source;
struct sockaddr_in6 *group;
mifi_t mifi;
mrtentry_t *mrtentry_ptr;
{
u_int8 *data_ptr;
u_int8 *data_start_ptr;
u_int32 local_preference;
u_int32 local_metric;
srcentry_t *srcentry_ptr;
/* Don't send assert if the outgoing interface a tunnel or register vif */
if (uvifs[mifi].uv_flags & (MIFF_REGISTER | VIFF_TUNNEL))
return (FALSE);
data_ptr = (u_int8 *) (pim6_send_buf + sizeof(struct pim));
data_start_ptr = data_ptr;
PUT_EGADDR6(group->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr);
PUT_EUADDR6(source->sin6_addr, data_ptr);
/*
* TODO: XXX: where to get the metric from: srcentry_ptr or mrtentry_ptr
* or from the kernel?
*/
if (mrtentry_ptr->flags & MRTF_PMBR)
{
/* (*,*,RP) */
srcentry_ptr = mrtentry_ptr->source;
/*
* TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
*/
}
else
if (mrtentry_ptr->flags & MRTF_RP)
{
/* (*,G) or (S,G)RPbit (iif toward RP) */
srcentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry;
/*
* TODO: set_incoming(srcentry_ptr, PIM_IIF_RP);
*/
}
else
{
/* (S,G) toward S */
srcentry_ptr = mrtentry_ptr->source;
/*
* TODO: set_incoming(srcentry_ptr, PIM_IIF_SOURCE);
*/
}
/*
* TODO: check again! local_metric = srcentry_ptr->metric;
* local_preference = srcentry_ptr->preference;
*/
local_metric = mrtentry_ptr->metric;
local_preference = mrtentry_ptr->preference;
if (mrtentry_ptr->flags & MRTF_RP)
local_preference |= PIM_ASSERT_RPT_BIT;
PUT_HOSTLONG(local_preference, data_ptr);
PUT_HOSTLONG(local_metric, data_ptr);
send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
&allpim6routers_group, PIM_ASSERT,
data_ptr - data_start_ptr);
uvifs[mifi].uv_out_pim6_assert++;
return (TRUE);
}
/* Return TRUE if the local win, otherwise FALSE */
static int
compare_metrics(local_preference, local_metric, local_address,
remote_preference, remote_metric, remote_address)
u_int32 local_preference;
u_int32 local_metric;
struct sockaddr_in6 *local_address;
u_int32 remote_preference;
u_int32 remote_metric;
struct sockaddr_in6 *remote_address;
{
/* Now lets see who has a smaller gun (aka "asserts war") */
/*
* FYI, the smaller gun...err metric wins, but if the same caliber, then
* the bigger network address wins. The order of threatment is:
* preference, metric, address.
*/
/*
* The RPT bits are already included as the most significant bits of the
* preferences.
*/
if (remote_preference > local_preference)
return TRUE;
if (remote_preference < local_preference)
return FALSE;
if (remote_metric > local_metric)
return TRUE;
if (remote_metric < local_metric)
return FALSE;
if (inet6_greaterthan(local_address, remote_address))
return TRUE;
return FALSE;
}
/************************************************************************
* PIM_BOOTSTRAP
************************************************************************/
#define PIM6_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
int
receive_pim6_bootstrap(src, dst, pim_message, datalen)
struct sockaddr_in6 *src,
*dst;
char *pim_message;
int datalen;
{
u_int8 *data_ptr;
u_int8 *max_data_ptr;
u_int16 new_bsr_fragment_tag;
u_int8 new_bsr_hash_masklen;
u_int8 new_bsr_priority;
pim6_encod_uni_addr_t new_bsr_uni_addr;
struct sockaddr_in6 new_bsr_address;
struct rpfctl rpfc;
pim_nbr_entry_t *n,
*rpf_neighbor;
struct sockaddr_in6 neighbor_addr;
mifi_t mifi,
incoming = NO_VIF;
int min_datalen;
pim6_encod_grp_addr_t curr_group_addr;
pim6_encod_uni_addr_t curr_rp_addr;
u_int8 curr_rp_count;
u_int8 curr_frag_rp_count;
u_int16 reserved_short;
u_int16 curr_rp_holdtime;
u_int8 curr_rp_priority;
u_int8 reserved_byte;
struct in6_addr curr_group_mask;
grp_mask_t *grp_mask_ptr;
grp_mask_t *grp_mask_next;
rp_grp_entry_t *grp_rp_entry_ptr;
rp_grp_entry_t *grp_rp_entry_next;
struct sockaddr_in6 prefix_h,
prefix_h2,
group_,
rpp_;
int i;
struct uvif *v;
if ((mifi=find_vif_direct(src)) == NO_VIF)
{
/*
* Either a local vif or somehow received PIM_BOOTSTRAP from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
log(LOG_INFO, 0,
"Ignoring PIM_BOOTSTRAP from non-neighbor router %s",
inet6_fmt(&src->sin6_addr));
return (FALSE);
}
/* sanity check for the minimum length */
if (datalen < PIM6_BOOTSTRAP_MINLEN) {
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: Bootstrap message size(%u) is"
" too short from %s",
datalen, inet6_fmt(&src->sin6_addr));
return(FALSE);
}
v = &uvifs[mifi];
v->uv_in_pim6_bootsrap++;
data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
/* Parse the PIM_BOOTSTRAP message */
GET_HOSTSHORT(new_bsr_fragment_tag, data_ptr);
GET_BYTE(new_bsr_hash_masklen, data_ptr);
GET_BYTE(new_bsr_priority, data_ptr);
GET_EUADDR6(&new_bsr_uni_addr, data_ptr);
/*
* BSR address must be a global unicast address.
* [draft-ietf-pim-ipv6-01.txt sec 4.5]
*/
if (IN6_IS_ADDR_MULTICAST(&new_bsr_uni_addr.unicast_addr) ||
IN6_IS_ADDR_LINKLOCAL(&new_bsr_uni_addr.unicast_addr) ||
IN6_IS_ADDR_SITELOCAL(&new_bsr_uni_addr.unicast_addr)) {
log(LOG_WARNING, 0,
"receive_pim6_bootstrap: invalid BSR address: %s",
inet6_fmt(&new_bsr_uni_addr.unicast_addr));
return(FALSE);
}
new_bsr_address.sin6_addr = new_bsr_uni_addr.unicast_addr;
new_bsr_address.sin6_len = sizeof(new_bsr_address);
new_bsr_address.sin6_family = AF_INET6;
new_bsr_address.sin6_scope_id = inet6_uvif2scopeid(&new_bsr_address, v);
if (local_address(&new_bsr_address) != NO_VIF)
{
IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
log(LOG_DEBUG, 0,
"receive_pim6_bootstrap: Bootstrap from myself(%s), ignored.",
inet6_fmt(&new_bsr_address.sin6_addr));
return (FALSE); /* The new BSR is one of my local addresses */
}
/*
* Compare the current BSR priority with the priority of the BSR included
* in the message.
*/
/*
* TODO: if I am just starting and will become the BSR, I should accept
* the message coming from the current BSR and get the current
* Cand-RP-Set.
*/
if ((curr_bsr_priority > new_bsr_priority) ||
((curr_bsr_priority == new_bsr_priority)
&& (inet6_greaterthan(&curr_bsr_address, &new_bsr_address))))
{
/* The message's BSR is less preferred than the current BSR */
log(LOG_DEBUG, 0,
"receive_pim6_bootstrap: BSR(%s, prio=%d) is less preferred"
" than the current BSR(%s, prio=%d)",
inet6_fmt(&new_bsr_address.sin6_addr), new_bsr_priority,
inet6_fmt(&curr_bsr_address.sin6_addr), curr_bsr_priority);
return (FALSE); /* Ignore the received BSR message */
}
/* Check the iif, if this was PIM-ROUTERS multicast */
if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &allpim6routers_group.sin6_addr))
{
k_req_incoming(&new_bsr_address, &rpfc);
if ((rpfc.iif == NO_VIF) ||
IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
{
/* coudn't find a route to the BSR */
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: can't find a route to the BSR(%s)",
inet6_fmt(&new_bsr_address.sin6_addr));
return (FALSE);
}
neighbor_addr = *src;
incoming = rpfc.iif;
if (uvifs[incoming].uv_flags &
(VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
{
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: Bootstrap from an invalid interface(%s)",
uvifs[incoming].uv_name);
return (FALSE); /* Shoudn't arrive on that interface */
}
/* Find the upstream router */
for (n = uvifs[incoming].uv_pim_neighbors; n != NULL; n = n->next)
{
if (inet6_lessthan(&neighbor_addr, &n->address))
continue;
if (inet6_equal(&neighbor_addr, &n->address))
{
rpf_neighbor = n;
break;
}
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: Bootstrap from an unrecognized "
"neighbor(%s) on %s",
inet6_fmt(&neighbor_addr.sin6_addr), uvifs[incoming].uv_name);
return (FALSE); /* No neighbor toward BSR found */
}
/* redundant checks? */
if ((n == (pim_nbr_entry_t *) NULL ))
{
return (FALSE); /* Sender of this message is not the RPF*/
}
/* neighbor */
if(!(inet6_equal(&n->address, src)))
{
return (FALSE);
}
}
else
{
if (local_address(dst) == NO_VIF)
/*
* TODO: XXX: this situation should be handled earlier: The
* destination is neither ALL_PIM_ROUTERS nor me
*/
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: Bootstrap with an invalid dst(%s)",
inet6_fmt(&dst->sin6_addr));
return (FALSE);
/* Probably unicasted from the current DR */
if (cand_rp_list != (cand_rp_t *) NULL)
{
/*
* Hmmm, I do have a Cand-RP-list, but some neighbor has a
* different opinion and is unicasting it to me. Ignore this guy.
*/
log(LOG_INFO, 0,
"receive_pim6_bootstrap: Bootstrap received but we already "
"have RPs. ignored.");
return (FALSE);
}
for (mifi = 0; mifi < numvifs; mifi++)
{
if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
MIFF_REGISTER))
continue;
if (inet6_equal(&uvifs[mifi].uv_linklocal->pa_addr,dst))
{
incoming = mifi;
break;
}
}
if (incoming == NO_VIF)
{
/* Cannot find the receiving iif toward that DR */
IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
log(LOG_DEBUG, 0,
"Unicast boostrap message from %s to %s ignored: "
"cannot find iif",
inet6_fmt(&src->sin6_addr), inet6_fmt(&dst->sin6_addr));
return (FALSE);
}
/*
* TODO: check the sender is directly connected and I am really the
* DR.
*/
}
if (cand_rp_flag == TRUE)
{
/* If change in the BSR address, send immediately Cand-RP-Adv */
/* TODO: use some random delay? */
if (!inet6_equal(&new_bsr_address , &curr_bsr_address))
{
send_pim6_cand_rp_adv();
SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
}
}
/* Forward the BSR Message first and then update the RP-set list */
/* XXX: should we do sanity checks before forwarding?? */
/* TODO: if the message was unicasted to me, resend? */
for (mifi = 0; mifi < numvifs; mifi++)
{
if (mifi == incoming)
continue;
if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
MIFF_REGISTER | VIFF_TUNNEL | VIFF_NONBRS))
continue;
bcopy(pim_message, (char *)(pim6_send_buf), datalen);
send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
&allpim6routers_group, PIM_BOOTSTRAP,
datalen - sizeof(struct pim));
}
max_data_ptr = (u_int8 *) pim_message + datalen;
/*
* TODO: XXX: this 24 is HARDCODING!!! Do a bunch of definitions and make
* it stylish!
* 24 = Encoded-Group Address(20) + RP-cound(1) + Frag-RP(1) + Reserved(2)
*/
min_datalen = 24;
if ((new_bsr_fragment_tag != curr_bsr_fragment_tag) ||
(inet6_equal(&new_bsr_address, &curr_bsr_address)))
{
/* Throw away the old segment */
delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
}
curr_bsr_address = new_bsr_address;
curr_bsr_priority = new_bsr_priority;
curr_bsr_fragment_tag = new_bsr_fragment_tag;
MASKLEN_TO_MASK6(new_bsr_hash_masklen, curr_bsr_hash_mask);
SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
while (data_ptr + min_datalen <= max_data_ptr)
{
GET_EGADDR6(&curr_group_addr, data_ptr);
GET_BYTE(curr_rp_count, data_ptr);
GET_BYTE(curr_frag_rp_count, data_ptr);
GET_HOSTSHORT(reserved_short, data_ptr);
MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
if (curr_rp_count == 0)
{
group_.sin6_addr = curr_group_addr.mcast_addr;
delete_grp_mask(&cand_rp_list, &grp_mask_list,
&group_, curr_group_mask);
continue;
}
if (curr_rp_count == curr_frag_rp_count)
{
/* Add all RPs */
while (curr_frag_rp_count--)
{
/*
* Sanity for the data length; the data packet must contain
* Encoded-Unicast-RP-Address(18) + RP-Holdtime(2) +
* RP-Priority(1) + Reserved(1).
*/
if (data_ptr + PIM6_ENCODE_UNI_ADDR_LEN + sizeof(u_int32_t)
> max_data_ptr) {
log(LOG_NOTICE, 0,
"receive_pim6_bootstrap: Bootstrap from %s on %s "
"does not have enough length to contatin RP information",
inet6_fmt(&src->sin6_addr), v->uv_name);
/*
* Ignore the rest of the message.
* XXX: should we discard the entire message?
*/
goto garbage_collect;
}
GET_EUADDR6(&curr_rp_addr, data_ptr);
GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
GET_BYTE(curr_rp_priority, data_ptr);
GET_BYTE(reserved_byte, data_ptr);
MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
rpp_.sin6_addr = curr_rp_addr.unicast_addr;
rpp_.sin6_len = sizeof(rpp_);
rpp_.sin6_family = AF_INET6;
/*
* The cand_rp address scope should be global.
* XXX: however, is a site-local RP sometimes useful?
* we currently discard such RP...
*/
if (IN6_IS_ADDR_LINKLOCAL(&rpp_.sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&rpp_.sin6_addr)) {
log(LOG_WARNING, 0,
"receive_pim6_bootstrap: invalid RP address: %s",
inet6_fmt(&rpp_.sin6_addr));
continue;
}
rpp_.sin6_scope_id = 0;
group_.sin6_addr = curr_group_addr.mcast_addr;
group_.sin6_len = sizeof(group_);
group_.sin6_family = AF_INET6;
group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
&rpp_, curr_rp_priority,
curr_rp_holdtime, &group_,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
continue;
}
/*
* This is a partial list of the RPs for this group prefix. Save
* until all segments arrive.
*/
for (i = 0; i < sizeof(struct in6_addr); i++) {
prefix_h.sin6_addr.s6_addr[i] =
curr_group_addr.mcast_addr.s6_addr[i]
& curr_group_mask.s6_addr[i];
}
for (grp_mask_ptr = segmented_grp_mask_list;
grp_mask_ptr != (grp_mask_t *) NULL;
grp_mask_ptr = grp_mask_ptr->next)
{
for (i = 0; i < sizeof(struct in6_addr); i++) {
prefix_h2.sin6_addr.s6_addr[i] =
grp_mask_ptr->group_addr.sin6_addr.s6_addr[i]
& grp_mask_ptr->group_mask.s6_addr[i];
}
if (inet6_lessthan(&prefix_h2, &prefix_h))
continue;
else
break;
}
if ((grp_mask_ptr != (grp_mask_t *) NULL)
&& (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_addr.sin6_addr,
&curr_group_addr.mcast_addr))
&& (IN6_ARE_ADDR_EQUAL(&grp_mask_ptr->group_mask, &curr_group_mask))
&& (grp_mask_ptr->group_rp_number + curr_frag_rp_count
== curr_rp_count))
{
/* All missing PRs have arrived. Add all RP entries */
while (curr_frag_rp_count--)
{
GET_EUADDR6(&curr_rp_addr, data_ptr);
GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
GET_BYTE(curr_rp_priority, data_ptr);
GET_BYTE(reserved_byte, data_ptr);
MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
rpp_.sin6_addr = curr_rp_addr.unicast_addr;
rpp_.sin6_scope_id=0;
group_.sin6_addr = curr_group_addr.mcast_addr;
group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
&rpp_,
curr_rp_priority,
curr_rp_holdtime,
&group_,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
/* Add the rest from the previously saved segments */
for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next)
{
group_.sin6_addr = curr_group_addr.mcast_addr;
group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
&grp_rp_entry_ptr->rp->rpentry->address,
grp_rp_entry_ptr->priority,
grp_rp_entry_ptr->holdtime,
&group_,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
group_.sin6_addr = curr_group_addr.mcast_addr;
group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
delete_grp_mask(&segmented_cand_rp_list,
&segmented_grp_mask_list,
&group_,
curr_group_mask);
}
else
{
/* Add the partially received RP-list to the group of pending RPs */
while (curr_frag_rp_count--)
{
GET_EUADDR6(&curr_rp_addr, data_ptr);
GET_HOSTSHORT(curr_rp_holdtime, data_ptr);
GET_BYTE(curr_rp_priority, data_ptr);
GET_BYTE(reserved_byte, data_ptr);
MASKLEN_TO_MASK6(curr_group_addr.masklen, curr_group_mask);
rpp_.sin6_addr = curr_rp_addr.unicast_addr;
group_.sin6_addr = curr_group_addr.mcast_addr;
group_.sin6_scope_id = inet6_uvif2scopeid(&group_,v);
add_rp_grp_entry(&segmented_cand_rp_list,
&segmented_grp_mask_list,
&rpp_,
curr_rp_priority,
curr_rp_holdtime,
&group_,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
}
}
garbage_collect:
/*
* Garbage collection. Check all group prefixes and if the fragment_tag
* for a group_prefix is the same as curr_bsr_fragment_tag, then remove
* all RPs for this group_prefix which have different fragment tag.
*/
for (grp_mask_ptr = grp_mask_list;
grp_mask_ptr != (grp_mask_t *) NULL;
grp_mask_ptr = grp_mask_next)
{
grp_mask_next = grp_mask_ptr->next;
if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
{
for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
grp_rp_entry_ptr = grp_rp_entry_next)
{
grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
grp_rp_entry_ptr);
}
}
}
/* Cleanup also the list used by incompleted segments */
for (grp_mask_ptr = segmented_grp_mask_list;
grp_mask_ptr != (grp_mask_t *) NULL;
grp_mask_ptr = grp_mask_next)
{
grp_mask_next = grp_mask_ptr->next;
if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag)
{
for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next;
grp_rp_entry_ptr != (rp_grp_entry_t *) NULL;
grp_rp_entry_ptr = grp_rp_entry_next)
{
grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next;
if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag)
delete_rp_grp_entry(&segmented_cand_rp_list,
&segmented_grp_mask_list,
grp_rp_entry_ptr);
}
}
}
return (TRUE);
}
void
send_pim6_bootstrap()
{
int datalen;
mifi_t mifi;
if ((datalen = create_pim6_bootstrap_message(pim6_send_buf)))
{
for (mifi = 0; mifi < numvifs; mifi++)
{
if (uvifs[mifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN |
MIFF_REGISTER | VIFF_TUNNEL))
continue;
send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr,
&allpim6routers_group, PIM_BOOTSTRAP, datalen);
uvifs[mifi].uv_out_pim6_bootsrap++;
}
}
}
/************************************************************************
* PIM_CAND_RP_ADV
************************************************************************/
/*
* minimum length of a cand. RP adv. message;
* length of PIM header + prefix-cnt(1) + priority(1) + holdtime(2) +
* encoded unicast RP addr(18)
*/
#define PIM6_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM6_ENCODE_UNI_ADDR_LEN)
/*
* If I am the Bootstrap router, process the advertisement, otherwise ignore
* it.
*/
int
receive_pim6_cand_rp_adv(src, dst, pim_message, datalen)
struct sockaddr_in6 *src,
*dst;
char *pim_message;
register int datalen;
{
u_int8 prefix_cnt;
u_int8 priority;
u_int16 holdtime;
pim6_encod_uni_addr_t cand_rp_addr;
pim6_encod_grp_addr_t encod_grp_addr;
u_int8 *data_ptr;
struct in6_addr grp_mask;
struct sockaddr_in6 group_, rpp_;
pim6dstat.in_pim6_cand_rp++;
/* if I am not the bootstrap RP, then do not accept the message */
if ((cand_bsr_flag != FALSE) &&
!inet6_equal(&curr_bsr_address, &my_bsr_address))
{
log(LOG_NOTICE, 0,
"receive_pim6_cand_rp_adv: receive cand_RP from %s "
"but I'm not the BSR",
inet6_fmt(&src->sin6_addr));
return (FALSE);
}
/* sanity check for the minimum length */
if (datalen < PIM6_CAND_RP_ADV_MINLEN) {
log(LOG_NOTICE, 0,
"receive_pim6_cand_rp_adv: cand_RP message size(%u) is"
" too short from %s",
datalen, inet6_fmt(&src->sin6_addr));
return(FALSE);
}
datalen -= PIM6_CAND_RP_ADV_MINLEN;
data_ptr = (u_int8 *) (pim_message + sizeof(struct pim));
/* Parse the CAND_RP_ADV message */
GET_BYTE(prefix_cnt, data_ptr);
GET_BYTE(priority, data_ptr);
GET_HOSTSHORT(holdtime, data_ptr);
GET_EUADDR6(&cand_rp_addr, data_ptr);
/*
* The RP Address field is set to the globally reachable IPv6 address
* [draft-ietf-pim-ipv6].
*/
if (IN6_IS_ADDR_LINKLOCAL(&cand_rp_addr.unicast_addr)) {
/* XXX: prohibit a site-local address as well? */
log(LOG_WARNING, 0,
"receive_pim6_cand_rp_adv: non global address(%s) as RP",
inet6_fmt(&cand_rp_addr.unicast_addr));
return(FALSE);
}
memset(&rpp_, 0, sizeof(rpp_));
if (prefix_cnt == 0)
{
/* The default ff:: and masklen of 8 */
MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
rpp_.sin6_addr = cand_rp_addr.unicast_addr;
/*
* note that we don't have to take care of scope id, since
* the address should be global(see above).
*/
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
&rpp_, priority, holdtime,
&sockaddr6_d, grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
return (TRUE);
}
while (prefix_cnt--)
{
/*
* Sanity check for the message length.
* XXX: do we have to do the check at an earlier stage and
* discard the whole message (instead of adopting a part of it)
* if it's bogus?
*/
if (datalen < PIM6_ENCODE_GRP_ADDR_LEN) {
log(LOG_NOTICE, 0,
"receive_pim6_cand_rp_adv: cand_RP message from %s is"
" too short to contain enough groups",
inet6_fmt(&src->sin6_addr));
return(FALSE);
}
datalen -= PIM6_ENCODE_GRP_ADDR_LEN;
GET_EGADDR6(&encod_grp_addr, data_ptr);
MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
group_.sin6_addr = encod_grp_addr.mcast_addr;
group_.sin6_scope_id = 0; /* XXX: what if for scoped multicast addr? */
rpp_.sin6_addr = cand_rp_addr.unicast_addr;
/* see above note on scope id */
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
&rpp_, priority, holdtime,
&group_, grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
}
return (TRUE);
}
int
send_pim6_cand_rp_adv()
{
u_int8 prefix_cnt;
struct in6_addr grp_mask;
pim6_encod_grp_addr_t encod_grp_addr;
u_int8 *data_ptr;
struct sockaddr_in6 group_;
if (!inet6_valid_host(&curr_bsr_address))
return (FALSE); /* No BSR yet */
if( inet6_equal(&curr_bsr_address, &my_bsr_address))
{
/* I am the BSR and have to include my own group_prefix stuff */
prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr;
if (prefix_cnt == 0)
{
/* The default ff00:: and masklen of 8 */
MASKLEN_TO_MASK6(ALL_MCAST_GROUPS_LENGTH, grp_mask);
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
&my_cand_rp_address, my_cand_rp_priority,
my_cand_rp_holdtime,
&sockaddr6_d,
grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
return (TRUE);
}
/* TODO: hardcoding!! */
/* 18 = sizeof(pim6_encod_uni_addr_t) without padding */
data_ptr = cand_rp_adv_message.buffer + (4 + 18);
while (prefix_cnt--)
{
GET_EGADDR6(&encod_grp_addr, data_ptr);
MASKLEN_TO_MASK6(encod_grp_addr.masklen, grp_mask);
group_.sin6_addr = encod_grp_addr.mcast_addr;
group_.sin6_scope_id = 0; /*XXX */
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
&my_cand_rp_address, my_cand_rp_priority,
my_cand_rp_holdtime,
&group_, grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
}
return (TRUE);
}
data_ptr = (u_int8 *) (pim6_send_buf + sizeof(struct pim));
bcopy((char *)cand_rp_adv_message.buffer, (char *) data_ptr,
cand_rp_adv_message.message_size);
send_pim6(pim6_send_buf, &my_cand_rp_address, &curr_bsr_address ,
PIM_CAND_RP_ADV, cand_rp_adv_message.message_size);
pim6dstat.out_pim6_cand_rp++;
return TRUE;
}