#include "pim_str.h"
#include "pim_iface.h"
#include "pim_time.h"
+#include "pim_vxlan.h"
// struct list *pim_channel_oil_list = NULL;
// struct hash *pim_channel_oil_hash = NULL;
+static void pim_channel_update_mute(struct channel_oil *c_oil);
+
char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
{
char *out;
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
- int input_vif_index, const char *name)
+ const char *name)
{
struct channel_oil *c_oil;
- struct interface *ifp;
c_oil = pim_find_channel_oil(pim, sg);
if (c_oil) {
- if (c_oil->oil.mfcc_parent != input_vif_index) {
- c_oil->oil_inherited_rescan = 1;
- if (PIM_DEBUG_MROUTE_DETAIL)
- zlog_debug(
- "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
- __PRETTY_FUNCTION__, sg,
- c_oil->oil.mfcc_parent,
- input_vif_index);
- }
- c_oil->oil.mfcc_parent = input_vif_index;
++c_oil->oil_ref_count;
- /* channel might be present prior to upstream */
- c_oil->up = pim_upstream_find(pim, sg);
+
+ if (!c_oil->up) {
+ /* channel might be present prior to upstream */
+ c_oil->up = pim_upstream_find(
+ pim, sg);
+ /* if the upstream entry is being anchored to an
+ * already existing channel OIL we need to re-evaluate
+ * the "Mute" state on AA OIFs
+ */
+ pim_channel_update_mute(c_oil);
+ }
+
+ /* check if the IIF has changed
+ * XXX - is this really needed
+ */
+ pim_upstream_mroute_iif_update(c_oil, __func__);
if (PIM_DEBUG_MROUTE)
zlog_debug(
return c_oil;
}
- if (input_vif_index != MAXVIFS) {
- ifp = pim_if_find_by_vif_index(pim, input_vif_index);
- if (!ifp) {
- /* warning only */
- zlog_warn(
- "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
- __PRETTY_FUNCTION__, name, sg, input_vif_index);
- }
- }
-
c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
c_oil->oil.mfcc_mcastgrp = sg->grp;
c_oil->oil.mfcc_origin = sg->src;
c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
- c_oil->oil.mfcc_parent = input_vif_index;
+ c_oil->oil.mfcc_parent = MAXVIFS;
c_oil->oil_ref_count = 1;
c_oil->installed = 0;
c_oil->up = pim_upstream_find(pim, sg);
listnode_add_sort(pim->channel_oil_list, c_oil);
if (PIM_DEBUG_MROUTE)
- zlog_debug(
- "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
- __PRETTY_FUNCTION__, name, sg, input_vif_index);
+ zlog_debug("%s(%s): c_oil %s add",
+ __func__, name, pim_str_sg_dump(sg));
+
return c_oil;
}
-void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
+struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
+ const char *name)
{
if (PIM_DEBUG_MROUTE) {
struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
hash_release(c_oil->pim->channel_oil_hash, c_oil);
pim_channel_oil_free(c_oil);
+ return NULL;
+ }
+
+ return c_oil;
+}
+
+void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
+{
+ /* The upstream entry associated with a channel_oil is abt to be
+ * deleted. If the channel_oil is kept around because of other
+ * references we need to remove upstream based states out of it.
+ */
+ c_oil = pim_channel_oil_del(c_oil, __func__);
+ if (c_oil) {
+ /* note: here we assume that c_oil->up has already been
+ * cleared
+ */
+ pim_channel_update_mute(c_oil);
}
}
int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
- uint32_t proto_mask)
+ uint32_t proto_mask, const char *caller)
{
struct pim_interface *pim_ifp;
channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
- if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
+ PIM_OIF_FLAG_PROTO_ANY) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
}
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+ /* clear mute; will be re-evaluated when the OIF becomes valid again */
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
source_str, sizeof(source_str));
zlog_debug(
- "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+ "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
+ __PRETTY_FUNCTION__, caller, source_str, group_str,
proto_mask, channel_oil->oil.mfcc_parent, oif->name,
pim_ifp->mroute_vif_index);
}
return 0;
}
+void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
+ struct interface *oif, const char *caller)
+{
+ struct pim_upstream *up = c_oil->up;
+
+ pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
+ caller);
+
+ /* if an inherited OIF is being removed join-desired can change
+ * if the inherited OIL is now empty and KAT is running
+ */
+ if (up && up->sg.src.s_addr != INADDR_ANY &&
+ pim_upstream_empty_inherited_olist(up))
+ pim_upstream_update_join_desired(up->pim, up);
+}
+
+static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
+ struct pim_interface *pim_ifp)
+{
+ struct pim_interface *pim_reg_ifp;
+ struct pim_interface *vxlan_ifp;
+ bool do_mute = false;
+ struct pim_instance *pim = c_oil->pim;
+
+ if (!c_oil->up)
+ return do_mute;
+
+ pim_reg_ifp = pim->regiface->info;
+ if (pim_ifp == pim_reg_ifp) {
+ /* 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))
+ do_mute = true;
+
+ return do_mute;
+ }
+
+ vxlan_ifp = pim_vxlan_get_term_ifp(pim);
+ if (pim_ifp == vxlan_ifp) {
+ /* 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.
+ * PS: This check has also been extended to non-orig mroutes
+ * that have a local SIP as such mroutes can move back and
+ * forth between orig<=>non-orig type.
+ * 2. vxlan termination device should be removed from the non-DF
+ * to prevent duplicates to the overlay rxer
+ */
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
+ PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
+ pim_vxlan_is_local_sip(c_oil->up))
+ do_mute = true;
+
+ return do_mute;
+ }
+
+ return do_mute;
+}
+
+void pim_channel_update_oif_mute(struct channel_oil *c_oil,
+ struct pim_interface *pim_ifp)
+{
+ bool old_mute;
+ bool new_mute;
+
+ /* If pim_ifp is not a part of the OIL there is nothing to do */
+ if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+ return;
+
+ old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
+ PIM_OIF_FLAG_MUTE);
+ new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
+ if (old_mute == new_mute)
+ return;
+
+ if (new_mute)
+ c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+ PIM_OIF_FLAG_MUTE;
+ else
+ c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+ ~PIM_OIF_FLAG_MUTE;
+
+ pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
+}
+
+/* pim_upstream has been set or cleared on the c_oil. re-eval mute state
+ * on all existing OIFs
+ */
+static void pim_channel_update_mute(struct channel_oil *c_oil)
+{
+ struct pim_interface *pim_reg_ifp;
+ struct pim_interface *vxlan_ifp;
+
+ pim_reg_ifp = c_oil->pim->regiface->info;
+ if (pim_reg_ifp)
+ pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
+ vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
+ if (vxlan_ifp)
+ pim_channel_update_oif_mute(c_oil, vxlan_ifp);
+}
int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
- uint32_t proto_mask)
+ uint32_t proto_mask, const char *caller)
{
struct pim_interface *pim_ifp;
int old_ttl;
- bool allow_iif_in_oil = false;
/*
* If we've gotten here we've gone bad, but let's
pim_ifp = oif->info;
-#ifdef PIM_ENFORCE_LOOPFREE_MFC
- /*
- 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.
- */
- if ((channel_oil->up &&
- PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) ||
- ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) {
- allow_iif_in_oil = true;
- }
-
- if (!allow_iif_in_oil &&
- pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
- channel_oil->oil_inherited_rescan = 1;
- if (PIM_DEBUG_MROUTE) {
- char group_str[INET_ADDRSTRLEN];
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<group?>",
- channel_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- pim_inet4_dump("<source?>",
- channel_oil->oil.mfcc_origin, source_str,
- sizeof(source_str));
- zlog_debug(
- "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__, proto_mask,
- oif->name, pim_ifp->mroute_vif_index,
- source_str, group_str);
- }
- return -2;
- }
-#endif
-
/* Prevent single protocol from subscribing same interface to
channel (S,G) multiple times */
if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
PIM_MROUTE_MIN_TTL;
+ /* Some OIFs are held in a muted state i.e. the PIM state machine
+ * decided to include the OIF but additional status check such as
+ * MLAG DF role prevent it from being activated for traffic
+ * forwarding.
+ */
+ if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+ PIM_OIF_FLAG_MUTE;
+ else
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+ ~PIM_OIF_FLAG_MUTE;
+
/* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
* valid to get installed in kernel.
*/
if (channel_oil->oil.mfcc_parent != MAXVIFS) {
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
source_str, sizeof(source_str));
zlog_debug(
- "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
- __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+ "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
+ __PRETTY_FUNCTION__, caller, source_str, group_str,
proto_mask, oif->name, pim_ifp->mroute_vif_index);
}
int pim_channel_oil_empty(struct channel_oil *c_oil)
{
- static uint32_t zero[MAXVIFS];
- static int inited = 0;
+ static struct mfcctl null_oil;
if (!c_oil)
return 1;
- /*
- * Not sure that this is necessary, but I would rather ensure
- * that this works.
- */
- if (!inited) {
- memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
- inited = 1;
- }
- return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
+ /* exclude pimreg from the OIL when checking if the inherited_oil is
+ * non-NULL.
+ * pimreg device (in all vrfs) uses a vifi of
+ * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
+ return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1],
+ sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0]));
}