child = pim_upstream_del(pim, child,
__PRETTY_FUNCTION__);
}
- if (child)
+ if (child) {
child->parent = NULL;
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(
+ child->channel_oil,
+ __func__);
+ }
}
list_delete(&up->sources);
}
&& (child != up)) {
child->parent = up;
listnode_add_sort(up->sources, child);
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(
+ child->channel_oil,
+ __func__);
}
}
}
static void upstream_channel_oil_detach(struct pim_upstream *up)
{
- if (up->channel_oil) {
+ struct channel_oil *channel_oil = up->channel_oil;
+
+ if (channel_oil) {
/* Detaching from channel_oil, channel_oil may exist post del,
but upstream would not keep reference of it
*/
- up->channel_oil->up = NULL;
- pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__);
+ channel_oil->up = NULL;
up->channel_oil = NULL;
+
+ /* attempt to delete channel_oil; if channel_oil is being held
+ * because of other references cleanup info such as "Mute"
+ * inferred from the parent upstream
+ */
+ pim_channel_oil_upstream_deref(channel_oil);
}
+
}
struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
bool notify_msdp = false;
struct prefix nht_p;
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
__PRETTY_FUNCTION__, name, up->sg_str, pim->vrf->name,
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);
nht_p.family = AF_INET;
nht_p.prefixlen = IPV4_MAX_BITLEN;
nht_p.u.prefix4 = up->upstream_addr;
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char buf[PREFIX2STR_BUFFER];
prefix2str(&nht_p, buf, sizeof(buf));
zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
void pim_upstream_send_join(struct pim_upstream *up)
{
if (!up->rpf.source_nexthop.interface) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
return;
}
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char rpf_str[PREFIX_STRLEN];
pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str,
sizeof(rpf_str));
up = THREAD_ARG(t);
if (!up->rpf.source_nexthop.interface) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
return 0;
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,
long join_timer_remain_msec;
if (!up->rpf.source_nexthop.interface) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
return;
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char rpf_str[INET_ADDRSTRLEN];
pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
zlog_debug(
}
if (join_timer_remain_msec < t_joinsuppress_msec) {
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s %s: suppressing Join(S,G)=%s for %ld msec",
__FILE__, __PRETTY_FUNCTION__, up->sg_str,
int t_override_msec;
if (!up->rpf.source_nexthop.interface) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
return;
t_override_msec =
pim_if_t_override_msec(up->rpf.source_nexthop.interface);
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char rpf_str[INET_ADDRSTRLEN];
pim_inet4_dump("<rpf?>", up->rpf.rpf_addr.u.prefix4, rpf_str,
sizeof(rpf_str));
}
if (join_timer_remain_msec > t_override_msec) {
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: decreasing (S,G)=%s join timer to t_override=%d msec",
debug_label, up->sg_str, t_override_msec);
if (up->rpf.source_nexthop.interface)
pim_ifp = up->rpf.source_nexthop.interface->info;
else {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
}
* 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)) {
/* remove regiface from the OIL if it is there*/
pim_channel_del_oif(up->channel_oil,
pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
up->reg_state = PIM_REG_NOINFO;
}
} else {
up->sg_str);
pim_channel_add_oif(up->channel_oil,
pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
up->reg_state = PIM_REG_JOIN;
}
}
}
}
+/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
+ * forward a S -
+ * 1. along the SPT if SPTbit is set
+ * 2. and along the RPT if SPTbit is not set
+ * If forwarding is hw accelerated i.e. control and dataplane components
+ * are separate you may not be able to reliably set SPT bit on intermediate
+ * routers while still fowarding on the (S,G,rpt).
+ *
+ * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
+ * criteria to decide between using the RPT vs. SPT for forwarding.
+ */
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+ bool update_mroute)
+{
+ bool old_use_rpt;
+ bool new_use_rpt;
+
+ if (up->sg.src.s_addr == INADDR_ANY)
+ return;
+
+ old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+
+ /* We will use the SPT (IIF=RPF_interface(S) if -
+ * 1. We have decided to join the SPT
+ * 2. We are FHR
+ * 3. Source is directly connected
+ * 4. We are RP (parent's IIF is lo or vrf-device)
+ * In all other cases the source will stay along the RPT and
+ * IIF=RPF_interface(RP).
+ */
+ if (up->join_state == PIM_UPSTREAM_JOINED ||
+ PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+ pim_if_connected_to_source(
+ up->rpf.source_nexthop.interface,
+ up->sg.src) ||
+ /* XXX - need to switch this to a more efficient
+ * lookup API
+ */
+ I_am_RP(up->pim, up->sg.grp))
+ /* use SPT */
+ PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags);
+ else
+ /* use RPT */
+ PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags);
+
+ new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ if (old_use_rpt != new_use_rpt) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug("%s switched from %s to %s",
+ up->sg_str,
+ old_use_rpt?"RPT":"SPT",
+ new_use_rpt?"RPT":"SPT");
+ if (update_mroute)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
+ }
+}
+
+/* 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);
}
}
up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+ up->pim = pim;
up->sg = *sg;
pim_str_sg_set(sg, up->sg_str);
if (ch)
*/
if (!pim_rp_set_upstream_addr(pim, &up->upstream_addr, sg->src,
sg->grp)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: Received a (*,G) with no RP configured",
__PRETTY_FUNCTION__);
}
up->reg_state = PIM_REG_NOINFO;
up->state_transition = pim_time_monotonic_sec();
up->channel_oil =
- pim_channel_oil_add(pim, &up->sg, MAXVIFS, __PRETTY_FUNCTION__);
+ pim_channel_oil_add(pim, &up->sg, __PRETTY_FUNCTION__);
up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
up->rpf.source_nexthop.interface = NULL;
pim_upstream_fill_static_iif(up, incoming);
pim_ifp = up->rpf.source_nexthop.interface->info;
assert(pim_ifp);
- pim_channel_oil_change_iif(pim, up->channel_oil,
- pim_ifp->mroute_vif_index,
- __PRETTY_FUNCTION__);
+ 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_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: Attempting to create upstream(%s), Unable to RPF for source",
__PRETTY_FUNCTION__, up->sg_str);
}
if (up->rpf.source_nexthop.interface) {
- pim_ifp = up->rpf.source_nexthop.interface->info;
- if (pim_ifp)
- pim_channel_oil_change_iif(
- pim, up->channel_oil,
- pim_ifp->mroute_vif_index,
- __PRETTY_FUNCTION__);
+ pim_upstream_mroute_iif_update(up->channel_oil,
+ __func__);
}
}
listnode_add_sort(pim->upstream_list, up);
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: Created Upstream %s upstream_addr %s ref count %d increment",
__PRETTY_FUNCTION__, up->sg_str,
}
struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
- struct interface *incoming,
- int flags, const char *name)
+ struct interface *incoming,
+ int flags, const char *name)
{
- struct pim_upstream *up;
- struct pim_interface *pim_ifp;
-
- pim_ifp = incoming->info;
-
- up = pim_upstream_find(pim_ifp->pim, sg);
-
- if (up) {
- if (!(up->flags & flags)) {
- up->flags |= flags;
- up->ref_count++;
- if (PIM_DEBUG_TRACE)
- zlog_debug(
- "%s(%s): upstream %s ref count %d increment",
- __PRETTY_FUNCTION__, name, up->sg_str,
- up->ref_count);
- }
- } else
- up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
- NULL);
+ struct pim_interface *pim_ifp = incoming->info;
- return up;
+ return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
+ NULL));
}
void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
{
+ /* when we go from non-FHR to FHR we need to re-eval traffic
+ * forwarding path
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) &&
+ PIM_UPSTREAM_FLAG_TEST_FHR(flags)) {
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ 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_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s(%s): upstream %s ref count %d increment",
__PRETTY_FUNCTION__, name, up->sg_str,
up->ref_count);
up = pim_upstream_new(pim, sg, incoming, flags, ch);
}
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
if (up) {
char buf[PREFIX2STR_BUFFER];
prefix2str(&up->rpf.rpf_addr, buf, sizeof(buf));
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;
}
/*
*/
for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char neigh_str[INET_ADDRSTRLEN];
char rpf_addr_str[PREFIX_STRLEN];
pim_inet4_dump("<neigh?>", neigh_addr, neigh_str,
if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
return;
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("kat expired on %s; clear fhr reg state",
up->sg_str);
THREAD_OFF(up->t_rs_timer);
/* remove regiface from the OIL if it is there*/
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
/* clear the register state */
up->reg_state = PIM_REG_NOINFO;
PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
{
if (pim_upstream_could_register(up)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"kat started on %s; set fhr reg state to joined",
up->sg_str);
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
if (up->reg_state == PIM_REG_NOINFO)
pim_register_join(up);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
}
/* 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);
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"kat expired on %s[%s]; remove stream reference",
up->sg_str, pim->vrf->name);
void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time)
{
if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("kat start on %s with no stream reference",
up->sg_str);
}
/* 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))
// iif == RPF_interfvace(S)
if (up->rpf.source_nexthop.interface != incoming) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: Incoming Interface: %s is different than RPF_interface(S) %s",
__PRETTY_FUNCTION__, incoming->name,
// AND JoinDesired(S,G) == true
if (!pim_upstream_evaluate_join_desired(up->channel_oil->pim, up)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: %s Join is not Desired",
__PRETTY_FUNCTION__, up->sg_str);
return;
// DirectlyConnected(S) == true
if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
up->sg.src)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: %s is directly connected to the source",
__PRETTY_FUNCTION__, up->sg_str);
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
.interface != starup->rpf.source_nexthop.interface) {
struct pim_upstream *starup = up->parent;
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: %s RPF_interface(S) != RPF_interface(RP(G))",
__PRETTY_FUNCTION__, up->sg_str);
// OR inherited_olist(S,G,rpt) == NULL
if (pim_upstream_is_sg_rpt(up)
&& pim_upstream_empty_inherited_olist(up)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
__PRETTY_FUNCTION__, up->sg_str);
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
// OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
// ( RPF'(S,G) != NULL ) )
if (up->parent && pim_rpf_is_same(&up->rpf, &up->parent->rpf)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
__PRETTY_FUNCTION__, up->sg_str);
up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
up = THREAD_ARG(t);
pim = up->channel_oil->pim;
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
char state_str[PIM_REG_STATE_STR_LEN];
zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
case PIM_REG_JOIN_PENDING:
up->reg_state = PIM_REG_JOIN;
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
break;
case PIM_REG_JOIN:
break;
case PIM_REG_PRUNE:
if (!up->rpf.source_nexthop.interface) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
return 0;
pim_ifp = up->rpf.source_nexthop.interface->info;
if (!pim_ifp) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: Interface: %s is not configured for pim",
__PRETTY_FUNCTION__,
if (((up->channel_oil->cc.lastused / 100)
> pim->keep_alive_time)
&& (I_am_RP(pim_ifp->pim, up->sg.grp))) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
__PRETTY_FUNCTION__);
} else
time = PIM_REGISTER_PROBE_PERIOD;
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: (S,G)=%s Starting upstream register stop timer %d",
__PRETTY_FUNCTION__, up->sg_str, time);
int output_intf = 0;
if (!up->rpf.source_nexthop.interface)
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: up %s RPF is not present",
__PRETTY_FUNCTION__, up->sg_str);
if (!ch)
flag = PIM_OIF_FLAG_PROTO_STAR;
- pim_channel_add_oif(up->channel_oil, ifp, flag);
+ pim_channel_add_oif(up->channel_oil, ifp, flag,
+ __func__);
output_intf++;
}
}
* 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
*/
for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
if (up->upstream_addr.s_addr == INADDR_ANY) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: RP not configured for Upstream %s",
__PRETTY_FUNCTION__, up->sg_str);
}
if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
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;
- /* "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,
+ pim_ifp = ifp->info;
+ if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent)
+ return false;
+
+ 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;
// No packet can have arrived here if this is the case
if (!up->channel_oil->installed) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug("%s: %s%s is not installed in mroute",
__PRETTY_FUNCTION__, up->sg_str,
pim->vrf->name);
* to get us up and working for the moment
*/
if (up->channel_oil->oil_inherited_rescan) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"%s: Handling unscanned inherited_olist for %s[%s]",
__PRETTY_FUNCTION__, up->sg_str,
// Have we seen packets?
if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
&& (up->channel_oil->cc.lastused / 100 > 30)) {
- if (PIM_DEBUG_TRACE) {
+ if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
/* Add a source reference to the stream if
* one doesn't already exist */
if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
- if (PIM_DEBUG_TRACE)
+ if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
"source reference created on kat restart %s[%s]",
up->sg_str, pim->vrf->name);
continue;
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
}
}
if (!nlist) {
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
continue;
}
g.u.prefix4 = up->sg.grp;
apply_new = prefix_list_apply(np, &g);
if (apply_new == PREFIX_DENY)
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
else
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
}
}