if (up->ref_count >= 1)
return up;
+ if (PIM_DEBUG_TRACE)
+ zlog_debug(
+ "pim_upstream free vrf:%s %s flags 0x%x",
+ pim->vrf->name, up->sg_str, up->flags);
+
THREAD_OFF(up->t_ka_timer);
THREAD_OFF(up->t_rs_timer);
THREAD_OFF(up->t_msdp_reg_timer);
up->rpf.rpf_addr.u.prefix4);
if (nbr)
- pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+ pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
pim_jp_agg_upstream_verification(up, false);
}
}
if (nbr)
- pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1);
+ pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr);
else {
THREAD_OFF(up->t_join_timer);
thread_add_timer(router->master, on_join_timer, up,
* is actually active; if it is not kat setup will trigger
* source
* registration whenever the flow becomes active. */
- if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer)
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+ !pim_upstream_is_kat_running(up))
continue;
if (pim_is_grp_ssm(pim, up->sg.grp)) {
}
}
+/* some events like RP change require re-evaluation of SGrpt across
+ * all groups
+ */
+void pim_upstream_reeval_use_rpt(struct pim_instance *pim)
+{
+ struct pim_upstream *up;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) {
+ if (up->sg.src.s_addr == INADDR_ANY)
+ continue;
+
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ }
+}
+
void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
enum pim_upstream_state new_state)
{
join_timer_start(up);
}
}
+ if (old_state != new_state)
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
} else {
+ bool old_use_rpt;
+ bool new_use_rpt;
+ bool send_xg_jp = false;
forward_off(up);
if (old_state == PIM_UPSTREAM_JOINED)
pim_msdp_up_join_state_changed(pim, up);
+ if (old_state != new_state) {
+ old_use_rpt =
+ !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ new_use_rpt =
+ !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ if (new_use_rpt &&
+ (new_use_rpt != old_use_rpt) &&
+ up->parent)
+ /* we have decided to switch from the SPT back
+ * to the RPT which means we need to cancel
+ * any previously sent SGrpt prunes immediately
+ */
+ send_xg_jp = true;
+ }
+
/* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
RP.
If I am RP for G then send S,G prune to its IIF. */
- if (pim_upstream_is_sg_rpt(up) && up->parent
- && !I_am_RP(pim, up->sg.grp)) {
+ if (pim_upstream_is_sg_rpt(up) && up->parent &&
+ !I_am_RP(pim, up->sg.grp))
+ send_xg_jp = true;
+ else
+ pim_jp_agg_single_upstream_send(&up->rpf, up,
+ 0 /* prune */);
+
+ if (send_xg_jp) {
if (PIM_DEBUG_PIM_TRACE_DETAIL)
zlog_debug(
- "%s: *,G IIF %s S,G IIF %s ",
- __PRETTY_FUNCTION__,
+ "re-join RPT; *,G IIF %s S,G IIF %s ",
up->parent->rpf.source_nexthop.interface ?
up->parent->rpf.source_nexthop.interface->name
: "Unknown",
pim_jp_agg_single_upstream_send(&up->parent->rpf,
up->parent,
1 /* (W,G) Join */);
- } else
- pim_jp_agg_single_upstream_send(&up->rpf, up,
- 0 /* prune */);
+ }
join_timer_stop(up);
}
-
- if (old_state != new_state)
- pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
int pim_upstream_compare(void *arg1, void *arg2)
pim_upstream_fill_static_iif(up, incoming);
pim_ifp = up->rpf.source_nexthop.interface->info;
assert(pim_ifp);
+ pim_upstream_update_use_rpt(up,
+ false /*update_mroute*/);
pim_upstream_mroute_iif_update(up->channel_oil, __func__);
if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags))
pim_upstream_keep_alive_timer_start(
up, pim->keep_alive_time);
} else if (up->upstream_addr.s_addr != INADDR_ANY) {
- rpf_result = pim_rpf_update(pim, up, NULL);
+ pim_upstream_update_use_rpt(up,
+ false /*update_mroute*/);
+ rpf_result = pim_rpf_update(pim, up, NULL, __func__);
if (rpf_result == PIM_RPF_FAILURE) {
if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
}
if (up->rpf.source_nexthop.interface) {
- pim_ifp = up->rpf.source_nexthop.interface->info;
- if (pim_ifp)
- pim_upstream_mroute_iif_update(up->channel_oil,
- __func__);
+ pim_upstream_mroute_iif_update(up->channel_oil,
+ __func__);
}
- pim_upstream_update_use_rpt(up,
- false /*update_mroute*/);
}
listnode_add_sort(pim->upstream_list, up);
pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
+ /* re-eval joinDesired; clearing peer-msdp-sa flag can
+ * cause JD to change
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) &&
+ PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) {
+ PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags);
+ pim_upstream_update_join_desired(up->pim, up);
+ }
+
up->flags |= flags;
++up->ref_count;
if (PIM_DEBUG_PIM_TRACE)
return up;
}
+/*
+ * Passed in up must be the upstream for ch. starch is NULL if no
+ * information
+ * This function is copied over from
+ * pim_upstream_evaluate_join_desired_interface but limited to
+ * parent (*,G)'s includes/joins.
+ */
+int pim_upstream_eval_inherit_if(struct pim_upstream *up,
+ struct pim_ifchannel *ch,
+ struct pim_ifchannel *starch)
+{
+ /* if there is an explicit prune for this interface we cannot
+ * add it to the OIL
+ */
+ if (ch) {
+ if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+ return 0;
+ }
+
+ /* Check if the OIF can be inherited fron the (*,G) entry
+ */
+ if (starch) {
+ if (!pim_macro_ch_lost_assert(starch)
+ && pim_macro_chisin_joins_or_include(starch))
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Passed in up must be the upstream for ch. starch is NULL if no
* information
* joins (*,G)
*/
if (starch) {
+ /* XXX: check on this with donald
+ * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
+ * upstream flags?
+ */
+#if 0
if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags))
return 0;
+#endif
if (!pim_macro_ch_lost_assert(starch)
&& pim_macro_chisin_joins_or_include(starch))
return 0;
}
-/*
- Evaluate JoinDesired(S,G):
-
- JoinDesired(S,G) is true if there is a downstream (S,G) interface I
- in the set:
-
- inherited_olist(S,G) =
- joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
-
- JoinDesired(S,G) may be affected by changes in the following:
-
- pim_ifp->primary_address
- pim_ifp->pim_dr_addr
- ch->ifassert_winner_metric
- ch->ifassert_winner
- ch->local_ifmembership
- ch->ifjoin_state
- ch->upstream->rpf.source_nexthop.mrib_metric_preference
- ch->upstream->rpf.source_nexthop.mrib_route_metric
- ch->upstream->rpf.source_nexthop.interface
-
- See also pim_upstream_update_join_desired() below.
+/* Returns true if immediate OIL is empty and is used to evaluate
+ * JoinDesired. See pim_upstream_evaluate_join_desired.
*/
-int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim,
struct pim_upstream *up)
{
struct interface *ifp;
- struct pim_ifchannel *ch, *starch;
- struct pim_upstream *starup = up->parent;
- int ret = 0;
+ struct pim_ifchannel *ch;
FOR_ALL_INTERFACES (pim->vrf, ifp) {
if (!ifp->info)
continue;
ch = pim_ifchannel_find(ifp, &up->sg);
-
- if (starup)
- starch = pim_ifchannel_find(ifp, &starup->sg);
- else
- starch = NULL;
-
- if (!ch && !starch)
+ if (!ch)
continue;
- ret += pim_upstream_evaluate_join_desired_interface(up, ch,
- starch);
+ /* If we have even one immediate OIF we can return with
+ * not-empty
+ */
+ if (pim_upstream_evaluate_join_desired_interface(up, ch,
+ NULL /* starch */))
+ return false;
} /* scan iface channel list */
- return ret; /* false */
+ /* immediate_oil is empty */
+ return true;
+}
+
+
+static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up)
+{
+ return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags);
+}
+
+/*
+ * bool JoinDesired(*,G) {
+ * if (immediate_olist(*,G) != NULL)
+ * return TRUE
+ * else
+ * return FALSE
+ * }
+ *
+ * bool JoinDesired(S,G) {
+ * return( immediate_olist(S,G) != NULL
+ * OR ( KeepaliveTimer(S,G) is running
+ * AND inherited_olist(S,G) != NULL ) )
+ * }
+ */
+int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+ struct pim_upstream *up)
+{
+ bool empty_imm_oil;
+ bool empty_inh_oil;
+
+ empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up);
+
+ /* (*,G) */
+ if (up->sg.src.s_addr == INADDR_ANY)
+ return !empty_imm_oil;
+
+ /* (S,G) */
+ if (!empty_imm_oil)
+ return true;
+ empty_inh_oil = pim_upstream_empty_inherited_olist(up);
+ if (!empty_inh_oil &&
+ (pim_upstream_is_kat_running(up) ||
+ pim_upstream_is_msdp_peer_sa(up)))
+ return true;
+
+ return false;
}
/*
/* source is no longer active - pull the SA from MSDP's cache */
pim_msdp_sa_local_del(pim, &up->sg);
+ /* JoinDesired can change when KAT is started or stopped */
+ pim_upstream_update_join_desired(pim, up);
+
/* if entry was created because of activity we need to deref it */
if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
pim_upstream_fhr_kat_expiry(pim, up);
/* any time keepalive is started against a SG we will have to
* re-evaluate our active source database */
pim_msdp_sa_local_update(up);
+ /* JoinDesired can change when KAT is started or stopped */
+ pim_upstream_update_join_desired(up->pim, up);
}
/* MSDP on RP needs to know if a source is registerable to this RP */
* SwitchToSptDesired(S,G) return true once a single packet has been
* received for the source and group.
*/
-int pim_upstream_switch_to_spt_desired(struct pim_instance *pim,
+int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
struct prefix_sg *sg)
{
if (I_am_RP(pim, sg->grp))
* switch on a stick so turn on forwarding to just accept the
* incoming packets so we don't bother the other stuff!
*/
- if (output_intf)
- pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED);
- else
+ pim_upstream_update_join_desired(pim, up);
+
+ if (!output_intf)
forward_on(up);
return output_intf;
struct listnode *up_node;
struct listnode *up_nextnode;
struct pim_upstream *up;
+ struct pim_rpf old;
+ enum pim_rpf_result rpf_result;
/*
* Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
zlog_debug(
"%s: Upstream %s without a path to send join, checking",
__PRETTY_FUNCTION__, up->sg_str);
- pim_rpf_update(pim, up, NULL);
+ old.source_nexthop.interface =
+ up->rpf.source_nexthop.interface;
+ rpf_result = pim_rpf_update(pim, up, &old, __func__);
+ if (rpf_result == PIM_RPF_CHANGED ||
+ (rpf_result == PIM_RPF_FAILURE &&
+ old.source_nexthop.interface))
+ pim_zebra_upstream_rpf_changed(pim, up, &old);
+ /* update kernel multicast forwarding cache (MFC) */
+ pim_upstream_mroute_iif_update(up->channel_oil,
+ __func__);
}
}
+ pim_zebra_update_all_interfaces(pim);
}
unsigned int pim_upstream_hash_key(const void *arg)
*/
static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
{
- struct pim_instance *pim = up->channel_oil->pim;
+ struct channel_oil *c_oil = up->channel_oil;
+ struct interface *ifp = up->rpf.source_nexthop.interface;
+ struct pim_interface *pim_ifp;
+
+ /* "iif == RPF_interface(S)" check is not easy to do as the info
+ * we get from the kernel/ASIC is really a "lookup/key hit".
+ * So we will do an approximate check here to avoid starting KAT
+ * because of (S,G,rpt) forwarding on a non-LHR.
+ */
+ if (!ifp)
+ return false;
+
+ pim_ifp = ifp->info;
+ if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent)
+ return false;
- /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
- * so we will skip that here */
- if (up->rpf.source_nexthop.interface &&
- pim_if_connected_to_source(up->rpf.source_nexthop.interface,
+ if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
up->sg.src)) {
return true;
}
if ((up->join_state == PIM_UPSTREAM_JOINED)
- && !pim_upstream_empty_inherited_olist(up)) {
- /* XXX: I have added this RP check just for 3.2 and it's a
- * digression from
- * what rfc-4601 says. Till now we were only running KAT on FHR
- * and RP and
- * there is some angst around making the change to run it all
- * routers that
- * maintain the (S, G) state. This is tracked via CM-13601 and
- * MUST be
- * removed to handle spt turn-arounds correctly in a 3-tier clos
- */
- if (I_am_RP(pim, up->sg.grp))
- return true;
+ && !pim_upstream_empty_inherited_olist(up)) {
+ return true;
}
return false;