up = pim_upstream_find_or_add(
&sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__);
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
return 0;
}
pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
up->channel_oil->cc.pktcnt++;
- PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
// resolve mfcc_parent prior to mroute_add in channel_add_oif
if (up->rpf.source_nexthop.interface &&
up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
- int vif_index = 0;
- vif_index = pim_if_find_vifindex_by_ifindex(
- pim_ifp->pim,
- up->rpf.source_nexthop.interface->ifindex);
- pim_channel_oil_change_iif(pim_ifp->pim, up->channel_oil,
- vif_index, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
}
pim_register_join(up);
pim_upstream_keep_alive_timer_start(
up, pim_ifp->pim->keep_alive_time);
pim_upstream_inherited_olist(pim_ifp->pim, up);
- pim_upstream_switch(pim_ifp->pim, up,
- PIM_UPSTREAM_JOINED);
+ pim_upstream_update_join_desired(pim_ifp->pim, up);
if (PIM_DEBUG_MROUTE)
zlog_debug("%s: Creating %s upstream on LHR",
pim_str_sg_dump(&sg));
return 0;
}
+
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug(
+ "%s register forward skipped, not FHR",
+ up->sg_str);
+ return 0;
+ }
+
pim_register_send((uint8_t *)buf + sizeof(struct ip),
ntohs(ip_hdr->ip_len) - sizeof(struct ip),
pim_ifp->primary_address, rpg, 0, up);
pim_upstream_inherited_olist(pim_ifp->pim, up);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil,
+ pim_upstream_mroute_add(up->channel_oil,
__PRETTY_FUNCTION__);
} else {
if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
up->channel_oil->cc.pktcnt++;
pim_register_join(up);
pim_upstream_inherited_olist(pim_ifp->pim, up);
+ if (!up->channel_oil->installed)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
// Send the packet to the RP
pim_mroute_msg_wholepkt(fd, ifp, buf);
PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__, NULL);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
}
return 0;
return 0;
}
-int pim_mroute_add(struct channel_oil *c_oil, const char *name)
+/*
+ * Prevent creating MFC entry with OIF=IIF.
+ *
+ * This is a protection against implementation mistakes.
+ *
+ * PIM protocol implicitely ensures loopfree multicast topology.
+ *
+ * IGMP must be protected against adding looped MFC entries created
+ * by both source and receiver attached to the same interface. See
+ * TODO T22.
+ * We shall allow igmp to create upstream when it is DR for the intf.
+ * Assume RP reachable via non DR.
+ */
+bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
+ int oif_index)
+{
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+ struct interface *ifp_out;
+ struct pim_interface *pim_ifp;
+
+ if (c_oil->up &&
+ PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
+ return true;
+
+ ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
+ if (!ifp_out)
+ return false;
+ pim_ifp = ifp_out->info;
+ if (!pim_ifp)
+ return false;
+ if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) &&
+ PIM_I_am_DR(pim_ifp))
+ return true;
+
+ return false;
+#else
+ return true;
+#endif
+}
+
+static inline void pim_mroute_copy(struct mfcctl *oil,
+ struct channel_oil *c_oil)
+{
+ int i;
+
+ oil->mfcc_origin = c_oil->oil.mfcc_origin;
+ oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp;
+ oil->mfcc_parent = c_oil->oil.mfcc_parent;
+
+ for (i = 0; i < MAXVIFS; ++i) {
+ if ((oil->mfcc_parent == i) &&
+ !pim_mroute_allow_iif_in_oil(c_oil, i)) {
+ oil->mfcc_ttls[i] = 0;
+ continue;
+ }
+
+ if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE)
+ oil->mfcc_ttls[i] = 0;
+ else
+ oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i];
+ }
+}
+
+/* This function must not be called directly 0
+ * use pim_upstream_mroute_add or pim_static_mroute_add instead
+ */
+static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;
+ struct mfcctl tmp_oil;
int err;
- int orig = 0;
- int orig_iif_vif = 0;
- struct pim_interface *pim_reg_ifp = NULL;
- int orig_pimreg_ttl = 0;
- bool pimreg_ttl_reset = false;
- struct pim_interface *vxlan_ifp = NULL;
- int orig_term_ttl = 0;
- bool orig_term_ttl_reset = false;
pim->mroute_add_last = pim_time_monotonic_sec();
++pim->mroute_add_events;
- /* Do not install route if incoming interface is undefined. */
- if (c_oil->oil.mfcc_parent >= MAXVIFS) {
- if (PIM_DEBUG_MROUTE) {
- char buf[1000];
- zlog_debug(
- "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
- __PRETTY_FUNCTION__, name,
- pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
- }
- return -2;
- }
+ /* Copy the oil to a temporary structure to fixup (without need to
+ * later restore) before sending the mroute add to the dataplane
+ */
+ pim_mroute_copy(&tmp_oil, c_oil);
/* The linux kernel *expects* the incoming
* vif to be part of the outgoing list
* in the case of a (*,G).
*/
if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
- orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
- c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
- }
-
- if (c_oil->up) {
- /* suppress pimreg in the OIL if the mroute is not supposed to
- * trigger register encapsulated data
- */
- if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) {
- pim_reg_ifp = pim->regiface->info;
- orig_pimreg_ttl =
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index];
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0;
- /* remember to flip it back after MFC programming */
- pimreg_ttl_reset = true;
- }
-
- vxlan_ifp = pim_vxlan_get_term_ifp(pim);
- /* 1. vxlan termination device must never be added to the
- * origination mroute (and that can actually happen because
- * of XG inheritance from the termination mroute) otherwise
- * traffic will end up looping.
- * 2. vxlan termination device should be removed from the non-DF
- * to prevent duplicates to the overlay rxer
- */
- if (vxlan_ifp &&
- (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
- PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) {
- orig_term_ttl_reset = true;
- orig_term_ttl =
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index];
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0;
- }
+ tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
}
/*
*/
if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
&& c_oil->oil.mfcc_parent != 0) {
- orig_iif_vif = c_oil->oil.mfcc_parent;
- c_oil->oil.mfcc_parent = 0;
+ tmp_oil.mfcc_parent = 0;
}
err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
- &c_oil->oil, sizeof(c_oil->oil));
+ &tmp_oil, sizeof(tmp_oil));
if (!err && !c_oil->installed
&& c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
- && orig_iif_vif != 0) {
- c_oil->oil.mfcc_parent = orig_iif_vif;
+ && c_oil->oil.mfcc_parent != 0) {
+ tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent;
err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
- &c_oil->oil, sizeof(c_oil->oil));
- }
-
- if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
- c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
-
- if (pimreg_ttl_reset) {
- assert(pim_reg_ifp);
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] =
- orig_pimreg_ttl;
+ &tmp_oil, sizeof(tmp_oil));
}
- if (orig_term_ttl_reset)
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] =
- orig_term_ttl;
-
if (err) {
zlog_warn(
"%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
return 0;
}
+static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
+ const char *name)
+{
+ vifi_t iif = MAXVIFS;
+ struct interface *ifp = NULL;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up = c_oil->up;
+
+ if (up) {
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
+ if (up->parent)
+ ifp = up->parent->rpf.source_nexthop.interface;
+ } else {
+ ifp = up->rpf.source_nexthop.interface;
+ }
+ if (ifp) {
+ pim_ifp = (struct pim_interface *)ifp->info;
+ if (pim_ifp)
+ iif = pim_ifp->mroute_vif_index;
+ }
+ }
+ return iif;
+}
+
+static int pim_upstream_mroute_update(struct channel_oil *c_oil,
+ const char *name)
+{
+ char buf[1000];
+
+ if (c_oil->oil.mfcc_parent >= MAXVIFS) {
+ /* the c_oil cannot be installed as a mroute yet */
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug(
+ "%s(%s) %s mroute not ready to be installed; %s",
+ __func__, name,
+ pim_channel_oil_dump(c_oil, buf,
+ sizeof(buf)),
+ c_oil->installed ?
+ "uninstall" : "skip");
+ /* if already installed flush it out as we are going to stop
+ * updates to it leaving it in a stale state
+ */
+ if (c_oil->installed)
+ pim_mroute_del(c_oil, name);
+ /* return success (skipped) */
+ return 0;
+ }
+
+ return pim_mroute_add(c_oil, name);
+}
+
+/* IIF associated with SGrpt entries are re-evaluated when the parent
+ * (*,G) entries IIF changes
+ */
+static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
+{
+ struct listnode *listnode;
+ struct pim_upstream *child;
+
+ for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
+ child)) {
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(child->channel_oil,
+ __func__);
+ }
+}
+
+/* In the case of "PIM state machine" added mroutes an upstream entry
+ * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
+ */
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ vifi_t iif;
+
+ iif = pim_upstream_get_mroute_iif(c_oil, name);
+
+ if (c_oil->oil.mfcc_parent != iif) {
+ c_oil->oil.mfcc_parent = iif;
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+ c_oil->up)
+ pim_upstream_all_sources_iif_update(c_oil->up);
+ } else {
+ c_oil->oil.mfcc_parent = iif;
+ }
+
+ return pim_upstream_mroute_update(c_oil, name);
+}
+
+/* Look for IIF changes and update the dateplane entry only if the IIF
+ * has changed.
+ */
+int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
+{
+ vifi_t iif;
+ char buf[1000];
+
+ iif = pim_upstream_get_mroute_iif(c_oil, name);
+ if (c_oil->oil.mfcc_parent == iif) {
+ /* no change */
+ return 0;
+ }
+ c_oil->oil.mfcc_parent = iif;
+
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+ c_oil->up)
+ pim_upstream_all_sources_iif_update(c_oil->up);
+
+ if (PIM_DEBUG_MROUTE_DETAIL)
+ zlog_debug("%s(%s) %s mroute iif update %d",
+ __func__, name,
+ pim_channel_oil_dump(c_oil, buf,
+ sizeof(buf)), iif);
+ /* XXX: is this hack needed? */
+ c_oil->oil_inherited_rescan = 1;
+ return pim_upstream_mroute_update(c_oil, name);
+}
+
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ return pim_mroute_add(c_oil, name);
+}
+
+void pim_static_mroute_iif_update(struct channel_oil *c_oil,
+ int input_vif_index,
+ const char *name)
+{
+ if (c_oil->oil.mfcc_parent == input_vif_index)
+ return;
+
+ c_oil->oil.mfcc_parent = input_vif_index;
+ if (input_vif_index == MAXVIFS)
+ pim_mroute_del(c_oil, name);
+ else
+ pim_static_mroute_add(c_oil, name);
+}
+
int pim_mroute_del(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;