654 lines
19 KiB
C
654 lines
19 KiB
C
|
/* $NetBSD: route.c,v 1.1 1999/07/17 14:06:26 itojun Exp $ */
|
||
|
|
||
|
/*
|
||
|
* Copyright (c) 1998 by the University of Oregon.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software and
|
||
|
* its documentation in source and binary forms for lawful
|
||
|
* purposes and without fee is hereby granted, provided
|
||
|
* that the above copyright notice appear in all copies and that both
|
||
|
* the copyright notice and this permission notice appear in supporting
|
||
|
* documentation, and that any documentation, advertising materials,
|
||
|
* and other materials related to such distribution and use acknowledge
|
||
|
* that the software was developed by the University of Oregon.
|
||
|
* The name of the University of Oregon may not be used to endorse or
|
||
|
* promote products derived from this software without specific prior
|
||
|
* written permission.
|
||
|
*
|
||
|
* THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
|
||
|
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
|
||
|
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
|
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
||
|
* NON-INFRINGEMENT.
|
||
|
*
|
||
|
* IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
|
||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
|
||
|
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
|
||
|
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*
|
||
|
* Other copyrights might apply to parts of this software and are so
|
||
|
* noted when applicable.
|
||
|
*/
|
||
|
/*
|
||
|
* Questions concerning this software should be directed to
|
||
|
* Kurt Windisch (kurtw@antc.uoregon.edu)
|
||
|
*
|
||
|
* KAME Id: route.c,v 1.24 1998/07/06 22:31:14 kurtw Exp
|
||
|
*/
|
||
|
/*
|
||
|
* Part of this program has been derived from PIM sparse-mode pimd.
|
||
|
* The pimd program is covered by the license in the accompanying file
|
||
|
* named "LICENSE.pimd".
|
||
|
*
|
||
|
* The pimd program is COPYRIGHT 1998 by University of Southern California.
|
||
|
*
|
||
|
* Part of this program has been derived from mrouted.
|
||
|
* The mrouted program is covered by the license in the accompanying file
|
||
|
* named "LICENSE.mrouted".
|
||
|
*
|
||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||
|
* Leland Stanford Junior University.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "defs.h"
|
||
|
|
||
|
|
||
|
static u_int16 max_prune_timeout __P((mrtentry_t *));
|
||
|
static void process_cache_miss __P((struct mrt6msg *im));
|
||
|
static void process_wrong_iif __P((struct mrt6msg *im));
|
||
|
|
||
|
u_int32 default_source_preference = DEFAULT_LOCAL_PREF;
|
||
|
u_int32 default_source_metric = DEFAULT_LOCAL_METRIC;
|
||
|
|
||
|
|
||
|
/* Return the iif for given address */
|
||
|
vifi_t
|
||
|
get_iif(address)
|
||
|
struct sockaddr_in6 *address;
|
||
|
{
|
||
|
struct rpfctl rpfc;
|
||
|
|
||
|
k_req_incoming(address, &rpfc);
|
||
|
if (IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
|
||
|
return (NO_VIF);
|
||
|
return (rpfc.iif);
|
||
|
}
|
||
|
|
||
|
/* Return the PIM neighbor toward a source */
|
||
|
/* If route not found or if a local source or if a directly connected source,
|
||
|
* but is not PIM router, or if the first hop router is not a PIM router,
|
||
|
* then return NULL.
|
||
|
*/
|
||
|
pim_nbr_entry_t *
|
||
|
find_pim6_nbr(source)
|
||
|
struct sockaddr_in6 *source;
|
||
|
{
|
||
|
struct rpfctl rpfc;
|
||
|
pim_nbr_entry_t *pim_nbr;
|
||
|
struct sockaddr_in6 *next_hop_router_addr;
|
||
|
|
||
|
if (local_address(source) != NO_VIF)
|
||
|
return (pim_nbr_entry_t *)NULL;
|
||
|
k_req_incoming(source, &rpfc);
|
||
|
if ((IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr))
|
||
|
|| (rpfc.iif == NO_VIF))
|
||
|
return (pim_nbr_entry_t *)NULL;
|
||
|
next_hop_router_addr = &rpfc.rpfneighbor;
|
||
|
for (pim_nbr = uvifs[rpfc.iif].uv_pim_neighbors;
|
||
|
pim_nbr != (pim_nbr_entry_t *)NULL;
|
||
|
pim_nbr = pim_nbr->next)
|
||
|
if (inet6_equal(&pim_nbr->address, next_hop_router_addr))
|
||
|
return(pim_nbr);
|
||
|
return (pim_nbr_entry_t *)NULL;
|
||
|
}
|
||
|
|
||
|
/* TODO: check again the exact setup if the source is local or directly
|
||
|
* connected!!!
|
||
|
*/
|
||
|
/* TODO: XXX: change the metric and preference for all (S,G) entries per
|
||
|
* source?
|
||
|
*/
|
||
|
/* PIMDM TODO - If possible, this would be the place to correct set the
|
||
|
* source's preference and metric to that obtained from the kernel
|
||
|
* and/or unicast routing protocol. For now, set it to the configured
|
||
|
* default for local pref/metric.
|
||
|
*/
|
||
|
/*
|
||
|
* Set the iif, upstream router, preference and metric for the route
|
||
|
* toward the source. Return TRUE is the route was found, othewise FALSE.
|
||
|
* If srctype==PIM_IIF_SOURCE and if the source is directly connected
|
||
|
* then the "upstream" is set to NULL.
|
||
|
* Note that srctype is a hold-over from the PIM-SM daemon and is unused.
|
||
|
*/
|
||
|
int
|
||
|
set_incoming(srcentry_ptr, srctype)
|
||
|
srcentry_t *srcentry_ptr;
|
||
|
int srctype;
|
||
|
{
|
||
|
struct rpfctl rpfc;
|
||
|
struct sockaddr_in6 *source = &srcentry_ptr->address;
|
||
|
struct sockaddr_in6 *neighbor_addr;
|
||
|
register struct uvif *v;
|
||
|
register pim_nbr_entry_t *n;
|
||
|
|
||
|
/* Preference will be 0 if directly connected */
|
||
|
srcentry_ptr->preference = 0;
|
||
|
srcentry_ptr->metric = 0;
|
||
|
|
||
|
if ((srcentry_ptr->incoming = local_address(source)) != NO_VIF) {
|
||
|
/* The source is a local address */
|
||
|
/* TODO: set the upstream to myself? */
|
||
|
srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
|
||
|
return (TRUE);
|
||
|
}
|
||
|
|
||
|
if ((srcentry_ptr->incoming = find_vif_direct(source)) == NO_VIF) {
|
||
|
/* TODO: probably need to check the case if the iif is disabled */
|
||
|
/* Use the lastest resource: the kernel unicast routing table */
|
||
|
k_req_incoming(source, &rpfc);
|
||
|
if ((rpfc.iif == NO_VIF) ||
|
||
|
IN6_IS_ADDR_UNSPECIFIED(&rpfc.rpfneighbor.sin6_addr)) {
|
||
|
/* couldn't find a route */
|
||
|
IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF)
|
||
|
log(LOG_DEBUG, 0, "NO ROUTE found for %s",
|
||
|
inet6_fmt(&source->sin6_addr));
|
||
|
return(FALSE);
|
||
|
}
|
||
|
srcentry_ptr->incoming = rpfc.iif;
|
||
|
neighbor_addr = &rpfc.rpfneighbor;
|
||
|
}
|
||
|
else {
|
||
|
/* The source is directly connected.
|
||
|
*/
|
||
|
srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
|
||
|
return (TRUE);
|
||
|
}
|
||
|
|
||
|
/* set the preference for sources that aren't directly connected. */
|
||
|
v = &uvifs[srcentry_ptr->incoming];
|
||
|
srcentry_ptr->preference = v->uv_local_pref;
|
||
|
srcentry_ptr->metric = v->uv_local_metric;
|
||
|
|
||
|
/*
|
||
|
* The upstream router must be a (PIM router) neighbor, otherwise we
|
||
|
* are in big trouble ;-)
|
||
|
*/
|
||
|
for (n = v->uv_pim_neighbors; n != NULL; n = n->next) {
|
||
|
if (inet6_lessthan(neighbor_addr, &n->address))
|
||
|
continue;
|
||
|
if (inet6_equal(neighbor_addr, &n->address)) {
|
||
|
/*
|
||
|
*The upstream router is found in the list of neighbors.
|
||
|
* We are safe!
|
||
|
*/
|
||
|
srcentry_ptr->upstream = n;
|
||
|
IF_DEBUG(DEBUG_RPF)
|
||
|
log(LOG_DEBUG, 0,
|
||
|
"For src %s, iif is %d, next hop router is %s",
|
||
|
inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
|
||
|
inet6_fmt(&neighbor_addr->sin6_addr));
|
||
|
return(TRUE);
|
||
|
}
|
||
|
else break;
|
||
|
}
|
||
|
|
||
|
/* TODO: control the number of messages! */
|
||
|
log(LOG_INFO, 0,
|
||
|
"For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER",
|
||
|
inet6_fmt(&source->sin6_addr), srcentry_ptr->incoming,
|
||
|
inet6_fmt(&neighbor_addr->sin6_addr));
|
||
|
srcentry_ptr->upstream = (pim_nbr_entry_t *)NULL;
|
||
|
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Set the leaves in a new mrtentry */
|
||
|
void set_leaves(mrtentry_ptr)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
struct uvif *v;
|
||
|
|
||
|
/* Check for a group report on each vif */
|
||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
|
||
|
if(check_multicast_listener(v, &mrtentry_ptr->group->group))
|
||
|
IF_SET(vifi, &mrtentry_ptr->leaves);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Handle new receiver
|
||
|
*
|
||
|
* TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
|
||
|
* we have source-specific Join/Prune.
|
||
|
*/
|
||
|
void
|
||
|
add_leaf(vifi, source, group)
|
||
|
vifi_t vifi;
|
||
|
struct sockaddr_in6 *source;
|
||
|
struct sockaddr_in6 *group;
|
||
|
{
|
||
|
grpentry_t *grpentry_ptr;
|
||
|
mrtentry_t *mrtentry_srcs;
|
||
|
if_set new_leaves;
|
||
|
int state_change;
|
||
|
|
||
|
grpentry_ptr = find_group(group);
|
||
|
if (grpentry_ptr == (grpentry_t *)NULL)
|
||
|
return;
|
||
|
|
||
|
/* walk the source list for the group and add vif to oiflist */
|
||
|
for (mrtentry_srcs = grpentry_ptr->mrtlink;
|
||
|
mrtentry_srcs != (mrtentry_t *)NULL;
|
||
|
mrtentry_srcs = mrtentry_srcs->grpnext) {
|
||
|
|
||
|
/* if applicable, add the vif to the leaves */
|
||
|
if (mrtentry_srcs->incoming == vifi)
|
||
|
continue;
|
||
|
|
||
|
if(!(IF_ISSET(vifi, &mrtentry_srcs->leaves))) {
|
||
|
|
||
|
IF_DEBUG(DEBUG_MRT)
|
||
|
log(LOG_DEBUG, 0, "Adding leaf vif %d for src %s group %s",
|
||
|
vifi,
|
||
|
inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
|
||
|
inet6_fmt(&group->sin6_addr));
|
||
|
|
||
|
IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
|
||
|
IF_SET(vifi, &new_leaves); /* Add the leaf */
|
||
|
|
||
|
state_change =
|
||
|
change_interfaces(mrtentry_srcs,
|
||
|
mrtentry_srcs->incoming,
|
||
|
&mrtentry_srcs->pruned_oifs,
|
||
|
&new_leaves);
|
||
|
|
||
|
/* Handle transition from negative cache */
|
||
|
if(state_change == 1)
|
||
|
trigger_join_alert(mrtentry_srcs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
|
||
|
* we have source-specific joins/prunes.
|
||
|
*/
|
||
|
void
|
||
|
delete_leaf(vifi, source, group)
|
||
|
vifi_t vifi;
|
||
|
struct sockaddr_in6 *source;
|
||
|
struct sockaddr_in6 *group;
|
||
|
{
|
||
|
grpentry_t *grpentry_ptr;
|
||
|
mrtentry_t *mrtentry_srcs;
|
||
|
if_set new_leaves;
|
||
|
int state_change;
|
||
|
|
||
|
/* mrtentry_t *mrtentry_ptr;
|
||
|
* mrtentry_t *mrtentry_srcs;
|
||
|
* vifbitmap_t new_oifs;
|
||
|
* vifbitmap_t old_oifs;
|
||
|
* vifbitmap_t new_leaves;
|
||
|
*/
|
||
|
|
||
|
grpentry_ptr = find_group(group);
|
||
|
if (grpentry_ptr == (grpentry_t *)NULL)
|
||
|
return;
|
||
|
|
||
|
/* walk the source list for the group and delete vif to leaves */
|
||
|
for (mrtentry_srcs = grpentry_ptr->mrtlink;
|
||
|
mrtentry_srcs != (mrtentry_t *)NULL;
|
||
|
mrtentry_srcs = mrtentry_srcs->grpnext) {
|
||
|
|
||
|
/* if applicable, delete the vif from the leaves */
|
||
|
if (mrtentry_srcs->incoming == vifi)
|
||
|
continue;
|
||
|
|
||
|
if(IF_ISSET(vifi, &mrtentry_srcs->leaves)) {
|
||
|
|
||
|
IF_DEBUG(DEBUG_MRT)
|
||
|
log(LOG_DEBUG, 0, "Deleting leaf vif %d for src %s, group %s",
|
||
|
vifi,
|
||
|
inet6_fmt(&mrtentry_srcs->source->address.sin6_addr),
|
||
|
inet6_fmt(&group->sin6_addr));
|
||
|
|
||
|
IF_COPY(&mrtentry_srcs->leaves, &new_leaves);
|
||
|
IF_CLR(vifi, &new_leaves); /* Remove the leaf */
|
||
|
|
||
|
state_change =
|
||
|
change_interfaces(mrtentry_srcs,
|
||
|
mrtentry_srcs->incoming,
|
||
|
&mrtentry_srcs->pruned_oifs,
|
||
|
&new_leaves);
|
||
|
|
||
|
/* Handle transition to negative cache */
|
||
|
if(state_change == -1)
|
||
|
trigger_prune_alert(mrtentry_srcs);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
calc_oifs(mrtentry_ptr, oifs_ptr)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
if_set *oifs_ptr;
|
||
|
{
|
||
|
if_set oifs;
|
||
|
|
||
|
/*
|
||
|
* oifs =
|
||
|
* ((nbr_ifs - my_prune) + my_leaves) - my_filters - incoming_interface,
|
||
|
* i.e.`leaves` have higher priority than `prunes`, but lower than `filters'.
|
||
|
* Asserted oifs (those that lost assert) are handled as pruned oifs.
|
||
|
* The incoming interface is always deleted from the oifs
|
||
|
*/
|
||
|
|
||
|
if (mrtentry_ptr == (mrtentry_t *)NULL) {
|
||
|
IF_ZERO(oifs_ptr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IF_COPY(&nbr_mifs, &oifs);
|
||
|
IF_CLR_MASK(&oifs, &mrtentry_ptr->pruned_oifs);
|
||
|
IF_MERGE(&oifs, &mrtentry_ptr->leaves, &oifs);
|
||
|
IF_CLR_MASK(&oifs, &mrtentry_ptr->filter_oifs);
|
||
|
IF_CLR(mrtentry_ptr->incoming, &oifs);
|
||
|
IF_COPY(&oifs, oifs_ptr);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set the iif, join/prune/leaves/asserted interfaces. Calculate and
|
||
|
* set the oifs.
|
||
|
* Return 1 if oifs change from NULL to not-NULL.
|
||
|
* Return -1 if oifs change from non-NULL to NULL
|
||
|
* else return 0
|
||
|
* If the iif change or if the oifs change from NULL to non-NULL
|
||
|
* or vice-versa, then schedule that mrtentry join/prune timer to
|
||
|
* timeout immediately.
|
||
|
*/
|
||
|
int
|
||
|
change_interfaces(mrtentry_ptr, new_iif, new_pruned_oifs,
|
||
|
new_leaves_)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
vifi_t new_iif;
|
||
|
if_set *new_pruned_oifs;
|
||
|
if_set *new_leaves_;
|
||
|
{
|
||
|
if_set old_pruned_oifs;
|
||
|
if_set old_leaves;
|
||
|
if_set new_leaves;
|
||
|
if_set new_real_oifs; /* The result oifs */
|
||
|
if_set old_real_oifs;
|
||
|
vifi_t old_iif;
|
||
|
int return_value;
|
||
|
|
||
|
if (mrtentry_ptr == (mrtentry_t *)NULL)
|
||
|
return (0);
|
||
|
|
||
|
IF_COPY(new_leaves_, &new_leaves);
|
||
|
|
||
|
old_iif = mrtentry_ptr->incoming;
|
||
|
IF_COPY(&mrtentry_ptr->leaves, &old_leaves);
|
||
|
IF_COPY(&mrtentry_ptr->pruned_oifs, &old_pruned_oifs);
|
||
|
|
||
|
IF_COPY(&mrtentry_ptr->oifs, &old_real_oifs);
|
||
|
|
||
|
mrtentry_ptr->incoming = new_iif;
|
||
|
IF_COPY(new_pruned_oifs, &mrtentry_ptr->pruned_oifs);
|
||
|
IF_COPY(&new_leaves, &mrtentry_ptr->leaves);
|
||
|
calc_oifs(mrtentry_ptr, &new_real_oifs);
|
||
|
|
||
|
if (IF_ISEMPTY(&old_real_oifs)) {
|
||
|
if (IF_ISEMPTY(&new_real_oifs))
|
||
|
return_value = 0;
|
||
|
else
|
||
|
return_value = 1;
|
||
|
} else {
|
||
|
if (IF_ISEMPTY(&new_real_oifs))
|
||
|
return_value = -1;
|
||
|
else
|
||
|
return_value = 0;
|
||
|
}
|
||
|
|
||
|
if ((IF_SAME(&new_real_oifs, &old_real_oifs))
|
||
|
&& (new_iif == old_iif))
|
||
|
return 0; /* Nothing to change */
|
||
|
|
||
|
IF_COPY(&new_real_oifs, &mrtentry_ptr->oifs);
|
||
|
|
||
|
k_chg_mfc(mld6_socket, &mrtentry_ptr->source->address,
|
||
|
&mrtentry_ptr->group->group, new_iif, &new_real_oifs);
|
||
|
|
||
|
#ifdef RSRR
|
||
|
rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
|
||
|
#endif /* RSRR */
|
||
|
|
||
|
return (return_value);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* TODO: implement it. Required to allow changing of the physical interfaces
|
||
|
* configuration without need to restart pimd.
|
||
|
*/
|
||
|
int
|
||
|
delete_vif_from_mrt(vifi)
|
||
|
vifi_t vifi;
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static u_int16
|
||
|
max_prune_timeout(mrtentry_ptr)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
{
|
||
|
vifi_t vifi;
|
||
|
#if 0
|
||
|
/* XXX: I don't understand how the variable works...(jinmei@kame.net) */
|
||
|
u_int16 time_left = 0;
|
||
|
#endif
|
||
|
u_int16 max_holdtime = 0;
|
||
|
|
||
|
for(vifi=0; vifi < numvifs; ++vifi)
|
||
|
if(IF_ISSET(vifi, &mrtentry_ptr->pruned_oifs) &&
|
||
|
mrtentry_ptr->prune_timers[vifi])
|
||
|
/* XXX - too expensive ? */
|
||
|
if(mrtentry_ptr->prune_timers[vifi] > max_holdtime)
|
||
|
max_holdtime = mrtentry_ptr->prune_timers[vifi];
|
||
|
#if 0
|
||
|
/* XXX: This is original. But does it have any meaning? */
|
||
|
max_holdtime = time_left;
|
||
|
#endif
|
||
|
|
||
|
if(max_holdtime == 0)
|
||
|
max_holdtime = (u_int16)PIM_JOIN_PRUNE_HOLDTIME;
|
||
|
|
||
|
return(max_holdtime);
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
process_kernel_call()
|
||
|
{
|
||
|
register struct mrt6msg *im; /* igmpmsg control struct */
|
||
|
|
||
|
im = (struct mrt6msg *) mld6_recv_buf;
|
||
|
|
||
|
switch (im->im6_msgtype) {
|
||
|
case MRT6MSG_NOCACHE:
|
||
|
process_cache_miss(im);
|
||
|
break;
|
||
|
case MRT6MSG_WRONGMIF:
|
||
|
process_wrong_iif(im);
|
||
|
break;
|
||
|
default:
|
||
|
IF_DEBUG(DEBUG_KERN)
|
||
|
log(LOG_DEBUG, 0, "Unknown kernel_call code, %d", im->im6_msgtype);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Protocol actions:
|
||
|
* 1. Create (S,G) entry (find_route(CREATE))
|
||
|
* a. set iif and oifs
|
||
|
*/
|
||
|
static void
|
||
|
process_cache_miss(im)
|
||
|
struct mrt6msg *im;
|
||
|
{
|
||
|
static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
|
||
|
static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
|
||
|
/*
|
||
|
* When there is a cache miss, we check only the header of the packet
|
||
|
* (and only it should be sent up by the kernel.
|
||
|
*/
|
||
|
|
||
|
group.sin6_addr = im->im6_dst;
|
||
|
source.sin6_addr = im->im6_src;
|
||
|
group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[im->im6_mif]);
|
||
|
source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[im->im6_mif]);
|
||
|
|
||
|
IF_DEBUG(DEBUG_MFC)
|
||
|
log(LOG_DEBUG, 0, "Cache miss, src %s, dst %s",
|
||
|
inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr));
|
||
|
|
||
|
/* Don't create routing entries for the LAN scoped addresses */
|
||
|
if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
|
||
|
IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
|
||
|
return;
|
||
|
|
||
|
/* Create the (S,G) entry */
|
||
|
mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE);
|
||
|
if (mrtentry_ptr == (mrtentry_t *)NULL)
|
||
|
return;
|
||
|
mrtentry_ptr->flags &= ~MRTF_NEW;
|
||
|
|
||
|
/* Set oifs */
|
||
|
set_leaves(mrtentry_ptr);
|
||
|
calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
|
||
|
|
||
|
/* Add it to the kernel */
|
||
|
k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming,
|
||
|
&mrtentry_ptr->oifs);
|
||
|
|
||
|
#ifdef RSRR
|
||
|
rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
|
||
|
#endif /* RSRR */
|
||
|
|
||
|
/* No need to call change_interfaces, but check for NULL oiflist */
|
||
|
if(IF_ISEMPTY(&mrtentry_ptr->oifs))
|
||
|
trigger_prune_alert(mrtentry_ptr);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* A multicast packet has been received on wrong iif by the kernel.
|
||
|
* If the packet was received on a point-to-point interface, rate-limit
|
||
|
* prunes. if the packet was received on a LAN interface, rate-limit
|
||
|
* asserts.
|
||
|
*/
|
||
|
static void
|
||
|
process_wrong_iif(im)
|
||
|
struct mrt6msg *im;
|
||
|
{
|
||
|
static struct sockaddr_in6 source = {sizeof(source), AF_INET6};
|
||
|
static struct sockaddr_in6 group = {sizeof(group), AF_INET6};
|
||
|
mifi_t mifi;
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
|
||
|
group.sin6_addr = im->im6_dst;
|
||
|
source.sin6_addr = im->im6_src;
|
||
|
mifi = (mifi_t)im->im6_mif;
|
||
|
group.sin6_scope_id = inet6_uvif2scopeid(&group, &uvifs[mifi]);
|
||
|
source.sin6_scope_id = inet6_uvif2scopeid(&source, &uvifs[mifi]);
|
||
|
|
||
|
/* PIMDM TODO Don't create routing entries for the LAN scoped addresses */
|
||
|
if (IN6_IS_ADDR_MC_NODELOCAL(&group.sin6_addr) ||/* sanity? */
|
||
|
IN6_IS_ADDR_MC_LINKLOCAL(&group.sin6_addr))
|
||
|
return;
|
||
|
|
||
|
mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE);
|
||
|
if(mrtentry_ptr == (mrtentry_t *)NULL)
|
||
|
return;
|
||
|
|
||
|
/* Ratelimit prunes or asserts */
|
||
|
#ifdef notyet
|
||
|
if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) {
|
||
|
|
||
|
/* Wrong vif on P2P interface - rate-limit prunes */
|
||
|
|
||
|
if(mrtentry_ptr->last_prune[mifi] == virtual_time)
|
||
|
/* Skip due to rate-limiting */
|
||
|
return;
|
||
|
mrtentry_ptr->last_prune[mifi] = virtual_time;
|
||
|
|
||
|
if(uvifs[mifi].uv_rmt_addr)
|
||
|
send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi,
|
||
|
uvifs[mifi].uv_rmt_addr,
|
||
|
max_prune_timeout(mrtentry_ptr));
|
||
|
else
|
||
|
log(LOG_WARNING, 0,
|
||
|
"Can't send wrongvif prune on p2p %s: no remote address",
|
||
|
uvifs[mifi].uv_lcl_addr);
|
||
|
} else
|
||
|
#endif
|
||
|
{
|
||
|
|
||
|
/* Wrong vif on LAN interface - rate-limit asserts */
|
||
|
|
||
|
if(mrtentry_ptr->last_assert[mifi] == virtual_time)
|
||
|
/* Skip due to rate-limiting */
|
||
|
return;
|
||
|
mrtentry_ptr->last_assert[mifi] = virtual_time;
|
||
|
|
||
|
/* Send the assert */
|
||
|
send_pim6_assert(&source, &group, mifi, mrtentry_ptr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
trigger_prune_alert(mrtentry_ptr)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
{
|
||
|
IF_DEBUG(DEBUG_MRT)
|
||
|
log(LOG_DEBUG, 0, "Now negative cache for src %s, grp %s - pruning",
|
||
|
inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
|
||
|
inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
|
||
|
|
||
|
/* Set the entry timer to the max of the prune timers */
|
||
|
SET_TIMER(mrtentry_ptr->timer, max_prune_timeout(mrtentry_ptr));
|
||
|
|
||
|
/* Send a prune */
|
||
|
if(mrtentry_ptr->upstream)
|
||
|
send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mrtentry_ptr->incoming,
|
||
|
&mrtentry_ptr->upstream->address,
|
||
|
max_prune_timeout(mrtentry_ptr));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
trigger_join_alert(mrtentry_ptr)
|
||
|
mrtentry_t *mrtentry_ptr;
|
||
|
{
|
||
|
IF_DEBUG(DEBUG_MRT)
|
||
|
log(LOG_DEBUG, 0, "Now forwarding state for src %s, grp %s - grafting",
|
||
|
inet6_fmt(&mrtentry_ptr->source->address.sin6_addr),
|
||
|
inet6_fmt(&mrtentry_ptr->group->group.sin6_addr));
|
||
|
|
||
|
/* Refresh the entry timer */
|
||
|
SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT);
|
||
|
|
||
|
/* Send graft */
|
||
|
send_pim6_graft(mrtentry_ptr);
|
||
|
}
|