/* * 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 #include #include "pimd.h" #include "mrt.h" #include "vif.h" #include #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 */ }