/* * Copyright (c) 1998 by the University of Oregon. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation in source and binary forms for lawful * non-commercial purposes and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both * the copyright notice and this permission notice appear in supporting * documentation, and that any documentation, advertising materials, * and other materials related to such distribution and use acknowledge * that the software was developed by the University of Oregon. * The name of the University of Oregon may not * be used to endorse or promote products derived from this software * without specific prior written permission. * * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND * NON-INFRINGEMENT. * * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, * THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Questions concerning this software should be directed to * Kurt Windisch (kurtw@antc.uoregon.edu) at the University of Oregon's * Advanced Network Technology Center (ANTC). * * Portions of this code are derived from the PIMSM implementation * for GateD by George Eddy (eddy@isi.edu). * * $Id: pimdm.c,v 1.24 1998/12/03 19:50:59 kurtw Exp $ */ /* NOTES: * * --- this implementation is yet untested for point-to-point interfaces, * meaning that prune rate limiting due to data on a p2p oif is * untested, among other things. * * --- This implementation complies with the pimdm specification with * the following exceptions: * * equal-cost multipaths (TBD) * * Prunes are not sent in response to data on point-to-point * links for negative cache (S,G) entries (Unix kernel does not * upcall for this). * * * DSPROTO Meanings: * * DSPROTO_MROUTE - The interface is a link on the PIMDM multicast tree, * either in forwarding or prune state. I.e, this router * has downstream neighbor routers. * * set when (1) source downstream interface entry * created with neighbors present, or (2) new PIM * neighbors recognized. * * reset when PIM neighbors leave * * DSPROTO_IGMP - The link has directly connected receivers * * set when aux_join is received * * reset when aux_prune received * * DSPROTO_STATIC - The link is statically configured for forwarding * * set when aux_join received * * reset when aux_prune received * */ /* * TODO: * - Detect equal cost multipaths and correclty choose a single upstream * neighbor. */ /* CHANGES: * * 12/2/98 Fixed pimdm_sg_creation_recv() and pimdm_assert_downstream. * PIMDM oifs are now merged with the kernel request's * downstream list instead of being added individually. * 12/2/98 Changed the amount of time that scheduled prunes are * delayed. Different pimdm spec revisions are inconsistent * in their wording, but it currently says that joins should * be sent in time less-than-or-equal-to 3 seconds. Thus, * prunes should be scheduled in time strictly greater-than * 3 seconds (4 seconds for gated). Regardless, it doesn't * hurt compatibility to delay an extra second. * 8/21/98 Fixed sender-is-member to only check for INTERNALLY-reached * sources. * 7/9/98 Fixed bug in prune receipt addressed to another router: * don't send join if we don't have an upstream neighbor * (i.e., we're directly connected). * 6/19/98 Changed Join and Prune delays back to 3 seconds for * compliance with the latest PIMDM spec revision, which * does NOT use the PIMSM delay of 4.5 seconds. * 6/18/98 Added pimdm-sender-is-member heuristic to the parser and * pimdm. If used, interest is only registered for groups * for which we have (S,G) state. For more info on this * refer to draft-thaler-multicast-interop-02.txt. * 6/17/98 nbr_timeout now deletes (S,G) entries for which the upstream * neighbor has timed out. A timed out upstream neighbor would * mean that you now have incongruent unicast and multicast * topologies and you are disconnected from the RPF tree. * 6/17/98 Fixed shutdown to unregister interest in groups with the mbr * 5/27/98 Fixed holdtime in prunes sent by assert winner. * 5/06/98 Fixed assert handling of preferences and metrics. * 5/06/98 Unregisters interest in (*,*) when terminating * 5/05/98 (pim_mib.c) fixed find_pimnbr so that searching accross * multiple interfaces works (doesn't return after failing on first) * 4/16/98 (pim.c) fixed graft tracing in pim_trace. * 4/16/98 Added check for interface owner when sending mbr alert- * triggered prunes and grafts. * 4/9/98 Fixed igmp and static joins and prunes: Now everything * happens through pimdm_aux_*_recv as specified by MBR_API. * 4/8/98 (pim.c) Changed pim_recv so that the interface passed to * pim*_recv is the interface matching the source address, NOT * the RPF interface. This will work in the presense of tunnels, * even if the control message doesn't go through the tunnel. * 4/8/98 Fixed assert_recv so that when an assert is received for * an (S,G) that doesn't exist, that (S,G) entry is created. * 4/8/98 Changed jp_suppression bit to a join_suppress source bit * 4/8/98 Fixed prune-triggered join send to be aware of MBR state * and send only if the upstream address from the prune matches * the upstream address for the (S,G) entry. * 3/16/98 Upstream neighbor changes are now reflected in the MBR's * version of the MRT via calls to mbr_reset_nbr(). * 2/24/98 Prunes to point-to-point interfaces now use the remote * address for the destination and upstream addr. Also, * prunes received on point-to-point interfaces are handled * regardless of the contents of their Addr fields. * 2/24/98 Fixed a bug in pimdm_jp_send_one: when a prune is sent * on an outgoing interface in response to wrongif, graft * state should not be canceled. */ #define INCLUDE_IF #define INCLUDE_MROUTE_KERNEL #include "include.h" #ifdef PROTO_PIMDM #define INCLUDE_ROUTE #include "inet.h" #include "mrt.h" #include "mbr.h" #include "targets.h" #include "igmp.h" #include "mroute.h" #include "pim.h" #include "pimdm.h" /* MRT Maintenance */ PROTOTYPE (pimdm_mrt_commence, static void, (task *)); PROTOTYPE (pimdm_src_refresh_if_used, static void, (task *, source_t *)); PROTOTYPE (pimdm_mrt_create_sg, static source_t *, (task *, sockaddr_un *, sockaddr_un *, sockaddr_un *, sockaddr_un *, u_int8)); /* MBR callbacks */ PROTOTYPE (pimdm_sg_creation_recv, static void, (task *, krt_request_t *)); PROTOTYPE (pimdm_sg_join_recv, static void,(task*, sockaddr_un*,sockaddr_un*, if_addr *)); PROTOTYPE (pimdm_sg_prune_recv, static void, (task *, sockaddr_un *, sockaddr_un *)); PROTOTYPE (pimdm_grp_join_recv, static void, (task *, sockaddr_un *, sockaddr_un *)); PROTOTYPE (pimdm_grp_prune_recv, static void, (task *, sockaddr_un *, sockaddr_un *)); PROTOTYPE (pimdm_sg_wrongif_recv, static void, (sockaddr_un *, sockaddr_un *, if_addr *)); PROTOTYPE (pimdm_aux_join_recv, static void, (sockaddr_un *, if_addr *, int)); PROTOTYPE (pimdm_aux_prune_recv, static void, (sockaddr_un *, if_addr *, int)); /* Interfaces */ PROTOTYPE (pimdm_assert_downstream, static downstream_t *, (source_t *, if_addr *, u_int16)); PROTOTYPE (pimdm_downstream_prune, static void, (source_t *, downstream_t *, time_t, u_int16)); PROTOTYPE (pimdm_downstream_forward, static void, (source_t *, downstream_t *, u_int16)); PROTOTYPE (pimdm_downstream_add, static void, (source_t *, downstream_t *, downstream_t *, time_t, u_int16)); PROTOTYPE (pimdm_downstream_remove, static void, (source_t *, downstream_t *, downstream_t *)); PROTOTYPE (pimdm_downstream_delete, void, (source_t *, if_addr *)); PROTOTYPE (pimdm_downstream_create, static downstream_t *, (source_t *, if_addr *)); PROTOTYPE (find_prune_ifap, static downstream_t *, (source_t *, if_addr *)); PROTOTYPE (pimdm_upstream_add, static void, (source_t *)); /* Rate Limits */ PROTOTYPE (pimdm_create_sg_rl_list, static void, (task *, source_t *)); PROTOTYPE (pimdm_free_sg_rl_list, static void, (source_t *)); PROTOTYPE (pimdm_find_sg_rl_if, static pimdm_rate_limit_times_t *, (source_t *, if_addr *)); /* PIM messaging */ PROTOTYPE (pimdm_jp_send_one, static void, (task *, source_t *, if_addr *, sockaddr_un *, sockaddr_un *, time_t, int)); PROTOTYPE (pimdm_graft_retransmit, static void, (task_timer *, time_t)); PROTOTYPE (pimdm_graft_send, void, (task *, source_t *)); PROTOTYPE (pimdm_graft_send_one, int, (task *, source_t *)); PROTOTYPE (pimdm_graft_ack_send, void, (task *, if_addr *, pim_t *, int)); PROTOTYPE (pimdm_graft_free_state, static void, (task *, source_t *)); PROTOTYPE (pimdm_delete_all_sg_ip_events, static void, (source_t *)); PROTOTYPE (pimdm_delete_all_jp_entries, static void, (source_t *)); PROTOTYPE (pimdm_assert_send, void, (if_addr *, source_t *, pref_t, metric_t)); PROTOTYPE (pimdm_schedule_jp, static void, (task *, source_t *, if_addr *, sockaddr_un *, time_t, int, time_t)); PROTOTYPE (pimdm_jp_delay_job, static void, (task_timer *, time_t)); /* Misc Functions */ PROTOTYPE (pimdm_have_nbrs, static int, (if_addr *)); PROTOTYPE (pimdm_max_prune_time, static time_t, (source_t *)); /**************************************************************************** * * Globals * ****************************************************************************/ /* Protocol-specific source data */ static block_t pimdm_source_block_index; /* Rate-limiting for prunes and asserts */ static block_t pimdm_rate_limit_times_block_index; /* Join/Prune Scheduling */ static block_t pimdm_scheduled_jp_block_index; /* Well-known addresses for PIMDM */ sockaddr_un *pimdm_anyone = 0; /* 0.0.0.0 */ /* PIM-DM default assert timeout - For consistent knowledge of the * (S,G) forwarder identity between upstream and downstream routers, * the assert timeout must be greater than or equal to the default * prune holdtime. Therefore, since RFC2117 values are being used for * prune holdtime and the specified assert timeout does not meet this * condition, I reset the assert timeout to be equal to the prune holdtime. */ time_t pimdm_default_assert_timeout; /* Sender is member heuristic for multicast interoperability: * See draft-thaler-multicast-interop-02.txt */ int pimdm_default_sender_is_member; /**************************************************************************** * * Initialization for PIMDM * pimdm_reinit * pimdm_var_init * pimdm_register_callbacks * pimdm_shutdown * ****************************************************************************/ /* pimdm_init() * * Called from pim.c to handle PIMDM specific inits. */ void pimdm_init() { pimdm_source_block_index = task_block_init (sizeof(pimdm_source_t), "pimdm_source_t"); pimdm_scheduled_jp_block_index = task_block_init (sizeof(pimdm_scheduled_jp_t), "pimdm_scheduled_jp_t"); pimdm_rate_limit_times_block_index = task_block_init (sizeof(pimdm_rate_limit_times_t), "pimdm_rate_limit_times_t"); } /* pimdm_reinit() * * Called once per component. Handle downstream (including pruned) * interface changes. * */ void pimdm_reinit (tp) task *tp; { pim_task_data_t *td = tp->task_data; source_t *src; trace_set(tp->task_trace, pim_trace_options); assert (tp->task_rtproto == RTPROTO_PIM); td = tp->task_data; /* if we have no config data, terminate the component */ if (!td->pim_config_data) { pim_terminate_component (tp); return; } /* now handle downstream interfaces owner changes */ MRT_WALK_SRC(td->pim_mrt, src) { downstream_t *ds; downstream_t *gonners; gonners = mrt_downstream_create(); /* Check the oiflist for owner changes */ DOWNSTREAM_LIST (ds, src->src_downstream) { if (mbr_get_iftask (ds->ds_ifap) != tp) { pimdm_downstream_remove (src, src->src_downstream, ds); DOWNSTREAM_ENQ(ds, gonners); } } DOWNSTREAM_LIST_END (ds, src->src_downstream); /* Check the prune list for owner changes */ DOWNSTREAM_LIST (ds, PIMDM_SRC_PRUNE(src)) { if (mbr_get_iftask (ds->ds_ifap) != tp) { pimdm_downstream_remove (src, PIMDM_SRC_PRUNE(src), ds); DOWNSTREAM_ENQ(ds, gonners); } } DOWNSTREAM_LIST_END (ds, PIMDM_SRC_PRUNE(src)); mrt_downstream_delete_list(gonners); mrt_downstream_free(gonners); } MRT_WALK_SRC_END(td->pim_mrt, src); /* Dense-mode protocols like DVMRP and PIM-DM need to register interest * in everything. So you need to call register_interest with 224.0.0.0/4 * when PIM-DM is enabled, and unregister it when it terminates. * If we had DWR's this wouldn't be necessary, but we don't. * We do however have a heuristic known as senders-are-members, * described in draft-thaler-multicast-interop-02.txt. */ if(!pimdm_default_sender_is_member) mbr_grp_register_interest(pim_wildcard, pim_wildcard_mask, tp); pimdm_mrt_commence(tp); } /* pimdm_var_init() * * Initialize DM protocol-specific variables. */ void pimdm_var_init() { #ifdef PIMDM_SPEC_COMPL /* Use these values from the pimdm spec internet draft instead of * the defaults from pim.c (from RFC2117) */ pim_default_mrt_timeout = 180; pim_default_jp_holdtime = 180; #endif PIMDM_SPEC_COMPL pimdm_default_assert_timeout = pim_default_jp_holdtime; if (!pimdm_anyone) pimdm_anyone = sockdup(sockbuild_in(0, htonl(PIM_ANYONE))); /* The random number generator, used with PIM-Join transmission and * PIM-Prune delay */ { struct timeval tv; gettimeofday(&tv, 0); grand_seed(tv.tv_sec ^ tv.tv_usec); } pimdm_default_sender_is_member = 0; /* Heuristic off */ } /* pimdm_register_callbacks() * * Register the PIMDM specific callbacks for the protocol. */ void pimdm_register_callbacks(tp) task *tp; { mbr_register(tp, pimdm_sg_creation_recv, pimdm_sg_wrongif_recv, pimdm_sg_join_recv, pimdm_sg_prune_recv, pimdm_grp_join_recv, pimdm_grp_prune_recv, pimdm_aux_join_recv, pimdm_aux_prune_recv, pim_get_neighbors); } /* pimdm_shutdown() * * Called by pim_terminate_component to free all pimdm-specific * data structures. Eliminates all scheduled joins/prunes, ip events, * and PIMDM-specific MRT structures. */ void pimdm_shutdown(tp) task *tp; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; if(!pimdm_default_sender_is_member) mbr_grp_unregister_interest(pim_wildcard, pim_wildcard_mask, tp); /* For each S,G */ MRT_WALK_SRC (td->pim_mrt, src) { /* Sender is member heuristic: if last (S,G) for this * group, then unregister interest for the group */ if(pimdm_default_sender_is_member && src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_unregister_interest(src->src_gaddr, inet_mask_host, tp); pimdm_delete_all_jp_entries(src); pimdm_free_sg_rl_list(src); pimdm_graft_free_state(tp, src); mrt_downstream_delete_list(PIMDM_SRC_PRUNE(src)); task_block_free(pimdm_source_block_index, src->src_data[0]); } MRT_WALK_SRC_END(td->pim_mrt, src); } /**************************************************************************** * * Dumping and Tracing * * pimdm_dump * ****************************************************************************/ void pimdm_dump __PF2(tp, task *, fp, FILE *) { /* Nothing PIMDM-specific to dump (???) */ } /**************************************************************************** * * IGMP/Static Joins/Deletes * pimdm_aux_join_recv * pimdm_aux_prune_recv * ****************************************************************************/ /* * pimdm_aux_join_recv * * Handle static or IGMP joins * * PROTOCOL: For each source having state for the given group, put the * given interface in the oiflist */ static void pimdm_aux_join_recv(gaddr, ifap, dsproto) sockaddr_un *gaddr; if_addr *ifap; int dsproto; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; group_t *grp; trace_tp (tp, TR_NORMAL, 0, ("pimdm_aux_join_recv[%s]: recieved a static or igmp join for %A on %s(%A)", tp->task_name, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); /* Step 1: Locate the group in the MRT */ grp = mrt_locate_group (td->pim_mrt, gaddr); if (!grp) { trace_tp(tp, TR_NORMAL, 0, ("pimdm_aux_join_recv: static/igmp group add request for non-existant group %A", gaddr)); return; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_join_recv[%s]: Adding all sources for group %A", tp->task_name, gaddr)); /* Step 2: foreach souce having state for the group */ MRT_WALK(grp->grp_srcs, src) { downstream_t *dp; /* if the interface is not in the downstream list */ if (!(dp = find_downstream_ifap(src, ifap))) { if (dp = find_prune_ifap(src, ifap)) { /* interface is pruned, add it to the oiflist */ pimdm_downstream_forward(src, dp, dsproto); trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_join_recv[%s]: %s (%A) unpruning (%A,%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), gaddr, src->addr)); } else { /* interface is not pruned and not in oiflit (i.e. is a leaf * network with no pim routers), create a downstream for it */ dp = pimdm_assert_downstream(src, ifap, dsproto); } trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_join_recv[%s]: Adding %s (%A) to (%A,%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), gaddr, src->addr)); } else { /* already in the oiflist, update its dsproto flag */ trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_join_recv[%s]: interface %s (%A) alread in (%A,%A) oiflist", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), gaddr, src->addr)); BIT_SET(dp->ds_protos, dsproto); } } MRT_WALK_END(grp->grp_srcs, src); } /* pimdm_aux_prune_recv() * * Called for static or igmp leaves. * * PROTOCOL: For each source having state for the group, remove the dsproto * flag for the given interface and remove that interface from the oiflist * if there are no other dsproto flags active (i.e., no other reason to * forward). */ static void pimdm_aux_prune_recv (gaddr, ifap, dsproto) sockaddr_un *gaddr; if_addr *ifap; int dsproto; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; group_t *grp; source_t *src; trace_tp(tp,TR_NORMAL, 0, ("pimdm_aux_prune_recv: igmp or static leave request for %A from %A(%s)", gaddr, IFA_UNIQUE_ADDR(ifap), ifap->ifa_name)); /* Step 1: Locate the group in the MRT */ grp = mrt_locate_group (td->pim_mrt, gaddr); if (!grp) { trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_aux_prune_recv: igmp or static group delete request for non-existant group %A", gaddr)); return; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_prune_recv[%s]: Removing IGMP or static for all sources for group %A", tp->task_name, gaddr)); /* Remove the interface from each (*,G) in which it is in the oiflist * but has no pim neighbor routers or other aux joins. */ /* Step 2: For each source in the group: */ MRT_WALK(grp->grp_srcs, src) { downstream_t *dp; if ((dp = find_downstream_ifap(src, ifap))) { BIT_RESET(dp->ds_protos, dsproto); if(!dp->ds_protos) { trace_tp (tp, TR_ROUTE, 0, ("pimdm_aux_prune_recv[%s]: Deleting %s (%A) from (%A,%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), gaddr, src->addr)); pimdm_downstream_delete(src, ifap); } } } MRT_WALK_END(grp->grp_srcs, src); } /**************************************************************************** * * Multicast Routing Table Maintenance * pimdm_mrt_commence * pimdm_mrt_cleanup * pimdm_sg_creation_recv * * Helper functions * pimdm_src_refresh_if_used * pimdm_assert_downstream * pimdm_mrt_create_sg * ****************************************************************************/ /* pimdm_src_refresh_if_used() * * Checks if the MRT source entry has forwarded any packets since the * last call to pimdm_src_refresh_if_used. Returns 1 if true, or 0 if false. */ static void pimdm_src_refresh_if_used(tp, src) task *tp; source_t *src; { if(mroute_src_stats(src)) { trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_src_refresh_if_used: check use count failed group %A source %A", src->src_group->addr, src->addr)); } if (src->src_lastpktcnt != src->src_pktcnt) { pim_src_refresh(src); trace_tp (tp, TR_TIMER, 0, ("pimdm_src_refresh_if_used[%s] (%A,%A) REFRESHED ", tp->task_name, src->addr, src->src_gaddr)); } } /* pimdm_mrt_cleanup * * this routine gets fired off once every td->mrt_period. It removes * timedout mrt entries and pruned downstream ifaps. * * PROTOCOL: Walk the S,G entries, for each: * 1. refresh the entry timer if the entry has forwarded packets since * the last cleanup. * 2. if the entry is timed out, delete it, otherwise, * 3. if the entry has assert state and that assert state has timed out, * switch back to RPF upstream nbr. * 4. walk the list of pruned interfaces, and move any interfaces with * timed out prune state to the oiflist. */ static void pimdm_mrt_cleanup (tip, interval) task_timer *tip; time_t interval; { task *tp = tip->task_timer_task; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; mrt_src_list_t *deathlist, *gonner, *sp; time_t nexttime = time_sec + td->pim_mrt_period; /* next src/ds to expire */ source_t *src; group_t *grp; int first = 1; trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] initial nexttime %d, mrt_period %d, time_sec %d", tp->task_name, nexttime, td->pim_mrt_period, time_sec)); trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] srcs: ", tp->task_name)); deathlist = mrt_src_list_alloc(); deathlist->forw = deathlist->back = deathlist; deathlist->src = (source_t *)0; MRT_WALK_SRC (td->pim_mrt, src) { downstream_t *dp; trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] checking (S, G) = (%A, %A)", tp->task_name, src->addr, src->src_gaddr)); /* Refresh the timers if packets have been forwarded since the last * cleanup. Note that the counters used count packets received, sourced * or forwarded, so only refresh when we have (S,G) forwarding state. */ if(src->src_downstream != src->src_downstream->forw) pimdm_src_refresh_if_used(tp, src); /* * we create a list of source entries that must be removed, * to avoid wiping out the radix trie stack used by the * MRT_WALK_SRC() macros. */ if (pim_src_expired(src)) { trace_tp (tp, TR_TIMER, 0, ("(%A, %A) EXPIRED", src->addr, src->src_gaddr)); gonner = mrt_src_list_alloc(); gonner->src = src; MRT_SRC_LIST_ENQ(gonner, deathlist->back); continue; } if (src->src_timeout < nexttime) { nexttime = src->src_timeout; trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] new nexttime %d for source (%A,%A)", tp->task_name, nexttime, src->addr, src->src_gaddr)); } if(PIMDM_SRC_ASSERT_TIMEOUT(src)) { trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup: (%A, %A) has assert state with upstream neighbor %A", src->addr, src->src_gaddr, src->src_upstream->nbr)); } else { trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup: (%A, %A) has RPF upstream neighbor %A", src->addr, src->src_gaddr, src->src_upstream->nbr)); } /* the entry hasn't timedout, now try the assert state */ if(PIMDM_SRC_ASSERT_TIMEOUT(src) && time_sec >= PIMDM_SRC_ASSERT_TIMEOUT(src)) { /* Assert timed out - set upstream to RPF */ trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] (%A, %A) ASSERT TIMEOUT", tp->task_name, src->addr, src->src_gaddr)); pimdm_upstream_add(src); mbr_reset_nbr(src, src->src_upstream->nbr); PIMDM_SRC_ASSERT_TIMEOUT(src) = 0; /* No assert state now */ } /* the entry hasn't timedout, now try the PRUNED downstream list */ trace_tp (tp, TR_TIMER, 0, ("pruned interfaces: ")); /* Walk the PRUNED interface list */ DOWNSTREAM_LIST(dp, PIMDM_SRC_PRUNE(src)) { trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] (%A, %A) has pruned interface %s(%A)", tp->task_name, src->addr , src->src_gaddr, dp->ds_ifap->ifa_name, IFA_UNIQUE_ADDR(dp->ds_ifap))); if (pim_downstream_expired(dp)) { downstream_t *aged = dp; dp = dp->forw; trace_tp(tp, TR_TIMER, 0, (" %s(%A) PRUNE EXPIRED", aged->ds_ifap->ifa_name, IFA_UNIQUE_ADDR(aged->ds_ifap))); /* Move the interface to the downstream forwarding (oif) list*/ pimdm_downstream_forward(src, aged, dp->ds_protos); } else if (dp->ds_timeout < nexttime) { nexttime = dp->ds_timeout; trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup[%s] new nexttime %d for (%A,%A) pruned interface", tp->task_name, nexttime, src->addr, src->src_gaddr)); } } DOWNSTREAM_LIST_END(dp, PIMDM_SRC_PRUNE(src)); } MRT_WALK_SRC_END(td->pim_mrt, src); /* now delete the entries on the deathlist */ rt_open(tp); while (deathlist != deathlist->forw) { mrt_src_list_t *slist = (mrt_src_list_t *) 0, *s; source_t *src = gonner->src; /* gonner points the first deathlist ele */ rt_head *rth = (rt_head *) 0; if (src->src_upstream) { rth = src->src_upstream->rt->rt_head; rttsi_get (rth, tp->task_rtbit, (byte *) &slist); if (!slist) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_mrt_cleanup[%s] (%A, %A): ", tp->task_name, src->addr, src->src_gaddr)); if (rth) { trace_log_tf (pim_trace_options, TRC_NL_AFTER, LOG_ERR, ("%A/%d has no slist.", rth->rth_dest, inet_prefix_mask(rth->rth_dest_mask))); } else { trace_log_tf (pim_trace_options, TRC_NL_AFTER, LOG_ERR, ("")); } } } if (slist) { /* remove this entry from up->rt's tsi list */ MRT_SRC_LIST(s, slist) { if (src == s->src) break; } MRT_SRC_LIST_END (s, slist); /* Note, there will be no source in the tsi list for if the upstream * is an assert winner. */ if (s && src->src_upstream) { MRT_SRC_LIST_DEQ(s); rtbit_reset (src->src_upstream->rt, tp->task_rtbit); } } /* Sender is member heuristic: if last (S,G) for this group, then * unregister interest for the group */ if(pimdm_default_sender_is_member && gonner->src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_unregister_interest(gonner->src->src_gaddr, inet_mask_host, tp); /* now actually delete the entry */ mbr_delete_cache(gonner->src->src_gaddr, gonner->src->addr, gonner->src->mask); pimdm_delete_all_jp_entries(gonner->src); pimdm_free_sg_rl_list(gonner->src); pimdm_graft_free_state(tp, gonner->src); mrt_downstream_delete_list(PIMDM_SRC_PRUNE(gonner->src)); task_block_free(pimdm_source_block_index, gonner->src->src_data[0]); mrt_source_delete(gonner->src); MRT_SRC_LIST_DEQ(gonner); mrt_src_list_free(gonner); gonner = deathlist->forw; } rt_close(tp, 0, 0, 0); mrt_src_list_free(deathlist); /* now we just run every td->mrt_period */ /* set the timer to the next expirey */ assert(nexttime-time_sec); task_restart_timer(&tip, tp, "MRT Clean UP", 0, nexttime-time_sec, pimdm_mrt_cleanup, (void_t) 0); trace_tp (tp, TR_TIMER, 0, ("pimdm_mrt_cleanup: resting cleanup timer to %d, offset %d.", nexttime, nexttime-time_sec)); } static void pimdm_mrt_commence(tp) task *tp; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; task_restart_timer(&td->pim_mrt_timer, tp, "MRT Clean UP", td->pim_mrt_period, 3, pimdm_mrt_cleanup, (void_t) 0); } /* Called by pimdm_mrt_create_sg - Add a downstream interface to a source */ static downstream_t * pimdm_assert_downstream(src, ifap, dsproto) source_t *src; /* IN: source entry */ if_addr *ifap; /* IN: downstream ifap to add */ u_int16 dsproto; { task *tp = mbr_get_iftask(ifap); downstream_t *new, *dp; /* * Don't add interface if it is the expecting incoming intf */ if (ifap == src->src_upstream->ifap) return (downstream_t *)0; /* * Look to see if interface is already in downstream list */ new = find_downstream_ifap(src, ifap); if (new) { return (downstream_t *)0; } trace_tp(tp, TR_ROUTE, 0, ("pimdm_assert_downstream: pimdm adding downstream interface %s (%A)", ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); /* Add it */ new = pimdm_downstream_create(src, ifap); BIT_SET(new->ds_protos, dsproto); /* The downstream list should be kept in sorted order */ dp = src->src_downstream->forw; while (dp != src->src_downstream) if (sock2ip(IFA_UNIQUE_ADDR(dp->ds_ifap)) < sock2ip(IFA_UNIQUE_ADDR(new->ds_ifap))) dp = dp->forw; else break; DOWNSTREAM_ENQ (new, dp->back); /* XXX Replaced with a downstream merge in sg_cration_recv */ /* Add the oif to the kernel cache! */ /* mbr_sg_add_downstream(src->addr, src->src_group->addr, new); */ return new; } /* pimdm_sg_creation_recv() * * This is called from the lower-level MBR routines when a kernel cache * miss occurs. Note that we aren't told anything about which interface * a packet arrived on, but we are told the RPF information. * * If the oiflist is empty (negative cache state) the mbr layer will * immediately trigger a prune with a call to pimdm_sg_prune_recv. */ static void pimdm_sg_creation_recv (tp, req) task *tp; krt_request_t *req; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src = (source_t *)0; trace_tp (tp, TR_NORMAL, 0, ("pimdm_sg_creation_recv[%s] (%A, %A)", tp->task_name, req->saddr, req->gaddr)); /* Step 1: create an (S,G) entry */ src = pimdm_mrt_create_sg (tp, req->gaddr, inet_mask_host, req->saddr, inet_mask_host, SPT_BIT); /* XXX Replaces mbr_sg_add_downstream in pimdm_assert_downstream */ mrt_downstream_merge(&req->ds, src->src_downstream, NULL); if (!src) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_sg_creation_recv[%s] create failed (%A, %A)", tp->task_name, src->addr, src->src_gaddr)); } } /* * pimdm_mrt_create_sg * * Do the dirty work of creating an S,G entry: * 1. create the entry * 2. set the upstream nbr * 3. initialize the data in the entry, setting the join suppression bit * so that added oifs won't trigger grafts during creation. * 4. add all applicable interfaces to the oiflist. * 5. reset the join suppression bit so that subsequent grafting may * happen normally. */ static source_t * pimdm_mrt_create_sg(tp, gaddr, gmask, saddr, smask, flags) task *tp; /* IN: PIMDM component */ sockaddr_un *gaddr, *gmask; /* IN: group and mask, the mask must be /32 */ sockaddr_un *saddr, *smask; /* IN: source and mask */ u_int8 flags; /* IN: flags to provide the src->src_flags */ { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *wc, *src = (source_t *) 0; if_addr *ifap; trace_tp (tp, TR_ROUTE, 0, ("pimdm_mrt_create_sg[%s] (%A/%d, %A/%d), flags: %x", tp->task_name, gaddr, inet_prefix_mask(gmask), saddr, inet_prefix_mask(smask), flags)); /* only handle host masks */ if (gmask != inet_mask_host || smask != inet_mask_host) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_mrt_create_sg[%s] host masks only: gmask %d, smask %d", tp->task_name, inet_prefix_mask(gmask), inet_prefix_mask(smask))); return src; } /* Create the (S,G) entry! */ src = mrt_create_entry (td->pim_mrt, gaddr, saddr); /* Fill in the RPF upstream router for (S,G) */ src->src_upstream = (upstream_t *)0; pimdm_upstream_add(src); if (!src->src_upstream) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_create_sg[%s]: (%A/%d, %A/%d), no RPF(%A).", tp->task_name, gaddr, inet_prefix_mask(gmask), saddr, inet_prefix_mask(smask), saddr)); return (source_t *) 0; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_create_sg[%s]: iif set to %s (%A).", tp->task_name, src->src_upstream->ifap->ifa_name, IFA_UNIQUE_ADDR(src->src_upstream->ifap))); src->src_flags = flags | NEG_CACHE_BIT; src->src_proto = IPMULTI_PROTO_PIM; /* Alloc the protocol-specific source data */ src->src_data[0] = (void *)task_block_alloc(pimdm_source_block_index); /* Suppress join and prune alerts while creating this entry */ BIT_SET(src->src_flags, JOIN_SUPP_BIT); /* Initialize prune state to be empty */ PIMDM_SRC_PRUNE(src) = mrt_downstream_create (); /* Initialize the scheduled prunes to be empty */ PIMDM_SRC_SCHED_JP(src) = (pimdm_scheduled_jp_t *) task_block_alloc (pimdm_scheduled_jp_block_index); PIMDM_SRC_SCHED_JP(src)->forw = PIMDM_SRC_SCHED_JP(src)->back = PIMDM_SRC_SCHED_JP(src); /* Initialize the assert state */ PIMDM_SRC_ASSERT_TIMEOUT(src) = 0; /* Initialize the graft state */ PIMDM_SRC_GRAFT_RETRANS(src) = (mrt_src_list_t *)0; /* Initialize the rate-limiting */ pimdm_create_sg_rl_list(tp, src); /* Initialize the MRT timer */ src->src_holdtime = td->pim_mrt_timeout; src->src_timeout = time_sec + src->src_holdtime; /* Fill in the downstream routers: * If iif has a boundary then, don't forward on any interfaces. If * downstream interface has a boundary, don't forward on that interface. * PIM neighbors other than the upstream, and interfaces with directly * connected hosts */ if(!mstatic_find_boundary(gaddr, src->src_upstream->ifap)) { IF_ADDR(ifap) { /* Add interfaces that have non-empty neighbor lists or IGMP members */ int dsproto=DSPROTO_NONE; /* Skip non-PIMDM interfaces */ if (mbr_get_iftask(ifap) != tp) continue; /* Skip boundaries */ if (mstatic_find_boundary(gaddr,ifap)) continue; /* if I have nbrs */ if (pimdm_have_nbrs(ifap)) dsproto |= DSPROTO_MROUTE; #ifdef PROTO_IGMP if (igmp_find_group(sock2ip(gaddr), ifap)) dsproto |= DSPROTO_IGMP; #endif if (mstatic_find_downstream(gaddr, ifap)) dsproto |= DSPROTO_STATIC; /* Add the interface to the downstream list */ if (dsproto) (void)pimdm_assert_downstream(src, ifap, dsproto); } IF_ADDR_END (ifap); } /* if !boundary */ /* Sender is member heuristic: if first (S,G) for this group and * S is an internally reached source, then register interest for the group */ if(pimdm_default_sender_is_member && src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_register_interest(gaddr, inet_mask_host, tp); /* Done creating entry, allow join and prune alerts */ BIT_RESET(src->src_flags, JOIN_SUPP_BIT); trace_tp (tp, TR_TIMER, 0, ("pimdm_create_sg[%s]: (S,G) timeout at %d (currnt time %d)", tp->task_name, src->src_timeout, time_sec)); return src; } /**************************************************************************** * * Wrong receiving interface! * * pimdm_sg_wrongif_recv * ****************************************************************************/ /* pimdm_sg_wrongif_recv() * * This is called when a packet is received on an interface other than * the upstream interface. Only the component owning the interface on * which the packet was received will get this alert. * * A wrongif may trigger two types of responses from pimdm depending on * the received interface type: * 1. if the packet arrived on a LAN, then rate-limit Assert for source IP * 2. if the packet arrived on a P-to-P, send rate-limited Prune */ void pimdm_sg_wrongif_recv (saddr, gaddr, ifap) sockaddr_un *saddr; sockaddr_un *gaddr; if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; pimdm_rate_limit_times_t *rlt; u_long metric; u_long pref; /* do an exact match lookup for s,g */ src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_wrongif_recv: wrong iif for (%A, %A) with no pim route", saddr, gaddr)); return; } trace_tp(tp, TR_NORMAL, 0, ("pimdm_wrongif_recv: received (%A, %A) data on outgoing interface %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); if (!BIT_TEST(ifap->ifa_state, IFS_POINTOPOINT)) { /* Step 1: Incoming interface is a LAN */ /* Enforce rate limit */ rlt = pimdm_find_sg_rl_if(src, ifap); if (rlt->last_assert == time_sec) return; rlt->last_assert = time_sec; if (src->src_upstream) { metric = src->src_upstream->rt->rt_metric; pref = src->src_upstream->rt->rt_preference; } else { /* TODO - ??? what to use if directly connected ??? */ pref = 0x7fffffff; metric = 0xffffffff; } pimdm_assert_send (ifap, src, pref, metric); } else { /* Step 2: Incoming interface is a Point-to-Point */ /* Only send if there is a neighbor (will only be one) */ /* Enforce rate limit */ rlt = pimdm_find_sg_rl_if(src, ifap); if (rlt->last_prune == time_sec) return; rlt->last_prune = time_sec; if(ifap->ifa_addr_remote) pimdm_jp_send_one(tp, src, ifap, ifap->ifa_addr_remote, ifap->ifa_addr_remote, pimdm_max_prune_time(src), PRUNE); else /* No nbr found */ trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_sg_wrongif_recv: attempt to prune an interface (%A) with no neighbor for (%A, %A)", IFA_UNIQUE_ADDR(ifap), src->src_gaddr, src->addr)); } } /**************************************************************************** * * Forwarding and Negative Cache State Transistion * * pimdm_sg_join_recv * pimdm_sg_prune_recv * pimdm_grp_join_recv * pimdm_grp_prune_recv * ****************************************************************************/ /* This is called by the lower-level MBR routines when the first oif is * added (by any component) to the actual kernel cache entry. The fact * that we're receiving this means that pimdm_sg_creation_recv was * called previously, and we own the upstream interface. We should now * trigger a graft upstream. * * Set the NEG_CACHE_BIT to indicate that there are downstream receivers * (possibly in other protocol components) when receiving LAN asserts or * prunes. * * PROTOCOL: if the join suppression bit is not set (meaning that we * this alert wasn't triggered in the middle of a creation_recv alert * due to adding interfaces for the flood), then send a graft to the * upstream nbr. Also, when beoming a forwarding state entry, refresh * the entry timer (this is not in the current spec but Liming Wei * indicated that this would be included in the next revision. */ static void pimdm_sg_join_recv(tp, saddr, gaddr, ifap) task *tp; /* IN: PIMDM component */ sockaddr_un *saddr; /* IN: source address */ sockaddr_un *gaddr; /* IN: group address */ if_addr *ifap; /* IN: new downstream iface (may or may not be ours) */ { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src = mrt_locate_source(td->pim_mrt, gaddr, saddr); if(!src) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_sg_join_recv[%s] could not find (%A, %A)", tp->task_name, saddr, gaddr)); return; } /* Unset the negative cache bit */ BIT_RESET(src->src_flags, NEG_CACHE_BIT); /* Refresh the entry timer */ pim_src_refresh(src); /* Dont send graft if we're in the process of creating initial state */ if(!BIT_TEST(src->src_flags, JOIN_SUPP_BIT)) { /* (S,G) is switching from negative cache state to forwarding state - * send a graft upstream. */ trace_tp (tp, TR_ROUTE, 0, ("pimdm_sg_join_recv[%s] (%A,%A) now has forwarding state, sending graft upstream", tp->task_name, saddr, gaddr)); pimdm_graft_send(tp, src); } } /* This is called by the lower-level MBR routines when the last oif is * removed (by any component) from the actual kernel cache entry. The * fact that we're receiving this means that pimdm_sg_creation_recv() was * called previously, and we own the upstream interface. We should now * trigger a prune upstream. * * Reset the NEG_CACHE_BIT to indicate that there are NO downstream receivers * (in any protocol components) when receiving LAN asserts or prunes. * * PROTOCOL: Update the entry timer with the max of the pruned interfaces * and send a prune to the upstream interface. */ static void pimdm_sg_prune_recv(tp, saddr, gaddr) task *tp; /* IN: PIMDM component */ sockaddr_un *saddr; /* IN: source address */ sockaddr_un *gaddr; /* IN: group address */ { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src = mrt_locate_source(td->pim_mrt, gaddr, saddr); if(!src) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_sg_prune_recv[%s] could not find (%A, %A)", tp->task_name, saddr, gaddr)); return; } /* Unset the negative cache bit */ BIT_SET(src->src_flags, NEG_CACHE_BIT); /* (S,G) is switching from forwarding state to negative cache state - * set (S,G) timer to min of prune timers and send a prune upstream. */ trace_tp (tp, TR_ROUTE, 0, ("pimdm_sg_prune_recv[%s] (%A,%A) now has negative state, sending prune upstream", tp->task_name, saddr, gaddr)); src->src_timeout = time_sec + pimdm_max_prune_time(src); pimdm_jp_send_one(tp, src, src->src_upstream->ifap, pim_all_routers, src->src_upstream->nbr, pimdm_max_prune_time(src), PRUNE); } /* This is called by the lower-level MBR routines when the first (other) * component decides it's interested in the given group range. Current * multicast protocols will either use 224/4 to request all groups, or * specify a single group. * * PROTOCOL: Walk the MRT and trigger an (S,G) Join alert for all * matching entries. */ static void pimdm_grp_join_recv(tp, gaddr, gmask) task *tp; /* IN: PIMDM component */ sockaddr_un *gaddr; sockaddr_un *gmask; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; /* if default mask ... */ if (inet_prefix_mask(gmask) <= 4) { MRT_WALK_SRC(td->pim_mrt, src) { pimdm_sg_join_recv(tp, src->addr, src->src_gaddr, NULL); } MRT_WALK_SRC_END(td->pim_mrt, src); } else { group_t *grp = mrt_locate_group (td->pim_mrt, gaddr); /* * now we have to walk the source trie, grafting upstream for every * existing S which has been pruned upstream. */ MRT_WALK(grp->grp_srcs, src) { pimdm_sg_join_recv(tp, src->addr, src->src_gaddr, NULL); } MRT_WALK_END(grp->grp_srcs, src); } } /* This is called by the lower-level MBR routines when the last (other) * component decides it's no longer interested in the given group range. * Current multicast protocols will either use 0/0 to un-request all groups, * or specify a single group. * * PROTOCOL: Walk the MRT and trigger an (S,G) Prune alert for all * matching entries. */ static void pimdm_grp_prune_recv(tp, gaddr, gmask) task *tp; /* IN: PIMDM component */ sockaddr_un *gaddr; sockaddr_un *gmask; { source_t *src; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; /* if default mask ... */ if (inet_prefix_mask(gmask) <= 4) { MRT_WALK_SRC(td->pim_mrt, src) { pimdm_sg_prune_recv(tp, src->addr, src->src_gaddr); } MRT_WALK_SRC_END(td->pim_mrt, src); } else { group_t *grp = mrt_locate_group (td->pim_mrt, gaddr); /* * now we have to walk the source trie, pruning upstream for every * existing S with a null oiflist */ MRT_WALK(grp->grp_srcs, src) { pimdm_sg_prune_recv(tp, src->addr, src->src_gaddr); } MRT_WALK_END(grp->grp_srcs, src); } } /**************************************************************************** * * Downstream/Pruned Interface Manipulators * * pimdm_downstream_create * pimdm_downstream_add * pimdm_downstream_remove * pimdm_downstream_delete * ****************************************************************************/ static downstream_t * pimdm_downstream_create (src, ifap) source_t *src; if_addr *ifap; { downstream_t *new; new = mrt_downstream_create(); /* alloc, set forw, back and times */ #ifdef KRT_IPMULTI_TTL0 IFA_ALLOC(new->ds_ifap = ifap); #else /* KRT_IPMULTI_TTL0 */ new->ds_ifindex = 0; /* XXX ???? */ #endif /* KRT_IPMULTI_TTL0 */ return new; } /* Move a downstream interface from the oiflist to the prune list */ /* Precondition: ds must be an entry in the downstream forwarding list */ static void pimdm_downstream_prune(src, ds, holdtime, dsproto) source_t *src; downstream_t *ds; time_t holdtime; /* holdtime for PRUNES */ u_int16 dsproto; { task *tp = mbr_get_iftask(src->src_upstream->ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; downstream_t *dp; /* Make sure the source has an upstream neighbor */ if (!src->src_upstream) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_downstream_prune: (%A, %A) has no upstream member", src->addr, src->src_gaddr)); return; } /* check to see that we don't mess with the iif */ if (ds->ds_ifap == src->src_upstream->ifap) { trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_downstream_prune: attempt to add RPF(%A) to downstream list of (%A, %A)", IFA_UNIQUE_ADDR(ds->ds_ifap), src->src_gaddr, src->addr)); return; } /* Delete it from the mrt oiflit */ DOWNSTREAM_DEQ(ds); /* Add it to the prune list in sorted order */ dp = PIMDM_SRC_PRUNE(src); while (dp != PIMDM_SRC_PRUNE(src)) if (sock2ip(IFA_UNIQUE_ADDR(dp->ds_ifap)) < sock2ip(IFA_UNIQUE_ADDR(ds->ds_ifap))) dp = dp->forw; else break; DOWNSTREAM_ENQ (ds, dp->back); BIT_SET(ds->ds_protos, dsproto); ds->ds_holdtime = holdtime; ds->ds_timeout = time_sec + holdtime; ds->ds_rtime = time_sec; /* Update the kernel */ mbr_sg_delete_downstream (src->addr, src->src_gaddr, ds); } /* Move a downstream interface from the prune list to the oiflist */ /* Precondition: ds must be in the prune list */ static void pimdm_downstream_forward(src, ds, dsproto) source_t *src; downstream_t *ds; u_int16 dsproto; { task *tp = mbr_get_iftask(src->src_upstream->ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; downstream_t *dp; /* Make sure the source has an upstream neighbor */ if (!src->src_upstream) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_downstream_forward: (%A, %A) has no upstream member", src->addr, src->src_gaddr)); return; } /* check to see that we don't mess with the iif */ if (ds->ds_ifap == src->src_upstream->ifap) { trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_downstream_forward: attempt to add RPF(%A) to downstream list of (%A, %A)", IFA_UNIQUE_ADDR(ds->ds_ifap), src->src_gaddr, src->addr)); return; } /* Delete it from the prune */ DOWNSTREAM_DEQ(ds); /* * ifap is not on the downstream list, so create a list entry * insert it and fill it in. The downstream list is kept sorted by * address. */ dp = src->src_downstream->forw; while (dp != src->src_downstream) if (sock2ip(IFA_UNIQUE_ADDR(dp->ds_ifap)) < sock2ip(IFA_UNIQUE_ADDR(ds->ds_ifap))) dp = dp->forw; else break; DOWNSTREAM_ENQ (ds, dp->back); BIT_SET(ds->ds_protos, dsproto); /* Add it to the kernel entry -- kernel will give us a join alert * if this is the first downstream entry added, so don't worry here */ mbr_sg_add_downstream (src->addr, src->src_gaddr, ds); } static void pimdm_downstream_add (src, dsl, ds, holdtime, dsproto) source_t *src; downstream_t *dsl; /* The downstream list to add to */ downstream_t *ds; time_t holdtime; /* holdtime for PRUNES */ u_int16 dsproto; { task *tp = mbr_get_iftask(src->src_upstream->ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; downstream_t *dp; /* Make sure the source has an upstream neighbor */ if (!src->src_upstream) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_downstream_add: (%A, %A) has no upstream member", src->addr, src->src_gaddr)); return; } /* * check to see that we don't add the iif to the oif list. */ if (ds->ds_ifap == src->src_upstream->ifap) { trace_log_tf (pim_trace_options, 0, LOG_WARNING, ("pimdm_downstream_add: attempt to add RPF(%A) to downstream list of (%A, %A)", IFA_UNIQUE_ADDR(ds->ds_ifap), src->src_gaddr, src->addr)); return; } /* * ifap is not on the downstream list, so create a list entry * insert it and fill it in. The downstream list is kept sorted by * address. */ dp = dsl->forw; while (dp != dsl) if (sock2ip(IFA_UNIQUE_ADDR(dp->ds_ifap)) < sock2ip(IFA_UNIQUE_ADDR(ds->ds_ifap))) dp = dp->forw; else break; BIT_SET(ds->ds_protos, dsproto); ds->ds_holdtime = holdtime; ds->ds_timeout = time_sec + holdtime; ds->ds_ctime = ds->ds_rtime = time_sec; DOWNSTREAM_ENQ (ds, dp->back); if (dsl == src->src_downstream) { /* Add it to the kernel entry -- kernel will give us a join alert * if this is the first downstream entry added, so don't worry here */ mbr_sg_add_downstream (src->addr, src->src_gaddr, ds); } } /* Works for downstreams that are either forwarding or pruned ! */ static void pimdm_downstream_remove (src, dsl, ds) source_t *src; downstream_t *dsl; /* The downstream list to remove from */ downstream_t *ds; { /* Delete it from the list and kernel entry */ DOWNSTREAM_DEQ(ds); if (dsl == src->src_downstream) { mbr_sg_delete_downstream (src->addr, src->src_gaddr, ds); } } /* Works for downstreams that are either forwarding or pruned ! */ void pimdm_downstream_delete (src, ifap) source_t *src; if_addr *ifap; { downstream_t ds, *dp = &ds; if (dp=find_downstream_ifap(src, ifap)) { mbr_sg_delete_downstream (src->addr, src->src_gaddr, dp); } else if(!(dp=find_prune_ifap(src, ifap))) { trace_tp (mbr_get_iftask(ifap), TR_ROUTE, 0, ("pimdm_downstream_delete: ifap: %A not in (%A, %A) downstream lists", IFA_UNIQUE_ADDR(ifap), src->addr, src->src_gaddr)); return; } DOWNSTREAM_DEQ(dp); mrt_downstream_free(dp); } /* OUT: downstream entry for iface from the prune list, or NULL if not found */ static downstream_t * find_prune_ifap(src, ifap) source_t *src; /* IN: source entry */ if_addr *ifap; /* IN: interface to find */ { register downstream_t *dp; DOWNSTREAM_LIST (dp, PIMDM_SRC_PRUNE(src)) { if (dp->ds_ifap == ifap) return dp; } DOWNSTREAM_LIST_END (dp, PIMDM_SRC_PRUNE(src)); return NULL; } /**************************************************************************** * * PIM Neighborhood * * pimdm_nbr_timeout * pimdm_new_nbr * ****************************************************************************/ /* pimdm_have_nbrs * * Checks if the given interface has other PIM neighbors and returns 1 if so, * 0 otherwise. */ static int pimdm_have_nbrs (ifap) if_addr *ifap; { pimnbr_t *list, *np; list = (pimnbr_t *) ifap->pim_nbrs; PIMNBR_LIST(np, list) { if (!(sockaddrcmp_in (np->nbr_addr, ifap->ifa_addr_local))) { return 1; } } PIMNBR_LIST_END(np,list); return 0; } /* pimdm_nbr_timeout() * * Called from pim_nbr_timeout when a neighbor goes away. * * PROTOCOL: If there are no remaining neighbors then for each S,G entry, * reset the MROUTE dsproto bit in the given downstream or pruned interface * and remove that interface if no other dsproto bits are set. * * If the neighbor going away was the upstream neighbor, then you * have a topology mismatch since there hasn't been a flash. You are * now disconnected from the RPF tree, so all you can do is delete the * (S,G) entry. :-( */ void pimdm_nbr_timeout(ifap, aged) if_addr *ifap; pimnbr_t *aged; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; pimnbr_t *nbr_list; mrt_src_list_t *deathlist, *gonner; deathlist = mrt_src_list_alloc(); deathlist->forw = deathlist->back = deathlist; deathlist->src = (source_t *)0; /* for each src, * if ifap is iif and aged is upstream nbr, delete entry * if ifap is downstream and no more nbrs delete the if from the * downstream list */ MRT_WALK_SRC(td->pim_mrt, src) { if(ifap == src->src_upstream->ifap) { if(aged->nbr_addr == src->src_upstream->nbr) { /* delete the entry */ gonner = mrt_src_list_alloc(); gonner->src = src; MRT_SRC_LIST_ENQ(gonner, deathlist->back); continue; } } /* If it's not the iif, adjust the downstream/prune lists */ else if(!(pimdm_have_nbrs(ifap))) { /* No more neighbors */ downstream_t *dp; if(find_downstream_ifap(src, ifap)) { /* reset MROUTE bit and delete if no other dsproto bits */ BIT_RESET(dp->ds_protos, DSPROTO_MROUTE); if(!dp->ds_protos) { pimdm_downstream_delete(src, ifap); /* sg_prune_recv will trigger if no more downstream * interfaces */ } } else if(dp = find_prune_ifap(src, ifap)) { /* reset MROUTE bit in prune list */ /* We can also remove the interface if it is pruned with no * IGMP receivers, static config, or neighbors */ BIT_RESET(dp->ds_protos, DSPROTO_MROUTE); if(!dp->ds_protos) { pimdm_downstream_delete(src, ifap); /* sg_prune_recv will trigger if no more downstream * interfaces */ } } } } MRT_WALK_SRC_END(td->pim_mrt, src); /* now delete the entries on the deathlist */ rt_open(tp); while (deathlist != deathlist->forw) { mrt_src_list_t *slist = (mrt_src_list_t *) 0, *s; source_t *src = gonner->src; /* gonner points the first deathlist ele */ rt_head *rth = (rt_head *) 0; if (src->src_upstream) { rth = src->src_upstream->rt->rt_head; rttsi_get (rth, tp->task_rtbit, (byte *) &slist); if (!slist) { if (rth) { trace_log_tf (pim_trace_options, TRC_NL_AFTER, LOG_ERR, ("%A/%d has no slist.", rth->rth_dest, inet_prefix_mask(rth->rth_dest_mask))); } else { trace_log_tf (pim_trace_options, TRC_NL_AFTER, LOG_ERR, ("")); } } } if (slist) { /* remove this entry from up->rt's tsi list */ MRT_SRC_LIST(s, slist) { if (src == s->src) break; } MRT_SRC_LIST_END (s, slist); /* Note, there will be no source in the tsi list for if the upstream * is an assert winner. */ if (s && src->src_upstream) { MRT_SRC_LIST_DEQ(s); rtbit_reset (src->src_upstream->rt, tp->task_rtbit); } } /* Sender is member heuristic: if last (S,G) for this group, then * unregister interest for the group */ if(pimdm_default_sender_is_member && gonner->src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_unregister_interest(gonner->src->src_gaddr, inet_mask_host, tp); /* now actually delete the entry */ mbr_delete_cache(gonner->src->src_gaddr, gonner->src->addr, gonner->src->mask); pimdm_delete_all_jp_entries(gonner->src); pimdm_free_sg_rl_list(gonner->src); pimdm_graft_free_state(tp, gonner->src); mrt_downstream_delete_list(PIMDM_SRC_PRUNE(gonner->src)); task_block_free(pimdm_source_block_index, gonner->src->src_data[0]); mrt_source_delete(gonner->src); MRT_SRC_LIST_DEQ(gonner); mrt_src_list_free(gonner); gonner = deathlist->forw; } rt_close(tp, 0, 0, 0); mrt_src_list_free(deathlist); } /* pimdm_new_nbr() * * Called by pim_nbr_refresh when a new neighbor is added. * * PROTOCOL: For each (S,G) entry, add the interface with a new neighbor to the * oiflist if it isn't already forwarding and set the MROUTE dsproto bit. * * Note, if the new nbr is going to be the new RPF nbr, then just add * it as a downstream for now and wait for the flash to fix it. */ void pimdm_new_nbr(ifap) if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; MRT_WALK_SRC(td->pim_mrt, src) { downstream_t *dp; /* Change state and flags for all downstream interfaces */ if(!(dp = find_downstream_ifap(src, ifap))) { if (dp = find_prune_ifap(src, ifap)) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_new_nbr: new nbr on %A, unpruning for (%A, %A)", IFA_UNIQUE_ADDR(ifap), src->addr, src->src_gaddr)); pimdm_downstream_forward(src, dp, DSPROTO_MROUTE); } else { trace_tp (tp, TR_NORMAL, 0, ("pimdm_new_nbr: new nbr on %A, unpruning for (%A, %A)", IFA_UNIQUE_ADDR(ifap), src->addr, src->src_gaddr)); dp = pimdm_assert_downstream(src, ifap, DSPROTO_MROUTE); } } else { /* ifap is forwarding, make sure MROUTE is set */ BIT_SET(dp->ds_protos, DSPROTO_MROUTE); } } MRT_WALK_SRC_END(td->pim_mrt, src); } /**************************************************************************** * * PIM Rate-limiting for Asserts and Prunes * * pimdm_create_sg_rl_list * pimdm_free_sg_rl_list * pimdm_find_sg_rl_if * ****************************************************************************/ static void pimdm_create_sg_rl_list (tp, src) task *tp; source_t *src; { pimdm_source_t *pimdm_src; pimdm_rate_limit_times_t *rl_list; pimdm_rate_limit_times_t *rl; if_addr *ifap; pimdm_src = (pimdm_source_t*)src->src_data[0]; rl_list = (pimdm_rate_limit_times_t *) task_block_alloc(pimdm_rate_limit_times_block_index); pimdm_src->src_rlt = rl_list; bzero((char *)rl_list, sizeof(pimdm_rate_limit_times_t)); rl_list->forw = rl_list->back = rl_list; /* Add an entry for each of the interfaces */ IF_ADDR(ifap) { if(tp != mbr_get_iftask(ifap)) continue; rl = (pimdm_rate_limit_times_t *) task_block_alloc(pimdm_rate_limit_times_block_index); rl->forw = rl->back = rl; IFA_ALLOC(rl->ifap = ifap); rl->last_assert = rl->last_prune = 0; PIMDM_RATE_LIMIT_ENQ(rl, rl_list); } IF_ADDR_END (ifap); } /* When a new ifap is added, add it to each of the source rate-limit lists */ void pimdm_sg_rl_add_ifap(ifap) if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; pimdm_rate_limit_times_t *rl_list; pimdm_rate_limit_times_t *rl, *new_rl; /* Check for each source if the interface is in the rate limit list */ MRT_WALK_SRC (td->pim_mrt, src) { rl_list = PIMDM_SRC_RATE_LIMITS(src); rl = rl_list->forw; while(rl_list != rl) { if(rl->ifap == ifap) break; rl = rl->forw; } if(rl_list == rl) { /* It wasn't in the list, add it! */ new_rl = (pimdm_rate_limit_times_t *) task_block_alloc(pimdm_rate_limit_times_block_index); new_rl->forw = new_rl->back = new_rl; IFA_ALLOC(new_rl->ifap = ifap); new_rl->last_assert = new_rl->last_prune = 0; PIMDM_RATE_LIMIT_ENQ(new_rl, rl_list); } } MRT_WALK_SRC_END(td->pim_mrt, src); } /* When a pim ifap is deleted, delete it to each of the source * rate-limit lists */ void pimdm_sg_rl_delete_ifap(ifap) if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; source_t *src; pimdm_rate_limit_times_t *rl_list; pimdm_rate_limit_times_t *rl; /* do temporary testing for correct task struct */ /* REMOVE vvv */ assert(tp && tp->task_rtproto == RTPROTO_PIM && td->pim_mode == PIM_DENSE); /* REMOVE ^^^ */ /* If called during pim_terminate sequence, td->pim_mrt already freed */ if(!td->pim_mrt) return; /* Check for each source if the interface is in the rate limit list */ MRT_WALK_SRC (td->pim_mrt, src) { rl_list = PIMDM_SRC_RATE_LIMITS(src); rl = rl_list->forw; while(rl_list != rl) { if(rl->ifap == ifap) break; rl = rl->forw; } if(rl_list != rl) { /* Found it - delete it */ PIMDM_RATE_LIMIT_DEQ(rl); if(rl->ifap) IFA_FREE(rl->ifap); task_block_free(pimdm_rate_limit_times_block_index, rl); } } MRT_WALK_SRC_END(td->pim_mrt, src); } static void pimdm_free_sg_rl_list(src) source_t *src; { pimdm_rate_limit_times_t *rl_list = PIMDM_SRC_RATE_LIMITS(src); pimdm_rate_limit_times_t *gonner, *rl = rl_list->forw; while(rl_list != rl) { gonner = rl; rl = rl->forw; if(gonner->ifap) IFA_FREE(gonner->ifap); task_block_free(pimdm_rate_limit_times_block_index, gonner); } task_block_free(pimdm_rate_limit_times_block_index, rl_list); } /* Find the rate-limit record for a given source and interface */ static pimdm_rate_limit_times_t * pimdm_find_sg_rl_if (src, ifap) source_t *src; if_addr *ifap; { pimdm_rate_limit_times_t *rl_list = PIMDM_SRC_RATE_LIMITS(src); pimdm_rate_limit_times_t *rl = rl_list->forw; while (rl != rl_list) { if(rl->ifap == ifap) return rl; rl = rl->forw; } assert(rl == rl_list); return rl; } /**************************************************************************** * * PIM Messaging - PIM-Prune * * pimdm_schedule_jp * pimdm_deschedule_jp * pimdm_jp_delay_job * pimdm_delete_all_jp_entries * pimdm_jp_recv * pimdm_jp_send * ****************************************************************************/ /* pimdm_schedule_jp() * * Schedule a jp event for a source. A jp event is either: * 1. a delayed prune action, where a prune was received on a LAN * and pruning of the interface was delayed to allow for join receipts, or * 2. a delayed join transmission, where a prune was recieved on a LAN * and this downstream want to tell the upstream nbr to cancel the prune. */ static void pimdm_schedule_jp(tp, src, ifap, addr, holdtime, jp, delay) task *tp; /* PIMDM task */ source_t *src; /* The S,G entry */ if_addr *ifap; /* The interface to join/prune */ sockaddr_un *addr; /* The address field for the join/prune */ /* (this address will be duplicated */ time_t holdtime; /* The prune holdtime */ int jp; /* flag: JOIN or PRUNE */ time_t delay; /* delay before sending */ { pimdm_scheduled_jp_t *new_sjp, *sjp; trace_tp(tp, TR_NORMAL, 0, ("pimdm_schedule_jp: scheduling join/prune with delay %d", delay)); /* Ignore duplicates */ SCHED_JP_LIST(sjp, PIMDM_SRC_SCHED_JP(src)) { if(sjp->ifap == ifap && sjp->jp == jp && sjp->addr == addr) break; } SCHED_JP_LIST_END(sjp, PIMDM_SRC_SCHED_JP(src)); if(sjp) { trace_tp(tp, TR_NORMAL, 0, ("pimdm_schedule_jp: join/prune already scheduled")); return; } /* If delay is 0, then send it immediately */ if (delay == 0) { trace_tp(tp, TR_NORMAL, 0, ("pimdm_schedule_jp: zero delay, sending immediately")); pimdm_jp_send_one(tp, src, ifap, pim_all_routers, addr, holdtime, jp); return; } /* Create a new one delayed join/prune */ new_sjp = (pimdm_scheduled_jp_t *) task_block_alloc (pimdm_scheduled_jp_block_index); new_sjp->forw = new_sjp->back = new_sjp; IFA_ALLOC(new_sjp->ifap = ifap); new_sjp->src = src; new_sjp->holdtime = holdtime; new_sjp->addr = sockdup(addr); new_sjp->jp = jp; new_sjp->ttp = (task_timer *)0; SCHED_JP_ENQ(new_sjp, PIMDM_SRC_SCHED_JP(src)); task_restart_timer(&(new_sjp->ttp), tp, "Join/Prune delay timer", 0, delay, pimdm_jp_delay_job, (void_t) new_sjp); } /* pimdm_deschedule_jp * * removes a scheduled join or prune from the timer q * returns 1 if there is a removal, 0 otherwise. */ static int pimdm_deschedule_jp(ifap, src, jp) if_addr *ifap; source_t *src; int jp; { pimdm_scheduled_jp_t *sjp; if(!PIMDM_SRC_SCHED_JP(src)) return 0; SCHED_JP_LIST(sjp, PIMDM_SRC_SCHED_JP(src)) { if(sjp->jp == jp && sjp->ifap == ifap) break; } SCHED_JP_LIST_END(sjp, PIMDM_SRC_SCHED_JP(src)); if(sjp && sjp != PIMDM_SRC_SCHED_JP(src)) { /* Found it - wipe it! */ SCHED_JP_DEQ(sjp); IFA_FREE(sjp->ifap); sockfree(sjp->addr); task_timer_delete(sjp->ttp); task_block_free(pimdm_scheduled_jp_block_index, sjp); return 1; } else return 0; } /* pimdm_delete_all_jp_entries() * * Deletes all delayed joins and prunes for a given source. * Called by pimdm_mrt_cleanup when (S,G) state is deleted. */ static void pimdm_delete_all_jp_entries(src) source_t *src; { pimdm_scheduled_jp_t *sjp, *gonner; sjp = PIMDM_SRC_SCHED_JP(src); while(sjp != PIMDM_SRC_SCHED_JP(src)) { gonner = sjp; sjp = sjp->forw; IFA_FREE(sjp->ifap); sockfree(gonner->addr); task_timer_delete(gonner->ttp); task_block_free(pimdm_scheduled_jp_block_index, gonner); } task_block_free(pimdm_scheduled_jp_block_index, sjp); } /* pimdm_jp_delay_job() * * Called when a join or prune delay timer expires. The source should * prune the interface or send a join to the LAN, accordingly. * */ static void pimdm_jp_delay_job(tip, interval) task_timer *tip; time_t interval; { task *tp = tip->task_timer_task; pimdm_scheduled_jp_t *sjp = (pimdm_scheduled_jp_t *)tip->task_timer_data; if(sjp->jp == JOIN) { trace_tp (tp, TR_TIMER, 0, ("pimdm_jp_delay_job: Sending scheduled (%A,%A) join on LAN i/f %s (%A)", sjp->src->addr, sjp->src->src_group->addr, sjp->ifap->ifa_name, IFA_UNIQUE_ADDR(sjp->ifap))); /* Time to send a join */ SCHED_JP_DEQ(sjp); pimdm_jp_send_one(tp, sjp->src, sjp->ifap, pim_all_routers, sjp->addr, 0, JOIN); IFA_FREE(sjp->ifap); sockfree(sjp->addr); task_timer_delete(sjp->ttp); task_block_free(pimdm_scheduled_jp_block_index, sjp); } else { downstream_t *dp; /* Time to prune the interface */ SCHED_JP_DEQ(sjp); dp = find_downstream_ifap(sjp->src, sjp->ifap); if(!dp) { /* Pruning an already pruned i/f - probably due to * prune rate-limiting... */ trace_tp (tp, TR_TIMER, 0, ("pimdm_jp_delay_job: (%A,%A) prune scheduled on forwarding i/f %s (%A)", sjp->src->addr, sjp->src->src_group->addr, sjp->ifap->ifa_name, IFA_UNIQUE_ADDR(sjp->ifap))); return; } /* Move it from the oiflist to the prune list */ pimdm_downstream_prune(sjp->src, dp, sjp->holdtime, dp->ds_protos); trace_tp (tp, TR_ROUTE, 0, ("pimdm_jp_delay_job: (%A,%A): pruned LAN i/f %s (%A)", sjp->src->addr, sjp->src->src_group->addr, sjp->ifap->ifa_name, IFA_UNIQUE_ADDR(sjp->ifap))); task_timer_delete(sjp->ttp); sockfree(sjp->addr); task_block_free(pimdm_scheduled_jp_block_index, sjp); } } /* pimdm_jp_recv() * * A monolithic procedure that handles reciept of any join or prune * message. * * Called when a join a prune message is received. * * PROTOCOL: * 1. For each joined (S,G): * a. if addressed to me or 0.0.0.0, cancel pruning of the interface if * it is scheduled. * b. if it is addressed to a different router, cancel transmission of * my join for this (S,G) if it is scheduled. * 2. For each pruned (S,G): * a. if received on P-to-P, prune the interface * b. if received on a LAN addressed to me or 0.0.0.0, * send prune back to LAN, schedule prune * b. if received on a LAN NOT addressed to me but to my upstream nbr * or 0.0.0.0, schedule transmission of a join back to LAN * if I (or other components) have interfaces in forwarding state. * */ void pimdm_jp_recv (ifap, pim, pimlen) if_addr *ifap; pim_t *pim; int pimlen; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; byte *cp = (byte *) (pim + 1); sockaddr_un *addr; sockaddr_un *gaddr, *gmask, *saddr, *smask; euaddr_t eu; byte ngroups; u_int16 holdtime; source_t *src; trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s], receiving join/prune on interface %s (%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); if ((u_int) pimlen < sizeof (jp_t)) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_jp_recv: join/prune message too small %d", pimlen)); return; } /* Decode the Address field */ GET_EUADDR_SOCK((euaddr_t *) &eu, addr, cp); if (!(BIT_TEST(ifap->ifa_state, IFS_POINTOPOINT)) && (!addr || (!sockaddrcmp_in (addr, pimdm_anyone)) && (if_withsubnet (addr) != ifap))) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_jp_recv[%s]: recv'd JP from non-local src %A", tp->task_name, addr)); return; } *cp++; /* reserved */ ngroups = *cp++; /* # of grps in this message */ GET_SHORT(holdtime, cp); trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s], addr %A, holdtime %d", tp->task_name, addr, holdtime)); /* For each group */ while (ngroups--) { u_int16 joins, prunes; eaddr_t eg, *egrp = ⪚ source_t *src; int len; int join = 0; /* get the current egrp */ GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (!gaddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s], error decoding the group addr", tp->task_name)); return; } trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s], group %A (%lx), mask %A", tp->task_name, gaddr, ntohl(sock2ip(gaddr)), gmask)); /* Verify that the group is a valid multicast address */ if (!IN_MULTICAST(ntohl(sock2ip(gaddr)))) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s]: is not a multicast address, ignoring remaining message", tp->task_name, gaddr)); return; } /* insist that group addresses in joins are hostmasks */ if (gmask != inet_mask_host) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_jp_recv[%s]: Group Address [%A/%d] aggregation not allowed", tp->task_name, gaddr, egrp->ea_masklen)); return; } /* cp must be pointing at the current # of joins */ GET_SHORT(joins, cp); GET_SHORT(prunes, cp); trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s], joins %d, prunes %d", tp->task_name, joins, prunes)); /* * now process the joinlist for the current group, cp points at * the first esrc. */ for (; joins; joins--) { eaddr_t es, *esrc = &es; int type; GET_EADDR_SOCK(esrc, saddr, smask, cp); if (!saddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s]: is invalid src addr for %A, ignoring remaining message", tp->task_name, gaddr)); return; } if (!smask) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s]: (%A, %A): masklen %d is invalid, ignoring the rest.", tp->task_name, saddr, gaddr, esrc->ea_masklen)); return; } if((EADDR_TYPE(esrc->ea_flags)) != EATYPE_SG) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: illegal packet type: (%A, %A) 0x%x", saddr, gaddr, esrc->ea_flags)); return; } /* Process the SG Join address */ src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: (%A, %A) join with no pim route", saddr, gaddr)); continue; } if(sockaddrcmp_in(addr, ifap->ifa_addr_local) || sockaddrcmp_in(addr, pimdm_anyone)) { /* Find the scheduled prune */ if (pimdm_deschedule_jp(ifap, src, PRUNE)) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: canceling (%A,%A) prune for interface %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); } else { /* No prune scheduled for this S,G and i/f */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: received (%A,%A) join but not prune scheduled for interface %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); continue; } } else { /* Receiving join not addressed to me */ /* Find the scheduled join if it exists */ if (pimdm_deschedule_jp(ifap, src, JOIN)) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: canceling (%A,%A) join for interface %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); } else { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: received (%A,%A) join on interface %s (%A) but I'm not the addressee and I don't have a join scheduled", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); continue; } } } /* * we've exhausted the join list, now we'll do the prune list. * esrc must be pointing to the first prune src */ for (; prunes; prunes--) { eaddr_t es, *esrc = &es; int type; GET_EADDR_SOCK(esrc, saddr, smask, cp); if (!saddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s]: %A: src %x is invalid, ignoring it", tp->task_name, gaddr, ntohl (esrc->ea_addr))); continue; } if (!smask) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv[%s]: prune (%A, %A): masklen %d is invalid, ignoring it", tp->task_name, saddr, gaddr, esrc->ea_masklen)); continue; } if((EADDR_TYPE(esrc->ea_flags)) != EATYPE_SG) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: illegal packet type: (%A, %A) 0x%x", saddr, gaddr, esrc->ea_flags)); return; } src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: (%A, %A) prune with no pim route", saddr, gaddr)); continue; } if (BIT_TEST(ifap->ifa_state, IFS_POINTOPOINT)) { /* Received prune on Point-to-Point interface */ if(sockaddrcmp_in(addr, ifap->ifa_addr_local) || sockaddrcmp_in(addr, pimdm_anyone)) { /* Process the Prune immediately */ downstream_t *dp; dp = find_downstream_ifap(src, ifap); if(!dp) { /* Receiving Prune for an already pruned i/f */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: received (%A,%A) prune on already pruned point-to-point i/f %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); continue; } /* Move it from the oiflist to the prune list */ pimdm_downstream_prune(src, dp, holdtime, dp->ds_protos); trace_tp (tp, TR_ROUTE, 0, ("pimdm_jp_recv: (%A,%A): pruned point-to-point i/f %s (%A)", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); } else { /* Received prune on point-to-point but not addressed to me: * something went wrong! */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_recv: received (%A,%A) prune on point-to-point i/f %s (%A) but it's not addressed to me.", saddr, gaddr, ifap->ifa_link->ifl_name, IFA_UNIQUE_ADDR(ifap))); continue; } } else { /* Received prune on LAN interface */ if(sockaddrcmp_in(addr, ifap->ifa_addr_local) || sockaddrcmp_in(addr, pimdm_anyone)) { /* The Prune message was for me - Schedule the Prune and send * one out again */ pimdm_jp_send_one(tp, src, ifap, pim_all_routers, addr, holdtime, PRUNE); pimdm_schedule_jp(tp, src, ifap, addr, holdtime, PRUNE, PIMDM_JOIN_DELAY+1); } else if((src->src_upstream->nbr && sockaddrcmp_in(addr, src->src_upstream->nbr)) || sockaddrcmp_in(addr, pimdm_anyone)) { /* The Prune message was for my upstream PIM neighbor on the * LAN - Send a join if I have receivers */ if(!(IS_NEG_CACHE(src->src_flags))) { pimdm_schedule_jp(tp, src, ifap, addr, 0, JOIN, grand(PIMDM_JOIN_DELAY)); } } } } } } static void pimdm_jp_send(ifap, ip, iplen) if_addr *ifap; struct ip *ip; int iplen; { pim_t *pim = (pim_t *) (ip + 1); int rc; pim->pim_vers = 0x23; rc = pim_send (ifap, ip, iplen, pim_all_routers, FALSE); } /* pimdm_max_prune_time() * * Calculate the maximume of the (S,G) prune timers. Returns the * maximum TIME REMAINING. */ static time_t pimdm_max_prune_time (src) source_t *src; { downstream_t *dp; time_t max = 0; DOWNSTREAM_LIST(dp, PIMDM_SRC_PRUNE(src)) { register time_t time_remaining = dp->ds_timeout - time_sec; if(time_remaining > max) max = time_remaining; } DOWNSTREAM_LIST_END(dp, PIMDM_SRC_PRUNE(src)); if (!max) { return pim_default_jp_holdtime; } return max; } /* Send a join or prune * * PROTOCOL: Send the join or prune specified. If I previously had * been sending Grafts and waiting for Graft-Ack, cancel the grafting. */ void pimdm_jp_send_one (tp, src, ifap, dest, addr, holdtime, join) task *tp; source_t *src; if_addr *ifap; sockaddr_un *dest; /* The destination host or group for the ip msg */ sockaddr_un *addr; /* The address field for the message */ time_t holdtime; int join; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; struct ip *ip = (struct ip *) task_get_send_buffer(struct ip *); pim_t *pim = (pim_t *) (ip + 1); byte *cp = (byte *) (pim + 1); eaddr_t *eaddr; int len; trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_send_one[%s], sending (%A,%A) prune: ifap %s (%A), dest %A, addr %A, holdtime %d, is_join %d", tp->task_name, src->addr, src->src_gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), dest, addr, holdtime, join)); if(mbr_get_iftask(ifap) != tp) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_send_one[%s], cant send join/prune on non-pimdm interface %s (%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); return; } if(!addr) { /* No upstream router */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_send_one[%s], no upstream router", tp->task_name)); return; } PUT_EUADDR(addr, cp); /* encd'd ucast nbr address */ *cp++ = 0; /* resrvd */ *cp++ = 1; /* # of groups */ PUT_SHORT(holdtime, cp); /* cp points at the first, and only encoded mcast group */ PUT_EADDR(src->src_gaddr, src->src_gmask, 0, cp); /* set the number of join and prune sources */ (join < 0 || join > 1) ? join = 1 : join; PUT_SHORT(join, cp); /* will be 1 for join or prune */ PUT_SHORT(!join, cp); eaddr = (eaddr_t *)cp; PUT_EADDR(src->addr, src->mask, 0, cp); eaddr->ea_flags = (byte)src->src_flags; len = (int) (cp - (byte *) ip); pim->pim_vers = 0x23; if(join == PRUNE && (ifap == src->src_upstream->ifap)) /* Cancel graft if sending prune on iif */ pimdm_graft_free_state(tp, src); pim_send (ifap, ip, len, dest, 0); } /**************************************************************************** * * PIM Messaging - PIM-Graft * * pimdm_graft_retransmit * pimdm_graft_send * pimdm_graft_recv * pimdm_graft_send_one * pimdm_graft_ack_recv * pimdm_graft_ack_send * pimdm_graft_free_state * ****************************************************************************/ /* Send out a graft for every MRT entry in the graft retransmission list */ static void pimdm_graft_retransmit(tip, interval) task_timer *tip; time_t interval; { task *tp = tip->task_timer_task; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; mrt_src_list_t *curr; trace_tp (tp, TR_TIMER, 0, ("pimdm_graft_retransmit: retransmitting all active grafts.")); assert(td->pimdm_graft_src_list); MRT_SRC_LIST(curr, td->pimdm_graft_src_list) { (void)pimdm_graft_send_one(tp, curr->src); } MRT_SRC_LIST_END(curr, td->pimdm_graft_src_list); task_restart_timer(&(td->pimdm_graft_retrans_timer), tp, "Graft Retransmission Timer", PIMDM_GRAFT_RETRANS_PERIOD, /* period */ 0, /* offset */ pimdm_graft_retransmit, (void_t) 0); } /* Begin sending grafts for an (S,G) entry: send the first and add the * entry to the graft retransmission list. */ void pimdm_graft_send(tp, src) task *tp; source_t *src; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; int was_sent; if(mbr_get_iftask(src->src_upstream->ifap) != tp) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_jp_send_one[%s], cant send graft on non-pimdm interface %s (%A)", tp->task_name, src->src_upstream->ifap->ifa_name, IFA_UNIQUE_ADDR(src->src_upstream->ifap))); return; } if(PIMDM_SRC_GRAFT_RETRANS(src)) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_send: Graft already active for (%A, %A)", src->addr, src->src_gaddr)); return; } /* Send the first one */ was_sent = pimdm_graft_send_one(tp, src); if(!was_sent) return; /* Set up retransmission for this (S,G) */ if(!td->pimdm_graft_src_list) { td->pimdm_graft_src_list = mrt_src_list_alloc(); td->pimdm_graft_src_list->forw = td->pimdm_graft_src_list->back = td->pimdm_graft_src_list; td->pimdm_graft_src_list->src = (source_t *)0; } if(td->pimdm_graft_src_list == td->pimdm_graft_src_list->forw) { /* Previously no grafts active: start the timer */ task_restart_timer(&(td->pimdm_graft_retrans_timer), tp, "Graft Retransmission Timer", PIMDM_GRAFT_RETRANS_PERIOD, /* period */ 0, /* offset */ pimdm_graft_retransmit, (void_t) 0); } MRT_SRC_LIST_ADD(src, td->pimdm_graft_src_list); PIMDM_SRC_GRAFT_RETRANS(src) = td->pimdm_graft_src_list->forw; } /* pimdm_graft_recv() * * PROTOCOL: * 1. If a prune is scheduled, cancel it. * 1. If receiving interface is pruned, then add it to the oiflist * 2. Unicast a graft ack back to the graft sender * * The mbr layer will trigger further grafts upstream if this entry * had negative cache state. * * NOTE that this implementation does not aggregate grafts. However, * we should accept aggregated grafts from other implementations just * in case... */ void pimdm_graft_recv (ifap, pim, pimlen) if_addr *ifap; pim_t *pim; int pimlen; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; byte *cp = (byte *) (pim + 1); sockaddr_un *addr; sockaddr_un *gaddr, *gmask, *saddr, *smask; euaddr_t eu; byte ngroups; u_int16 holdtime; source_t *src; trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv[%s], receiving graft on interface %s (%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); if ((u_int) pimlen < sizeof (jp_t)) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_graft_recv: graft message too small %d", pimlen)); return; } /* Decode the Address field - Unsed for graft */ GET_EUADDR_SOCK((euaddr_t *) &eu, addr, cp); *cp++; /* reserved */ ngroups = *cp++; /* # of grps in this message */ GET_SHORT(holdtime, cp); /* unsed for graft */ /* For each group */ while (ngroups--) { u_int16 joins, prunes; eaddr_t eg, *egrp = ⪚ source_t *src; int len; int join = 0; /* get the current egrp */ GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (!gaddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv[%s], error decoding the group addr", tp->task_name)); return; } /* Verify that the group is a valid multicast address */ if (!IN_MULTICAST(ntohl(sock2ip(gaddr)))) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv[%s]: is not a multicast address, ignoring remaining message", tp->task_name, gaddr)); return; } /* insist that group addresses in joins are hostmasks */ if (gmask != inet_mask_host) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_graft_recv[%s]: Group Address [%A/%d] aggregation not allowed", tp->task_name, gaddr, egrp->ea_masklen)); return; } /* cp must be pointing at the current # of joins */ GET_SHORT(joins, cp); GET_SHORT(prunes, cp); /* * now process the graft (join) list for the current group, cp points at * the first esrc. */ for (; joins; joins--) { downstream_t *dp; eaddr_t es, *esrc = &es; int type; GET_EADDR_SOCK(esrc, saddr, smask, cp); if (!saddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv[%s]: is invalid src addr for %A, ignoring remaining message", tp->task_name, gaddr)); return; } if (!smask) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv[%s]: (%A, %A): masklen %d is invalid, ignoring the rest.", tp->task_name, saddr, gaddr, esrc->ea_masklen)); return; } if((EADDR_TYPE(esrc->ea_flags)) != EATYPE_SG) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv: illegal packet type: (%A, %A) 0x%x", saddr, gaddr, esrc->ea_flags)); return; } /* Process the SG Join address */ src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv: (%A, %A) graft with no pim route", saddr, gaddr)); continue; } /* Everything is okay */ pimdm_deschedule_jp(ifap, src, PRUNE); if(dp = find_prune_ifap(src, ifap)) { /* interface is pruned, add it to the oiflist */ trace_tp (tp, TR_ROUTE, 0, ("pimdm_graft_recv: (%A,%A) grafting interface %s (%A)", gaddr, src->addr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); pimdm_downstream_forward(src, dp, dp->ds_protos); } else { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_recv: (%A, %A) graft for forwarding interface", saddr, gaddr)); if(dp=find_downstream_ifap(src,ifap)) /* Be sure the MROUTE bit is set (why wouldn't it be???) */ BIT_SET(dp->ds_protos, DSPROTO_MROUTE); } /* Respond with a graft-ack */ pimdm_graft_ack_send(tp, ifap, pim, pimlen); } /* Ignore prunes within the message - grafts will always be in the * join portion (according to Dino). */ } } /* Returns 1 if the graft was sent, 0 otherwise */ int pimdm_graft_send_one(tp, src) task *tp; source_t *src; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; struct ip *ip = (struct ip *) task_get_send_buffer(struct ip *); pim_t *pim = (pim_t *) (ip + 1); byte *cp = (byte *) (pim + 1); int len; sockaddr_un *nhop; if_addr *ifap; eaddr_t *eaddr; trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_send_one[%s], sending (%A, %A) graft", tp->task_name, src->addr, src->src_gaddr)); pim->pim_vers = 0x26; /* src_upstream->nbr should only be NULL iff src is local */ nhop = src->src_upstream->nbr; ifap = src->src_upstream->ifap; if (!nhop) { /* iff S is on a local subnet */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_send_one[%s], no upstream router", tp->task_name)); return 0; } PUT_EUADDR(nhop, cp); /* encd'd ucast RPF nbr */ *cp++ = 0; /* resrvd */ *cp++ = 1; /* # of groups */ PUT_SHORT(0, cp); /* No holdtime for graft */ /* cp points at the first, and only encoded mcast group */ PUT_EADDR(src->src_gaddr, src->src_gmask, 0, cp); /* Grafts always go in the JOIN portion of the Join/Prune packet format */ PUT_SHORT(1, cp); /* 1 join address for the graft*/ PUT_SHORT(0, cp); /* no prune addresses */ eaddr = (eaddr_t *)cp; PUT_EADDR(src->addr, src->mask, 0, cp); eaddr->ea_flags = (byte)src->src_flags; len = (int) (cp - (byte *) ip); pim_send (ifap, ip, len, nhop, 0); return 1; } /* pimdm_graft_ack_recv() * * PROTOCOL: * 1. stop retransmitting PIM-Grafts. * 2. delete the graft-retrans timer if there are no other grafts to transmit. */ void pimdm_graft_ack_recv (ifap, pim, pimlen) if_addr *ifap; pim_t *pim; int pimlen; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; byte *cp = (byte *) (pim + 1); sockaddr_un *addr; sockaddr_un *gaddr, *gmask, *saddr, *smask; euaddr_t eu; byte ngroups; u_int16 holdtime; trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv[%s], receiving graft on interface %s (%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); if ((u_int) pimlen < sizeof (jp_t)) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_graft_ack_recv: graft message too small %d", pimlen)); return; } /* Decode the Address field - Unsed for graft */ GET_EUADDR_SOCK((euaddr_t *) &eu, addr, cp); *cp++; /* reserved */ ngroups = *cp++; /* # of grps in this message */ GET_SHORT(holdtime, cp); /* unsed for graft */ /* For each group */ while (ngroups--) { u_int16 joins, prunes; eaddr_t eg, *egrp = ⪚ int len; int join = 0; /* get the current egrp */ GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (!gaddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv[%s], error decoding the group addr", tp->task_name)); return; } /* Verify that the group is a valid multicast address */ if (!IN_MULTICAST(ntohl(sock2ip(gaddr)))) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv[%s]: is not a multicast address, ignoring remaining message", tp->task_name, gaddr)); return; } /* insist that group addresses in joins are hostmasks */ if (gmask != inet_mask_host) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_graft_ack_recv[%s]: Group Address [%A/%d] aggregation not allowed", tp->task_name, gaddr, egrp->ea_masklen)); return; } /* cp must be pointing at the current # of joins */ GET_SHORT(joins, cp); GET_SHORT(prunes, cp); /* * now process the graft (join) list for the current group, cp points at * the first esrc. */ for (; joins; joins--) { eaddr_t es, *esrc = &es; source_t *src; int type; GET_EADDR_SOCK(esrc, saddr, smask, cp); if (!saddr) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv[%s]: is invalid src addr for %A, ignoring remaining message", tp->task_name, gaddr)); return; } if (!smask) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv[%s]: (%A, %A): masklen %d is invalid, ignoring the rest.", tp->task_name, saddr, gaddr, esrc->ea_masklen)); return; } if((EADDR_TYPE(esrc->ea_flags)) != EATYPE_SG) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv: illegal packet type: (%A, %A) 0x%x", saddr, gaddr, esrc->ea_flags)); return; } /* Process the SG Join address */ src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv: (%A, %A) graft-ack with no pim route", saddr, gaddr)); continue; } /* Process the ack - remove the grafts from the queue */ if(!PIMDM_SRC_GRAFT_RETRANS(src)) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_recv: (%A,%A) graft-ack received on interface %s (%A) but NO GRAFT SENT", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); continue; } assert(td->pimdm_graft_src_list); MRT_SRC_LIST_DEQ(PIMDM_SRC_GRAFT_RETRANS(src)); mrt_src_list_free(PIMDM_SRC_GRAFT_RETRANS(src)); PIMDM_SRC_GRAFT_RETRANS(src) = (mrt_src_list_t *)0; if(td->pimdm_graft_src_list == td->pimdm_graft_src_list->forw) { /* No more active grafts */ task_timer_delete(td->pimdm_graft_retrans_timer); td->pimdm_graft_retrans_timer = (task_timer *)0; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_graft_ack_recv: (%A,%A) graft on interface %s (%A) acknowledged", saddr, gaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); } } } /* Copy the graft packet and send it out as a graft-ack */ void pimdm_graft_ack_send(tp, ifap, graft_pim, graft_pimlen) task *tp; if_addr *ifap; /* ifap to send on */ pim_t *graft_pim; /* The graft packet responding to */ int graft_pimlen; /* length of graft mesg */ { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; struct ip *ip = (struct ip *) task_get_send_buffer(struct ip *); pim_t *pim = (pim_t *) (ip + 1); int len; trace_tp (tp, TR_NORMAL, 0, ("pimdm_graft_ack_send[%s], sending graft-ack on interface %s (%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); /* Send the graft-ack as a copy of the original graft packet */ bcopy(graft_pim, pim, graft_pimlen); pim->pim_vers = 0x27; len = (int)((byte *) pim - (byte *) ip) + graft_pimlen; /* task_recv_srcaddr is a global indicating the source of the most * recently received packet */ pim_send(ifap, ip, len, task_recv_srcaddr, 0); } static void pimdm_graft_free_state(tp, src) task *tp; source_t *src; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; if(PIMDM_SRC_GRAFT_RETRANS(src)) { assert(td->pimdm_graft_src_list); MRT_SRC_LIST_DEQ(PIMDM_SRC_GRAFT_RETRANS(src)); mrt_src_list_free(PIMDM_SRC_GRAFT_RETRANS(src)); if(td->pimdm_graft_src_list == td->pimdm_graft_src_list->forw) { /* No more active grafts */ task_timer_delete(td->pimdm_graft_retrans_timer); td->pimdm_graft_retrans_timer = (task_timer *)0; } } } /**************************************************************************** * * PIM Messaging - PIM-Assert * * pimdm_assert_send * pimdm_assert_recv * ****************************************************************************/ void pimdm_assert_send (ifap, src, pref, metric) if_addr *ifap; /* IN: the wrong iff */ source_t *src; /* IN: the source */ pref_t pref; /* IN: assert preference */ metric_t metric; /* IN: assert metric */ { task *tp = mbr_get_iftask(ifap); struct ip *ip = (struct ip *) task_get_send_buffer(struct ip *); pim_t *pim = (pim_t *) (ip + 1); byte *cp = (byte *) (pim + 1); sockaddr_un *saddr = src->addr; sockaddr_un *gaddr = src->src_gaddr; trace_tp (tp, TR_NORMAL, 0, ("pimdm_assert_send: Assert for (%A, %A): pref %d, metric %d", saddr, gaddr, pref, metric)); pim->pim_vers = 0x25; PUT_EADDR(gaddr,inet_mask_host, 0, cp); PUT_EUADDR(saddr, cp); /* cp points at the first encoded group address */ PUT_LONG(pref, cp); PUT_LONG(metric, cp); pim_send (ifap, ip, cp - (byte *) ip, pim_all_routers, 0); } /* pimdm_assert_recv() * * PROTOCOL: * 1. if there is no (S,G) entry matching the assert, create one. * 2. assert arrived on an oif: * a. compare metrics and prefs * b. if we lost, prune the interface immediately * c. if we won and there are no directly-connected receivers, schedule * prune and send a prune for this interface to the LAN, then wait * for possible joins from downstream routers on the LAN. Also send * an assert for my route to the LAN to announce winner. * 3. assert arrived on iif: * a. if the assert wins, change the upsteam nbr to the assert winner * if not set already. * b. create assert state * c. send a delayed join if I (or another component) has downstream * receivers * */ void pimdm_assert_recv (ifap, pim, pimlen) if_addr *ifap; /* IN: iif */ pim_t *pim; /* IN: pim header */ int pimlen; /* IN: length of the pim msg */ { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; byte *cp = (byte *) (pim + 1); /* src addr */ euaddr_t eu; eaddr_t eg, *egrp = ⪚ source_t *src = (source_t *) 0; int they_won = 0; sockaddr_un *saddr, *gaddr, *gmask; u_long metric, pref; downstream_t *dp, *pp; GET_EADDR_SOCK(egrp, gaddr, gmask, cp); GET_EUADDR_SOCK((euaddr_t *) &eu, saddr, cp); GET_LONG(pref, cp); GET_LONG(metric, cp); trace_tp (tp, TR_NORMAL, 0, ("pimdm_assert_recv: Assert from %A on interface %s (%A) for (%A, %A): pref %d, metric %d", task_recv_srcaddr, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), saddr, gaddr, pref, metric)); src = mrt_locate_source (td->pim_mrt, gaddr, saddr); if (!src) { /* For some reason, it's possible for asserts to be processed * before the data alerts a cache miss. Therefore, when an * assert is received, create (S,G) state and continue, since * we know by the assert that there are upstream forwarders. */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_assert_recv: no mrt entry for (%A, %A)", saddr, gaddr)); src = pimdm_mrt_create_sg (tp, gaddr, inet_mask_host, saddr, inet_mask_host, SPT_BIT); } if (src->src_upstream->ifap != ifap) { /* * The assert did not come in on the iif, we first check * whether it came in on an oif. if not ignore. */ if(!(dp = find_downstream_ifap(src, ifap)) && !(pp = find_prune_ifap(src, ifap))) { /* the packet did not come in on an oif interface, ignore */ trace_tp (tp, TR_NORMAL, 0, ("pimdm_assert_recv (%A, %A) came in non{iif,oif} interface %A, ignoring", saddr, gaddr, IFA_UNIQUE_ADDR(ifap))); return; } /* Okay, we have an assert that arrived on an oif */ they_won = (pref < src->src_upstream->rt->rt_preference) ? 1 : ((pref == src->src_upstream->rt->rt_preference) ? ((metric < src->src_upstream->rt->rt_metric) ? 1 : ((metric == src->src_upstream->rt->rt_metric) ? ((sock2ip(ifap->ifa_addr_local) < sock2ip(task_recv_srcaddr)) ? 1 : 0) : 0)) : 0); if(they_won) { if(dp) { trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: lost assert election for (%A, %A) on non{iif,oif} interface %A, pruning", saddr, gaddr, IFA_UNIQUE_ADDR(ifap))); pimdm_downstream_prune(src, dp, pim_default_jp_holdtime, dp->ds_protos); } else { trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: lost assert election for (%A, %A) on non{iif,oif} interface %A, interface already pruned!", saddr, gaddr, IFA_UNIQUE_ADDR(ifap))); return; } } else { if(dp) { /* we won! * fling the assert back to were it came from */ trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: won assert election for (%A, %A) on non{iif,oif} interface %A", saddr, gaddr, IFA_UNIQUE_ADDR(ifap))); /* Check if there are directly connected members */ if (!(igmp_find_group(sock2ip(gaddr), ifap))) { /* No, there are NO directly connected members - schedule prune */ pimdm_jp_send_one(tp, src, ifap, pim_all_routers, ifap->ifa_addr_local, pim_default_jp_holdtime, PRUNE); pimdm_schedule_jp(tp, src, ifap, ifap->ifa_addr_local, pim_default_jp_holdtime, PRUNE, PIMDM_JOIN_DELAY+1); } pimdm_assert_send (ifap, src, src->src_upstream->rt->rt_preference, src->src_upstream->rt->rt_metric); } } /* we won */ } /* Assert arrived on iif - this is a downstream router on the LAN - * a. listen for the assert winner * b. update iif entry * c. send a delayed join if I have downstream receivers */ else { trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: current upstream nbr: %A, upstream pref %d, upstream metric %d", src->src_upstream->nbr, src->src_upstream->pref, src->src_upstream->metric)); if(!(src->src_upstream->nbr)) { trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pimdm_assert_recv: received PIM-Assert for (%A, %A) on iif with null upstream neighbor!", saddr, gaddr)); return; } /* If assert received from previous winner with same pref/metric, * then they win again! */ if(src->src_upstream->nbr && sockaddrcmp_in(src->src_upstream->nbr, task_recv_srcaddr) && pref == src->src_upstream->pref && metric == src->src_upstream->metric) they_won = 1; else they_won = /* they being the challenger */ (pref < src->src_upstream->pref) ? 1 : ((pref == src->src_upstream->pref) ? ((metric < src->src_upstream->metric) ? 1 : ((metric == src->src_upstream->metric) ? ((sock2ip(src->src_upstream->nbr) < sock2ip(task_recv_srcaddr)) ? 1 : 0) : 0)) : 0); if (!they_won) { trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: challenger %A lost assert election for (%A, %A) to %A", task_recv_srcaddr, saddr, gaddr, src->src_upstream->nbr)); return; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: challenger %A WON assert election for (%A, %A)", task_recv_srcaddr, saddr, gaddr)); /* at this point the new assert has won */ if (!sockaddrcmp_in(src->src_upstream->nbr, task_recv_srcaddr)) { trace_tp (tp, TR_ROUTE, 0, ("pimdm_assert_recv: updating assert state for new forwarder", task_recv_srcaddr, saddr, gaddr)); /* Change to the new upstream neighbor */ src->src_upstream->nbr = sockdup (task_recv_srcaddr); mbr_reset_nbr(src, task_recv_srcaddr); if (ifap != src->src_upstream->ifap) { IFA_ALLOC(src->src_upstream->ifap = ifap); mbr_reset_upstream(src); } src->src_upstream->pref = pref; src->src_upstream->metric = metric; } /* _new_ assert winner */ /* Update state and send Join for current assert winner */ PIMDM_SRC_ASSERT_TIMEOUT(src) = time_sec + pimdm_default_assert_timeout; /* Check the src_flags NEG_CACHE_BIT, which is sensitive records * whether the MBR state is negative cache or forwarding (instead * of just checking pimdm interfaces only). */ if(!(IS_NEG_CACHE(src->src_flags))) { pimdm_schedule_jp(tp, src, ifap, task_recv_srcaddr, 0, JOIN, grand(PIMDM_JOIN_DELAY)); } /* oiflist isn't null */ } } /**************************************************************************** * * Unicast Routing Changes * * pimdm_upstream_delete * pimdm_upstream_add * pimdm_flash * ****************************************************************************/ static void pimdm_upstream_delete (src) source_t *src; { task *tp = src->src_task; upstream_t *up = src->src_upstream; mrt_src_list_t *srcs, *s = (mrt_src_list_t *) 0; if (!up) return; /* now get the src list from up->rt */ rttsi_get (up->rt->rt_head, tp->task_rtbit, (byte *) &srcs); /* find the srclist entry that points to src */ if (srcs) { MRT_SRC_LIST(s, srcs) { if (s->src == src) break; } MRT_SRC_LIST_END(s, srcs); } if (s) { /* remvoe src list from srcs */ MRT_SRC_LIST_DEQ(s); if (srcs == srcs->forw) { /* if the list is empty reset the announcement bit on up->rt */ rt_open(tp); rtbit_reset (up->rt, tp->task_rtbit); rt_close(tp, 0, 0, 0); /* get rid of pim tsi from up->rt */ mrt_src_list_free(srcs); rttsi_reset (up->rt->rt_head, tp->task_rtbit); } } else { trace_log_tf (pim_trace_options, TRC_NL_AFTER, LOG_ERR, ("pimdm_upstream_delete[%s]: no rt_entry has no tsi info about (%A, %A)", tp->task_name, src->addr, src->src_gaddr)); } IFA_FREE(src->src_upstream->ifap); mrt_upstream_delete (src->src_upstream); src->src_upstream = (upstream_t *) 0; } /* * this function points src->src_rt at the RPF rt_entry used for * incoming data from src. it also sets the tsi bit and adds * src to the mrt_src_list stored by tsi_data. */ static void pimdm_upstream_add (src) source_t *src; { task *tp = MRT_SRC_TASK(src); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; mrt_src_list_t *srcs, *s, *ns; sockaddr_un *saddr = src->addr; upstream_t *up; up = mbr_locate_upstream(saddr); if (!up) { trace_tp (tp, TR_NORMAL, 0, ("pimdm_upstream_add[%s]: could not determine RPF for (%A, %A)\n", tp->task_name, src->addr, src->src_gaddr)); return; } if (src->src_upstream) { if (up->ifap == src->src_ifap && sockaddrcmp (up->nbr, src->src_nbr)) { /* we already have this upstream, just return. */ return; } pimdm_upstream_delete (src); } src->src_upstream = mrt_upstream_dup (up); up = src->src_upstream; IFA_ALLOC(src->src_upstream->ifap); trace_tp (tp, TR_ROUTE, 0, ("pimdm_upstream_add[%s]: upstream set to RPF %A for (%A, %A)\n", tp->task_name, src->src_upstream->nbr, src->addr, src->src_gaddr)); /* lay our claim to up->rt, when it changes will be know in pimdm_flash() */ rt_open(tp); rtbit_set (up->rt, tp->task_rtbit); rt_close(tp, 0, 0, 0); /* now alloc an mrt_src_list for the pim tsi data, we share this with pimsm */ rttsi_get (up->rt->rt_head, tp->task_rtbit, (byte *) &srcs); if (!srcs) { srcs = mrt_src_list_alloc(); srcs->forw = srcs->back = srcs; srcs->src = (source_t *) 0; rttsi_set (up->rt->rt_head, tp->task_rtbit, (byte *) &srcs); } /* * verify that we are not already on this list. the list will * sorted according to saddr */ MRT_SRC_LIST(s, srcs) { if (s->src == src) { /* this src is already in the list, log a warning and be on our way */ trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pimdm_upstream_add[%s]: source %A is already in tsi src list\n", tp->task_name, src->addr)); break; } else if (sockaddrcmp2 (s->src->addr, src->addr) > 0) { break; } } MRT_SRC_LIST_END(s, srcs); if (!s) s = srcs; ns = mrt_src_list_alloc(); ns->src = src; MRT_SRC_LIST_ENQ(ns, s->back); } /* PROTOCOL: * 1. Get the new and previous route entries * 2. If the route is new, look for mrt's the will benefit - if iif changes, * then update upstream and downtream ifaps and send grafts/prunes. * 3. If a route is deleted, see if it is subsumed by another route. If, * so then update mrt, otherwise delete the (S,G) entry. * */ void pimdm_flash (tp, change_list) task *tp; rt_list *change_list; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; rt_head *rth; source_t *src; mrt_src_list_t update, *srclist = 0, *next; trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash[%s]", tp->task_name)); update.forw = update.back = (mrt_src_list_t *) &update; update.src = (source_t *) 0; rt_open(tp); RT_LIST (rth, change_list, rt_head) { mrt_src_list_t *head = 0, *up_head = 0; rt_entry *new = rth->rth_rib_active[RIB_MULTICAST]; rt_entry *old = (rt_entry *) 0; /* 1. Get the new and previous route entries */ if (rth->rth_rib_last_active[RIB_MULTICAST] && rtbit_isset(rth->rth_rib_last_active[RIB_MULTICAST], tp->task_rtbit)) { /* we had an mrt entry off the last active route */ old = rth->rth_rib_last_active[RIB_MULTICAST]; rttsi_get(rth, tp->task_rtbit, (byte *) &head); assert (head); } if (!old && !new) { rt_close(tp, (gw_entry *) 0, 0, NULL); return; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash[%s]: route change for: \n", tp->task_name)); if (new) { trace_tp (tp, TR_ROUTE, 0, ("\tnew: %A/%d gateway %A ifap %s(%A) ", new->rt_dest, inet_prefix_mask(new->rt_dest_mask), RT_ROUTER(new), RT_IFAP(new)->ifa_name, IFA_UNIQUE_ADDR(RT_IFAP(new)))); } if (old) { trace_tp (tp, TR_ROUTE, 0, ("\told: %A/%d gateway %A ifap %s(%A)\n", old->rt_dest, inet_prefix_mask(old->rt_dest_mask), RT_ROUTER(old), RT_IFAP(old)->ifa_name, IFA_UNIQUE_ADDR(RT_IFAP(old)))); } if (!old) { /* we have a new route, but no old. look to see if new->rt_head's parent had src's associated with it that will use the new route. */ rt_head *parent = rt_table_locate_parent(rth); while (parent && !parent->rth_rib_active[RIB_MULTICAST]) parent = rt_table_locate_parent(parent); if (parent && parent->rth_rib_active[RIB_MULTICAST] && rtbit_isset(parent->rth_rib_active[RIB_MULTICAST], tp->task_rtbit)) { rttsi_get (rth, tp->task_rtbit, (byte *) &up_head); if (up_head) { /* the parent has a source list, now we must traverse it looking for src's that fall under the new route */ srclist = up_head->forw; while (srclist != up_head) { src = srclist->src; trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash: checking (%A, %A) for new route %A/%d", src->addr, src->src_gaddr, rth->rth_dest, inet_prefix_mask(rth->rth_dest_mask))); /* see if the current source is affected -- affected if: * - the source is covered by the new route address and * mask, AND * - the source does not have PIM-ASSERT state */ if ( !(PIMDM_SRC_ASSERT_TIMEOUT(src)) && sockaddrcmp_mask(rth->rth_dest, src->addr, rth->rth_dest_mask)) { mrt_src_list_t *next = srclist->forw; /* since the current src falls under the new route we must remove it from the parent tsi srclist and add it to the new routes tsi srclist */ REMQUE(srclist); head = mrt_src_list_alloc(); head->forw = head->back = head; /* check iif for a change */ if (RT_IFAP(new) != src->src_upstream->ifap) { if_addr *save_ifap = src->src_upstream->ifap; downstream_t *dp; int dsproto=DSPROTO_NONE; /* The iif has changed */ pimdm_downstream_delete (src, RT_IFAP(new)); MRT_SRC_LIST_ADD(src, &update); /* Possibly add the old iif to the downstream list! */ /* if I have nbrs */ if (pimdm_have_nbrs(save_ifap)) dsproto |= DSPROTO_MROUTE; #ifdef PROTO_IGMP if (igmp_find_group(sock2ip(src->src_gaddr), save_ifap)) dsproto |= DSPROTO_IGMP; #endif if (mstatic_find_downstream(src->src_gaddr, save_ifap)) dsproto |= DSPROTO_STATIC; /* Add the interface to the downstream list */ if (dsproto && !mstatic_find_boundary(src->src_gaddr,save_ifap)) { dp = pimdm_assert_downstream(src, save_ifap, dsproto); } pimdm_jp_send_one(tp, src, save_ifap, pim_all_routers, pimdm_anyone, pim_default_jp_holdtime, PRUNE); } else if (!sockaddrcmp_in (RT_ROUTER(new), src->src_upstream->nbr)) { MRT_SRC_LIST_ADD(src, &update); } trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash[%s]: new iif for (%A, %A), nbr %A, ifap %s(%A)\n", src->addr, src->src_gaddr, src->src_upstream->nbr, src->src_upstream->ifap->ifa_name, IFA_UNIQUE_ADDR(src->src_upstream->ifap))); rttsi_set(rth, tp->task_rtbit, (byte *) &head); rtbit_set (new, tp->task_rtbit); INSQUE(srclist, head->forw); srclist = next; } else { srclist = srclist->forw; } } /* if all mrt's got moved off parent clean it up a reset bits */ if (up_head == up_head->forw) { mrt_src_list_free (up_head); rtbit_reset(parent->rth_rib_active[RIB_MULTICAST], tp->task_rtbit); rttsi_reset(parent, tp->task_rtbit); } } } continue; } /* * we have deleted a route, See if we can move these * mrt's to a less specific */ if (!new) { rt_head *parent = rt_table_locate_parent(rth); while (parent && !parent->rth_rib_active[RIB_MULTICAST]) parent = rt_table_locate_parent(parent); /* look to see if a parent of the old will take over responsibility XXX: is this correct??? */ if (parent) { srclist = head->forw; while (srclist != head) { src = srclist->src; next = srclist->forw; REMQUE(srclist); /* * if parent subsumes this route (an we don't have assert * state), move mrt's to parent, also perform upstream * interface check */ if ( !(PIMDM_SRC_ASSERT_TIMEOUT(src)) && sockaddrcmp_mask (parent->rth_dest, src->addr, parent->rth_dest_mask)) { if (RT_IFAP (parent->rth_rib_active[RIB_MULTICAST]) != src->src_upstream->ifap) { if_addr *save_ifap = src->src_upstream->ifap; downstream_t *dp; int dsproto=DSPROTO_NONE; /* The iif has changed */ pimdm_downstream_delete (src, RT_IFAP(parent->rth_rib_active[RIB_MULTICAST])); MRT_SRC_LIST_ADD(src, &update); /* Possibly add the old iif to the downstream list! */ /* if I have nbrs */ if (pimdm_have_nbrs(save_ifap)) dsproto |= DSPROTO_MROUTE; #ifdef PROTO_IGMP if (igmp_find_group(sock2ip(src->src_gaddr), save_ifap)) dsproto |= DSPROTO_IGMP; #endif if (mstatic_find_downstream(src->src_gaddr, save_ifap)) dsproto |= DSPROTO_STATIC; /* Add the interface to the downstream list */ if (dsproto && !mstatic_find_boundary(src->src_gaddr,save_ifap)) { dp = pimdm_assert_downstream(src, save_ifap, dsproto); } pimdm_jp_send_one(tp, src, save_ifap, pim_all_routers, pimdm_anyone, pim_default_jp_holdtime, PRUNE); } else if (!(PIMDM_SRC_ASSERT_TIMEOUT(src)) && !sockaddrcmp (RT_ROUTER(parent->rth_rib_active[RIB_MULTICAST]), RT_ROUTER(parent->rth_rib_active[RIB_MULTICAST]))) { MRT_SRC_LIST_ADD(src, &update); } trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash: moving (%A, %A) to new route %A/%d", src->addr, src->src_gaddr, parent->rth_dest, inet_prefix_mask(parent->rth_dest_mask))); if (rtbit_isset(parent->rth_rib_active[RIB_MULTICAST], tp->task_rtbit)) { rttsi_get(rth, tp->task_rtbit, (byte *) &up_head); } else { up_head = mrt_src_list_alloc(); up_head->forw = up_head; up_head->back = up_head; } INSQUE(srclist, up_head->forw); } else { /* no route back to source, must delete mrt */ BIT_RESET(src->src_proto, IPMULTI_BIT(IPMULTI_PROTO_PIM)); if (!src->src_proto) { /* Sender is member heuristic: if last (S,G) for this * group, then unregister interest for the group */ if(pimdm_default_sender_is_member && src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_unregister_interest(src->src_gaddr, inet_mask_host, tp); (void) mbr_delete_cache(src->src_gaddr, src->addr, src->mask); pimdm_delete_all_jp_entries(src); pimdm_free_sg_rl_list(src); pimdm_graft_free_state(tp, src); mrt_downstream_delete_list(PIMDM_SRC_PRUNE(src)); task_block_free(pimdm_source_block_index, src->src_data[0]); mrt_source_delete(src); } mrt_src_list_free(srclist); } srclist = next; } } else { /* XXX: must we check for RIB_MULTICAST */ /* no route back to source delete mrt's */ srclist = head->forw; while (srclist != head) { src = srclist->src; next = srclist->forw; REMQUE(srclist); BIT_RESET(src->src_proto, IPMULTI_BIT(IPMULTI_PROTO_PIM)); if (!src->src_proto) { /* Sender is member heuristic: if last (S,G) for this * group, then unregister interest for the group */ if(pimdm_default_sender_is_member && src->src_group->grp_table->mrt_routes == 1 && mbr_get_iftask(src->src_upstream->ifap) == tp) mbr_grp_unregister_interest(src->src_gaddr, inet_mask_host, tp); (void) mbr_delete_cache(src->src_gaddr, src->addr, src->mask); pimdm_delete_all_jp_entries(src); pimdm_free_sg_rl_list(src); pimdm_graft_free_state(tp, src); mrt_downstream_delete_list(PIMDM_SRC_PRUNE(src)); task_block_free(pimdm_source_block_index, src->src_data[0]); mrt_source_delete(src); } mrt_src_list_free(srclist); srclist = next; } } rtbit_reset(old, tp->task_rtbit); rttsi_reset(rth, tp->task_rtbit); mrt_src_list_free(head); continue; } /* we have changed a route, perform upstream interface check if * we don't have assert state. */ if (old && new) { srclist = head->forw; while (srclist != head) { src = srclist->src; next = srclist->forw; /* don't we need to walk the source list??? */ if (old != new) rtbit_reset(old, tp->task_rtbit); if (!(PIMDM_SRC_ASSERT_TIMEOUT(src)) && RT_IFAP(old) != RT_IFAP(new)) { pimdm_downstream_delete (src, RT_IFAP(new)); MRT_SRC_LIST_ADD(src, &update); } else if (!(PIMDM_SRC_ASSERT_TIMEOUT(src)) && RT_ROUTER(old) != RT_ROUTER(old)) { MRT_SRC_LIST_ADD(src, &update); } srclist = next; } } } RT_LIST_END(rth, change_list, rt_head); rt_close(tp, (gw_entry *) 0, 0, NULL); /* this is done here rather than on spot because pimdm_upstream_add() opens the rt_table. we'll core dump if we run pimdm_upstream_add() in the RT_LIST() loop. */ srclist = update.forw; while (srclist != &update) { source_t *src = srclist->src; downstream_t *dp; next = srclist->forw; pimdm_upstream_add (src); mbr_reset_nbr(src, src->src_upstream->nbr); assert (src->src_upstream); pimdm_graft_send(tp, src); mrt_src_list_free(srclist); srclist = next; } trace_tp (tp, TR_ROUTE, 0, ("pimdm_flash[%s] done \n", tp->task_name)); } #endif /*PROTO_PIMDM*/