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