1c335da8a3
Obtained from: KAME
1302 lines
37 KiB
C
1302 lines
37 KiB
C
/*
|
|
* Copyright (c) 1998 by the University of Southern California.
|
|
* All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and
|
|
* its documentation in source and binary forms for lawful
|
|
* purposes and without fee is hereby granted, provided
|
|
* that the above copyright notice appear in all copies and that both
|
|
* the copyright notice and this permission notice appear in supporting
|
|
* documentation, and that any documentation, advertising materials,
|
|
* and other materials related to such distribution and use acknowledge
|
|
* that the software was developed by the University of Southern
|
|
* California and/or Information Sciences Institute.
|
|
* The name of the University of Southern California may not
|
|
* be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
|
|
* ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
|
|
* PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
|
* NON-INFRINGEMENT.
|
|
*
|
|
* IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
|
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
|
|
* TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
|
|
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Other copyrights might apply to parts of this software and are so
|
|
* noted when applicable.
|
|
*/
|
|
/*
|
|
* Questions concerning this software should be directed to
|
|
* Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
|
|
*
|
|
*/
|
|
/*
|
|
* This program has been derived from pim6dd.
|
|
* The pim6dd program is covered by the license in the accompanying file
|
|
* named "LICENSE.pim6dd".
|
|
*/
|
|
/*
|
|
* This program has been derived from pimd.
|
|
* The pimd program is covered by the license in the accompanying file
|
|
* named "LICENSE.pimd".
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <syslog.h>
|
|
#include "pimd.h"
|
|
#include "mrt.h"
|
|
#include "vif.h"
|
|
#include <netinet6/ip6_mroute.h>
|
|
#include "timer.h"
|
|
#include "debug.h"
|
|
#include "rp.h"
|
|
#include "pim6_proto.h"
|
|
#include "mld6.h"
|
|
#include "mld6_proto.h"
|
|
#include "route.h"
|
|
#include "kern.h"
|
|
#include "debug.h"
|
|
#include "inet6.h"
|
|
|
|
/*
|
|
* Global variables
|
|
*/
|
|
/*
|
|
* XXX: The RATE is in bits/s. To include the header overhead, the
|
|
* approximation is 1 byte/s = 10 bits/s `whatever_bytes` is the maximum
|
|
* number of bytes within the test interval.
|
|
*/
|
|
|
|
u_int32 timer_interval=DEFAULT_TIMER_INTERVAL;
|
|
u_int32 pim_reg_rate_bytes =
|
|
(PIM_DEFAULT_REG_RATE * PIM_DEFAULT_REG_RATE_INTERVAL) / 10;
|
|
u_int32 pim_reg_rate_check_interval = PIM_DEFAULT_REG_RATE_INTERVAL;
|
|
u_int32 pim_data_rate_bytes =
|
|
(PIM_DEFAULT_DATA_RATE * PIM_DEFAULT_DATA_RATE_INTERVAL) / 10;
|
|
u_int32 pim_data_rate_check_interval = PIM_DEFAULT_DATA_RATE_INTERVAL;
|
|
u_int32 pim_hello_period = PIM_TIMER_HELLO_PERIOD;
|
|
u_int32 pim_hello_holdtime = PIM_TIMER_HELLO_HOLDTIME;
|
|
u_int32 pim_join_prune_period = PIM_JOIN_PRUNE_PERIOD;
|
|
u_int32 pim_join_prune_holdtime = PIM_JOIN_PRUNE_HOLDTIME;
|
|
u_int32 pim_data_timeout=PIM_DATA_TIMEOUT;
|
|
u_int32 pim_register_suppression_timeout=PIM_REGISTER_SUPPRESSION_TIMEOUT;
|
|
u_int32 pim_register_probe_time=PIM_REGISTER_PROBE_TIME;
|
|
u_int32 pim_assert_timeout=PIM_ASSERT_TIMEOUT;
|
|
|
|
|
|
/*
|
|
* Local functions definitions.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Local variables
|
|
*/
|
|
u_int16 unicast_routing_timer; /* Used to check periodically for any
|
|
* change in the unicast routing. */
|
|
u_int16 unicast_routing_check_interval;
|
|
u_int8 ucast_flag; /* Used to indicate there was a timeout */
|
|
|
|
u_int16 pim_data_rate_timer; /* Used to check periodically the
|
|
* datarate of the active sources and
|
|
* eventually switch to the shortest
|
|
* path (if forwarder) */
|
|
u_int8 pim_data_rate_flag; /* Used to indicate there was a
|
|
* timeout */
|
|
|
|
u_int16 pim_reg_rate_timer; /* The same as above, but used by the
|
|
* RP to switch to the shortest path
|
|
* and avoid the PIM registers. */
|
|
u_int8 pim_reg_rate_flag;
|
|
u_int8 rate_flag;
|
|
|
|
/*
|
|
* TODO: XXX: the timers below are not used. Instead, the data rate timer is
|
|
* used.
|
|
*/
|
|
u_int16 kernel_cache_timer; /* Used to timeout the kernel cache
|
|
* entries for idle sources */
|
|
u_int16 kernel_cache_check_interval;
|
|
|
|
/* to request and compare any route changes */
|
|
|
|
srcentry_t srcentry_save;
|
|
rpentry_t rpentry_save;
|
|
|
|
/*
|
|
* Init some timers
|
|
*/
|
|
void
|
|
init_timers()
|
|
{
|
|
unicast_routing_check_interval = UCAST_ROUTING_CHECK_INTERVAL;
|
|
SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
|
|
|
|
/*
|
|
* The routing_check and the rate_check timers are interleaved to reduce
|
|
* the amount of work that has to be done at once.
|
|
*/
|
|
/* XXX: for simplicity, both the intervals are the same */
|
|
|
|
if (pim_data_rate_check_interval < pim_reg_rate_check_interval)
|
|
pim_reg_rate_check_interval = pim_data_rate_check_interval;
|
|
|
|
SET_TIMER(pim_data_rate_timer, 3 * pim_data_rate_check_interval / 2);
|
|
SET_TIMER(pim_reg_rate_timer, 3 * pim_reg_rate_check_interval / 2);
|
|
|
|
/*
|
|
* Initialize the srcentry and rpentry used to save the old routes during
|
|
* unicast routing change discovery process.
|
|
*/
|
|
|
|
srcentry_save.prev = (srcentry_t *) NULL;
|
|
srcentry_save.next = (srcentry_t *) NULL;
|
|
memset(&srcentry_save.address, 0, sizeof(struct sockaddr_in6));
|
|
srcentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
|
|
srcentry_save.address.sin6_family= AF_INET6;
|
|
srcentry_save.mrtlink = (mrtentry_t *) NULL;
|
|
srcentry_save.incoming = NO_VIF;
|
|
srcentry_save.upstream = (pim_nbr_entry_t *) NULL;
|
|
srcentry_save.metric = ~0;
|
|
srcentry_save.preference = ~0;
|
|
RESET_TIMER(srcentry_save.timer);
|
|
srcentry_save.cand_rp = (cand_rp_t *) NULL;
|
|
|
|
rpentry_save.prev = (rpentry_t *) NULL;
|
|
rpentry_save.next = (rpentry_t *) NULL;
|
|
memset(&rpentry_save.address, 0, sizeof(struct sockaddr_in6));
|
|
rpentry_save.address.sin6_len = sizeof(struct sockaddr_in6);
|
|
rpentry_save.address.sin6_family= AF_INET6;
|
|
rpentry_save.mrtlink = (mrtentry_t *) NULL;
|
|
rpentry_save.incoming = NO_VIF;
|
|
rpentry_save.upstream = (pim_nbr_entry_t *) NULL;
|
|
rpentry_save.metric = ~0;
|
|
rpentry_save.preference = ~0;
|
|
RESET_TIMER(rpentry_save.timer);
|
|
rpentry_save.cand_rp = (cand_rp_t *) NULL;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* On every timer interrupt, advance (i.e. decrease) the timer for each
|
|
* neighbor and group entry for each vif.
|
|
*/
|
|
void
|
|
age_vifs()
|
|
{
|
|
vifi_t vifi;
|
|
register struct uvif *v;
|
|
register pim_nbr_entry_t *next_nbr,
|
|
*curr_nbr;
|
|
|
|
/*
|
|
* XXX: TODO: currently, sending to qe* interface which is DOWN doesn't
|
|
* return error (ENETDOWN) on my Solaris machine, so have to check
|
|
* periodically the interfaces status. If this is fixed, just remove the
|
|
* defs around the "if (vifs_down)" line.
|
|
*/
|
|
|
|
#if (!((defined SunOS) && (SunOS >= 50)))
|
|
if (vifs_down)
|
|
#endif /* Solaris */
|
|
check_vif_state();
|
|
|
|
/* Age many things */
|
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v)
|
|
{
|
|
if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | MIFF_REGISTER))
|
|
continue;
|
|
|
|
/* Timeout the MLD querier (unless we re the querier) */
|
|
if ((v->uv_flags & VIFF_QUERIER) == 0 &&
|
|
v->uv_querier) { /* this must be non-NULL, but check for safety. */
|
|
IF_TIMEOUT(v->uv_querier->al_timer) {
|
|
v->uv_querier_timo++; /* count statistics */
|
|
|
|
/* act as a querier by myself */
|
|
v->uv_flags |= VIFF_QUERIER;
|
|
v->uv_querier->al_addr = v->uv_linklocal->pa_addr;
|
|
v->uv_querier->al_timer = MLD6_OTHER_QUERIER_PRESENT_INTERVAL;
|
|
time(&v->uv_querier->al_ctime); /* reset timestamp */
|
|
query_groups(v);
|
|
}
|
|
}
|
|
|
|
/* Timeout neighbors */
|
|
for (curr_nbr = v->uv_pim_neighbors; curr_nbr != NULL;
|
|
curr_nbr = next_nbr)
|
|
{
|
|
next_nbr = curr_nbr->next;
|
|
|
|
/*
|
|
* Never timeout neighbors with holdtime = 0xffff. This may be
|
|
* used with ISDN lines to avoid keeping the link up with
|
|
* periodic Hello messages.
|
|
*/
|
|
/* TODO: XXX: TIMER implem. dependency! */
|
|
|
|
if (PIM_MESSAGE_HELLO_HOLDTIME_FOREVER == curr_nbr->timer)
|
|
continue;
|
|
IF_NOT_TIMEOUT(curr_nbr->timer)
|
|
continue;
|
|
|
|
v->uv_pim6_nbr_timo++;
|
|
IF_DEBUG(DEBUG_PIM_HELLO)
|
|
log(LOG_DEBUG, 0,
|
|
"%s on %s is dead , delete it",
|
|
inet6_fmt(&curr_nbr->address.sin6_addr),
|
|
uvifs[curr_nbr->vifi].uv_name);
|
|
delete_pim6_nbr(curr_nbr);
|
|
}
|
|
|
|
/* PIM_HELLO periodic */
|
|
IF_TIMEOUT(v->uv_pim_hello_timer)
|
|
send_pim6_hello(v, pim_hello_holdtime);
|
|
|
|
/* MLD6 query periodic */
|
|
IF_TIMEOUT(v->uv_gq_timer)
|
|
query_groups(v);
|
|
}
|
|
|
|
IF_DEBUG(DEBUG_IF) {
|
|
dump_vifs(log_fp);
|
|
;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan the whole routing table and timeout a bunch of timers:
|
|
* - oifs timers
|
|
* - Join/Prune timer
|
|
* - routing entry
|
|
* - Assert timer
|
|
* - Register-Suppression timer
|
|
*
|
|
* - If the global timer for checking the unicast routing has expired, perform
|
|
* also iif/upstream router change verification
|
|
* - If the global timer for checking the data rate has expired, check the
|
|
* number of bytes forwarded after the lastest timeout. If bigger than
|
|
* a given threshold, then switch to the shortest path.
|
|
* If `number_of_bytes == 0`, then delete the kernel cache entry.
|
|
*
|
|
* Only the entries which have the Join/Prune timer expired are sent.
|
|
* In the special case when we have ~(S,G)RPbit Prune entry, we must
|
|
* include any (*,G) or (*,*,RP) XXX: ???? what and why?
|
|
*
|
|
* Below is a table which summarizes the segmantic rules.
|
|
*
|
|
* On the left side is "if A must be included in the J/P message".
|
|
* On the top is "shall/must include B?"
|
|
* "Y" means "MUST include"
|
|
* "SY" means "SHOULD include"
|
|
* "N" means "NO NEED to include"
|
|
* (G is a group that matches to RP)
|
|
*
|
|
* -----------||-----------||-----------
|
|
* || (*,*,RP) || (*,G) || (S,G) ||
|
|
* ||-----------||-----------||-----------||
|
|
* || J | P || J | P || J | P ||
|
|
* ==================================================||
|
|
* J || n/a | n/a || N | Y || N | Y ||
|
|
* (*,*,RP) -----------------------------------------||
|
|
* P || n/a | n/a || SY | N || SY | N ||
|
|
* ==================================================||
|
|
* J || N | N || n/a | n/a || N | Y ||
|
|
* (*,G) -----------------------------------------||
|
|
* P || N | N || n/a | n/a || SY | N ||
|
|
* ==================================================||
|
|
* J || N | N || N | N || n/a | n/a ||
|
|
* (S,G) -----------------------------------------||
|
|
* P || N | N || N | N || n/a | n/a ||
|
|
* ==================================================
|
|
*
|
|
*/
|
|
|
|
void
|
|
age_routes()
|
|
{
|
|
cand_rp_t *cand_rp_ptr;
|
|
grpentry_t *grpentry_ptr;
|
|
grpentry_t *grpentry_ptr_next;
|
|
mrtentry_t *mrtentry_grp;
|
|
mrtentry_t *mrtentry_rp;
|
|
mrtentry_t *mrtentry_wide;
|
|
mrtentry_t *mrtentry_srcs;
|
|
mrtentry_t *mrtentry_srcs_next;
|
|
struct uvif *v;
|
|
vifi_t vifi;
|
|
pim_nbr_entry_t *pim_nbr_ptr;
|
|
int change_flag;
|
|
int rp_action,
|
|
grp_action,
|
|
src_action=0,
|
|
src_action_rp=0;
|
|
int dont_calc_action;
|
|
int did_switch_flag;
|
|
rp_grp_entry_t *rp_grp_entry_ptr;
|
|
kernel_cache_t *kernel_cache_ptr;
|
|
kernel_cache_t *kernel_cache_next;
|
|
u_long curr_bytecnt;
|
|
rpentry_t *rpentry_ptr;
|
|
int update_rp_iif;
|
|
int update_src_iif;
|
|
if_set new_pruned_oifs;
|
|
|
|
/*
|
|
* Timing out of the global `unicast_routing_timer` and `data_rate_timer`
|
|
*/
|
|
|
|
IF_TIMEOUT(unicast_routing_timer)
|
|
{
|
|
ucast_flag = TRUE;
|
|
SET_TIMER(unicast_routing_timer, unicast_routing_check_interval);
|
|
}
|
|
ELSE
|
|
{
|
|
ucast_flag = FALSE;
|
|
}
|
|
|
|
IF_TIMEOUT(pim_data_rate_timer)
|
|
{
|
|
pim_data_rate_flag = TRUE;
|
|
SET_TIMER(pim_data_rate_timer, pim_data_rate_check_interval);
|
|
}
|
|
ELSE
|
|
{
|
|
pim_data_rate_flag = FALSE;
|
|
}
|
|
|
|
IF_TIMEOUT(pim_reg_rate_timer)
|
|
{
|
|
pim_reg_rate_flag = TRUE;
|
|
SET_TIMER(pim_reg_rate_timer, pim_reg_rate_check_interval);
|
|
}
|
|
ELSE
|
|
{
|
|
pim_reg_rate_flag = FALSE;
|
|
}
|
|
|
|
rate_flag = pim_data_rate_flag | pim_reg_rate_flag;
|
|
|
|
/* Scan the (*,*,RP) entries */
|
|
|
|
for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *) NULL;
|
|
cand_rp_ptr = cand_rp_ptr->next)
|
|
{
|
|
|
|
rpentry_ptr = cand_rp_ptr->rpentry;
|
|
|
|
/*
|
|
* Need to save only `incoming` and `upstream` to discover unicast
|
|
* route changes. `metric` and `preference` are not interesting for
|
|
* us.
|
|
*/
|
|
|
|
rpentry_save.incoming = rpentry_ptr->incoming;
|
|
rpentry_save.upstream = rpentry_ptr->upstream;
|
|
|
|
update_rp_iif = FALSE;
|
|
if ((ucast_flag == TRUE) &&
|
|
(!inet6_equal(&rpentry_ptr->address ,&my_cand_rp_address)))
|
|
{
|
|
/*
|
|
* I am not the RP. If I was the RP, then the iif is register_vif
|
|
* and no need to reset it.
|
|
*/
|
|
|
|
if (set_incoming(rpentry_ptr, PIM_IIF_RP) != TRUE)
|
|
{
|
|
/*
|
|
* TODO: XXX: no route to that RP. Panic? There is a high
|
|
* probability the network is partitioning so immediately
|
|
* remapping to other RP is not a good idea. Better wait the
|
|
* Bootstrap mechanism to take care of it and provide me with
|
|
* correct Cand-RP-Set.
|
|
*/
|
|
;
|
|
}
|
|
else
|
|
{
|
|
if ((rpentry_save.upstream != rpentry_ptr->upstream)
|
|
|| (rpentry_save.incoming != rpentry_ptr->incoming))
|
|
{
|
|
/*
|
|
* Routing change has occur. Update all (*,G) and
|
|
* (S,G)RPbit iifs mapping to that RP
|
|
*/
|
|
update_rp_iif = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
rp_action = PIM_ACTION_NOTHING;
|
|
mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
|
|
|
|
if (mrtentry_rp != (mrtentry_t *) NULL)
|
|
{
|
|
/* outgoing interfaces timers */
|
|
|
|
change_flag = FALSE;
|
|
for (vifi = 0; vifi < numvifs; vifi++)
|
|
{
|
|
if (IF_ISSET(vifi, &mrtentry_rp->joined_oifs))
|
|
IF_TIMEOUT(mrtentry_rp->vif_timers[vifi])
|
|
{
|
|
uvifs[vifi].uv_outif_timo++;
|
|
IF_CLR(vifi, &mrtentry_rp->joined_oifs);
|
|
change_flag = TRUE;
|
|
}
|
|
}
|
|
if ((change_flag == TRUE) || (update_rp_iif == TRUE))
|
|
{
|
|
change_interfaces(mrtentry_rp,
|
|
rpentry_ptr->incoming,
|
|
&mrtentry_rp->joined_oifs,
|
|
&mrtentry_rp->pruned_oifs,
|
|
&mrtentry_rp->leaves,
|
|
&mrtentry_rp->asserted_oifs, 0);
|
|
mrtentry_rp->upstream = rpentry_ptr->upstream;
|
|
}
|
|
|
|
if (rate_flag == TRUE)
|
|
{
|
|
/* Check the activity for this entry */
|
|
|
|
/*
|
|
* XXX: the spec says to start monitoring first the total
|
|
* traffic for all senders for particular (*,*,RP) or (*,G)
|
|
* and if the total traffic exceeds some predefined
|
|
* threshold, then start monitoring the data traffic for each
|
|
* particular sender for this group: (*,G) or (*,*,RP).
|
|
* However, because the kernel cache/traffic info is of the
|
|
* form (S,G), it is easier if we are simply collecting (S,G)
|
|
* traffic all the time.
|
|
*
|
|
* For (*,*,RP) if the number of bytes received between the last
|
|
* check and now exceeds some precalculated value (based on
|
|
* interchecking period and datarate threshold AND if there
|
|
* are directly connected members (i.e. we are their last
|
|
* hop(e) router), then create (S,G) and start initiating
|
|
* (S,G) Join toward the source. The same applies for (*,G).
|
|
* The spec does not say that if the datarate goes below a
|
|
* given threshold, then will switch back to the shared tree,
|
|
* hence after a switch to the source-specific tree occurs, a
|
|
* source with low datarate, but periodically sending will
|
|
* keep the (S,G) states.
|
|
*
|
|
* If a source with kernel cache entry has been idle after the
|
|
* last time a check of the datarate for the whole routing
|
|
* table, then delete its kernel cache entry.
|
|
*/
|
|
for (kernel_cache_ptr = mrtentry_rp->kernel_cache;
|
|
kernel_cache_ptr != (kernel_cache_t *) NULL;
|
|
kernel_cache_ptr = kernel_cache_next)
|
|
{
|
|
kernel_cache_next = kernel_cache_ptr->next;
|
|
curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
|
|
if (k_get_sg_cnt(udp_socket, &kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group,
|
|
&kernel_cache_ptr->sg_count)
|
|
|| (curr_bytecnt ==
|
|
kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
/*
|
|
* Either for some reason there is no such routing
|
|
* entry or that particular (s,g) was idle. Delete
|
|
* the routing entry from the kernel.
|
|
*/
|
|
|
|
delete_single_kernel_cache(mrtentry_rp,
|
|
kernel_cache_ptr);
|
|
continue;
|
|
}
|
|
/*
|
|
* Check if the datarate was high enough to switch to
|
|
* source specific tree.
|
|
*/
|
|
/* Forwarder initiated switch */
|
|
|
|
did_switch_flag = FALSE;
|
|
if (curr_bytecnt + pim_data_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt)
|
|
{
|
|
if (vif_forwarder(&mrtentry_rp->leaves,
|
|
&mrtentry_rp->oifs) == TRUE)
|
|
{
|
|
#ifdef KERNEL_MFC_WC_G
|
|
// TODO (one day... :))
|
|
if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
|
|
{
|
|
delete_single_kernel_cache(mrtentry_rp,
|
|
kernel_cache_ptr);
|
|
mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
|
|
continue;
|
|
}
|
|
#endif /* KERNEL_MFC_WC_G */
|
|
pim6dstat.pim6_trans_spt_forward++;
|
|
switch_shortest_path(&kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group);
|
|
did_switch_flag = TRUE;
|
|
}
|
|
}
|
|
|
|
/* RP initiated switch */
|
|
|
|
if ((did_switch_flag == FALSE)
|
|
&& (curr_bytecnt + pim_reg_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
if (mrtentry_rp->incoming == reg_vif_num)
|
|
|
|
#ifdef KERNEL_MFC_WC_G
|
|
// TODO (one day :))
|
|
if (kernel_cache_ptr->source == IN6ADDR_ANY_N)
|
|
{
|
|
delete_single_kernel_cache(mrtentry_rp,
|
|
kernel_cache_ptr);
|
|
mrtentry_rp->flags |= MRTF_MFC_CLONE_SG;
|
|
continue;
|
|
}
|
|
#endif /* KERNEL_MFC_WC_G */
|
|
pim6dstat.pim6_trans_spt_rp++;
|
|
switch_shortest_path(&kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Join/Prune timer */
|
|
IF_TIMEOUT(mrtentry_rp->jp_timer)
|
|
{
|
|
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"Join/Prune timer expired");
|
|
|
|
rp_action = join_or_prune(mrtentry_rp,
|
|
mrtentry_rp->upstream);
|
|
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"rp_action = %d",rp_action);
|
|
|
|
if (rp_action != PIM_ACTION_NOTHING)
|
|
add_jp_entry(mrtentry_rp->upstream,
|
|
pim_join_prune_holdtime,
|
|
&sockaddr6_d,
|
|
STAR_STAR_RP_MSK6LEN,
|
|
&mrtentry_rp->source->address,
|
|
SINGLE_SRC_MSK6LEN,
|
|
MRTF_RP | MRTF_WC,
|
|
rp_action);
|
|
SET_TIMER(mrtentry_rp->jp_timer, pim_join_prune_period);
|
|
}
|
|
|
|
/* Assert timer */
|
|
if (mrtentry_rp->flags & MRTF_ASSERTED)
|
|
{
|
|
IF_TIMEOUT(mrtentry_rp->assert_timer)
|
|
{
|
|
/* TODO: XXX: reset the upstream router now */
|
|
mrtentry_rp->flags &= ~MRTF_ASSERTED;
|
|
}
|
|
}
|
|
/* Register-Suppression timer */
|
|
/*
|
|
* TODO: to reduce the kernel calls, if the timer is running,
|
|
* install a negative cache entry in the kernel?
|
|
*/
|
|
/*
|
|
* TODO: can we have Register-Suppression timer for (*,*,RP)?
|
|
* Currently no...
|
|
*/
|
|
|
|
IF_TIMEOUT(mrtentry_rp->rs_timer)
|
|
;
|
|
/* routing entry */
|
|
|
|
if ((TIMEOUT(mrtentry_rp->timer))
|
|
&& (IF_ISEMPTY(&mrtentry_rp->leaves)))
|
|
{
|
|
pim6dstat.pim6_rtentry_timo++;
|
|
delete_mrtentry(mrtentry_rp);
|
|
}
|
|
} /* mrtentry_rp != NULL */
|
|
|
|
/* Just in case if that (*,*,RP) was deleted */
|
|
|
|
mrtentry_rp = cand_rp_ptr->rpentry->mrtlink;
|
|
|
|
/* Check the (*,G) and (S,G) entries */
|
|
|
|
for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
|
|
rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
|
|
rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next)
|
|
{
|
|
|
|
for (grpentry_ptr = rp_grp_entry_ptr->grplink;
|
|
grpentry_ptr != (grpentry_t *) NULL;
|
|
grpentry_ptr = grpentry_ptr_next)
|
|
{
|
|
grpentry_ptr_next = grpentry_ptr->rpnext;
|
|
mrtentry_grp = grpentry_ptr->grp_route;
|
|
mrtentry_srcs = grpentry_ptr->mrtlink;
|
|
|
|
grp_action = PIM_ACTION_NOTHING;
|
|
if (mrtentry_grp != (mrtentry_t *) NULL)
|
|
{
|
|
/* The (*,G) entry */
|
|
/* outgoing interfaces timers */
|
|
|
|
change_flag = FALSE;
|
|
for (vifi = 0; vifi < numvifs; vifi++)
|
|
{
|
|
if (IF_ISSET(vifi, &mrtentry_grp->joined_oifs))
|
|
IF_TIMEOUT(mrtentry_grp->vif_timers[vifi])
|
|
{
|
|
IF_CLR(vifi, &mrtentry_grp->joined_oifs);
|
|
uvifs[vifi].uv_outif_timo++;
|
|
change_flag = TRUE;
|
|
}
|
|
}
|
|
|
|
if ((change_flag == TRUE) || (update_rp_iif == TRUE))
|
|
{
|
|
change_interfaces(mrtentry_grp,
|
|
rpentry_ptr->incoming,
|
|
&mrtentry_grp->joined_oifs,
|
|
&mrtentry_grp->pruned_oifs,
|
|
&mrtentry_grp->leaves,
|
|
&mrtentry_grp->asserted_oifs, 0);
|
|
mrtentry_grp->upstream = rpentry_ptr->upstream;
|
|
}
|
|
|
|
/* Check the sources activity */
|
|
|
|
if (rate_flag == TRUE)
|
|
{
|
|
for (kernel_cache_ptr = mrtentry_grp->kernel_cache;
|
|
kernel_cache_ptr != (kernel_cache_t *) NULL;
|
|
kernel_cache_ptr = kernel_cache_next)
|
|
{
|
|
kernel_cache_next = kernel_cache_ptr->next;
|
|
curr_bytecnt =
|
|
kernel_cache_ptr->sg_count.bytecnt;
|
|
if (k_get_sg_cnt(udp_socket,
|
|
&kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group,
|
|
&kernel_cache_ptr->sg_count)
|
|
|| (curr_bytecnt ==
|
|
kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
/*
|
|
* Either for whatever reason there is no
|
|
* such routing entry or that particular
|
|
* (s,g) was idle. Delete the routing entry
|
|
* from the kernel.
|
|
*/
|
|
|
|
delete_single_kernel_cache(mrtentry_grp,
|
|
kernel_cache_ptr);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Check if the datarate was high enough to
|
|
* switch to source specific tree.
|
|
*/
|
|
/* Forwarder initiated switch */
|
|
|
|
did_switch_flag = FALSE;
|
|
if (curr_bytecnt + pim_data_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt)
|
|
{
|
|
if (vif_forwarder(&mrtentry_grp->leaves,
|
|
&mrtentry_grp->oifs) == TRUE)
|
|
{
|
|
#ifdef KERNEL_MFC_WC_G
|
|
// TODO
|
|
if (kernel_cache_ptr->source
|
|
== IN6ADDR_ANY_N)
|
|
{
|
|
delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
|
|
mrtentry_grp->flags
|
|
|= MRTF_MFC_CLONE_SG;
|
|
continue;
|
|
}
|
|
#endif /* KERNEL_MFC_WC_G */
|
|
|
|
pim6dstat.pim6_trans_spt_forward++;
|
|
switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
|
|
did_switch_flag = TRUE;
|
|
}
|
|
}
|
|
|
|
/* RP initiated switch */
|
|
|
|
if ((did_switch_flag == FALSE)
|
|
&& (curr_bytecnt + pim_reg_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
if (mrtentry_grp->incoming == reg_vif_num)
|
|
#ifdef KERNEL_MFC_WC_G
|
|
// TODO
|
|
if (kernel_cache_ptr->source
|
|
== IN6ADDR_ANY_N)
|
|
{
|
|
delete_single_kernel_cache(mrtentry_grp, kernel_cache_ptr);
|
|
mrtentry_grp->flags
|
|
|= MRTF_MFC_CLONE_SG;
|
|
continue;
|
|
}
|
|
#endif /* KERNEL_MFC_WC_G */
|
|
pim6dstat.pim6_trans_spt_rp++;
|
|
switch_shortest_path(&kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group);
|
|
}
|
|
}
|
|
}
|
|
|
|
dont_calc_action = FALSE;
|
|
if (rp_action != PIM_ACTION_NOTHING)
|
|
{
|
|
grp_action = join_or_prune(mrtentry_grp,
|
|
mrtentry_grp->upstream);
|
|
dont_calc_action = TRUE;
|
|
if (((rp_action == PIM_ACTION_JOIN)
|
|
&& (grp_action == PIM_ACTION_PRUNE))
|
|
|| ((rp_action == PIM_ACTION_PRUNE)
|
|
&& (grp_action == PIM_ACTION_JOIN)))
|
|
FIRE_TIMER(mrtentry_grp->jp_timer);
|
|
}
|
|
|
|
/* Join/Prune timer */
|
|
|
|
IF_TIMEOUT(mrtentry_grp->jp_timer)
|
|
{
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"Join/Prune timer expired");
|
|
|
|
if (dont_calc_action != TRUE)
|
|
grp_action = join_or_prune(mrtentry_grp,
|
|
mrtentry_grp->upstream);
|
|
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"grp_action = %d",grp_action);
|
|
|
|
if (grp_action != PIM_ACTION_NOTHING)
|
|
{
|
|
add_jp_entry(mrtentry_grp->upstream,
|
|
pim_join_prune_holdtime,
|
|
&mrtentry_grp->group->group,
|
|
SINGLE_GRP_MSK6LEN,
|
|
&cand_rp_ptr->rpentry->address,
|
|
SINGLE_SRC_MSK6LEN,
|
|
MRTF_RP | MRTF_WC,
|
|
grp_action);
|
|
}
|
|
|
|
SET_TIMER(mrtentry_grp->jp_timer, pim_join_prune_period);
|
|
}
|
|
|
|
/* Assert timer */
|
|
if (mrtentry_grp->flags & MRTF_ASSERTED)
|
|
{
|
|
IF_TIMEOUT(mrtentry_grp->assert_timer)
|
|
{
|
|
/* TODO: XXX: reset the upstream router now */
|
|
mrtentry_grp->flags &= ~MRTF_ASSERTED;
|
|
}
|
|
}
|
|
/* Register-Suppression timer */
|
|
/*
|
|
* TODO: to reduce the kernel calls, if the timer is
|
|
* running, install a negative cache entry in the kernel?
|
|
*/
|
|
/*
|
|
* TODO: currently cannot have Register-Suppression timer
|
|
* for (*,G) entry, but keep this around.
|
|
*/
|
|
IF_TIMEOUT(mrtentry_grp->rs_timer)
|
|
;
|
|
|
|
/* routing entry */
|
|
|
|
if ((TIMEOUT(mrtentry_grp->timer))
|
|
&& (IF_ISEMPTY(&mrtentry_grp->leaves)))
|
|
{
|
|
pim6dstat.pim6_rtentry_timo++;
|
|
delete_mrtentry(mrtentry_grp);
|
|
}
|
|
} /* if (mrtentry_grp != NULL) */
|
|
|
|
|
|
/* For all (S,G) for this group */
|
|
/* XXX: mrtentry_srcs was set before */
|
|
for (; mrtentry_srcs != (mrtentry_t *) NULL;
|
|
mrtentry_srcs = mrtentry_srcs_next)
|
|
{
|
|
/* routing entry */
|
|
mrtentry_srcs_next = mrtentry_srcs->grpnext;
|
|
|
|
/* outgoing interfaces timers */
|
|
|
|
change_flag = FALSE;
|
|
for (vifi = 0; vifi < numvifs; vifi++)
|
|
{
|
|
if (IF_ISSET(vifi, &mrtentry_srcs->joined_oifs))
|
|
{
|
|
/* TODO: checking for reg_num_vif is slow! */
|
|
|
|
if (vifi != reg_vif_num)
|
|
{
|
|
IF_TIMEOUT(mrtentry_srcs->vif_timers[vifi])
|
|
{
|
|
IF_CLR(vifi,
|
|
&mrtentry_srcs->joined_oifs);
|
|
change_flag = TRUE;
|
|
uvifs[vifi].uv_outif_timo++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
update_src_iif = FALSE;
|
|
if (ucast_flag == TRUE)
|
|
{
|
|
if (!(mrtentry_srcs->flags & MRTF_RP))
|
|
{
|
|
/* iif toward the source */
|
|
srcentry_save.incoming =
|
|
mrtentry_srcs->source->incoming;
|
|
srcentry_save.upstream =
|
|
mrtentry_srcs->source->upstream;
|
|
if (set_incoming(mrtentry_srcs->source,
|
|
PIM_IIF_SOURCE) != TRUE)
|
|
{
|
|
|
|
/*
|
|
* XXX: not in the spec! Cannot find route
|
|
* toward that source. This is bad. Delete
|
|
* the entry.
|
|
*/
|
|
|
|
delete_mrtentry(mrtentry_srcs);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* iif info found */
|
|
|
|
if ((srcentry_save.incoming !=
|
|
mrtentry_srcs->incoming)
|
|
|| (srcentry_save.upstream !=
|
|
mrtentry_srcs->upstream))
|
|
{
|
|
/* Route change has occur */
|
|
update_src_iif = TRUE;
|
|
mrtentry_srcs->incoming =
|
|
mrtentry_srcs->source->incoming;
|
|
mrtentry_srcs->upstream =
|
|
mrtentry_srcs->source->upstream;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* (S,G)RPBit with iif toward RP */
|
|
if ((rpentry_save.upstream !=
|
|
mrtentry_srcs->upstream)
|
|
|| (rpentry_save.incoming !=
|
|
mrtentry_srcs->incoming))
|
|
{
|
|
update_src_iif = TRUE; /* XXX: a hack */
|
|
/* XXX: setup the iif now! */
|
|
mrtentry_srcs->incoming =
|
|
rpentry_ptr->incoming;
|
|
mrtentry_srcs->upstream =
|
|
rpentry_ptr->upstream;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((change_flag == TRUE) || (update_src_iif == TRUE))
|
|
/* Flush the changes */
|
|
change_interfaces(mrtentry_srcs,
|
|
mrtentry_srcs->incoming,
|
|
&mrtentry_srcs->joined_oifs,
|
|
&mrtentry_srcs->pruned_oifs,
|
|
&mrtentry_srcs->leaves,
|
|
&mrtentry_srcs->asserted_oifs, 0);
|
|
|
|
if (rate_flag == TRUE)
|
|
{
|
|
for (kernel_cache_ptr = mrtentry_srcs->kernel_cache;
|
|
kernel_cache_ptr != (kernel_cache_t *) NULL;
|
|
kernel_cache_ptr = kernel_cache_next)
|
|
{
|
|
kernel_cache_next = kernel_cache_ptr->next;
|
|
curr_bytecnt = kernel_cache_ptr->sg_count.bytecnt;
|
|
if (k_get_sg_cnt(udp_socket,
|
|
&kernel_cache_ptr->source,
|
|
&kernel_cache_ptr->group,
|
|
&kernel_cache_ptr->sg_count)
|
|
|| (curr_bytecnt ==
|
|
kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
/*
|
|
* Either for some reason there is no such
|
|
* routing entry or that particular (s,g) was
|
|
* idle. Delete the routing entry from the
|
|
* kernel.
|
|
*/
|
|
|
|
delete_single_kernel_cache(mrtentry_srcs,
|
|
kernel_cache_ptr);
|
|
continue;
|
|
}
|
|
/*
|
|
* Check if the datarate was high enough to
|
|
* switch to source specific tree. Need to check
|
|
* only when we have (S,G)RPbit in the forwarder
|
|
* or the RP itself.
|
|
*/
|
|
|
|
if (!(mrtentry_srcs->flags & MRTF_RP))
|
|
continue;
|
|
|
|
/* Forwarder initiated switch */
|
|
|
|
did_switch_flag = FALSE;
|
|
if (curr_bytecnt + pim_data_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt)
|
|
{
|
|
if (vif_forwarder(&mrtentry_srcs->leaves,
|
|
&mrtentry_srcs->oifs)
|
|
== TRUE)
|
|
{
|
|
switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
|
|
did_switch_flag = TRUE;
|
|
}
|
|
}
|
|
/* RP initiated switch */
|
|
if ((did_switch_flag == FALSE)
|
|
&& (curr_bytecnt + pim_reg_rate_bytes
|
|
< kernel_cache_ptr->sg_count.bytecnt))
|
|
{
|
|
if (mrtentry_srcs->incoming == reg_vif_num)
|
|
switch_shortest_path(&kernel_cache_ptr->source, &kernel_cache_ptr->group);
|
|
}
|
|
|
|
/*
|
|
* XXX: currentry the spec doesn't say to switch
|
|
* back to the shared tree if low datarate, but
|
|
* if needed to implement, the check must be done
|
|
* here. Don't forget to check whether I am a
|
|
* forwarder for that source.
|
|
*/
|
|
}
|
|
}
|
|
|
|
mrtentry_wide = mrtentry_srcs->group->grp_route;
|
|
if (mrtentry_wide == (mrtentry_t *) NULL)
|
|
mrtentry_wide = mrtentry_rp;
|
|
|
|
dont_calc_action = FALSE;
|
|
if ((rp_action != PIM_ACTION_NOTHING)
|
|
|| (grp_action != PIM_ACTION_NOTHING))
|
|
{
|
|
src_action_rp = join_or_prune(mrtentry_srcs,
|
|
rpentry_ptr->upstream);
|
|
src_action = src_action_rp;
|
|
dont_calc_action = TRUE;
|
|
if (src_action_rp == PIM_ACTION_JOIN)
|
|
{
|
|
if ((grp_action == PIM_ACTION_PRUNE)
|
|
|| (rp_action == PIM_ACTION_PRUNE))
|
|
FIRE_TIMER(mrtentry_srcs->jp_timer);
|
|
}
|
|
else
|
|
if (src_action_rp == PIM_ACTION_PRUNE)
|
|
{
|
|
if ((grp_action == PIM_ACTION_JOIN)
|
|
|| (rp_action == PIM_ACTION_JOIN))
|
|
FIRE_TIMER(mrtentry_srcs->jp_timer);
|
|
}
|
|
}
|
|
|
|
/* Join/Prune timer */
|
|
|
|
IF_TIMEOUT(mrtentry_srcs->jp_timer)
|
|
{
|
|
if ((dont_calc_action != TRUE)
|
|
|| (rpentry_ptr->upstream
|
|
!= mrtentry_srcs->upstream))
|
|
src_action = join_or_prune(mrtentry_srcs,
|
|
mrtentry_srcs->upstream);
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"src_action = %d",src_action);
|
|
|
|
if (src_action != PIM_ACTION_NOTHING)
|
|
add_jp_entry(mrtentry_srcs->upstream,
|
|
pim_join_prune_holdtime,
|
|
&mrtentry_srcs->group->group,
|
|
SINGLE_GRP_MSK6LEN,
|
|
&mrtentry_srcs->source->address,
|
|
SINGLE_SRC_MSK6LEN,
|
|
mrtentry_srcs->flags & MRTF_RP,
|
|
src_action);
|
|
if (mrtentry_wide != (mrtentry_t *) NULL)
|
|
{
|
|
/*
|
|
* Have both (S,G) and (*,G) (or (*,*,RP)). Check
|
|
* if need to send (S,G) PRUNE toward RP
|
|
*/
|
|
|
|
if (mrtentry_srcs->upstream
|
|
!= mrtentry_wide->upstream)
|
|
{
|
|
if (dont_calc_action != TRUE)
|
|
src_action_rp =
|
|
join_or_prune(mrtentry_srcs,
|
|
mrtentry_wide->upstream);
|
|
/*
|
|
* XXX: TODO: do error check if src_action ==
|
|
* PIM_ACTION_JOIN, which should be an error.
|
|
*/
|
|
|
|
if (src_action_rp == PIM_ACTION_PRUNE)
|
|
{
|
|
IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
|
|
log(LOG_DEBUG,0,"src_action = %d",src_action);
|
|
add_jp_entry(mrtentry_wide->upstream,
|
|
pim_join_prune_holdtime,
|
|
&mrtentry_srcs->group->group,
|
|
SINGLE_GRP_MSK6LEN,
|
|
&mrtentry_srcs->source->address,
|
|
SINGLE_SRC_MSK6LEN,
|
|
MRTF_RP,
|
|
src_action_rp);
|
|
}
|
|
}
|
|
}
|
|
SET_TIMER(mrtentry_srcs->jp_timer, pim_join_prune_period);
|
|
}
|
|
/* Assert timer */
|
|
if (mrtentry_srcs->flags & MRTF_ASSERTED)
|
|
{
|
|
IF_TIMEOUT(mrtentry_srcs->assert_timer)
|
|
{
|
|
/* TODO: XXX: reset the upstream router now */
|
|
mrtentry_srcs->flags &= ~MRTF_ASSERTED;
|
|
}
|
|
}
|
|
/* Register-Suppression timer */
|
|
/*
|
|
* TODO: to reduce the kernel calls, if the timer is
|
|
* running, install a negative cache entry in the kernel?
|
|
*/
|
|
|
|
IF_TIMER_SET(mrtentry_srcs->rs_timer)
|
|
{
|
|
IF_TIMEOUT(mrtentry_srcs->rs_timer)
|
|
{
|
|
/* Start encapsulating the packets */
|
|
IF_COPY(&mrtentry_srcs->pruned_oifs,
|
|
&new_pruned_oifs);
|
|
IF_CLR(reg_vif_num, &new_pruned_oifs);
|
|
change_interfaces(mrtentry_srcs,
|
|
mrtentry_srcs->incoming,
|
|
&mrtentry_srcs->joined_oifs,
|
|
&new_pruned_oifs,
|
|
&mrtentry_srcs->leaves,
|
|
&mrtentry_srcs->asserted_oifs, 0);
|
|
}
|
|
ELSE
|
|
{
|
|
/*
|
|
* The register suppression timer is running.
|
|
* Check whether it is time to send
|
|
* PIM_NULL_REGISTER.
|
|
*/
|
|
/* TODO: XXX: TIMER implem. dependency! */
|
|
|
|
if (mrtentry_srcs->rs_timer
|
|
<= pim_register_probe_time)
|
|
{
|
|
/* Time to send a PIM_NULL_REGISTER */
|
|
/*
|
|
* XXX: a (bad) hack! This will be sending
|
|
* periodically NULL_REGISTERS between
|
|
* pim_register_probe_time and 0. Well,
|
|
* because PROBE_TIME is 5 secs , it will
|
|
* happen only once ( if granularity is 5 and prob 5!)
|
|
* , so it helps to avoid
|
|
* adding a flag to the routing entry whether
|
|
* a NULL_REGISTER was sent.
|
|
*/
|
|
send_pim6_null_register(mrtentry_srcs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* routing entry */
|
|
if (TIMEOUT(mrtentry_srcs->timer))
|
|
{
|
|
pim6dstat.pim6_rtentry_timo++;
|
|
|
|
if (IF_ISEMPTY(&mrtentry_srcs->leaves))
|
|
{
|
|
delete_mrtentry(mrtentry_srcs);
|
|
continue;
|
|
}
|
|
/*
|
|
* XXX: if DR, Register suppressed, and leaf oif
|
|
* inherited from (*,G), the directly connected
|
|
* source is not active anymore, this (S,G) entry
|
|
* won't timeout. Check if the leaf oifs are
|
|
* inherited from (*,G); if true. delete the (S,G)
|
|
* entry.
|
|
*/
|
|
|
|
if (mrtentry_srcs->group->grp_route
|
|
!= (mrtentry_t *) NULL)
|
|
{
|
|
if_set r_and, r_xor;
|
|
vif_and(&mrtentry_srcs->group->grp_route->leaves,
|
|
&mrtentry_srcs->leaves,
|
|
&r_and);
|
|
vif_xor(&r_and ,&mrtentry_srcs->leaves,
|
|
&r_xor);
|
|
if (IF_ISEMPTY(&r_xor))
|
|
{
|
|
delete_mrtentry(mrtentry_srcs);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} /* End of (S,G) loop */
|
|
} /* End of (*,G) loop */
|
|
}
|
|
} /* For all cand RPs */
|
|
|
|
/* TODO: check again! */
|
|
for (vifi = 0, v = &uvifs[0]; vifi < numvifs; vifi++, v++)
|
|
{
|
|
/* Send all pending Join/Prune messages */
|
|
for (pim_nbr_ptr = v->uv_pim_neighbors;
|
|
pim_nbr_ptr != (pim_nbr_entry_t *) NULL;
|
|
pim_nbr_ptr = pim_nbr_ptr->next)
|
|
{
|
|
pack_and_send_jp6_message(pim_nbr_ptr);
|
|
}
|
|
}
|
|
|
|
IF_DEBUG(DEBUG_PIM_MRT)
|
|
dump_pim_mrt(log_fp);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* TODO: timeout the RP-group mapping entries during the scan of the whole
|
|
* routing table?
|
|
*/
|
|
void
|
|
age_misc()
|
|
{
|
|
rp_grp_entry_t *rp_grp_entry_ptr;
|
|
rp_grp_entry_t *rp_grp_entry_next;
|
|
grp_mask_t *grp_mask_ptr;
|
|
grp_mask_t *grp_mask_next;
|
|
|
|
/* Timeout the Cand-RP-set entries */
|
|
for (grp_mask_ptr = grp_mask_list;
|
|
grp_mask_ptr != (grp_mask_t *) NULL;
|
|
grp_mask_ptr = grp_mask_next)
|
|
{
|
|
/*
|
|
* If we timeout an entry, the grp_mask_ptr entry might be removed.
|
|
*/
|
|
grp_mask_next = grp_mask_ptr->next;
|
|
for (rp_grp_entry_ptr = grp_mask_ptr->grp_rp_next;
|
|
rp_grp_entry_ptr != (rp_grp_entry_t *) NULL;
|
|
rp_grp_entry_ptr = rp_grp_entry_next)
|
|
{
|
|
rp_grp_entry_next = rp_grp_entry_ptr->grp_rp_next;
|
|
IF_TIMEOUT(rp_grp_entry_ptr->holdtime) {
|
|
delete_rp_grp_entry(&cand_rp_list, &grp_mask_list,
|
|
rp_grp_entry_ptr);
|
|
pim6dstat.pim6_rpgrp_timo++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cand-RP-Adv timer */
|
|
if (cand_rp_flag == TRUE)
|
|
{
|
|
IF_TIMEOUT(pim_cand_rp_adv_timer)
|
|
{
|
|
send_pim6_cand_rp_adv();
|
|
SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
|
|
}
|
|
}
|
|
|
|
/* bootstrap-timer */
|
|
|
|
IF_TIMEOUT(pim_bootstrap_timer)
|
|
{
|
|
pim6dstat.pim6_bootstrap_timo++;
|
|
|
|
if (cand_bsr_flag == FALSE)
|
|
{
|
|
/*
|
|
* If I am not Cand-BSR, start accepting Bootstrap messages from
|
|
* anyone. XXX: Even if the BSR has timeout, the existing
|
|
* Cand-RP-Set is kept.
|
|
*/
|
|
curr_bsr_fragment_tag = 0;
|
|
curr_bsr_priority = 0; /* Lowest priority */
|
|
memset(&curr_bsr_address, 0, sizeof(struct sockaddr_in6));
|
|
curr_bsr_address.sin6_len = sizeof(struct sockaddr_in6);
|
|
curr_bsr_address.sin6_family = AF_INET6;
|
|
MASKLEN_TO_MASK6(RP_DEFAULT_IPV6_HASHMASKLEN, curr_bsr_hash_mask);
|
|
SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
|
|
}
|
|
else
|
|
{
|
|
/* I am Cand-BSR, so set the current BSR to me */
|
|
if (inet6_equal(&curr_bsr_address, &my_bsr_address))
|
|
{
|
|
SET_TIMER(pim_bootstrap_timer, my_bsr_period);
|
|
send_pim6_bootstrap();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Short delay before becoming the BSR and start sending of
|
|
* the Cand-RP set (to reduce the transient control
|
|
* overhead).
|
|
*/
|
|
SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
|
|
curr_bsr_fragment_tag = RANDOM();
|
|
curr_bsr_priority = my_bsr_priority;
|
|
curr_bsr_address = my_bsr_address;
|
|
memcpy(&curr_bsr_hash_mask , &my_bsr_hash_mask , sizeof(struct in6_addr));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
IF_DEBUG(DEBUG_PIM_BOOTSTRAP | DEBUG_PIM_CAND_RP)
|
|
dump_rp_set(log_fp);
|
|
/* TODO: XXX: anything else to timeout */
|
|
}
|