back to topotato report
topotato coverage report
Current view: top level - pimd - pim_ifchannel.c (source / functions) Hit Total Coverage
Test: test_pim_crp.py::PIMCandidateBSRTest Lines: 32 631 5.1 %
Date: 2023-02-16 02:09:37 Functions: 4 32 12.5 %

          Line data    Source code
       1             : /*
       2             :  * PIM for Quagga
       3             :  * Copyright (C) 2008  Everton da Silva Marques
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify
       6             :  * it under the terms of the GNU General Public License as published by
       7             :  * the Free Software Foundation; either version 2 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but
      11             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  */
      19             : 
      20             : #include <zebra.h>
      21             : 
      22             : #include "linklist.h"
      23             : #include "thread.h"
      24             : #include "memory.h"
      25             : #include "if.h"
      26             : #include "vrf.h"
      27             : #include "hash.h"
      28             : #include "jhash.h"
      29             : #include "prefix.h"
      30             : 
      31             : #include "pimd.h"
      32             : #include "pim_instance.h"
      33             : #include "pim_str.h"
      34             : #include "pim_iface.h"
      35             : #include "pim_ifchannel.h"
      36             : #include "pim_zebra.h"
      37             : #include "pim_time.h"
      38             : #include "pim_msg.h"
      39             : #include "pim_pim.h"
      40             : #include "pim_join.h"
      41             : #include "pim_rpf.h"
      42             : #include "pim_macro.h"
      43             : #include "pim_oil.h"
      44             : #include "pim_upstream.h"
      45             : #include "pim_ssm.h"
      46             : #include "pim_rp.h"
      47             : #include "pim_mlag.h"
      48             : 
      49           0 : RB_GENERATE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb, pim_ifchannel_compare);
      50             : 
      51           0 : int pim_ifchannel_compare(const struct pim_ifchannel *ch1,
      52             :                           const struct pim_ifchannel *ch2)
      53             : {
      54           0 :         struct pim_interface *pim_ifp1;
      55           0 :         struct pim_interface *pim_ifp2;
      56             : 
      57           0 :         pim_ifp1 = ch1->interface->info;
      58           0 :         pim_ifp2 = ch2->interface->info;
      59             : 
      60           0 :         if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
      61             :                 return -1;
      62             : 
      63           0 :         if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
      64             :                 return 1;
      65             : 
      66           0 :         return pim_sgaddr_cmp(ch1->sg, ch2->sg);
      67             : }
      68             : 
      69             : /*
      70             :  * A (*,G) or a (*,*) is going away
      71             :  * remove the parent pointer from
      72             :  * those pointing at us
      73             :  */
      74           0 : static void pim_ifchannel_remove_children(struct pim_ifchannel *ch)
      75             : {
      76           0 :         struct pim_ifchannel *child;
      77             : 
      78           0 :         if (!ch->sources)
      79             :                 return;
      80             : 
      81           0 :         while (!list_isempty(ch->sources)) {
      82           0 :                 child = listnode_head(ch->sources);
      83           0 :                 child->parent = NULL;
      84           0 :                 listnode_delete(ch->sources, child);
      85             :         }
      86             : }
      87             : 
      88             : /*
      89             :  * A (*,G) or a (*,*) is being created
      90             :  * find all the children that would point
      91             :  * at us.
      92             :  */
      93           0 : static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch)
      94             : {
      95           0 :         struct pim_interface *pim_ifp = ch->interface->info;
      96           0 :         struct pim_ifchannel *child;
      97             : 
      98             :         // Basic Sanity that we are not being silly
      99           0 :         if (!pim_addr_is_any(ch->sg.src) && !pim_addr_is_any(ch->sg.grp))
     100             :                 return;
     101             : 
     102           0 :         if (pim_addr_is_any(ch->sg.src) && pim_addr_is_any(ch->sg.grp))
     103             :                 return;
     104             : 
     105           0 :         RB_FOREACH (child, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
     106           0 :                 if (!pim_addr_is_any(ch->sg.grp) &&
     107           0 :                     !pim_addr_cmp(child->sg.grp, ch->sg.grp) && (child != ch)) {
     108           0 :                         child->parent = ch;
     109           0 :                         listnode_add_sort(ch->sources, child);
     110             :                 }
     111             :         }
     112             : }
     113             : 
     114           0 : void pim_ifchannel_delete(struct pim_ifchannel *ch)
     115             : {
     116           0 :         struct pim_interface *pim_ifp;
     117           0 :         struct pim_upstream *up;
     118             : 
     119           0 :         pim_ifp = ch->interface->info;
     120             : 
     121           0 :         if (PIM_DEBUG_PIM_TRACE)
     122           0 :                 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__,
     123             :                            ch->sg_str, ch->interface->name);
     124             : 
     125           0 :         if (PIM_I_am_DualActive(pim_ifp)) {
     126           0 :                 if (PIM_DEBUG_MLAG)
     127           0 :                         zlog_debug(
     128             :                                 "%s: if-chnanel-%s is deleted from a Dual active Interface",
     129             :                                 __func__, ch->sg_str);
     130             :                 /* Post Delete only if it is the last Dual-active Interface */
     131           0 :                 if (ch->upstream->dualactive_ifchannel_count == 1) {
     132           0 :                         pim_mlag_up_local_del(pim_ifp->pim, ch->upstream);
     133           0 :                         PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
     134             :                                 ch->upstream->flags);
     135             :                 }
     136           0 :                 ch->upstream->dualactive_ifchannel_count--;
     137             :         }
     138             : 
     139           0 :         if (ch->upstream->channel_oil) {
     140           0 :                 uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
     141           0 :                 if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
     142           0 :                         mask |= PIM_OIF_FLAG_PROTO_GM;
     143             : 
     144             :                 /*
     145             :                  * A S,G RPT channel can have an empty oil, we also
     146             :                  * need to take into account the fact that a ifchannel
     147             :                  * might have been suppressing a *,G ifchannel from
     148             :                  * being inherited.  So let's figure out what
     149             :                  * needs to be done here
     150             :                  */
     151           0 :                 if (!pim_addr_is_any(ch->sg.src) &&
     152           0 :                     pim_upstream_evaluate_join_desired_interface(
     153             :                             ch->upstream, ch, ch->parent))
     154           0 :                         pim_channel_add_oif(ch->upstream->channel_oil,
     155             :                                         ch->interface,
     156             :                                         PIM_OIF_FLAG_PROTO_STAR,
     157             :                                         __func__);
     158             : 
     159           0 :                 pim_channel_del_oif(ch->upstream->channel_oil,
     160             :                                         ch->interface, mask, __func__);
     161             :                 /*
     162             :                  * Do we have any S,G's that are inheriting?
     163             :                  * Nuke from on high too.
     164             :                  */
     165           0 :                 if (ch->upstream->sources) {
     166           0 :                         struct pim_upstream *child;
     167           0 :                         struct listnode *up_node;
     168             : 
     169           0 :                         for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
     170             :                                                   up_node, child))
     171           0 :                                 pim_channel_del_inherited_oif(
     172             :                                                 child->channel_oil,
     173             :                                                 ch->interface,
     174             :                                                 __func__);
     175             :                 }
     176             :         }
     177             : 
     178             :         /*
     179             :          * When this channel is removed
     180             :          * we need to find all our children
     181             :          * and make sure our pointers are fixed
     182             :          */
     183           0 :         pim_ifchannel_remove_children(ch);
     184             : 
     185           0 :         if (ch->sources)
     186           0 :                 list_delete(&ch->sources);
     187             : 
     188           0 :         listnode_delete(ch->upstream->ifchannels, ch);
     189             : 
     190           0 :         up = ch->upstream;
     191             : 
     192             :         /* upstream is common across ifchannels, check if upstream's
     193             :            ifchannel list is empty before deleting upstream_del
     194             :            ref count will take care of it.
     195             :         */
     196           0 :         if (ch->upstream->ref_count > 0)
     197           0 :                 up = pim_upstream_del(pim_ifp->pim, ch->upstream, __func__);
     198             : 
     199             :         else {
     200           0 :                 if (PIM_DEBUG_PIM_TRACE)
     201           0 :                         zlog_debug(
     202             :                                 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
     203             :                                 __func__, ch->upstream->ref_count,
     204             :                                 ch->interface->name, ch->sg_str);
     205             :         }
     206             : 
     207           0 :         ch->upstream = NULL;
     208             : 
     209           0 :         THREAD_OFF(ch->t_ifjoin_expiry_timer);
     210           0 :         THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
     211           0 :         THREAD_OFF(ch->t_ifassert_timer);
     212             : 
     213           0 :         if (ch->parent) {
     214           0 :                 listnode_delete(ch->parent->sources, ch);
     215           0 :                 ch->parent = NULL;
     216             :         }
     217             : 
     218           0 :         RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
     219             : 
     220           0 :         if (PIM_DEBUG_PIM_TRACE)
     221           0 :                 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__,
     222             :                            ch->sg_str, ch->interface->name);
     223             : 
     224           0 :         XFREE(MTYPE_PIM_IFCHANNEL, ch);
     225             : 
     226           0 :         if (up)
     227           0 :                 pim_upstream_update_join_desired(pim_ifp->pim, up);
     228           0 : }
     229             : 
     230          11 : void pim_ifchannel_delete_all(struct interface *ifp)
     231             : {
     232          11 :         struct pim_interface *pim_ifp;
     233          11 :         struct pim_ifchannel *ch;
     234             : 
     235          11 :         pim_ifp = ifp->info;
     236          11 :         if (!pim_ifp)
     237             :                 return;
     238             : 
     239          11 :         while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
     240           0 :                 ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
     241             : 
     242           0 :                 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
     243           0 :                 pim_ifchannel_delete(ch);
     244             :         }
     245             : }
     246             : 
     247           0 : void delete_on_noinfo(struct pim_ifchannel *ch)
     248             : {
     249           0 :         if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
     250           0 :             && ch->ifjoin_state == PIM_IFJOIN_NOINFO
     251           0 :             && ch->t_ifjoin_expiry_timer == NULL)
     252           0 :                 pim_ifchannel_delete(ch);
     253           0 : }
     254             : 
     255           0 : void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
     256             :                                  enum pim_ifjoin_state new_state)
     257             : {
     258           0 :         enum pim_ifjoin_state old_state = ch->ifjoin_state;
     259           0 :         struct pim_interface *pim_ifp = ch->interface->info;
     260           0 :         struct pim_ifchannel *child_ch;
     261             : 
     262           0 :         if (PIM_DEBUG_PIM_EVENTS)
     263           0 :                 zlog_debug(
     264             :                         "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
     265             :                         ch->interface->name, ch->sg_str,
     266             :                         pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
     267             :                         pim_ifchannel_ifjoin_name(new_state, 0));
     268             : 
     269             : 
     270           0 :         if (old_state == new_state) {
     271           0 :                 if (PIM_DEBUG_PIM_EVENTS) {
     272           0 :                         zlog_debug(
     273             :                                 "%s called by %s: non-transition on state %d (%s)",
     274             :                                 __func__, caller, new_state,
     275             :                                 pim_ifchannel_ifjoin_name(new_state, 0));
     276             :                 }
     277           0 :                 return;
     278             :         }
     279             : 
     280           0 :         ch->ifjoin_state = new_state;
     281             : 
     282           0 :         if (pim_addr_is_any(ch->sg.src)) {
     283           0 :                 struct pim_upstream *up = ch->upstream;
     284           0 :                 struct pim_upstream *child;
     285           0 :                 struct listnode *up_node;
     286             : 
     287           0 :                 if (up) {
     288           0 :                         if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
     289           0 :                                 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
     290             :                                                           child)) {
     291           0 :                                         struct channel_oil *c_oil =
     292             :                                                 child->channel_oil;
     293             : 
     294           0 :                                         if (PIM_DEBUG_PIM_TRACE)
     295           0 :                                                 zlog_debug(
     296             :                                                         "%s %s: Prune(S,G)=%s from %s",
     297             :                                                         __FILE__, __func__,
     298             :                                                         child->sg_str,
     299             :                                                         up->sg_str);
     300           0 :                                         if (!c_oil)
     301           0 :                                                 continue;
     302             : 
     303             :                                         /*
     304             :                                          * If the S,G has no if channel and the
     305             :                                          * c_oil still
     306             :                                          * has output here then the *,G was
     307             :                                          * supplying the implied
     308             :                                          * if channel.  So remove it.
     309             :                                          */
     310           0 :                                         if (oil_if_has(c_oil,
     311           0 :                                                        pim_ifp->mroute_vif_index))
     312           0 :                                                 pim_channel_del_inherited_oif(
     313             :                                                         c_oil, ch->interface,
     314             :                                                         __func__);
     315             :                                 }
     316             :                         }
     317           0 :                         if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
     318           0 :                                 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
     319             :                                                           child)) {
     320           0 :                                         if (PIM_DEBUG_PIM_TRACE)
     321           0 :                                                 zlog_debug(
     322             :                                                         "%s %s: Join(S,G)=%s from %s",
     323             :                                                         __FILE__, __func__,
     324             :                                                         child->sg_str,
     325             :                                                         up->sg_str);
     326             : 
     327             :                                         /* check if the channel can be
     328             :                                          * inherited into the SG's OIL
     329             :                                          */
     330           0 :                                         child_ch = pim_ifchannel_find(
     331             :                                                         ch->interface,
     332             :                                                         &child->sg);
     333           0 :                                         if (pim_upstream_eval_inherit_if(
     334             :                                                     child, child_ch, ch)) {
     335           0 :                                                 pim_channel_add_oif(
     336             :                                                         child->channel_oil,
     337             :                                                         ch->interface,
     338             :                                                         PIM_OIF_FLAG_PROTO_STAR,
     339             :                                                         __func__);
     340           0 :                                                 pim_upstream_update_join_desired(
     341             :                                                         pim_ifp->pim, child);
     342             :                                         }
     343             :                                 }
     344             :                         }
     345             :                 }
     346             :         }
     347             :         /* Transition to/from NOINFO ? */
     348           0 :         if ((old_state == PIM_IFJOIN_NOINFO)
     349           0 :             || (new_state == PIM_IFJOIN_NOINFO)) {
     350             : 
     351           0 :                 if (PIM_DEBUG_PIM_EVENTS) {
     352           0 :                         zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
     353             :                                    ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
     354             :                                                                      : "UP"),
     355             :                                    ch->sg_str, ch->interface->name);
     356             :                 }
     357             : 
     358             :                 /*
     359             :                   Record uptime of state transition to/from NOINFO
     360             :                 */
     361           0 :                 ch->ifjoin_creation = pim_time_monotonic_sec();
     362             : 
     363           0 :                 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
     364           0 :                 pim_ifchannel_update_could_assert(ch);
     365           0 :                 pim_ifchannel_update_assert_tracking_desired(ch);
     366             :         }
     367             : }
     368             : 
     369           0 : const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
     370             :                                       int flags)
     371             : {
     372           0 :         switch (ifjoin_state) {
     373           0 :         case PIM_IFJOIN_NOINFO:
     374           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
     375             :                         return "SGRpt(NI)";
     376             :                 else
     377           0 :                         return "NOINFO";
     378             :         case PIM_IFJOIN_JOIN:
     379             :                 return "JOIN";
     380           0 :         case PIM_IFJOIN_PRUNE:
     381           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
     382             :                         return "SGRpt(P)";
     383             :                 else
     384           0 :                         return "PRUNE";
     385           0 :         case PIM_IFJOIN_PRUNE_PENDING:
     386           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
     387             :                         return "SGRpt(PP)";
     388             :                 else
     389           0 :                         return "PRUNEP";
     390           0 :         case PIM_IFJOIN_PRUNE_TMP:
     391           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
     392             :                         return "SGRpt(P')";
     393             :                 else
     394           0 :                         return "PRUNET";
     395           0 :         case PIM_IFJOIN_PRUNE_PENDING_TMP:
     396           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
     397             :                         return "SGRpt(PP')";
     398             :                 else
     399           0 :                         return "PRUNEPT";
     400             :         }
     401             : 
     402           0 :         return "ifjoin_bad_state";
     403             : }
     404             : 
     405           0 : const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
     406             : {
     407           0 :         switch (ifassert_state) {
     408             :         case PIM_IFASSERT_NOINFO:
     409             :                 return "NOINFO";
     410           0 :         case PIM_IFASSERT_I_AM_WINNER:
     411           0 :                 return "WINNER";
     412           0 :         case PIM_IFASSERT_I_AM_LOSER:
     413           0 :                 return "LOSER";
     414             :         }
     415             : 
     416           0 :         return "ifassert_bad_state";
     417             : }
     418             : 
     419             : /*
     420             :   RFC 4601: 4.6.5.  Assert State Macros
     421             : 
     422             :   AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
     423             :   defaults to Infinity when in the NoInfo state.
     424             : */
     425           0 : void reset_ifassert_state(struct pim_ifchannel *ch)
     426             : {
     427           0 :         THREAD_OFF(ch->t_ifassert_timer);
     428             : 
     429           0 :         pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, PIMADDR_ANY,
     430           0 :                                 router->infinite_assert_metric);
     431           0 : }
     432             : 
     433           0 : struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_sgaddr *sg)
     434             : {
     435           0 :         struct pim_interface *pim_ifp;
     436           0 :         struct pim_ifchannel *ch;
     437           0 :         struct pim_ifchannel lookup;
     438             : 
     439           0 :         pim_ifp = ifp->info;
     440             : 
     441           0 :         if (!pim_ifp) {
     442           0 :                 zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
     443             :                           __func__, sg, ifp->name);
     444           0 :                 return NULL;
     445             :         }
     446             : 
     447           0 :         lookup.sg = *sg;
     448           0 :         lookup.interface = ifp;
     449           0 :         ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
     450             : 
     451           0 :         return ch;
     452             : }
     453             : 
     454           0 : static void ifmembership_set(struct pim_ifchannel *ch,
     455             :                              enum pim_ifmembership membership)
     456             : {
     457           0 :         struct pim_interface *pim_ifp = ch->interface->info;
     458             : 
     459           0 :         if (ch->local_ifmembership == membership)
     460             :                 return;
     461             : 
     462           0 :         if (PIM_DEBUG_PIM_EVENTS) {
     463           0 :                 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
     464             :                            __func__, ch->sg_str,
     465             :                            membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
     466             :                                                                   : "NOINFO",
     467             :                            ch->interface->name);
     468             :         }
     469             : 
     470           0 :         ch->local_ifmembership = membership;
     471             : 
     472           0 :         pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
     473           0 :         pim_ifchannel_update_could_assert(ch);
     474           0 :         pim_ifchannel_update_assert_tracking_desired(ch);
     475             : }
     476             : 
     477             : 
     478           3 : void pim_ifchannel_membership_clear(struct interface *ifp)
     479             : {
     480           3 :         struct pim_interface *pim_ifp;
     481           3 :         struct pim_ifchannel *ch;
     482             : 
     483           3 :         pim_ifp = ifp->info;
     484           3 :         assert(pim_ifp);
     485             : 
     486           6 :         RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
     487           0 :                 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
     488           3 : }
     489             : 
     490           3 : void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
     491             : {
     492           3 :         struct pim_interface *pim_ifp;
     493           3 :         struct pim_ifchannel *ch, *ch_tmp;
     494             : 
     495           3 :         pim_ifp = ifp->info;
     496           3 :         assert(pim_ifp);
     497             : 
     498           6 :         RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
     499           0 :                 delete_on_noinfo(ch);
     500           3 : }
     501             : 
     502             : /*
     503             :  * For a given Interface, if we are given a S,G
     504             :  * Find the *,G (If we have it).
     505             :  * If we are passed a *,G, find the *,* ifchannel
     506             :  * if we have it.
     507             :  */
     508           0 : static struct pim_ifchannel *pim_ifchannel_find_parent(struct pim_ifchannel *ch)
     509             : {
     510           0 :         pim_sgaddr parent_sg = ch->sg;
     511           0 :         struct pim_ifchannel *parent = NULL;
     512             : 
     513             :         // (S,G)
     514           0 :         if (!pim_addr_is_any(parent_sg.src) &&
     515           0 :             !pim_addr_is_any(parent_sg.grp)) {
     516           0 :                 parent_sg.src = PIMADDR_ANY;
     517           0 :                 parent = pim_ifchannel_find(ch->interface, &parent_sg);
     518             : 
     519           0 :                 if (parent)
     520           0 :                         listnode_add(parent->sources, ch);
     521           0 :                 return parent;
     522             :         }
     523             : 
     524             :         return NULL;
     525             : }
     526             : 
     527           0 : struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, pim_sgaddr *sg,
     528             :                                         uint8_t source_flags, int up_flags)
     529             : {
     530           0 :         struct pim_interface *pim_ifp;
     531           0 :         struct pim_ifchannel *ch;
     532           0 :         struct pim_upstream *up;
     533             : 
     534           0 :         ch = pim_ifchannel_find(ifp, sg);
     535           0 :         if (ch) {
     536           0 :                 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
     537           0 :                         PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
     538             : 
     539           0 :                 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
     540           0 :                         PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
     541             : 
     542           0 :                 ch->upstream->flags |= up_flags;
     543             : 
     544           0 :                 return ch;
     545             :         }
     546             : 
     547           0 :         pim_ifp = ifp->info;
     548             : 
     549           0 :         ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
     550             : 
     551           0 :         ch->flags = 0;
     552           0 :         if ((source_flags & PIM_ENCODE_RPT_BIT)
     553             :             && !(source_flags & PIM_ENCODE_WC_BIT))
     554           0 :                 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
     555             : 
     556           0 :         ch->interface = ifp;
     557           0 :         ch->sg = *sg;
     558           0 :         snprintfrr(ch->sg_str, sizeof(ch->sg_str), "%pSG", sg);
     559           0 :         ch->parent = pim_ifchannel_find_parent(ch);
     560           0 :         if (pim_addr_is_any(ch->sg.src)) {
     561           0 :                 ch->sources = list_new();
     562           0 :                 ch->sources->cmp =
     563             :                         (int (*)(void *, void *))pim_ifchannel_compare;
     564             :         } else
     565           0 :                 ch->sources = NULL;
     566             : 
     567           0 :         pim_ifchannel_find_new_children(ch);
     568           0 :         ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
     569             : 
     570           0 :         ch->ifjoin_state = PIM_IFJOIN_NOINFO;
     571           0 :         ch->t_ifjoin_expiry_timer = NULL;
     572           0 :         ch->t_ifjoin_prune_pending_timer = NULL;
     573           0 :         ch->ifjoin_creation = 0;
     574             : 
     575           0 :         RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
     576             : 
     577           0 :         up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch);
     578             : 
     579           0 :         ch->upstream = up;
     580             : 
     581           0 :         listnode_add_sort(up->ifchannels, ch);
     582             : 
     583           0 :         ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
     584           0 :         ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
     585             : 
     586           0 :         ch->ifassert_winner = PIMADDR_ANY;
     587             : 
     588             :         /* Assert state */
     589           0 :         ch->t_ifassert_timer = NULL;
     590           0 :         ch->ifassert_state = PIM_IFASSERT_NOINFO;
     591           0 :         reset_ifassert_state(ch);
     592           0 :         if (pim_macro_ch_could_assert_eval(ch))
     593           0 :                 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
     594             :         else
     595           0 :                 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
     596             : 
     597           0 :         if (pim_macro_assert_tracking_desired_eval(ch))
     598           0 :                 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
     599             :         else
     600           0 :                 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
     601             : 
     602             :         /*
     603             :          * advertise MLAG Data to MLAG peer
     604             :          */
     605           0 :         if (PIM_I_am_DualActive(pim_ifp)) {
     606           0 :                 up->dualactive_ifchannel_count++;
     607             :                 /* Sync once for upstream */
     608           0 :                 if (up->dualactive_ifchannel_count == 1) {
     609           0 :                         PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags);
     610           0 :                         pim_mlag_up_local_add(pim_ifp->pim, up);
     611             :                 }
     612           0 :                 if (PIM_DEBUG_MLAG)
     613           0 :                         zlog_debug(
     614             :                                 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
     615             :                                 __func__, up->sg_str,
     616             :                                 up->dualactive_ifchannel_count, up->flags);
     617             :         }
     618             : 
     619           0 :         if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
     620           0 :                 PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
     621             : 
     622           0 :         if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
     623           0 :                 PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
     624             : 
     625           0 :         if (PIM_DEBUG_PIM_TRACE)
     626           0 :                 zlog_debug("%s: ifchannel %s(%s) is created ", __func__,
     627             :                            ch->sg_str, ch->interface->name);
     628             : 
     629             :         return ch;
     630             : }
     631             : 
     632           0 : static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
     633             : {
     634           0 :         pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
     635           0 :         pim_forward_stop(ch);
     636             : 
     637           0 :         PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
     638             : 
     639           0 :         PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
     640             : 
     641           0 :         delete_on_noinfo(ch);
     642           0 : }
     643             : 
     644           0 : static void on_ifjoin_expiry_timer(struct thread *t)
     645             : {
     646           0 :         struct pim_ifchannel *ch;
     647             : 
     648           0 :         ch = THREAD_ARG(t);
     649             : 
     650           0 :         if (PIM_DEBUG_PIM_TRACE)
     651           0 :                 zlog_debug("%s: ifchannel %s expiry timer", __func__,
     652             :                            ch->sg_str);
     653             : 
     654           0 :         ifjoin_to_noinfo(ch);
     655             :         /* ch may have been deleted */
     656           0 : }
     657             : 
     658           0 : static void on_ifjoin_prune_pending_timer(struct thread *t)
     659             : {
     660           0 :         struct pim_ifchannel *ch;
     661           0 :         int send_prune_echo; /* boolean */
     662           0 :         struct interface *ifp;
     663           0 :         struct pim_interface *pim_ifp;
     664             : 
     665           0 :         ch = THREAD_ARG(t);
     666             : 
     667           0 :         if (PIM_DEBUG_PIM_TRACE)
     668           0 :                 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
     669             :                            __func__, &ch->sg,
     670             :                            pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
     671             : 
     672           0 :         if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
     673           0 :                 ifp = ch->interface;
     674           0 :                 pim_ifp = ifp->info;
     675           0 :                 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
     676             :                         /* Send PruneEcho(S,G) ? */
     677           0 :                         send_prune_echo =
     678           0 :                                 (listcount(pim_ifp->pim_neighbor_list) > 1);
     679             : 
     680           0 :                         if (send_prune_echo) {
     681           0 :                                 struct pim_rpf rpf;
     682             : 
     683           0 :                                 rpf.source_nexthop.interface = ifp;
     684           0 :                                 rpf.rpf_addr = pim_ifp->primary_address;
     685           0 :                                 pim_jp_agg_single_upstream_send(
     686             :                                         &rpf, ch->upstream, 0);
     687             :                         }
     688             : 
     689           0 :                         ifjoin_to_noinfo(ch);
     690             :                 } else {
     691             :                         /* If SGRpt flag is set on ifchannel, Trigger SGRpt
     692             :                          *  message on RP path upon prune timer expiry.
     693             :                          */
     694           0 :                         ch->ifjoin_state = PIM_IFJOIN_PRUNE;
     695           0 :                         struct pim_upstream *parent =
     696           0 :                                 ch->upstream->parent;
     697             : 
     698           0 :                         pim_upstream_update_join_desired(pim_ifp->pim,
     699             :                                                          ch->upstream);
     700             : 
     701           0 :                         pim_jp_agg_single_upstream_send(&parent->rpf,
     702             :                                                         parent, true);
     703             :                         /*
     704             :                          * SGRpt prune pending expiry has to install
     705             :                          * SG entry with empty olist to drop the SG
     706             :                          * traffic incase no other intf exists.
     707             :                          * On that scenario, SG entry wouldn't have
     708             :                          * got installed until Prune pending timer
     709             :                          * expired. So install now.
     710             :                          */
     711           0 :                         pim_channel_del_oif(
     712           0 :                                 ch->upstream->channel_oil, ifp,
     713             :                                 PIM_OIF_FLAG_PROTO_STAR, __func__);
     714           0 :                         pim_channel_del_oif(ch->upstream->channel_oil, ifp,
     715             :                                             PIM_OIF_FLAG_PROTO_PIM, __func__);
     716           0 :                         if (!ch->upstream->channel_oil->installed)
     717           0 :                                 pim_upstream_mroute_add(
     718             :                                         ch->upstream->channel_oil,
     719             :                                         __func__);
     720             :                 }
     721             :                 /* from here ch may have been deleted */
     722             :         }
     723           0 : }
     724             : 
     725           0 : static void check_recv_upstream(int is_join, struct interface *recv_ifp,
     726             :                                 pim_addr upstream, pim_sgaddr *sg,
     727             :                                 uint8_t source_flags, int holdtime)
     728             : {
     729           0 :         struct pim_upstream *up;
     730           0 :         struct pim_interface *pim_ifp = recv_ifp->info;
     731           0 :         pim_addr rpf_addr;
     732             : 
     733             :         /* Upstream (S,G) in Joined state ? */
     734           0 :         up = pim_upstream_find(pim_ifp->pim, sg);
     735           0 :         if (!up)
     736           0 :                 return;
     737           0 :         if (up->join_state != PIM_UPSTREAM_JOINED)
     738             :                 return;
     739             : 
     740             :         /* Upstream (S,G) in Joined state */
     741             : 
     742           0 :         if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
     743             :                 /* RPF'(S,G) not found */
     744           0 :                 zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__,
     745             :                           up->sg_str);
     746           0 :                 return;
     747             :         }
     748             : 
     749           0 :         rpf_addr = up->rpf.rpf_addr;
     750             : 
     751             :         /* upstream directed to RPF'(S,G) ? */
     752           0 :         if (pim_addr_cmp(upstream, rpf_addr)) {
     753           0 :                 zlog_warn(
     754             :                         "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
     755             :                         __FILE__, __func__, up->sg_str, &upstream, &rpf_addr,
     756             :                         recv_ifp->name);
     757           0 :                 return;
     758             :         }
     759             :         /* upstream directed to RPF'(S,G) */
     760             : 
     761           0 :         if (is_join) {
     762             :                 /* Join(S,G) to RPF'(S,G) */
     763           0 :                 pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
     764           0 :                 return;
     765             :         }
     766             : 
     767             :         /* Prune to RPF'(S,G) */
     768             : 
     769           0 :         if (source_flags & PIM_RPT_BIT_MASK) {
     770           0 :                 if (source_flags & PIM_WILDCARD_BIT_MASK) {
     771             :                         /* Prune(*,G) to RPF'(S,G) */
     772           0 :                         pim_upstream_join_timer_decrease_to_t_override(
     773             :                                 "Prune(*,G)", up);
     774           0 :                         return;
     775             :                 }
     776             : 
     777             :                 /* Prune(S,G,rpt) to RPF'(S,G) */
     778           0 :                 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
     779             :                                                                up);
     780           0 :                 return;
     781             :         }
     782             : 
     783             :         /* Prune(S,G) to RPF'(S,G) */
     784           0 :         pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
     785             : }
     786             : 
     787           0 : static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
     788             :                              pim_addr upstream, pim_sgaddr *sg,
     789             :                              uint8_t source_flags, uint16_t holdtime)
     790             : {
     791           0 :         struct pim_interface *recv_pim_ifp;
     792           0 :         int is_local; /* boolean */
     793             : 
     794           0 :         recv_pim_ifp = recv_ifp->info;
     795           0 :         assert(recv_pim_ifp);
     796             : 
     797           0 :         is_local = !pim_addr_cmp(upstream, recv_pim_ifp->primary_address);
     798             : 
     799           0 :         if (is_local)
     800             :                 return 0;
     801             : 
     802           0 :         if (PIM_DEBUG_PIM_TRACE_DETAIL)
     803           0 :                 zlog_warn(
     804             :                         "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
     805             :                         __func__, is_join ? "join" : "prune", sg, &upstream,
     806             :                         recv_ifp->name);
     807             : 
     808             :         /*
     809             :          * Since recv upstream addr was not directed to our primary
     810             :          * address, check if we should react to it in any way.
     811             :          */
     812           0 :         check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
     813             :                             holdtime);
     814             : 
     815           0 :         return 1; /* non-local */
     816             : }
     817             : 
     818           0 : static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
     819             :                 struct pim_interface *pim_ifp)
     820             : {
     821           0 :         pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
     822           0 :         PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
     823             :         /* check if the interface qualifies as an immediate
     824             :          * OIF
     825             :          */
     826           0 :         if (pim_upstream_evaluate_join_desired_interface(
     827             :                                 ch->upstream, ch,
     828             :                                 NULL /*starch*/)) {
     829           0 :                 pim_channel_add_oif(ch->upstream->channel_oil,
     830             :                                 ch->interface,
     831             :                                 PIM_OIF_FLAG_PROTO_PIM,
     832             :                                 __func__);
     833           0 :                 pim_upstream_update_join_desired(pim_ifp->pim,
     834             :                                 ch->upstream);
     835             :         }
     836           0 : }
     837             : 
     838             : 
     839           0 : void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr,
     840             :                             pim_addr upstream, pim_sgaddr *sg,
     841             :                             uint8_t source_flags, uint16_t holdtime)
     842             : {
     843           0 :         struct pim_interface *pim_ifp;
     844           0 :         struct pim_ifchannel *ch;
     845             : 
     846           0 :         if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
     847             :                               holdtime)) {
     848             :                 return;
     849             :         }
     850             : 
     851           0 :         ch = pim_ifchannel_add(ifp, sg, source_flags,
     852             :                                PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
     853             : 
     854             :         /*
     855             :           RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
     856             : 
     857             :           Transitions from "I am Assert Loser" State
     858             : 
     859             :           Receive Join(S,G) on Interface I
     860             : 
     861             :           We receive a Join(S,G) that has the Upstream Neighbor Address
     862             :           field set to my primary IP address on interface I.  The action is
     863             :           to transition to NoInfo state, delete this (S,G) assert state
     864             :           (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
     865             :           to operate.
     866             : 
     867             :           Notice: The nonlocal_upstream() test above ensures the upstream
     868             :           address of the join message is our primary address.
     869             :          */
     870           0 :         if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
     871           0 :                 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
     872             :                           __func__, ch->sg_str, &neigh_addr, ifp->name);
     873             : 
     874           0 :                 assert_action_a5(ch);
     875             :         }
     876             : 
     877           0 :         pim_ifp = ifp->info;
     878           0 :         assert(pim_ifp);
     879             : 
     880           0 :         switch (ch->ifjoin_state) {
     881           0 :         case PIM_IFJOIN_NOINFO:
     882           0 :                 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
     883           0 :                 if (pim_macro_chisin_oiflist(ch)) {
     884           0 :                         pim_upstream_inherited_olist(pim_ifp->pim,
     885             :                                                      ch->upstream);
     886           0 :                         pim_forward_start(ch);
     887             :                 }
     888             :                 /*
     889             :                  * If we are going to be a LHR, we need to note it
     890             :                  */
     891           0 :                 if (ch->upstream->parent &&
     892           0 :                         (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
     893             :                                                    ch->upstream->parent->flags))
     894           0 :                     && !(ch->upstream->flags
     895           0 :                          & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
     896           0 :                         pim_upstream_ref(ch->upstream,
     897             :                                          PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
     898             :                                          __func__);
     899           0 :                         pim_upstream_keep_alive_timer_start(
     900           0 :                                 ch->upstream, pim_ifp->pim->keep_alive_time);
     901             :                 }
     902             :                 break;
     903           0 :         case PIM_IFJOIN_JOIN:
     904           0 :                 assert(!ch->t_ifjoin_prune_pending_timer);
     905             : 
     906             :                 /*
     907             :                   In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
     908             :                   a
     909             :                   previously received join message with holdtime=0xFFFF.
     910             :                  */
     911           0 :                 if (ch->t_ifjoin_expiry_timer) {
     912           0 :                         unsigned long remain = thread_timer_remain_second(
     913             :                                 ch->t_ifjoin_expiry_timer);
     914           0 :                         if (remain > holdtime) {
     915             :                                 /*
     916             :                                   RFC 4601: 4.5.3.  Receiving (S,G) Join/Prune
     917             :                                   Messages
     918             : 
     919             :                                   Transitions from Join State
     920             : 
     921             :                                   The (S,G) downstream state machine on
     922             :                                   interface I remains in
     923             :                                   Join state, and the Expiry Timer (ET) is
     924             :                                   restarted, set to
     925             :                                   maximum of its current value and the HoldTime
     926             :                                   from the
     927             :                                   triggering Join/Prune message.
     928             : 
     929             :                                   Conclusion: Do not change the ET if the
     930             :                                   current value is
     931             :                                   higher than the received join holdtime.
     932             :                                  */
     933             :                                 return;
     934             :                         }
     935             :                 }
     936           0 :                 THREAD_OFF(ch->t_ifjoin_expiry_timer);
     937             :                 break;
     938           0 :         case PIM_IFJOIN_PRUNE:
     939           0 :                 if (source_flags & PIM_ENCODE_RPT_BIT) {
     940           0 :                         pim_ifchannel_ifjoin_switch(__func__, ch,
     941             :                                                     PIM_IFJOIN_NOINFO);
     942           0 :                         THREAD_OFF(ch->t_ifjoin_expiry_timer);
     943           0 :                         delete_on_noinfo(ch);
     944           0 :                         return;
     945             :                 } else
     946           0 :                         pim_ifchannel_ifjoin_handler(ch, pim_ifp);
     947           0 :                 break;
     948           0 :         case PIM_IFJOIN_PRUNE_PENDING:
     949             :                 /*
     950             :                  * Transitions from Prune-Pending State (Receive Join)
     951             :                  * RFC 7761 Sec 4.5.2:
     952             :                  *    The (S,G) downstream state machine on interface I
     953             :                  * transitions to the Join state.  The Prune-Pending Timer is
     954             :                  * canceled (without triggering an expiry event).  The
     955             :                  * Expiry Timer (ET) is restarted and is then set to the
     956             :                  * maximum of its current value and the HoldTime from the
     957             :                  * triggering Join/Prune message.
     958             :                  */
     959           0 :                 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
     960             : 
     961             :                 /* Check if SGRpt join Received */
     962           0 :                 if ((source_flags & PIM_ENCODE_RPT_BIT) &&
     963           0 :                     !pim_addr_is_any(sg->src)) {
     964             :                         /*
     965             :                          * Transitions from Prune-Pending State (Rcv SGRpt Join)
     966             :                          * RFC 7761 Sec 4.5.3:
     967             :                          * The (S,G,rpt) downstream state machine on interface
     968             :                          * I transitions to the NoInfo state.The ET and PPT are
     969             :                          * cancelled.
     970             :                          */
     971           0 :                         THREAD_OFF(ch->t_ifjoin_expiry_timer);
     972           0 :                         pim_ifchannel_ifjoin_switch(__func__, ch,
     973             :                                                     PIM_IFJOIN_NOINFO);
     974           0 :                         return;
     975             :                 }
     976             : 
     977           0 :                 pim_ifchannel_ifjoin_handler(ch, pim_ifp);
     978             : 
     979           0 :                 if (ch->t_ifjoin_expiry_timer) {
     980           0 :                         unsigned long remain = thread_timer_remain_second(
     981             :                                 ch->t_ifjoin_expiry_timer);
     982             : 
     983           0 :                         if (remain > holdtime)
     984             :                                 return;
     985             :                 }
     986           0 :                 THREAD_OFF(ch->t_ifjoin_expiry_timer);
     987             : 
     988             :                 break;
     989             :         case PIM_IFJOIN_PRUNE_TMP:
     990             :                 break;
     991             :         case PIM_IFJOIN_PRUNE_PENDING_TMP:
     992             :                 break;
     993             :         }
     994             : 
     995           0 :         if (holdtime != 0xFFFF) {
     996           0 :                 thread_add_timer(router->master, on_ifjoin_expiry_timer, ch,
     997             :                                  holdtime, &ch->t_ifjoin_expiry_timer);
     998             :         }
     999             : }
    1000             : 
    1001           0 : void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream,
    1002             :                          pim_sgaddr *sg, uint8_t source_flags,
    1003             :                          uint16_t holdtime)
    1004             : {
    1005           0 :         struct pim_ifchannel *ch;
    1006           0 :         struct pim_interface *pim_ifp;
    1007           0 :         int jp_override_interval_msec;
    1008             : 
    1009           0 :         if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
    1010             :                               holdtime)) {
    1011             :                 return;
    1012             :         }
    1013             : 
    1014           0 :         ch = pim_ifchannel_find(ifp, sg);
    1015           0 :         if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
    1016           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1017           0 :                         zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
    1018             :                                    __func__, ifp->name, sg,
    1019             :                                    source_flags);
    1020           0 :                 return;
    1021             :         }
    1022             : 
    1023           0 :         ch = pim_ifchannel_add(ifp, sg, source_flags,
    1024             :                                PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
    1025             : 
    1026           0 :         pim_ifp = ifp->info;
    1027             : 
    1028           0 :         switch (ch->ifjoin_state) {
    1029           0 :         case PIM_IFJOIN_NOINFO:
    1030           0 :                 if (source_flags & PIM_ENCODE_RPT_BIT) {
    1031           0 :                         if (!(source_flags & PIM_ENCODE_WC_BIT))
    1032           0 :                                 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
    1033             : 
    1034           0 :                         ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
    1035           0 :                         if (listcount(pim_ifp->pim_neighbor_list) > 1)
    1036           0 :                                 jp_override_interval_msec =
    1037           0 :                                         pim_if_jp_override_interval_msec(ifp);
    1038             :                         else
    1039             :                                 jp_override_interval_msec =
    1040             :                                         0; /* schedule to expire immediately */
    1041             :                         /* If we called ifjoin_prune() directly instead, care
    1042             :                            should
    1043             :                            be taken not to use "ch" afterwards since it would be
    1044             :                            deleted. */
    1045             : 
    1046           0 :                         THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
    1047           0 :                         THREAD_OFF(ch->t_ifjoin_expiry_timer);
    1048           0 :                         thread_add_timer_msec(
    1049             :                                 router->master, on_ifjoin_prune_pending_timer,
    1050             :                                 ch, jp_override_interval_msec,
    1051             :                                 &ch->t_ifjoin_prune_pending_timer);
    1052           0 :                         thread_add_timer(router->master, on_ifjoin_expiry_timer,
    1053             :                                          ch, holdtime,
    1054             :                                          &ch->t_ifjoin_expiry_timer);
    1055           0 :                         pim_upstream_update_join_desired(pim_ifp->pim,
    1056             :                                                          ch->upstream);
    1057             :                 }
    1058             :                 break;
    1059             :         case PIM_IFJOIN_PRUNE_PENDING:
    1060             :                 /* nothing to do */
    1061             :                 break;
    1062           0 :         case PIM_IFJOIN_JOIN:
    1063             :                 /*
    1064             :                  * The (S,G) downstream state machine on interface I
    1065             :                  * transitions to the Prune-Pending state.  The
    1066             :                  * Prune-Pending Timer is started.  It is set to the
    1067             :                  * J/P_Override_Interval(I) if the router has more than one
    1068             :                  * neighbor on that interface; otherwise, it is set to zero,
    1069             :                  * causing it to expire immediately.
    1070             :                  */
    1071             : 
    1072           0 :                 pim_ifchannel_ifjoin_switch(__func__, ch,
    1073             :                                             PIM_IFJOIN_PRUNE_PENDING);
    1074             : 
    1075           0 :                 if (listcount(pim_ifp->pim_neighbor_list) > 1)
    1076           0 :                         jp_override_interval_msec =
    1077           0 :                                 pim_if_jp_override_interval_msec(ifp);
    1078             :                 else
    1079             :                         jp_override_interval_msec =
    1080             :                                 0; /* schedule to expire immediately */
    1081             :                 /* If we called ifjoin_prune() directly instead, care should
    1082             :                    be taken not to use "ch" afterwards since it would be
    1083             :                    deleted. */
    1084           0 :                 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
    1085           0 :                 thread_add_timer_msec(router->master,
    1086             :                                       on_ifjoin_prune_pending_timer, ch,
    1087             :                                       jp_override_interval_msec,
    1088             :                                       &ch->t_ifjoin_prune_pending_timer);
    1089           0 :                 break;
    1090           0 :         case PIM_IFJOIN_PRUNE:
    1091           0 :                 if (source_flags & PIM_ENCODE_RPT_BIT) {
    1092           0 :                         THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
    1093             :                         /*
    1094             :                          * While in Prune State, Receive SGRpt Prune.
    1095             :                          * RFC 7761 Sec 4.5.3:
    1096             :                          * The (S,G,rpt) downstream state machine on interface I
    1097             :                          * remains in Prune state.  The Expiry Timer (ET) is
    1098             :                          * restarted and is then set to the maximum of its
    1099             :                          * current value and the HoldTime from the triggering
    1100             :                          * Join/Prune message.
    1101             :                          */
    1102           0 :                         if (ch->t_ifjoin_expiry_timer) {
    1103           0 :                                 unsigned long rem = thread_timer_remain_second(
    1104             :                                         ch->t_ifjoin_expiry_timer);
    1105             : 
    1106           0 :                                 if (rem > holdtime)
    1107             :                                         return;
    1108           0 :                                 THREAD_OFF(ch->t_ifjoin_expiry_timer);
    1109             :                         }
    1110             : 
    1111           0 :                         thread_add_timer(router->master, on_ifjoin_expiry_timer,
    1112             :                                          ch, holdtime,
    1113             :                                          &ch->t_ifjoin_expiry_timer);
    1114             :                 }
    1115             :                 break;
    1116           0 :         case PIM_IFJOIN_PRUNE_TMP:
    1117           0 :                 if (source_flags & PIM_ENCODE_RPT_BIT) {
    1118           0 :                         ch->ifjoin_state = PIM_IFJOIN_PRUNE;
    1119           0 :                         THREAD_OFF(ch->t_ifjoin_expiry_timer);
    1120           0 :                         thread_add_timer(router->master, on_ifjoin_expiry_timer,
    1121             :                                          ch, holdtime,
    1122             :                                          &ch->t_ifjoin_expiry_timer);
    1123             :                 }
    1124             :                 break;
    1125           0 :         case PIM_IFJOIN_PRUNE_PENDING_TMP:
    1126           0 :                 if (source_flags & PIM_ENCODE_RPT_BIT) {
    1127           0 :                         ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
    1128           0 :                         THREAD_OFF(ch->t_ifjoin_expiry_timer);
    1129           0 :                         thread_add_timer(router->master, on_ifjoin_expiry_timer,
    1130             :                                          ch, holdtime,
    1131             :                                          &ch->t_ifjoin_expiry_timer);
    1132             :                 }
    1133             :                 break;
    1134             :         }
    1135             : }
    1136             : 
    1137           0 : int pim_ifchannel_local_membership_add(struct interface *ifp, pim_sgaddr *sg,
    1138             :                                        bool is_vxlan)
    1139             : {
    1140           0 :         struct pim_ifchannel *ch, *starch;
    1141           0 :         struct pim_interface *pim_ifp;
    1142           0 :         struct pim_instance *pim;
    1143           0 :         int up_flags;
    1144             : 
    1145             :         /* PIM enabled on interface? */
    1146           0 :         pim_ifp = ifp->info;
    1147           0 :         if (!pim_ifp) {
    1148           0 :                 if (PIM_DEBUG_EVENTS)
    1149           0 :                         zlog_debug("%s:%pSG Expected pim interface setup for %s",
    1150             :                                    __func__, sg, ifp->name);
    1151           0 :                 return 0;
    1152             :         }
    1153             : 
    1154           0 :         if (!pim_ifp->pim_enable) {
    1155           0 :                 if (PIM_DEBUG_EVENTS)
    1156           0 :                         zlog_debug("%s:%pSG PIM is not configured on this interface %s",
    1157             :                                    __func__, sg, ifp->name);
    1158           0 :                 return 0;
    1159             :         }
    1160             : 
    1161           0 :         pim = pim_ifp->pim;
    1162             : 
    1163             :         /* skip (*,G) ch creation if G is of type SSM */
    1164           0 :         if (pim_addr_is_any(sg->src)) {
    1165           0 :                 if (pim_is_grp_ssm(pim, sg->grp)) {
    1166           0 :                         if (PIM_DEBUG_PIM_EVENTS)
    1167           0 :                                 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
    1168             :                                            __func__, sg);
    1169           0 :                         return 1;
    1170             :                 }
    1171             :         }
    1172             : 
    1173             :         /* vxlan term mroutes use ipmr-lo as local member to
    1174             :          * pull down multicast vxlan tunnel traffic
    1175             :          */
    1176           0 :         up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM :
    1177             :                 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP;
    1178           0 :         ch = pim_ifchannel_add(ifp, sg, 0, up_flags);
    1179             : 
    1180           0 :         ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
    1181             : 
    1182           0 :         if (pim_addr_is_any(sg->src)) {
    1183           0 :                 struct pim_upstream *up = pim_upstream_find(pim, sg);
    1184           0 :                 struct pim_upstream *child;
    1185           0 :                 struct listnode *up_node;
    1186             : 
    1187           0 :                 starch = ch;
    1188             : 
    1189           0 :                 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
    1190           0 :                         if (PIM_DEBUG_EVENTS)
    1191           0 :                                 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
    1192             :                                            __FILE__, __func__, child->sg_str,
    1193             :                                            ifp->name, up->sg_str);
    1194             : 
    1195           0 :                         if (!child->rpf.source_nexthop.interface) {
    1196             :                                 /* when iif unknown, do not inherit */
    1197           0 :                                 if (PIM_DEBUG_EVENTS)
    1198           0 :                                         zlog_debug(
    1199             :                                                 "Skipped (S,G)=%s(%s) from %s: no iif",
    1200             :                                                 child->sg_str, ifp->name,
    1201             :                                                 up->sg_str);
    1202           0 :                                 continue;
    1203             :                         }
    1204             : 
    1205           0 :                         ch = pim_ifchannel_find(ifp, &child->sg);
    1206           0 :                         if (pim_upstream_evaluate_join_desired_interface(
    1207             :                                     child, ch, starch)) {
    1208           0 :                                 pim_channel_add_oif(child->channel_oil, ifp,
    1209             :                                                     PIM_OIF_FLAG_PROTO_STAR,
    1210             :                                                         __func__);
    1211           0 :                                 pim_upstream_update_join_desired(pim, child);
    1212             :                         }
    1213             :                 }
    1214             : 
    1215           0 :                 if (pim->spt.switchover == PIM_SPT_INFINITY) {
    1216           0 :                         if (pim->spt.plist) {
    1217           0 :                                 struct prefix_list *plist = prefix_list_lookup(
    1218             :                                         AFI_IP, pim->spt.plist);
    1219           0 :                                 struct prefix g;
    1220             : 
    1221           0 :                                 pim_addr_to_prefix(&g, up->sg.grp);
    1222           0 :                                 if (prefix_list_apply_ext(plist, NULL, &g,
    1223             :                                                           true) ==
    1224             :                                     PREFIX_DENY) {
    1225           0 :                                         pim_channel_add_oif(
    1226             :                                                 up->channel_oil, pim->regiface,
    1227             :                                                 PIM_OIF_FLAG_PROTO_GM,
    1228             :                                                 __func__);
    1229             :                                 }
    1230             :                         }
    1231             :                 } else
    1232           0 :                         pim_channel_add_oif(up->channel_oil, pim->regiface,
    1233             :                                             PIM_OIF_FLAG_PROTO_GM, __func__);
    1234             :         }
    1235             : 
    1236             :         return 1;
    1237             : }
    1238             : 
    1239           0 : void pim_ifchannel_local_membership_del(struct interface *ifp, pim_sgaddr *sg)
    1240             : {
    1241           0 :         struct pim_ifchannel *starch, *ch, *orig;
    1242           0 :         struct pim_interface *pim_ifp;
    1243             : 
    1244             :         /* PIM enabled on interface? */
    1245           0 :         pim_ifp = ifp->info;
    1246           0 :         if (!pim_ifp)
    1247             :                 return;
    1248           0 :         if (!pim_ifp->pim_enable)
    1249             :                 return;
    1250             : 
    1251           0 :         orig = ch = pim_ifchannel_find(ifp, sg);
    1252           0 :         if (!ch)
    1253             :                 return;
    1254           0 :         ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
    1255             : 
    1256           0 :         if (pim_addr_is_any(sg->src)) {
    1257           0 :                 struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
    1258           0 :                 struct pim_upstream *child;
    1259           0 :                 struct listnode *up_node, *up_nnode;
    1260             : 
    1261           0 :                 starch = ch;
    1262             : 
    1263           0 :                 for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
    1264           0 :                         struct channel_oil *c_oil = child->channel_oil;
    1265           0 :                         struct pim_ifchannel *chchannel =
    1266           0 :                                 pim_ifchannel_find(ifp, &child->sg);
    1267             : 
    1268           0 :                         pim_ifp = ifp->info;
    1269             : 
    1270           0 :                         if (PIM_DEBUG_EVENTS)
    1271           0 :                                 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
    1272             :                                            __FILE__, __func__, up->sg_str,
    1273             :                                            ifp->name, child->sg_str);
    1274             : 
    1275           0 :                         ch = pim_ifchannel_find(ifp, &child->sg);
    1276             :                         /*
    1277             :                          * If the S,G has no if channel and the c_oil still
    1278             :                          * has output here then the *,G was supplying the
    1279             :                          * implied
    1280             :                          * if channel.  So remove it.
    1281             :                          */
    1282           0 :                         if (!pim_upstream_evaluate_join_desired_interface(
    1283           0 :                                 child, ch, starch) ||
    1284           0 :                                 (!chchannel &&
    1285           0 :                                  oil_if_has(c_oil, pim_ifp->mroute_vif_index))) {
    1286           0 :                                 pim_channel_del_inherited_oif(c_oil, ifp,
    1287             :                                                 __func__);
    1288             :                         }
    1289             : 
    1290             :                         /* Child node removal/ref count-- will happen as part of
    1291             :                          * parent' delete_no_info */
    1292             :                 }
    1293             :         }
    1294             : 
    1295             :         /* Resettng the IGMP flags here */
    1296           0 :         if (orig->upstream)
    1297           0 :                 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig->upstream->flags);
    1298             : 
    1299           0 :         PIM_IF_FLAG_UNSET_PROTO_IGMP(orig->flags);
    1300             : 
    1301           0 :         delete_on_noinfo(orig);
    1302             : }
    1303             : 
    1304           0 : void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
    1305             : {
    1306           0 :         int old_couldassert =
    1307           0 :                 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
    1308           0 :         int new_couldassert =
    1309           0 :                 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
    1310             : 
    1311           0 :         if (new_couldassert == old_couldassert)
    1312             :                 return;
    1313             : 
    1314           0 :         if (PIM_DEBUG_PIM_EVENTS)
    1315           0 :                 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
    1316             :                            __func__, &ch->sg.src, &ch->sg.grp,
    1317             :                            ch->interface->name, old_couldassert,
    1318             :                            new_couldassert);
    1319             : 
    1320           0 :         if (new_couldassert) {
    1321             :                 /* CouldAssert(S,G,I) switched from false to true */
    1322           0 :                 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
    1323             :         } else {
    1324             :                 /* CouldAssert(S,G,I) switched from true to false */
    1325           0 :                 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
    1326             : 
    1327           0 :                 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
    1328           0 :                         assert_action_a4(ch);
    1329             :                 }
    1330             :         }
    1331             : 
    1332           0 :         pim_ifchannel_update_my_assert_metric(ch);
    1333             : }
    1334             : 
    1335             : /*
    1336             :   my_assert_metric may be affected by:
    1337             : 
    1338             :   CouldAssert(S,G)
    1339             :   pim_ifp->primary_address
    1340             :   rpf->source_nexthop.mrib_metric_preference;
    1341             :   rpf->source_nexthop.mrib_route_metric;
    1342             :  */
    1343           0 : void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
    1344             : {
    1345           0 :         struct pim_assert_metric my_metric_new =
    1346           0 :                 pim_macro_ch_my_assert_metric_eval(ch);
    1347             : 
    1348           0 :         if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
    1349           0 :                 return;
    1350             : 
    1351           0 :         if (PIM_DEBUG_PIM_EVENTS)
    1352           0 :                 zlog_debug(
    1353             :                         "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
    1354             :                         __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
    1355             :                         ch->ifassert_my_metric.rpt_bit_flag,
    1356             :                         ch->ifassert_my_metric.metric_preference,
    1357             :                         ch->ifassert_my_metric.route_metric,
    1358             :                         &ch->ifassert_my_metric.ip_address,
    1359             :                         my_metric_new.rpt_bit_flag,
    1360             :                         my_metric_new.metric_preference,
    1361             :                         my_metric_new.route_metric, &my_metric_new.ip_address);
    1362             : 
    1363           0 :         ch->ifassert_my_metric = my_metric_new;
    1364             : 
    1365           0 :         if (pim_assert_metric_better(&ch->ifassert_my_metric,
    1366           0 :                                      &ch->ifassert_winner_metric)) {
    1367           0 :                 assert_action_a5(ch);
    1368             :         }
    1369             : }
    1370             : 
    1371           0 : void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
    1372             : {
    1373           0 :         int old_atd = PIM_FORCE_BOOLEAN(
    1374             :                 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
    1375           0 :         int new_atd =
    1376           0 :                 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
    1377             : 
    1378           0 :         if (new_atd == old_atd)
    1379             :                 return;
    1380             : 
    1381           0 :         if (PIM_DEBUG_PIM_EVENTS)
    1382           0 :                 zlog_debug(
    1383             :                         "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
    1384             :                         __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
    1385             :                         old_atd, new_atd);
    1386             : 
    1387           0 :         if (new_atd) {
    1388             :                 /* AssertTrackingDesired(S,G,I) switched from false to true */
    1389           0 :                 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
    1390             :         } else {
    1391             :                 /* AssertTrackingDesired(S,G,I) switched from true to false */
    1392           0 :                 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
    1393             : 
    1394           0 :                 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
    1395           0 :                         assert_action_a5(ch);
    1396             :                 }
    1397             :         }
    1398             : }
    1399             : 
    1400             : /*
    1401             :  * If we have a new pim interface, check to
    1402             :  * see if any of the pre-existing channels have
    1403             :  * their upstream out that way and turn on forwarding
    1404             :  * for that ifchannel then.
    1405             :  */
    1406          78 : void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
    1407             : {
    1408          78 :         struct pim_interface *new_pim_ifp = new_ifp->info;
    1409          78 :         struct pim_instance *pim = new_pim_ifp->pim;
    1410          78 :         struct interface *ifp;
    1411             : 
    1412         384 :         FOR_ALL_INTERFACES (pim->vrf, ifp) {
    1413         228 :                 struct pim_interface *loop_pim_ifp = ifp->info;
    1414         228 :                 struct pim_ifchannel *ch;
    1415             : 
    1416         228 :                 if (!loop_pim_ifp)
    1417           0 :                         continue;
    1418             : 
    1419         228 :                 if (new_pim_ifp == loop_pim_ifp)
    1420          78 :                         continue;
    1421             : 
    1422         300 :                 RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
    1423           0 :                         if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
    1424           0 :                                 struct pim_upstream *up = ch->upstream;
    1425           0 :                                 if ((!up->channel_oil)
    1426           0 :                                     && (up->rpf.source_nexthop
    1427           0 :                                                 .interface == new_ifp))
    1428           0 :                                         pim_forward_start(ch);
    1429             :                         }
    1430             :                 }
    1431             :         }
    1432          78 : }
    1433             : 
    1434             : /*
    1435             :  * Downstream per-interface (S,G,rpt) state machine
    1436             :  * states that we need to move (S,G,rpt) items
    1437             :  * into different states at the start of the
    1438             :  * reception of a *,G join as well, when
    1439             :  * we get End of Message
    1440             :  */
    1441           0 : void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
    1442             :                                          uint8_t join)
    1443             : {
    1444           0 :         bool send_upstream_starg = false;
    1445           0 :         struct pim_ifchannel *child;
    1446           0 :         struct listnode *ch_node, *nch_node;
    1447           0 :         struct pim_instance *pim =
    1448           0 :                 ((struct pim_interface *)ch->interface->info)->pim;
    1449           0 :         struct pim_upstream *starup = ch->upstream;
    1450             : 
    1451           0 :         if (PIM_DEBUG_PIM_TRACE)
    1452           0 :                 zlog_debug(
    1453             :                         "%s: %s %s eom: %d join %u", __func__,
    1454             :                         pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
    1455             :                         ch->sg_str, eom, join);
    1456           0 :         if (!ch->sources)
    1457             :                 return;
    1458             : 
    1459           0 :         for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
    1460           0 :                 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
    1461           0 :                         continue;
    1462             : 
    1463           0 :                 switch (child->ifjoin_state) {
    1464             :                 case PIM_IFJOIN_NOINFO:
    1465             :                 case PIM_IFJOIN_JOIN:
    1466             :                         break;
    1467           0 :                 case PIM_IFJOIN_PRUNE:
    1468           0 :                         if (!eom)
    1469           0 :                                 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
    1470             :                         break;
    1471           0 :                 case PIM_IFJOIN_PRUNE_PENDING:
    1472           0 :                         if (!eom)
    1473           0 :                                 child->ifjoin_state =
    1474             :                                         PIM_IFJOIN_PRUNE_PENDING_TMP;
    1475             :                         break;
    1476           0 :                 case PIM_IFJOIN_PRUNE_TMP:
    1477             :                 case PIM_IFJOIN_PRUNE_PENDING_TMP:
    1478           0 :                         if (!eom)
    1479             :                                 break;
    1480             : 
    1481           0 :                         if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
    1482           0 :                                 THREAD_OFF(child->t_ifjoin_prune_pending_timer);
    1483           0 :                         THREAD_OFF(child->t_ifjoin_expiry_timer);
    1484             : 
    1485           0 :                         PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
    1486           0 :                         child->ifjoin_state = PIM_IFJOIN_NOINFO;
    1487             : 
    1488           0 :                         if ((I_am_RP(pim, child->sg.grp)) &&
    1489           0 :                             (!pim_upstream_empty_inherited_olist(
    1490             :                                 child->upstream))) {
    1491           0 :                                 pim_channel_add_oif(
    1492           0 :                                         child->upstream->channel_oil,
    1493             :                                         ch->interface, PIM_OIF_FLAG_PROTO_STAR,
    1494             :                                         __func__);
    1495           0 :                                 pim_upstream_update_join_desired(pim,
    1496             :                                                 child->upstream);
    1497             :                         }
    1498           0 :                         send_upstream_starg = true;
    1499             : 
    1500           0 :                         delete_on_noinfo(child);
    1501           0 :                         break;
    1502             :                 }
    1503             :         }
    1504             : 
    1505           0 :         if (send_upstream_starg)
    1506           0 :                 pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
    1507             : }

Generated by: LCOV version v1.16-topotato