]> git.proxmox.com Git - mirror_frr.git/blobdiff - pimd/pim_oil.c
Merge pull request #5767 from ton31337/fix/replace_s_addr_0_to_INADDR_ANY
[mirror_frr.git] / pimd / pim_oil.c
index d14293491663d98e4ca19df1c41530021a69d6de..598988f88f7704f9a8ee8cbe4f46db09903154d7 100644 (file)
 #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;
@@ -61,8 +64,8 @@ char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
        return buf;
 }
 
-static int pim_channel_oil_compare(struct channel_oil *c1,
-                                  struct channel_oil *c2)
+int pim_channel_oil_compare(const struct channel_oil *c1,
+                           const struct channel_oil *c2)
 {
        if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
            < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
@@ -83,48 +86,19 @@ static int pim_channel_oil_compare(struct channel_oil *c1,
        return 0;
 }
 
-static bool pim_oil_equal(const void *arg1, const void *arg2)
-{
-       const struct channel_oil *c1 = (const struct channel_oil *)arg1;
-       const struct channel_oil *c2 = (const struct channel_oil *)arg2;
-
-       if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
-           && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
-               return true;
-
-       return false;
-}
-
-static unsigned int pim_oil_hash_key(const void *arg)
-{
-       const struct channel_oil *oil = arg;
-
-       return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
-                           oil->oil.mfcc_origin.s_addr, 0);
-}
-
 void pim_oil_init(struct pim_instance *pim)
 {
-       char hash_name[64];
-
-       snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name);
-       pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key,
-                                                pim_oil_equal, hash_name);
-
-       pim->channel_oil_list = list_new();
-       pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
-       pim->channel_oil_list->cmp =
-               (int (*)(void *, void *))pim_channel_oil_compare;
+       rb_pim_oil_init(&pim->channel_oil_head);
 }
 
 void pim_oil_terminate(struct pim_instance *pim)
 {
-       if (pim->channel_oil_list)
-               list_delete(&pim->channel_oil_list);
+       struct channel_oil *c_oil;
 
-       if (pim->channel_oil_hash)
-               hash_free(pim->channel_oil_hash);
-       pim->channel_oil_hash = NULL;
+       while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
+               pim_channel_oil_free(c_oil);
+
+       rb_pim_oil_fini(&pim->channel_oil_head);
 }
 
 void pim_channel_oil_free(struct channel_oil *c_oil)
@@ -141,71 +115,36 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
        lookup.oil.mfcc_mcastgrp = sg->grp;
        lookup.oil.mfcc_origin = sg->src;
 
-       c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
+       c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
 
        return c_oil;
 }
 
-void pim_channel_oil_change_iif(struct pim_instance *pim,
-                               struct channel_oil *c_oil,
-                               int input_vif_index,
-                               const char *name)
-{
-       int old_vif_index = c_oil->oil.mfcc_parent;
-       struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
-                              .grp = c_oil->oil.mfcc_origin};
-
-       if (c_oil->oil.mfcc_parent == input_vif_index) {
-               if (PIM_DEBUG_MROUTE_DETAIL)
-                       zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF",
-                                  __PRETTY_FUNCTION__, name, &sg,
-                                  input_vif_index);
-
-               return;
-       }
-
-       if (PIM_DEBUG_MROUTE_DETAIL)
-               zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d",
-                          __PRETTY_FUNCTION__, name, &sg,
-                          c_oil->oil.mfcc_parent, input_vif_index,
-                          c_oil->installed);
-
-       c_oil->oil.mfcc_parent = input_vif_index;
-       if (c_oil->installed) {
-               if (input_vif_index == MAXVIFS)
-                       pim_mroute_del(c_oil, name);
-               else
-                       pim_mroute_add(c_oil, name);
-       } else
-               if (old_vif_index == MAXVIFS)
-                       pim_mroute_add(c_oil, name);
-
-       return;
-}
-
 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);
-               }
-               pim_channel_oil_change_iif(pim, c_oil, input_vif_index,
-                                          name);
                ++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(
@@ -215,38 +154,28 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                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);
        c_oil->pim = pim;
 
-       listnode_add_sort(pim->channel_oil_list, c_oil);
+       rb_pim_oil_add(&pim->channel_oil_head, 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,
@@ -265,15 +194,32 @@ void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
                 * called by list_delete_all_node()
                 */
                c_oil->up = NULL;
-               listnode_delete(c_oil->pim->channel_oil_list, c_oil);
-               hash_release(c_oil->pim->channel_oil_hash, c_oil);
+               rb_pim_oil_del(&c_oil->pim->channel_oil_head, 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;
 
@@ -311,7 +257,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
 
        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];
@@ -333,8 +280,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
        }
 
        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];
@@ -363,8 +312,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
                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);
        }
@@ -372,13 +321,114 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
        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
@@ -391,48 +441,6 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
 
        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) {
@@ -517,11 +525,23 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
        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];
@@ -557,8 +577,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
                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);
        }
 
@@ -567,19 +587,15 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
 
 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]));
 }